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