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
TDeskbarMenu(TBarView * barView)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
AttachedToWindow()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
DetachedFromWindow()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
StartBuildingItemList()133 TDeskbarMenu::StartBuildingItemList()
134 {
135 RemoveItems(0, CountItems(), true);
136 fAddState = kStart;
137 return BNavMenu::StartBuildingItemList();
138 }
139
140
141 void
DoneBuildingItemList()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
AddNextItem()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
AddStandardDeskbarMenuItems()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
ClearMenuBuildingState()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
ResetTargets()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
ScreenLocation()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
DefaultTarget()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
TRecentsMenu(const char * name,TBarView * bar,int32 which,const char * signature,entry_ref * appRef)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
~TRecentsMenu()476 TRecentsMenu::~TRecentsMenu()
477 {
478 delete fAppRef;
479 free(fSignature);
480 }
481
482
483 void
DetachedFromWindow()484 TRecentsMenu::DetachedFromWindow()
485 {
486 // BNavMenu::DetachedFromWindow sets the TypesList to NULL
487 BMenu::DetachedFromWindow();
488 }
489
490
491 bool
StartBuildingItemList()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
AddNextItem()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
AddRecents(int32 count)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
DoneBuildingItemList()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
ClearMenuBuildingState()627 TRecentsMenu::ClearMenuBuildingState()
628 {
629 fMenuBuilt = false;
630 BNavMenu::ClearMenuBuildingState();
631 }
632
633
634 void
ResetTargets()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
DeskbarMountMenu(const char * name)654 DeskbarMountMenu::DeskbarMountMenu(const char* name)
655 : BPrivate::MountMenu(name)
656 {
657 }
658
659
660 bool
AddDynamicItem(add_state s)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