diff --git a/shared/comgtk/CMakeLists.txt b/shared/comgtk/CMakeLists.txt index 80e149f..da1b223 100644 --- a/shared/comgtk/CMakeLists.txt +++ b/shared/comgtk/CMakeLists.txt @@ -24,6 +24,7 @@ include(../../packaging/cmake-inc/libraries/CMakeLists.txt) include(../../packaging/cmake-inc/linking/CMakeLists.txt) include(../../packaging/cmake-inc/packaging/CMakeLists.txt) +wintc_resolve_library(gio-2.0 GIO) wintc_resolve_library(glib-2.0 GLIB) wintc_resolve_library(gtk+-3.0 GTK3) @@ -50,6 +51,8 @@ add_library( public/marshal.h src/memory.c public/memory.h + src/menu.c + public/menu.h src/msgbox.c public/msgbox.h src/profile.c @@ -85,18 +88,21 @@ target_include_directories( libwintc-comgtk SYSTEM BEFORE + PRIVATE ${GIO_INCLUDE_DIRS} PRIVATE ${GLIB_INCLUDE_DIRS} PRIVATE ${GTK3_INCLUDE_DIRS} ) target_link_directories( libwintc-comgtk + PRIVATE ${GIO_LIBRARY_DIRS} PRIVATE ${GLIB_LIBRARY_DIRS} PRIVATE ${GTK3_LIBRARY_DIRS} ) target_link_libraries( libwintc-comgtk + PRIVATE ${GIO_LIBRARIES} PRIVATE ${GLIB_LIBRARIES} PRIVATE ${GTK3_LIBRARIES} ) diff --git a/shared/comgtk/public/libapi.h.in b/shared/comgtk/public/libapi.h.in index e72580a..14939ee 100644 --- a/shared/comgtk/public/libapi.h.in +++ b/shared/comgtk/public/libapi.h.in @@ -11,6 +11,7 @@ #include "@LIB_HEADER_DIR@/list.h" #include "@LIB_HEADER_DIR@/marshal.h" #include "@LIB_HEADER_DIR@/memory.h" +#include "@LIB_HEADER_DIR@/menu.h" #include "@LIB_HEADER_DIR@/msgbox.h" #include "@LIB_HEADER_DIR@/profile.h" #include "@LIB_HEADER_DIR@/regex.h" diff --git a/shared/comgtk/public/menu.h b/shared/comgtk/public/menu.h new file mode 100644 index 0000000..f7fbf43 --- /dev/null +++ b/shared/comgtk/public/menu.h @@ -0,0 +1,25 @@ +/** @file */ + +#ifndef __COMGTK_MENU_H__ +#define __COMGTK_MENU_H__ + +#include +#include + +// +// PUBLIC FUNCTIONS +// + +/** + * Merges multiple menu models to create a new one. + * + * @param menu1 The first menu. + * @param ... The other menus to merge, followed by NULL. + * @return A new menu, created by merging the provided ones. + */ +GMenuModel* wintc_menu_model_merge( + GMenuModel* menu1, + ... +); + +#endif diff --git a/shared/comgtk/src/menu.c b/shared/comgtk/src/menu.c new file mode 100644 index 0000000..318c513 --- /dev/null +++ b/shared/comgtk/src/menu.c @@ -0,0 +1,71 @@ +#include +#include + +#include "../public/menu.h" + +// +// FORWARD DECLARATIONS +// +static void menu_merge_model( + GMenu* menu, + GMenuModel* menu_model +); + +// +// PUBLIC FUNCTIONS +// +GMenuModel* wintc_menu_model_merge( + GMenuModel* menu1, + ... +) +{ + GMenu* menu = g_menu_new(); + + menu_merge_model(menu, menu1); + + // Iterate over models + // + va_list ap; + GMenuModel* next_model; + + va_start(ap, menu1); + + next_model = va_arg(ap, GMenuModel*); + + while (next_model) + { + menu_merge_model(menu, next_model); + + next_model = va_arg(ap, GMenuModel*); + } + + va_end(ap); + + return G_MENU_MODEL(menu); +} + +// +// PRIVATE FUNCTIONS +// +static void menu_merge_model( + GMenu* menu, + GMenuModel* menu_model +) +{ + // This seems really dumb... but in order to merge menus you essentially + // have to copy TWICE - first to copy out into a new item, then copy that + // item into the target menu + // + // I would've thought there be a better way, but I am not sure there is + // + gint n_items = g_menu_model_get_n_items(menu_model); + + for (gint i = 0; i < n_items; i++) + { + GMenuItem* menu_item = g_menu_item_new_from_model(menu_model, i); + + g_menu_append_item(menu, menu_item); + + g_object_unref(menu_item); + } +} diff --git a/shared/exec/src/exec.c b/shared/exec/src/exec.c index 600c8bd..cf7e172 100644 --- a/shared/exec/src/exec.c +++ b/shared/exec/src/exec.c @@ -90,6 +90,10 @@ gboolean wintc_launch_command( if (error != NULL) { g_propagate_error(out_error, error); + + g_free(real_cmdline); + g_free(tmp_cmdline); + return FALSE; } @@ -188,8 +192,10 @@ static gboolean parse_file_in_cmdline( { g_propagate_error(out_error, error); } - - g_clear_error(&error); + else + { + g_clear_error(&error); + } WINTC_SAFE_REF_SET(out_cmdline, g_strdup(cmdline)); diff --git a/shared/shell/CMakeLists.txt b/shared/shell/CMakeLists.txt index 07746c0..746a028 100644 --- a/shared/shell/CMakeLists.txt +++ b/shared/shell/CMakeLists.txt @@ -28,6 +28,9 @@ wintc_resolve_library(wintc-comgtk WINTC_COMGTK) wintc_resolve_library(wintc-exec WINTC_EXEC) wintc_resolve_library(wintc-shcommon WINTC_SHCOMMON) wintc_resolve_library(wintc-shellext WINTC_SHELLEXT) +wintc_resolve_library(wintc-shlang WINTC_SHLANG) + +wintc_compile_resources() add_library( libwintc-shell @@ -39,6 +42,7 @@ add_library( public/error.h src/icnvwbeh.c public/icnvwbeh.h + src/resources.c src/trevwbeh.c public/trevwbeh.h src/nmspace.c @@ -76,6 +80,7 @@ target_include_directories( PRIVATE ${WINTC_EXEC_INCLUDE_DIRS} PRIVATE ${WINTC_SHCOMMON_INCLUDE_DIRS} PRIVATE ${WINTC_SHELLEXT_INCLUDE_DIRS} + PRIVATE ${WINTC_SHLANG_INCLUDE_DIRS} ) target_link_directories( @@ -87,6 +92,7 @@ target_link_directories( PRIVATE ${WINTC_EXEC_LIBRARY_DIRS} PRIVATE ${WINTC_SHCOMMON_LIBRARY_DIRS} PRIVATE ${WINTC_SHELLEXT_LIBRARY_DIRS} + PRIVATE ${WINTC_SHLANG_LIBRARY_DIRS} ) target_link_libraries( @@ -98,6 +104,7 @@ target_link_libraries( PRIVATE ${WINTC_EXEC_LIBRARIES} PRIVATE ${WINTC_SHCOMMON_LIBRARIES} PRIVATE ${WINTC_SHELLEXT_LIBRARIES} + PRIVATE ${WINTC_SHLANG_LIBRARIES} ) # Installation diff --git a/shared/shell/src/icnvwbeh.c b/shared/shell/src/icnvwbeh.c index 9c89a23..f553db4 100644 --- a/shared/shell/src/icnvwbeh.c +++ b/shared/shell/src/icnvwbeh.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "../public/browser.h" #include "../public/icnvwbeh.h" @@ -30,12 +31,38 @@ static void wintc_sh_icon_view_behaviour_set_property( GParamSpec* pspec ); +static void action_view_operation( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); + +static gboolean on_icon_view_button_press_event( + GtkIconView* self, + GdkEventButton* event, + gpointer user_data +); static void on_icon_view_item_activated( GtkIconView* self, GtkTreePath* path, gpointer user_data ); +// +// STATIC DATA +// +static GSimpleAction* s_action_noop = NULL; + +static GActionEntry s_actions[] = { + { + .name = "view-op", + .activate = action_view_operation, + .parameter_type = "i", + .state = NULL, + .change_state = NULL + } +}; + // // GTK OOP CLASS/INSTANCE DEFINITIONS // @@ -115,6 +142,40 @@ static void wintc_sh_icon_view_behaviour_constructed( return; } + // Define GActions + // + GSimpleActionGroup* action_group = g_simple_action_group_new(); + + if (!s_action_noop) + { + s_action_noop = + g_simple_action_new("no-op", NULL); + + g_simple_action_set_enabled( + s_action_noop, + FALSE + ); + } + + g_action_map_add_action_entries( + G_ACTION_MAP(action_group), + s_actions, + G_N_ELEMENTS(s_actions), + behaviour + ); + g_action_map_add_action( + G_ACTION_MAP(action_group), + G_ACTION(s_action_noop) + ); + + gtk_widget_insert_action_group( + behaviour->icon_view, + "control", + G_ACTION_GROUP(action_group) + ); + + g_object_unref(action_group); + // Attach stuff to view // gtk_icon_view_set_model( @@ -130,8 +191,19 @@ static void wintc_sh_icon_view_behaviour_constructed( 1 ); + gtk_icon_view_set_selection_mode( + GTK_ICON_VIEW(behaviour->icon_view), + GTK_SELECTION_MULTIPLE + ); + // Attach signals // + g_signal_connect( + behaviour->icon_view, + "button-press-event", + G_CALLBACK(on_icon_view_button_press_event), + behaviour + ); g_signal_connect( behaviour->icon_view, "item-activated", @@ -150,6 +222,13 @@ static void wintc_sh_icon_view_behaviour_dispose( WinTCShIconViewBehaviour* behaviour = WINTC_SH_ICON_VIEW_BEHAVIOUR(object); + // Cheeky? Deselect items from the icon view, otherwise it might throw + // GTK-Critical errors during destruction (?) + // + gtk_icon_view_unselect_all( + GTK_ICON_VIEW(behaviour->icon_view) + ); + g_clear_object(&(behaviour->browser)); g_clear_object(&(behaviour->icon_view)); @@ -204,6 +283,266 @@ WinTCShIconViewBehaviour* wintc_sh_icon_view_behaviour_new( // // CALLBACKS // +static void action_view_operation( + WINTC_UNUSED(GSimpleAction* action), + GVariant* parameter, + gpointer user_data +) +{ + WinTCShIconViewBehaviour* behaviour = + WINTC_SH_ICON_VIEW_BEHAVIOUR(user_data); + + WINTC_LOG_DEBUG("op selected: %d", g_variant_get_int32(parameter)); + + // Prepare items - need to convert the selected items to their hashes + // + GList* item_hashes = NULL; + GtkTreeModel* model = gtk_icon_view_get_model( + GTK_ICON_VIEW(behaviour->icon_view) + ); + GList* selected_items = gtk_icon_view_get_selected_items( + GTK_ICON_VIEW(behaviour->icon_view) + ); + GtkTreeIter tree_iter; + + for (GList* iter = selected_items; iter; iter = iter->next) + { + guint hash; + + gtk_tree_model_get_iter( + model, + &tree_iter, + (GtkTreePath*) iter->data + ); + + gtk_tree_model_get( + model, + &tree_iter, + 2, &hash, + -1 + ); + + WINTC_LOG_DEBUG("shell: icnvw - append item to op: %u", hash); + + item_hashes = + g_list_prepend( + item_hashes, + GUINT_TO_POINTER(hash) + ); + } + + item_hashes = g_list_reverse(item_hashes); + + g_list_free_full( + selected_items, + (GDestroyNotify) gtk_tree_path_free + ); + + // Ask the view to spawn the operation + // + GError* error = NULL; + WinTCShextOperation* operation; + gint operation_id = g_variant_get_int32(parameter); + WinTCIShextView* view = wintc_sh_browser_get_current_view( + behaviour->browser + ); + + operation = + wintc_ishext_view_spawn_operation( + view, + operation_id, + item_hashes, // Ownership transferred + &error + ); + +// g_list_free(item_hashes); + + if (!operation) + { + wintc_display_error_and_clear(&error); + return; + } + + // Execute! + // + GtkWidget* toplevel = gtk_widget_get_toplevel(behaviour->icon_view); + GtkWindow* wnd = NULL; + + if (GTK_IS_WINDOW(toplevel)) + { + wnd = GTK_WINDOW(toplevel); + } + + if (!(operation->func) (view, operation, wnd, &error)) + { + wintc_display_error_and_clear(&error); + } + + g_free(operation); +} + +static gboolean on_icon_view_button_press_event( + GtkIconView* self, + GdkEventButton* event, + gpointer user_data +) +{ + WinTCShIconViewBehaviour* behaviour = + WINTC_SH_ICON_VIEW_BEHAVIOUR(user_data); + + if (event->button == GDK_BUTTON_SECONDARY) + { + // We need to update the hit test target ourselves, since this signal + // always runs before the icon view handles clicks + // + gboolean found = FALSE; + GList* selected_items = gtk_icon_view_get_selected_items(self); + GtkTreePath* target_item = gtk_icon_view_get_path_at_pos( + self, + event->x, + event->y + ); + + if (target_item) + { + // See if the item is in the selected items, otherwise we must + // select this one + // + for (GList* iter = selected_items; iter; iter = iter->next) + { + GtkTreePath* check_item = (GtkTreePath*) iter->data; + + if (gtk_tree_path_compare(target_item, check_item) == 0) + { + found = TRUE; + break; + } + } + + if (!found) + { + gtk_icon_view_unselect_all(self); + gtk_icon_view_select_path(self, target_item); + } + } + + g_list_free_full( + selected_items, + (GDestroyNotify) gtk_tree_path_free + ); + + // Determine the target... + // the view itself? + // single view item? + // multiple view items? + // + GtkWidget* menu = NULL; + GMenuModel* menu_model = NULL; + + if (!target_item) // The view itself + { + menu_model = + wintc_ishext_view_get_operations_for_view( + wintc_sh_browser_get_current_view( + behaviour->browser + ) + ); + + if (menu_model) + { + // We merge the common context menu with this one + // + GtkBuilder* builder; + GMenuModel* menu_model_cmn; + GMenuModel* menu_model_merged; + + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/shell/menuctx.ui" + ); + + wintc_lc_builder_preprocess_widget_text(builder); + + menu_model_cmn = + G_MENU_MODEL( + g_object_ref( + gtk_builder_get_object(builder, "menu") + ) + ); + + menu_model_merged = + wintc_menu_model_merge( + menu_model_cmn, + menu_model, + NULL + ); + + // Now create the actual popup menu + // + menu = gtk_menu_new_from_model(menu_model_merged); + + g_object_unref(builder); + g_object_unref(menu_model); + g_object_unref(menu_model_cmn); + } + } + else // Item(s) + { + // Always use the menu for the target item, nothing fancy required + // + guint item_hash; + GtkTreeIter iter; + GtkTreeModel* model = gtk_icon_view_get_model(self); + + gtk_tree_model_get_iter( + model, + &iter, + target_item + ); + + gtk_tree_model_get( + model, + &iter, + 2, &item_hash, + -1 + ); + + menu_model = + wintc_ishext_view_get_operations_for_item( + wintc_sh_browser_get_current_view(behaviour->browser), + item_hash + ); + + if (menu_model) + { + menu = gtk_menu_new_from_model(menu_model); + + g_object_unref(menu_model); + } + + gtk_tree_path_free(target_item); + } + + if (menu) + { + gtk_menu_attach_to_widget( + GTK_MENU(menu), + behaviour->icon_view, + NULL + ); + + gtk_menu_popup_at_pointer( + GTK_MENU(menu), + (GdkEvent*) event + ); + } + + return GDK_EVENT_STOP; + } + + return GDK_EVENT_PROPAGATE; +} + static void on_icon_view_item_activated( GtkIconView* self, GtkTreePath* path, diff --git a/shared/shell/src/res/menuctx.ui b/shared/shell/src/res/menuctx.ui new file mode 100644 index 0000000..f2d494c --- /dev/null +++ b/shared/shell/src/res/menuctx.ui @@ -0,0 +1,18 @@ + + + +
+ + View + +
+
+ + Arrange Icons By + + + Refresh + +
+
+
diff --git a/shared/shell/src/res/menufs.ui b/shared/shell/src/res/menufs.ui new file mode 100644 index 0000000..0735414 --- /dev/null +++ b/shared/shell/src/res/menufs.ui @@ -0,0 +1,33 @@ + + + +
+ + control.no-op + Customize This Folder... + +
+
+ + control.no-op + Paste + + + control.no-op + Paste Shortcut + +
+
+ + control.no-op + New + +
+
+ + control.no-op + Properties + +
+
+
diff --git a/shared/shell/src/res/menufslf.ui b/shared/shell/src/res/menufslf.ui new file mode 100644 index 0000000..4ca4546 --- /dev/null +++ b/shared/shell/src/res/menufslf.ui @@ -0,0 +1,52 @@ + + + +
+ + control.view-op + Open + 1 + + + control.no-op + Open With + +
+
+ + control.no-op + Send To + +
+
+ + control.no-op + Cut + + + control.no-op + Copy + +
+
+ + control.no-op + Create Shortcut + + + control.no-op + Delete + + + control.no-op + Rename + +
+
+ + control.no-op + Properties + +
+
+
diff --git a/shared/shell/src/res/menufsvw.ui b/shared/shell/src/res/menufsvw.ui new file mode 100644 index 0000000..b85fbbc --- /dev/null +++ b/shared/shell/src/res/menufsvw.ui @@ -0,0 +1,62 @@ + + + +
+ + control.view-op + Open + 1 + + + control.no-op + Explore + + + control.no-op + Search + +
+
+ + control.no-op + Sharing and Security... + +
+
+ + control.no-op + Send To + +
+
+ + control.no-op + Cut + + + control.no-op + Copy + +
+
+ + control.no-op + Create Shortcut + + + control.no-op + Delete + + + control.no-op + Rename + +
+
+ + control.no-op + Properties + +
+
+
diff --git a/shared/shell/src/res/resources.xml b/shared/shell/src/res/resources.xml new file mode 100644 index 0000000..1ca6adb --- /dev/null +++ b/shared/shell/src/res/resources.xml @@ -0,0 +1,10 @@ + + + + + menuctx.ui + menufs.ui + menufslf.ui + menufsvw.ui + + diff --git a/shared/shell/src/vwcpl.c b/shared/shell/src/vwcpl.c index 40000ef..a6ce25e 100644 --- a/shared/shell/src/vwcpl.c +++ b/shared/shell/src/vwcpl.c @@ -38,22 +38,19 @@ static gboolean wintc_sh_view_cpl_activate_item( WinTCShextPathInfo* path_info, GError** error ); -static void wintc_sh_view_cpl_refresh_items( - WinTCIShextView* view -); -static void wintc_sh_view_cpl_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -); -static void wintc_sh_view_cpl_get_actions_for_view( - WinTCIShextView* view -); static const gchar* wintc_sh_view_cpl_get_display_name( WinTCIShextView* view ); static const gchar* wintc_sh_view_cpl_get_icon_name( WinTCIShextView* view ); +static GMenuModel* wintc_sh_view_cpl_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +); +static GMenuModel* wintc_sh_view_cpl_get_operations_for_view( + WinTCIShextView* view +); static void wintc_sh_view_cpl_get_parent_path( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -68,6 +65,15 @@ static guint wintc_sh_view_cpl_get_unique_hash( static gboolean wintc_sh_view_cpl_has_parent( WinTCIShextView* view ); +static void wintc_sh_view_cpl_refresh_items( + WinTCIShextView* view +); +static WinTCShextOperation* wintc_sh_view_cpl_spawn_operation( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error +); // // GLIB OOP/CLASS INSTANCE DEFINITIONS @@ -124,16 +130,17 @@ static void wintc_sh_view_cpl_ishext_view_interface_init( WinTCIShextViewInterface* iface ) { - iface->activate_item = wintc_sh_view_cpl_activate_item; - iface->refresh_items = wintc_sh_view_cpl_refresh_items; - iface->get_actions_for_item = wintc_sh_view_cpl_get_actions_for_item; - iface->get_actions_for_view = wintc_sh_view_cpl_get_actions_for_view; - iface->get_display_name = wintc_sh_view_cpl_get_display_name; - iface->get_icon_name = wintc_sh_view_cpl_get_icon_name; - iface->get_parent_path = wintc_sh_view_cpl_get_parent_path; - iface->get_path = wintc_sh_view_cpl_get_path; - iface->get_unique_hash = wintc_sh_view_cpl_get_unique_hash; - iface->has_parent = wintc_sh_view_cpl_has_parent; + iface->activate_item = wintc_sh_view_cpl_activate_item; + iface->get_display_name = wintc_sh_view_cpl_get_display_name; + iface->get_icon_name = wintc_sh_view_cpl_get_icon_name; + iface->get_operations_for_item = wintc_sh_view_cpl_get_operations_for_item; + iface->get_operations_for_view = wintc_sh_view_cpl_get_operations_for_view; + iface->get_parent_path = wintc_sh_view_cpl_get_parent_path; + iface->get_path = wintc_sh_view_cpl_get_path; + iface->get_unique_hash = wintc_sh_view_cpl_get_unique_hash; + iface->has_parent = wintc_sh_view_cpl_has_parent; + iface->refresh_items = wintc_sh_view_cpl_refresh_items; + iface->spawn_operation = wintc_sh_view_cpl_spawn_operation; } // @@ -216,6 +223,75 @@ static gboolean wintc_sh_view_cpl_activate_item( return TRUE; } +static const gchar* wintc_sh_view_cpl_get_display_name( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + // FIXME: Localisation + // + return "Control Panel"; +} + +static const gchar* wintc_sh_view_cpl_get_icon_name( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + return "preferences-other"; +} + +static GMenuModel* wintc_sh_view_cpl_get_operations_for_item( + WINTC_UNUSED(WinTCIShextView* view), + WINTC_UNUSED(guint item_hash) +) +{ + g_warning("%s Not Implemented", __func__); + return NULL; +} + +static GMenuModel* wintc_sh_view_cpl_get_operations_for_view( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + g_warning("%s Not Implemented", __func__); + return NULL; +} + +static void wintc_sh_view_cpl_get_parent_path( + WINTC_UNUSED(WinTCIShextView* view), + WinTCShextPathInfo* path_info +) +{ + path_info->base_path = + g_strdup( + wintc_sh_get_place_path(WINTC_SH_PLACE_DRIVES) + ); +} + +static void wintc_sh_view_cpl_get_path( + WINTC_UNUSED(WinTCIShextView* view), + WinTCShextPathInfo* path_info +) +{ + path_info->base_path = + g_strdup( + wintc_sh_get_place_path(WINTC_SH_PLACE_CONTROLPANEL) + ); +} + +static guint wintc_sh_view_cpl_get_unique_hash( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + return g_str_hash(wintc_sh_get_place_path(WINTC_SH_PLACE_CONTROLPANEL)); +} + +static gboolean wintc_sh_view_cpl_has_parent( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + return TRUE; +} + static void wintc_sh_view_cpl_refresh_items( WinTCIShextView* view ) @@ -280,71 +356,15 @@ static void wintc_sh_view_cpl_refresh_items( g_list_free(items); } -static void wintc_sh_view_cpl_get_actions_for_item( +static WinTCShextOperation* wintc_sh_view_cpl_spawn_operation( WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(WinTCShextViewItem* item) + WINTC_UNUSED(gint operation_id), + WINTC_UNUSED(GList* targets), + WINTC_UNUSED(GError** error) ) { - g_critical("%s Not Implemented", __func__); -} - -static void wintc_sh_view_cpl_get_actions_for_view( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - g_critical("%s Not Implemented", __func__); -} - -static const gchar* wintc_sh_view_cpl_get_display_name( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - // FIXME: Localisation - // - return "Control Panel"; -} - -static const gchar* wintc_sh_view_cpl_get_icon_name( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - return "preferences-other"; -} - -static void wintc_sh_view_cpl_get_parent_path( - WINTC_UNUSED(WinTCIShextView* view), - WinTCShextPathInfo* path_info -) -{ - path_info->base_path = - g_strdup( - wintc_sh_get_place_path(WINTC_SH_PLACE_DRIVES) - ); -} - -static void wintc_sh_view_cpl_get_path( - WINTC_UNUSED(WinTCIShextView* view), - WinTCShextPathInfo* path_info -) -{ - path_info->base_path = - g_strdup( - wintc_sh_get_place_path(WINTC_SH_PLACE_CONTROLPANEL) - ); -} - -static guint wintc_sh_view_cpl_get_unique_hash( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - return g_str_hash(wintc_sh_get_place_path(WINTC_SH_PLACE_CONTROLPANEL)); -} - -static gboolean wintc_sh_view_cpl_has_parent( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - return TRUE; + g_critical("Not implemented %s", __func__); + return NULL; } // diff --git a/shared/shell/src/vwdesk.c b/shared/shell/src/vwdesk.c index 0a62eab..97b74d1 100644 --- a/shared/shell/src/vwdesk.c +++ b/shared/shell/src/vwdesk.c @@ -75,22 +75,19 @@ static gboolean wintc_sh_view_desktop_activate_item( WinTCShextPathInfo* path_info, GError** error ); -static void wintc_sh_view_desktop_refresh_items( - WinTCIShextView* view -); -static void wintc_sh_view_desktop_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -); -static void wintc_sh_view_desktop_get_actions_for_view( - WinTCIShextView* view -); static const gchar* wintc_sh_view_desktop_get_display_name( WinTCIShextView* view ); static const gchar* wintc_sh_view_desktop_get_icon_name( WinTCIShextView* view ); +static GMenuModel* wintc_sh_view_desktop_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +); +static GMenuModel* wintc_sh_view_desktop_get_operations_for_view( + WinTCIShextView* view +); static void wintc_sh_view_desktop_get_parent_path( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -105,6 +102,15 @@ static guint wintc_sh_view_desktop_get_unique_hash( static gboolean wintc_sh_view_desktop_has_parent( WinTCIShextView* view ); +static void wintc_sh_view_desktop_refresh_items( + WinTCIShextView* view +); +static WinTCShextOperation* wintc_sh_view_desktop_spawn_operation( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error +); // // GLIB OOP CLASS/INSTANCE DEFINITIONS @@ -190,16 +196,19 @@ static void wintc_sh_view_desktop_ishext_view_interface_init( WinTCIShextViewInterface* iface ) { - iface->activate_item = wintc_sh_view_desktop_activate_item; - iface->refresh_items = wintc_sh_view_desktop_refresh_items; - iface->get_actions_for_item = wintc_sh_view_desktop_get_actions_for_item; - iface->get_actions_for_view = wintc_sh_view_desktop_get_actions_for_view; - iface->get_display_name = wintc_sh_view_desktop_get_display_name; - iface->get_icon_name = wintc_sh_view_desktop_get_icon_name; - iface->get_parent_path = wintc_sh_view_desktop_get_parent_path; - iface->get_path = wintc_sh_view_desktop_get_path; - iface->get_unique_hash = wintc_sh_view_desktop_get_unique_hash; - iface->has_parent = wintc_sh_view_desktop_has_parent; + iface->activate_item = wintc_sh_view_desktop_activate_item; + iface->get_display_name = wintc_sh_view_desktop_get_display_name; + iface->get_icon_name = wintc_sh_view_desktop_get_icon_name; + iface->get_operations_for_item = + wintc_sh_view_desktop_get_operations_for_item; + iface->get_operations_for_view = + wintc_sh_view_desktop_get_operations_for_view; + iface->get_parent_path = wintc_sh_view_desktop_get_parent_path; + iface->get_path = wintc_sh_view_desktop_get_path; + iface->get_unique_hash = wintc_sh_view_desktop_get_unique_hash; + iface->has_parent = wintc_sh_view_desktop_has_parent; + iface->refresh_items = wintc_sh_view_desktop_refresh_items; + iface->spawn_operation = wintc_sh_view_desktop_spawn_operation; } // @@ -259,44 +268,6 @@ static gboolean wintc_sh_view_desktop_activate_item( return TRUE; } -static void wintc_sh_view_desktop_refresh_items( - WinTCIShextView* view -) -{ - WINTC_LOG_DEBUG("%s", "shell: refresh desktop view"); - - _wintc_ishext_view_refreshing(view); - - // Just emit the default items for now - // TODO: Should aggregate with user desktop files - // - WinTCShextViewItemsUpdate update = { 0 }; - - GList* items = g_hash_table_get_values(s_desktop_map); - - update.data = items; - update.done = TRUE; - - _wintc_ishext_view_items_added(view, &update); - - g_list_free(items); -} - -static void wintc_sh_view_desktop_get_actions_for_item( - WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(WinTCShextViewItem* item) -) -{ - g_critical("%s Not Implemented", __func__); -} - -static void wintc_sh_view_desktop_get_actions_for_view( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - g_critical("%s Not Implemented", __func__); -} - static const gchar* wintc_sh_view_desktop_get_display_name( WINTC_UNUSED(WinTCIShextView* view) ) @@ -312,6 +283,23 @@ static const gchar* wintc_sh_view_desktop_get_icon_name( return "user-desktop"; } +static GMenuModel* wintc_sh_view_desktop_get_operations_for_item( + WINTC_UNUSED(WinTCIShextView* view), + WINTC_UNUSED(guint item_hash) +) +{ + g_warning("%s Not Implemented", __func__); + return NULL; +} + +static GMenuModel* wintc_sh_view_desktop_get_operations_for_view( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + g_warning("%s Not Implemented", __func__); + return NULL; +} + static void wintc_sh_view_desktop_get_parent_path( WINTC_UNUSED(WinTCIShextView* view), WINTC_UNUSED(WinTCShextPathInfo* path_info) @@ -342,6 +330,40 @@ static gboolean wintc_sh_view_desktop_has_parent( return FALSE; } +static void wintc_sh_view_desktop_refresh_items( + WinTCIShextView* view +) +{ + WINTC_LOG_DEBUG("%s", "shell: refresh desktop view"); + + _wintc_ishext_view_refreshing(view); + + // Just emit the default items for now + // TODO: Should aggregate with user desktop files + // + WinTCShextViewItemsUpdate update = { 0 }; + + GList* items = g_hash_table_get_values(s_desktop_map); + + update.data = items; + update.done = TRUE; + + _wintc_ishext_view_items_added(view, &update); + + g_list_free(items); +} + +static WinTCShextOperation* wintc_sh_view_desktop_spawn_operation( + WINTC_UNUSED(WinTCIShextView* view), + WINTC_UNUSED(gint operation_id), + WINTC_UNUSED(GList* targets), + WINTC_UNUSED(GError** error) +) +{ + g_critical("Not implemented %s", __func__); + return NULL; +} + // // PUBLIC FUNCTIONS // diff --git a/shared/shell/src/vwdrives.c b/shared/shell/src/vwdrives.c index 776fb83..9c3bc2d 100644 --- a/shared/shell/src/vwdrives.c +++ b/shared/shell/src/vwdrives.c @@ -48,22 +48,19 @@ static gboolean wintc_sh_view_drives_activate_item( WinTCShextPathInfo* path_info, GError** error ); -static void wintc_sh_view_drives_refresh_items( - WinTCIShextView* view -); -static void wintc_sh_view_drives_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -); -static void wintc_sh_view_drives_get_actions_for_view( - WinTCIShextView* view -); static const gchar* wintc_sh_view_drives_get_display_name( WinTCIShextView* view ); static const gchar* wintc_sh_view_drives_get_icon_name( WinTCIShextView* view ); +static GMenuModel* wintc_sh_view_drives_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +); +static GMenuModel* wintc_sh_view_drives_get_operations_for_view( + WinTCIShextView* view +); static void wintc_sh_view_drives_get_parent_path( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -78,6 +75,15 @@ static guint wintc_sh_view_drives_get_unique_hash( static gboolean wintc_sh_view_drives_has_parent( WinTCIShextView* view ); +static void wintc_sh_view_drives_refresh_items( + WinTCIShextView* view +); +static WinTCShextOperation* wintc_sh_view_drives_spawn_operation( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error +); // // GLIB OOP/CLASS INSTANCE DEFINITIONS @@ -154,16 +160,19 @@ static void wintc_sh_view_drives_ishext_view_interface_init( WinTCIShextViewInterface* iface ) { - iface->activate_item = wintc_sh_view_drives_activate_item; - iface->refresh_items = wintc_sh_view_drives_refresh_items; - iface->get_actions_for_item = wintc_sh_view_drives_get_actions_for_item; - iface->get_actions_for_view = wintc_sh_view_drives_get_actions_for_view; - iface->get_display_name = wintc_sh_view_drives_get_display_name; - iface->get_icon_name = wintc_sh_view_drives_get_icon_name; - iface->get_parent_path = wintc_sh_view_drives_get_parent_path; - iface->get_path = wintc_sh_view_drives_get_path; - iface->get_unique_hash = wintc_sh_view_drives_get_unique_hash; - iface->has_parent = wintc_sh_view_drives_has_parent; + iface->activate_item = wintc_sh_view_drives_activate_item; + iface->get_display_name = wintc_sh_view_drives_get_display_name; + iface->get_icon_name = wintc_sh_view_drives_get_icon_name; + iface->get_operations_for_item = + wintc_sh_view_drives_get_operations_for_item; + iface->get_operations_for_view = + wintc_sh_view_drives_get_operations_for_view; + iface->get_parent_path = wintc_sh_view_drives_get_parent_path; + iface->get_path = wintc_sh_view_drives_get_path; + iface->get_unique_hash = wintc_sh_view_drives_get_unique_hash; + iface->has_parent = wintc_sh_view_drives_has_parent; + iface->refresh_items = wintc_sh_view_drives_refresh_items; + iface->spawn_operation = wintc_sh_view_drives_spawn_operation; } // @@ -219,44 +228,6 @@ static gboolean wintc_sh_view_drives_activate_item( return TRUE; } -static void wintc_sh_view_drives_refresh_items( - WinTCIShextView* view -) -{ - WINTC_LOG_DEBUG("%s", "shell: refresh drives view"); - - _wintc_ishext_view_refreshing(view); - - // Emit only the root '/' for now - // TODO: Basically everything in My Computer! - // - WinTCShextViewItemsUpdate update = { 0 }; - - GList* items = g_hash_table_get_values(s_drives_map); - - update.data = items; - update.done = TRUE; - - _wintc_ishext_view_items_added(view, &update); - - g_list_free(items); -} - -static void wintc_sh_view_drives_get_actions_for_item( - WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(WinTCShextViewItem* item) -) -{ - g_critical("%s Not Implemented", __func__); -} - -static void wintc_sh_view_drives_get_actions_for_view( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - g_critical("%s Not Implemented", __func__); -} - static const gchar* wintc_sh_view_drives_get_display_name( WINTC_UNUSED(WinTCIShextView* view) ) @@ -273,6 +244,23 @@ static const gchar* wintc_sh_view_drives_get_icon_name( return "computer"; } +static GMenuModel* wintc_sh_view_drives_get_operations_for_item( + WINTC_UNUSED(WinTCIShextView* view), + WINTC_UNUSED(guint item_hash) +) +{ + g_warning("%s Not Implemented", __func__); + return NULL; +} + +static GMenuModel* wintc_sh_view_drives_get_operations_for_view( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + g_warning("%s Not Implemented", __func__); + return NULL; +} + static void wintc_sh_view_drives_get_parent_path( WINTC_UNUSED(WinTCIShextView* view), WinTCShextPathInfo* path_info @@ -309,6 +297,40 @@ static gboolean wintc_sh_view_drives_has_parent( return TRUE; } +static void wintc_sh_view_drives_refresh_items( + WinTCIShextView* view +) +{ + WINTC_LOG_DEBUG("%s", "shell: refresh drives view"); + + _wintc_ishext_view_refreshing(view); + + // Emit only the root '/' for now + // TODO: Basically everything in My Computer! + // + WinTCShextViewItemsUpdate update = { 0 }; + + GList* items = g_hash_table_get_values(s_drives_map); + + update.data = items; + update.done = TRUE; + + _wintc_ishext_view_items_added(view, &update); + + g_list_free(items); +} + +static WinTCShextOperation* wintc_sh_view_drives_spawn_operation( + WINTC_UNUSED(WinTCIShextView* view), + WINTC_UNUSED(gint operation_id), + WINTC_UNUSED(GList* targets), + WINTC_UNUSED(GError** error) +) +{ + g_critical("Not implemented %s", __func__); + return NULL; +} + // // PUBLIC FUNCTIONS // diff --git a/shared/shell/src/vwfs.c b/shared/shell/src/vwfs.c index e23e6bb..67d020d 100644 --- a/shared/shell/src/vwfs.c +++ b/shared/shell/src/vwfs.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "../public/vwfs.h" @@ -49,16 +50,6 @@ static gboolean wintc_sh_view_fs_activate_item( WinTCShextPathInfo* path_info, GError** error ); -static void wintc_sh_view_fs_refresh_items( - WinTCIShextView* view -); -static void wintc_sh_view_fs_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -); -static void wintc_sh_view_fs_get_actions_for_view( - WinTCIShextView* view -); static guint wintc_sh_view_fs_get_unique_hash( WinTCIShextView* view ); @@ -68,6 +59,13 @@ static const gchar* wintc_sh_view_fs_get_display_name( static const gchar* wintc_sh_view_fs_get_icon_name( WinTCIShextView* view ); +static GMenuModel* wintc_sh_view_fs_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +); +static GMenuModel* wintc_sh_view_fs_get_operations_for_view( + WinTCIShextView* view +); static void wintc_sh_view_fs_get_parent_path( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -79,11 +77,34 @@ static void wintc_sh_view_fs_get_path( static gboolean wintc_sh_view_fs_has_parent( WinTCIShextView* view ); +static void wintc_sh_view_fs_refresh_items( + WinTCIShextView* view +); +static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error +); static void clear_view_item( WinTCShextViewItem* item ); +static gboolean real_activate_item( + WinTCShViewFS* view_fs, + WinTCShextViewItem* item, + WinTCShextPathInfo* path_info, + GError** error +); + +static gboolean shopr_open( + WinTCIShextView* view, + WinTCShextOperation* operation, + GtkWindow* wnd, + GError** error +); + static void on_file_monitor_changed( GFileMonitor* self, GFile* file, @@ -177,16 +198,17 @@ static void wintc_sh_view_fs_ishext_view_interface_init( WinTCIShextViewInterface* iface ) { - iface->activate_item = wintc_sh_view_fs_activate_item; - iface->refresh_items = wintc_sh_view_fs_refresh_items; - iface->get_actions_for_item = wintc_sh_view_fs_get_actions_for_item; - iface->get_actions_for_view = wintc_sh_view_fs_get_actions_for_view; - iface->get_display_name = wintc_sh_view_fs_get_display_name; - iface->get_icon_name = wintc_sh_view_fs_get_icon_name; - iface->get_parent_path = wintc_sh_view_fs_get_parent_path; - iface->get_path = wintc_sh_view_fs_get_path; - iface->get_unique_hash = wintc_sh_view_fs_get_unique_hash; - iface->has_parent = wintc_sh_view_fs_has_parent; + iface->activate_item = wintc_sh_view_fs_activate_item; + iface->get_display_name = wintc_sh_view_fs_get_display_name; + iface->get_icon_name = wintc_sh_view_fs_get_icon_name; + iface->get_operations_for_item = wintc_sh_view_fs_get_operations_for_item; + iface->get_operations_for_view = wintc_sh_view_fs_get_operations_for_view; + iface->get_parent_path = wintc_sh_view_fs_get_parent_path; + iface->get_path = wintc_sh_view_fs_get_path; + iface->get_unique_hash = wintc_sh_view_fs_get_unique_hash; + iface->has_parent = wintc_sh_view_fs_has_parent; + iface->refresh_items = wintc_sh_view_fs_refresh_items; + iface->spawn_operation = wintc_sh_view_fs_spawn_operation; } // @@ -295,8 +317,7 @@ static gboolean wintc_sh_view_fs_activate_item( GError** error ) { - GError* local_error = NULL; - WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); WINTC_SAFE_REF_CLEAR(error); @@ -318,51 +339,160 @@ static gboolean wintc_sh_view_fs_activate_item( return FALSE; } - // Retrieve MIME for the item - // - gchar* next_path = g_build_path( - G_DIR_SEPARATOR_S, - view_fs->path, - item->display_name, - NULL - ); - gchar* mime_type = wintc_query_mime_for_file( - next_path, - &local_error - ); + return real_activate_item( + view_fs, + item, + path_info, + error + ); +} - if (!mime_type) +static const gchar* wintc_sh_view_fs_get_display_name( + WinTCIShextView* view +) +{ + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + + if (g_strcmp0(view_fs->path, "/") == 0) { - g_propagate_error(error, local_error); - return FALSE; + return view_fs->path; } - // Handle the item based on MIME, if it's a directory then we can set that - // as our next location - otherwise, it depends if there is a shell - // extension for handling the MIME type or not + // FIXME: This could be broken if the path itself contains an escaped dir + // separator, cba for now // - if (g_strcmp0(mime_type, "inode/directory") == 0) + return g_strrstr(view_fs->path, G_DIR_SEPARATOR_S) + 1; +} + +static const gchar* wintc_sh_view_fs_get_icon_name( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + return "inode-directory"; +} + +static GMenuModel* wintc_sh_view_fs_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +) +{ + // Retrieve the view item itself + // + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + + WinTCShextViewItem* view_item = + (WinTCShextViewItem*) + g_hash_table_lookup( + view_fs->fs_map_entries, + GUINT_TO_POINTER(item_hash) + ); + + if (!view_item) { - path_info->base_path = g_strdup_printf("file://%s", next_path); + g_critical("%s", "shell: fs - no such item for menu"); + return NULL; + } + + // FIXME: Extremely simplistic for now, does not cover shell extensions or + // anything! + // + GtkBuilder* builder; + GMenuModel* menu; + + builder = + gtk_builder_new_from_resource( + view_item->is_leaf ? + "/uk/oddmatics/wintc/shell/menufslf.ui" : + "/uk/oddmatics/wintc/shell/menufsvw.ui" + ); + + wintc_lc_builder_preprocess_widget_text(builder); + + menu = + G_MENU_MODEL( + g_object_ref( + gtk_builder_get_object(builder, "menu") + ) + ); + + g_object_unref(builder); + + return menu; +} + +static GMenuModel* wintc_sh_view_fs_get_operations_for_view( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + GtkBuilder* builder; + GMenuModel* menu; + + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/shell/menufs.ui" + ); + + wintc_lc_builder_preprocess_widget_text(builder); + + menu = + G_MENU_MODEL( + g_object_ref( + gtk_builder_get_object(builder, "menu") + ) + ); + + g_object_unref(builder); + + return menu; +} + +static void wintc_sh_view_fs_get_parent_path( + WinTCIShextView* view, + WinTCShextPathInfo* path_info +) +{ + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + + if (view_fs->parent_path) + { + path_info->base_path = + g_strdup_printf("file://%s", view_fs->parent_path); } else { - if (wintc_shext_host_has_view_for_mime(view_fs->shext_host, mime_type)) - { - path_info->base_path = g_strdup_printf("file://%s", next_path); - } - else - { - if (!wintc_launch_command(next_path, &local_error)) - { - g_propagate_error(error, local_error); - } - } + path_info->base_path = + g_strdup( + wintc_sh_get_place_path( + WINTC_SH_PLACE_DRIVES + ) + ); } +} - g_free(mime_type); - g_free(next_path); +static void wintc_sh_view_fs_get_path( + WinTCIShextView* view, + WinTCShextPathInfo* path_info +) +{ + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + path_info->base_path = + g_strdup_printf("file://%s", view_fs->path); +} + +static guint wintc_sh_view_fs_get_unique_hash( + WinTCIShextView* view +) +{ + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + + return g_str_hash(view_fs->path); +} + +static gboolean wintc_sh_view_fs_has_parent( + WINTC_UNUSED(WinTCIShextView* view) +) +{ return TRUE; } @@ -478,93 +608,37 @@ static void wintc_sh_view_fs_refresh_items( g_list_free(items); } -static void wintc_sh_view_fs_get_actions_for_item( +static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(WinTCShextViewItem* item) + gint operation_id, + GList* targets, + WINTC_UNUSED(GError** error) ) { - g_critical("%s Not Implemented", __func__); -} - -static void wintc_sh_view_fs_get_actions_for_view( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - g_critical("%s Not Implemented", __func__); -} - -static const gchar* wintc_sh_view_fs_get_display_name( - WinTCIShextView* view -) -{ - WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); - - if (g_strcmp0(view_fs->path, "/") == 0) + if (operation_id > WINTC_SHEXT_KNOWN_OP_OPEN) { - return view_fs->path; + g_critical("Not implemented %s", __func__); + return NULL; } - // FIXME: This could be broken if the path itself contains an escaped dir - // separator, cba for now + // Spawn op // - return g_strrstr(view_fs->path, G_DIR_SEPARATOR_S) + 1; -} + WinTCShextOperation* ret = g_new(WinTCShextOperation, 1); -static const gchar* wintc_sh_view_fs_get_icon_name( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - return "inode-directory"; -} - -static void wintc_sh_view_fs_get_parent_path( - WinTCIShextView* view, - WinTCShextPathInfo* path_info -) -{ - WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); - - if (view_fs->parent_path) + switch (operation_id) { - path_info->base_path = - g_strdup_printf("file://%s", view_fs->parent_path); + case WINTC_SHEXT_KNOWN_OP_OPEN: + ret->func = shopr_open; + ret->priv = targets; + break; + + default: + g_free(ret); + g_critical("%s", "shell: fs - invalid op"); + return NULL; } - else - { - path_info->base_path = - g_strdup( - wintc_sh_get_place_path( - WINTC_SH_PLACE_DRIVES - ) - ); - } -} -static void wintc_sh_view_fs_get_path( - WinTCIShextView* view, - WinTCShextPathInfo* path_info -) -{ - WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); - - path_info->base_path = - g_strdup_printf("file://%s", view_fs->path); -} - -static guint wintc_sh_view_fs_get_unique_hash( - WinTCIShextView* view -) -{ - WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); - - return g_str_hash(view_fs->path); -} - -static gboolean wintc_sh_view_fs_has_parent( - WINTC_UNUSED(WinTCIShextView* view) -) -{ - return TRUE; + return ret; } // @@ -596,6 +670,136 @@ static void clear_view_item( g_free(item); } +static gboolean real_activate_item( + WinTCShViewFS* view_fs, + WinTCShextViewItem* item, + WinTCShextPathInfo* path_info, + GError** error +) +{ + // If the target is a dir or has a shell extension then set that as + // the target path + // + gchar* next_path = g_build_path( + G_DIR_SEPARATOR_S, + view_fs->path, + item->display_name, + NULL + ); + gchar* target_path = NULL; + + if (!(item->is_leaf)) + { + target_path = g_strdup_printf("file://%s", next_path); + } + else + { + gchar* mime_type = wintc_query_mime_for_file(next_path, error); + + if (!mime_type) + { + g_free(next_path); + return FALSE; + } + + if (wintc_shext_host_has_view_for_mime(view_fs->shext_host, mime_type)) + { + target_path = g_strdup_printf("file://%s", next_path); + } + } + + // If there is a target path, then either navigate there, or open a new + // window to navigate there + // + // Otherwise just run the command + // + gboolean success = TRUE; + + if (target_path) + { + if (path_info) + { + path_info->base_path = target_path; + } + else + { + success = wintc_launch_command(target_path, error); + + g_free(target_path); + } + } + else + { + success = wintc_launch_command(next_path, error); + } + + g_free(next_path); + + return success; +} + +static gboolean shopr_open( + WinTCIShextView* view, + WinTCShextOperation* operation, + WINTC_UNUSED(GtkWindow* wnd), + GError** error +) +{ + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + + GList* targets = operation->priv; + + if (!targets) + { + g_warning("%s", "shell: fs open op - no files specified?"); + return TRUE; + } + + // Iterate over to activate the items + // + GError* local_error = NULL; + gboolean success = TRUE; + + for (GList* iter = targets; iter; iter = iter->next) + { + WinTCShextViewItem* item = + g_hash_table_lookup( + view_fs->fs_map_entries, + iter->data + ); + + if (!item) + { + continue; + } + + // Valid - attempt launch + // + // Only store the first error + // + if ( + !real_activate_item( + view_fs, + item, + NULL, + local_error ? NULL : &local_error + ) + ) + { + success = FALSE; + } + } + + if (!success && local_error) + { + g_propagate_error(error, local_error); + } + + g_list_free(targets); + + return success; +} + static void on_file_monitor_changed( WINTC_UNUSED(GFileMonitor* self), GFile* file, diff --git a/shared/shellext/CMakeLists.txt b/shared/shellext/CMakeLists.txt index a7851d4..9191ae5 100644 --- a/shared/shellext/CMakeLists.txt +++ b/shared/shellext/CMakeLists.txt @@ -41,6 +41,7 @@ add_library( src/if_view.c public/if_view.h public/viewitem.h + public/viewops.h ) set_target_properties( diff --git a/shared/shellext/public/if_view.h b/shared/shellext/public/if_view.h index e8a5fe4..d27f4ff 100644 --- a/shared/shellext/public/if_view.h +++ b/shared/shellext/public/if_view.h @@ -7,6 +7,7 @@ #include #include "viewitem.h" +#include "viewops.h" // // PUBLIC STRUCTURES @@ -41,20 +42,19 @@ struct _WinTCIShextViewInterface GError** error ); - void (*get_actions_for_item) ( - WinTCIShextView* view, - WinTCShextViewItem* item - ); - void (*get_actions_for_view) ( - WinTCIShextView* view - ); - const gchar* (*get_display_name) ( WinTCIShextView* view ); const gchar* (*get_icon_name) ( WinTCIShextView* view ); + GMenuModel* (*get_operations_for_item) ( + WinTCIShextView* view, + guint item_hash + ); + GMenuModel* (*get_operations_for_view) ( + WinTCIShextView* view + ); void (*get_parent_path) ( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -73,6 +73,13 @@ struct _WinTCIShextViewInterface void (*refresh_items) ( WinTCIShextView* view ); + + WinTCShextOperation* (*spawn_operation) ( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error + ); }; // @@ -89,20 +96,19 @@ void wintc_ishext_view_refresh_items( WinTCIShextView* view ); -void wintc_ishext_view_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -); -void wintc_ishext_view_get_actions_for_view( - WinTCIShextView* view -); - const gchar* wintc_ishext_view_get_display_name( WinTCIShextView* view ); const gchar* wintc_ishext_view_get_icon_name( WinTCIShextView* view ); +GMenuModel* wintc_ishext_view_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +); +GMenuModel* wintc_ishext_view_get_operations_for_view( + WinTCIShextView* view +); void wintc_ishext_view_get_parent_path( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -117,6 +123,12 @@ guint wintc_ishext_view_get_unique_hash( gboolean wintc_ishext_view_has_parent( WinTCIShextView* view ); +WinTCShextOperation* wintc_ishext_view_spawn_operation( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error +); // // PUBLIC FUNCTIONS diff --git a/shared/shellext/public/libapi.h.in b/shared/shellext/public/libapi.h.in index 8e80a25..6fcb23c 100644 --- a/shared/shellext/public/libapi.h.in +++ b/shared/shellext/public/libapi.h.in @@ -6,5 +6,6 @@ #include "@LIB_HEADER_DIR@/host.h" #include "@LIB_HEADER_DIR@/if_view.h" #include "@LIB_HEADER_DIR@/viewitem.h" +#include "@LIB_HEADER_DIR@/viewops.h" #endif diff --git a/shared/shellext/public/viewops.h b/shared/shellext/public/viewops.h new file mode 100644 index 0000000..4a04cc0 --- /dev/null +++ b/shared/shellext/public/viewops.h @@ -0,0 +1,62 @@ +/** @file */ + +#ifndef __SHELLEXT_VIEWOPS_H__ +#define __SHELLEXT_VIEWOPS_H__ + +#include + +// +// FORWARD DECLARATIONS +// +typedef struct _WinTCIShextView WinTCIShextView; +typedef struct _WinTCShextOperation WinTCShextOperation; + +// +// PUBLIC ENUMS +// +typedef enum +{ + WINTC_SHEXT_OP_PRIORITY_NONE, + WINTC_SHEXT_OP_PRIORITY_PRIMARY, + WINTC_SHEXT_OP_PRIORITY_SECONDARY +} WinTCShextOperationPriority; + +typedef enum +{ + WINTC_SHEXT_OP_INVALID = 0, + + WINTC_SHEXT_KNOWN_OP_OPEN = 1, + WINTC_SHEXT_KNOWN_OP_OPEN_WITH, + WINTC_SHEXT_KNOWN_OP_RUN_AS, + WINTC_SHEXT_KNOWN_OP_CUT, + WINTC_SHEXT_KNOWN_OP_COPY, + WINTC_SHEXT_KNOWN_OP_PASTE, + WINTC_SHEXT_KNOWN_OP_PASTE_SHORTCUT, + WINTC_SHEXT_KNOWN_OP_CREATE_SHORTCUT, + WINTC_SHEXT_KNOWN_OP_DELETE, + WINTC_SHEXT_KNOWN_OP_RENAME, + WINTC_SHEXT_KNOWN_OP_SEND_TO, + WINTC_SHEXT_KNOWN_OP_MOVE_TO, + WINTC_SHEXT_KNOWN_OP_COPY_TO, + WINTC_SHEXT_KNOWN_OP_PROPERTIES, + + WINTC_SHEXT_OP_CUSTOM = 100 +} WinTCShextOperationId; + +// +// PUBLIC STRUCTURES +// +typedef gboolean (*WinTCShextOperationFunc) ( + WinTCIShextView* view, + WinTCShextOperation* operation, + GtkWindow* wnd, + GError** error +); + +struct _WinTCShextOperation +{ + WinTCShextOperationFunc func; + gpointer priv; +}; + +#endif diff --git a/shared/shellext/src/if_view.c b/shared/shellext/src/if_view.c index af604e1..e3b8056 100644 --- a/shared/shellext/src/if_view.c +++ b/shared/shellext/src/if_view.c @@ -107,27 +107,6 @@ gboolean wintc_ishext_view_activate_item( ); } -void wintc_ishext_view_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -) -{ - WinTCIShextViewInterface* iface = - WINTC_ISHEXT_VIEW_GET_IFACE(view); - - iface->get_actions_for_item(view, item); -} - -void wintc_ishext_view_get_actions_for_view( - WinTCIShextView* view -) -{ - WinTCIShextViewInterface* iface = - WINTC_ISHEXT_VIEW_GET_IFACE(view); - - iface->get_actions_for_view(view); -} - const gchar* wintc_ishext_view_get_display_name( WinTCIShextView* view ) @@ -148,6 +127,27 @@ const gchar* wintc_ishext_view_get_icon_name( return iface->get_icon_name(view); } +GMenuModel* wintc_ishext_view_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +) +{ + WinTCIShextViewInterface* iface = + WINTC_ISHEXT_VIEW_GET_IFACE(view); + + return iface->get_operations_for_item(view, item_hash); +} + +GMenuModel* wintc_ishext_view_get_operations_for_view( + WinTCIShextView* view +) +{ + WinTCIShextViewInterface* iface = + WINTC_ISHEXT_VIEW_GET_IFACE(view); + + return iface->get_operations_for_view(view); +} + void wintc_ishext_view_get_parent_path( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -200,6 +200,24 @@ void wintc_ishext_view_refresh_items( iface->refresh_items(view); } +WinTCShextOperation* wintc_ishext_view_spawn_operation( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error +) +{ + WinTCIShextViewInterface* iface = + WINTC_ISHEXT_VIEW_GET_IFACE(view); + + return iface->spawn_operation( + view, + operation_id, + targets, + error + ); +} + // // PUBLIC FUNCTIONS // diff --git a/shell/cpl/printers/src/vwprntrs.c b/shell/cpl/printers/src/vwprntrs.c index fd3996a..185d3c2 100644 --- a/shell/cpl/printers/src/vwprntrs.c +++ b/shell/cpl/printers/src/vwprntrs.c @@ -48,22 +48,19 @@ static gboolean wintc_cpl_view_printers_activate_item( WinTCShextPathInfo* path_info, GError** error ); -static void wintc_cpl_view_printers_refresh_items( - WinTCIShextView* view -); -static void wintc_cpl_view_printers_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -); -static void wintc_cpl_view_printers_get_actions_for_view( - WinTCIShextView* view -); static const gchar* wintc_cpl_view_printers_get_display_name( WinTCIShextView* view ); static const gchar* wintc_cpl_view_printers_get_icon_name( WinTCIShextView* view ); +static GMenuModel* wintc_cpl_view_printers_get_operations_for_item( + WinTCIShextView* view, + guint item_hash +); +static GMenuModel* wintc_cpl_view_printers_get_operations_for_view( + WinTCIShextView* view +); static void wintc_cpl_view_printers_get_parent_path( WinTCIShextView* view, WinTCShextPathInfo* path_info @@ -78,6 +75,9 @@ static guint wintc_cpl_view_printers_get_unique_hash( static gboolean wintc_cpl_view_printers_has_parent( WinTCIShextView* view ); +static void wintc_cpl_view_printers_refresh_items( + WinTCIShextView* view +); // // GLIB OOP/CLASS INSTANCE DEFINITIONS @@ -145,16 +145,18 @@ static void wintc_cpl_view_printers_ishext_view_interface_init( WinTCIShextViewInterface* iface ) { - iface->activate_item = wintc_cpl_view_printers_activate_item; - iface->refresh_items = wintc_cpl_view_printers_refresh_items; - iface->get_actions_for_item = wintc_cpl_view_printers_get_actions_for_item; - iface->get_actions_for_view = wintc_cpl_view_printers_get_actions_for_view; - iface->get_display_name = wintc_cpl_view_printers_get_display_name; - iface->get_icon_name = wintc_cpl_view_printers_get_icon_name; - iface->get_parent_path = wintc_cpl_view_printers_get_parent_path; - iface->get_path = wintc_cpl_view_printers_get_path; - iface->get_unique_hash = wintc_cpl_view_printers_get_unique_hash; - iface->has_parent = wintc_cpl_view_printers_has_parent; + iface->activate_item = wintc_cpl_view_printers_activate_item; + iface->get_display_name = wintc_cpl_view_printers_get_display_name; + iface->get_icon_name = wintc_cpl_view_printers_get_icon_name; + iface->get_operations_for_item = + wintc_cpl_view_printers_get_operations_for_item; + iface->get_operations_for_view = + wintc_cpl_view_printers_get_operations_for_view; + iface->get_parent_path = wintc_cpl_view_printers_get_parent_path; + iface->get_path = wintc_cpl_view_printers_get_path; + iface->get_unique_hash = wintc_cpl_view_printers_get_unique_hash; + iface->has_parent = wintc_cpl_view_printers_has_parent; + iface->refresh_items = wintc_cpl_view_printers_refresh_items; } // @@ -199,42 +201,21 @@ static gboolean wintc_cpl_view_printers_activate_item( return FALSE; } -static void wintc_cpl_view_printers_refresh_items( - WinTCIShextView* view +static GMenuModel* wintc_cpl_view_printers_get_operations_for_item( + WINTC_UNUSED(WinTCIShextView* view), + WINTC_UNUSED(guint item_hash) ) { - WINTC_LOG_DEBUG("%s", "cpl-prntrs: refresh printers view"); - - _wintc_ishext_view_refreshing(view); - - // Emit the FPO printer for now - // TODO: Implement this! - // - WinTCShextViewItemsUpdate update = { 0 }; - - GList* items = g_hash_table_get_values(s_printers_map); - - update.data = items; - update.done = TRUE; - - _wintc_ishext_view_items_added(view, &update); - - g_list_free(items); + g_warning("%s Not Implemented", __func__); + return NULL; } -static void wintc_cpl_view_printers_get_actions_for_item( - WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(WinTCShextViewItem* item) -) -{ - g_critical("%s Not Implemented", __func__); -} - -static void wintc_cpl_view_printers_get_actions_for_view( +static GMenuModel* wintc_cpl_view_printers_get_operations_for_view( WINTC_UNUSED(WinTCIShextView* view) ) { - g_critical("%s Not Implemented", __func__); + g_warning("%s Not Implemented", __func__); + return NULL; } static const gchar* wintc_cpl_view_printers_get_display_name( @@ -289,6 +270,29 @@ static gboolean wintc_cpl_view_printers_has_parent( return TRUE; } +static void wintc_cpl_view_printers_refresh_items( + WinTCIShextView* view +) +{ + WINTC_LOG_DEBUG("%s", "cpl-prntrs: refresh printers view"); + + _wintc_ishext_view_refreshing(view); + + // Emit the FPO printer for now + // TODO: Implement this! + // + WinTCShextViewItemsUpdate update = { 0 }; + + GList* items = g_hash_table_get_values(s_printers_map); + + update.data = items; + update.done = TRUE; + + _wintc_ishext_view_items_added(view, &update); + + g_list_free(items); +} + // // PUBLIC FUNCTIONS // diff --git a/shell/shext/zip/src/vwzip.c b/shell/shext/zip/src/vwzip.c index 093a6a9..dbaf488 100644 --- a/shell/shext/zip/src/vwzip.c +++ b/shell/shext/zip/src/vwzip.c @@ -45,14 +45,11 @@ static gboolean wintc_view_zip_activate_item( WinTCShextPathInfo* path_info, GError** error ); -static void wintc_view_zip_refresh_items( - WinTCIShextView* view +static GMenuModel* wintc_view_zip_get_operations_for_item( + WinTCIShextView* view, + guint item_hash ); -static void wintc_view_zip_get_actions_for_item( - WinTCIShextView* view, - WinTCShextViewItem* item -); -static void wintc_view_zip_get_actions_for_view( +static GMenuModel* wintc_view_zip_get_operations_for_view( WinTCIShextView* view ); static const gchar* wintc_view_zip_get_display_name( @@ -75,6 +72,9 @@ static guint wintc_view_zip_get_unique_hash( static gboolean wintc_view_zip_has_parent( WinTCIShextView* view ); +static void wintc_view_zip_refresh_items( + WinTCIShextView* view +); static guint zip_entry_hash( const gchar* zip_file_path, @@ -172,16 +172,16 @@ static void wintc_view_zip_ishext_view_interface_init( WinTCIShextViewInterface* iface ) { - iface->activate_item = wintc_view_zip_activate_item; - iface->refresh_items = wintc_view_zip_refresh_items; - iface->get_actions_for_item = wintc_view_zip_get_actions_for_item; - iface->get_actions_for_view = wintc_view_zip_get_actions_for_view; - iface->get_display_name = wintc_view_zip_get_display_name; - iface->get_icon_name = wintc_view_zip_get_icon_name; - iface->get_parent_path = wintc_view_zip_get_parent_path; - iface->get_path = wintc_view_zip_get_path; - iface->get_unique_hash = wintc_view_zip_get_unique_hash; - iface->has_parent = wintc_view_zip_has_parent; + iface->activate_item = wintc_view_zip_activate_item; + iface->get_operations_for_item = wintc_view_zip_get_operations_for_item; + iface->get_operations_for_view = wintc_view_zip_get_operations_for_view; + iface->get_display_name = wintc_view_zip_get_display_name; + iface->get_icon_name = wintc_view_zip_get_icon_name; + iface->get_parent_path = wintc_view_zip_get_parent_path; + iface->get_path = wintc_view_zip_get_path; + iface->get_unique_hash = wintc_view_zip_get_unique_hash; + iface->has_parent = wintc_view_zip_has_parent; + iface->refresh_items = wintc_view_zip_refresh_items; } // @@ -312,120 +312,21 @@ static gboolean wintc_view_zip_activate_item( return FALSE; } -static void wintc_view_zip_refresh_items( - WinTCIShextView* view +static GMenuModel* wintc_view_zip_get_operations_for_item( + WINTC_UNUSED(WinTCIShextView* view), + WINTC_UNUSED(guint item_hash) ) { - WinTCViewZip* view_zip = WINTC_VIEW_ZIP(view); - - WINTC_LOG_DEBUG("%s", "shext-zip: refresh zip view"); - - _wintc_ishext_view_refreshing(view); - - if (view_zip->map_items) - { - g_hash_table_destroy( - g_steal_pointer(&(view_zip->map_items)) - ); - } - - view_zip->map_items = - g_hash_table_new_full( - g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) clear_view_item - ); - - // Open the archive - // - const gchar* path = view_zip->zip_uri + strlen("file://"); - - gint zip_error = 0; - zip_t* zip_file = zip_open(path, 0, &zip_error); - - if (!zip_file) - { - zip_error_t zip_error_st; - - zip_error_init_with_code(&zip_error_st, zip_error); - - // FIXME: Need a proper way of returning error to caller! - g_critical( - "shext-zip: can't open %s , %s", - path, - zip_error_strerror(&zip_error_st) - ); - - zip_error_fini(&zip_error_st); - - return; - } - - // Read through the entries - // FIXME: Probably want to cap max num of entries? Check zip bombs to see - // what happens - don't want to get nuked by a crazy zip archive - // - gint64 n_entries = zip_get_num_entries(zip_file, 0); - - for (gint64 i = 0; i < n_entries; i++) - { - const gchar* entry_name = zip_get_name(zip_file, (guint64) i, 0); - - // Only want to add the entries in the current relative dir - // - gint name_offset = 0; - - if (!zip_entry_is_in_dir(view_zip->rel_path, entry_name, &name_offset)) - { - continue; - } - - gchar* entry_copy = g_strdup(entry_name); - WinTCShextViewItem* item = g_new(WinTCShextViewItem, 1); - - item->display_name = entry_copy + name_offset; - item->is_leaf = !g_str_has_suffix(entry_name, G_DIR_SEPARATOR_S); - item->icon_name = item->is_leaf ? "empty" : "inode-directory"; - item->hash = zip_entry_hash(path, entry_name); - item->priv = entry_copy; - - g_hash_table_insert( - view_zip->map_items, - GUINT_TO_POINTER(item->hash), - item - ); - } - - zip_close(zip_file); - - // Emit the entries - // - WinTCShextViewItemsUpdate update = { 0 }; - - GList* items = g_hash_table_get_values(view_zip->map_items); - - update.data = items; - update.done = TRUE; - - _wintc_ishext_view_items_added(view, &update); - - g_list_free(items); + g_warning("%s Not Implemented", __func__); + return NULL; } -static void wintc_view_zip_get_actions_for_item( - WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(WinTCShextViewItem* item) -) -{ - g_critical("%s Not Implemented", __func__); -} - -static void wintc_view_zip_get_actions_for_view( +static GMenuModel* wintc_view_zip_get_operations_for_view( WINTC_UNUSED(WinTCIShextView* view) ) { - g_critical("%s Not Implemented", __func__); + g_warning("%s Not Implemented", __func__); + return NULL; } static const gchar* wintc_view_zip_get_display_name( @@ -562,6 +463,107 @@ static gboolean wintc_view_zip_has_parent( return TRUE; } +static void wintc_view_zip_refresh_items( + WinTCIShextView* view +) +{ + WinTCViewZip* view_zip = WINTC_VIEW_ZIP(view); + + WINTC_LOG_DEBUG("%s", "shext-zip: refresh zip view"); + + _wintc_ishext_view_refreshing(view); + + if (view_zip->map_items) + { + g_hash_table_destroy( + g_steal_pointer(&(view_zip->map_items)) + ); + } + + view_zip->map_items = + g_hash_table_new_full( + g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) clear_view_item + ); + + // Open the archive + // + const gchar* path = view_zip->zip_uri + strlen("file://"); + + gint zip_error = 0; + zip_t* zip_file = zip_open(path, 0, &zip_error); + + if (!zip_file) + { + zip_error_t zip_error_st; + + zip_error_init_with_code(&zip_error_st, zip_error); + + // FIXME: Need a proper way of returning error to caller! + g_critical( + "shext-zip: can't open %s , %s", + path, + zip_error_strerror(&zip_error_st) + ); + + zip_error_fini(&zip_error_st); + + return; + } + + // Read through the entries + // FIXME: Probably want to cap max num of entries? Check zip bombs to see + // what happens - don't want to get nuked by a crazy zip archive + // + gint64 n_entries = zip_get_num_entries(zip_file, 0); + + for (gint64 i = 0; i < n_entries; i++) + { + const gchar* entry_name = zip_get_name(zip_file, (guint64) i, 0); + + // Only want to add the entries in the current relative dir + // + gint name_offset = 0; + + if (!zip_entry_is_in_dir(view_zip->rel_path, entry_name, &name_offset)) + { + continue; + } + + gchar* entry_copy = g_strdup(entry_name); + WinTCShextViewItem* item = g_new(WinTCShextViewItem, 1); + + item->display_name = entry_copy + name_offset; + item->is_leaf = !g_str_has_suffix(entry_name, G_DIR_SEPARATOR_S); + item->icon_name = item->is_leaf ? "empty" : "inode-directory"; + item->hash = zip_entry_hash(path, entry_name); + item->priv = entry_copy; + + g_hash_table_insert( + view_zip->map_items, + GUINT_TO_POINTER(item->hash), + item + ); + } + + zip_close(zip_file); + + // Emit the entries + // + WinTCShextViewItemsUpdate update = { 0 }; + + GList* items = g_hash_table_get_values(view_zip->map_items); + + update.data = items; + update.done = TRUE; + + _wintc_ishext_view_items_added(view, &update); + + g_list_free(items); +} + // // PUBLIC FUNCTIONS //