xref: /haiku/src/apps/deskbar/DeskbarMenu.cpp (revision 3634f142352af2428aed187781fc9d75075e9140)
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("Power off"),
298 		new BMessage(kShutdownSystem));
299 	item->SetEnabled(!dragging);
300 	shutdownMenu->AddItem(item);
301 
302 	item = new BMenuItem(B_TRANSLATE("Restart system"),
303 		new BMessage(kRebootSystem));
304 	item->SetEnabled(!dragging);
305 	shutdownMenu->AddItem(item);
306 
307 	B_TRANSLATE_MARK_VOID("Suspend");
308 
309 #ifdef APM_SUPPORT
310 	if (_kapm_control_(APM_CHECK_ENABLED) == B_OK) {
311 		item = new BMenuItem(B_TRANSLATE_NOCOLLECT("Suspend"),
312 			new BMessage(kSuspendSystem));
313 		item->SetEnabled(!dragging);
314 		shutdownMenu->AddItem(item);
315 	}
316 #endif
317 
318 	shutdownMenu->SetTargetForItems(be_app);
319 
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->ExpandoState();
401 	bool left = fBarView->Left();
402 	BPoint point;
403 
404 	BRect rect = Supermenu()->Bounds();
405 	Supermenu()->ConvertToScreen(&rect);
406 
407 	if (vertical && expando && left) {
408 		PRINT(("Left\n"));
409 		point = rect.RightTop() + BPoint(0, 3);
410 	} else if (vertical && expando && !left) {
411 		PRINT(("Right\n"));
412 		point = rect.LeftTop() - BPoint(Bounds().Width(), 0) + BPoint(0, 3);
413 	} else
414 		point = BMenu::ScreenLocation();
415 
416 	return point;
417 }
418 
419 
420 /*static*/
421 BMessenger
422 TDeskbarMenu::DefaultTarget()
423 {
424 	// if Tracker is not available we target the BarApp
425 	BMessenger target(kTrackerSignature);
426 	if (target.IsValid())
427 		return target;
428 
429 	return BMessenger(be_app);
430 }
431 
432 
433 //	#pragma mark -
434 
435 
436 TRecentsMenu::TRecentsMenu(const char* name, TBarView* bar, int32 which,
437 		const char* signature, entry_ref* appRef)
438 	: BNavMenu(name, B_REFS_RECEIVED, TDeskbarMenu::DefaultTarget()),
439 	fWhich(which),
440 	fAppRef(NULL),
441 	fSignature(NULL),
442 	fRecentsCount(0),
443 	fRecentsEnabled(false),
444 	fItemIndex(0),
445 	fBarView(bar)
446 {
447 	TBarApp* app = dynamic_cast<TBarApp*>(be_app);
448 	if (app == NULL)
449 		return;
450 
451 	switch (which) {
452 		case kRecentDocuments:
453 			fRecentsCount = app->Settings()->recentDocsCount;
454 			fRecentsEnabled = app->Settings()->recentDocsEnabled;
455 			break;
456 		case kRecentApplications:
457 			fRecentsCount = app->Settings()->recentAppsCount;
458 			fRecentsEnabled = app->Settings()->recentAppsEnabled;
459 			break;
460 		case kRecentAppDocuments:
461 			fRecentsCount = app->Settings()->recentDocsCount;
462 			fRecentsEnabled = app->Settings()->recentDocsEnabled;
463 			if (signature != NULL)
464 				fSignature = strdup(signature);
465 			if (appRef != NULL)
466 				fAppRef = new entry_ref(*appRef);
467 			break;
468 		case kRecentFolders:
469 			fRecentsCount = app->Settings()->recentFoldersCount;
470 			fRecentsEnabled = app->Settings()->recentFoldersEnabled;
471 			break;
472 	}
473 }
474 
475 
476 TRecentsMenu::~TRecentsMenu()
477 {
478 	delete fAppRef;
479 	free(fSignature);
480 }
481 
482 
483 void
484 TRecentsMenu::DetachedFromWindow()
485 {
486 	// BNavMenu::DetachedFromWindow sets the TypesList to NULL
487 	BMenu::DetachedFromWindow();
488 }
489 
490 
491 bool
492 TRecentsMenu::StartBuildingItemList()
493 {
494 	RemoveItems(0, CountItems(), true);
495 
496 	// !! note: don't call inherited from here
497 	// the navref is not set for this menu
498 	// but it still needs to be a draggable navmenu
499 	// simply return true so that AddNextItem is called
500 	//
501 	// return BNavMenu::StartBuildingItemList();
502 	return true;
503 }
504 
505 
506 bool
507 TRecentsMenu::AddNextItem()
508 {
509 	if (fRecentsCount > 0 && fRecentsEnabled && AddRecents(fRecentsCount))
510 		return true;
511 
512 	fItemIndex = 0;
513 	return false;
514 }
515 
516 
517 bool
518 TRecentsMenu::AddRecents(int32 count)
519 {
520 	if (fItemIndex == 0) {
521 		fRecentList.MakeEmpty();
522 		BRoster roster;
523 
524 		switch (fWhich) {
525 			case kRecentDocuments:
526 				roster.GetRecentDocuments(&fRecentList, count);
527 				break;
528 			case kRecentApplications:
529 				roster.GetRecentApps(&fRecentList, count);
530 				break;
531 			case kRecentAppDocuments:
532 				roster.GetRecentDocuments(&fRecentList, count, NULL,
533 					fSignature);
534 				break;
535 			case kRecentFolders:
536 				roster.GetRecentFolders(&fRecentList, count);
537 				break;
538 			default:
539 				return false;
540 		}
541 	}
542 
543 	for (;;) {
544 		entry_ref ref;
545 		if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK)
546 			break;
547 
548 		if (ref.name && strlen(ref.name) > 0) {
549 			Model model(&ref, true);
550 
551 			if (fWhich != kRecentApplications) {
552 				BMessage* message = new BMessage(B_REFS_RECEIVED);
553 				if (fWhich == kRecentAppDocuments) {
554 					// add application as handler
555 					message->AddRef("handler", fAppRef);
556 				}
557 
558 				ModelMenuItem* item = BNavMenu::NewModelItem(&model,
559 					message, Target(), false, NULL, TypesList());
560 
561 				if (item)
562 					AddItem(item);
563 			} else {
564 				// The application items expand to a list of recent documents
565 				// for that application - so they must be handled extra
566 				BFile file(&ref, B_READ_ONLY);
567 				char signature[B_MIME_TYPE_LENGTH];
568 
569 				BAppFileInfo appInfo(&file);
570 				if (appInfo.InitCheck() != B_OK
571 					|| appInfo.GetSignature(signature) != B_OK)
572 					continue;
573 
574 				ModelMenuItem* item = NULL;
575 				BMessage doc;
576 				be_roster->GetRecentDocuments(&doc, 1, NULL, signature);
577 					// ToDo: check if the documents do exist at all to
578 					//		avoid the creation of the submenu.
579 
580 				if (doc.CountNames(B_REF_TYPE) > 0) {
581 					// create recents menu that will contain the recent docs of
582 					// this app
583 					TRecentsMenu* docs = new TRecentsMenu(model.Name(),
584 						fBarView, kRecentAppDocuments, signature, &ref);
585 					docs->SetTypesList(TypesList());
586 					docs->SetTarget(Target());
587 
588 					item = new ModelMenuItem(&model, docs);
589 				} else
590 					item = new ModelMenuItem(&model, model.Name(), NULL);
591 
592 				if (item) {
593 					// add refs-message so that the recent app can be launched
594 					BMessage* msg = new BMessage(B_REFS_RECEIVED);
595 					msg->AddRef("refs", &ref);
596 					item->SetMessage(msg);
597 					item->SetTarget(Target());
598 
599 					AddItem(item);
600 				}
601 			}
602 
603 			// return true so that we know to reenter this list
604 			return true;
605 		}
606 	}
607 
608 	// return false if we are done with this list
609 	return false;
610 }
611 
612 
613 void
614 TRecentsMenu::DoneBuildingItemList()
615 {
616 	// !! note: don't call inherited here
617 	// the object list is not built
618 	// and this list does not need to be sorted
619 	// BNavMenu::DoneBuildingItemList();
620 
621 	if (CountItems() > 0)
622 		SetTargetForItems(Target());
623 }
624 
625 
626 void
627 TRecentsMenu::ClearMenuBuildingState()
628 {
629 	fMenuBuilt = false;
630 	BNavMenu::ClearMenuBuildingState();
631 }
632 
633 
634 void
635 TRecentsMenu::ResetTargets()
636 {
637 	BNavMenu::ResetTargets();
638 
639 	// if we are dragging, set the target to whatever was set
640 	// else set it to the default (Tracker)
641 	if (!fBarView->Dragging())
642 		SetTarget(TDeskbarMenu::DefaultTarget());
643 
644 	// now set the target for the menuitems to the currently
645 	// set target, which may or may not be tracker
646 	SetTargetForItems(Target());
647 }
648 
649 
650 //	#pragma mark - DeskbarMountMenu
651 
652 
653 #ifdef MOUNT_MENU_IN_DESKBAR
654 DeskbarMountMenu::DeskbarMountMenu(const char* name)
655 	: BPrivate::MountMenu(name)
656 {
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