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