6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
40 #include <Application.h>
42 #include <Directory.h>
46 #include <StopWatch.h>
47 #include <VolumeRoster.h>
50 #include "Attributes.h"
52 #include "ContainerWindow.h"
53 #include "DesktopPoseView.h"
55 #include "FunctionObject.h"
56 #include "IconMenuItem.h"
58 #include "QueryPoseView.h"
59 #include "SlowContextPopup.h"
62 #include "VirtualDirectoryEntryList.h"
65 #undef B_TRANSLATION_CONTEXT
66 #define B_TRANSLATION_CONTEXT "SlowContextPopup"
69 // #pragma mark - BSlowContextMenu
72 BSlowContextMenu::BSlowContextMenu(const char* title
)
74 BPopUpMenu(title
, false, false),
76 fMessage(B_REFS_RECEIVED
),
81 fIteratingDesktop(false),
87 SetFont(be_plain_font
);
88 SetTriggersEnabled(false);
92 BSlowContextMenu::~BSlowContextMenu()
98 BSlowContextMenu::AttachedToWindow()
100 // showing flag is set immediately as
101 // it may take a while to build the menu's
104 // it should get set only once when Go is called
105 // and will get reset in DetachedFromWindow
107 // this flag is used in ContainerWindow::ShowContextMenu
108 // to determine whether we should show this menu, and
109 // the only reason we need to do this is because this
110 // menu is spawned ::Go as an asynchronous menu, which
111 // is done because we will deadlock if the target's
112 // window is open... so there
115 BPopUpMenu::AttachedToWindow();
117 SpringLoadedFolderSetMenuStates(this, fTypesList
);
119 // allow an opportunity to reset the target for each of the items
120 SetTargetForItems(Target());
125 BSlowContextMenu::DetachedFromWindow()
127 // see note above in AttachedToWindow
129 // does this need to set this to null?
130 // the parent, handling dnd should set this
133 // if this changes, BeMenu and RecentsMenu
134 // in Deskbar should also change
140 BSlowContextMenu::SetNavDir(const entry_ref
* ref
)
143 // reset the slow menu building mechanism so we can add more stuff
150 BSlowContextMenu::ForceRebuild()
152 ClearMenuBuildingState();
158 BSlowContextMenu::NeedsToRebuild() const
165 BSlowContextMenu::ClearMenu()
167 RemoveItems(0, CountItems(), true);
174 BSlowContextMenu::ClearMenuBuildingState()
179 // item list is non-owning, need to delete the items because
180 // they didn't get added to the menu
182 RemoveItems(0, fItemList
->CountItems(), true);
189 const int32 kItemsToAddChunk
= 20;
190 const bigtime_t kMaxTimeBuildingMenu
= 200000;
194 BSlowContextMenu::AddDynamicItem(add_state state
)
199 if (state
== B_ABORT
) {
200 ClearMenuBuildingState();
204 if (state
== B_INITIAL_ADD
&& !StartBuildingItemList()) {
205 ClearMenuBuildingState();
209 bigtime_t timeToBail
= system_time() + kMaxTimeBuildingMenu
;
210 for (int32 count
= 0; count
< kItemsToAddChunk
; count
++) {
211 if (!AddNextItem()) {
213 DoneBuildingItemList();
214 ClearMenuBuildingState();
216 // done with menu, don't call again
218 if (system_time() > timeToBail
)
219 // we have been in here long enough, come back later
224 // call me again, got more to show
229 BSlowContextMenu::StartBuildingItemList()
231 // return false when done building
234 if (fNavDir
.device
< 0 || entry
.SetTo(&fNavDir
) != B_OK
235 || !entry
.Exists()) {
239 fIteratingDesktop
= false;
242 status_t err
= entry
.GetParent(&parent
);
243 fItemList
= new BObjectList
<BMenuItem
>(50);
245 // if ref is the root item then build list of volume root dirs
246 fVolsOnly
= (err
== B_ENTRY_NOT_FOUND
);
251 Model
startModel(&entry
, true);
252 if (startModel
.InitCheck() == B_OK
) {
253 if (!startModel
.IsContainer())
256 if (startModel
.IsQuery())
257 fContainer
= new QueryEntryListCollection(&startModel
);
258 else if (startModel
.IsVirtualDirectory())
259 fContainer
= new VirtualDirectoryEntryList(&startModel
);
260 else if (startModel
.IsDesktop()) {
261 fIteratingDesktop
= true;
262 fContainer
= DesktopPoseView::InitDesktopDirentIterator(0,
263 startModel
.EntryRef());
264 AddRootItemsIfNeeded();
267 BDirectory
* directory
= dynamic_cast<BDirectory
*>(
270 ASSERT(directory
!= NULL
);
272 if (directory
!= NULL
)
273 fContainer
= new DirectoryEntryList(*directory
);
276 if (fContainer
->InitCheck() != B_OK
)
279 fContainer
->Rewind();
287 BSlowContextMenu::AddRootItemsIfNeeded()
289 BVolumeRoster roster
;
292 while (roster
.GetNextVolume(&volume
) == B_OK
) {
296 if (!volume
.IsPersistent()
297 || volume
.GetRootDirectory(&root
) != B_OK
298 || root
.GetEntry(&entry
) != B_OK
)
308 BSlowContextMenu::AddTrashItem()
311 if (find_directory(B_TRASH_DIRECTORY
, &path
) == B_OK
) {
312 BEntry
entry(path
.Path());
320 BSlowContextMenu::AddNextItem()
327 // limit nav menus to 500 items only
328 if (fItemList
->CountItems() > 500)
332 if (fContainer
->GetNextEntry(&entry
) != B_OK
)
336 Model
model(&entry
, true);
337 if (model
.InitCheck() != B_OK
) {
338 // PRINT(("not showing hidden item %s, wouldn't open\n", model->Name()));
345 model
.Node()->ReadAttr(kAttrPoseInfo
, B_RAW_TYPE
, 0,
346 &poseInfo
, sizeof(poseInfo
));
351 if (!BPoseView::PoseVisible(&model
, &poseInfo
)) {
361 BSlowContextMenu::AddOneItem(Model
* model
)
363 BMenuItem
* item
= NewModelItem(model
, &fMessage
, fMessenger
, false,
364 dynamic_cast<BContainerWindow
*>(fParentWindow
), fTypesList
,
367 fItemList
->AddItem(item
);
372 BSlowContextMenu::NewModelItem(Model
* model
, const BMessage
* invokeMessage
,
373 const BMessenger
&target
, bool suppressFolderHierarchy
,
374 BContainerWindow
* parentWindow
, const BObjectList
<BString
>* typeslist
,
375 TrackingHookData
* hook
)
377 if (model
->InitCheck() != B_OK
)
381 bool container
= false;
382 if (model
->IsSymLink()) {
384 Model
* newResolvedModel
= NULL
;
385 Model
* result
= model
->LinkTo();
388 newResolvedModel
= new Model(model
->EntryRef(), true, true);
390 if (newResolvedModel
->InitCheck() != B_OK
) {
391 // broken link, still can show though, bail
392 delete newResolvedModel
;
393 newResolvedModel
= NULL
;
396 result
= newResolvedModel
;
400 BModelOpener
opener(result
);
401 // open the model, if it ain't open already
405 if (result
->Node()) {
406 result
->Node()->ReadAttr(kAttrPoseInfo
, B_RAW_TYPE
, 0,
407 &poseInfo
, sizeof(poseInfo
));
412 ref
= *result
->EntryRef();
413 container
= result
->IsContainer();
415 model
->SetLinkTo(result
);
417 ref
= *model
->EntryRef();
418 container
= model
->IsContainer();
421 BMessage
* message
= new BMessage(*invokeMessage
);
422 message
->AddRef("refs", model
->EntryRef());
424 // Truncate the name if necessary
425 BString
truncatedString(model
->Name());
426 be_plain_font
->TruncateString(&truncatedString
, B_TRUNCATE_END
,
427 BNavMenu::GetMaxMenuWidth());
429 ModelMenuItem
* item
= NULL
;
430 if (!container
|| suppressFolderHierarchy
) {
431 item
= new ModelMenuItem(model
, truncatedString
.String(), message
);
432 if (invokeMessage
->what
!= B_REFS_RECEIVED
)
433 item
->SetEnabled(false);
435 BNavMenu
* menu
= new BNavMenu(truncatedString
.String(),
436 invokeMessage
->what
, target
, parentWindow
, typeslist
);
438 menu
->SetNavDir(&ref
);
440 menu
->InitTrackingHook(hook
->fTrackingHook
, &(hook
->fTarget
),
443 item
= new ModelMenuItem(model
, menu
);
444 item
->SetMessage(message
);
452 BSlowContextMenu::BuildVolumeMenu()
454 BVolumeRoster roster
;
458 while (roster
.GetNextVolume(&volume
) == B_OK
) {
460 if (!volume
.IsPersistent())
464 if (volume
.GetRootDirectory(&startDir
) == B_OK
) {
466 startDir
.GetEntry(&entry
);
468 Model
* model
= new Model(&entry
);
469 if (model
->InitCheck() != B_OK
) {
474 BNavMenu
* menu
= new BNavMenu(model
->Name(), fMessage
.what
,
475 fMessenger
, fParentWindow
, fTypesList
);
477 menu
->SetNavDir(model
->EntryRef());
478 menu
->InitTrackingHook(fTrackingHook
.fTrackingHook
,
479 &(fTrackingHook
.fTarget
), fTrackingHook
.fDragMessage
);
481 ASSERT(menu
->Name());
483 ModelMenuItem
* item
= new ModelMenuItem(model
, menu
);
484 BMessage
* message
= new BMessage(fMessage
);
486 message
->AddRef("refs", model
->EntryRef());
487 item
->SetMessage(message
);
488 fItemList
->AddItem(item
);
489 ASSERT(item
->Label());
496 BSlowContextMenu::DoneBuildingItemList()
498 // add sorted items to menu
499 if (TrackerSettings().SortFolderNamesFirst())
500 fItemList
->SortItems(&BNavMenu::CompareFolderNamesFirstOne
);
502 fItemList
->SortItems(&BNavMenu::CompareOne
);
504 int32 count
= fItemList
->CountItems();
505 for (int32 index
= 0; index
< count
; index
++)
506 AddItem(fItemList
->ItemAt(index
));
508 fItemList
->MakeEmpty();
511 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("Empty folder"), 0);
512 item
->SetEnabled(false);
516 SetTargetForItems(fMessenger
);
521 BSlowContextMenu::SetTypesList(const BObjectList
<BString
>* list
)
528 BSlowContextMenu::SetTarget(const BMessenger
&target
)
535 BSlowContextMenu::InitTrackingHook(bool (*hook
)(BMenu
*, void*),
536 const BMessenger
* target
, const BMessage
* dragMessage
)
538 fTrackingHook
.fTrackingHook
= hook
;
540 fTrackingHook
.fTarget
= *target
;
541 fTrackingHook
.fDragMessage
= dragMessage
;
542 SetTrackingHookDeep(this, hook
, &fTrackingHook
);
543 return &fTrackingHook
;
548 BSlowContextMenu::SetTrackingHookDeep(BMenu
* menu
,
549 bool (*func
)(BMenu
*, void*), void* state
)
551 menu
->SetTrackingHook(func
, state
);
552 int32 count
= menu
->CountItems();
553 for (int32 index
= 0; index
< count
; index
++) {
554 BMenuItem
* item
= menu
->ItemAt(index
);
558 BMenu
* submenu
= item
->Submenu();
560 SetTrackingHookDeep(submenu
, func
, state
);