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 "BarWindow.h"
38
39#include <stdio.h>
40
41#include <Application.h>
42#include <AutoDeleter.h>
43#include <Catalog.h>
44#include <ControlLook.h>
45#include <Directory.h>
46#include <FindDirectory.h>
47#include <Path.h>
48#include <Debug.h>
49#include <File.h>
50#include <Locale.h>
51#include <MenuItem.h>
52#include <MessageFilter.h>
53#include <MessagePrivate.h>
54#include <Screen.h>
55
56#include <DeskbarPrivate.h>
57#include <tracker_private.h>
58
59#include "BarApp.h"
60#include "BarMenuBar.h"
61#include "BarView.h"
62#include "DeskbarUtils.h"
63#include "DeskbarMenu.h"
64#include "ExpandoMenuBar.h"
65#include "StatusView.h"
66
67
68#undef B_TRANSLATION_CONTEXT
69#define B_TRANSLATION_CONTEXT "MainWindow"
70
71
72// This is a bit of a hack to be able to call BMenuBar::StartMenuBar(), which
73// is private. Don't do this at home!
74class TStartableMenuBar : public BMenuBar {
75public:
76	TStartableMenuBar();
77	void StartMenuBar(int32 menuIndex, bool sticky = true, bool showMenu = false,
78		BRect* special_rect = NULL) { BMenuBar::StartMenuBar(menuIndex, sticky, showMenu,
79			special_rect); }
80};
81
82
83TDeskbarMenu* TBarWindow::sDeskbarMenu = NULL;
84
85
86TBarWindow::TBarWindow()
87	:
88	BWindow(BRect(-1000.0f, -1000.0f, -1000.0f, -1000.0f),
89		"Deskbar", /* no B_TRANSLATE_SYSTEM_NAME, for binary compatibility */
90		B_BORDERED_WINDOW,
91		B_WILL_ACCEPT_FIRST_CLICK | B_NOT_ZOOMABLE | B_NOT_CLOSABLE
92			| B_NOT_MINIMIZABLE | B_NOT_MOVABLE | B_NOT_V_RESIZABLE
93			| B_AVOID_FRONT | B_ASYNCHRONOUS_CONTROLS,
94		B_ALL_WORKSPACES),
95	fBarApp(static_cast<TBarApp*>(be_app)),
96	fBarView(NULL),
97	fMenusShown(0)
98{
99	desk_settings* settings = fBarApp->Settings();
100	if (settings->alwaysOnTop)
101		SetFeel(B_FLOATING_ALL_WINDOW_FEEL);
102
103	fBarView = new TBarView(Bounds(), settings->vertical, settings->left,
104		settings->top, settings->state, settings->width);
105	AddChild(fBarView);
106
107	RemoveShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY);
108	AddShortcut('F', B_COMMAND_KEY, new BMessage(kFindButton));
109
110	SetSizeLimits();
111}
112
113
114void
115TBarWindow::MenusBeginning()
116{
117	BPath path;
118	entry_ref ref;
119	BEntry entry;
120
121	if (GetDeskbarSettingsDirectory(path) == B_OK
122		&& path.Append(kDeskbarMenuEntriesFileName) == B_OK
123		&& entry.SetTo(path.Path(), true) == B_OK
124		&& entry.Exists()
125		&& entry.GetRef(&ref) == B_OK) {
126		sDeskbarMenu->SetNavDir(&ref);
127	} else if (GetDeskbarDataDirectory(path) == B_OK
128		&& path.Append(kDeskbarMenuEntriesFileName) == B_OK
129		&& entry.SetTo(path.Path(), true) == B_OK
130		&& entry.Exists()
131		&& entry.GetRef(&ref) == B_OK) {
132		sDeskbarMenu->SetNavDir(&ref);
133	} else {
134		//	this really should never happen
135		TRESPASS();
136		return;
137	}
138
139	// raise Deskbar on menu open in auto-raise mode unless always-on-top
140	desk_settings* settings = fBarApp->Settings();
141	bool alwaysOnTop = settings->alwaysOnTop;
142	bool autoRaise = settings->autoRaise;
143	if (!alwaysOnTop && autoRaise)
144		fBarView->RaiseDeskbar(true);
145
146	sDeskbarMenu->ResetTargets();
147
148	fMenusShown++;
149	BWindow::MenusBeginning();
150}
151
152
153void
154TBarWindow::MenusEnded()
155{
156	fMenusShown--;
157	BWindow::MenusEnded();
158
159	// lower Deskbar back down again on menu close in auto-raise mode
160	// unless another menu is open or always-on-top.
161	desk_settings* settings = fBarApp->Settings();
162	bool alwaysOnTop = settings->alwaysOnTop;
163	bool autoRaise = settings->autoRaise;
164	if (!alwaysOnTop && autoRaise && fMenusShown <= 0)
165		fBarView->RaiseDeskbar(false);
166
167	if (sDeskbarMenu->LockLooper()) {
168		sDeskbarMenu->ForceRebuild();
169		sDeskbarMenu->UnlockLooper();
170	}
171}
172
173
174void
175TBarWindow::MessageReceived(BMessage* message)
176{
177	switch (message->what) {
178		case kFindButton:
179		{
180			BMessenger tracker(kTrackerSignature);
181			tracker.SendMessage(message);
182			break;
183		}
184
185		case kMsgLocation:
186			GetLocation(message);
187			break;
188
189		case kMsgSetLocation:
190			SetLocation(message);
191			break;
192
193		case kMsgIsExpanded:
194			IsExpanded(message);
195			break;
196
197		case kMsgExpand:
198			Expand(message);
199			break;
200
201		case kMsgGetItemInfo:
202			ItemInfo(message);
203			break;
204
205		case kMsgHasItem:
206			ItemExists(message);
207			break;
208
209		case kMsgCountItems:
210			CountItems(message);
211			break;
212
213		case kMsgMaxItemSize:
214			MaxItemSize(message);
215			break;
216
217		case kMsgAddAddOn:
218		case kMsgAddView:
219			AddItem(message);
220			break;
221
222		case kMsgRemoveItem:
223			RemoveItem(message);
224			break;
225
226		case 'iloc':
227			GetIconFrame(message);
228			break;
229
230		default:
231			BWindow::MessageReceived(message);
232			break;
233	}
234}
235
236
237void
238TBarWindow::Minimize(bool minimize)
239{
240	// Don't allow the Deskbar to be minimized
241	if (!minimize)
242		BWindow::Minimize(false);
243}
244
245
246void
247TBarWindow::FrameResized(float width, float height)
248{
249	if (!fBarView->Vertical())
250		return BWindow::FrameResized(width, height);
251
252	bool setToHiddenSize = fBarApp->Settings()->autoHide
253		&& fBarView->IsHidden() && !fBarView->DragRegion()->IsDragging();
254	if (!setToHiddenSize) {
255		// constrain within limits
256		float newWidth;
257		if (width < gMinimumWindowWidth)
258			newWidth = gMinimumWindowWidth;
259		else if (width > gMaximumWindowWidth)
260			newWidth = gMaximumWindowWidth;
261		else
262			newWidth = width;
263
264		float oldWidth = fBarApp->Settings()->width;
265
266		// update width setting
267		fBarApp->Settings()->width = newWidth;
268
269		if (oldWidth != newWidth) {
270			fBarView->ResizeTo(width, fBarView->Bounds().Height());
271			if (fBarView->Vertical() && fBarView->ExpandoMenuBar() != NULL)
272				fBarView->ExpandoMenuBar()->SetMaxContentWidth(width);
273
274			fBarView->UpdatePlacement();
275		}
276	}
277}
278
279
280void
281TBarWindow::SaveSettings()
282{
283	fBarView->SaveSettings();
284}
285
286
287bool
288TBarWindow::QuitRequested()
289{
290	be_app->PostMessage(B_QUIT_REQUESTED);
291
292	return BWindow::QuitRequested();
293}
294
295
296void
297TBarWindow::WorkspaceActivated(int32 workspace, bool active)
298{
299	BWindow::WorkspaceActivated(workspace, active);
300
301	if (active && !(fBarView->ExpandoState() && fBarView->Vertical()))
302		fBarView->UpdatePlacement();
303	else {
304		BRect screenFrame = (BScreen(fBarView->Window())).Frame();
305		fBarView->SizeWindow(screenFrame);
306		fBarView->PositionWindow(screenFrame);
307		fBarView->Invalidate();
308	}
309}
310
311
312void
313TBarWindow::ScreenChanged(BRect size, color_space depth)
314{
315	BWindow::ScreenChanged(size, depth);
316
317	SetSizeLimits();
318
319	if (fBarView != NULL) {
320		fBarView->DragRegion()->CalculateRegions();
321		fBarView->UpdatePlacement();
322	}
323}
324
325
326void
327TBarWindow::SetDeskbarMenu(TDeskbarMenu* menu)
328{
329	sDeskbarMenu = menu;
330}
331
332
333TDeskbarMenu*
334TBarWindow::DeskbarMenu()
335{
336	return sDeskbarMenu;
337}
338
339
340void
341TBarWindow::ShowDeskbarMenu()
342{
343	TStartableMenuBar* menuBar = (TStartableMenuBar*)fBarView->BarMenuBar();
344	if (menuBar == NULL)
345		menuBar = (TStartableMenuBar*)KeyMenuBar();
346
347	if (menuBar == NULL)
348		return;
349
350	menuBar->StartMenuBar(0, true, true, NULL);
351}
352
353
354void
355TBarWindow::ShowTeamMenu()
356{
357	int32 index = 0;
358	if (fBarView->BarMenuBar() == NULL)
359		index = 2;
360
361	if (KeyMenuBar() == NULL)
362		return;
363
364	((TStartableMenuBar*)KeyMenuBar())->StartMenuBar(index, true, true, NULL);
365}
366
367
368// determines the actual location of the window
369
370deskbar_location
371TBarWindow::DeskbarLocation() const
372{
373	bool left = fBarView->Left();
374	bool top = fBarView->Top();
375
376	if (fBarView->AcrossTop())
377		return B_DESKBAR_TOP;
378
379	if (fBarView->AcrossBottom())
380		return B_DESKBAR_BOTTOM;
381
382	if (left && top)
383		return B_DESKBAR_LEFT_TOP;
384
385	if (!left && top)
386		return B_DESKBAR_RIGHT_TOP;
387
388	if (left && !top)
389		return B_DESKBAR_LEFT_BOTTOM;
390
391	return B_DESKBAR_RIGHT_BOTTOM;
392}
393
394
395void
396TBarWindow::GetLocation(BMessage* message)
397{
398	BMessage reply('rply');
399	reply.AddInt32("location", (int32)DeskbarLocation());
400	reply.AddBool("expanded", fBarView->ExpandoState());
401
402	message->SendReply(&reply);
403}
404
405
406void
407TBarWindow::SetDeskbarLocation(deskbar_location location, bool newExpandState)
408{
409	// left top and right top are the only two that
410	// currently pay attention to expand, ignore for all others
411
412	bool left = false, top = true, vertical, expand;
413
414	switch (location) {
415		case B_DESKBAR_TOP:
416			left = true;
417			top = true;
418			vertical = false;
419			expand = true;
420			break;
421
422		case B_DESKBAR_BOTTOM:
423			left = true;
424			top = false;
425			vertical = false;
426			expand = true;
427			break;
428
429		case B_DESKBAR_LEFT_TOP:
430			left = true;
431			top = true;
432			vertical = true;
433			expand = newExpandState;
434			break;
435
436		case B_DESKBAR_RIGHT_TOP:
437			left = false;
438			top = true;
439			vertical = true;
440			expand = newExpandState;
441			break;
442
443		case B_DESKBAR_LEFT_BOTTOM:
444			left = true;
445			top = false;
446			vertical = true;
447			expand = false;
448			break;
449
450		case B_DESKBAR_RIGHT_BOTTOM:
451			left = false;
452			top = false;
453			vertical = true;
454			expand = false;
455			break;
456
457		default:
458			left = true;
459			top = true;
460			vertical = false;
461			expand = true;
462			break;
463	}
464
465	fBarView->ChangeState(expand, vertical, left, top);
466}
467
468
469void
470TBarWindow::SetLocation(BMessage* message)
471{
472	deskbar_location location;
473	bool expand;
474	if (message->FindInt32("location", (int32*)&location) == B_OK
475		&& message->FindBool("expand", &expand) == B_OK)
476		SetDeskbarLocation(location, expand);
477}
478
479
480void
481TBarWindow::IsExpanded(BMessage* message)
482{
483	BMessage reply('rply');
484	reply.AddBool("expanded", fBarView->ExpandoState());
485	message->SendReply(&reply);
486}
487
488
489void
490TBarWindow::Expand(BMessage* message)
491{
492	bool expand;
493	if (message->FindBool("expand", &expand) == B_OK) {
494		bool vertical = fBarView->Vertical();
495		bool left = fBarView->Left();
496		bool top = fBarView->Top();
497		fBarView->ChangeState(expand, vertical, left, top);
498	}
499}
500
501
502void
503TBarWindow::ItemInfo(BMessage* message)
504{
505	BMessage replyMsg;
506	const char* name;
507	int32 id;
508	DeskbarShelf shelf;
509	if (message->FindInt32("id", &id) == B_OK) {
510		if (fBarView->ItemInfo(id, &name, &shelf) == B_OK) {
511			replyMsg.AddString("name", name);
512#if SHELF_AWARE
513			replyMsg.AddInt32("shelf", (int32)shelf);
514#endif
515		}
516	} else if (message->FindString("name", &name) == B_OK) {
517		if (fBarView->ItemInfo(name, &id, &shelf) == B_OK) {
518			replyMsg.AddInt32("id", id);
519#if SHELF_AWARE
520			replyMsg.AddInt32("shelf", (int32)shelf);
521#endif
522		}
523	}
524
525	message->SendReply(&replyMsg);
526}
527
528
529void
530TBarWindow::ItemExists(BMessage* message)
531{
532	BMessage replyMsg;
533	const char* name;
534	int32 id;
535	DeskbarShelf shelf;
536
537#if SHELF_AWARE
538	if (message->FindInt32("shelf", (int32*)&shelf) != B_OK)
539#endif
540		shelf = B_DESKBAR_TRAY;
541
542	bool exists = false;
543	if (message->FindInt32("id", &id) == B_OK)
544		exists = fBarView->ItemExists(id, shelf);
545	else if (message->FindString("name", &name) == B_OK)
546		exists = fBarView->ItemExists(name, shelf);
547
548	replyMsg.AddBool("exists", exists);
549	message->SendReply(&replyMsg);
550}
551
552
553void
554TBarWindow::CountItems(BMessage* message)
555{
556	DeskbarShelf shelf;
557
558#if SHELF_AWARE
559	if (message->FindInt32("shelf", (int32*)&shelf) != B_OK)
560#endif
561		shelf = B_DESKBAR_TRAY;
562
563	BMessage reply('rply');
564	reply.AddInt32("count", fBarView->CountItems(shelf));
565	message->SendReply(&reply);
566}
567
568
569void
570TBarWindow::MaxItemSize(BMessage* message)
571{
572	DeskbarShelf shelf;
573#if SHELF_AWARE
574	if (message->FindInt32("shelf", (int32*)&shelf) != B_OK)
575#endif
576		shelf = B_DESKBAR_TRAY;
577
578	BSize size = fBarView->MaxItemSize(shelf);
579
580	BMessage reply('rply');
581	reply.AddFloat("width", size.width);
582	reply.AddFloat("height", size.height);
583	message->SendReply(&reply);
584}
585
586
587void
588TBarWindow::AddItem(BMessage* message)
589{
590	DeskbarShelf shelf = B_DESKBAR_TRAY;
591	entry_ref ref;
592	int32 id = 999;
593	BMessage reply;
594	status_t err = B_ERROR;
595
596	BMessage* archivedView = new BMessage();
597	ObjectDeleter<BMessage> deleter(archivedView);
598	if (message->FindMessage("view", archivedView) == B_OK) {
599#if SHELF_AWARE
600		message->FindInt32("shelf", &shelf);
601#endif
602		err = fBarView->AddItem(archivedView, shelf, &id);
603		if (err == B_OK) {
604			// Detach the deleter since AddReplicant is taking ownership
605			// on success. This should be changed on server side.
606			deleter.Detach();
607		}
608	} else if (message->FindRef("addon", &ref) == B_OK) {
609		BEntry entry(&ref);
610		err = entry.InitCheck();
611		if (err == B_OK)
612			err = fBarView->AddItem(&entry, shelf, &id);
613	}
614
615	if (err == B_OK)
616		reply.AddInt32("id", id);
617	else
618		reply.AddInt32("error", err);
619
620	message->SendReply(&reply);
621}
622
623
624void
625TBarWindow::RemoveItem(BMessage* message)
626{
627	int32 id;
628	const char* name;
629
630	// ids ought to be unique across all shelves, assuming, of course,
631	// that sometime in the future there may be more than one
632#if SHELF_AWARE
633	if (message->FindInt32("shelf", (int32*)&shelf) == B_OK) {
634		if (message->FindString("name", &name) == B_OK)
635			fBarView->RemoveItem(name, shelf);
636	} else {
637#endif
638		if (message->FindInt32("id", &id) == B_OK) {
639			fBarView->RemoveItem(id);
640		// remove the following two lines if and when the
641		// shelf option returns
642		} else if (message->FindString("name", &name) == B_OK)
643			fBarView->RemoveItem(name, B_DESKBAR_TRAY);
644
645#if SHELF_AWARE
646	}
647#endif
648}
649
650
651void
652TBarWindow::GetIconFrame(BMessage* message)
653{
654	BRect frame(0, 0, 0, 0);
655
656	const char* name;
657	int32 id;
658	if (message->FindInt32("id", &id) == B_OK)
659		frame = fBarView->IconFrame(id);
660	else if (message->FindString("name", &name) == B_OK)
661		frame = fBarView->IconFrame(name);
662
663	BMessage reply('rply');
664	reply.AddRect("frame", frame);
665	message->SendReply(&reply);
666}
667
668
669bool
670TBarWindow::IsShowingMenu() const
671{
672	return fMenusShown > 0;
673}
674
675
676void
677TBarWindow::SetSizeLimits()
678{
679	BRect screenFrame = (BScreen(this)).Frame();
680	bool setToHiddenSize = fBarApp->Settings()->autoHide
681		&& fBarView->IsHidden() && !fBarView->DragRegion()->IsDragging();
682
683	if (setToHiddenSize) {
684		if (fBarView->Vertical())
685			BWindow::SetSizeLimits(0, kHiddenDimension, 0, kHiddenDimension);
686		else {
687			BWindow::SetSizeLimits(screenFrame.Width(), screenFrame.Width(),
688				0, kHiddenDimension);
689		}
690	} else {
691		float minHeight;
692		float maxHeight;
693		float minWidth;
694		float maxWidth;
695
696		if (fBarView->Vertical()) {
697			minHeight = fBarView->TabHeight();
698			maxHeight = B_SIZE_UNLIMITED;
699			minWidth = gMinimumWindowWidth;
700			maxWidth = gMaximumWindowWidth;
701		} else {
702			// horizontal
703			if (fBarView->MiniState()) {
704				// horizontal mini-mode
705				minWidth = gMinimumWindowWidth;
706				maxWidth = B_SIZE_UNLIMITED;
707				minHeight = fBarView->TabHeight();
708				maxHeight = std::max(fBarView->TabHeight(), kGutter
709					+ fBarView->ReplicantTray()->MaxReplicantHeight()
710					+ kGutter);
711			} else {
712				// horizontal expando-mode
713				const int32 max
714					= be_control_look->ComposeIconSize(kMaximumIconSize)
715						.IntegerWidth() + 1;
716				const float iconPadding
717					= be_control_look->ComposeSpacing(kIconPadding);
718
719				minWidth = maxWidth = screenFrame.Width();
720				minHeight = kMenuBarHeight - 1;
721				maxHeight = max + iconPadding / 2;
722			}
723		}
724
725		BWindow::SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
726	}
727}
728
729
730bool
731TBarWindow::_IsFocusMessage(BMessage* message)
732{
733	BMessage::Private messagePrivate(message);
734	if (!messagePrivate.UsePreferredTarget())
735		return false;
736
737	bool feedFocus;
738	if (message->HasInt32("_token")
739		&& (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
740		return false;
741
742	return true;
743}
744