xref: /haiku/src/apps/deskbar/BarView.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered
30 trademarks of Be Incorporated in the United States and other countries. Other
31 brand product names are registered trademarks or trademarks of their respective
32 holders.
33 All rights reserved.
34 */
35 
36 
37 #include "BarView.h"
38 
39 #include <AppFileInfo.h>
40 #include <Bitmap.h>
41 #include <Debug.h>
42 #include <Directory.h>
43 #include <LocaleRoster.h>
44 #include <NodeInfo.h>
45 #include <Roster.h>
46 #include <Screen.h>
47 #include <String.h>
48 
49 #include "icons.h"
50 #include "BarApp.h"
51 #include "BarMenuBar.h"
52 #include "BarWindow.h"
53 #include "DeskbarMenu.h"
54 #include "DeskbarUtils.h"
55 #include "ExpandoMenuBar.h"
56 #include "FSUtils.h"
57 #include "InlineScrollView.h"
58 #include "ResourceSet.h"
59 #include "StatusView.h"
60 #include "TeamMenuItem.h"
61 
62 
63 const int32 kDefaultRecentDocCount = 10;
64 const int32 kDefaultRecentAppCount = 10;
65 
66 const int32 kMenuTrackMargin = 20;
67 const float kMinTeamItemHeight = 20.0f;
68 const float kVPad = 2.0f;
69 const float kIconPadding = 8.0f;
70 const float kScrollerDimension = 12.0f;
71 
72 const uint32 kUpdateOrientation = 'UpOr';
73 
74 
75 class BarViewMessageFilter : public BMessageFilter
76 {
77 	public:
78 		BarViewMessageFilter(TBarView* barView);
79 		virtual ~BarViewMessageFilter();
80 
81 		virtual filter_result Filter(BMessage* message, BHandler** target);
82 
83 	private:
84 		TBarView* fBarView;
85 };
86 
87 
88 BarViewMessageFilter::BarViewMessageFilter(TBarView* barView)
89 	:
90 	BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
91 	fBarView(barView)
92 {
93 }
94 
95 
96 BarViewMessageFilter::~BarViewMessageFilter()
97 {
98 }
99 
100 
101 filter_result
102 BarViewMessageFilter::Filter(BMessage* message, BHandler** target)
103 {
104 	if (message->what == B_MOUSE_DOWN || message->what == B_MOUSE_MOVED) {
105 		BPoint where = message->FindPoint("be:view_where");
106 		uint32 transit = message->FindInt32("be:transit");
107 		BMessage* dragMessage = NULL;
108 		if (message->HasMessage("be:drag_message")) {
109 			dragMessage = new BMessage();
110 			message->FindMessage("be:drag_message", dragMessage);
111 		}
112 
113 		switch (message->what) {
114 			case B_MOUSE_DOWN:
115 				fBarView->MouseDown(where);
116 				break;
117 
118 			case B_MOUSE_MOVED:
119 				fBarView->MouseMoved(where, transit, dragMessage);
120 				break;
121 		}
122 
123 		delete dragMessage;
124 	}
125 
126 	return B_DISPATCH_MESSAGE;
127 }
128 
129 
130 //	#pragma mark - TBarView
131 
132 
133 TBarView::TBarView(BRect frame, bool vertical, bool left, bool top,
134 	int32 state, float)
135 	:
136 	BView(frame, "BarView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
137 	fBarApp(static_cast<TBarApp*>(be_app)),
138 	fBarWindow(NULL),
139 	fInlineScrollView(NULL),
140 	fBarMenuBar(NULL),
141 	fExpandoMenuBar(NULL),
142 	fTrayLocation(1),
143 	fIsRaised(false),
144 	fMouseDownOutside(false),
145 	fVertical(vertical),
146 	fTop(top),
147 	fLeft(left),
148 	fState(state),
149 	fRefsRcvdOnly(true),
150 	fDragMessage(NULL),
151 	fCachedTypesList(NULL),
152 	fMaxRecentDocs(kDefaultRecentDocCount),
153 	fMaxRecentApps(kDefaultRecentAppCount),
154 	fLastDragItem(NULL),
155 	fMouseFilter(NULL),
156 	fTabHeight(kMenuBarHeight)
157 {
158 	// get window tab height
159 	BWindow* tmpWindow = new(std::nothrow) BWindow(BRect(), NULL,
160 		B_TITLED_WINDOW, 0);
161 	if (tmpWindow != NULL) {
162 		BMessage settings;
163 		if (tmpWindow->GetDecoratorSettings(&settings) == B_OK) {
164 			BRect tabRect;
165 			if (settings.FindRect("tab frame", &tabRect) == B_OK)
166 				fTabHeight = tabRect.Height();
167 		}
168 		delete tmpWindow;
169 	}
170 
171 	// determine the initial Be menu size
172 	// (will be updated later)
173 	BRect menuFrame(frame);
174 	if (fVertical)
175 		menuFrame.bottom = menuFrame.top + fTabHeight - 1;
176 	else
177 		menuFrame.bottom = menuFrame.top + TeamMenuItemHeight();
178 
179 	// create and add the Be menu
180 	fBarMenuBar = new TBarMenuBar(menuFrame, "BarMenuBar", this);
181 	AddChild(fBarMenuBar);
182 
183 	// create the status tray
184 	fReplicantTray = new TReplicantTray(this);
185 
186 	// create the resize control
187 	fResizeControl = new TResizeControl(this);
188 
189 	// create the drag region and add the resize control
190 	// and replicant tray to it
191 	fDragRegion = new TDragRegion(this, fReplicantTray);
192 	fDragRegion->AddChild(fResizeControl);
193 	fDragRegion->AddChild(fReplicantTray);
194 
195 	// Add the drag region
196 	if (fTrayLocation != 0)
197 		AddChild(fDragRegion);
198 
199 	// create and add the expando menu bar
200 	fExpandoMenuBar = new TExpandoMenuBar(
201 		fVertical ? B_ITEMS_IN_COLUMN : B_ITEMS_IN_ROW, this);
202 	fInlineScrollView = new TInlineScrollView(fExpandoMenuBar,
203 		fVertical ? B_VERTICAL : B_HORIZONTAL);
204 	AddChild(fInlineScrollView);
205 
206 	// hide the expando menu bar in mini-mode
207 	if (state == kMiniState)
208 		fInlineScrollView->Hide();
209 
210 	// if auto-hide is on and we're not already hidden, hide ourself
211 	if (fBarApp->Settings()->autoHide && !IsHidden())
212 		Hide();
213 }
214 
215 
216 TBarView::~TBarView()
217 {
218 	delete fDragMessage;
219 	delete fCachedTypesList;
220 	delete fBarMenuBar;
221 }
222 
223 
224 void
225 TBarView::AttachedToWindow()
226 {
227 	BView::AttachedToWindow();
228 
229 	fBarWindow = dynamic_cast<TBarWindow*>(Window());
230 
231 	SetViewUIColor(B_MENU_BACKGROUND_COLOR);
232 	SetFont(be_plain_font);
233 
234 	fMouseFilter = new BarViewMessageFilter(this);
235 	Window()->AddCommonFilter(fMouseFilter);
236 
237 	fTrackingHookData.fTrackingHook = MenuTrackingHook;
238 	fTrackingHookData.fTarget = BMessenger(this);
239 	fTrackingHookData.fDragMessage = new BMessage(B_REFS_RECEIVED);
240 
241 	if (!fVertical)
242 		UpdatePlacement(); // update MenuBarHeight
243 }
244 
245 
246 void
247 TBarView::DetachedFromWindow()
248 {
249 	Window()->RemoveCommonFilter(fMouseFilter);
250 	delete fMouseFilter;
251 	fMouseFilter = NULL;
252 	delete fTrackingHookData.fDragMessage;
253 	fTrackingHookData.fDragMessage = NULL;
254 }
255 
256 
257 void
258 TBarView::Draw(BRect)
259 {
260 	BRect bounds(Bounds());
261 
262 	rgb_color hilite = tint_color(ViewColor(), B_DARKEN_1_TINT);
263 
264 	SetHighColor(hilite);
265 	if (AcrossTop())
266 		StrokeLine(bounds.LeftBottom(), bounds.RightBottom());
267 	else if (AcrossBottom())
268 		StrokeLine(bounds.LeftTop(), bounds.RightTop());
269 
270 	if (fVertical && fState == kExpandoState) {
271 		SetHighColor(hilite);
272 		BRect frame(fExpandoMenuBar->Frame());
273 		StrokeLine(BPoint(frame.left, frame.top - 1),
274 			BPoint(frame.right, frame.top -1));
275 	}
276 }
277 
278 
279 void
280 TBarView::MessageReceived(BMessage* message)
281 {
282 	switch (message->what) {
283 		case B_LOCALE_CHANGED:
284 		case kRealignReplicants:
285 		case kShowHideTime:
286 		case kShowSeconds:
287 		case kShowDayOfWeek:
288 		case kShowTimeZone:
289 		case kGetClockSettings:
290 			fReplicantTray->MessageReceived(message);
291 			break;
292 
293 		case B_REFS_RECEIVED:
294 			// received when an item is selected during DnD
295 			// message is targeted here from Be menu
296 			HandleDeskbarMenu(message);
297 			break;
298 
299 		case B_ARCHIVED_OBJECT:
300 		{
301 			// this message has been retargeted to here
302 			// instead of directly to the replicant tray
303 			// so that I can follow the common pathway
304 			// for adding icons to the tray
305 			int32 id;
306 			if (AddItem(message, B_DESKBAR_TRAY, &id) == B_OK)
307 				Looper()->DetachCurrentMessage();
308 			break;
309 		}
310 
311 		case kUpdateOrientation:
312 		{
313 			_ChangeState(message);
314 			break;
315 		}
316 
317 		default:
318 			BView::MessageReceived(message);
319 	}
320 }
321 
322 
323 void
324 TBarView::MouseDown(BPoint where)
325 {
326 	// exit if menu or calendar is showing
327 	if (fBarWindow == NULL || fBarWindow->IsShowingMenu()
328 		|| fReplicantTray->fTime->IsShowingCalendar()) {
329 		return BView::MouseDown(where);
330 	}
331 
332 	// where is relative to status tray while mouse is over it so pull
333 	// the screen point out of the message instead
334 	BMessage* currentMessage = Window()->CurrentMessage();
335 	if (currentMessage == NULL)
336 		return BView::MouseDown(where);
337 
338 	desk_settings* settings = fBarApp->Settings();
339 	bool alwaysOnTop = settings->alwaysOnTop;
340 	bool autoRaise = settings->autoRaise;
341 	bool autoHide = settings->autoHide;
342 
343 	BPoint whereScreen = currentMessage->GetPoint("screen_where",
344 		ConvertToScreen(where));
345 	fMouseDownOutside = !Window()->Frame().Contains(whereScreen);
346 
347 	if (fMouseDownOutside) {
348 		// lower Deskbar
349 		if (!alwaysOnTop && autoRaise && fIsRaised)
350 			RaiseDeskbar(false);
351 
352 		// hide Deskbar
353 		if (autoHide && !IsHidden())
354 			HideDeskbar(true);
355 	} else {
356 		// Activate Deskbar on click only if not in auto-raise mode and not
357 		// in always-on-top mode. In auto-raise mode click activates through
358 		// foreground windows, which we don't want. We don't ever want to
359 		// activate Deskbar in always-on-top mode because Deskbar is
360 		// already on top and we don't want to change the active window.
361 		if (!autoRaise && !alwaysOnTop)
362 			Window()->Activate(true);
363 
364 		if ((modifiers() & (B_CONTROL_KEY | B_COMMAND_KEY | B_OPTION_KEY
365 				| B_SHIFT_KEY)) == (B_CONTROL_KEY | B_COMMAND_KEY)) {
366 			// The window key was pressed - enter dragging code
367 			fDragRegion->MouseDown(fDragRegion->DragRegion().LeftTop());
368 			return BView::MouseDown(where);
369 		}
370 	}
371 
372 	BView::MouseDown(where);
373 }
374 
375 
376 void
377 TBarView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
378 {
379 	if (fDragRegion->IsDragging())
380 		return fDragRegion->MouseMoved(where, transit, dragMessage);
381 	else if (fResizeControl->IsResizing())
382 		return BView::MouseMoved(where, transit, dragMessage);
383 
384 	desk_settings* settings = fBarApp->Settings();
385 	bool alwaysOnTop = settings->alwaysOnTop;
386 	bool autoRaise = settings->autoRaise;
387 	bool autoHide = settings->autoHide;
388 
389 	// exit if both auto-raise and auto-hide are off
390 	if (!autoRaise && !autoHide) {
391 		// turn off mouse tracking
392 		SetEventMask(0);
393 
394 		return BView::MouseMoved(where, transit, dragMessage);
395 	} else if (EventMask() != B_POINTER_EVENTS) {
396 		// track mouse outside view
397 		SetEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
398 	}
399 
400 	// exit if menu or calendar is showing
401 	if (fBarWindow == NULL || fBarWindow->IsShowingMenu()
402 		|| fReplicantTray->fTime->IsShowingCalendar()) {
403 		return BView::MouseMoved(where, transit, dragMessage);
404 	}
405 
406 	// where is relative to status tray while mouse is over it so pull
407 	// the screen point out of the message instead
408 	BMessage* currentMessage = Window()->CurrentMessage();
409 	if (currentMessage == NULL)
410 		return BView::MouseMoved(where, transit, dragMessage);
411 
412 	BPoint whereScreen = currentMessage->GetPoint("screen_where",
413 		ConvertToScreen(where));
414 	BRect screenFrame = (BScreen(Window())).Frame();
415 	bool onScreenEdge = whereScreen.x == screenFrame.left
416 		|| whereScreen.x == screenFrame.right
417 		|| whereScreen.y == screenFrame.top
418 		|| whereScreen.y == screenFrame.bottom;
419 
420 	// Auto-Raise and Auto-Hide
421 	if (!Window()->Frame().Contains(whereScreen)) {
422 		// lower Deskbar
423 		if (!alwaysOnTop && autoRaise && fIsRaised && !fMouseDownOutside)
424 			RaiseDeskbar(false);
425 
426 		// check if cursor to bar distance is below threshold
427 		BRect preventHideArea = Window()->Frame().InsetByCopy(
428 			-kMaxPreventHidingDist, -kMaxPreventHidingDist);
429 		if (!preventHideArea.Contains(whereScreen)
430 			&& autoHide && !IsHidden()) {
431 			// hide Deskbar
432 			HideDeskbar(true);
433 		}
434 	} else if (onScreenEdge) {
435 		// cursor is on a screen edge within the window frame
436 
437 		// raise Deskbar
438 		if (!alwaysOnTop && autoRaise && !fIsRaised && !fMouseDownOutside)
439 			RaiseDeskbar(true);
440 
441 		// show Deskbar
442 		if (autoHide && IsHidden())
443 			HideDeskbar(false);
444 	}
445 
446 	BView::MouseMoved(where, transit, dragMessage);
447 }
448 
449 
450 void
451 TBarView::MouseUp(BPoint where)
452 {
453 	fMouseDownOutside = false;
454 
455 	BView::MouseUp(where);
456 }
457 
458 
459 void
460 TBarView::PlaceDeskbarMenu()
461 {
462 	float width = 0;
463 	float height = 0;
464 
465 	// Calculate the size of the deskbar menu
466 	BRect menuFrame(Bounds());
467 	if (fVertical) {
468 		if (fState == kMiniState)
469 			width = gMinimumWindowWidth;
470 		else
471 			width = fBarApp->Settings()->width;
472 
473 		height = fTabHeight;
474 	} else {
475 		width = gMinimumWindowWidth / 2 + kIconPadding;
476 		height = std::max(TeamMenuItemHeight(),
477 			kGutter + fReplicantTray->MaxReplicantHeight() + kGutter);
478 	}
479 	menuFrame.bottom = menuFrame.top + height;
480 
481 	if (fBarMenuBar == NULL) {
482 		// create the Be menu
483 		fBarMenuBar = new TBarMenuBar(menuFrame, "BarMenuBar", this);
484 		AddChild(fBarMenuBar);
485 	} else
486 		fBarMenuBar->SmartResize(-1, -1);
487 
488 	if (fVertical) {
489 		switch (fState) {
490 			case kFullState:
491 			case kExpandoState:
492 			default:
493 				// shows apps below tray
494 				fBarMenuBar->RemoveTeamMenu();
495 				fBarMenuBar->RemoveSeperatorItem();
496 				break;
497 
498 			case kMiniState:
499 				// leaf menu next to team menu
500 				fBarMenuBar->RemoveSeperatorItem();
501 				fBarMenuBar->AddTeamMenu();
502 				break;
503 		}
504 	} else {
505 		// shows apps to the right of the leaf menu
506 		fBarMenuBar->RemoveTeamMenu();
507 		fBarMenuBar->AddSeparatorItem();
508 	}
509 
510 	fBarMenuBar->SmartResize(width, height);
511 	fBarMenuBar->MoveTo(B_ORIGIN);
512 }
513 
514 
515 void
516 TBarView::PlaceTray(bool vertSwap, bool leftSwap)
517 {
518 	BPoint statusLoc;
519 	if (fTrayLocation == 0) {
520 		// no replicant tray mode, not used
521 		if (!fReplicantTray->IsHidden())
522 			fReplicantTray->Hide();
523 		return;
524 	} else if (fReplicantTray->IsHidden())
525 		fReplicantTray->Show();
526 
527 	fReplicantTray->RealignReplicants();
528 	fDragRegion->ResizeToPreferred();
529 		// also resizes replicant tray
530 
531 	if (fVertical) {
532 		if (fState == kExpandoState || fState == kFullState) {
533 			// move replicant tray down by 2px
534 			if (fLeft) {
535 				// move replicant tray past dragger and shrink by same amount
536 				fReplicantTray->MoveTo(kDragWidth + 1, 2);
537 				fReplicantTray->ResizeBy(-(kDragWidth + 1), -2);
538 			} else {
539 				fReplicantTray->MoveTo(0, 2);
540 				fReplicantTray->ResizeBy(0, -2);
541 			}
542 
543 			statusLoc.x = 0;
544 			statusLoc.y = fBarMenuBar->Frame().bottom + 1;
545 		} else if (fState == kMiniState) {
546 			statusLoc.x = fLeft ? fBarMenuBar->Frame().right + 1 : 0;
547 			statusLoc.y = 0;
548 
549 			// move past dragger and top border
550 			// and make room for the top and bottom borders
551 			fReplicantTray->MoveTo(fLeft ? kDragWidth : 0, kGutter);
552 			fReplicantTray->ResizeBy(0, -4);
553 		}
554 	} else {
555 		// horizontal
556 
557 		// move tray right and down to not cover border, resize by same
558 		fReplicantTray->MoveTo(2, 0);
559 		fReplicantTray->ResizeBy(-2, 0);
560 		BRect screenFrame = (BScreen(Window())).Frame();
561 		statusLoc.x = screenFrame.right - fDragRegion->Bounds().Width();
562 		statusLoc.y = 0;
563 	}
564 
565 	fDragRegion->MoveTo(statusLoc);
566 
567 	// make room for top and bottom border
568 	fResizeControl->ResizeTo(kDragWidth,  fDragRegion->Bounds().Height() - 2);
569 
570 	// resize control is only used in vertical expando and full state
571 	if (fVertical && fState != kMiniState) {
572 		// move resize control into place based on width setting
573 		fResizeControl->MoveTo(
574 			fLeft ? fBarApp->Settings()->width - kDragWidth : 0, 1);
575 		if (fResizeControl->IsHidden())
576 			fResizeControl->Show();
577 	} else {
578 		// hide resize control
579 		if (!fResizeControl->IsHidden())
580 			fResizeControl->Hide();
581 	}
582 
583 	fDragRegion->Invalidate();
584 }
585 
586 
587 void
588 TBarView::PlaceApplicationBar()
589 {
590 	BRect screenFrame = (BScreen(Window())).Frame();
591 	if (fVertical && fState == kMiniState) {
592 		if (!fInlineScrollView->IsHidden())
593 			fInlineScrollView->Hide();
594 
595 		SizeWindow(screenFrame);
596 		PositionWindow(screenFrame);
597 		Window()->UpdateIfNeeded();
598 		// move the menu bar into place after the window has been resized
599 		// based on replicant tray
600 		fBarMenuBar->MoveTo(fLeft ? 0 : fDragRegion->Bounds().right + 1, 0);
601 		Invalidate();
602 		return;
603 	}
604 
605 	if (fInlineScrollView->IsHidden())
606 		fInlineScrollView->Show();
607 
608 	BRect expandoFrame(0, 0, 0, 0);
609 	if (fVertical) {
610 		// left or right
611 		expandoFrame.left = fDragRegion->Frame().left;
612 		expandoFrame.top = fTrayLocation != 0 ? fDragRegion->Frame().bottom + 1
613 			: fBarMenuBar->Frame().bottom + 1;
614 		expandoFrame.right = fBarApp->Settings()->width;
615 		expandoFrame.bottom = fState == kFullState ? screenFrame.bottom
616 			: Frame().bottom;
617 	} else {
618 		// top or bottom
619 		expandoFrame.top = 0;
620 		expandoFrame.bottom = TeamMenuItemHeight();
621 		expandoFrame.left = gMinimumWindowWidth / 2 + kIconPadding;
622 		expandoFrame.right = screenFrame.Width();
623 		if (fTrayLocation != 0 && fDragRegion != NULL)
624 			expandoFrame.right -= fDragRegion->Frame().Width() + 1;
625 	}
626 
627 	fInlineScrollView->DetachScrollers();
628 	fInlineScrollView->MoveTo(expandoFrame.LeftTop());
629 	fInlineScrollView->ResizeTo(expandoFrame.Width(), fVertical
630 		? screenFrame.bottom - expandoFrame.top : expandoFrame.bottom);
631 	fExpandoMenuBar->ResizeTo(expandoFrame.Width(), expandoFrame.Height());
632 	fExpandoMenuBar->MoveTo(0, 0);
633 	fExpandoMenuBar->BuildItems();
634 	fExpandoMenuBar->SizeWindow(0);
635 }
636 
637 
638 void
639 TBarView::GetPreferredWindowSize(BRect screenFrame, float* width,
640 	float* height)
641 {
642 	float windowHeight = 0;
643 	float windowWidth = 0;
644 	bool setToHiddenSize = fBarApp->Settings()->autoHide && IsHidden()
645 		&& !fDragRegion->IsDragging();
646 
647 	if (setToHiddenSize) {
648 		windowHeight = kHiddenDimension;
649 
650 		if (fState == kExpandoState && !fVertical) {
651 			// top or bottom, full
652 			windowWidth = screenFrame.Width();
653 		} else
654 			windowWidth = kHiddenDimension;
655 	} else if (!fVertical) {
656 		// horizontal top or bottom
657 		windowHeight = std::max(TeamMenuItemHeight(),
658 			kGutter + fReplicantTray->MaxReplicantHeight() + kGutter);
659 		windowWidth = screenFrame.Width();
660 	} else {
661 		// vertical
662 		switch (fState) {
663 			case kFullState:
664 			case kExpandoState:
665 			default:
666 				// top left or right
667 				if (fTrayLocation != 0)
668 					windowHeight = fDragRegion->Frame().bottom + 1;
669 				else
670 					windowHeight = fBarMenuBar->Frame().bottom + 1;
671 
672 				// use Height() here not bottom because view can be scrolled
673 				if (fExpandoMenuBar != NULL)
674 					windowHeight += fExpandoMenuBar->Bounds().Height();
675 
676 				// full state has minimum screen window height
677 				if (fState == kFullState)
678 					windowHeight = std::max(screenFrame.bottom, windowHeight);
679 
680 				windowWidth = fBarApp->Settings()->width;
681 				break;
682 
683 			case kMiniState:
684 				// four corners
685 				windowHeight = fBarMenuBar->Frame().bottom;
686 				windowWidth = fDragRegion->Frame().Width()
687 					+ fBarMenuBar->Frame().Width() + 1;
688 				break;
689 		}
690 	}
691 
692 	*width = windowWidth;
693 	*height = windowHeight;
694 }
695 
696 
697 void
698 TBarView::SizeWindow(BRect screenFrame)
699 {
700 	float windowWidth;
701 	float windowHeight;
702 	GetPreferredWindowSize(screenFrame, &windowWidth, &windowHeight);
703 	Window()->ResizeTo(windowWidth, windowHeight);
704 	ResizeTo(windowWidth, windowHeight);
705 }
706 
707 
708 void
709 TBarView::PositionWindow(BRect screenFrame)
710 {
711 	float windowWidth;
712 	float windowHeight;
713 	GetPreferredWindowSize(screenFrame, &windowWidth, &windowHeight);
714 
715 	BPoint moveLoc(0, 0);
716 	// right, expanded, mini, or full
717 	if (!fLeft && fVertical)
718 		moveLoc.x = screenFrame.right - windowWidth;
719 
720 	// bottom, full
721 	if (!fTop)
722 		moveLoc.y = screenFrame.bottom - windowHeight;
723 
724 	Window()->MoveTo(moveLoc);
725 }
726 
727 
728 void
729 TBarView::CheckForScrolling()
730 {
731 	if (fInlineScrollView == NULL && fExpandoMenuBar == NULL)
732 		return;
733 
734 	if (fExpandoMenuBar->CheckForSizeOverrun())
735 		fInlineScrollView->AttachScrollers();
736 	else
737 		fInlineScrollView->DetachScrollers();
738 }
739 
740 
741 void
742 TBarView::SaveSettings()
743 {
744 	desk_settings* settings = fBarApp->Settings();
745 
746 	settings->vertical = fVertical;
747 	settings->left = fLeft;
748 	settings->top = fTop;
749 	settings->state = fState;
750 
751 	fReplicantTray->SaveTimeSettings();
752 }
753 
754 
755 void
756 TBarView::UpdatePlacement()
757 {
758 	ChangeState(fState, fVertical, fLeft, fTop);
759 }
760 
761 
762 void
763 TBarView::ChangeState(int32 state, bool vertical, bool left, bool top,
764 	bool async)
765 {
766 	BMessage message(kUpdateOrientation);
767 	message.AddInt32("state", state);
768 	message.AddBool("vertical", vertical);
769 	message.AddBool("left", left);
770 	message.AddBool("top", top);
771 
772 	if (async)
773 		BMessenger(this).SendMessage(&message);
774 	else
775 		_ChangeState(&message);
776 }
777 
778 
779 void
780 TBarView::_ChangeState(BMessage* message)
781 {
782 	int32 state = message->FindInt32("state");
783 	bool vertical = message->FindBool("vertical");
784 	bool left = message->FindBool("left");
785 	bool top = message->FindBool("top");
786 
787 	bool vertSwap = (fVertical != vertical);
788 	bool leftSwap = (fLeft != left);
789 	bool stateChanged = (fState != state);
790 
791 	fState = state;
792 	fVertical = vertical;
793 	fLeft = left;
794 	fTop = top;
795 
796 	if (stateChanged || vertSwap) {
797 		be_app->PostMessage(kStateChanged);
798 			// Send a message to the preferences window to let it know to
799 			// enable or disable preference items.
800 
801 		TBarWindow* barWindow = dynamic_cast<TBarWindow*>(Window());
802 		if (barWindow != NULL)
803 			barWindow->SetSizeLimits();
804 
805 		if (vertSwap && fExpandoMenuBar != NULL) {
806 			if (fVertical) {
807 				fInlineScrollView->SetOrientation(B_VERTICAL);
808 				fExpandoMenuBar->SetMenuLayout(B_ITEMS_IN_COLUMN);
809 				fExpandoMenuBar->StartMonitoringWindows();
810 			} else {
811 				fInlineScrollView->SetOrientation(B_HORIZONTAL);
812 				fExpandoMenuBar->SetMenuLayout(B_ITEMS_IN_ROW);
813 				fExpandoMenuBar->StopMonitoringWindows();
814 			}
815 		}
816 	}
817 
818 	PlaceDeskbarMenu();
819 	PlaceTray(vertSwap, leftSwap);
820 	PlaceApplicationBar();
821 }
822 
823 
824 void
825 TBarView::RaiseDeskbar(bool raise)
826 {
827 	fIsRaised = raise;
828 
829 	// raise or lower Deskbar without changing the active window
830 	if (raise) {
831 		Window()->SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
832 		Window()->SetFeel(B_NORMAL_WINDOW_FEEL);
833 	} else
834 		Window()->SendBehind(Window());
835 }
836 
837 
838 void
839 TBarView::HideDeskbar(bool hide)
840 {
841 	BRect screenFrame = (BScreen(Window())).Frame();
842 
843 	if (hide) {
844 		Hide();
845 		if (fBarWindow != NULL)
846 			fBarWindow->SetSizeLimits();
847 
848 		PositionWindow(screenFrame);
849 		SizeWindow(screenFrame);
850 	} else {
851 		Show();
852 		if (fBarWindow != NULL)
853 			fBarWindow->SetSizeLimits();
854 
855 		SizeWindow(screenFrame);
856 		PositionWindow(screenFrame);
857 	}
858 }
859 
860 
861 //	#pragma mark - Drag and Drop
862 
863 
864 void
865 TBarView::CacheDragData(const BMessage* incoming)
866 {
867 	if (!incoming)
868 		return;
869 
870 	if (Dragging() && SpringLoadedFolderCompareMessages(incoming, fDragMessage))
871 		return;
872 
873 	// disposes then fills cached drag message and
874 	// mimetypes list
875 	SpringLoadedFolderCacheDragData(incoming, &fDragMessage, &fCachedTypesList);
876 }
877 
878 
879 static void
880 init_tracking_hook(BMenuItem* item,
881 	bool (*hookFunction)(BMenu*, void*), void* state)
882 {
883 	if (!item)
884 		return;
885 
886 	BMenu* windowMenu = item->Submenu();
887 	if (windowMenu) {
888 		// have a menu, set the tracking hook
889 		windowMenu->SetTrackingHook(hookFunction, state);
890 	}
891 }
892 
893 
894 status_t
895 TBarView::DragStart()
896 {
897 	if (!Dragging())
898 		return B_OK;
899 
900 	BPoint loc;
901 	uint32 buttons;
902 	GetMouse(&loc, &buttons);
903 
904 	if (fExpandoMenuBar != NULL && fExpandoMenuBar->Frame().Contains(loc)) {
905 		ConvertToScreen(&loc);
906 		BPoint expandoLocation = fExpandoMenuBar->ConvertFromScreen(loc);
907 		TTeamMenuItem* item = fExpandoMenuBar->TeamItemAtPoint(expandoLocation);
908 
909 		if (fLastDragItem)
910 			init_tracking_hook(fLastDragItem, NULL, NULL);
911 
912 		if (item != NULL) {
913 			if (item == fLastDragItem)
914 				return B_OK;
915 
916 			fLastDragItem = item;
917 		}
918 	}
919 
920 	return B_OK;
921 }
922 
923 
924 bool
925 TBarView::MenuTrackingHook(BMenu* menu, void* castToThis)
926 {
927 	// return true if the menu should go away
928 	TrackingHookData* data = static_cast<TrackingHookData*>(castToThis);
929 	if (!data)
930 		return false;
931 
932 	TBarView* barView = dynamic_cast<TBarView*>(data->fTarget.Target(NULL));
933 	if (!barView || !menu->LockLooper())
934 		return false;
935 
936 	uint32 buttons;
937 	BPoint location;
938 	menu->GetMouse(&location, &buttons);
939 
940 	bool endMenu = true;
941 	BRect frame(menu->Bounds());
942 	frame.InsetBy(-kMenuTrackMargin, -kMenuTrackMargin);
943 
944 	if (frame.Contains(location)) {
945 		// if current loc is still in the menu
946 		// keep tracking
947 		endMenu = false;
948 	} else {
949 		// see if the mouse is in the team/deskbar menu item
950 		menu->ConvertToScreen(&location);
951 		if (barView->LockLooper()) {
952 			TExpandoMenuBar* expandoMenuBar = barView->ExpandoMenuBar();
953 			TBarWindow* barWindow
954 				= dynamic_cast<TBarWindow*>(barView->Window());
955 			TDeskbarMenu* deskbarMenu = barWindow->DeskbarMenu();
956 
957 			if (deskbarMenu && deskbarMenu->LockLooper()) {
958 				deskbarMenu->ConvertFromScreen(&location);
959 				if (deskbarMenu->Frame().Contains(location))
960 					endMenu = false;
961 
962 				deskbarMenu->UnlockLooper();
963 			}
964 
965 			if (endMenu && expandoMenuBar) {
966 				expandoMenuBar->ConvertFromScreen(&location);
967 				BMenuItem* item = expandoMenuBar->TeamItemAtPoint(location);
968 				if (item)
969 					endMenu = false;
970 			}
971 			barView->UnlockLooper();
972 		}
973 	}
974 
975 	menu->UnlockLooper();
976 
977 	return endMenu;
978 }
979 
980 
981 // used by WindowMenu and TeamMenu to
982 // set the tracking hook for dragging
983 TrackingHookData*
984 TBarView::GetTrackingHookData()
985 {
986 	// all tracking hook data is
987 	// preset in AttachedToWindow
988 	// data should never change
989 	return &fTrackingHookData;
990 }
991 
992 
993 void
994 TBarView::DragStop(bool full)
995 {
996 	if (!Dragging())
997 		return;
998 
999 	if (fExpandoMenuBar != NULL) {
1000 		if (fLastDragItem != NULL) {
1001 			init_tracking_hook(fLastDragItem, NULL, NULL);
1002 			fLastDragItem = NULL;
1003 		}
1004 	}
1005 
1006 	if (full) {
1007 		delete fDragMessage;
1008 		fDragMessage = NULL;
1009 
1010 		delete fCachedTypesList;
1011 		fCachedTypesList = NULL;
1012 	}
1013 }
1014 
1015 
1016 bool
1017 TBarView::AppCanHandleTypes(const char* signature)
1018 {
1019 	// used for filtering apps/teams in the ExpandoMenuBar and TeamMenu
1020 
1021 	if (modifiers() & B_CONTROL_KEY) {
1022 		// control key forces acceptance, just like drag&drop on icons
1023 		return true;
1024 	}
1025 
1026 	if (!signature || strlen(signature) == 0
1027 		|| !fCachedTypesList || fCachedTypesList->CountItems() == 0)
1028 		return false;
1029 
1030 	if (strcasecmp(signature, kTrackerSignature) == 0) {
1031 		// tracker should support all types
1032 		// and should pass them on to the appropriate application
1033 		return true;
1034 	}
1035 
1036 	entry_ref hintref;
1037 	BMimeType appmime(signature);
1038 	if (appmime.GetAppHint(&hintref) != B_OK)
1039 		return false;
1040 
1041 	// an app was found, now see if it supports any of
1042 	// the refs in the message
1043 	BFile file(&hintref, O_RDONLY);
1044 	BAppFileInfo fileinfo(&file);
1045 
1046 	// scan the cached mimetype list and see if this app
1047 	// supports anything in the list
1048 	// only one item needs to match in the list of refs
1049 
1050 	int32 count = fCachedTypesList->CountItems();
1051 	for (int32 i = 0 ; i < count ; i++) {
1052 		if (fileinfo.IsSupportedType(fCachedTypesList->ItemAt(i)->String()))
1053 			return true;
1054 	}
1055 
1056 	return false;
1057 }
1058 
1059 
1060 void
1061 TBarView::SetDragOverride(bool on)
1062 {
1063 	fRefsRcvdOnly = on;
1064 }
1065 
1066 
1067 bool
1068 TBarView::DragOverride()
1069 {
1070 	return fRefsRcvdOnly;
1071 }
1072 
1073 
1074 status_t
1075 TBarView::SendDragMessage(const char* signature, entry_ref* ref)
1076 {
1077 	status_t err = B_ERROR;
1078 	if (fDragMessage != NULL) {
1079 		if (fRefsRcvdOnly) {
1080 			// current message sent to apps is only B_REFS_RECEIVED
1081 			fDragMessage->what = B_REFS_RECEIVED;
1082 		}
1083 
1084 		BRoster roster;
1085 		if (signature != NULL && *signature != '\0'
1086 			&& roster.IsRunning(signature)) {
1087 			BMessenger messenger(signature);
1088 			// drag message is still owned by DB, copy is sent
1089 			// can toss it after send
1090 			err = messenger.SendMessage(fDragMessage);
1091 		} else if (ref != NULL) {
1092 			FSLaunchItem((const entry_ref*)ref, (const BMessage*)fDragMessage,
1093 				true, true);
1094 		} else if (signature != NULL && *signature != '\0')
1095 			roster.Launch(signature, fDragMessage);
1096 	}
1097 
1098 	return err;
1099 }
1100 
1101 
1102 bool
1103 TBarView::InvokeItem(const char* signature)
1104 {
1105 	// sent from TeamMenuItem
1106 	if (Dragging() && AppCanHandleTypes(signature)) {
1107 		SendDragMessage(signature);
1108 		// invoking okay to toss memory
1109 		DragStop(true);
1110 		return true;
1111 	}
1112 
1113 	return false;
1114 }
1115 
1116 
1117 void
1118 TBarView::HandleDeskbarMenu(BMessage* messagewithdestination)
1119 {
1120 	if (!Dragging())
1121 		return;
1122 
1123 	// in mini-mode
1124 	if (fVertical && fState != kExpandoState) {
1125 		// if drop is in the team menu, bail
1126 		if (fBarMenuBar->CountItems() >= 2) {
1127 			uint32 buttons;
1128 			BPoint location;
1129 			GetMouse(&location, &buttons);
1130 			if (fBarMenuBar->ItemAt(1)->Frame().Contains(location))
1131 				return;
1132 		}
1133 	}
1134 
1135 	if (messagewithdestination) {
1136 		entry_ref ref;
1137 		if (messagewithdestination->FindRef("refs", &ref) == B_OK) {
1138 			BEntry entry(&ref, true);
1139 			if (entry.IsDirectory()) {
1140 				// if the ref received (should only be 1) is a directory
1141 				// then add the drag refs to the directory
1142 				AddRefsToDeskbarMenu(DragMessage(), &ref);
1143 			} else
1144 				SendDragMessage(NULL, &ref);
1145 		}
1146 	} else {
1147 		// adds drag refs to top level in deskbar menu
1148 		AddRefsToDeskbarMenu(DragMessage(), NULL);
1149 	}
1150 
1151 	// clean up drag message and types list
1152 	DragStop(true);
1153 }
1154 
1155 
1156 //	#pragma mark - Add-ons
1157 
1158 
1159 // shelf is ignored for now,
1160 // it exists in anticipation of having other 'shelves' for
1161 // storing items
1162 
1163 status_t
1164 TBarView::ItemInfo(int32 id, const char** name, DeskbarShelf* shelf)
1165 {
1166 	*shelf = B_DESKBAR_TRAY;
1167 	return fReplicantTray->ItemInfo(id, name);
1168 }
1169 
1170 
1171 status_t
1172 TBarView::ItemInfo(const char* name, int32* id, DeskbarShelf* shelf)
1173 {
1174 	*shelf = B_DESKBAR_TRAY;
1175 	return fReplicantTray->ItemInfo(name, id);
1176 }
1177 
1178 
1179 bool
1180 TBarView::ItemExists(int32 id, DeskbarShelf)
1181 {
1182 	return fReplicantTray->IconExists(id);
1183 }
1184 
1185 
1186 bool
1187 TBarView::ItemExists(const char* name, DeskbarShelf)
1188 {
1189 	return fReplicantTray->IconExists(name);
1190 }
1191 
1192 
1193 int32
1194 TBarView::CountItems(DeskbarShelf)
1195 {
1196 	return fReplicantTray->ReplicantCount();
1197 }
1198 
1199 
1200 BSize
1201 TBarView::MaxItemSize(DeskbarShelf shelf)
1202 {
1203 	return BSize(fReplicantTray->MaxReplicantWidth(),
1204 		fReplicantTray->MaxReplicantHeight());
1205 }
1206 
1207 
1208 status_t
1209 TBarView::AddItem(BMessage* item, DeskbarShelf, int32* id)
1210 {
1211 	return fReplicantTray->AddIcon(item, id);
1212 }
1213 
1214 
1215 status_t
1216 TBarView::AddItem(BEntry* entry, DeskbarShelf, int32* id)
1217 {
1218 	return fReplicantTray->LoadAddOn(entry, id);
1219 }
1220 
1221 
1222 void
1223 TBarView::RemoveItem(int32 id)
1224 {
1225 	fReplicantTray->RemoveIcon(id);
1226 }
1227 
1228 
1229 void
1230 TBarView::RemoveItem(const char* name, DeskbarShelf)
1231 {
1232 	fReplicantTray->RemoveIcon(name);
1233 }
1234 
1235 
1236 BRect
1237 TBarView::OffsetIconFrame(BRect rect) const
1238 {
1239 	BRect frame(Frame());
1240 
1241 	frame.left += fDragRegion->Frame().left + fReplicantTray->Frame().left
1242 		+ rect.left;
1243 	frame.top += fDragRegion->Frame().top + fReplicantTray->Frame().top
1244 		+ rect.top;
1245 
1246 	frame.right = frame.left + rect.Width();
1247 	frame.bottom = frame.top + rect.Height();
1248 
1249 	return frame;
1250 }
1251 
1252 
1253 BRect
1254 TBarView::IconFrame(int32 id) const
1255 {
1256 	return OffsetIconFrame(fReplicantTray->IconFrame(id));
1257 }
1258 
1259 
1260 BRect
1261 TBarView::IconFrame(const char* name) const
1262 {
1263 	return OffsetIconFrame(fReplicantTray->IconFrame(name));
1264 }
1265 
1266 
1267 float
1268 TBarView::TeamMenuItemHeight() const
1269 {
1270 	const int32 iconSize = fBarApp->IconSize();
1271 	float iconSizePadded = kVPad + iconSize + kVPad;
1272 
1273 	font_height fontHeight;
1274 	if (fExpandoMenuBar != NULL)
1275 		fExpandoMenuBar->GetFontHeight(&fontHeight);
1276 	else
1277 		GetFontHeight(&fontHeight);
1278 
1279 	float labelHeight = fontHeight.ascent + fontHeight.descent;
1280 	labelHeight = labelHeight < kMinTeamItemHeight ? kMinTeamItemHeight
1281 		: ceilf(labelHeight * 1.1f);
1282 
1283 	bool hideLabels = static_cast<TBarApp*>(be_app)->Settings()->hideLabels;
1284 	if (hideLabels && iconSize > B_MINI_ICON) {
1285 		// height is determined based solely on icon size
1286 		return iconSizePadded;
1287 	} else if (!fVertical || (fVertical && iconSize <= B_LARGE_ICON)) {
1288 		// horizontal or vertical with label on same row as icon:
1289 		// height based on icon size or font size, whichever is bigger
1290 		return std::max(iconSizePadded, labelHeight);
1291 	} else if (fVertical && iconSize > B_LARGE_ICON) {
1292 		// vertical with label below icon: height based on icon and label
1293 		return ceilf(iconSizePadded + labelHeight);
1294 	} else {
1295 		// height is determined based solely on label height
1296 		return labelHeight;
1297 	}
1298 }
1299