diff --git a/private/play/browser/wintc-browser.desktop b/private/play/browser/wintc-browser.desktop index 81cc8f3..62a06c5 100644 --- a/private/play/browser/wintc-browser.desktop +++ b/private/play/browser/wintc-browser.desktop @@ -6,4 +6,4 @@ Icon=wintc-browser Terminal=false StartupNotify=false Type=Application -Categories=Utility;GTK; +Categories=Utility;GTK;X-WinTC-Dev; diff --git a/private/play/dnd/wintc-dnd-test.desktop b/private/play/dnd/wintc-dnd-test.desktop index 44bf1a4..b3b2e12 100644 --- a/private/play/dnd/wintc-dnd-test.desktop +++ b/private/play/dnd/wintc-dnd-test.desktop @@ -6,4 +6,4 @@ Icon=wintc-dnd-test Terminal=false StartupNotify=false Type=Application -Categories=Utility;GTK; +Categories=Utility;GTK;X-WinTC-Dev; diff --git a/private/play/menutest/wintc-menutest.desktop b/private/play/menutest/wintc-menutest.desktop index b25f3fc..a1546bf 100644 --- a/private/play/menutest/wintc-menutest.desktop +++ b/private/play/menutest/wintc-menutest.desktop @@ -6,4 +6,4 @@ Icon=wintc-menutest Terminal=false StartupNotify=false Type=Application -Categories=Utility;GTK; +Categories=Utility;GTK;X-WinTC-Dev; diff --git a/private/play/patest/wintc-patest.desktop b/private/play/patest/wintc-patest.desktop index 88a0e8d..2f0456e 100644 --- a/private/play/patest/wintc-patest.desktop +++ b/private/play/patest/wintc-patest.desktop @@ -6,4 +6,4 @@ Icon=wintc-patest Terminal=false StartupNotify=false Type=Application -Categories=Utility;GTK; +Categories=Utility;GTK;X-WinTC-Dev; diff --git a/private/samples/gtk3/hello/wintc-hello.desktop b/private/samples/gtk3/hello/wintc-hello.desktop index 4a04fa9..be50c3e 100644 --- a/private/samples/gtk3/hello/wintc-hello.desktop +++ b/private/samples/gtk3/hello/wintc-hello.desktop @@ -6,4 +6,4 @@ Icon=wintc-hello Terminal=false StartupNotify=false Type=Application -Categories=Utility;GTK; +Categories=Utility;GTK;X-WinTC-Dev; diff --git a/private/samples/gtk4/hello/wintc-hello4.desktop b/private/samples/gtk4/hello/wintc-hello4.desktop index 8f182db..0c96a6a 100644 --- a/private/samples/gtk4/hello/wintc-hello4.desktop +++ b/private/samples/gtk4/hello/wintc-hello4.desktop @@ -6,4 +6,4 @@ Icon=wintc-hello Terminal=false StartupNotify=false Type=Application -Categories=Utility;GTK; +Categories=Utility;GTK;X-WinTC-Dev; diff --git a/shell/taskband/CMakeLists.txt b/shell/taskband/CMakeLists.txt index 5d2dacc..37a35a2 100644 --- a/shell/taskband/CMakeLists.txt +++ b/shell/taskband/CMakeLists.txt @@ -56,6 +56,8 @@ set( src/window.h src/start/menumod.c src/start/menumod.h + src/start/mfu.c + src/start/mfu.h src/start/personal.c src/start/personal.h src/start/progmenu.c diff --git a/shell/taskband/src/start/mfu.c b/shell/taskband/src/start/mfu.c new file mode 100644 index 0000000..319e947 --- /dev/null +++ b/shell/taskband/src/start/mfu.c @@ -0,0 +1,464 @@ +#include +#include +#include +#include + +#include "mfu.h" +#include "util.h" + +#define MFU_WINNERS_FILENAME "mfu-winners" + +// +// PRIVATE ENUMS +// +enum +{ + SIGNAL_MFU_UPDATED = 0, + N_SIGNALS +}; + +// +// PRIVATE STRUCTS +// +typedef struct _WinTCStartMfuEntry +{ + GarconMenuItem* garcon_item; + gint score; +} WinTCStartMfuEntry; + +// +// FORWARD DECLARATIONS +// +static void wintc_start_mfu_tracker_dispose( + GObject* object +); + +static void wintc_start_mfu_tracker_bubble_entry( + WinTCStartMfuTracker* mfu_tracker, + GList* li_entry +); +static void wintc_start_mfu_tracker_load_winners( + WinTCStartMfuTracker* mfu_tracker +); +static void wintc_start_mfu_tracker_save_winners( + WinTCStartMfuTracker* mfu_tracker +); + +// +// STATIC DATA +// +static gint wintc_start_mfu_tracker_signals[N_SIGNALS] = { 0 }; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCStartMfuTracker +{ + GObject __parent__; + + gboolean loading_winners; + + GList* list_scores; + GHashTable* map_cmdline_to_list_item; + GarconMenu* menu_all; +}; + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCStartMfuTracker, + wintc_start_mfu_tracker, + G_TYPE_OBJECT +) + +static void wintc_start_mfu_tracker_class_init( + WinTCStartMfuTrackerClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = wintc_start_mfu_tracker_dispose; + + wintc_start_mfu_tracker_signals[SIGNAL_MFU_UPDATED] = + g_signal_new( + "mfu-updated", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0 + ); +} + +static void wintc_start_mfu_tracker_init( + WinTCStartMfuTracker* self +) +{ + GError* error = NULL; + + // Internally, we track pretty much every possible item that could be + // launched + // + self->menu_all = + garcon_menu_new_for_path( + WINTC_ASSETS_DIR "/shell-res/all.menu" + ); + + if (!garcon_menu_load(self->menu_all, NULL, &error)) + { + wintc_display_error_and_clear(&error, NULL); + return; + } + + // Populate collections with the garcon items + // + // --> The list tracks the score of each garcon item, as programs are + // launched, their score increases and they are 'bubbled up' in the + // score list + // --> The map allows translation of cmdline to the item in the + // list + // + GList* elements = garcon_menu_get_elements(self->menu_all); + + self->map_cmdline_to_list_item = + g_hash_table_new_full( + g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + NULL + ); + + for (GList* iter = elements; iter; iter = iter->next) + { + WinTCStartMfuEntry* entry = g_new0(WinTCStartMfuEntry, 1); + + entry->garcon_item = GARCON_MENU_ITEM(iter->data); + + self->list_scores = + g_list_prepend( + self->list_scores, + entry + ); + + g_hash_table_insert( + self->map_cmdline_to_list_item, + garcon_menu_item_get_command_expanded(entry->garcon_item), + self->list_scores + ); + } + + self->list_scores = g_list_reverse(self->list_scores); + + g_list_free(elements); + + // Load the previous winners (if applicable) + // + wintc_start_mfu_tracker_load_winners(self); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_start_mfu_tracker_dispose( + GObject* object +) +{ + WinTCStartMfuTracker* mfu_tracker = WINTC_START_MFU_TRACKER(object); + + g_hash_table_destroy( + g_steal_pointer(&(mfu_tracker->map_cmdline_to_list_item)) + ); + g_clear_list( + &(mfu_tracker->list_scores), + (GDestroyNotify) g_free + ); + g_clear_object(&(mfu_tracker->menu_all)); + + (G_OBJECT_CLASS(wintc_start_mfu_tracker_parent_class)) + ->dispose(object); +} + +// +// PUBLIC FUNCTIONS +// +WinTCStartMfuTracker* wintc_start_mfu_tracker_get_default(void) +{ + static WinTCStartMfuTracker* singleton_tracker = NULL; + + if (!singleton_tracker) + { + singleton_tracker = + WINTC_START_MFU_TRACKER( + g_object_new( + WINTC_TYPE_START_MFU_TRACKER, + NULL + ) + ); + } + + g_object_ref(singleton_tracker); + + return singleton_tracker; +} + +void wintc_start_mfu_tracker_bump_cmdline( + WinTCStartMfuTracker* mfu_tracker, + const gchar* cmdline +) +{ + GList* li_entry = + g_hash_table_lookup( + mfu_tracker->map_cmdline_to_list_item, + cmdline + ); + + // For now, just find the Garcon item and report on it + // + if (li_entry) + { + WinTCStartMfuEntry* entry = (WinTCStartMfuEntry*) li_entry->data; + + entry->score++; + + wintc_start_mfu_tracker_bubble_entry( + mfu_tracker, + li_entry + ); + + WINTC_LOG_DEBUG( + "mfu: bumped %s", + garcon_menu_item_get_name(entry->garcon_item) + ); + WINTC_LOG_DEBUG( + "mfu: new pos: %d", + g_list_position( + mfu_tracker->list_scores, + li_entry + ) + ); + } + else + { + WINTC_LOG_DEBUG("mfu: unknown cmdline '%s'", cmdline); + } +} + +GList* wintc_start_mfu_tracker_get_mfu_list( + WinTCStartMfuTracker* mfu_tracker +) +{ + GList* list_mfu = NULL; + + // We return the top 6 items here, if possible + // + gint i; + GList* iter; + + for ( + iter = mfu_tracker->list_scores, i = 0; + iter && i < 6; + iter = iter->next, i++ + ) + { + list_mfu = + g_list_prepend( + list_mfu, + ((WinTCStartMfuEntry*) iter->data)->garcon_item + ); + } + + return g_list_reverse(list_mfu); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_start_mfu_tracker_bubble_entry( + WinTCStartMfuTracker* mfu_tracker, + GList* li_entry +) +{ + WinTCStartMfuEntry* this_entry = (WinTCStartMfuEntry*) li_entry->data; + + // Very crude method of bubbling up the item based on score, we check + // + GList* iter; + + for (iter = li_entry->prev; iter; iter = iter->prev) + { + WinTCStartMfuEntry* cmp_entry = (WinTCStartMfuEntry*) iter->data; + + if (this_entry->score < cmp_entry->score) + { + break; + } + } + + // Did we move at all? + // + if (iter == li_entry->prev) + { + return; + } + + // Reinsert the list item in the right place + // + mfu_tracker->list_scores = + g_list_remove_link( + mfu_tracker->list_scores, + li_entry + ); + + if (!iter) + { + mfu_tracker->list_scores = + g_list_insert_before_link( + mfu_tracker->list_scores, + mfu_tracker->list_scores, + li_entry + ); + } + else + { + mfu_tracker->list_scores = + g_list_insert_before_link( + mfu_tracker->list_scores, + iter->next, + li_entry + ); + } + + g_signal_emit( + mfu_tracker, + wintc_start_mfu_tracker_signals[SIGNAL_MFU_UPDATED], + 0 + ); + + if (!(mfu_tracker->loading_winners)) + { + wintc_start_mfu_tracker_save_winners(mfu_tracker); + } +} + +static void wintc_start_mfu_tracker_load_winners( + WinTCStartMfuTracker* mfu_tracker +) +{ + GError* error = NULL; + + // Reset all scores to 0 to start with, a clean slate + // + for (GList* iter = mfu_tracker->list_scores; iter; iter = iter->next) + { + ((WinTCStartMfuEntry*) iter->data)->score = 0; + } + + // Grab the winners + // + GList* list_winners; + gchar* winners_text = NULL; + + if ( + !wintc_profile_get_file_contents( + WINTC_COMPONENT_SHELL, + MFU_WINNERS_FILENAME, + &winners_text, + NULL, + &error + ) + ) + { + wintc_log_error_and_clear(&error); + return; + } + + list_winners = wintc_list_read_from_string(winners_text); + g_free(winners_text); + + // Translate to list and look up the winners, set some initial scores + // + gint i; + GList* iter; + + mfu_tracker->loading_winners = TRUE; // Will prevent saving during bumps + + for ( + iter = list_winners, i = 6; + iter && i > 0; + iter = iter->next, i-- + ) + { + GList* li_entry = + g_hash_table_lookup( + mfu_tracker->map_cmdline_to_list_item, + (gchar*) iter->data + ); + + if (!li_entry) + { + continue; + } + + ((WinTCStartMfuEntry*) li_entry->data)->score = i * 2; + + wintc_start_mfu_tracker_bubble_entry( + mfu_tracker, + li_entry + ); + } + + mfu_tracker->loading_winners = FALSE; + + g_list_free_full(list_winners, (GDestroyNotify) g_free); +} + +static void wintc_start_mfu_tracker_save_winners( + WinTCStartMfuTracker* mfu_tracker +) +{ + GError* error = NULL; + + // Get all the cmdlines for the winners + // + GList* list_cmd = NULL; + GList* list_mfu = wintc_start_mfu_tracker_get_mfu_list(mfu_tracker); + + for (GList* iter = list_mfu; iter; iter = iter->next) + { + list_cmd = + g_list_prepend( + list_cmd, + garcon_menu_item_get_command_expanded( + GARCON_MENU_ITEM(iter->data) + ) + ); + } + + list_cmd = g_list_reverse(list_cmd); + + // Write out to profile + // + gchar* winners_file = + wintc_list_implode_strings(list_cmd); + + if ( + !wintc_profile_set_file_contents( + WINTC_COMPONENT_SHELL, + MFU_WINNERS_FILENAME, + winners_file, + -1, + &error + ) + ) + { + wintc_log_error_and_clear(&error); + } + + g_free(winners_file); + g_list_free_full(list_cmd, (GDestroyNotify) g_free); + g_list_free(list_mfu); +} diff --git a/shell/taskband/src/start/mfu.h b/shell/taskband/src/start/mfu.h new file mode 100644 index 0000000..c887a8a --- /dev/null +++ b/shell/taskband/src/start/mfu.h @@ -0,0 +1,30 @@ +#ifndef __START_MFU_H__ +#define __START_MFU_H__ + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_START_MFU_TRACKER (wintc_start_mfu_tracker_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCStartMfuTracker, + wintc_start_mfu_tracker, + WINTC, + START_MFU_TRACKER, + GObject +) + +// +// PUBLIC FUNCTIONS +// +WinTCStartMfuTracker* wintc_start_mfu_tracker_get_default(void); + +void wintc_start_mfu_tracker_bump_cmdline( + WinTCStartMfuTracker* mfu_tracker, + const gchar* cmdline +); +GList* wintc_start_mfu_tracker_get_mfu_list( + WinTCStartMfuTracker* mfu_tracker +); + +#endif diff --git a/shell/taskband/src/start/personal.c b/shell/taskband/src/start/personal.c index fb03dee..2698471 100644 --- a/shell/taskband/src/start/personal.c +++ b/shell/taskband/src/start/personal.c @@ -12,6 +12,7 @@ #include #include "menumod.h" +#include "mfu.h" #include "personal.h" #include "progmenu.h" #include "shared.h" @@ -55,16 +56,15 @@ static GtkWidget* create_personal_menu_item_from_desktop_entry( const gchar* comment, const gchar* generic_name ); -static GtkWidget* create_personal_menu_item_from_garcon_item( - GarconMenuItem* garcon_item, - StartSignalTuple* signal_tuple -); static void refresh_personal_menu( WinTCToolbarStart* toolbar_start ); static void refresh_userpic( WinTCToolbarStart* toolbar_start ); +static void update_personal_menu_mfu_items( + WinTCToolbarStart* toolbar_start +); static void clear_signal_tuple( StartSignalTuple* tuple @@ -104,11 +104,20 @@ static void on_menu_shell_submenu_selection_done( GtkMenuShell* self, gpointer user_data ); +static void on_mfu_tracker_updated( + WinTCStartMfuTracker* self, + gpointer user_data +); static void on_personal_menu_hide( GtkWidget* self, gpointer user_data ); +// +// STATIC DATA +// +static GQuark S_QUARK_PERSONAL_ITEM_TUPLE = 0; + // // INTERNAL FUNCTIONS // @@ -125,6 +134,12 @@ void create_personal_menu( { GtkBuilder* builder; + if (!S_QUARK_PERSONAL_ITEM_TUPLE) + { + S_QUARK_PERSONAL_ITEM_TUPLE = + g_quark_from_static_string("personal-tuple"); + } + // Set default states // toolbar_start->personal.sync_menu_refresh = TRUE; @@ -515,34 +530,37 @@ static GtkWidget* create_personal_menu_item( // Attempt to load the icon... // - GdkPixbuf* pixbuf_icon = - gtk_icon_theme_load_icon( - gtk_icon_theme_get_default(), - icon_name ? icon_name : "application-x-generic", - PROGRAM_ICON_SIZE, - GTK_ICON_LOOKUP_FORCE_SIZE, - NULL - ); - - if (!pixbuf_icon) + if (icon_name) { - gtk_icon_theme_load_icon( - gtk_icon_theme_get_default(), - "application-x-generic", - PROGRAM_ICON_SIZE, - GTK_ICON_LOOKUP_FORCE_SIZE, - NULL - ); - } + GdkPixbuf* pixbuf_icon = + gtk_icon_theme_load_icon( + gtk_icon_theme_get_default(), + icon_name, + PROGRAM_ICON_SIZE, + GTK_ICON_LOOKUP_FORCE_SIZE, + NULL + ); - if (pixbuf_icon) - { - gtk_image_set_from_pixbuf( - GTK_IMAGE(image_icon), - pixbuf_icon - ); + if (!pixbuf_icon) + { + gtk_icon_theme_load_icon( + gtk_icon_theme_get_default(), + "application-x-generic", + PROGRAM_ICON_SIZE, + GTK_ICON_LOOKUP_FORCE_SIZE, + NULL + ); + } - g_object_unref(pixbuf_icon); + if (pixbuf_icon) + { + gtk_image_set_from_pixbuf( + GTK_IMAGE(image_icon), + pixbuf_icon + ); + + g_object_unref(pixbuf_icon); + } } // Set up label properties @@ -563,6 +581,10 @@ static GtkWidget* create_personal_menu_item( // Ensure image widget always requests the right size, in case no icon was // loaded // + gtk_image_set_pixel_size( + GTK_IMAGE(image_icon), + PROGRAM_ICON_SIZE + ); gtk_widget_set_size_request( image_icon, PROGRAM_ICON_SIZE, @@ -737,36 +759,6 @@ static GtkWidget* create_personal_menu_item_from_desktop_entry( return menu_item; } -static GtkWidget* create_personal_menu_item_from_garcon_item( - GarconMenuItem* garcon_item, - StartSignalTuple* signal_tuple -) -{ - GtkWidget* menu_item = - create_personal_menu_item( - GTK_MENU_SHELL( - signal_tuple->toolbar_start->personal.menubar_programs - ), - garcon_menu_item_get_icon_name(garcon_item), - garcon_menu_item_get_name(garcon_item), - garcon_menu_item_get_comment(garcon_item), - NULL - ); - - signal_tuple->is_action = FALSE; - signal_tuple->user_data = - garcon_menu_item_get_command_expanded(garcon_item); - - g_signal_connect( - menu_item, - "activate", - G_CALLBACK(on_menu_item_launcher_activate), - signal_tuple - ); - - return menu_item; -} - static void refresh_personal_menu( WinTCToolbarStart* toolbar_start ) @@ -860,54 +852,58 @@ static void refresh_personal_menu( gtk_separator_menu_item_new() ); - // Add MFU items - // FIXME: In future we will need some >>>algorithm<<< to come up with a - // 'top' programs list to display here, and also reserve the last - // slot for the latest newly added program, if any + // Loop over to add the MFU items // - // For now we simply grab the first items that are pulled in via the - // all.menu file - // - GarconMenu* all_entries = garcon_menu_new_for_path( - WINTC_ASSETS_DIR "/shell-res/all.menu" - ); - GError* error = NULL; - - if (!garcon_menu_load(all_entries, NULL, &error)) + for (gint i = 0; i < MAX_MFU_ITEM_COUNT; i++) { - wintc_display_error_and_clear(&error, NULL); - return; - } + GtkWidget* personal_item = + create_personal_menu_item( + GTK_MENU_SHELL( + toolbar_start->personal.menubar_programs + ), + NULL, + NULL, + NULL, + NULL + ); - // Loop over to add the items - // - GList* elements = garcon_menu_get_elements(all_entries); - GList* li = elements; - gint i = 0; - - while (i < MAX_MFU_ITEM_COUNT && li != NULL) - { tuple = &g_array_index( toolbar_start->personal.tuples_programs, StartSignalTuple, DEFAULT_ITEM_COUNT + i ); + + tuple->is_action = FALSE; tuple->toolbar_start = toolbar_start; + g_object_set_qdata( + G_OBJECT(personal_item), + S_QUARK_PERSONAL_ITEM_TUPLE, + tuple + ); + + g_signal_connect( + personal_item, + "activate", + G_CALLBACK(on_menu_item_launcher_activate), + tuple + ); + gtk_menu_shell_append( GTK_MENU_SHELL(toolbar_start->personal.menubar_programs), - create_personal_menu_item_from_garcon_item( - GARCON_MENU_ITEM(li->data), - tuple - ) + personal_item ); - - i++; - li = li->next; } - g_list_free(g_steal_pointer(&elements)); + update_personal_menu_mfu_items(toolbar_start); + + g_signal_connect( + wintc_start_mfu_tracker_get_default(), + "mfu-updated", + G_CALLBACK(on_mfu_tracker_updated), + toolbar_start + ); // Re-append All Programs items // @@ -947,6 +943,135 @@ static void refresh_userpic( ); } +static void update_personal_menu_mfu_items( + WinTCToolbarStart* toolbar_start +) +{ + // Find the first MFU item + // + GList* li_mfu = NULL; + GList* list_children = + gtk_container_get_children( + GTK_CONTAINER(toolbar_start->personal.menubar_programs) + ); + + for (GList* iter = list_children; iter; iter = iter->next) + { + if ( + g_object_get_qdata( + G_OBJECT(iter->data), + S_QUARK_PERSONAL_ITEM_TUPLE + ) + ) + { + li_mfu = iter; + break; + } + } + + if (!li_mfu) + { + g_critical("%s", "taskband: somehow found no mfu items"); + g_list_free(list_children); + return; + } + + // Start updating the items + // + GList* list_mfu = + wintc_start_mfu_tracker_get_mfu_list( + wintc_start_mfu_tracker_get_default() + ); + + for ( + GList* iter = list_mfu; + iter; + iter = iter->next, li_mfu = li_mfu->next + ) + { + StartSignalTuple* tuple = + g_object_get_qdata( + G_OBJECT(li_mfu->data), + S_QUARK_PERSONAL_ITEM_TUPLE + ); + + // Protect against the case where there could be more MFU items tracked + // than personal menu items + // + if (!tuple) + { + break; + } + + // Update the menu item and tuple + // + GarconMenuItem* garcon_item = GARCON_MENU_ITEM(iter->data); + const gchar* icon_name = garcon_menu_item_get_icon_name( + garcon_item + ); + GList* list_menu_children; + GtkWidget* menu_item = GTK_WIDGET(li_mfu->data); + GdkPixbuf* pixbuf_icon = NULL; + + list_menu_children = + gtk_container_get_children( + GTK_CONTAINER( + gtk_bin_get_child(GTK_BIN(menu_item)) + ) + ); + + if (icon_name) + { + pixbuf_icon = + gtk_icon_theme_load_icon( + gtk_icon_theme_get_default(), + icon_name, + PROGRAM_ICON_SIZE, + GTK_ICON_LOOKUP_FORCE_SIZE, + NULL + ); + } + + if (!pixbuf_icon) + { + gtk_icon_theme_load_icon( + gtk_icon_theme_get_default(), + "application-x-generic", + PROGRAM_ICON_SIZE, + GTK_ICON_LOOKUP_FORCE_SIZE, + NULL + ); + } + + gtk_image_set_from_pixbuf( + GTK_IMAGE(list_menu_children->data), + pixbuf_icon + ); + gtk_label_set_text( + GTK_LABEL(list_menu_children->next->data), + garcon_menu_item_get_name(garcon_item) + ); + gtk_widget_set_tooltip_text( + menu_item, + garcon_menu_item_get_comment(garcon_item) + ); + + g_free(tuple->user_data); + tuple->user_data = + garcon_menu_item_get_command_expanded(garcon_item); + + if (pixbuf_icon) + { + g_object_unref(pixbuf_icon); + } + + g_list_free(list_menu_children); + } + + g_list_free(list_mfu); + g_list_free(list_children); +} + // // CALLBACKS // @@ -1055,7 +1180,13 @@ static void on_menu_item_launcher_activate( } else { - wintc_launch_command(tuple->user_data, &error); + if (wintc_launch_command(tuple->user_data, &error)) + { + wintc_start_mfu_tracker_bump_cmdline( + wintc_start_mfu_tracker_get_default(), + tuple->user_data + ); + } } if (error) @@ -1128,12 +1259,22 @@ static void on_menu_shell_submenu_selection_done( toolbar_start->sync_menu_should_close = TRUE; } +static void on_mfu_tracker_updated( + WINTC_UNUSED(WinTCStartMfuTracker* self), + gpointer user_data +) +{ + WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data); + + update_personal_menu_mfu_items(toolbar_start); +} + static void on_personal_menu_hide( WINTC_UNUSED(GtkWidget* self), gpointer user_data ) { - WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data); + WinTCToolbarStart* toolbar_start = WINTC_TOOLBAR_START(user_data); // Track the last closed time, important for toggling the menu properly // (see toolbar.c) diff --git a/shell/taskband/src/start/progmenu.c b/shell/taskband/src/start/progmenu.c index a074f49..6acfc58 100644 --- a/shell/taskband/src/start/progmenu.c +++ b/shell/taskband/src/start/progmenu.c @@ -13,6 +13,7 @@ #include #include "progmenu.h" +#include "mfu.h" #define WINTC_COMPONENT_START_MENU "start-menu" @@ -27,8 +28,9 @@ #define K_DIR_GAMES "/Games" #define K_DIR_STARTUP "/Startup" -#define K_DIR_GNOME "/GNOME" -#define K_DIR_KDE "/KDE" +#define K_DIR_GNOME "/GNOME" +#define K_DIR_KDE "/KDE" +#define K_DIR_WINTC_DEV "/WinTC Developer" #define K_DIR_DOOM "/DOOM" #define K_DIR_LIBREOFFICE "/LibreOffice" @@ -190,8 +192,9 @@ static const gchar* S_EXCLUDED_CATEGORIES[] = { }; static const gchar* S_VENDOR_MAPPINGS[] = { - "GNOME", K_DIR_GNOME, - "KDE", K_DIR_KDE + "GNOME", K_DIR_GNOME, + "KDE", K_DIR_KDE, + "X-WinTC-Dev", K_DIR_WINTC_DEV }; static DesktopAppInfoFilterFunc S_ENTRY_FILTERS[] = { @@ -1865,17 +1868,19 @@ static void action_launch( WINTC_UNUSED(gpointer user_data) ) { - GError* error = NULL; + const gchar* cmdline = g_variant_get_string(parameter, NULL); + GError* error = NULL; - if ( - !wintc_launch_command( - g_variant_get_string(parameter, NULL), - &error - ) - ) + if (!wintc_launch_command(cmdline, &error)) { wintc_display_error_and_clear(&error, NULL); + return; } + + wintc_start_mfu_tracker_bump_cmdline( + wintc_start_mfu_tracker_get_default(), + cmdline + ); } static const gchar* filter_doom( diff --git a/shell/taskband/src/systray/icon.c b/shell/taskband/src/systray/icon.c index 6a14264..7b32230 100644 --- a/shell/taskband/src/systray/icon.c +++ b/shell/taskband/src/systray/icon.c @@ -404,7 +404,7 @@ static void wintc_notif_area_icon_unmap( } (GTK_WIDGET_CLASS(wintc_notif_area_icon_parent_class)) - ->map(widget); + ->unmap(widget); } static void wintc_notif_area_icon_unrealize( diff --git a/shell/taskband/src/window.c b/shell/taskband/src/window.c index e1d9c7d..ff8a73c 100644 --- a/shell/taskband/src/window.c +++ b/shell/taskband/src/window.c @@ -160,7 +160,11 @@ static void wintc_taskband_window_dispose( { WinTCTaskbandWindow* taskband = WINTC_TASKBAND_WINDOW(object); - g_clear_slist(&(taskband->toolbars), g_object_unref); + g_clear_slist(&(taskband->toolbars), NULL); + + g_clear_object(&(taskband->uictl_notifarea)); + g_clear_object(&(taskband->uictl_start)); + g_clear_object(&(taskband->uictl_taskbuttons)); (G_OBJECT_CLASS(wintc_taskband_window_parent_class))->dispose(object); } diff --git a/tools/resvwr/src/window.c b/tools/resvwr/src/window.c index 9d81d48..9884c1d 100644 --- a/tools/resvwr/src/window.c +++ b/tools/resvwr/src/window.c @@ -364,7 +364,7 @@ static void wintc_resvwr_window_refresh( } else { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear(&error, GTK_WINDOW(wnd)); } g_object_unref(builder); diff --git a/tools/resvwr/wintc-resvwr.desktop b/tools/resvwr/wintc-resvwr.desktop index 3f980d5..001d29e 100644 --- a/tools/resvwr/wintc-resvwr.desktop +++ b/tools/resvwr/wintc-resvwr.desktop @@ -6,4 +6,4 @@ Icon=wintc-resvwr Terminal=false StartupNotify=false Type=Application -Categories=Development;GTK; +Categories=Development;GTK;X-WinTC-Dev;