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