From d058a4aed272a9fe79ef9e8443fad3bb5352e433 Mon Sep 17 00:00:00 2001 From: Philippe Saint-Pierre Date: Thu, 11 Jul 2013 12:32:19 -0400 Subject: [PATCH] Tracker: store default add-ons shortcuts in resource * Default shortcuts for add-ons are now stored within the binary as a resource (it was previously appended to the file name, as Open Terminal-T, for example) * Use ~/config/shortcuts_settings to override those default shortcuts (editable with Shortcuts preflet) * Tracker avoid rescanning the add-ons directories when unnecessary * Monitor the shortcuts_settings to apply changes on the fly * Fallback to default shortcuts whenever appropriate (settings file deleted, etc.) * Should fix #4446 (with resource rather than attributes) --- src/kits/tracker/ContainerWindow.cpp | 184 ++++------------- src/kits/tracker/ContainerWindow.h | 12 +- src/kits/tracker/DeskWindow.cpp | 299 ++++++++++++++++++++++----- src/kits/tracker/DeskWindow.h | 14 +- src/kits/tracker/Jamfile | 5 +- 5 files changed, 308 insertions(+), 206 deletions(-) diff --git a/src/kits/tracker/ContainerWindow.cpp b/src/kits/tracker/ContainerWindow.cpp index c2380b20c0..3721538173 100644 --- a/src/kits/tracker/ContainerWindow.cpp +++ b/src/kits/tracker/ContainerWindow.cpp @@ -145,6 +145,8 @@ const int32 kWindowStaggerBy = 17; BRect BContainerWindow::sNewWindRect(85, 50, 548, 280); +LockingList* BContainerWindow::fAddonsList + = new LockingList(10, true); namespace BPrivate { @@ -165,48 +167,6 @@ ActivateWindowFilter(BMessage*, BHandler** target, BMessageFilter*) } -static void -StripShortcut(const Model* model, char* result, uint32 &shortcut) -{ - // model name (possibly localized) for the menu item label - strlcpy(result, model->Name(), B_FILE_NAME_LENGTH); - - // check if there is a shortcut in the model name - uint32 length = strlen(result); - if (length > 2 && result[length - 2] == '-') { - shortcut = result[length - 1]; - result[length - 2] = '\0'; - return; - } - - // check if there is a shortcut in the filename - char* refName = model->EntryRef()->name; - length = strlen(refName); - if (length > 2 && refName[length - 2] == '-') { - shortcut = refName[length - 1]; - return; - } - - shortcut = '\0'; -} - - -static const Model* -MatchOne(const Model* model, void* castToName) -{ - char buffer[B_FILE_NAME_LENGTH]; - uint32 dummy; - StripShortcut(model, buffer, dummy); - - if (strcmp(buffer, (const char*)castToName) == 0) { - // found match, bail out - return model; - } - - return 0; -} - - int CompareLabels(const BMenuItem* item1, const BMenuItem* item2) { @@ -217,8 +177,8 @@ CompareLabels(const BMenuItem* item1, const BMenuItem* item2) static bool -AddOneAddon(const Model* model, const char* name, uint32 shortcut, - bool primary, void* context) +AddOneAddon(const Model* model, const char* name, uint32 shortcut, + uint32 modifiers, bool primary, void* context) { AddOneAddonParams* params = (AddOneAddonParams*)context; @@ -226,7 +186,7 @@ AddOneAddon(const Model* model, const char* name, uint32 shortcut, message->AddRef("refs", model->EntryRef()); ModelMenuItem* item = new ModelMenuItem(model, name, message, - (char)shortcut, B_OPTION_KEY); + (char)shortcut, modifiers); if (primary) params->primaryList->AddItem(item); @@ -2919,114 +2879,56 @@ BContainerWindow::AddTrashContextMenus(BMenu* menu) void BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*, - uint32 shortcut, bool primary, void* context), void* passThru, - BObjectList &mimeTypes) + uint32 shortcut, uint32 modifiers, bool primary, void* context), + void* passThru, BObjectList &mimeTypes) { - BObjectList uniqueList(10, true); - BPath path; - bool bail = false; - if (find_directory(B_BEOS_ADDONS_DIRECTORY, &path) == B_OK) - bail = EachAddon(path, eachAddon, &uniqueList, passThru, mimeTypes); + AutoLock > lock(fAddonsList); + if (lock.IsLocked()) { + for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) { + struct AddonShortcut* item = fAddonsList->ItemAt(i); + bool primary = false; - if (!bail && find_directory(B_USER_ADDONS_DIRECTORY, &path) == B_OK) - bail = EachAddon(path, eachAddon, &uniqueList, passThru, mimeTypes); + if (mimeTypes.CountItems()) { + BFile file(item->model->EntryRef(), B_READ_ONLY); + if (file.InitCheck() == B_OK) { + BAppFileInfo info(&file); + if (info.InitCheck() == B_OK) { + bool secondary = true; - if (!bail && find_directory(B_COMMON_ADDONS_DIRECTORY, &path) == B_OK) - EachAddon(path, eachAddon, &uniqueList, passThru, mimeTypes); -} + // does this add-on has types set at all? + BMessage message; + if (info.GetSupportedTypes(&message) == B_OK) { + type_code type; + int32 count; + if (message.GetInfo("types", &type, + &count) == B_OK) + secondary = false; + } - -bool -BContainerWindow::EachAddon(BPath &path, bool (*eachAddon)(const Model*, - const char*, uint32 shortcut, bool primary, void*), - BObjectList* uniqueList, void* params, - BObjectList &mimeTypes) -{ - path.Append("Tracker"); - - BDirectory dir; - BEntry entry; - - if (dir.SetTo(path.Path()) != B_OK) - return false; - - dir.Rewind(); - while (dir.GetNextEntry(&entry) == B_OK) { - Model* model = new Model(&entry); - - if (model->InitCheck() == B_OK && model->IsSymLink()) { - // resolve symlinks - Model* resolved = new Model(model->EntryRef(), true, true); - if (resolved->InitCheck() == B_OK) - model->SetLinkTo(resolved); - else - delete resolved; - } - if (model->InitCheck() != B_OK - || !model->ResolveIfLink()->IsExecutable()) { - delete model; - continue; - } - - // check if it supports at least one of the selected entries - - bool primary = false; - - if (mimeTypes.CountItems()) { - BFile file(&entry, B_READ_ONLY); - if (file.InitCheck() == B_OK) { - BAppFileInfo info(&file); - if (info.InitCheck() == B_OK) { - bool secondary = true; - - // does this add-on has types set at all? - BMessage message; - if (info.GetSupportedTypes(&message) == B_OK) { - type_code type; - int32 count; - if (message.GetInfo("types", &type, &count) == B_OK) - secondary = false; - } - - // check all supported types if it has some set - if (!secondary) { - for (int32 i = mimeTypes.CountItems(); - !primary && i-- > 0;) { - BString* type = mimeTypes.ItemAt(i); - if (info.IsSupportedType(type->String())) { - BMimeType mimeType(type->String()); - if (info.Supports(&mimeType)) - primary = true; - else - secondary = true; + // check all supported types if it has some set + if (!secondary) { + for (int32 i = mimeTypes.CountItems(); + !primary && i-- > 0;) { + BString* type = mimeTypes.ItemAt(i); + if (info.IsSupportedType(type->String())) { + BMimeType mimeType(type->String()); + if (info.Supports(&mimeType)) + primary = true; + else + secondary = true; + } } } - } - if (!secondary && !primary) { - delete model; - continue; + if (!secondary && !primary) + continue; } } } + ((eachAddon)(item->model, item->model->Name(), item->key, + item->modifiers, primary, passThru)); } - - char name[B_FILE_NAME_LENGTH]; - uint32 key; - StripShortcut(model, name, key); - - // do a uniqueness check - if (uniqueList->EachElement(MatchOne, name)) { - // found one already in the list - delete model; - continue; - } - uniqueList->AddItem(model); - - if ((eachAddon)(model, name, key, primary, params)) - return true; } - return false; } diff --git a/src/kits/tracker/ContainerWindow.h b/src/kits/tracker/ContainerWindow.h index 138d4f6afe..6935c247ad 100644 --- a/src/kits/tracker/ContainerWindow.h +++ b/src/kits/tracker/ContainerWindow.h @@ -72,6 +72,13 @@ enum { kRestoreDecor = 0x4 }; +struct AddonShortcut { + Model* model; + char key; + char defaultKey; + uint32 modifiers; +}; + class BContainerWindow : public BWindow { public: BContainerWindow(LockingList* windowList, @@ -173,7 +180,8 @@ class BContainerWindow : public BWindow { // add-on iteration void EachAddon(bool (*)(const Model*, const char*, uint32 shortcut, - bool primary, void*), void*, BObjectList &); + uint32 modifiers, bool primary, void*), void*, + BObjectList &); BPopUpMenu* ContextMenu(); @@ -291,6 +299,8 @@ class BContainerWindow : public BWindow { uint32 fContainerWindowFlags; BackgroundImage* fBackgroundImage; + static LockingList* fAddonsList; + private: BRect fSavedZoomRect; BRect fPreviousBounds; diff --git a/src/kits/tracker/DeskWindow.cpp b/src/kits/tracker/DeskWindow.cpp index b8ea334441..1584f9b176 100644 --- a/src/kits/tracker/DeskWindow.cpp +++ b/src/kits/tracker/DeskWindow.cpp @@ -38,7 +38,9 @@ All rights reserved. #include #include #include +#include #include +#include #include #include #include @@ -55,6 +57,7 @@ All rights reserved. #include "DeskWindow.h" #include "FSUtils.h" #include "IconMenuItem.h" +#include "KeyInfos.h" #include "MountMenu.h" #include "PoseView.h" #include "Tracker.h" @@ -64,47 +67,120 @@ All rights reserved. const char* kShelfPath = "tracker_shelf"; // replicant support +const char* kShortcutsSettings = "shortcuts_settings"; +const char* kDefaultShortcut = "default_shortcut"; +const uint32 kDefaultModifiers = B_OPTION_KEY | B_COMMAND_KEY; + +static struct AddonShortcut* +MatchOne(struct AddonShortcut* item, void* castToName) +{ + if (strcmp(item->model->Name(), (const char*)castToName) == 0) { + // found match, bail out + return item; + } + + return 0; +} + static void -WatchAddOnDir(directory_which dirName, BDeskWindow* window) +AddOneShortcut(Model* model, char key, uint32 modifiers, BDeskWindow* window) +{ + if (key == '\0') + return; + BMessage* runAddon = new BMessage(kLoadAddOn); + runAddon->AddRef("refs", model->EntryRef()); + window->AddShortcut(key, modifiers, runAddon); +} + + + +static struct AddonShortcut* +RevertToDefault(struct AddonShortcut* item, void* castToWindow) +{ + if (item->key != item->defaultKey || item->modifiers != kDefaultModifiers) { + BDeskWindow* window = static_cast(castToWindow); + if (window != NULL) { + window->RemoveShortcut(item->key, item->modifiers); + item->key = item->defaultKey; + item->modifiers = kDefaultModifiers; + AddOneShortcut(item->model, item->key, item->modifiers, window); + } + } + return 0; +} + + +static struct AddonShortcut* +FindElement(struct AddonShortcut* item, void* castToOther) +{ + Model* other = static_cast(castToOther); + if (*item->model->EntryRef() == *other->EntryRef()) + return item; + + return 0; +} + + +static void +LoadAddOnDir(directory_which dirName, BDeskWindow* window, + LockingList* list) { BPath path; if (find_directory(dirName, &path) == B_OK) { path.Append("Tracker"); + + BDirectory dir; + BEntry entry; + + if (dir.SetTo(path.Path()) != B_OK) + return; + + while (dir.GetNextEntry(&entry) == B_OK) { + Model* model = new Model(&entry); + if (model->InitCheck() == B_OK && model->IsSymLink()) { + // resolve symlinks + Model* resolved = new Model(model->EntryRef(), true, true); + if (resolved->InitCheck() == B_OK) + model->SetLinkTo(resolved); + else + delete resolved; + } + if (model->InitCheck() != B_OK + || !model->ResolveIfLink()->IsExecutable()) { + delete model; + continue; + } + + char* name = strdup(model->Name()); + if (!list->EachElement(MatchOne, name)) { + struct AddonShortcut* item = new struct AddonShortcut; + item->model = model; + + BResources resources(model->ResolveIfLink()->EntryRef()); + size_t size; + char* shortcut = (char*)resources.LoadResource(B_STRING_TYPE, + kDefaultShortcut, &size); + if (shortcut == NULL || strlen(shortcut) > 1) + item->key = '\0'; + else + item->key = shortcut[0]; + AddOneShortcut(model, item->key, kDefaultModifiers, window); + item->defaultKey = item->key; + list->AddItem(item); + } + free(name); + } + BNode node(path.Path()); node_ref nodeRef; node.GetNodeRef(&nodeRef); + TTracker::WatchNode(&nodeRef, B_WATCH_DIRECTORY, window); } } -struct AddOneShortcutParams { - BDeskWindow* window; - std::set* currentAddonShortcuts; -}; - -static bool -AddOneShortcut(const Model* model, const char*, uint32 shortcut, - bool /*primary*/, void* context) -{ - if (!shortcut) - // no shortcut, bail - return false; - - AddOneShortcutParams* params = (AddOneShortcutParams*)context; - BMessage* runAddon = new BMessage(kLoadAddOn); - runAddon->AddRef("refs", model->EntryRef()); - - params->window->AddShortcut(shortcut, B_OPTION_KEY | B_COMMAND_KEY, - runAddon); - params->currentAddonShortcuts->insert(shortcut); - PRINT(("adding new shortcut %c\n", (char)shortcut)); - - return false; -} - - // #pragma mark - #undef B_TRANSLATION_CONTEXT @@ -118,7 +194,8 @@ BDeskWindow::BDeskWindow(LockingList* windowList) | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS, B_ALL_WORKSPACES), fDeskShelf(0), fTrashContextMenu(0), - fShouldUpdateAddonShortcuts(true) + fNodeRef(NULL), + fShortcutsSettings(NULL) { // Add icon view switching shortcuts. These are displayed in the context // menu, although they obviously don't work from those menu items. @@ -173,43 +250,133 @@ BDeskWindow::Init(const BMessage*) if (fDeskShelf) fDeskShelf->SetDisplaysZombies(true); } - - // watch add-on directories so that we can track the addons with - // corresponding shortcuts - WatchAddOnDir(B_USER_ADDONS_DIRECTORY, this); - WatchAddOnDir(B_COMMON_ADDONS_DIRECTORY, this); - WatchAddOnDir(B_SYSTEM_ADDONS_DIRECTORY, this); + InitKeyIndices(); + InitAddonsList(false); + ApplyShortcutPreferences(false); _inherited::Init(); } void -BDeskWindow::MenusBeginning() +BDeskWindow::InitAddonsList(bool update) { - _inherited::MenusBeginning(); - - if (fShouldUpdateAddonShortcuts) { - PRINT(("updating addon shortcuts\n")); - fShouldUpdateAddonShortcuts = false; - - // remove all current addon shortcuts - for (std::set::iterator it= fCurrentAddonShortcuts.begin(); - it != fCurrentAddonShortcuts.end(); it++) { - PRINT(("removing shortcut %c\n", (int)*it)); - RemoveShortcut(*it, B_OPTION_KEY | B_COMMAND_KEY); + AutoLock > lock(fAddonsList); + if (lock.IsLocked()) { + if (update) { + for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) { + AddonShortcut* item = fAddonsList->ItemAt(i); + RemoveShortcut(item->key, B_OPTION_KEY | B_COMMAND_KEY); + } + fAddonsList->MakeEmpty(true); } - fCurrentAddonShortcuts.clear(); + LoadAddOnDir(B_USER_ADDONS_DIRECTORY, this, fAddonsList); + LoadAddOnDir(B_COMMON_ADDONS_DIRECTORY, this, fAddonsList); + LoadAddOnDir(B_SYSTEM_ADDONS_DIRECTORY, this, fAddonsList); + } +} - AddOneShortcutParams params; - params.window = this; - params.currentAddonShortcuts = &fCurrentAddonShortcuts; - BObjectList mimeTypes(10, true); - BuildMimeTypeList(mimeTypes); +void +BDeskWindow::ApplyShortcutPreferences(bool update) +{ + AutoLock > lock(fAddonsList); + if (lock.IsLocked()) { + if (!update) { + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { + BPathMonitor::StartWatching(path.Path(), B_WATCH_STAT | B_WATCH_FILES_ONLY, this); + path.Append(kShortcutsSettings); + fShortcutsSettings = new char[strlen(path.Path()) + 1]; + strcpy(fShortcutsSettings, path.Path()); + } + } - EachAddon(&AddOneShortcut, ¶ms, mimeTypes); + fAddonsList->EachElement(RevertToDefault, this); + + BFile shortcutSettings(fShortcutsSettings, B_READ_ONLY); + BMessage fileMsg; + if (shortcutSettings.InitCheck() != B_OK + || fileMsg.Unflatten(&shortcutSettings) != B_OK) { + fNodeRef = NULL; + return; + } + shortcutSettings.GetNodeRef(fNodeRef); + + int i = 0; + BMessage message; + while (fileMsg.FindMessage("spec", i++, &message) == B_OK) { + + int32 key; + BMessage actMsg; + + if (message.FindInt32("key", &key) == B_OK + && message.FindMessage("act", &actMsg) == B_OK) { + + // only handle shortcuts referring add-ons + BString command; + if (actMsg.FindString("largv", &command) != B_OK) + continue; + BPath path; + bool isInAddons = false; + if (find_directory(B_SYSTEM_ADDONS_DIRECTORY, &path) + == B_OK) { + path.Append("Tracker/"); + isInAddons = command.FindFirst(path.Path()) == 0; + } + if (!isInAddons + && (find_directory(B_COMMON_ADDONS_DIRECTORY, &path) + == B_OK)) { + path.Append("Tracker/"); + isInAddons = command.FindFirst(path.Path()) == 0; + } + if (!isInAddons + && (find_directory(B_USER_ADDONS_DIRECTORY, &path) + == B_OK)) { + path.Append("Tracker/"); + isInAddons = command.FindFirst(path.Path()) == 0; + } + if (!isInAddons) + continue; + + BEntry entry(command); + if (entry.InitCheck() != B_OK) + continue; + + const char* shortcut = GetKeyName(key); + if (strlen(shortcut) != 1) + continue; + + uint32 modifiers = 0; + int32 value; + if (message.FindInt32("mcidx", 0, &value) == B_OK) + modifiers |= (value != 0 ? B_SHIFT_KEY : 0); + + if (message.FindInt32("mcidx", 1, &value) == B_OK) + modifiers |= (value != 0 ? B_CONTROL_KEY : 0); + + if (message.FindInt32("mcidx", 2, &value) == B_OK) + modifiers |= (value != 0 ? B_COMMAND_KEY : 0); + + if (message.FindInt32("mcidx", 3, &value) == B_OK) + modifiers |= (value != 0 ? B_OPTION_KEY : 0); + + if (modifiers == 0) + modifiers = kDefaultModifiers; + + Model model(&entry); + AddonShortcut* item = fAddonsList->EachElement(FindElement, + &model); + if (item != NULL) { + if (item->key != '\0') + RemoveShortcut(item->key, item->modifiers); + item->key = shortcut[0]; + item->modifiers = modifiers; + AddOneShortcut(&model, item->key, item->modifiers, this); + } + } + } } } @@ -228,6 +395,11 @@ BDeskWindow::Quit() fNavigationItem = 0; } + while (fAddonsList->CountItems()) + fAddonsList->RemoveItem(0); + + delete fAddonsList; + delete fTrashContextMenu; fTrashContextMenu = NULL; @@ -482,9 +654,28 @@ BDeskWindow::MessageReceived(BMessage* message) } switch (message->what) { + case B_PATH_MONITOR: + { + const char* path = ""; + if (!(message->FindString("path", &path) == B_OK + && strcmp(path, fShortcutsSettings) == 0)) { + + dev_t device; + ino_t node; + if (fNodeRef == NULL + || message->FindInt32("device", &device) != B_OK + || message->FindInt64("node", &node) != B_OK + || device != fNodeRef->device + || node != fNodeRef->node) + break; + } + ApplyShortcutPreferences(true); + break; + } case B_NODE_MONITOR: PRINT(("will update addon shortcuts\n")); - fShouldUpdateAddonShortcuts = true; + InitAddonsList(true); + ApplyShortcutPreferences(true); break; default: diff --git a/src/kits/tracker/DeskWindow.h b/src/kits/tracker/DeskWindow.h index d83a55a715..8b670aa7ce 100644 --- a/src/kits/tracker/DeskWindow.h +++ b/src/kits/tracker/DeskWindow.h @@ -75,24 +75,20 @@ protected: virtual BPoseView* NewPoseView(Model*, BRect, uint32); virtual void WorkspaceActivated(int32, bool); - virtual void MenusBeginning(); virtual void MessageReceived(BMessage*); private: + void InitAddonsList(bool); + void ApplyShortcutPreferences(bool); + BShelf* fDeskShelf; // shelf for replicant support BPopUpMenu* fTrashContextMenu; BRect fOldFrame; - // in the desktop window addon shortcuts have to be added by AddShortcut - // and we don't always get the MenusBeginning call to check for new - // addons/update the shortcuts -- instead we need to node monitor the - // addon directory and keep a dirty flag that triggers shortcut - // reinstallation - bool fShouldUpdateAddonShortcuts; - std::set fCurrentAddonShortcuts; - // keeps track of which shortcuts are installed for Tracker addons + node_ref* fNodeRef; + char* fShortcutsSettings; typedef BContainerWindow _inherited; }; diff --git a/src/kits/tracker/Jamfile b/src/kits/tracker/Jamfile index 248e9c3ee2..bedab83b0f 100644 --- a/src/kits/tracker/Jamfile +++ b/src/kits/tracker/Jamfile @@ -7,6 +7,8 @@ UsePrivateHeaders interface mount shared storage support tracker ; AddResources libtracker.so : TrackerIcons.rdef libtracker.rdef ; +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src add-ons input_server filters shortcut_catcher ] ; + SubDirC++Flags -D_BUILDING_tracker=1 # -D_INCLUDES_CLASS_DEVICE_MAP=1 @@ -89,7 +91,8 @@ SharedLibrary libtracker.so : VolumeWindow.cpp WidgetAttributeText.cpp - : be translation $(vector_icon_libs) $(TARGET_LIBSTDC++) $(HAIKU_LOCALE_LIBS) libshared.a + : be translation $(vector_icon_libs) $(TARGET_LIBSTDC++) $(HAIKU_LOCALE_LIBS) libshared.a + libshortcuts_shared.a ; DoCatalogs libtracker.so :