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