From 7a68cd7f131d5679fefe3fa792e6c8d499ceaa29 Mon Sep 17 00:00:00 2001 From: Rory Fewell Date: Sun, 23 Feb 2025 14:23:24 +0000 Subject: [PATCH] Enhancement: Fixes #431, taskband - Start menu - All Programs should use filesystem-based tree, drop GarconGtkMenu --- shared/comgtk/CMakeLists.txt | 2 + shared/comgtk/public/hashtable.h | 25 + shared/comgtk/public/libapi.h.in | 1 + shared/comgtk/public/strings.h | 10 + shared/comgtk/src/hashtable.c | 22 + shared/comgtk/src/strings.c | 15 + shell/taskband/CMakeLists.txt | 2 + shell/taskband/src/start/personal.c | 17 +- shell/taskband/src/start/progmenu.c | 813 ++++++++++++++++++++++++++++ shell/taskband/src/start/progmenu.h | 16 + 10 files changed, 916 insertions(+), 7 deletions(-) create mode 100644 shared/comgtk/public/hashtable.h create mode 100644 shared/comgtk/src/hashtable.c create mode 100644 shell/taskband/src/start/progmenu.c create mode 100644 shell/taskband/src/start/progmenu.h diff --git a/shared/comgtk/CMakeLists.txt b/shared/comgtk/CMakeLists.txt index beba864..ada32fc 100644 --- a/shared/comgtk/CMakeLists.txt +++ b/shared/comgtk/CMakeLists.txt @@ -51,6 +51,8 @@ add_library( public/delegate.h src/errors.c public/errors.h + src/hashtable.c + public/hashtable.h src/icons.c public/icons.h src/list.c diff --git a/shared/comgtk/public/hashtable.h b/shared/comgtk/public/hashtable.h new file mode 100644 index 0000000..d7c1bab --- /dev/null +++ b/shared/comgtk/public/hashtable.h @@ -0,0 +1,25 @@ +/** @file */ + +#ifndef __COMGTK_HASHTABLE_H__ +#define __COMGTK_HASHTABLE_H__ + +#include + +// +// PUBLIC FUNCTIONS +// + +/** + * Inserts mapping pairs from an array into a hash table. + * + * @param hash_table The hash table. + * @param mappings The array of mappings pairs. + * @param arr_size The total elements in the array (not number of pairs). + */ +void wintc_hash_table_insert_from_array( + GHashTable* hash_table, + void** mappings, + gsize arr_size +); + +#endif diff --git a/shared/comgtk/public/libapi.h.in b/shared/comgtk/public/libapi.h.in index 215c3cd..434cecf 100644 --- a/shared/comgtk/public/libapi.h.in +++ b/shared/comgtk/public/libapi.h.in @@ -11,6 +11,7 @@ #include "@LIB_HEADER_DIR@/defprocs.h" #include "@LIB_HEADER_DIR@/delegate.h" #include "@LIB_HEADER_DIR@/errors.h" +#include "@LIB_HEADER_DIR@/hashtable.h" #include "@LIB_HEADER_DIR@/icons.h" #include "@LIB_HEADER_DIR@/list.h" #include "@LIB_HEADER_DIR@/listbox.h" diff --git a/shared/comgtk/public/strings.h b/shared/comgtk/public/strings.h index e8b5087..cf93904 100644 --- a/shared/comgtk/public/strings.h +++ b/shared/comgtk/public/strings.h @@ -9,6 +9,16 @@ // PUBLIC FUNCTIONS // +/** + * Gets the last component of a path. + * + * @param path The path. + * @return A pointer to the last component of the path. + */ +const gchar* wintc_basename( + const gchar* path +); + /** * Copies a string, ensuring it has the specified prefix. * diff --git a/shared/comgtk/src/hashtable.c b/shared/comgtk/src/hashtable.c new file mode 100644 index 0000000..5b77f86 --- /dev/null +++ b/shared/comgtk/src/hashtable.c @@ -0,0 +1,22 @@ +#include + +#include "../public/hashtable.h" + +// +// PUBLIC FUNCTIONS +// +void wintc_hash_table_insert_from_array( + GHashTable* hash_table, + void** mappings, + gsize arr_size +) +{ + for (gsize i = 0; i < arr_size; i += 2) + { + g_hash_table_insert( + hash_table, + mappings[i], + mappings[i + 1] + ); + } +} diff --git a/shared/comgtk/src/strings.c b/shared/comgtk/src/strings.c index e82814e..9e9a205 100644 --- a/shared/comgtk/src/strings.c +++ b/shared/comgtk/src/strings.c @@ -6,6 +6,21 @@ // // PUBLIC FUNCTIONS // +const gchar* wintc_basename( + const gchar* path +) +{ + const gchar* last_sep = + strrchr(path, G_DIR_SEPARATOR); + + if (last_sep) + { + return last_sep + 1; + } + + return path; +} + gchar* wintc_str_set_prefix( const gchar* str, const gchar* prefix diff --git a/shell/taskband/CMakeLists.txt b/shell/taskband/CMakeLists.txt index 73a5d6a..1f90f41 100644 --- a/shell/taskband/CMakeLists.txt +++ b/shell/taskband/CMakeLists.txt @@ -57,6 +57,8 @@ set( src/start/menumod.h src/start/personal.c src/start/personal.h + src/start/progmenu.c + src/start/progmenu.h src/start/shared.h src/start/toolbar.c src/start/toolbar.h diff --git a/shell/taskband/src/start/personal.c b/shell/taskband/src/start/personal.c index 345572a..ed061e7 100644 --- a/shell/taskband/src/start/personal.c +++ b/shell/taskband/src/start/personal.c @@ -13,6 +13,7 @@ #include "../toolbar.h" #include "menumod.h" #include "personal.h" +#include "progmenu.h" #include "shared.h" #include "toolbar.h" #include "util.h" @@ -125,6 +126,14 @@ void create_personal_menu( GtkBuilder* builder; WinTCTaskbandToolbar* toolbar = WINTC_TASKBAND_TOOLBAR(toolbar_start); + GError* error = NULL; + + if (!wintc_toolbar_start_progmenu_init(&error)) + { + wintc_display_error_and_clear(&error, NULL); + return; + } + // Set default states // toolbar_start->personal.sync_menu_refresh = TRUE; @@ -253,15 +262,9 @@ void create_personal_menu( // Attach All Programs submenu // - GarconMenu* programs_menu = garcon_menu_new_for_path( - WINTC_ASSETS_DIR - "/shell-res/applications.menu" - ); - GtkWidget* programs_submenu = garcon_gtk_menu_new(programs_menu); - gtk_menu_item_set_submenu( GTK_MENU_ITEM(toolbar_start->personal.menuitem_all_programs), - programs_submenu + wintc_toolbar_start_progmenu_new_gtk_menu() ); // Transfer to popup diff --git a/shell/taskband/src/start/progmenu.c b/shell/taskband/src/start/progmenu.c new file mode 100644 index 0000000..13ee182 --- /dev/null +++ b/shell/taskband/src/start/progmenu.c @@ -0,0 +1,813 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "progmenu.h" + +#define WINTC_COMPONENT_START_MENU "start-menu" + +#define K_DIR_ROOT "" +#define K_DIR_ACCESSORIES "/Accessories" +#define K_DIR_ACCESSIBILITY "/Accessories/Accessibility" +#define K_DIR_COMMUNICATIONS "/Accessories/Communications" +#define K_DIR_ENTERTAINMENT "/Accessories/Entertainment" +#define K_DIR_SYSTEM_TOOLS "/Accessories/System Tools" +#define K_DIR_GAMES "/Games" +#define K_DIR_STARTUP "/Startup" + +#define K_DIR_GNOME "/GNOME" +#define K_DIR_KDE "/KDE" + +#define K_DIR_DOOM "/DOOM" +#define K_DIR_LIBREOFFICE "/LibreOffice" +#define K_DIR_QT_DEV_TOOLS "/Qt Developer Tools" + +// +// LOCAL TYPEDEFS +// +typedef const gchar* (*DesktopAppInfoFilterFunc) ( + GDesktopAppInfo* entry +); + +// +// FORWARD DECLARATIONS +// +static void wintc_toolbar_start_progmenu_build_fs(void); +static const gchar* wintc_toolbar_start_progmenu_filter_entry( + GDesktopAppInfo* entry +); +static GMenu* wintc_toolbar_start_progmenu_menu_from_filelist( + GList* files, + GHashTable** map_dir_to_menu +); + +static gboolean create_symlink( + const gchar* rel_path, + const gchar* entry_name, + const gchar* target +); + +static void action_launch( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); + +static const gchar* filter_doom( + GDesktopAppInfo* entry +); +static const gchar* filter_libreoffice( + GDesktopAppInfo* entry +); +static const gchar* filter_qt_dev_tools( + GDesktopAppInfo* entry +); + +// +// STATIC DATA +// +static gboolean S_INIT_DONE = FALSE; + +static gchar* S_DIR_START_MENU = NULL; + +static GHashTable* S_KNOWN_MAPPINGS_TABLE = NULL; +static GHashTable* S_VENDOR_MAPPINGS_TABLE = NULL; + +static gchar* S_KNOWN_MAPPINGS[] = { + "explorer.desktop", K_DIR_ACCESSORIES, + "firefox.desktop", K_DIR_ROOT, + "firefox-esr.desktop", K_DIR_ROOT, + "iexplore.desktop", K_DIR_ROOT, + "mspaint.desktop", K_DIR_ACCESSORIES, + "notepad.desktop", K_DIR_ACCESSORIES +}; + +static const gchar* S_EXCLUDED_CATEGORIES[] = { + "Screensaver", + "Settings", + "X-XFCE" +}; + +static const gchar* S_VENDOR_MAPPINGS[] = { + "GNOME", K_DIR_GNOME, + "KDE", K_DIR_KDE +}; + +static DesktopAppInfoFilterFunc S_ENTRY_FILTERS[] = { + &filter_doom, + &filter_libreoffice, + &filter_qt_dev_tools +}; + +// // // // + +static GMenu* S_MENU_PROGRAMS = NULL; + +static GHashTable* S_MAP_DIR_TO_MENU = NULL; + +static GActionEntry S_ACTIONS[] = { + { + .name = "launch", + .activate = action_launch, + .parameter_type = "s", + .state = NULL, + .change_state = NULL + } +}; + +// +// PUBLIC FUNCTIONS +// +gboolean wintc_toolbar_start_progmenu_init( + GError** error +) +{ + if (S_INIT_DONE) + { + return TRUE; + } + + // Sort out profile + // + S_DIR_START_MENU = + wintc_profile_get_path(WINTC_COMPONENT_START_MENU, ""); + + if (!wintc_profile_ensure_exists(WINTC_COMPONENT_START_MENU, error)) + { + return FALSE; + } + + // Set up known desktop entry mappings + // + S_KNOWN_MAPPINGS_TABLE = g_hash_table_new(g_str_hash, g_str_equal); + S_VENDOR_MAPPINGS_TABLE = g_hash_table_new(g_str_hash, g_str_equal); + + wintc_hash_table_insert_from_array( + S_KNOWN_MAPPINGS_TABLE, + (void**) S_KNOWN_MAPPINGS, + G_N_ELEMENTS(S_KNOWN_MAPPINGS) + ); + wintc_hash_table_insert_from_array( + S_VENDOR_MAPPINGS_TABLE, + (void**) S_VENDOR_MAPPINGS, + G_N_ELEMENTS(S_VENDOR_MAPPINGS) + ); + + // Attempt to pull the structure from here + // + GList* files = + wintc_sh_fs_get_names_as_list( + S_DIR_START_MENU, + TRUE, + G_FILE_TEST_IS_REGULAR, + TRUE, + error + ); + + if (!files) + { + wintc_toolbar_start_progmenu_build_fs(); + + files = + wintc_sh_fs_get_names_as_list( + S_DIR_START_MENU, + TRUE, + G_FILE_TEST_IS_REGULAR, + TRUE, + error + ); + } + + files = g_list_sort(files, (GCompareFunc) g_ascii_strcasecmp); + + // Construct the menu + // + S_MENU_PROGRAMS = + wintc_toolbar_start_progmenu_menu_from_filelist( + files, + &S_MAP_DIR_TO_MENU + ); + + g_list_free_full(files, g_free); + + S_INIT_DONE = TRUE; + + return TRUE; +} + +GtkWidget* wintc_toolbar_start_progmenu_new_gtk_menu() +{ + if (!S_INIT_DONE) + { + g_critical( + "%s", + "start menu - progmenu not ready to make a GtkMenu" + ); + + return NULL; + } + + // Ensure we have an action map for the menu items to call + // + static GSimpleActionGroup* s_action_group = NULL; + + if (!s_action_group) + { + s_action_group = g_simple_action_group_new(); + + g_action_map_add_action_entries( + G_ACTION_MAP(s_action_group), + S_ACTIONS, + G_N_ELEMENTS(S_ACTIONS), + NULL + ); + } + + // Create the menu + // + GtkWidget* menu = gtk_menu_new_from_model(G_MENU_MODEL(S_MENU_PROGRAMS)); + + gtk_widget_insert_action_group( + menu, + "progmenu", + G_ACTION_GROUP(s_action_group) + ); + + return menu; +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_toolbar_start_progmenu_build_fs(void) +{ + GList* all_entries = + wintc_sh_fs_get_names_as_list( + WINTC_RT_PREFIX "/share/applications", + TRUE, + G_FILE_TEST_IS_REGULAR, + TRUE, + NULL // FIXME: Error handling + ); + + for (GList* iter = all_entries; iter; iter = iter->next) + { + gchar* entry_path = (gchar*) iter->data; + + WINTC_LOG_DEBUG("start menu - analyse %s", entry_path); + + GDesktopAppInfo* entry = + g_desktop_app_info_new_from_filename(entry_path); + + if (!entry) + { + g_warning("start menu - failed to load %s", entry_path); + continue; + } + + const gchar* rel_path = + wintc_toolbar_start_progmenu_filter_entry( + entry + ); + + if (rel_path) + { + create_symlink( + rel_path, + wintc_basename(g_desktop_app_info_get_filename(entry)), + g_desktop_app_info_get_filename(entry) + ); + } + + g_object_unref(entry); + } + + g_list_free_full(all_entries, g_free); +} + +static const gchar* wintc_toolbar_start_progmenu_filter_entry( + GDesktopAppInfo* entry +) +{ + const gchar* basename = + wintc_basename(g_desktop_app_info_get_filename(entry)); + + // Check if this entry should be skipped (NoDisplay) + // + if (g_desktop_app_info_get_nodisplay(entry)) + { + WINTC_LOG_DEBUG( + "start menu - NoDisplay skipping %s", + basename + ); + + return NULL; + } + + // Check if this entry has a direct mapping + // + const gchar* known_target = + g_hash_table_lookup(S_KNOWN_MAPPINGS_TABLE, basename); + + if (known_target) + { + WINTC_LOG_DEBUG( + "start menu - filter - suggest (via known) %s/%s", + known_target, + basename + ); + + return known_target; + } + + // Category checks... + // + const gchar* categories_str = g_desktop_app_info_get_categories(entry); + + if (!categories_str) + { + WINTC_LOG_DEBUG( + "start menu - filter - no categories for %s, excluding", + basename + ); + + return NULL; + } + + // The categories string is literally just given raw by GLib, so we must + // deal with the semicolon delimiter here + // + gchar** categories = g_strsplit(categories_str, ";", 0); + + // See if entry is in an excluded category + // + gchar** iter_cat = categories; + + while (*iter_cat) + { + gchar* category = *iter_cat; + + for (gsize i = 0; i < G_N_ELEMENTS(S_EXCLUDED_CATEGORIES); i++) + { + if (g_strcmp0(category, S_EXCLUDED_CATEGORIES[i]) == 0) + { + WINTC_LOG_DEBUG( + "start menu - filter - %s is in excluded category %s", + basename, + S_EXCLUDED_CATEGORIES[i] + ); + + g_strfreev(categories); + + return NULL; + } + } + + iter_cat++; + } + + // See if there is a filter func for this entry + // + for (gsize i = 0; i < G_N_ELEMENTS(S_ENTRY_FILTERS); i++) + { + const gchar* fn_target = + (*S_ENTRY_FILTERS[i]) (entry); + + if (fn_target) + { + g_strfreev(categories); + return fn_target; + } + } + + // See if there is a vendor-specific category we can filter + // + iter_cat = categories; + + while (*iter_cat) + { + gchar* category = *iter_cat; + + const gchar* vendor_target = + g_hash_table_lookup(S_VENDOR_MAPPINGS_TABLE, category); + + if (vendor_target) + { + WINTC_LOG_DEBUG( + "start menu - filter - suggest (via vendor) %s/%s", + vendor_target, + basename + ); + + g_strfreev(categories); + + return vendor_target; + } + + iter_cat++; + } + + g_strfreev(categories); + + // Didn't find anything, plop it in the root + // + g_message( + "start menu - filter - suggest (default) %s/%s", + K_DIR_ROOT, + basename + ); + + return K_DIR_ROOT; +} + +static GMenu* wintc_toolbar_start_progmenu_menu_from_filelist( + GList* files, + GHashTable** map_dir_to_menu +) +{ + GMenu* menu = g_menu_new(); + + *map_dir_to_menu = g_hash_table_new(g_str_hash, g_str_equal); + + // Add the top level menu to start with... + // + g_hash_table_insert( + *map_dir_to_menu, + "", + menu + ); + + // Construct the menus from this file list + // + gint len_root = strlen(S_DIR_START_MENU); + + for (GList* iter = files; iter; iter = iter->next) + { + const gchar* entry_path = (gchar*) iter->data; + GDesktopAppInfo* entry = + g_desktop_app_info_new_from_filename(entry_path); + + WINTC_LOG_DEBUG("start menu - found: %s", (gchar*) iter->data); + + // Pull the directory components out of the path to check the submenu + // that should own this item + // + const gchar* dir_end = strrchr(entry_path, G_DIR_SEPARATOR); + const gchar* dir_start = entry_path + len_root; + + gchar* rel_dir = g_malloc0((dir_end - dir_start) + 1); + + if (dir_start != dir_end) + { + memcpy(rel_dir, dir_start, dir_end - dir_start); + } + + // Fetch the menu + // + GMenu* menu_owner = + g_hash_table_lookup(*map_dir_to_menu, rel_dir); + + if (!menu_owner) + { + menu_owner = g_menu_new(); + + g_hash_table_insert( + *map_dir_to_menu, + rel_dir, + menu_owner + ); + } + else + { + g_free(rel_dir); // No longer needed + } + + // Set up the cmdline + // + gchar* cmd = wintc_desktop_app_info_get_command(entry); + + // Create the menu item + // + GMenuItem* new_item = g_menu_item_new(NULL, NULL); + GVariant* variant = g_variant_new_string(cmd); + + g_menu_item_set_action_and_target_value( + new_item, + "progmenu.launch", + variant + ); + g_menu_item_set_icon( + new_item, + g_app_info_get_icon(G_APP_INFO(entry)) + ); + g_menu_item_set_label( + new_item, + g_app_info_get_name(G_APP_INFO(entry)) + ); + + g_menu_append_item(menu_owner, new_item); + + g_free(cmd); + g_object_unref(new_item); + g_object_unref(entry); + } + + // Link up submenus + // + GList* submenu_keys = g_hash_table_get_keys(*map_dir_to_menu); + + for (GList* iter = submenu_keys; iter; iter = iter->next) + { + gchar* this_dir = (gchar*) iter->data; + + if (g_strcmp0(this_dir, "") == 0) + { + continue; + } + + // Hold onto this menu + // + GMenu* this_menu = + g_hash_table_lookup( + *map_dir_to_menu, + this_dir + ); + + // Nav up dir components to find parent menus, it could be that a menu + // does not exist for a parent yet! + // + // strdup simplifies things so we can just free() + // + GMenu* cur_menu = this_menu; + gchar* iter = g_strdup(this_dir); + + while (iter) + { + // Retrieve the parent path + // + const gchar* dir_end = strrchr(iter, G_DIR_SEPARATOR); + + gchar* parent = g_malloc0(dir_end - iter + 1); + + memcpy(parent, iter, dir_end - iter); + + // Create the menu item for the submenu + // + // FIXME: The icon will not show up until a custom menu tracker + // implementation is made - GTK3 itself does not bind the + // icon attribute for menu items with a submenu -_- + // + static GIcon* s_icon_programs = NULL; + + if (!s_icon_programs) + { + s_icon_programs = g_themed_icon_new("applications-other"); + } + + GMenuItem* submenu_item = g_menu_item_new(NULL, NULL); + + g_menu_item_set_icon( + submenu_item, + g_themed_icon_new("add") + ); + g_menu_item_set_label( + submenu_item, + dir_end + 1 + ); + g_menu_item_set_submenu( + submenu_item, + G_MENU_MODEL(cur_menu) + ); + + // Check if the parent exists + // + GMenu* parent_menu = + g_hash_table_lookup( + *map_dir_to_menu, + parent + ); + + if (parent_menu) + { + g_menu_prepend_item( + parent_menu, + submenu_item + ); + + // No more work required + g_free(parent); + g_object_unref(submenu_item); + break; + } + + // Parent doesn't exist, create it... + // + parent_menu = g_menu_new(); + + g_hash_table_insert( + *map_dir_to_menu, + parent, + parent_menu + ); + + // ...add us to it and continue iterating, because this new menu + // will need to be added to its parent too + // + g_menu_prepend_item( + parent_menu, + submenu_item + ); + + g_object_unref(submenu_item); + + cur_menu = parent_menu; + + g_free(iter); + iter = parent; + } + + g_free(iter); + } + + g_list_free(submenu_keys); + + return menu; +} + +static gboolean create_symlink( + const gchar* rel_path, + const gchar* entry_name, + const gchar* target +) +{ + gboolean ret = TRUE; + + // Ensure the dir exists + // + gchar* full_dir_path = + g_build_path(G_DIR_SEPARATOR_S, S_DIR_START_MENU, rel_path, NULL); + + if ( + mkdir( + full_dir_path, + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH + ) < 0 + ) + { + // EEXIST is fine, anything else is a problem + // + if (errno != EEXIST) + { + g_warning( + "Start menu mapping: unable to create dir %s (err %d)", + full_dir_path, + errno + ); + + g_free(full_dir_path); + return FALSE; + } + } + + // Attempt to create the symlink now + // + gchar* full_link_path = + g_build_path(G_DIR_SEPARATOR_S, full_dir_path, entry_name, NULL); + + if ( + symlink( + target, + full_link_path + ) < 0 + ) + { + // Even EEXIST is a bit odd here... maybe the user already created a + // link with this name + // + g_warning( + "Start menu mapping: unable to create link %s (err %d)", + full_link_path, + errno + ); + + ret = FALSE; + } + + g_free(full_dir_path); + g_free(full_link_path); + + return ret; +} + +// +// CALLBACKS +// +static void action_launch( + WINTC_UNUSED(GSimpleAction* action), + GVariant* parameter, + WINTC_UNUSED(gpointer user_data) +) +{ + GError* error = NULL; + + if ( + !wintc_launch_command( + g_variant_get_string(parameter, NULL), + &error + ) + ) + { + wintc_display_error_and_clear(&error, NULL); + } +} + +static const gchar* filter_doom( + GDesktopAppInfo* entry +) +{ + // Check if 'doom' is found in the keywords + // + const gchar* const* iter = + g_desktop_app_info_get_keywords(entry); + + if (!iter) + { + return NULL; + } + + for (; *iter; iter++) + { + const gchar* keyword = *iter; + + if (g_strcmp0(keyword, "doom") == 0) + { + return K_DIR_DOOM; + } + } + + // Dunno if it's a packaging bug, but for some reason Crispy Doom and + // Chocolate Doom do not have the keyword 'doom'... ( -_- ) + // + // IDK, just catch everything else that ends with -doom + // + const gchar* cmdline = + g_app_info_get_commandline(G_APP_INFO(entry)); + + if (g_str_has_suffix(cmdline, "-doom")) + { + return K_DIR_DOOM; + } + + return NULL; +} + +static const gchar* filter_libreoffice( + GDesktopAppInfo* entry +) +{ + if ( + g_str_has_prefix( + g_app_info_get_name(G_APP_INFO(entry)), + "LibreOffice" + ) + ) + { + return K_DIR_LIBREOFFICE; + } + + return NULL; +} + +static const gchar* filter_qt_dev_tools( + GDesktopAppInfo* entry +) +{ + // Assume stuff that's bundled with Qt is for the dev tools + // + // Under like /usr/lib/qtX/bin + // + const gchar* cmdline = + g_app_info_get_commandline(G_APP_INFO(entry)); + + if (g_str_has_prefix(cmdline, WINTC_RT_PREFIX "/lib/qt")) + { + return K_DIR_QT_DEV_TOOLS; + } + + // Or... could be qtcreator itself + // + if (g_str_has_prefix(cmdline, "qtcreator")) + { + return K_DIR_QT_DEV_TOOLS; + } + + return NULL; +} diff --git a/shell/taskband/src/start/progmenu.h b/shell/taskband/src/start/progmenu.h new file mode 100644 index 0000000..11964b8 --- /dev/null +++ b/shell/taskband/src/start/progmenu.h @@ -0,0 +1,16 @@ +#ifndef __START_PROGMENU_H__ +#define __START_PROGMENU_H__ + +#include +#include + +// +// PUBLIC FUNCTIONS +// +gboolean wintc_toolbar_start_progmenu_init( + GError** error +); + +GtkWidget* wintc_toolbar_start_progmenu_new_gtk_menu(void); + +#endif