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