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