From bc8427c52293af9ae9ac4f4ec9b3e32fe41aa7e5 Mon Sep 17 00:00:00 2001 From: Hrishi Hiraskar Date: Fri, 4 May 2018 18:35:28 +0000 Subject: [PATCH] Tracker: Allow add-ons to dynamically extend popup menu. An add-on may now add custom entries to the popup menu, in order to provide more than one action. Change-Id: Ib4726c0c1e70c59e3ba4d752df24b76cfee1c4a4 --- headers/os/add-ons/tracker/TrackerAddOn.h | 9 +- headers/os/app/AppDefs.h | 1 + src/kits/tracker/ContainerWindow.cpp | 161 +++++++++++++++++++--- src/kits/tracker/ContainerWindow.h | 7 +- 4 files changed, 155 insertions(+), 23 deletions(-) diff --git a/headers/os/add-ons/tracker/TrackerAddOn.h b/headers/os/add-ons/tracker/TrackerAddOn.h index f8e18650a0..d1c2ffff6e 100644 --- a/headers/os/add-ons/tracker/TrackerAddOn.h +++ b/headers/os/add-ons/tracker/TrackerAddOn.h @@ -7,8 +7,13 @@ struct entry_ref; class BMessage; +class BMenu; +class BHandler; -extern "C" void process_refs(entry_ref directory, BMessage* refs, - void* reserved); +extern "C" { + void process_refs(entry_ref directory, BMessage* refs, void* reserved); + void populate_menu(BMessage* msg, BMenu* menu, BHandler* handler); + void message_received(BMessage* msg); +} #endif // _TRACKER_ADDON_H diff --git a/headers/os/app/AppDefs.h b/headers/os/app/AppDefs.h index d48c58580a..0533c327cb 100644 --- a/headers/os/app/AppDefs.h +++ b/headers/os/app/AppDefs.h @@ -69,6 +69,7 @@ enum { B_ZOOM = '_WZM', B_COLORS_UPDATED = '_CLU', B_FONTS_UPDATED = '_FNU', + B_TRACKER_ADDON_MESSAGE = '_TAM', _APP_MENU_ = '_AMN', _BROWSER_MENUS_ = '_BRM', _MENU_EVENT_ = '_MEV', diff --git a/src/kits/tracker/ContainerWindow.cpp b/src/kits/tracker/ContainerWindow.cpp index a95d9d9975..7e7781245e 100644 --- a/src/kits/tracker/ContainerWindow.cpp +++ b/src/kits/tracker/ContainerWindow.cpp @@ -178,9 +178,100 @@ CompareLabels(const BMenuItem* item1, const BMenuItem* item2) } // namespace BPrivate +static int32 +AddOnMenuGenerate(const entry_ref* addonRef, BMenu* menu, + BContainerWindow* window) +{ + BEntry entry(addonRef); + BPath path; + status_t result = entry.InitCheck(); + if (result != B_OK) + return result; + + result = entry.GetPath(&path); + if (result != B_OK) + return result; + + image_id addonImage = load_add_on(path.Path()); + if (addonImage < 0) + return addonImage; + + void (*populateMenu)(BMessage*, BMenu*, BHandler*); + result = get_image_symbol(addonImage, "populate_menu", 2, + (void**)&populateMenu); + if (result < 0) { + PRINT(("Couldn't find populate_menu\n")); + unload_add_on(addonImage); + return result; + } + + BMessage* message = window->AddOnMessage(B_TRACKER_ADDON_MESSAGE); + message->AddRef("addon_ref", addonRef); + + // call add-on code + (*populateMenu)(message, menu, window->PoseView()); + + unload_add_on(addonImage); + return B_OK; +} + + +static status_t +RunAddOnMessageThread(BMessage *message, void *) +{ + entry_ref addonRef; + BEntry entry; + BPath path; + status_t result = message->FindRef("addon_ref", &addonRef); + if (result != B_OK) + goto end; + + entry = BEntry(&addonRef); + result = entry.InitCheck(); + if (result != B_OK) + goto end; + + result = entry.GetPath(&path); + if (result != B_OK) + goto end; + + image_id addonImage = load_add_on(path.Path()); + if (addonImage < 0) { + result = addonImage; + goto end; + } + void (*messageReceived)(BMessage*); + result = get_image_symbol(addonImage, "message_received", 2, + (void**)&messageReceived); + + if (result < 0) { + PRINT(("Couldn't find populate_menu\n")); + unload_add_on(addonImage); + goto end; + } + // call add-on code + (*messageReceived)(message); + unload_add_on(addonImage); + return B_OK; + +end: + BString buffer(B_TRANSLATE("Error %error loading add-On %name.")); + buffer.ReplaceFirst("%error", strerror(result)); + buffer.ReplaceFirst("%name", addonRef.name); + + BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), + 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); + alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); + alert->Go(); + + return result; +} + + static bool AddOneAddon(const Model* model, const char* name, uint32 shortcut, - uint32 modifiers, bool primary, void* context) + uint32 modifiers, bool primary, void* context, + BContainerWindow* window, BMenu* menu) { AddOneAddonParams* params = (AddOneAddonParams*)context; @@ -190,6 +281,9 @@ AddOneAddon(const Model* model, const char* name, uint32 shortcut, ModelMenuItem* item = new ModelMenuItem(model, name, message, (char)shortcut, modifiers); + const entry_ref* addonRef = model->EntryRef(); + AddOnMenuGenerate(addonRef, menu, window); + if (primary) params->primaryList->AddItem(item); else @@ -1613,6 +1707,12 @@ BContainerWindow::MessageReceived(BMessage* message) } break; + case B_TRACKER_ADDON_MESSAGE: + { + _PassMessageToAddOn(message); + break; + } + case B_OBSERVER_NOTICE_CHANGE: { int32 observerWhat; @@ -2981,8 +3081,9 @@ BContainerWindow::AddTrashContextMenus(BMenu* menu) void BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*, - uint32 shortcut, uint32 modifiers, bool primary, void* context), - void* passThru, BStringList& mimeTypes) + uint32 shortcut, uint32 modifiers, bool primary, void* context, + BContainerWindow* window, BMenu* menu), + void* passThru, BStringList& mimeTypes, BMenu* menu) { AutoLock > lock(fAddonsList); if (lock.IsLocked()) { @@ -3029,7 +3130,7 @@ BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*, } } ((eachAddon)(item->model, item->model->Name(), item->key, - item->modifiers, primary, passThru)); + item->modifiers, primary, passThru, this, menu)); } } } @@ -3062,22 +3163,22 @@ BContainerWindow::BuildMimeTypeList(BStringList& mimeTypes) void -BContainerWindow::BuildAddOnMenu(BMenu* menu) +BContainerWindow::BuildAddOnMenu(BMenu* parentMenu) { - BMenuItem* item = menu->FindItem(B_TRANSLATE("Add-ons")); - if (menu->IndexOf(item) == 0) { + BMenuItem* item = parentMenu->FindItem(B_TRANSLATE("Add-ons")); + if (parentMenu->IndexOf(item) == 0) { // the folder of the context menu seems to be named "Add-Ons" // so we just take the last menu item, which is correct if not // build with debug option - item = menu->ItemAt(menu->CountItems() - 1); + item = parentMenu->ItemAt(parentMenu->CountItems() - 1); } if (item == NULL) return; BFont font; - menu->GetFont(&font); + parentMenu->GetFont(&font); - menu = item->Submenu(); + BMenu* menu = item->Submenu(); if (menu == NULL) return; @@ -3102,7 +3203,7 @@ BContainerWindow::BuildAddOnMenu(BMenu* menu) // build a list of the MIME types of the selected items - EachAddon(AddOneAddon, ¶ms, mimeTypes); + EachAddon(AddOneAddon, ¶ms, mimeTypes, parentMenu); primaryList.SortItems(CompareLabels); secondaryList.SortItems(CompareLabels); @@ -3256,6 +3357,26 @@ BContainerWindow::UpdateMenu(BMenu* menu, UpdateMenuContext context) } +BMessage* +BContainerWindow::AddOnMessage(int32 what) +{ + BMessage* message = new BMessage(what); + + // add selected refs to message + BObjectList* selectionList = PoseView()->SelectionList(); + + int32 index = 0; + BPose* pose; + while ((pose = selectionList->ItemAt(index++)) != NULL) + message->AddRef("refs", pose->TargetModel()->EntryRef()); + + message->AddRef("dir_ref", TargetModel()->EntryRef()); + message->AddMessenger("TrackerViewToken", BMessenger(PoseView())); + + return message; +} + + void BContainerWindow::LoadAddOn(BMessage* message) { @@ -3276,15 +3397,7 @@ BContainerWindow::LoadAddOn(BMessage* message) } // add selected refs to message - BMessage* refs = new BMessage(B_REFS_RECEIVED); - BObjectList* selectionList = PoseView()->SelectionList(); - - int32 index = 0; - BPose* pose; - while ((pose = selectionList->ItemAt(index++)) != NULL) - refs->AddRef("refs", pose->TargetModel()->EntryRef()); - - refs->AddMessenger("TrackerViewToken", BMessenger(PoseView())); + BMessage* refs = AddOnMessage(B_REFS_RECEIVED); LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs, addonRef, *TargetModel()->EntryRef()); @@ -3342,6 +3455,14 @@ BContainerWindow::_AddFolderIcon() } +void +BContainerWindow::_PassMessageToAddOn(BMessage* message) +{ + LaunchInNewThread("Add-on-Pass-Message", B_NORMAL_PRIORITY, + &RunAddOnMessageThread, new BMessage(*message), (void*)NULL); +} + + BMenuItem* BContainerWindow::NewAttributeMenuItem(const char* label, const char* name, int32 type, float width, int32 align, bool editable, bool statField) diff --git a/src/kits/tracker/ContainerWindow.h b/src/kits/tracker/ContainerWindow.h index 39fd9a225f..b556f8e082 100644 --- a/src/kits/tracker/ContainerWindow.h +++ b/src/kits/tracker/ContainerWindow.h @@ -189,7 +189,11 @@ public: // add-on iteration void EachAddon(bool (*)(const Model*, const char*, uint32 shortcut, - uint32 modifiers, bool primary, void*), void*, BStringList&); + uint32 modifiers, bool primary, void*, BContainerWindow*, BMenu*), + void*, BStringList&, BMenu*); + + BMessage* AddOnMessage(int32); + entry_ref GetCurrentDirRef(); BPopUpMenu* ContextMenu(); @@ -342,6 +346,7 @@ private: void _UpdateSelectionMIMEInfo(); void _AddFolderIcon(); + void _PassMessageToAddOn(BMessage*); };