xref: /haiku/src/apps/deskbar/BarWindow.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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 <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!
74 class TStartableMenuBar : public BMenuBar {
75 public:
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 
83 TDeskbarMenu* TBarWindow::sDeskbarMenu = NULL;
84 
85 
86 TBarWindow::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 
114 void
115 TBarWindow::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 
153 void
154 TBarWindow::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 
174 void
175 TBarWindow::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 
237 void
238 TBarWindow::Minimize(bool minimize)
239 {
240 	// Don't allow the Deskbar to be minimized
241 	if (!minimize)
242 		BWindow::Minimize(false);
243 }
244 
245 
246 void
247 TBarWindow::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 
280 void
281 TBarWindow::SaveSettings()
282 {
283 	fBarView->SaveSettings();
284 }
285 
286 
287 bool
288 TBarWindow::QuitRequested()
289 {
290 	be_app->PostMessage(B_QUIT_REQUESTED);
291 
292 	return BWindow::QuitRequested();
293 }
294 
295 
296 void
297 TBarWindow::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 
312 void
313 TBarWindow::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 
326 void
327 TBarWindow::SetDeskbarMenu(TDeskbarMenu* menu)
328 {
329 	sDeskbarMenu = menu;
330 }
331 
332 
333 TDeskbarMenu*
334 TBarWindow::DeskbarMenu()
335 {
336 	return sDeskbarMenu;
337 }
338 
339 
340 void
341 TBarWindow::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 
354 void
355 TBarWindow::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 
370 deskbar_location
371 TBarWindow::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 
395 void
396 TBarWindow::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 
406 void
407 TBarWindow::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 
469 void
470 TBarWindow::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 
480 void
481 TBarWindow::IsExpanded(BMessage* message)
482 {
483 	BMessage reply('rply');
484 	reply.AddBool("expanded", fBarView->ExpandoState());
485 	message->SendReply(&reply);
486 }
487 
488 
489 void
490 TBarWindow::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 
502 void
503 TBarWindow::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 
529 void
530 TBarWindow::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 
553 void
554 TBarWindow::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 
569 void
570 TBarWindow::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 
587 void
588 TBarWindow::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 
624 void
625 TBarWindow::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 
651 void
652 TBarWindow::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 
669 bool
670 TBarWindow::IsShowingMenu() const
671 {
672 	return fMenusShown > 0;
673 }
674 
675 
676 void
677 TBarWindow::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 
730 bool
731 TBarWindow::_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