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