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