xref: /haiku/src/apps/deskbar/BarView.cpp (revision 04171cfc5c10c98b9ba3c7233a271f6165cdd36f)
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 		width = static_cast<TBarApp*>(be_app)->Settings()->width;
469 		height = fTabHeight;
470 	} else {
471 		// horizontal
472 		if (fState == kMiniState) {
473 			width = gMinimumWindowWidth;
474 			height = std::max(fTabHeight,
475 				kGutter + fReplicantTray->MaxReplicantHeight() + kGutter);
476 		} else {
477 			width = gMinimumWindowWidth / 2 + kIconPadding;
478 			height = std::max(TeamMenuItemHeight(),
479 				kGutter + fReplicantTray->MaxReplicantHeight() + kGutter);
480 		}
481 	}
482 	menuFrame.bottom = menuFrame.top + height;
483 
484 	if (fBarMenuBar == NULL) {
485 		// create the Be menu
486 		fBarMenuBar = new TBarMenuBar(menuFrame, "BarMenuBar", this);
487 		AddChild(fBarMenuBar);
488 	} else
489 		fBarMenuBar->SmartResize(-1, -1);
490 
491 	if (fState == kMiniState) {
492 		// vertical or horizontal mini
493 		fBarMenuBar->RemoveSeperatorItem();
494 		fBarMenuBar->AddTeamMenu();
495 	} else if (fVertical) {
496 		fBarMenuBar->RemoveSeperatorItem();
497 		fBarMenuBar->RemoveTeamMenu();
498 	} else {
499 		fBarMenuBar->RemoveTeamMenu();
500 		fBarMenuBar->AddSeparatorItem();
501 	}
502 
503 	fBarMenuBar->SmartResize(width, height);
504 	fBarMenuBar->MoveTo(B_ORIGIN);
505 }
506 
507 
508 void
509 TBarView::PlaceTray(bool vertSwap, bool leftSwap)
510 {
511 	BPoint statusLoc;
512 	if (fTrayLocation == 0) {
513 		// no replicant tray mode, not used
514 		if (!fReplicantTray->IsHidden())
515 			fReplicantTray->Hide();
516 		return;
517 	} else if (fReplicantTray->IsHidden())
518 		fReplicantTray->Show();
519 
520 	fReplicantTray->RealignReplicants();
521 	fDragRegion->ResizeToPreferred();
522 		// also resizes replicant tray
523 
524 	if (fVertical) {
525 		if (fResizeControl->IsHidden())
526 			fResizeControl->Show();
527 
528 		if (fLeft) {
529 			// move replicant tray past dragger width on left
530 			// also down 1px so it won't cover the border
531 			fReplicantTray->MoveTo(kDragWidth + kGutter, kGutter);
532 
533 			// shrink width by same amount
534 			fReplicantTray->ResizeBy(-(kDragWidth + kGutter), 0);
535 		} else {
536 			// move replicant tray down 1px so it won't cover the border
537 			fReplicantTray->MoveTo(0, kGutter);
538 		}
539 
540 		statusLoc.x = 0;
541 		statusLoc.y = fBarMenuBar->Frame().bottom + 1;
542 	} else {
543 		// horizontal
544 		if (fState == kMiniState) {
545 			// horizontal mini
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 		} else {
554 			// move tray right and down to not cover border, resize by same
555 			fReplicantTray->MoveTo(2, 0);
556 			fReplicantTray->ResizeBy(-2, 0);
557 			BRect screenFrame = (BScreen(Window())).Frame();
558 			statusLoc.x = screenFrame.right - fDragRegion->Bounds().Width();
559 			statusLoc.y = 0;
560 		}
561 	}
562 
563 	fDragRegion->MoveTo(statusLoc);
564 
565 	// make room for top and bottom border
566 	fResizeControl->ResizeTo(kDragWidth, fDragRegion->Bounds().Height() - 2);
567 
568 	if (fVertical) {
569 		// move resize control into place based on width setting
570 		fResizeControl->MoveTo(
571 			fLeft ? fBarApp->Settings()->width - kDragWidth : 0, 1);
572 		if (fResizeControl->IsHidden())
573 			fResizeControl->Show();
574 	} else {
575 		// hide resize control
576 		if (!fResizeControl->IsHidden())
577 			fResizeControl->Hide();
578 	}
579 
580 	fDragRegion->Invalidate();
581 }
582 
583 
584 void
585 TBarView::PlaceApplicationBar()
586 {
587 	BRect screenFrame = (BScreen(Window())).Frame();
588 	if (fState == kMiniState) {
589 		if (!fInlineScrollView->IsHidden())
590 			fInlineScrollView->Hide();
591 
592 		SizeWindow(screenFrame);
593 		PositionWindow(screenFrame);
594 		Window()->UpdateIfNeeded();
595 		if (!fVertical) {
596 			// move the menu bar into place after the window has been resized
597 			// based on replicant tray
598 			fBarMenuBar->MoveTo(fLeft ? 0 : fDragRegion->Bounds().right + 1,
599 				0);
600 		}
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 		if (fState == kFullState) {
657 			// full state has minimum screen window height
658 			windowHeight = std::max(screenFrame.bottom, windowHeight);
659 		} else {
660 			// mini or expando
661 			if (fTrayLocation != 0)
662 				windowHeight = fDragRegion->Frame().bottom;
663 			else
664 				windowHeight = fBarMenuBar->Frame().bottom;
665 
666 			if (fState == kExpandoState && fExpandoMenuBar != NULL) {
667 				// top left or right
668 				windowHeight += fExpandoMenuBar->Bounds().Height();
669 					// use Height() here, not bottom so view can be scrolled
670 			}
671 		}
672 
673 		windowWidth = fBarApp->Settings()->width;
674 	} else {
675 		// horizontal
676 		if (fState == kMiniState) {
677 			// four corners horizontal
678 			windowHeight = fBarMenuBar->Frame().Height();
679 			windowWidth = fDragRegion->Frame().Width()
680 				+ fBarMenuBar->Frame().Width() + 1;
681 		} else {
682 			// horizontal top or bottom
683 			windowHeight = std::max(TeamMenuItemHeight(),
684 				kGutter + fReplicantTray->MaxReplicantHeight() + kGutter);
685 			windowWidth = screenFrame.Width();
686 		}
687 	}
688 
689 	*width = windowWidth;
690 	*height = windowHeight;
691 }
692 
693 
694 void
695 TBarView::SizeWindow(BRect screenFrame)
696 {
697 	float windowWidth;
698 	float windowHeight;
699 	GetPreferredWindowSize(screenFrame, &windowWidth, &windowHeight);
700 	Window()->ResizeTo(windowWidth, windowHeight);
701 	ResizeTo(windowWidth, windowHeight);
702 }
703 
704 
705 void
706 TBarView::PositionWindow(BRect screenFrame)
707 {
708 	float windowWidth;
709 	float windowHeight;
710 	GetPreferredWindowSize(screenFrame, &windowWidth, &windowHeight);
711 
712 	BPoint moveLoc(0, 0);
713 	// right, expanded, mini, or full
714 	if (!fLeft && (fVertical || fState == kMiniState))
715 		moveLoc.x = screenFrame.right - windowWidth;
716 
717 	// bottom, full
718 	if (!fTop)
719 		moveLoc.y = screenFrame.bottom - windowHeight;
720 
721 	Window()->MoveTo(moveLoc);
722 }
723 
724 
725 void
726 TBarView::CheckForScrolling()
727 {
728 	if (fInlineScrollView == NULL && fExpandoMenuBar == NULL)
729 		return;
730 
731 	if (fExpandoMenuBar->CheckForSizeOverrun())
732 		fInlineScrollView->AttachScrollers();
733 	else
734 		fInlineScrollView->DetachScrollers();
735 }
736 
737 
738 void
739 TBarView::SaveSettings()
740 {
741 	desk_settings* settings = fBarApp->Settings();
742 
743 	settings->vertical = fVertical;
744 	settings->left = fLeft;
745 	settings->top = fTop;
746 	settings->state = fState;
747 
748 	fReplicantTray->SaveTimeSettings();
749 }
750 
751 
752 void
753 TBarView::UpdatePlacement()
754 {
755 	ChangeState(fState, fVertical, fLeft, fTop);
756 }
757 
758 
759 void
760 TBarView::ChangeState(int32 state, bool vertical, bool left, bool top,
761 	bool async)
762 {
763 	BMessage message(kUpdateOrientation);
764 	message.AddInt32("state", state);
765 	message.AddBool("vertical", vertical);
766 	message.AddBool("left", left);
767 	message.AddBool("top", top);
768 
769 	if (async)
770 		BMessenger(this).SendMessage(&message);
771 	else
772 		_ChangeState(&message);
773 }
774 
775 
776 void
777 TBarView::_ChangeState(BMessage* message)
778 {
779 	int32 state = message->FindInt32("state");
780 	bool vertical = message->FindBool("vertical");
781 	bool left = message->FindBool("left");
782 	bool top = message->FindBool("top");
783 
784 	bool vertSwap = (fVertical != vertical);
785 	bool leftSwap = (fLeft != left);
786 	bool stateChanged = (fState != state);
787 
788 	fState = state;
789 	fVertical = vertical;
790 	fLeft = left;
791 	fTop = top;
792 
793 	if (stateChanged || vertSwap) {
794 		be_app->PostMessage(kStateChanged);
795 			// Send a message to the preferences window to let it know to
796 			// enable or disable preference items.
797 
798 		TBarWindow* barWindow = dynamic_cast<TBarWindow*>(Window());
799 		if (barWindow != NULL)
800 			barWindow->SetSizeLimits();
801 
802 		if (vertSwap && fExpandoMenuBar != NULL) {
803 			if (fVertical) {
804 				fInlineScrollView->SetOrientation(B_VERTICAL);
805 				fExpandoMenuBar->SetMenuLayout(B_ITEMS_IN_COLUMN);
806 				fExpandoMenuBar->StartMonitoringWindows();
807 			} else {
808 				fInlineScrollView->SetOrientation(B_HORIZONTAL);
809 				fExpandoMenuBar->SetMenuLayout(B_ITEMS_IN_ROW);
810 				fExpandoMenuBar->StopMonitoringWindows();
811 			}
812 		}
813 	}
814 
815 	PlaceDeskbarMenu();
816 	PlaceTray(vertSwap, leftSwap);
817 	PlaceApplicationBar();
818 }
819 
820 
821 void
822 TBarView::RaiseDeskbar(bool raise)
823 {
824 	fIsRaised = raise;
825 
826 	// raise or lower Deskbar without changing the active window
827 	if (raise) {
828 		Window()->SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
829 		Window()->SetFeel(B_NORMAL_WINDOW_FEEL);
830 	} else
831 		Window()->SendBehind(Window());
832 }
833 
834 
835 void
836 TBarView::HideDeskbar(bool hide)
837 {
838 	BRect screenFrame = (BScreen(Window())).Frame();
839 
840 	if (hide) {
841 		Hide();
842 		if (fBarWindow != NULL)
843 			fBarWindow->SetSizeLimits();
844 
845 		PositionWindow(screenFrame);
846 		SizeWindow(screenFrame);
847 	} else {
848 		Show();
849 		if (fBarWindow != NULL)
850 			fBarWindow->SetSizeLimits();
851 
852 		SizeWindow(screenFrame);
853 		PositionWindow(screenFrame);
854 	}
855 }
856 
857 
858 //	#pragma mark - Drag and Drop
859 
860 
861 void
862 TBarView::CacheDragData(const BMessage* incoming)
863 {
864 	if (!incoming)
865 		return;
866 
867 	if (Dragging() && SpringLoadedFolderCompareMessages(incoming, fDragMessage))
868 		return;
869 
870 	// disposes then fills cached drag message and
871 	// mimetypes list
872 	SpringLoadedFolderCacheDragData(incoming, &fDragMessage, &fCachedTypesList);
873 }
874 
875 
876 static void
877 init_tracking_hook(BMenuItem* item,
878 	bool (*hookFunction)(BMenu*, void*), void* state)
879 {
880 	if (!item)
881 		return;
882 
883 	BMenu* windowMenu = item->Submenu();
884 	if (windowMenu) {
885 		// have a menu, set the tracking hook
886 		windowMenu->SetTrackingHook(hookFunction, state);
887 	}
888 }
889 
890 
891 status_t
892 TBarView::DragStart()
893 {
894 	if (!Dragging())
895 		return B_OK;
896 
897 	BPoint loc;
898 	uint32 buttons;
899 	GetMouse(&loc, &buttons);
900 
901 	if (fExpandoMenuBar != NULL && fExpandoMenuBar->Frame().Contains(loc)) {
902 		ConvertToScreen(&loc);
903 		BPoint expandoLocation = fExpandoMenuBar->ConvertFromScreen(loc);
904 		TTeamMenuItem* item = fExpandoMenuBar->TeamItemAtPoint(expandoLocation);
905 
906 		if (fLastDragItem)
907 			init_tracking_hook(fLastDragItem, NULL, NULL);
908 
909 		if (item != NULL) {
910 			if (item == fLastDragItem)
911 				return B_OK;
912 
913 			fLastDragItem = item;
914 		}
915 	}
916 
917 	return B_OK;
918 }
919 
920 
921 bool
922 TBarView::MenuTrackingHook(BMenu* menu, void* castToThis)
923 {
924 	// return true if the menu should go away
925 	TrackingHookData* data = static_cast<TrackingHookData*>(castToThis);
926 	if (!data)
927 		return false;
928 
929 	TBarView* barView = dynamic_cast<TBarView*>(data->fTarget.Target(NULL));
930 	if (!barView || !menu->LockLooper())
931 		return false;
932 
933 	uint32 buttons;
934 	BPoint location;
935 	menu->GetMouse(&location, &buttons);
936 
937 	bool endMenu = true;
938 	BRect frame(menu->Bounds());
939 	frame.InsetBy(-kMenuTrackMargin, -kMenuTrackMargin);
940 
941 	if (frame.Contains(location)) {
942 		// if current loc is still in the menu
943 		// keep tracking
944 		endMenu = false;
945 	} else {
946 		// see if the mouse is in the team/deskbar menu item
947 		menu->ConvertToScreen(&location);
948 		if (barView->LockLooper()) {
949 			TExpandoMenuBar* expandoMenuBar = barView->ExpandoMenuBar();
950 			TBarWindow* barWindow
951 				= dynamic_cast<TBarWindow*>(barView->Window());
952 			TDeskbarMenu* deskbarMenu = barWindow->DeskbarMenu();
953 
954 			if (deskbarMenu && deskbarMenu->LockLooper()) {
955 				deskbarMenu->ConvertFromScreen(&location);
956 				if (deskbarMenu->Frame().Contains(location))
957 					endMenu = false;
958 
959 				deskbarMenu->UnlockLooper();
960 			}
961 
962 			if (endMenu && expandoMenuBar) {
963 				expandoMenuBar->ConvertFromScreen(&location);
964 				BMenuItem* item = expandoMenuBar->TeamItemAtPoint(location);
965 				if (item)
966 					endMenu = false;
967 			}
968 			barView->UnlockLooper();
969 		}
970 	}
971 
972 	menu->UnlockLooper();
973 
974 	return endMenu;
975 }
976 
977 
978 // used by WindowMenu and TeamMenu to
979 // set the tracking hook for dragging
980 TrackingHookData*
981 TBarView::GetTrackingHookData()
982 {
983 	// all tracking hook data is
984 	// preset in AttachedToWindow
985 	// data should never change
986 	return &fTrackingHookData;
987 }
988 
989 
990 void
991 TBarView::DragStop(bool full)
992 {
993 	if (!Dragging())
994 		return;
995 
996 	if (fExpandoMenuBar != NULL) {
997 		if (fLastDragItem != NULL) {
998 			init_tracking_hook(fLastDragItem, NULL, NULL);
999 			fLastDragItem = NULL;
1000 		}
1001 	}
1002 
1003 	if (full) {
1004 		delete fDragMessage;
1005 		fDragMessage = NULL;
1006 
1007 		delete fCachedTypesList;
1008 		fCachedTypesList = NULL;
1009 	}
1010 }
1011 
1012 
1013 bool
1014 TBarView::AppCanHandleTypes(const char* signature)
1015 {
1016 	// used for filtering apps/teams in the ExpandoMenuBar and TeamMenu
1017 
1018 	if (modifiers() & B_CONTROL_KEY) {
1019 		// control key forces acceptance, just like drag&drop on icons
1020 		return true;
1021 	}
1022 
1023 	if (!signature || strlen(signature) == 0
1024 		|| !fCachedTypesList || fCachedTypesList->CountItems() == 0)
1025 		return false;
1026 
1027 	if (strcasecmp(signature, kTrackerSignature) == 0) {
1028 		// tracker should support all types
1029 		// and should pass them on to the appropriate application
1030 		return true;
1031 	}
1032 
1033 	entry_ref hintref;
1034 	BMimeType appmime(signature);
1035 	if (appmime.GetAppHint(&hintref) != B_OK)
1036 		return false;
1037 
1038 	// an app was found, now see if it supports any of
1039 	// the refs in the message
1040 	BFile file(&hintref, O_RDONLY);
1041 	BAppFileInfo fileinfo(&file);
1042 
1043 	// scan the cached mimetype list and see if this app
1044 	// supports anything in the list
1045 	// only one item needs to match in the list of refs
1046 
1047 	int32 count = fCachedTypesList->CountItems();
1048 	for (int32 i = 0 ; i < count ; i++) {
1049 		if (fileinfo.IsSupportedType(fCachedTypesList->ItemAt(i)->String()))
1050 			return true;
1051 	}
1052 
1053 	return false;
1054 }
1055 
1056 
1057 void
1058 TBarView::SetDragOverride(bool on)
1059 {
1060 	fRefsRcvdOnly = on;
1061 }
1062 
1063 
1064 bool
1065 TBarView::DragOverride()
1066 {
1067 	return fRefsRcvdOnly;
1068 }
1069 
1070 
1071 status_t
1072 TBarView::SendDragMessage(const char* signature, entry_ref* ref)
1073 {
1074 	status_t err = B_ERROR;
1075 	if (fDragMessage != NULL) {
1076 		if (fRefsRcvdOnly) {
1077 			// current message sent to apps is only B_REFS_RECEIVED
1078 			fDragMessage->what = B_REFS_RECEIVED;
1079 		}
1080 
1081 		BRoster roster;
1082 		if (signature != NULL && *signature != '\0'
1083 			&& roster.IsRunning(signature)) {
1084 			BMessenger messenger(signature);
1085 			// drag message is still owned by DB, copy is sent
1086 			// can toss it after send
1087 			err = messenger.SendMessage(fDragMessage);
1088 		} else if (ref != NULL) {
1089 			FSLaunchItem((const entry_ref*)ref, (const BMessage*)fDragMessage,
1090 				true, true);
1091 		} else if (signature != NULL && *signature != '\0')
1092 			roster.Launch(signature, fDragMessage);
1093 	}
1094 
1095 	return err;
1096 }
1097 
1098 
1099 bool
1100 TBarView::InvokeItem(const char* signature)
1101 {
1102 	// sent from TeamMenuItem
1103 	if (Dragging() && AppCanHandleTypes(signature)) {
1104 		SendDragMessage(signature);
1105 		// invoking okay to toss memory
1106 		DragStop(true);
1107 		return true;
1108 	}
1109 
1110 	return false;
1111 }
1112 
1113 
1114 void
1115 TBarView::HandleDeskbarMenu(BMessage* messagewithdestination)
1116 {
1117 	if (!Dragging())
1118 		return;
1119 
1120 	// in mini-mode
1121 	if (fVertical && fState != kExpandoState) {
1122 		// if drop is in the team menu, bail
1123 		if (fBarMenuBar->CountItems() >= 2) {
1124 			uint32 buttons;
1125 			BPoint location;
1126 			GetMouse(&location, &buttons);
1127 			if (fBarMenuBar->ItemAt(1)->Frame().Contains(location))
1128 				return;
1129 		}
1130 	}
1131 
1132 	if (messagewithdestination) {
1133 		entry_ref ref;
1134 		if (messagewithdestination->FindRef("refs", &ref) == B_OK) {
1135 			BEntry entry(&ref, true);
1136 			if (entry.IsDirectory()) {
1137 				// if the ref received (should only be 1) is a directory
1138 				// then add the drag refs to the directory
1139 				AddRefsToDeskbarMenu(DragMessage(), &ref);
1140 			} else
1141 				SendDragMessage(NULL, &ref);
1142 		}
1143 	} else {
1144 		// adds drag refs to top level in deskbar menu
1145 		AddRefsToDeskbarMenu(DragMessage(), NULL);
1146 	}
1147 
1148 	// clean up drag message and types list
1149 	DragStop(true);
1150 }
1151 
1152 
1153 //	#pragma mark - Add-ons
1154 
1155 
1156 // shelf is ignored for now,
1157 // it exists in anticipation of having other 'shelves' for
1158 // storing items
1159 
1160 status_t
1161 TBarView::ItemInfo(int32 id, const char** name, DeskbarShelf* shelf)
1162 {
1163 	*shelf = B_DESKBAR_TRAY;
1164 	return fReplicantTray->ItemInfo(id, name);
1165 }
1166 
1167 
1168 status_t
1169 TBarView::ItemInfo(const char* name, int32* id, DeskbarShelf* shelf)
1170 {
1171 	*shelf = B_DESKBAR_TRAY;
1172 	return fReplicantTray->ItemInfo(name, id);
1173 }
1174 
1175 
1176 bool
1177 TBarView::ItemExists(int32 id, DeskbarShelf)
1178 {
1179 	return fReplicantTray->IconExists(id);
1180 }
1181 
1182 
1183 bool
1184 TBarView::ItemExists(const char* name, DeskbarShelf)
1185 {
1186 	return fReplicantTray->IconExists(name);
1187 }
1188 
1189 
1190 int32
1191 TBarView::CountItems(DeskbarShelf)
1192 {
1193 	return fReplicantTray->ReplicantCount();
1194 }
1195 
1196 
1197 BSize
1198 TBarView::MaxItemSize(DeskbarShelf shelf)
1199 {
1200 	return BSize(fReplicantTray->MaxReplicantWidth(),
1201 		fReplicantTray->MaxReplicantHeight());
1202 }
1203 
1204 
1205 status_t
1206 TBarView::AddItem(BMessage* item, DeskbarShelf, int32* id)
1207 {
1208 	return fReplicantTray->AddIcon(item, id);
1209 }
1210 
1211 
1212 status_t
1213 TBarView::AddItem(BEntry* entry, DeskbarShelf, int32* id)
1214 {
1215 	return fReplicantTray->LoadAddOn(entry, id);
1216 }
1217 
1218 
1219 void
1220 TBarView::RemoveItem(int32 id)
1221 {
1222 	fReplicantTray->RemoveIcon(id);
1223 }
1224 
1225 
1226 void
1227 TBarView::RemoveItem(const char* name, DeskbarShelf)
1228 {
1229 	fReplicantTray->RemoveIcon(name);
1230 }
1231 
1232 
1233 BRect
1234 TBarView::OffsetIconFrame(BRect rect) const
1235 {
1236 	BRect frame(Frame());
1237 
1238 	frame.left += fDragRegion->Frame().left + fReplicantTray->Frame().left
1239 		+ rect.left;
1240 	frame.top += fDragRegion->Frame().top + fReplicantTray->Frame().top
1241 		+ rect.top;
1242 
1243 	frame.right = frame.left + rect.Width();
1244 	frame.bottom = frame.top + rect.Height();
1245 
1246 	return frame;
1247 }
1248 
1249 
1250 BRect
1251 TBarView::IconFrame(int32 id) const
1252 {
1253 	return OffsetIconFrame(fReplicantTray->IconFrame(id));
1254 }
1255 
1256 
1257 BRect
1258 TBarView::IconFrame(const char* name) const
1259 {
1260 	return OffsetIconFrame(fReplicantTray->IconFrame(name));
1261 }
1262 
1263 
1264 float
1265 TBarView::TeamMenuItemHeight() const
1266 {
1267 	const int32 iconSize = fBarApp->IconSize();
1268 	float iconSizePadded = kVPad + iconSize + kVPad;
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