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