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