[WIP] Normalisation of context menus creation/callbacks.

TODO: UpdateContextMenu()
This commit is contained in:
Lea Anthony 2021-01-07 21:34:04 +11:00
commit f460bf91ef
No known key found for this signature in database
GPG key ID: 33DAF7BB90A58405
8 changed files with 484 additions and 298 deletions

View file

@ -8,6 +8,12 @@
#include "hashmap.h"
#include "vec.h"
#define STREQ(a,b) strcmp(a, b) == 0
#define STRCOPY(a) concat(a, "")
#define STR_HAS_CHARS(input) input != NULL && strlen(input) > 0
#define MEMFREE(input) free((void*)input); input = NULL;
#define FREE_AND_SET(variable, value) if( variable != NULL ) { MEMFREE(variable); } variable = value
// Credit: https://stackoverflow.com/a/8465083
char* concat(const char *string1, const char *string2)
{

View file

@ -0,0 +1,251 @@
////
//// Created by Lea Anthony on 6/1/21.
////
//
#ifndef CONTEXTMENU_DARWIN_H
#define CONTEXTMENU_DARWIN_H
#include "common.h"
#include "menu_darwin.h"
#include "contextmenustore_darwin.h"
typedef struct {
const char* ID;
id nsmenu;
Menu* menu;
} ContextMenu;
ContextMenu* NewContextMenu(JsonNode* menuData, ContextMenuStore *store) {
ContextMenu* result = malloc(sizeof(ContextMenu));
result->menu = NewMenu(menuData);
result->nsmenu = NULL;
result->menu->menuType = ContextMenuType;
result->menu->parentData = store;
return result;
}
ContextMenuStore* NewContextMenuStore(const char* contextMenusAsJSON) {
ContextMenuStore* result = malloc(sizeof(ContextMenuStore));
// Init members
result->contextMenusAsJSON = contextMenusAsJSON;
result->processedContextMenus = NULL;
result->contextMenuData = NULL;
// Allocate Context Menu Store
if( 0 != hashmap_create((const unsigned)4, &result->contextMenuStore)) {
ABORT("[NewContextMenus] Not enough memory to allocate contextMenuStore!");
}
return result;
}
ContextMenu* GetContextMenuByID(ContextMenuStore* store, const char *contextMenuID) {
return (ContextMenu*)hashmap_get(&store->contextMenuStore, (char*)contextMenuID, strlen(contextMenuID));
}
void DeleteContextMenu(ContextMenu* contextMenu) {
// Free Menu
DeleteMenu(contextMenu->menu);
// Free context menu
free(contextMenu);
}
int freeContextMenu(void *const context, struct hashmap_element_s *const e) {
DeleteContextMenu(e->data);
return -1;
}
void DeleteContextMenuStore(ContextMenuStore* store) {
// Delete context menus
if( hashmap_num_entries(&store->contextMenuStore) > 0 ) {
if (0 != hashmap_iterate_pairs(&store->contextMenuStore, freeContextMenu, NULL)) {
ABORT("[DeleteContextMenuStore] Failed to release contextMenuStore entries!");
}
}
// Free context menu hashmap
hashmap_destroy(&store->contextMenuStore);
// Destroy processed Context Menus
if( store->processedContextMenus != NULL) {
json_delete(store->processedContextMenus);
store->processedContextMenus = NULL;
}
// Delete any context menu data we may have stored
if( store->contextMenuData != NULL ) {
MEMFREE(store->contextMenuData);
}
}
void ProcessContextMenus(ContextMenuStore* store) {
// Decode the context menus JSON
store->processedContextMenus = json_decode(store->contextMenusAsJSON);
if( store->processedContextMenus == NULL ) {
ABORT("[ProcessContextMenus] Unable to parse Context Menus JSON: %s", store->contextMenusAsJSON);
}
// // Get the context menu items
// JsonNode *contextMenuItems = json_find_member(store->processedContextMenus, "Items");
// if( contextMenuItems == NULL ) {
// ABORT("[ProcessContextMenus] Unable to find Items in processedContextMenus!");
// }
// Iterate context menus
JsonNode *contextMenu;
json_foreach(contextMenu, store->processedContextMenus) {
const char* ID = getJSONString(contextMenu, "ID");
if ( ID == NULL ) {
ABORT("Unable to read ID of contextMenu\n");
}
JsonNode* processedMenu = json_find_member(contextMenu, "ProcessedMenu");
if ( processedMenu == NULL ) {
ABORT("Unable to read ProcessedMenu of contextMenu\n");
}
// Create a new context menu instance
ContextMenu *thisContextMenu = NewContextMenu(processedMenu, store);
// Store the item in the context menu map
hashmap_put(&store->contextMenuStore, (char*)ID, strlen(ID), thisContextMenu);
}
}
//
//
//bool ContextMenuExists(ContextMenus *contextMenus, const char* contextMenuID) {
// return hashmap_get(&contextMenus->contextMenuStore, contextMenuID, strlen(contextMenuID)) != NULL;
//}
//
//bool AddContextMenu(ContextMenu* contextMenu) {
//
// // Check if we already have this
// if( ContextMenuExists(contextMenu->ID) ) {
// return false;
// }
//
// // Store the context menu
// if (0 != hashmap_put(&contextMenus->contextMenuStore, contextMenu->ID, strlen(contextMenu->ID), contextMenu)) {
// ABORT("Unable to add context menu with ID '%s'", contextMenu->ID);
// }
//
// return true;
//}
//
//ContextMenus* NewContextMenus(const char* contextMenusAsJSON) {
//
// ContextMenus* result = malloc(sizeof(ContextMenus));
//
// // Allocate Context Menu Store
// if( 0 != hashmap_create((const unsigned)4, &result->contextMenuStore)) {
// ABORT("[NewContextMenus] Not enough memory to allocate contextMenuStore!");
// }
//
// //
//
// return result;
//}
//
//void ProcessContextMenus() {
// // Parse the context menu json
// processedContextMenus = json_decode(contextMenusAsJSON);
// if( processedContextMenus == NULL ) {
// // Parse error!
// ABORT("Unable to parse Context Menus JSON: %s", contextMenusAsJSON);
// }
//
// JsonNode *contextMenuItems = json_find_member(processedContextMenus, "Items");
// if( contextMenuItems == NULL ) {
// // Parse error!
// ABORT("Unable to find Items in Context menus");
// }
// // Iterate context menus
// JsonNode *contextMenu;
// json_foreach(contextMenu, contextMenuItems) {
// Menu *contextMenu = NewMenu()
//
// // Store the item in the context menu map
// hashmap_put(&contextMenuMap, (char*)contextMenu->key, strlen(contextMenu->key), menu);
// }
//
//}
//
//ContextMenu* NewContextMenu() {
// ContextMenu* result = malloc(sizeof(ContextMenu));
//
// result->menu = NewMenu(contextMenuAsJSON);
//
// return result;
//}
//
//
//void InitContextMenuStore() {
//
//}
//
//
//void DeleteContextMenuStore() {
// // Free radio group members
// if( hashmap_num_entries(&contextMenuStore) > 0 ) {
// if (0 != hashmap_iterate_pairs(&contextMenuStore, freeContextMenu, NULL)) {
// ABORT("[DeleteContextMenuStore] Failed to release contextMenuStore entries!");
// }
// }
//}
//
void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *contextMenuID, const char *contextMenuData) {
printf("Show context menu '%s'. Also got data '%s'.\n\n", contextMenuID, contextMenuData);
// If no context menu ID was given, abort
if( contextMenuID == NULL ) {
return;
}
ContextMenu* contextMenu = GetContextMenuByID(store, contextMenuID);
// We don't need the ID now
MEMFREE(contextMenuID);
if( contextMenu == NULL ) {
// Free context menu data
if( contextMenuData != NULL ) {}
MEMFREE(contextMenuData);
return;
}
// We need to store the context menu data. Free existing data if we have it
// and set to the new value.
FREE_AND_SET(store->contextMenuData, contextMenuData);
// Grab the content view and show the menu
id contentView = msg(mainWindow, s("contentView"));
// Get the triggering event
id menuEvent = msg(mainWindow, s("currentEvent"));
if( contextMenu->nsmenu == NULL ) {
// GetMenu creates the NSMenu
contextMenu->nsmenu = GetMenu(contextMenu->menu);
}
printf("\n\nContext menu NSMenu = %p\n\n", contextMenu->menu->menu);
// Show popup
msg(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
}
#endif //CONTEXTMENU_DARWIN_H

View file

@ -0,0 +1,24 @@
//
// Created by Lea Anthony on 7/1/21.
//
#ifndef CONTEXTMENUSTORE_DARWIN_H
#define CONTEXTMENUSTORE_DARWIN_H
typedef struct {
// This is our context menu store which keeps track
// of all instances of ContextMenus
struct hashmap_s contextMenuStore;
// The raw JSON defining the context menus
const char* contextMenusAsJSON;
// The optional data that may be passed with a context menu selection
const char* contextMenuData;
// The processed context menus
JsonNode* processedContextMenus;
} ContextMenuStore;
#endif //ASSETS_C_CONTEXTMENUSTORE_DARWIN_H

View file

@ -3,6 +3,7 @@
#include "ffenestri_darwin.h"
#include "menu_darwin.h"
#include "contextmenus_darwin.h"
// References to assets
@ -28,15 +29,6 @@ struct hashmap_s radioGroupMapForTrayMenu;
// A cache for all our tray icons
struct hashmap_s trayIconCache;
// contextMenuMap is a hashmap of context menus keyed on a string ID
struct hashmap_s contextMenuMap;
// MenuItem map for the context menus
struct hashmap_s menuItemMapForContextMenus;
// RadioGroup map for the context menus. Maps a menuitem id with its associated radio group items
struct hashmap_s radioGroupMapForContextMenus;
// A cache for all our dialog icons
struct hashmap_s dialogIconCache;
@ -140,6 +132,7 @@ struct Application {
id statusItem;
// Context Menus
ContextMenuStore *contextMenuStore;
const char *contextMenusAsJSON;
JsonNode *processedContextMenus;
@ -232,40 +225,6 @@ void applyWindowColour(struct Application *app) {
}
}
void showContextMenu(struct Application *app, const char *contextMenuID) {
// If no context menu ID was given
if( contextMenuID == NULL ) {
// Show default context menu if we have one
return;
}
// Look for the context menu for this ID
id contextMenu = (id)hashmap_get(&contextMenuMap, (char*)contextMenuID, strlen(contextMenuID));
if( contextMenu == NULL ) {
// printf("No context menu called '%s'. Available:", contextMenuID);
// dumpHashmap("contextMenuMap", &contextMenuMap);
// Free menu id
MEMFREE(contextMenuID);
return;
}
// Free menu id
MEMFREE(contextMenuID);
// Grab the content view and show the menu
id contentView = msg(app->mainWindow, s("contentView"));
// Get the triggering event
id menuEvent = msg(app->mainWindow, s("currentEvent"));
// Show popup
msg(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu, menuEvent, contentView);
}
void SetColour(struct Application *app, int red, int green, int blue, int alpha) {
app->red = red;
app->green = green;
@ -345,10 +304,12 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
JsonNode *contextMenuIDNode = json_find_member(contextMenuMessageJSON, "id");
if( contextMenuIDNode == NULL ) {
Debug(app, "Error decoding context menu ID: %s", contextMenuMessage);
json_delete(contextMenuMessageJSON);
return;
}
if( contextMenuIDNode->tag != JSON_STRING ) {
Debug(app, "Error decoding context menu ID (Not a string): %s", contextMenuMessage);
json_delete(contextMenuMessageJSON);
return;
}
@ -356,23 +317,26 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
JsonNode *contextMenuDataNode = json_find_member(contextMenuMessageJSON, "data");
if( contextMenuDataNode == NULL ) {
Debug(app, "Error decoding context menu data: %s", contextMenuMessage);
json_delete(contextMenuMessageJSON);
return;
}
if( contextMenuDataNode->tag != JSON_STRING ) {
Debug(app, "Error decoding context menu data (Not a string): %s", contextMenuMessage);
json_delete(contextMenuMessageJSON);
return;
}
// Save a copy of the context menu data
if ( contextMenuData != NULL ) {
MEMFREE(contextMenuData);
}
contextMenuData = STRCOPY(contextMenuDataNode->string_);
// We need to copy these as the JSON node will be destroyed on this thread and the
// string data will become corrupt. These need to be freed by the context menu code.
const char* contextMenuID = STRCOPY(contextMenuIDNode->string_);
const char* contextMenuData = STRCOPY(contextMenuDataNode->string_);
ON_MAIN_THREAD(
showContextMenu(app, contextMenuIDNode->string_);
ShowContextMenu(app->contextMenuStore, app->mainWindow, contextMenuID, contextMenuData);
);
json_delete(contextMenuMessageJSON);
} else {
// const char *m = (const char *)msg(msg(message, s("body")), s("UTF8String"));
const char *m = cstr(msg(message, s("body")));
@ -380,15 +344,7 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
}
}
// Creates a JSON message for the given menuItemID and data
const char* createContextMenuMessage(const char *menuItemID, const char *givenContextMenuData) {
JsonNode *jsonObject = json_mkobject();
json_append_member(jsonObject, "menuItemID", json_mkstring(menuItemID));
json_append_member(jsonObject, "data", json_mkstring(givenContextMenuData));
const char *result = json_encode(jsonObject);
json_delete(jsonObject);
return result;
}
// Callback for tray items
void menuItemPressedForTrayMenu(id self, SEL cmd, id sender) {
@ -400,16 +356,16 @@ void menuItemPressedForTrayMenu(id self, SEL cmd, id sender) {
}
// Callback for context menu items
void menuItemPressedForContextMenus(id self, SEL cmd, id sender) {
const char *menuItemID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
// Notify the backend
const char *contextMenuMessage = createContextMenuMessage(menuItemID, contextMenuData);
const char *message = concat("XC", contextMenuMessage);
messageFromWindowCallback(message);
MEMFREE(message);
MEMFREE(contextMenuMessage);
}
//// Callback for context menu items
//void menuItemPressedForContextMenus(id self, SEL cmd, id sender) {
// const char *menuItemID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
// // Notify the backend
// const char *contextMenuMessage = createContextMenuMessage(menuItemID, contextMenuData);
// const char *message = concat("XC", contextMenuMessage);
// messageFromWindowCallback(message);
// MEMFREE(message);
// MEMFREE(contextMenuMessage);
//}
// Callback for tray menu items
void checkboxMenuItemPressedForTrayMenu(id self, SEL cmd, id sender, struct hashmap_s *menuItemMap) {
@ -431,25 +387,25 @@ void checkboxMenuItemPressedForTrayMenu(id self, SEL cmd, id sender, struct hash
}
// Callback for context menu items
void checkboxMenuItemPressedForContextMenus(id self, SEL cmd, id sender, struct hashmap_s *menuItemMap) {
const char *menuItemID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
// Get the menu item from the menu item map
id menuItem = (id)hashmap_get(&menuItemMapForContextMenus, (char*)menuItemID, strlen(menuItemID));
// Get the current state
bool state = msg(menuItem, s("state"));
// Toggle the state
msg(menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
// Notify the backend
const char *contextMenuMessage = createContextMenuMessage(menuItemID, contextMenuData);
const char *message = concat("XC", contextMenuMessage);
messageFromWindowCallback(message);
MEMFREE(message);
MEMFREE(contextMenuMessage);
}
//void checkboxMenuItemPressedForContextMenus(id self, SEL cmd, id sender, struct hashmap_s *menuItemMap) {
// const char *menuItemID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
//
// // Get the menu item from the menu item map
// id menuItem = (id)hashmap_get(&menuItemMapForContextMenus, (char*)menuItemID, strlen(menuItemID));
//
// // Get the current state
// bool state = msg(menuItem, s("state"));
//
// // Toggle the state
// msg(menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
//
// // Notify the backend
// const char *contextMenuMessage = createContextMenuMessage(menuItemID, contextMenuData);
// const char *message = concat("XC", contextMenuMessage);
// messageFromWindowCallback(message);
// MEMFREE(message);
// MEMFREE(contextMenuMessage);
//}
// radioMenuItemPressedForTrayMenu
void radioMenuItemPressedForTrayMenu(id self, SEL cmd, id sender) {
@ -488,42 +444,42 @@ void radioMenuItemPressedForTrayMenu(id self, SEL cmd, id sender) {
}
// radioMenuItemPressedForContextMenus
void radioMenuItemPressedForContextMenus(id self, SEL cmd, id sender) {
const char *menuItemID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
// Get the menu item from the menu item map
id menuItem = (id)hashmap_get(&menuItemMapForContextMenus, (char*)menuItemID, strlen(menuItemID));
// Check the menu items' current state
bool selected = msg(menuItem, s("state"));
// If it's already selected, exit early
if (selected) {
return;
}
// Get this item's radio group members and turn them off
id *members = (id*)hashmap_get(&radioGroupMapForContextMenus, (char*)menuItemID, strlen(menuItemID));
// Uncheck all members of the group
id thisMember = members[0];
int count = 0;
while(thisMember != NULL) {
msg(thisMember, s("setState:"), NSControlStateValueOff);
count = count + 1;
thisMember = members[count];
}
// check the selected menu item
msg(menuItem, s("setState:"), NSControlStateValueOn);
// Notify the backend
const char *contextMenuMessage = createContextMenuMessage(menuItemID, contextMenuData);
const char *message = concat("XC", contextMenuMessage);
messageFromWindowCallback(message);
MEMFREE(message);
MEMFREE(contextMenuMessage);
}
//void radioMenuItemPressedForContextMenus(id self, SEL cmd, id sender) {
// const char *menuItemID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
//
// // Get the menu item from the menu item map
// id menuItem = (id)hashmap_get(&menuItemMapForContextMenus, (char*)menuItemID, strlen(menuItemID));
//
// // Check the menu items' current state
// bool selected = msg(menuItem, s("state"));
//
// // If it's already selected, exit early
// if (selected) {
// return;
// }
//
// // Get this item's radio group members and turn them off
// id *members = (id*)hashmap_get(&radioGroupMapForContextMenus, (char*)menuItemID, strlen(menuItemID));
//
// // Uncheck all members of the group
// id thisMember = members[0];
// int count = 0;
// while(thisMember != NULL) {
// msg(thisMember, s("setState:"), NSControlStateValueOff);
// count = count + 1;
// thisMember = members[count];
// }
//
// // check the selected menu item
// msg(menuItem, s("setState:"), NSControlStateValueOn);
//
// // Notify the backend
// const char *contextMenuMessage = createContextMenuMessage(menuItemID, contextMenuData);
// const char *message = concat("XC", contextMenuMessage);
// messageFromWindowCallback(message);
// MEMFREE(message);
// MEMFREE(contextMenuMessage);
//}
// closeWindow is called when the close button is pressed
void closeWindow(id self, SEL cmd, id sender) {
@ -600,28 +556,6 @@ void allocateTrayHashMaps(struct Application *app) {
}
}
void allocateContextMenuHashMaps(struct Application *app) {
// Allocate new context menu map
if( 0 != hashmap_create((const unsigned)4, &contextMenuMap)) {
// Couldn't allocate map
Fatal(app, "Not enough memory to allocate contextMenuMap!");
}
// Allocate new menuItem map
if( 0 != hashmap_create((const unsigned)16, &menuItemMapForContextMenus)) {
// Couldn't allocate map
Fatal(app, "Not enough memory to allocate menuItemMapForContextMenus!");
}
// Allocate the Radio Group Cache
if( 0 != hashmap_create((const unsigned)4, &radioGroupMapForContextMenus)) {
// Couldn't allocate map
Fatal(app, "Not enough memory to allocate radioGroupMapForContextMenus!");
return;
}
}
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel) {
// Setup main application struct
struct Application *result = malloc(sizeof(struct Application));
@ -670,6 +604,7 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
result->statusItem = NULL;
// Context Menus
result->contextMenuStore = NULL;
result->contextMenusAsJSON = NULL;
result->processedContextMenus = NULL;
contextMenuData = NULL;
@ -690,36 +625,7 @@ int releaseNSObject(void *const context, struct hashmap_element_s *const e) {
}
void destroyContextMenus(struct Application *app) {
// Free menu item hashmap
hashmap_destroy(&menuItemMapForContextMenus);
// Free radio group members
if( hashmap_num_entries(&radioGroupMapForContextMenus) > 0 ) {
if (0!=hashmap_iterate_pairs(&radioGroupMapForContextMenus, freeHashmapItem, NULL)) {
Fatal(app, "failed to deallocate hashmap entries!");
}
}
//Free radio groups hashmap
hashmap_destroy(&radioGroupMapForContextMenus);
// Free context menus
if( hashmap_num_entries(&contextMenuMap) > 0 ) {
if (0!=hashmap_iterate_pairs(&contextMenuMap, releaseNSObject, NULL)) {
Fatal(app, "failed to deallocate hashmap entries!");
}
}
//Free context menu map
hashmap_destroy(&contextMenuMap);
// Destroy processed Context Menus
if( app->processedContextMenus != NULL) {
json_delete(app->processedContextMenus);
app->processedContextMenus = NULL;
}
DeleteContextMenuStore(app->contextMenuStore);
}
void freeDialogIconCache(struct Application *app) {
@ -1298,10 +1204,9 @@ void SetTray(struct Application *app, const char *trayMenuAsJSON, const char *tr
// SetContextMenus sets the context menu map for this application
void SetContextMenus(struct Application *app, const char *contextMenusAsJSON) {
app->contextMenusAsJSON = contextMenusAsJSON;
app->contextMenuStore = NewContextMenuStore(contextMenusAsJSON);
}
void SetBindings(struct Application *app, const char *bindings) {
const char* temp = concat("window.wailsbindings = \"", bindings);
const char* jscall = concat(temp, "\";");
@ -1391,9 +1296,9 @@ void createDelegate(struct Application *app) {
class_addMethod(delegateClass, s("menuCallbackForTrayMenu:"), (IMP)menuItemPressedForTrayMenu, "v@:@");
class_addMethod(delegateClass, s("checkboxMenuCallbackForTrayMenu:"), (IMP) checkboxMenuItemPressedForTrayMenu, "v@:@");
class_addMethod(delegateClass, s("radioMenuCallbackForTrayMenu:"), (IMP) radioMenuItemPressedForTrayMenu, "v@:@");
class_addMethod(delegateClass, s("menuCallbackForContextMenus:"), (IMP)menuItemPressedForContextMenus, "v@:@");
class_addMethod(delegateClass, s("checkboxMenuCallbackForContextMenus:"), (IMP) checkboxMenuItemPressedForContextMenus, "v@:@");
class_addMethod(delegateClass, s("radioMenuCallbackForContextMenus:"), (IMP) radioMenuItemPressedForContextMenus, "v@:@");
// class_addMethod(delegateClass, s("menuCallbackForContextMenus:"), (IMP)menuItemPressedForContextMenus, "v@:@");
// class_addMethod(delegateClass, s("checkboxMenuCallbackForContextMenus:"), (IMP) checkboxMenuItemPressedForContextMenus, "v@:@");
// class_addMethod(delegateClass, s("radioMenuCallbackForContextMenus:"), (IMP) radioMenuItemPressedForContextMenus, "v@:@");
// Refactoring menu handling
class_addMethod(delegateClass, s("menuItemCallback:"), (IMP)menuItemCallback, "v@:@");
@ -1799,52 +1704,52 @@ void UpdateMenu(struct Application *app, const char *menuAsJSON) {
);
}
void dumpContextMenus(struct Application *app) {
dumpHashmap("menuItemMapForContextMenus", &menuItemMapForContextMenus);
printf("&menuItemMapForContextMenus = %p\n", &menuItemMapForContextMenus);
//void dumpContextMenus(struct Application *app) {
// dumpHashmap("menuItemMapForContextMenus", &menuItemMapForContextMenus);
// printf("&menuItemMapForContextMenus = %p\n", &menuItemMapForContextMenus);
//
// //Free radio groups hashmap
// dumpHashmap("radioGroupMapForContextMenus", &radioGroupMapForContextMenus);
// printf("&radioGroupMapForContextMenus = %p\n", &radioGroupMapForContextMenus);
//
// //Free context menu map
// dumpHashmap("contextMenuMap", &contextMenuMap);
// printf("&contextMenuMap = %p\n", &contextMenuMap);
//}
//Free radio groups hashmap
dumpHashmap("radioGroupMapForContextMenus", &radioGroupMapForContextMenus);
printf("&radioGroupMapForContextMenus = %p\n", &radioGroupMapForContextMenus);
//Free context menu map
dumpHashmap("contextMenuMap", &contextMenuMap);
printf("&contextMenuMap = %p\n", &contextMenuMap);
}
void parseContextMenus(struct Application *app) {
// Parse the context menu json
app->processedContextMenus = json_decode(app->contextMenusAsJSON);
if( app->processedContextMenus == NULL ) {
// Parse error!
Fatal(app, "Unable to parse Context Menus JSON: %s", app->contextMenusAsJSON);
return;
}
JsonNode *contextMenuItems = json_find_member(app->processedContextMenus, "Items");
if( contextMenuItems == NULL ) {
// Parse error!
Fatal(app, "Unable to find Items:", app->processedContextMenus);
return;
}
// Iterate context menus
JsonNode *contextMenu;
json_foreach(contextMenu, contextMenuItems) {
// Create a new menu
id menu = createMenu(str(""));
// parse the menu
parseMenu(app, menu, contextMenu, &menuItemMapForContextMenus,
"checkboxMenuCallbackForContextMenus:", "radioMenuCallbackForContextMenus:", "menuCallbackForContextMenus:");
// Store the item in the context menu map
hashmap_put(&contextMenuMap, (char*)contextMenu->key, strlen(contextMenu->key), menu);
}
// dumpContextMenus(app);
}
//void parseContextMenus(struct Application *app) {
//
// // Parse the context menu json
// app->processedContextMenus = json_decode(app->contextMenusAsJSON);
//
// if( app->processedContextMenus == NULL ) {
// // Parse error!
// Fatal(app, "Unable to parse Context Menus JSON: %s", app->contextMenusAsJSON);
// return;
// }
//
// JsonNode *contextMenuItems = json_find_member(app->processedContextMenus, "Items");
// if( contextMenuItems == NULL ) {
// // Parse error!
// Fatal(app, "Unable to find Items:", app->processedContextMenus);
// return;
// }
// // Iterate context menus
// JsonNode *contextMenu;
// json_foreach(contextMenu, contextMenuItems) {
// // Create a new menu
// id menu = createMenu(str(""));
//
// // parse the menu
// parseMenu(app, menu, contextMenu, &menuItemMapForContextMenus,
// "checkboxMenuCallbackForContextMenus:", "radioMenuCallbackForContextMenus:", "menuCallbackForContextMenus:");
//
// // Store the item in the context menu map
// hashmap_put(&contextMenuMap, (char*)contextMenu->key, strlen(contextMenu->key), menu);
// }
//
//// dumpContextMenus(app);
//}
void UpdateTrayLabel(struct Application *app, const char *label) {
id statusBarButton = msg(app->statusItem, s("button"));
@ -1971,17 +1876,17 @@ void UpdateTray(struct Application *app, const char *trayMenuAsJSON) {
}
void UpdateContextMenus(struct Application *app, const char *contextMenusAsJSON) {
ON_MAIN_THREAD (
dumpContextMenus(app);
// Free up memory
destroyContextMenus(app);
// Set the context menu JSON
app->contextMenusAsJSON = contextMenusAsJSON;
parseContextMenus(app);
);
// ON_MAIN_THREAD (
//
// dumpContextMenus(app);
//
// // Free up memory
// destroyContextMenus(app);
//
// // Set the context menu JSON
// app->contextMenusAsJSON = contextMenusAsJSON;
// parseContextMenus(app);
// );
}
void processDialogIcons(struct hashmap_s *hashmap, const unsigned char *dialogIcons[]) {
@ -2207,12 +2112,9 @@ void Run(struct Application *app, int argc, char **argv) {
parseTrayData(app);
}
// Allocation the hashmaps we need
allocateContextMenuHashMaps(app);
// If we have context menus, process them
if( app->contextMenusAsJSON != NULL ) {
parseContextMenus(app);
if( app->contextMenuStore != NULL ) {
ProcessContextMenus(app->contextMenuStore);
}
// Process dialog icons

View file

@ -119,7 +119,24 @@ func (a *Application) processPlatformSettings() error {
// Process context menus
contextMenus := options.GetContextMenus(a.config)
if contextMenus != nil {
contextMenusJSON, err := json.Marshal(contextMenus)
type ProcessedContextMenu struct {
ID string
ProcessedMenu *ProcessedMenu
}
var processedContextMenus []*ProcessedContextMenu
// We need to iterate each context menu and pre-process it
for contextMenuID, contextMenu := range contextMenus.Items {
thisContextMenu := &ProcessedContextMenu{
ID: contextMenuID,
ProcessedMenu: NewProcessedMenu(contextMenu),
}
processedContextMenus = append(processedContextMenus, thisContextMenu )
}
contextMenusJSON, err := json.Marshal(processedContextMenus)
fmt.Printf("\n\nCONTEXT MENUS:\n %+v\n\n", string(contextMenusJSON))
if err != nil {
return err

View file

@ -27,11 +27,6 @@
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("bounds"))
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))msg)(receiver, s("backingScaleFactor"))
#define STREQ(a,b) strcmp(a, b) == 0
#define STRCOPY(a) concat(a, "")
#define STR_HAS_CHARS(input) input != NULL && strlen(input) > 0
#define MEMFREE(input) free((void*)input); input = NULL;
#define ON_MAIN_THREAD(str) dispatch( ^{ str; } )
#define MAIN_WINDOW_CALL(str) msg(app->mainWindow, s((str)))

View file

@ -6,9 +6,10 @@
#define MENU_DARWIN_H
#include "common.h"
#include "contextmenustore_darwin.h"
enum MenuItemType {Text = 0, Checkbox = 1, Radio = 2};
enum MenuType {ApplicationMenu = 0};
enum MenuType {ApplicationMenuType = 0, ContextMenuType = 1};
extern void messageFromWindowCallback(const char *);
@ -18,7 +19,7 @@ typedef struct {
/*** Internal ***/
const char *menuAsJSON;
// The decoded version of the Menu JSON
JsonNode *processedMenu;
struct hashmap_s menuItemMap;
@ -30,6 +31,9 @@ typedef struct {
// The NSMenu for this menu
id menu;
// The parent data, eg ContextMenuStore or Tray
void *parentData;
// The commands for the menu callbacks
const char *callbackCommand;
@ -40,12 +44,11 @@ typedef struct {
} Menu;
// NewMenu creates a new Menu struct, saving the given menu structure as JSON
Menu* NewMenu(const char *menuAsJSON) {
Menu* NewMenu(JsonNode *menuData) {
Menu *result = malloc(sizeof(Menu));
// menuAsJSON is allocated and freed by Go
result->menuAsJSON = menuAsJSON;
result->processedMenu = menuData;
// No title by default
result->title = "";
@ -66,8 +69,16 @@ Menu* NewMenu(const char *menuAsJSON) {
}
Menu* NewApplicationMenu(const char *menuAsJSON) {
Menu *result = NewMenu(menuAsJSON);
result->menuType = ApplicationMenu;
// Parse the menu json
JsonNode *processedMenu = json_decode(menuAsJSON);
if( processedMenu == NULL ) {
// Parse error!
ABORT("Unable to parse Menu JSON: %s", menuAsJSON);
}
Menu *result = NewMenu(processedMenu);
result->menuType = ApplicationMenuType;
return result;
}
@ -121,31 +132,14 @@ void DeleteMenu(Menu *menu) {
free(menu);
}
const char* createTextMenuMessage(MenuItemCallbackData *callbackData) {
switch( callbackData->menu->menuType ) {
case ApplicationMenu:
return concat("MC", callbackData->menuID);
}
return NULL;
}
const char* createCheckBoxMenuMessage(MenuItemCallbackData *callbackData) {
switch( callbackData->menu->menuType ) {
case ApplicationMenu:
return concat("MC", callbackData->menuID);
}
return NULL;
}
const char* createRadioMenuMessage(MenuItemCallbackData *callbackData) {
switch( callbackData->menu->menuType ) {
case ApplicationMenu:
return concat("MC", callbackData->menuID);
}
return NULL;
// Creates a JSON message for the given menuItemID and data
const char* createContextMenuMessage(const char *menuItemID, const char *givenContextMenuData) {
JsonNode *jsonObject = json_mkobject();
json_append_member(jsonObject, "menuItemID", json_mkstring(menuItemID));
json_append_member(jsonObject, "data", json_mkstring(givenContextMenuData));
const char *result = json_encode(jsonObject);
json_delete(jsonObject);
return result;
}
// Callback for text menu items
@ -182,8 +176,13 @@ void menuItemCallback(id self, SEL cmd, id sender) {
}
// Generate message to send to backend
if( callbackData->menu->menuType == ApplicationMenu ) {
if( callbackData->menu->menuType == ApplicationMenuType ) {
message = concat("MC", callbackData->menuID);
} else if( callbackData->menu->menuType == ContextMenuType ) {
// Get the context menu data from the menu
ContextMenuStore* store = (ContextMenuStore*) callbackData->menu->parentData;
const char *contextMenuMessage = createContextMenuMessage(callbackData->menuID, store->contextMenuData);
message = concat("XC", contextMenuMessage);
}
// TODO: Add other menu types here!
@ -647,8 +646,6 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
name = menuNameNode->string_;
}
printf("\n\nProcessing submenu %s!!!\n\n", name);
id thisMenuItem = createMenuItemNoAutorelease(str(name), NULL, "");
id thisMenu = createMenu(str(name));
@ -750,7 +747,7 @@ void processMenuData(Menu *menu, JsonNode *menuData) {
JsonNode *items = json_find_member(menuData, "Items");
if( items == NULL ) {
// Parse error!
ABORT("Unable to find 'Items' in menu JSON:", menu->menuAsJSON);
ABORT("Unable to find 'Items' in menu JSON!");
}
// Iterate items
@ -797,27 +794,17 @@ void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup) {
id GetMenu(Menu *menu) {
menu->menu = createMenu(str(""));
// Parse the menu json
JsonNode *processedMenu = json_decode(menu->menuAsJSON);
if( processedMenu == NULL ) {
// Parse error!
ABORT("Unable to parse Menu JSON: %s", menu->menuAsJSON);
}
// Pull out the menu data
JsonNode *menuData = json_find_member(processedMenu, "Menu");
JsonNode *menuData = json_find_member(menu->processedMenu, "Menu");
if( menuData == NULL ) {
ABORT("Unable to find Menu data: %s", processedMenu);
ABORT("Unable to find Menu data: %s", menu->processedMenu);
}
menu->menu = createMenu(str(""));
// Process the menu data
processMenuData(menu, menuData);
// Save the reference so we can delete it later
menu->processedMenu = processedMenu;
// Create the radiogroup cache
JsonNode *radioGroups = json_find_member(menu->processedMenu, "RadioGroups");
if( radioGroups == NULL ) {

View file

@ -26,6 +26,10 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
// Parse will attempt to parse the given message
func Parse(message string) (*parsedMessage, error) {
if len(message) == 0 {
return nil, fmt.Errorf("MessageParser received blank message");
}
parseMethod := messageParsers[message[0]]
if parseMethod == nil {
return nil, fmt.Errorf("message type '%c' invalid", message[0])