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