xref: /haiku/src/apps/deskbar/DeskbarMenu.cpp (revision a127b88ecbfab58f64944c98aa47722a18e363b2)
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->SetFont(be_plain_font);
319 	shutdownMenu->SetTargetForItems(be_app);
320 
321 	BMessage* message = new BMessage(kShutdownSystem);
322 	message->AddBool("confirm", true);
323 	AddItem(new BMenuItem(shutdownMenu, message));
324 
325 	fAddState = kAddingRecents;
326 
327 	return true;
328 }
329 
330 
331 void
332 TDeskbarMenu::ClearMenuBuildingState()
333 {
334 	fAddState = kDone;
335 	fMenuBuilt = false;
336 		// force the menu to get rebuilt each time
337 	BNavMenu::ClearMenuBuildingState();
338 }
339 
340 
341 void
342 TDeskbarMenu::ResetTargets()
343 {
344 	// This method does not recurse into submenus
345 	// and does not affect menu items in submenus.
346 	// (e.g. "Restart System" and "Power Off")
347 
348 	BNavMenu::ResetTargets();
349 
350 	// if we are dragging, set the target to whatever was set
351 	// else set it to the default (Tracker)
352 	if (!fBarView->Dragging())
353 		SetTarget(DefaultTarget());
354 
355 	// now set the target for the menuitems to the currently
356 	// set target, which may or may not be tracker
357 	SetTargetForItems(Target());
358 
359 	for (int32 i = 0; ; i++) {
360 		BMenuItem* item = ItemAt(i);
361 		if (item == NULL)
362 			break;
363 
364 		if (item->Message()) {
365 			switch (item->Message()->what) {
366 				case kFindButton:
367 					item->SetTarget(BMessenger(kTrackerSignature));
368 					break;
369 
370 				case kShowSplash:
371 				case kToggleDraggers:
372 				case kConfigShow:
373 				case kConfigQuit:
374 				case kAlwaysTop:
375 				case kExpandNewTeams:
376 				case kHideLabels:
377 				case kResizeTeamIcons:
378 				case kSortRunningApps:
379 				case kTrackerFirst:
380 				case kRebootSystem:
381 				case kSuspendSystem:
382 				case kShutdownSystem:
383 				case kRealignReplicants:
384 				case kShowHideTime:
385 				case kShowSeconds:
386 				case kShowDayOfWeek:
387 				case kShowTimeZone:
388 				case kGetClockSettings:
389 					item->SetTarget(be_app);
390 					break;
391 			}
392 		}
393 	}
394 }
395 
396 
397 BPoint
398 TDeskbarMenu::ScreenLocation()
399 {
400 	bool vertical = fBarView->Vertical();
401 	int32 expando = fBarView->ExpandoState();
402 	bool left = fBarView->Left();
403 	BPoint point;
404 
405 	BRect rect = Supermenu()->Bounds();
406 	Supermenu()->ConvertToScreen(&rect);
407 
408 	if (vertical && expando && left) {
409 		PRINT(("Left\n"));
410 		point = rect.RightTop() + BPoint(0, 3);
411 	} else if (vertical && expando && !left) {
412 		PRINT(("Right\n"));
413 		point = rect.LeftTop() - BPoint(Bounds().Width(), 0) + BPoint(0, 3);
414 	} else
415 		point = BMenu::ScreenLocation();
416 
417 	return point;
418 }
419 
420 
421 /*static*/
422 BMessenger
423 TDeskbarMenu::DefaultTarget()
424 {
425 	// if Tracker is not available we target the BarApp
426 	BMessenger target(kTrackerSignature);
427 	if (target.IsValid())
428 		return target;
429 
430 	return BMessenger(be_app);
431 }
432 
433 
434 //	#pragma mark -
435 
436 
437 TRecentsMenu::TRecentsMenu(const char* name, TBarView* bar, int32 which,
438 		const char* signature, entry_ref* appRef)
439 	: BNavMenu(name, B_REFS_RECEIVED, TDeskbarMenu::DefaultTarget()),
440 	fWhich(which),
441 	fAppRef(NULL),
442 	fSignature(NULL),
443 	fRecentsCount(0),
444 	fRecentsEnabled(false),
445 	fItemIndex(0),
446 	fBarView(bar)
447 {
448 	TBarApp* app = dynamic_cast<TBarApp*>(be_app);
449 	if (app == NULL)
450 		return;
451 
452 	switch (which) {
453 		case kRecentDocuments:
454 			fRecentsCount = app->Settings()->recentDocsCount;
455 			fRecentsEnabled = app->Settings()->recentDocsEnabled;
456 			break;
457 		case kRecentApplications:
458 			fRecentsCount = app->Settings()->recentAppsCount;
459 			fRecentsEnabled = app->Settings()->recentAppsEnabled;
460 			break;
461 		case kRecentAppDocuments:
462 			fRecentsCount = app->Settings()->recentDocsCount;
463 			fRecentsEnabled = app->Settings()->recentDocsEnabled;
464 			if (signature != NULL)
465 				fSignature = strdup(signature);
466 			if (appRef != NULL)
467 				fAppRef = new entry_ref(*appRef);
468 			break;
469 		case kRecentFolders:
470 			fRecentsCount = app->Settings()->recentFoldersCount;
471 			fRecentsEnabled = app->Settings()->recentFoldersEnabled;
472 			break;
473 	}
474 }
475 
476 
477 TRecentsMenu::~TRecentsMenu()
478 {
479 	delete fAppRef;
480 	free(fSignature);
481 }
482 
483 
484 void
485 TRecentsMenu::DetachedFromWindow()
486 {
487 	// BNavMenu::DetachedFromWindow sets the TypesList to NULL
488 	BMenu::DetachedFromWindow();
489 }
490 
491 
492 bool
493 TRecentsMenu::StartBuildingItemList()
494 {
495 	RemoveItems(0, CountItems(), true);
496 
497 	// !! note: don't call inherited from here
498 	// the navref is not set for this menu
499 	// but it still needs to be a draggable navmenu
500 	// simply return true so that AddNextItem is called
501 	//
502 	// return BNavMenu::StartBuildingItemList();
503 	return true;
504 }
505 
506 
507 bool
508 TRecentsMenu::AddNextItem()
509 {
510 	if (fRecentsCount > 0 && fRecentsEnabled && AddRecents(fRecentsCount))
511 		return true;
512 
513 	fItemIndex = 0;
514 	return false;
515 }
516 
517 
518 bool
519 TRecentsMenu::AddRecents(int32 count)
520 {
521 	if (fItemIndex == 0) {
522 		fRecentList.MakeEmpty();
523 		BRoster roster;
524 
525 		switch (fWhich) {
526 			case kRecentDocuments:
527 				roster.GetRecentDocuments(&fRecentList, count);
528 				break;
529 			case kRecentApplications:
530 				roster.GetRecentApps(&fRecentList, count);
531 				break;
532 			case kRecentAppDocuments:
533 				roster.GetRecentDocuments(&fRecentList, count, NULL,
534 					fSignature);
535 				break;
536 			case kRecentFolders:
537 				roster.GetRecentFolders(&fRecentList, count);
538 				break;
539 			default:
540 				return false;
541 		}
542 	}
543 
544 	for (;;) {
545 		entry_ref ref;
546 		if (fRecentList.FindRef("refs", fItemIndex++, &ref) != B_OK)
547 			break;
548 
549 		if (ref.name && strlen(ref.name) > 0) {
550 			Model model(&ref, true);
551 
552 			if (fWhich != kRecentApplications) {
553 				BMessage* message = new BMessage(B_REFS_RECEIVED);
554 				if (fWhich == kRecentAppDocuments) {
555 					// add application as handler
556 					message->AddRef("handler", fAppRef);
557 				}
558 
559 				ModelMenuItem* item = BNavMenu::NewModelItem(&model,
560 					message, Target(), false, NULL, TypesList());
561 
562 				if (item)
563 					AddItem(item);
564 			} else {
565 				// The application items expand to a list of recent documents
566 				// for that application - so they must be handled extra
567 				BFile file(&ref, B_READ_ONLY);
568 				char signature[B_MIME_TYPE_LENGTH];
569 
570 				BAppFileInfo appInfo(&file);
571 				if (appInfo.InitCheck() != B_OK
572 					|| appInfo.GetSignature(signature) != B_OK)
573 					continue;
574 
575 				ModelMenuItem* item = NULL;
576 				BMessage doc;
577 				be_roster->GetRecentDocuments(&doc, 1, NULL, signature);
578 					// ToDo: check if the documents do exist at all to
579 					//		avoid the creation of the submenu.
580 
581 				if (doc.CountNames(B_REF_TYPE) > 0) {
582 					// create recents menu that will contain the recent docs of
583 					// this app
584 					TRecentsMenu* docs = new TRecentsMenu(model.Name(),
585 						fBarView, kRecentAppDocuments, signature, &ref);
586 					docs->SetTypesList(TypesList());
587 					docs->SetTarget(Target());
588 
589 					item = new ModelMenuItem(&model, docs);
590 				} else
591 					item = new ModelMenuItem(&model, model.Name(), NULL);
592 
593 				if (item) {
594 					// add refs-message so that the recent app can be launched
595 					BMessage* msg = new BMessage(B_REFS_RECEIVED);
596 					msg->AddRef("refs", &ref);
597 					item->SetMessage(msg);
598 					item->SetTarget(Target());
599 
600 					AddItem(item);
601 				}
602 			}
603 
604 			// return true so that we know to reenter this list
605 			return true;
606 		}
607 	}
608 
609 	// return false if we are done with this list
610 	return false;
611 }
612 
613 
614 void
615 TRecentsMenu::DoneBuildingItemList()
616 {
617 	// !! note: don't call inherited here
618 	// the object list is not built
619 	// and this list does not need to be sorted
620 	// BNavMenu::DoneBuildingItemList();
621 
622 	if (CountItems() > 0)
623 		SetTargetForItems(Target());
624 }
625 
626 
627 void
628 TRecentsMenu::ClearMenuBuildingState()
629 {
630 	fMenuBuilt = false;
631 	BNavMenu::ClearMenuBuildingState();
632 }
633 
634 
635 void
636 TRecentsMenu::ResetTargets()
637 {
638 	BNavMenu::ResetTargets();
639 
640 	// if we are dragging, set the target to whatever was set
641 	// else set it to the default (Tracker)
642 	if (!fBarView->Dragging())
643 		SetTarget(TDeskbarMenu::DefaultTarget());
644 
645 	// now set the target for the menuitems to the currently
646 	// set target, which may or may not be tracker
647 	SetTargetForItems(Target());
648 }
649 
650 
651 //	#pragma mark - DeskbarMountMenu
652 
653 
654 #ifdef MOUNT_MENU_IN_DESKBAR
655 DeskbarMountMenu::DeskbarMountMenu(const char* name)
656 	: BPrivate::MountMenu(name)
657 {
658 	SetFont(be_plain_font);
659 }
660 
661 
662 bool
663 DeskbarMountMenu::AddDynamicItem(add_state s)
664 {
665 	BPrivate::MountMenu::AddDynamicItem(s);
666 
667 	SetTargetForItems(BMessenger(kTrackerSignature));
668 
669 	return false;
670 }
671 #endif	// MOUNT_MENU_IN_DESKBAR
672