xref: /haiku/src/apps/deskbar/DeskbarMenu.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 "DeskbarMenu.h"
38 
39 #include <Debug.h>
40 #include <Bitmap.h>
41 #include <Catalog.h>
42 #include <Dragger.h>
43 #include <Locale.h>
44 #include <Menu.h>
45 #include <MenuItem.h>
46 #include <Roster.h>
47 
48 #include "BarApp.h"
49 #include "BarView.h"
50 #include "DeskbarUtils.h"
51 #include "IconMenuItem.h"
52 #include "MountMenu.h"
53 #include "RecentItems.h"
54 #include "StatusView.h"
55 
56 #include "tracker_private.h"
57 
58 #undef B_TRANSLATION_CONTEXT
59 #define B_TRANSLATION_CONTEXT "DeskbarMenu"
60 
61 #define ROSTER_SIG "application/x-vnd.Be-ROST"
62 
63 #ifdef MOUNT_MENU_IN_DESKBAR
64 class DeskbarMountMenu : public BPrivate::MountMenu {
65 public:
66 	DeskbarMountMenu(const char* name);
67 	virtual bool AddDynamicItem(add_state s);
68 };
69 #endif	// MOUNT_MENU_IN_DESKBAR
70 
71 //#define SHOW_RECENT_FIND_ITEMS
72 
73 namespace BPrivate {
74 	BMenu* TrackerBuildRecentFindItemsMenu(const char*);
75 }
76 
77 using namespace BPrivate;
78 
79 
80 //	#pragma mark - TDeskbarMenu
81 
82 
83 TDeskbarMenu::TDeskbarMenu(TBarView* barView)
84 	:
85 	BNavMenu("DeskbarMenu", B_REFS_RECEIVED, DefaultTarget()),
86 	fAddState(kStart),
87 	fBarView(barView)
88 {
89 }
90 
91 
92 void
93 TDeskbarMenu::AttachedToWindow()
94 {
95 	if (fBarView != NULL && fBarView->LockLooper()) {
96 		if (fBarView->Dragging()) {
97 			SetTypesList(fBarView->CachedTypesList());
98 			SetTarget(BMessenger(fBarView));
99 			SetTrackingHookDeep(this, fBarView->MenuTrackingHook,
100 				fBarView->GetTrackingHookData());
101 			fBarView->DragStart();
102 		} else {
103 			SetTypesList(NULL);
104 			SetTarget(DefaultTarget());
105 			SetTrackingHookDeep(this, NULL, NULL);
106 		}
107 
108 		fBarView->UnlockLooper();
109 	}
110 
111 	BNavMenu::AttachedToWindow();
112 }
113 
114 
115 void
116 TDeskbarMenu::DetachedFromWindow()
117 {
118 	if (fBarView != NULL) {
119 		BLooper* looper = fBarView->Looper();
120 		if (looper != NULL && looper->Lock()) {
121 			fBarView->DragStop();
122 			looper->Unlock();
123 		}
124 	}
125 
126 	// don't call BNavMenu::DetachedFromWindow
127 	// it sets the TypesList to NULL
128 	BMenu::DetachedFromWindow();
129 }
130 
131 
132 bool
133 TDeskbarMenu::StartBuildingItemList()
134 {
135 	RemoveItems(0, CountItems(), true);
136 	fAddState = kStart;
137 	return BNavMenu::StartBuildingItemList();
138 }
139 
140 
141 void
142 TDeskbarMenu::DoneBuildingItemList()
143 {
144 	if (fItemList->CountItems() <= 0) {
145 		BMenuItem* item
146 			= new BMenuItem(B_TRANSLATE("<Deskbar folder is empty>"), 0);
147 		item->SetEnabled(false);
148 		AddItem(item);
149 	} else
150 		BNavMenu::DoneBuildingItemList();
151 }
152 
153 
154 bool
155 TDeskbarMenu::AddNextItem()
156 {
157 	if (fAddState == kStart)
158 		return AddStandardDeskbarMenuItems();
159 
160 	TrackingHookData* data = fBarView->GetTrackingHookData();
161 	if (fAddState == kAddingRecents) {
162 		static const char* recentTitle[] = {
163 			B_TRANSLATE_MARK("Recent documents"),
164 			B_TRANSLATE_MARK("Recent folders"),
165 			B_TRANSLATE_MARK("Recent applications")};
166 		const int recentType[] = {kRecentDocuments, kRecentFolders,
167 			kRecentApplications};
168 		const int recentTypes = 3;
169 		TRecentsMenu* recentItem[recentTypes];
170 
171 		bool enabled = false;
172 
173 		for (int i = 0; i < recentTypes; i++) {
174 			recentItem[i]
175 				= new TRecentsMenu(B_TRANSLATE_NOCOLLECT(recentTitle[i]),
176 					fBarView, recentType[i]);
177 
178 			if (recentItem[i])
179 				enabled |= recentItem[i]->RecentsEnabled();
180 		}
181 		if (enabled) {
182 			AddSeparatorItem();
183 
184 			for (int i = 0; i < recentTypes; i++) {
185 				if (!recentItem[i])
186 					continue;
187 
188 				if (recentItem[i]->RecentsEnabled()) {
189 					recentItem[i]->SetTypesList(TypesList());
190 					recentItem[i]->SetTarget(Target());
191 					AddItem(recentItem[i]);
192 				}
193 
194 				if (data && fBarView && fBarView->Dragging()) {
195 					recentItem[i]->InitTrackingHook(data->fTrackingHook,
196 						&data->fTarget, data->fDragMessage);
197 				}
198 			}
199 		} else {
200 			for (int i = 0; i < recentTypes; i++)
201 				delete recentItem[i];
202 		}
203 
204 		AddSeparatorItem();
205 		fAddState = kAddingDeskbarMenu;
206 		return true;
207 	}
208 
209 	if (fAddState == kAddingDeskbarMenu) {
210 		// keep reentering and adding items
211 		// until this returns false
212 		bool done = BNavMenu::AddNextItem();
213 		BMenuItem* item = ItemAt(CountItems() - 1);
214 		if (item) {
215 			BNavMenu* menu = dynamic_cast<BNavMenu*>(item->Menu());
216 			if (menu) {
217 				if (data && fBarView->Dragging()) {
218 					menu->InitTrackingHook(data->fTrackingHook,
219 						&data->fTarget, data->fDragMessage);
220 				} else
221 					menu->InitTrackingHook(0, NULL, NULL);
222 			}
223 		}
224 
225 		if (!done)
226 			fAddState = kDone;
227 		return done;
228 	}
229 
230 	return false;
231 }
232 
233 
234 bool
235 TDeskbarMenu::AddStandardDeskbarMenuItems()
236 {
237 	bool dragging = false;
238 	if (fBarView)
239 		dragging = fBarView->Dragging();
240 
241 	BMenuItem* item;
242 
243 // One of them is used if HAIKU_DISTRO_COMPATIBILITY_OFFICIAL, and the other if
244 // not. However, we want both of them to end up in the catalog, so we have to
245 // make them visible to collectcatkeys in either case.
246 B_TRANSLATE_MARK_VOID("About Haiku")
247 B_TRANSLATE_MARK_VOID("About this system")
248 
249 	item = new BMenuItem(
250 #ifdef HAIKU_DISTRO_COMPATIBILITY_OFFICIAL
251 	B_TRANSLATE_NOCOLLECT("About Haiku")
252 #else
253 	B_TRANSLATE_NOCOLLECT("About this system")
254 #endif
255 		, new BMessage(kShowSplash));
256 	item->SetEnabled(!dragging);
257 	AddItem(item);
258 
259 	static const char* kFindMenuItemStr
260 		= B_TRANSLATE_MARK("Find" B_UTF8_ELLIPSIS);
261 
262 #ifdef SHOW_RECENT_FIND_ITEMS
263 	item = new BMenuItem(
264 		TrackerBuildRecentFindItemsMenu(kFindMenuItemStr),
265 		new BMessage(kFindButton));
266 #else
267 	item = new BMenuItem(B_TRANSLATE_NOCOLLECT(kFindMenuItemStr),
268 		new BMessage(kFindButton));
269 #endif
270 	item->SetEnabled(!dragging);
271 	AddItem(item);
272 
273 	item = new BMenuItem(B_TRANSLATE("Show replicants"),
274 		new BMessage(kToggleDraggers));
275 	item->SetEnabled(!dragging);
276 	item->SetMarked(BDragger::AreDraggersDrawn());
277 	AddItem(item);
278 
279 	static const char* kMountMenuStr = B_TRANSLATE_MARK("Mount");
280 
281 #ifdef MOUNT_MENU_IN_DESKBAR
282 	DeskbarMountMenu* mountMenu = new DeskbarMountMenu(
283 		B_TRANSLATE_NOCOLLECT(kMountMenuStr));
284 	mountMenu->SetEnabled(!dragging);
285 	AddItem(mountMenu);
286 #endif
287 
288 	item = new BMenuItem(B_TRANSLATE("Deskbar preferences" B_UTF8_ELLIPSIS),
289 		new BMessage(kConfigShow));
290 	item->SetTarget(be_app);
291 	AddItem(item);
292 
293 	AddSeparatorItem();
294 
295 	BMenu* shutdownMenu = new BMenu(B_TRANSLATE("Shutdown" B_UTF8_ELLIPSIS));
296 
297 	item = new BMenuItem(B_TRANSLATE("Restart system"),
298 		new BMessage(kRebootSystem));
299 	item->SetEnabled(!dragging);
300 	shutdownMenu->AddItem(item);
301 
302 	B_TRANSLATE_MARK_VOID("Suspend");
303 
304 #ifdef APM_SUPPORT
305 	if (_kapm_control_(APM_CHECK_ENABLED) == B_OK) {
306 		item = new BMenuItem(B_TRANSLATE_NOCOLLECT("Suspend"),
307 			new BMessage(kSuspendSystem));
308 		item->SetEnabled(!dragging);
309 		shutdownMenu->AddItem(item);
310 	}
311 #endif
312 
313 	item = new BMenuItem(B_TRANSLATE("Power off"),
314 		new BMessage(kShutdownSystem));
315 	item->SetEnabled(!dragging);
316 	shutdownMenu->AddItem(item);
317 	shutdownMenu->SetFont(be_plain_font);
318 
319 	shutdownMenu->SetTargetForItems(be_app);
320 	BMessage* message = new BMessage(kShutdownSystem);
321 	message->AddBool("confirm", true);
322 	AddItem(new BMenuItem(shutdownMenu, message));
323 
324 	fAddState = kAddingRecents;
325 
326 	return true;
327 }
328 
329 
330 void
331 TDeskbarMenu::ClearMenuBuildingState()
332 {
333 	fAddState = kDone;
334 	fMenuBuilt = false;
335 		// force the menu to get rebuilt each time
336 	BNavMenu::ClearMenuBuildingState();
337 }
338 
339 
340 void
341 TDeskbarMenu::ResetTargets()
342 {
343 	// This method does not recurse into submenus
344 	// and does not affect menu items in submenus.
345 	// (e.g. "Restart System" and "Power Off")
346 
347 	BNavMenu::ResetTargets();
348 
349 	// if we are dragging, set the target to whatever was set
350 	// else set it to the default (Tracker)
351 	if (!fBarView->Dragging())
352 		SetTarget(DefaultTarget());
353 
354 	// now set the target for the menuitems to the currently
355 	// set target, which may or may not be tracker
356 	SetTargetForItems(Target());
357 
358 	for (int32 i = 0; ; i++) {
359 		BMenuItem* item = ItemAt(i);
360 		if (item == NULL)
361 			break;
362 
363 		if (item->Message()) {
364 			switch (item->Message()->what) {
365 				case kFindButton:
366 					item->SetTarget(BMessenger(kTrackerSignature));
367 					break;
368 
369 				case kShowSplash:
370 				case kToggleDraggers:
371 				case kConfigShow:
372 				case kConfigQuit:
373 				case kAlwaysTop:
374 				case kExpandNewTeams:
375 				case kHideLabels:
376 				case kResizeTeamIcons:
377 				case kSortRunningApps:
378 				case kTrackerFirst:
379 				case kRebootSystem:
380 				case kSuspendSystem:
381 				case kShutdownSystem:
382 				case kRealignReplicants:
383 				case kShowHideTime:
384 				case kShowSeconds:
385 				case kShowDayOfWeek:
386 				case kShowTimeZone:
387 				case kGetClockSettings:
388 					item->SetTarget(be_app);
389 					break;
390 			}
391 		}
392 	}
393 }
394 
395 
396 BPoint
397 TDeskbarMenu::ScreenLocation()
398 {
399 	bool vertical = fBarView->Vertical();
400 	int32 expando = (fBarView->State() == kExpandoState);
401 	BPoint point;
402 
403 	BRect rect = Supermenu()->Bounds();
404 	Supermenu()->ConvertToScreen(&rect);
405 
406 	if (expando && vertical && fBarView->Left()) {
407 		PRINT(("Left\n"));
408 		point = rect.RightTop() + BPoint(0, 3);
409 	} else if (expando && vertical && !fBarView->Left()) {
410 		PRINT(("Right\n"));
411 		point = rect.LeftTop() - BPoint(Bounds().Width(), 0) + BPoint(0, 3);
412 	} else
413 		point = BMenu::ScreenLocation();
414 
415 	return point;
416 }
417 
418 
419 /*static*/
420 BMessenger
421 TDeskbarMenu::DefaultTarget()
422 {
423 	// if Tracker is not available we target the BarApp
424 	BMessenger target(kTrackerSignature);
425 	if (target.IsValid())
426 		return target;
427 
428 	return BMessenger(be_app);
429 }
430 
431 
432 //	#pragma mark -
433 
434 
435 TRecentsMenu::TRecentsMenu(const char* name, TBarView* bar, int32 which,
436 		const char* signature, entry_ref* appRef)
437 	: BNavMenu(name, B_REFS_RECEIVED, TDeskbarMenu::DefaultTarget()),
438 	fWhich(which),
439 	fAppRef(NULL),
440 	fSignature(NULL),
441 	fRecentsCount(0),
442 	fRecentsEnabled(false),
443 	fItemIndex(0),
444 	fBarView(bar)
445 {
446 	TBarApp* app = dynamic_cast<TBarApp*>(be_app);
447 	if (app == NULL)
448 		return;
449 
450 	switch (which) {
451 		case kRecentDocuments:
452 			fRecentsCount = app->Settings()->recentDocsCount;
453 			fRecentsEnabled = app->Settings()->recentDocsEnabled;
454 			break;
455 		case kRecentApplications:
456 			fRecentsCount = app->Settings()->recentAppsCount;
457 			fRecentsEnabled = app->Settings()->recentAppsEnabled;
458 			break;
459 		case kRecentAppDocuments:
460 			fRecentsCount = app->Settings()->recentDocsCount;
461 			fRecentsEnabled = app->Settings()->recentDocsEnabled;
462 			if (signature != NULL)
463 				fSignature = strdup(signature);
464 			if (appRef != NULL)
465 				fAppRef = new entry_ref(*appRef);
466 			break;
467 		case kRecentFolders:
468 			fRecentsCount = app->Settings()->recentFoldersCount;
469 			fRecentsEnabled = app->Settings()->recentFoldersEnabled;
470 			break;
471 	}
472 }
473 
474 
475 TRecentsMenu::~TRecentsMenu()
476 {
477 	delete fAppRef;
478 	free(fSignature);
479 }
480 
481 
482 void
483 TRecentsMenu::DetachedFromWindow()
484 {
485 	// BNavMenu::DetachedFromWindow sets the TypesList to NULL
486 	BMenu::DetachedFromWindow();
487 }
488 
489 
490 bool
491 TRecentsMenu::StartBuildingItemList()
492 {
493 	RemoveItems(0, CountItems(), true);
494 
495 	// !! note: don't call inherited from here
496 	// the navref is not set for this menu
497 	// but it still needs to be a draggable navmenu
498 	// simply return true so that AddNextItem is called
499 	//
500 	// return BNavMenu::StartBuildingItemList();
501 	return true;
502 }
503 
504 
505 bool
506 TRecentsMenu::AddNextItem()
507 {
508 	if (fRecentsCount > 0 && fRecentsEnabled && AddRecents(fRecentsCount))
509 		return true;
510 
511 	fItemIndex = 0;
512 	return false;
513 }
514 
515 
516 bool
517 TRecentsMenu::AddRecents(int32 count)
518 {
519 	if (fItemIndex == 0) {
520 		fRecentList.MakeEmpty();
521 		BRoster roster;
522 
523 		switch (fWhich) {
524 			case kRecentDocuments:
525 				roster.GetRecentDocuments(&fRecentList, count);
526 				break;
527 			case kRecentApplications:
528 				roster.GetRecentApps(&fRecentList, count);
529 				break;
530 			case kRecentAppDocuments:
531 				roster.GetRecentDocuments(&fRecentList, count, NULL,
532 					fSignature);
533 				break;
534 			case kRecentFolders:
535 				roster.GetRecentFolders(&fRecentList, count);
536 				break;
537 			default:
538 				return false;
539 		}
540 	}
541 
542 	for (;;) {
543 		entry_ref ref;
544 		if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK)
545 			break;
546 
547 		if (ref.name && strlen(ref.name) > 0) {
548 			Model model(&ref, true);
549 
550 			if (fWhich != kRecentApplications) {
551 				BMessage* message = new BMessage(B_REFS_RECEIVED);
552 				if (fWhich == kRecentAppDocuments) {
553 					// add application as handler
554 					message->AddRef("handler", fAppRef);
555 				}
556 
557 				ModelMenuItem* item = BNavMenu::NewModelItem(&model,
558 					message, Target(), false, NULL, TypesList());
559 
560 				if (item)
561 					AddItem(item);
562 			} else {
563 				// The application items expand to a list of recent documents
564 				// for that application - so they must be handled extra
565 				BFile file(&ref, B_READ_ONLY);
566 				char signature[B_MIME_TYPE_LENGTH];
567 
568 				BAppFileInfo appInfo(&file);
569 				if (appInfo.InitCheck() != B_OK
570 					|| appInfo.GetSignature(signature) != B_OK)
571 					continue;
572 
573 				ModelMenuItem* item = NULL;
574 				BMessage doc;
575 				be_roster->GetRecentDocuments(&doc, 1, NULL, signature);
576 					// ToDo: check if the documents do exist at all to
577 					//		avoid the creation of the submenu.
578 
579 				if (doc.CountNames(B_REF_TYPE) > 0) {
580 					// create recents menu that will contain the recent docs of
581 					// this app
582 					TRecentsMenu* docs = new TRecentsMenu(model.Name(),
583 						fBarView, kRecentAppDocuments, signature, &ref);
584 					docs->SetTypesList(TypesList());
585 					docs->SetTarget(Target());
586 
587 					item = new ModelMenuItem(&model, docs);
588 				} else
589 					item = new ModelMenuItem(&model, model.Name(), NULL);
590 
591 				if (item) {
592 					// add refs-message so that the recent app can be launched
593 					BMessage* msg = new BMessage(B_REFS_RECEIVED);
594 					msg->AddRef("refs", &ref);
595 					item->SetMessage(msg);
596 					item->SetTarget(Target());
597 
598 					AddItem(item);
599 				}
600 			}
601 
602 			// return true so that we know to reenter this list
603 			return true;
604 		}
605 	}
606 
607 	// return false if we are done with this list
608 	return false;
609 }
610 
611 
612 void
613 TRecentsMenu::DoneBuildingItemList()
614 {
615 	// !! note: don't call inherited here
616 	// the object list is not built
617 	// and this list does not need to be sorted
618 	// BNavMenu::DoneBuildingItemList();
619 
620 	if (CountItems() > 0)
621 		SetTargetForItems(Target());
622 }
623 
624 
625 void
626 TRecentsMenu::ClearMenuBuildingState()
627 {
628 	fMenuBuilt = false;
629 	BNavMenu::ClearMenuBuildingState();
630 }
631 
632 
633 void
634 TRecentsMenu::ResetTargets()
635 {
636 	BNavMenu::ResetTargets();
637 
638 	// if we are dragging, set the target to whatever was set
639 	// else set it to the default (Tracker)
640 	if (!fBarView->Dragging())
641 		SetTarget(TDeskbarMenu::DefaultTarget());
642 
643 	// now set the target for the menuitems to the currently
644 	// set target, which may or may not be tracker
645 	SetTargetForItems(Target());
646 }
647 
648 
649 //	#pragma mark - DeskbarMountMenu
650 
651 
652 #ifdef MOUNT_MENU_IN_DESKBAR
653 DeskbarMountMenu::DeskbarMountMenu(const char* name)
654 	: BPrivate::MountMenu(name)
655 {
656 	SetFont(be_plain_font);
657 }
658 
659 
660 bool
661 DeskbarMountMenu::AddDynamicItem(add_state s)
662 {
663 	BPrivate::MountMenu::AddDynamicItem(s);
664 
665 	SetTargetForItems(BMessenger(kTrackerSignature));
666 
667 	return false;
668 }
669 #endif	// MOUNT_MENU_IN_DESKBAR
670