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