xref: /haiku/src/apps/deskbar/BarWindow.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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->ExpandoMenuBar() != NULL)
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 	SetSizeLimits();
316 
317 	if (fBarView != NULL) {
318 		fBarView->DragRegion()->CalculateRegions();
319 		fBarView->UpdatePlacement();
320 	}
321 }
322 
323 
324 void
325 TBarWindow::SetDeskbarMenu(TDeskbarMenu* menu)
326 {
327 	sDeskbarMenu = menu;
328 }
329 
330 
331 TDeskbarMenu*
332 TBarWindow::DeskbarMenu()
333 {
334 	return sDeskbarMenu;
335 }
336 
337 
338 void
339 TBarWindow::ShowDeskbarMenu()
340 {
341 	TStartableMenuBar* menuBar = (TStartableMenuBar*)fBarView->BarMenuBar();
342 	if (menuBar == NULL)
343 		menuBar = (TStartableMenuBar*)KeyMenuBar();
344 
345 	if (menuBar == NULL)
346 		return;
347 
348 	menuBar->StartMenuBar(0, true, true, NULL);
349 }
350 
351 
352 void
353 TBarWindow::ShowTeamMenu()
354 {
355 	int32 index = 0;
356 	if (fBarView->BarMenuBar() == NULL)
357 		index = 2;
358 
359 	if (KeyMenuBar() == NULL)
360 		return;
361 
362 	((TStartableMenuBar*)KeyMenuBar())->StartMenuBar(index, true, true, NULL);
363 }
364 
365 
366 // determines the actual location of the window
367 
368 deskbar_location
369 TBarWindow::DeskbarLocation() const
370 {
371 	bool left = fBarView->Left();
372 	bool top = fBarView->Top();
373 
374 	if (fBarView->AcrossTop())
375 		return B_DESKBAR_TOP;
376 
377 	if (fBarView->AcrossBottom())
378 		return B_DESKBAR_BOTTOM;
379 
380 	if (left && top)
381 		return B_DESKBAR_LEFT_TOP;
382 
383 	if (!left && top)
384 		return B_DESKBAR_RIGHT_TOP;
385 
386 	if (left && !top)
387 		return B_DESKBAR_LEFT_BOTTOM;
388 
389 	return B_DESKBAR_RIGHT_BOTTOM;
390 }
391 
392 
393 void
394 TBarWindow::GetLocation(BMessage* message)
395 {
396 	BMessage reply('rply');
397 	reply.AddInt32("location", (int32)DeskbarLocation());
398 	reply.AddBool("expanded", fBarView->ExpandoState());
399 
400 	message->SendReply(&reply);
401 }
402 
403 
404 void
405 TBarWindow::SetDeskbarLocation(deskbar_location location, bool newExpandState)
406 {
407 	// left top and right top are the only two that
408 	// currently pay attention to expand, ignore for all others
409 
410 	bool left = false, top = true, vertical, expand;
411 
412 	switch (location) {
413 		case B_DESKBAR_TOP:
414 			left = true;
415 			top = true;
416 			vertical = false;
417 			expand = true;
418 			break;
419 
420 		case B_DESKBAR_BOTTOM:
421 			left = true;
422 			top = false;
423 			vertical = false;
424 			expand = true;
425 			break;
426 
427 		case B_DESKBAR_LEFT_TOP:
428 			left = true;
429 			top = true;
430 			vertical = true;
431 			expand = newExpandState;
432 			break;
433 
434 		case B_DESKBAR_RIGHT_TOP:
435 			left = false;
436 			top = true;
437 			vertical = true;
438 			expand = newExpandState;
439 			break;
440 
441 		case B_DESKBAR_LEFT_BOTTOM:
442 			left = true;
443 			top = false;
444 			vertical = true;
445 			expand = false;
446 			break;
447 
448 		case B_DESKBAR_RIGHT_BOTTOM:
449 			left = false;
450 			top = false;
451 			vertical = true;
452 			expand = false;
453 			break;
454 
455 		default:
456 			left = true;
457 			top = true;
458 			vertical = false;
459 			expand = true;
460 			break;
461 	}
462 
463 	fBarView->ChangeState(expand, vertical, left, top);
464 }
465 
466 
467 void
468 TBarWindow::SetLocation(BMessage* message)
469 {
470 	deskbar_location location;
471 	bool expand;
472 	if (message->FindInt32("location", (int32*)&location) == B_OK
473 		&& message->FindBool("expand", &expand) == B_OK)
474 		SetDeskbarLocation(location, expand);
475 }
476 
477 
478 void
479 TBarWindow::IsExpanded(BMessage* message)
480 {
481 	BMessage reply('rply');
482 	reply.AddBool("expanded", fBarView->ExpandoState());
483 	message->SendReply(&reply);
484 }
485 
486 
487 void
488 TBarWindow::Expand(BMessage* message)
489 {
490 	bool expand;
491 	if (message->FindBool("expand", &expand) == B_OK) {
492 		bool vertical = fBarView->Vertical();
493 		bool left = fBarView->Left();
494 		bool top = fBarView->Top();
495 		fBarView->ChangeState(expand, vertical, left, top);
496 	}
497 }
498 
499 
500 void
501 TBarWindow::ItemInfo(BMessage* message)
502 {
503 	BMessage replyMsg;
504 	const char* name;
505 	int32 id;
506 	DeskbarShelf shelf;
507 	if (message->FindInt32("id", &id) == B_OK) {
508 		if (fBarView->ItemInfo(id, &name, &shelf) == B_OK) {
509 			replyMsg.AddString("name", name);
510 #if SHELF_AWARE
511 			replyMsg.AddInt32("shelf", (int32)shelf);
512 #endif
513 		}
514 	} else if (message->FindString("name", &name) == B_OK) {
515 		if (fBarView->ItemInfo(name, &id, &shelf) == B_OK) {
516 			replyMsg.AddInt32("id", id);
517 #if SHELF_AWARE
518 			replyMsg.AddInt32("shelf", (int32)shelf);
519 #endif
520 		}
521 	}
522 
523 	message->SendReply(&replyMsg);
524 }
525 
526 
527 void
528 TBarWindow::ItemExists(BMessage* message)
529 {
530 	BMessage replyMsg;
531 	const char* name;
532 	int32 id;
533 	DeskbarShelf shelf;
534 
535 #if SHELF_AWARE
536 	if (message->FindInt32("shelf", (int32*)&shelf) != B_OK)
537 #endif
538 		shelf = B_DESKBAR_TRAY;
539 
540 	bool exists = false;
541 	if (message->FindInt32("id", &id) == B_OK)
542 		exists = fBarView->ItemExists(id, shelf);
543 	else if (message->FindString("name", &name) == B_OK)
544 		exists = fBarView->ItemExists(name, shelf);
545 
546 	replyMsg.AddBool("exists", exists);
547 	message->SendReply(&replyMsg);
548 }
549 
550 
551 void
552 TBarWindow::CountItems(BMessage* message)
553 {
554 	DeskbarShelf shelf;
555 
556 #if SHELF_AWARE
557 	if (message->FindInt32("shelf", (int32*)&shelf) != B_OK)
558 #endif
559 		shelf = B_DESKBAR_TRAY;
560 
561 	BMessage reply('rply');
562 	reply.AddInt32("count", fBarView->CountItems(shelf));
563 	message->SendReply(&reply);
564 }
565 
566 
567 void
568 TBarWindow::MaxItemSize(BMessage* message)
569 {
570 	DeskbarShelf shelf;
571 #if SHELF_AWARE
572 	if (message->FindInt32("shelf", (int32*)&shelf) != B_OK)
573 #endif
574 		shelf = B_DESKBAR_TRAY;
575 
576 	BSize size = fBarView->MaxItemSize(shelf);
577 
578 	BMessage reply('rply');
579 	reply.AddFloat("width", size.width);
580 	reply.AddFloat("height", size.height);
581 	message->SendReply(&reply);
582 }
583 
584 
585 void
586 TBarWindow::AddItem(BMessage* message)
587 {
588 	DeskbarShelf shelf = B_DESKBAR_TRAY;
589 	entry_ref ref;
590 	int32 id = 999;
591 	BMessage reply;
592 	status_t err = B_ERROR;
593 
594 	BMessage* archivedView = new BMessage();
595 	ObjectDeleter<BMessage> deleter(archivedView);
596 	if (message->FindMessage("view", archivedView) == B_OK) {
597 #if SHELF_AWARE
598 		message->FindInt32("shelf", &shelf);
599 #endif
600 		err = fBarView->AddItem(archivedView, shelf, &id);
601 		if (err == B_OK) {
602 			// Detach the deleter since AddReplicant is taking ownership
603 			// on success. This should be changed on server side.
604 			deleter.Detach();
605 		}
606 	} else if (message->FindRef("addon", &ref) == B_OK) {
607 		BEntry entry(&ref);
608 		err = entry.InitCheck();
609 		if (err == B_OK)
610 			err = fBarView->AddItem(&entry, shelf, &id);
611 	}
612 
613 	if (err == B_OK)
614 		reply.AddInt32("id", id);
615 	else
616 		reply.AddInt32("error", err);
617 
618 	message->SendReply(&reply);
619 }
620 
621 
622 void
623 TBarWindow::RemoveItem(BMessage* message)
624 {
625 	int32 id;
626 	const char* name;
627 
628 	// ids ought to be unique across all shelves, assuming, of course,
629 	// that sometime in the future there may be more than one
630 #if SHELF_AWARE
631 	if (message->FindInt32("shelf", (int32*)&shelf) == B_OK) {
632 		if (message->FindString("name", &name) == B_OK)
633 			fBarView->RemoveItem(name, shelf);
634 	} else {
635 #endif
636 		if (message->FindInt32("id", &id) == B_OK) {
637 			fBarView->RemoveItem(id);
638 		// remove the following two lines if and when the
639 		// shelf option returns
640 		} else if (message->FindString("name", &name) == B_OK)
641 			fBarView->RemoveItem(name, B_DESKBAR_TRAY);
642 
643 #if SHELF_AWARE
644 	}
645 #endif
646 }
647 
648 
649 void
650 TBarWindow::GetIconFrame(BMessage* message)
651 {
652 	BRect frame(0, 0, 0, 0);
653 
654 	const char* name;
655 	int32 id;
656 	if (message->FindInt32("id", &id) == B_OK)
657 		frame = fBarView->IconFrame(id);
658 	else if (message->FindString("name", &name) == B_OK)
659 		frame = fBarView->IconFrame(name);
660 
661 	BMessage reply('rply');
662 	reply.AddRect("frame", frame);
663 	message->SendReply(&reply);
664 }
665 
666 
667 bool
668 TBarWindow::IsShowingMenu() const
669 {
670 	return fMenusShown > 0;
671 }
672 
673 
674 void
675 TBarWindow::SetSizeLimits()
676 {
677 	BRect screenFrame = (BScreen(this)).Frame();
678 	bool setToHiddenSize = fBarApp->Settings()->autoHide
679 		&& fBarView->IsHidden() && !fBarView->DragRegion()->IsDragging();
680 
681 	if (setToHiddenSize) {
682 		if (fBarView->Vertical())
683 			BWindow::SetSizeLimits(0, kHiddenDimension, 0, kHiddenDimension);
684 		else {
685 			BWindow::SetSizeLimits(screenFrame.Width(), screenFrame.Width(),
686 				0, kHiddenDimension);
687 		}
688 	} else {
689 		float minHeight;
690 		float maxHeight;
691 		float minWidth;
692 		float maxWidth;
693 
694 		if (fBarView->Vertical()) {
695 			minHeight = fBarView->TabHeight();
696 			maxHeight = B_SIZE_UNLIMITED;
697 			minWidth = gMinimumWindowWidth;
698 			maxWidth = gMaximumWindowWidth;
699 		} else {
700 			// horizontal
701 			if (fBarView->MiniState()) {
702 				minWidth = gMinimumWindowWidth;
703 				maxWidth = B_SIZE_UNLIMITED;
704 				minHeight = fBarView->TabHeight();
705 				maxHeight = std::max(fBarView->TabHeight(), kGutter
706 					+ fBarView->ReplicantTray()->MaxReplicantHeight()
707 					+ kGutter);
708 			} else {
709 				minWidth = maxWidth = screenFrame.Width();
710 				minHeight = kMenuBarHeight - 1;
711 				maxHeight = kMaximumIconSize + 4;
712 			}
713 		}
714 
715 		BWindow::SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
716 	}
717 }
718 
719 
720 bool
721 TBarWindow::_IsFocusMessage(BMessage* message)
722 {
723 	BMessage::Private messagePrivate(message);
724 	if (!messagePrivate.UsePreferredTarget())
725 		return false;
726 
727 	bool feedFocus;
728 	if (message->HasInt32("_token")
729 		&& (message->FindBool("_feed_focus", &feedFocus) != B_OK || !feedFocus))
730 		return false;
731 
732 	return true;
733 }
734