xref: /haiku/src/apps/deskbar/BarView.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 = gMinimumWindowWidth / 2 + be_control_look->ComposeSpacing(kIconPadding);
621 		expandoFrame.right = screenFrame.Width();
622 		if (fTrayLocation != 0 && fDragRegion != NULL)
623 			expandoFrame.right -= fDragRegion->Frame().Width() + 1;
624 	}
625 
626 	fInlineScrollView->DetachScrollers();
627 	fInlineScrollView->MoveTo(expandoFrame.LeftTop());
628 	fInlineScrollView->ResizeTo(expandoFrame.Width(), fVertical
629 		? screenFrame.bottom - expandoFrame.top : expandoFrame.bottom);
630 	fExpandoMenuBar->ResizeTo(expandoFrame.Width(), expandoFrame.Height());
631 	fExpandoMenuBar->MoveTo(0, 0);
632 	fExpandoMenuBar->BuildItems();
633 	fExpandoMenuBar->SizeWindow(0);
634 }
635 
636 
637 void
638 TBarView::GetPreferredWindowSize(BRect screenFrame, float* width,
639 	float* height)
640 {
641 	float windowHeight = 0;
642 	float windowWidth = 0;
643 	bool setToHiddenSize = fBarApp->Settings()->autoHide && IsHidden()
644 		&& !fDragRegion->IsDragging();
645 
646 	if (setToHiddenSize) {
647 		windowHeight = kHiddenDimension;
648 
649 		if (fState == kExpandoState && !fVertical) {
650 			// top or bottom, full
651 			windowWidth = screenFrame.Width();
652 		} else
653 			windowWidth = kHiddenDimension;
654 	} else if (fVertical) {
655 		if (fState == kFullState) {
656 			// full state has minimum screen window height
657 			windowHeight = std::max(screenFrame.bottom, windowHeight);
658 		} else {
659 			// mini or expando
660 			if (fTrayLocation != 0)
661 				windowHeight = fDragRegion->Frame().bottom;
662 			else
663 				windowHeight = fBarMenuBar->Frame().bottom;
664 
665 			if (fState == kExpandoState && fExpandoMenuBar != NULL) {
666 				// top left or right
667 				windowHeight += fExpandoMenuBar->Bounds().Height();
668 					// use Height() here, not bottom so view can be scrolled
669 			}
670 		}
671 
672 		windowWidth = fBarApp->Settings()->width;
673 	} else {
674 		// horizontal
675 		if (fState == kMiniState) {
676 			// four corners horizontal
677 			windowHeight = fBarMenuBar->Frame().Height();
678 			windowWidth = fDragRegion->Frame().Width()
679 				+ fBarMenuBar->Frame().Width() + 1;
680 		} else {
681 			// horizontal top or bottom
682 			windowHeight = std::max(TeamMenuItemHeight(),
683 				kGutter + fReplicantTray->MaxReplicantHeight() + kGutter);
684 			windowWidth = screenFrame.Width();
685 		}
686 	}
687 
688 	*width = windowWidth;
689 	*height = windowHeight;
690 }
691 
692 
693 void
694 TBarView::SizeWindow(BRect screenFrame)
695 {
696 	float windowWidth;
697 	float windowHeight;
698 	GetPreferredWindowSize(screenFrame, &windowWidth, &windowHeight);
699 	Window()->ResizeTo(windowWidth, windowHeight);
700 	ResizeTo(windowWidth, windowHeight);
701 }
702 
703 
704 void
705 TBarView::PositionWindow(BRect screenFrame)
706 {
707 	float windowWidth;
708 	float windowHeight;
709 	GetPreferredWindowSize(screenFrame, &windowWidth, &windowHeight);
710 
711 	BPoint moveLoc(0, 0);
712 	// right, expanded, mini, or full
713 	if (!fLeft && (fVertical || fState == kMiniState))
714 		moveLoc.x = screenFrame.right - windowWidth;
715 
716 	// bottom, full
717 	if (!fTop)
718 		moveLoc.y = screenFrame.bottom - windowHeight;
719 
720 	Window()->MoveTo(moveLoc);
721 }
722 
723 
724 void
725 TBarView::CheckForScrolling()
726 {
727 	if (fInlineScrollView == NULL && fExpandoMenuBar == NULL)
728 		return;
729 
730 	if (fExpandoMenuBar->CheckForSizeOverrun())
731 		fInlineScrollView->AttachScrollers();
732 	else
733 		fInlineScrollView->DetachScrollers();
734 }
735 
736 
737 void
738 TBarView::SaveSettings()
739 {
740 	desk_settings* settings = fBarApp->Settings();
741 
742 	settings->vertical = fVertical;
743 	settings->left = fLeft;
744 	settings->top = fTop;
745 	settings->state = fState;
746 
747 	fReplicantTray->SaveTimeSettings();
748 }
749 
750 
751 void
752 TBarView::UpdatePlacement()
753 {
754 	ChangeState(fState, fVertical, fLeft, fTop);
755 }
756 
757 
758 void
759 TBarView::ChangeState(int32 state, bool vertical, bool left, bool top,
760 	bool async)
761 {
762 	BMessage message(kUpdateOrientation);
763 	message.AddInt32("state", state);
764 	message.AddBool("vertical", vertical);
765 	message.AddBool("left", left);
766 	message.AddBool("top", top);
767 
768 	if (async)
769 		BMessenger(this).SendMessage(&message);
770 	else
771 		_ChangeState(&message);
772 }
773 
774 
775 void
776 TBarView::_ChangeState(BMessage* message)
777 {
778 	int32 state = message->FindInt32("state");
779 	bool vertical = message->FindBool("vertical");
780 	bool left = message->FindBool("left");
781 	bool top = message->FindBool("top");
782 
783 	bool vertSwap = (fVertical != vertical);
784 	bool leftSwap = (fLeft != left);
785 	bool stateChanged = (fState != state);
786 
787 	fState = state;
788 	fVertical = vertical;
789 	fLeft = left;
790 	fTop = top;
791 
792 	if (stateChanged || vertSwap) {
793 		be_app->PostMessage(kStateChanged);
794 			// Send a message to the preferences window to let it know to
795 			// enable or disable preference items.
796 
797 		TBarWindow* barWindow = dynamic_cast<TBarWindow*>(Window());
798 		if (barWindow != NULL)
799 			barWindow->SetSizeLimits();
800 
801 		if (vertSwap && fExpandoMenuBar != NULL) {
802 			if (fVertical) {
803 				fInlineScrollView->SetOrientation(B_VERTICAL);
804 				fExpandoMenuBar->SetMenuLayout(B_ITEMS_IN_COLUMN);
805 				fExpandoMenuBar->StartMonitoringWindows();
806 			} else {
807 				fInlineScrollView->SetOrientation(B_HORIZONTAL);
808 				fExpandoMenuBar->SetMenuLayout(B_ITEMS_IN_ROW);
809 				fExpandoMenuBar->StopMonitoringWindows();
810 			}
811 		}
812 	}
813 
814 	PlaceDeskbarMenu();
815 	PlaceTray(vertSwap, leftSwap);
816 	PlaceApplicationBar();
817 }
818 
819 
820 void
821 TBarView::RaiseDeskbar(bool raise)
822 {
823 	fIsRaised = raise;
824 
825 	// raise or lower Deskbar without changing the active window
826 	if (raise) {
827 		Window()->SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
828 		Window()->SetFeel(B_NORMAL_WINDOW_FEEL);
829 	} else
830 		Window()->SendBehind(Window());
831 }
832 
833 
834 void
835 TBarView::HideDeskbar(bool hide)
836 {
837 	BRect screenFrame = (BScreen(Window())).Frame();
838 
839 	if (hide) {
840 		Hide();
841 		if (fBarWindow != NULL)
842 			fBarWindow->SetSizeLimits();
843 
844 		PositionWindow(screenFrame);
845 		SizeWindow(screenFrame);
846 	} else {
847 		Show();
848 		if (fBarWindow != NULL)
849 			fBarWindow->SetSizeLimits();
850 
851 		SizeWindow(screenFrame);
852 		PositionWindow(screenFrame);
853 	}
854 }
855 
856 
857 //	#pragma mark - Drag and Drop
858 
859 
860 void
861 TBarView::CacheDragData(const BMessage* incoming)
862 {
863 	if (!incoming)
864 		return;
865 
866 	if (Dragging() && SpringLoadedFolderCompareMessages(incoming, fDragMessage))
867 		return;
868 
869 	// disposes then fills cached drag message and
870 	// mimetypes list
871 	SpringLoadedFolderCacheDragData(incoming, &fDragMessage, &fCachedTypesList);
872 }
873 
874 
875 static void
876 init_tracking_hook(BMenuItem* item,
877 	bool (*hookFunction)(BMenu*, void*), void* state)
878 {
879 	if (!item)
880 		return;
881 
882 	BMenu* windowMenu = item->Submenu();
883 	if (windowMenu) {
884 		// have a menu, set the tracking hook
885 		windowMenu->SetTrackingHook(hookFunction, state);
886 	}
887 }
888 
889 
890 status_t
891 TBarView::DragStart()
892 {
893 	if (!Dragging())
894 		return B_OK;
895 
896 	BPoint loc;
897 	uint32 buttons;
898 	GetMouse(&loc, &buttons);
899 
900 	if (fExpandoMenuBar != NULL && fExpandoMenuBar->Frame().Contains(loc)) {
901 		ConvertToScreen(&loc);
902 		BPoint expandoLocation = fExpandoMenuBar->ConvertFromScreen(loc);
903 		TTeamMenuItem* item = fExpandoMenuBar->TeamItemAtPoint(expandoLocation);
904 
905 		if (fLastDragItem)
906 			init_tracking_hook(fLastDragItem, NULL, NULL);
907 
908 		if (item != NULL) {
909 			if (item == fLastDragItem)
910 				return B_OK;
911 
912 			fLastDragItem = item;
913 		}
914 	}
915 
916 	return B_OK;
917 }
918 
919 
920 bool
921 TBarView::MenuTrackingHook(BMenu* menu, void* castToThis)
922 {
923 	// return true if the menu should go away
924 	TrackingHookData* data = static_cast<TrackingHookData*>(castToThis);
925 	if (!data)
926 		return false;
927 
928 	TBarView* barView = dynamic_cast<TBarView*>(data->fTarget.Target(NULL));
929 	if (!barView || !menu->LockLooper())
930 		return false;
931 
932 	uint32 buttons;
933 	BPoint location;
934 	menu->GetMouse(&location, &buttons);
935 
936 	bool endMenu = true;
937 	BRect frame(menu->Bounds());
938 	frame.InsetBy(-kMenuTrackMargin, -kMenuTrackMargin);
939 
940 	if (frame.Contains(location)) {
941 		// if current loc is still in the menu
942 		// keep tracking
943 		endMenu = false;
944 	} else {
945 		// see if the mouse is in the team/deskbar menu item
946 		menu->ConvertToScreen(&location);
947 		if (barView->LockLooper()) {
948 			TExpandoMenuBar* expandoMenuBar = barView->ExpandoMenuBar();
949 			TBarWindow* barWindow
950 				= dynamic_cast<TBarWindow*>(barView->Window());
951 			TDeskbarMenu* deskbarMenu = barWindow->DeskbarMenu();
952 
953 			if (deskbarMenu && deskbarMenu->LockLooper()) {
954 				deskbarMenu->ConvertFromScreen(&location);
955 				if (deskbarMenu->Frame().Contains(location))
956 					endMenu = false;
957 
958 				deskbarMenu->UnlockLooper();
959 			}
960 
961 			if (endMenu && expandoMenuBar) {
962 				expandoMenuBar->ConvertFromScreen(&location);
963 				BMenuItem* item = expandoMenuBar->TeamItemAtPoint(location);
964 				if (item)
965 					endMenu = false;
966 			}
967 			barView->UnlockLooper();
968 		}
969 	}
970 
971 	menu->UnlockLooper();
972 
973 	return endMenu;
974 }
975 
976 
977 // used by WindowMenu and TeamMenu to
978 // set the tracking hook for dragging
979 TrackingHookData*
980 TBarView::GetTrackingHookData()
981 {
982 	// all tracking hook data is
983 	// preset in AttachedToWindow
984 	// data should never change
985 	return &fTrackingHookData;
986 }
987 
988 
989 void
990 TBarView::DragStop(bool full)
991 {
992 	if (!Dragging())
993 		return;
994 
995 	if (fExpandoMenuBar != NULL) {
996 		if (fLastDragItem != NULL) {
997 			init_tracking_hook(fLastDragItem, NULL, NULL);
998 			fLastDragItem = NULL;
999 		}
1000 	}
1001 
1002 	if (full) {
1003 		delete fDragMessage;
1004 		fDragMessage = NULL;
1005 
1006 		delete fCachedTypesList;
1007 		fCachedTypesList = NULL;
1008 	}
1009 }
1010 
1011 
1012 bool
1013 TBarView::AppCanHandleTypes(const char* signature)
1014 {
1015 	// used for filtering apps/teams in the ExpandoMenuBar and TeamMenu
1016 
1017 	if (modifiers() & B_CONTROL_KEY) {
1018 		// control key forces acceptance, just like drag&drop on icons
1019 		return true;
1020 	}
1021 
1022 	if (!signature || strlen(signature) == 0
1023 		|| !fCachedTypesList || fCachedTypesList->CountItems() == 0)
1024 		return false;
1025 
1026 	if (strcasecmp(signature, kTrackerSignature) == 0) {
1027 		// tracker should support all types
1028 		// and should pass them on to the appropriate application
1029 		return true;
1030 	}
1031 
1032 	entry_ref hintref;
1033 	BMimeType appmime(signature);
1034 	if (appmime.GetAppHint(&hintref) != B_OK)
1035 		return false;
1036 
1037 	// an app was found, now see if it supports any of
1038 	// the refs in the message
1039 	BFile file(&hintref, O_RDONLY);
1040 	BAppFileInfo fileinfo(&file);
1041 
1042 	// scan the cached mimetype list and see if this app
1043 	// supports anything in the list
1044 	// only one item needs to match in the list of refs
1045 
1046 	int32 count = fCachedTypesList->CountItems();
1047 	for (int32 i = 0 ; i < count ; i++) {
1048 		if (fileinfo.IsSupportedType(fCachedTypesList->ItemAt(i)->String()))
1049 			return true;
1050 	}
1051 
1052 	return false;
1053 }
1054 
1055 
1056 void
1057 TBarView::SetDragOverride(bool on)
1058 {
1059 	fRefsRcvdOnly = on;
1060 }
1061 
1062 
1063 bool
1064 TBarView::DragOverride()
1065 {
1066 	return fRefsRcvdOnly;
1067 }
1068 
1069 
1070 status_t
1071 TBarView::SendDragMessage(const char* signature, entry_ref* ref)
1072 {
1073 	status_t err = B_ERROR;
1074 	if (fDragMessage != NULL) {
1075 		if (fRefsRcvdOnly) {
1076 			// current message sent to apps is only B_REFS_RECEIVED
1077 			fDragMessage->what = B_REFS_RECEIVED;
1078 		}
1079 
1080 		BRoster roster;
1081 		if (signature != NULL && *signature != '\0'
1082 			&& roster.IsRunning(signature)) {
1083 			BMessenger messenger(signature);
1084 			// drag message is still owned by DB, copy is sent
1085 			// can toss it after send
1086 			err = messenger.SendMessage(fDragMessage);
1087 		} else if (ref != NULL) {
1088 			FSLaunchItem((const entry_ref*)ref, (const BMessage*)fDragMessage,
1089 				true, true);
1090 		} else if (signature != NULL && *signature != '\0')
1091 			roster.Launch(signature, fDragMessage);
1092 	}
1093 
1094 	return err;
1095 }
1096 
1097 
1098 bool
1099 TBarView::InvokeItem(const char* signature)
1100 {
1101 	// sent from TeamMenuItem
1102 	if (Dragging() && AppCanHandleTypes(signature)) {
1103 		SendDragMessage(signature);
1104 		// invoking okay to toss memory
1105 		DragStop(true);
1106 		return true;
1107 	}
1108 
1109 	return false;
1110 }
1111 
1112 
1113 void
1114 TBarView::HandleDeskbarMenu(BMessage* messagewithdestination)
1115 {
1116 	if (!Dragging())
1117 		return;
1118 
1119 	// in mini-mode
1120 	if (fVertical && fState != kExpandoState) {
1121 		// if drop is in the team menu, bail
1122 		if (fBarMenuBar->CountItems() >= 2) {
1123 			uint32 buttons;
1124 			BPoint location;
1125 			GetMouse(&location, &buttons);
1126 			if (fBarMenuBar->ItemAt(1)->Frame().Contains(location))
1127 				return;
1128 		}
1129 	}
1130 
1131 	if (messagewithdestination) {
1132 		entry_ref ref;
1133 		if (messagewithdestination->FindRef("refs", &ref) == B_OK) {
1134 			BEntry entry(&ref, true);
1135 			if (entry.IsDirectory()) {
1136 				// if the ref received (should only be 1) is a directory
1137 				// then add the drag refs to the directory
1138 				AddRefsToDeskbarMenu(DragMessage(), &ref);
1139 			} else
1140 				SendDragMessage(NULL, &ref);
1141 		}
1142 	} else {
1143 		// adds drag refs to top level in deskbar menu
1144 		AddRefsToDeskbarMenu(DragMessage(), NULL);
1145 	}
1146 
1147 	// clean up drag message and types list
1148 	DragStop(true);
1149 }
1150 
1151 
1152 //	#pragma mark - Add-ons
1153 
1154 
1155 // shelf is ignored for now,
1156 // it exists in anticipation of having other 'shelves' for
1157 // storing items
1158 
1159 status_t
1160 TBarView::ItemInfo(int32 id, const char** name, DeskbarShelf* shelf)
1161 {
1162 	*shelf = B_DESKBAR_TRAY;
1163 	return fReplicantTray->ItemInfo(id, name);
1164 }
1165 
1166 
1167 status_t
1168 TBarView::ItemInfo(const char* name, int32* id, DeskbarShelf* shelf)
1169 {
1170 	*shelf = B_DESKBAR_TRAY;
1171 	return fReplicantTray->ItemInfo(name, id);
1172 }
1173 
1174 
1175 bool
1176 TBarView::ItemExists(int32 id, DeskbarShelf)
1177 {
1178 	return fReplicantTray->IconExists(id);
1179 }
1180 
1181 
1182 bool
1183 TBarView::ItemExists(const char* name, DeskbarShelf)
1184 {
1185 	return fReplicantTray->IconExists(name);
1186 }
1187 
1188 
1189 int32
1190 TBarView::CountItems(DeskbarShelf)
1191 {
1192 	return fReplicantTray->ReplicantCount();
1193 }
1194 
1195 
1196 BSize
1197 TBarView::MaxItemSize(DeskbarShelf shelf)
1198 {
1199 	return BSize(fReplicantTray->MaxReplicantWidth(),
1200 		fReplicantTray->MaxReplicantHeight());
1201 }
1202 
1203 
1204 status_t
1205 TBarView::AddItem(BMessage* item, DeskbarShelf, int32* id)
1206 {
1207 	return fReplicantTray->AddIcon(item, id);
1208 }
1209 
1210 
1211 status_t
1212 TBarView::AddItem(BEntry* entry, DeskbarShelf, int32* id)
1213 {
1214 	return fReplicantTray->LoadAddOn(entry, id);
1215 }
1216 
1217 
1218 void
1219 TBarView::RemoveItem(int32 id)
1220 {
1221 	fReplicantTray->RemoveIcon(id);
1222 }
1223 
1224 
1225 void
1226 TBarView::RemoveItem(const char* name, DeskbarShelf)
1227 {
1228 	fReplicantTray->RemoveIcon(name);
1229 }
1230 
1231 
1232 BRect
1233 TBarView::OffsetIconFrame(BRect rect) const
1234 {
1235 	BRect frame(Frame());
1236 
1237 	frame.left += fDragRegion->Frame().left + fReplicantTray->Frame().left
1238 		+ rect.left;
1239 	frame.top += fDragRegion->Frame().top + fReplicantTray->Frame().top
1240 		+ rect.top;
1241 
1242 	frame.right = frame.left + rect.Width();
1243 	frame.bottom = frame.top + rect.Height();
1244 
1245 	return frame;
1246 }
1247 
1248 
1249 BRect
1250 TBarView::IconFrame(int32 id) const
1251 {
1252 	return OffsetIconFrame(fReplicantTray->IconFrame(id));
1253 }
1254 
1255 
1256 BRect
1257 TBarView::IconFrame(const char* name) const
1258 {
1259 	return OffsetIconFrame(fReplicantTray->IconFrame(name));
1260 }
1261 
1262 
1263 float
1264 TBarView::TeamMenuItemHeight() const
1265 {
1266 	const int32 iconSize = fBarApp->IconSize();
1267 	float iconSizePadded = iconSize +
1268 		ceilf(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 2);
1269 
1270 	font_height fontHeight;
1271 	if (fExpandoMenuBar != NULL)
1272 		fExpandoMenuBar->GetFontHeight(&fontHeight);
1273 	else
1274 		GetFontHeight(&fontHeight);
1275 
1276 	float labelHeight = fontHeight.ascent + fontHeight.descent;
1277 	labelHeight = labelHeight < kMinTeamItemHeight ? kMinTeamItemHeight
1278 		: ceilf(labelHeight * 1.1f);
1279 
1280 	bool hideLabels = static_cast<TBarApp*>(be_app)->Settings()->hideLabels;
1281 	if (hideLabels && iconSize > B_MINI_ICON) {
1282 		// height is determined based solely on icon size
1283 		return iconSizePadded;
1284 	} else if (!fVertical || (fVertical && iconSize <= B_LARGE_ICON)) {
1285 		// horizontal or vertical with label on same row as icon:
1286 		// height based on icon size or font size, whichever is bigger
1287 		return std::max(iconSizePadded, labelHeight);
1288 	} else if (fVertical && iconSize > B_LARGE_ICON) {
1289 		// vertical with label below icon: height based on icon and label
1290 		return ceilf(iconSizePadded + labelHeight);
1291 	} else {
1292 		// height is determined based solely on label height
1293 		return labelHeight;
1294 	}
1295 }
1296