Tracker: Reimplement BSlowContextPopup as BPopUpNavMenu.

BSlowContextPopup was mostly a duplicate class of BNavMenu
only on top of BPopUpMenu instead of BMenu. Now, BPopUpNavMenu
just subclasses BNavMenu and adds the few features of BPopUpMenu
neccessary.

Drag-and-drop of files using the pop-up nav menu seems to still work fine.

Change-Id: Ic1f49c5bed60fff7a3076a22f74aebc6eba51d57
This commit is contained in:
Augustin Cavalier 2023-08-30 17:04:16 -04:00
parent 18ee9c8397
commit 8e8c7a073f
7 changed files with 107 additions and 707 deletions

View File

@ -148,6 +148,30 @@ protected:
TrackingHookData fTrackingHook;
};
class BPopUpNavMenu : public BNavMenu {
public:
BPopUpNavMenu(const char* title);
~BPopUpNavMenu();
void ClearMenu();
void Go(BPoint where);
bool IsShowing() const;
protected:
BPoint ScreenLocation();
private:
void _WaitForTrackThread();
static int32 _TrackThread(void* menu);
private:
BPoint fWhere;
thread_id fTrackThread;
};
// Spring Loaded Folder convenience routines
// used in both Tracker and Deskbar
#ifndef _IMPEXP_TRACKER

View File

@ -865,7 +865,7 @@ BContainerWindow::AddContextMenus()
fDropContextMenu = new BPopUpMenu("DropContext", false, false);
AddDropContextMenus(fDropContextMenu);
fDragContextMenu = new BSlowContextMenu("DragContext");
fDragContextMenu = new BPopUpNavMenu("DragContext");
// will get added and built dynamically in ShowContextMenu
fTrashContextMenu = new BPopUpMenu("TrashContext", false, false);
@ -2849,7 +2849,7 @@ BContainerWindow::ShowContextMenu(BPoint where, const entry_ref* ref)
// this is now asynchronous so that we don't
// deadlock in Window::Quit,
fDragContextMenu->Go(global, true, false, true);
fDragContextMenu->Go(global);
}
return;

View File

@ -41,7 +41,7 @@ All rights reserved.
#include "LockingList.h"
#include "Model.h"
#include "SlowContextPopup.h"
#include "NavMenu.h"
#include "TaskLoop.h"
@ -294,7 +294,7 @@ protected:
BPopUpMenu* fDropContextMenu;
BPopUpMenu* fVolumeContextMenu;
BPopUpMenu* fTrashContextMenu;
BSlowContextMenu* fDragContextMenu;
BPopUpNavMenu* fDragContextMenu;
BMenuItem* fMoveToItem;
BMenuItem* fCopyToItem;
BMenuItem* fCreateLinkItem;

View File

@ -73,7 +73,6 @@ for architectureObject in [ MultiArchSubDirSetup ] {
SelectionWindow.cpp
Settings.cpp
SettingsViews.cpp
SlowContextPopup.cpp
SlowMenu.cpp
StatusWindow.cpp
TaskLoop.cpp
@ -137,7 +136,6 @@ DoCatalogs libtracker.so :
RegExp.cpp
SelectionWindow.cpp
SettingsViews.cpp
SlowContextPopup.cpp
StatusWindow.cpp
TemplatesMenu.cpp
TextWidget.cpp

View File

@ -900,3 +900,82 @@ BNavMenu::SetTrackingHookDeep(BMenu* menu, bool (*func)(BMenu*, void*),
SetTrackingHookDeep(submenu, func, state);
}
}
// #pragma mark - BPopUpNavMenu
BPopUpNavMenu::BPopUpNavMenu(const char* title)
:
BNavMenu(title, B_REFS_RECEIVED, BMessenger(), NULL, NULL),
fTrackThread(-1)
{
}
BPopUpNavMenu::~BPopUpNavMenu()
{
_WaitForTrackThread();
}
void
BPopUpNavMenu::_WaitForTrackThread()
{
if (fTrackThread >= 0) {
status_t status;
while (wait_for_thread(fTrackThread, &status) == B_INTERRUPTED)
;
}
}
void
BPopUpNavMenu::ClearMenu()
{
RemoveItems(0, CountItems(), true);
fMenuBuilt = false;
}
void
BPopUpNavMenu::Go(BPoint where)
{
_WaitForTrackThread();
fWhere = where;
fTrackThread = spawn_thread(_TrackThread, "popup", B_DISPLAY_PRIORITY, this);
}
bool
BPopUpNavMenu::IsShowing() const
{
return Window() != NULL && !Window()->IsHidden();
}
BPoint
BPopUpNavMenu::ScreenLocation()
{
return fWhere;
}
int32
BPopUpNavMenu::_TrackThread(void* _menu)
{
BPopUpNavMenu* menu = static_cast<BPopUpNavMenu*>(_menu);
menu->Show();
BMenuItem* result = menu->Track();
if (result != NULL)
static_cast<BInvoker*>(result)->Invoke();
menu->Hide();
return 0;
}

View File

@ -1,562 +0,0 @@
/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#include <string.h>
#include <stdlib.h>
#include <Debug.h>
#include <Application.h>
#include <Catalog.h>
#include <Directory.h>
#include <Locale.h>
#include <Path.h>
#include <Query.h>
#include <StopWatch.h>
#include <VolumeRoster.h>
#include <Volume.h>
#include "Attributes.h"
#include "Commands.h"
#include "ContainerWindow.h"
#include "DesktopPoseView.h"
#include "FSUtils.h"
#include "FunctionObject.h"
#include "IconMenuItem.h"
#include "PoseView.h"
#include "QueryPoseView.h"
#include "SlowContextPopup.h"
#include "Thread.h"
#include "Tracker.h"
#include "VirtualDirectoryEntryList.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "SlowContextPopup"
// #pragma mark - BSlowContextMenu
BSlowContextMenu::BSlowContextMenu(const char* title)
:
BPopUpMenu(title, false, false),
fMenuBuilt(false),
fMessage(B_REFS_RECEIVED),
fParentWindow(NULL),
fVolsOnly(false),
fItemList(NULL),
fContainer(NULL),
fIteratingDesktop(false),
fTypesList(NULL),
fIsShowing(false)
{
InitIconPreloader();
SetFont(be_plain_font);
SetTriggersEnabled(false);
}
BSlowContextMenu::~BSlowContextMenu()
{
}
void
BSlowContextMenu::AttachedToWindow()
{
// showing flag is set immediately as
// it may take a while to build the menu's
// contents.
//
// it should get set only once when Go is called
// and will get reset in DetachedFromWindow
//
// this flag is used in ContainerWindow::ShowContextMenu
// to determine whether we should show this menu, and
// the only reason we need to do this is because this
// menu is spawned ::Go as an asynchronous menu, which
// is done because we will deadlock if the target's
// window is open... so there
fIsShowing = true;
BPopUpMenu::AttachedToWindow();
SpringLoadedFolderSetMenuStates(this, fTypesList);
// allow an opportunity to reset the target for each of the items
SetTargetForItems(Target());
}
void
BSlowContextMenu::DetachedFromWindow()
{
// see note above in AttachedToWindow
fIsShowing = false;
// does this need to set this to null?
// the parent, handling dnd should set this
// appropriately
//
// if this changes, BeMenu and RecentsMenu
// in Deskbar should also change
fTypesList = NULL;
}
void
BSlowContextMenu::SetNavDir(const entry_ref* ref)
{
ForceRebuild();
// reset the slow menu building mechanism so we can add more stuff
fNavDir = *ref;
}
void
BSlowContextMenu::ForceRebuild()
{
ClearMenuBuildingState();
fMenuBuilt = false;
}
bool
BSlowContextMenu::NeedsToRebuild() const
{
return !fMenuBuilt;
}
void
BSlowContextMenu::ClearMenu()
{
RemoveItems(0, CountItems(), true);
fMenuBuilt = false;
}
void
BSlowContextMenu::ClearMenuBuildingState()
{
delete fContainer;
fContainer = NULL;
// item list is non-owning, need to delete the items because
// they didn't get added to the menu
if (fItemList) {
RemoveItems(0, fItemList->CountItems(), true);
delete fItemList;
fItemList = NULL;
}
}
const int32 kItemsToAddChunk = 20;
const bigtime_t kMaxTimeBuildingMenu = 200000;
bool
BSlowContextMenu::AddDynamicItem(add_state state)
{
if (fMenuBuilt)
return false;
if (state == B_ABORT) {
ClearMenuBuildingState();
return false;
}
if (state == B_INITIAL_ADD && !StartBuildingItemList()) {
ClearMenuBuildingState();
return false;
}
bigtime_t timeToBail = system_time() + kMaxTimeBuildingMenu;
for (int32 count = 0; count < kItemsToAddChunk; count++) {
if (!AddNextItem()) {
fMenuBuilt = true;
DoneBuildingItemList();
ClearMenuBuildingState();
return false;
// done with menu, don't call again
}
if (system_time() > timeToBail)
// we have been in here long enough, come back later
break;
}
return true;
// call me again, got more to show
}
bool
BSlowContextMenu::StartBuildingItemList()
{
// return false when done building
BEntry entry;
if (fNavDir.device < 0 || entry.SetTo(&fNavDir) != B_OK
|| !entry.Exists()) {
return false;
}
fItemList = new BObjectList<BMenuItem>(50);
fIteratingDesktop = false;
BDirectory parent;
status_t err = entry.GetParent(&parent);
// if ref is the root item then build list of volume root dirs
fVolsOnly = (err == B_ENTRY_NOT_FOUND);
if (fVolsOnly)
return true;
Model startModel(&entry, true);
if (startModel.InitCheck() != B_OK || !startModel.IsContainer())
return false;
if (startModel.IsQuery()) {
fContainer = new QueryEntryListCollection(&startModel);
} else if (startModel.IsVirtualDirectory()) {
fContainer = new VirtualDirectoryEntryList(&startModel);
} else if (startModel.IsDesktop()) {
fIteratingDesktop = true;
fContainer = DesktopPoseView::InitDesktopDirentIterator(0,
startModel.EntryRef());
AddRootItemsIfNeeded();
AddTrashItem();
} else {
BDirectory* directory = dynamic_cast<BDirectory*>(
startModel.Node());
ASSERT(directory != NULL);
if (directory != NULL)
fContainer = new DirectoryEntryList(*directory);
}
if (fContainer->InitCheck() != B_OK)
return false;
fContainer->Rewind();
return true;
}
void
BSlowContextMenu::AddRootItemsIfNeeded()
{
BVolumeRoster roster;
roster.Rewind();
BVolume volume;
while (roster.GetNextVolume(&volume) == B_OK) {
BDirectory root;
BEntry entry;
if (!volume.IsPersistent()
|| volume.GetRootDirectory(&root) != B_OK
|| root.GetEntry(&entry) != B_OK) {
continue;
}
Model model(&entry);
AddOneItem(&model);
}
}
void
BSlowContextMenu::AddTrashItem()
{
BPath path;
if (find_directory(B_TRASH_DIRECTORY, &path) == B_OK) {
BEntry entry(path.Path());
Model model(&entry);
AddOneItem(&model);
}
}
bool
BSlowContextMenu::AddNextItem()
{
if (fVolsOnly) {
BuildVolumeMenu();
return false;
}
// limit nav menus to 500 items only
if (fItemList->CountItems() > 500)
return false;
BEntry entry;
if (fContainer->GetNextEntry(&entry) != B_OK)
// we're finished
return false;
Model model(&entry, true);
if (model.InitCheck() != B_OK) {
// PRINT(("not showing hidden item %s, wouldn't open\n", model->Name()));
return true;
}
PoseInfo poseInfo;
if (model.Node()) {
model.Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0,
&poseInfo, sizeof(poseInfo));
}
model.CloseNode();
if (!BPoseView::PoseVisible(&model, &poseInfo)) {
return true;
}
AddOneItem(&model);
return true;
}
void
BSlowContextMenu::AddOneItem(Model* model)
{
BMenuItem* item = NewModelItem(model, &fMessage, fMessenger, false,
dynamic_cast<BContainerWindow*>(fParentWindow), fTypesList,
&fTrackingHook);
if (item != NULL)
fItemList->AddItem(item);
}
ModelMenuItem*
BSlowContextMenu::NewModelItem(Model* model, const BMessage* invokeMessage,
const BMessenger &target, bool suppressFolderHierarchy,
BContainerWindow* parentWindow, const BObjectList<BString>* typeslist,
TrackingHookData* hook)
{
if (model->InitCheck() != B_OK)
return NULL;
entry_ref ref;
bool container = false;
if (model->IsSymLink()) {
Model* newResolvedModel = NULL;
Model* result = model->LinkTo();
if (!result) {
newResolvedModel = new Model(model->EntryRef(), true, true);
if (newResolvedModel->InitCheck() != B_OK) {
// broken link, still can show though, bail
delete newResolvedModel;
newResolvedModel = NULL;
}
result = newResolvedModel;
}
if (result) {
BModelOpener opener(result);
// open the model, if it ain't open already
PoseInfo poseInfo;
if (result->Node()) {
result->Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0,
&poseInfo, sizeof(poseInfo));
}
result->CloseNode();
ref = *result->EntryRef();
container = result->IsContainer();
}
model->SetLinkTo(result);
} else {
ref = *model->EntryRef();
container = model->IsContainer();
}
BMessage* message = new BMessage(*invokeMessage);
message->AddRef("refs", model->EntryRef());
// Truncate the name if necessary
BString truncatedString(model->Name());
be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END,
BNavMenu::GetMaxMenuWidth());
ModelMenuItem* item = NULL;
if (!container || suppressFolderHierarchy) {
item = new ModelMenuItem(model, truncatedString.String(), message);
if (invokeMessage->what != B_REFS_RECEIVED)
item->SetEnabled(false);
} else {
BNavMenu* menu = new BNavMenu(truncatedString.String(),
invokeMessage->what, target, parentWindow, typeslist);
menu->SetNavDir(&ref);
if (hook)
menu->InitTrackingHook(hook->fTrackingHook, &(hook->fTarget),
hook->fDragMessage);
item = new ModelMenuItem(model, menu);
item->SetMessage(message);
}
return item;
}
void
BSlowContextMenu::BuildVolumeMenu()
{
BVolumeRoster roster;
BVolume volume;
roster.Rewind();
while (roster.GetNextVolume(&volume) == B_OK) {
if (!volume.IsPersistent())
continue;
BDirectory startDir;
if (volume.GetRootDirectory(&startDir) == B_OK) {
BEntry entry;
startDir.GetEntry(&entry);
Model* model = new Model(&entry);
if (model->InitCheck() != B_OK) {
delete model;
continue;
}
BNavMenu* menu = new BNavMenu(model->Name(), fMessage.what,
fMessenger, fParentWindow, fTypesList);
menu->SetNavDir(model->EntryRef());
menu->InitTrackingHook(fTrackingHook.fTrackingHook,
&(fTrackingHook.fTarget), fTrackingHook.fDragMessage);
ASSERT(menu->Name());
ModelMenuItem* item = new ModelMenuItem(model, menu);
BMessage* message = new BMessage(fMessage);
message->AddRef("refs", model->EntryRef());
item->SetMessage(message);
fItemList->AddItem(item);
ASSERT(item->Label());
}
}
}
void
BSlowContextMenu::DoneBuildingItemList()
{
// add sorted items to menu
if (TrackerSettings().SortFolderNamesFirst())
fItemList->SortItems(&BNavMenu::CompareFolderNamesFirstOne);
else
fItemList->SortItems(&BNavMenu::CompareOne);
int32 count = fItemList->CountItems();
for (int32 index = 0; index < count; index++)
AddItem(fItemList->ItemAt(index));
fItemList->MakeEmpty();
if (!count) {
BMenuItem* item = new BMenuItem(B_TRANSLATE("Empty folder"), 0);
item->SetEnabled(false);
AddItem(item);
}
SetTargetForItems(fMessenger);
}
void
BSlowContextMenu::SetTypesList(const BObjectList<BString>* list)
{
fTypesList = list;
}
void
BSlowContextMenu::SetTarget(const BMessenger &target)
{
fMessenger = target;
}
TrackingHookData*
BSlowContextMenu::InitTrackingHook(bool (*hook)(BMenu*, void*),
const BMessenger* target, const BMessage* dragMessage)
{
fTrackingHook.fTrackingHook = hook;
if (target)
fTrackingHook.fTarget = *target;
fTrackingHook.fDragMessage = dragMessage;
SetTrackingHookDeep(this, hook, &fTrackingHook);
return &fTrackingHook;
}
void
BSlowContextMenu::SetTrackingHookDeep(BMenu* menu,
bool (*func)(BMenu*, void*), void* state)
{
menu->SetTrackingHook(func, state);
int32 count = menu->CountItems();
for (int32 index = 0; index < count; index++) {
BMenuItem* item = menu->ItemAt(index);
if (!item)
continue;
BMenu* submenu = item->Submenu();
if (submenu)
SetTrackingHookDeep(submenu, func, state);
}
}

View File

@ -1,139 +0,0 @@
/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#ifndef _SLOW_CONTEXT_POPUP_H
#define _SLOW_CONTEXT_POPUP_H
#include <PopUpMenu.h>
#include "NavMenu.h"
namespace BPrivate {
class BSlowContextMenu : public BPopUpMenu {
public:
BSlowContextMenu(const char* title);
virtual ~BSlowContextMenu();
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
void SetNavDir(const entry_ref*);
void ClearMenu();
void ForceRebuild();
bool NeedsToRebuild() const;
// will cause menu to get rebuilt next time it is shown
void SetTarget(const BMessenger &);
const BMessenger Target() const;
void SetTypesList(const BObjectList<BString>* list);
const BObjectList<BString>* TypesList() const;
static ModelMenuItem* NewModelItem(Model*, const BMessage*,
const BMessenger&, bool suppressFolderHierarchy = false,
BContainerWindow* = NULL,
const BObjectList<BString>* typeslist = NULL,
TrackingHookData* hook = NULL);
TrackingHookData* InitTrackingHook(bool (*)(BMenu*, void*),
const BMessenger* target, const BMessage* dragMessage);
const bool IsShowing() const;
protected:
virtual bool AddDynamicItem(add_state state);
virtual bool StartBuildingItemList();
virtual bool AddNextItem();
virtual void DoneBuildingItemList();
virtual void ClearMenuBuildingState();
void BuildVolumeMenu();
void AddOneItem(Model*);
void AddRootItemsIfNeeded();
void AddTrashItem();
static void SetTrackingHookDeep(BMenu*, bool (*)(BMenu*, void*), void*);
bool fMenuBuilt;
private:
entry_ref fNavDir;
BMessage fMessage;
BMessenger fMessenger;
BWindow* fParentWindow;
// menu building state
bool fVolsOnly;
BObjectList<BMenuItem>* fItemList;
EntryListBase* fContainer;
bool fIteratingDesktop;
const BObjectList<BString>* fTypesList;
TrackingHookData fTrackingHook;
bool fIsShowing;
// see note in AttachedToWindow
};
} // namespace BPrivate
using namespace BPrivate;
inline const BObjectList<BString>*
BSlowContextMenu::TypesList() const
{
return fTypesList;
}
inline const BMessenger
BSlowContextMenu::Target() const
{
return fMessenger;
}
inline const bool
BSlowContextMenu::IsShowing() const
{
return fIsShowing;
}
#endif // _SLOW_CONTEXT_POPUP_H