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