diff --git a/shared/shell/CMakeLists.txt b/shared/shell/CMakeLists.txt index 17da538..07746c0 100644 --- a/shared/shell/CMakeLists.txt +++ b/shared/shell/CMakeLists.txt @@ -39,6 +39,7 @@ add_library( public/error.h src/icnvwbeh.c public/icnvwbeh.h + src/trevwbeh.c public/trevwbeh.h src/nmspace.c public/nmspace.h diff --git a/shared/shell/public/browser.h b/shared/shell/public/browser.h index 370ce2a..505ca81 100644 --- a/shared/shell/public/browser.h +++ b/shared/shell/public/browser.h @@ -52,6 +52,10 @@ gboolean wintc_sh_browser_can_navigate_to_parent( WinTCShBrowser* browser ); +WinTCIShextView* wintc_sh_browser_get_current_view( + WinTCShBrowser* browser +); + void wintc_sh_browser_get_location( WinTCShBrowser* browser, WinTCShextPathInfo* path_info @@ -61,6 +65,10 @@ GtkTreeModel* wintc_sh_browser_get_model( WinTCShBrowser* browser ); +WinTCShextHost* wintc_sh_browser_get_shext_host( + WinTCShBrowser* browser +); + const gchar* wintc_sh_browser_get_view_display_name( WinTCShBrowser* browser ); diff --git a/shared/shell/public/trevwbeh.h b/shared/shell/public/trevwbeh.h index d52d594..8a76dff 100644 --- a/shared/shell/public/trevwbeh.h +++ b/shared/shell/public/trevwbeh.h @@ -27,7 +27,7 @@ GType wintc_sh_tree_view_behaviour_get_type(void) G_GNUC_CONST; // PUBLIC FUNCTIONS // WinTCShTreeViewBehaviour* wintc_sh_tree_view_behaviour_new( - GtkTreeView* icon_view, + GtkTreeView* tree_view, WinTCShBrowser* browser ); diff --git a/shared/shell/src/browser.c b/shared/shell/src/browser.c index 09c503f..566e64c 100644 --- a/shared/shell/src/browser.c +++ b/shared/shell/src/browser.c @@ -284,6 +284,13 @@ gboolean wintc_sh_browser_can_navigate_to_parent( return wintc_ishext_view_has_parent(browser->current_view); } +WinTCIShextView* wintc_sh_browser_get_current_view( + WinTCShBrowser* browser +) +{ + return browser->current_view; +} + void wintc_sh_browser_get_location( WinTCShBrowser* browser, WinTCShextPathInfo* path_info @@ -307,6 +314,13 @@ GtkTreeModel* wintc_sh_browser_get_model( return GTK_TREE_MODEL(browser->view_model); } +WinTCShextHost* wintc_sh_browser_get_shext_host( + WinTCShBrowser* browser +) +{ + return browser->shext_host; +} + const gchar* wintc_sh_browser_get_view_display_name( WinTCShBrowser* browser ) diff --git a/shared/shell/src/trevwbeh.c b/shared/shell/src/trevwbeh.c new file mode 100644 index 0000000..c2fb186 --- /dev/null +++ b/shared/shell/src/trevwbeh.c @@ -0,0 +1,680 @@ +#include +#include +#include + +#include "../public/browser.h" +#include "../public/trevwbeh.h" + +// +// PRIVATE ENUMS +// +enum +{ + PROP_BROWSER = 1, + PROP_TREE_VIEW +}; + +enum +{ + COL_ICON_NAME = 0, + COL_ENTRY_NAME, + COL_VIEW_HASH, + NUM_COLS +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_sh_tree_view_behaviour_constructed( + GObject* object +); +static void wintc_sh_tree_view_behaviour_dispose( + GObject* object +); +static void wintc_sh_tree_view_behaviour_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static void on_browser_load_changed( + WinTCShBrowser* self, + WinTCShBrowserLoadEvent load_event, + gpointer user_data +); + +static void on_view_items_added( + WinTCIShextView* view, + WinTCShextViewItemsAddedData* items_data, + gpointer user_data +); + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +struct _WinTCShTreeViewBehaviourClass +{ + GObjectClass __parent__; +}; + +struct _WinTCShTreeViewBehaviour +{ + GObject __parent__; + + WinTCShBrowser* browser; + WinTCShextHost* shext_host; + + GtkTreeStore* tree_model; + GHashTable* map_iter_to_view; + GHashTable* map_hash_to_iter; + + GtkWidget* tree_view; +}; + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCShTreeViewBehaviour, + wintc_sh_tree_view_behaviour, + G_TYPE_OBJECT +) + +static void wintc_sh_tree_view_behaviour_class_init( + WinTCShTreeViewBehaviourClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = wintc_sh_tree_view_behaviour_constructed; + object_class->dispose = wintc_sh_tree_view_behaviour_dispose; + object_class->set_property = wintc_sh_tree_view_behaviour_set_property; + + g_object_class_install_property( + object_class, + PROP_BROWSER, + g_param_spec_object( + "browser", + "Browser", + "The shell browser instance to bind to.", + WINTC_TYPE_SH_BROWSER, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); + g_object_class_install_property( + object_class, + PROP_TREE_VIEW, + g_param_spec_object( + "tree-view", + "TreeView", + "The tree view to manage.", + GTK_TYPE_TREE_VIEW, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); +} + +static void wintc_sh_tree_view_behaviour_init( + WinTCShTreeViewBehaviour* self +) +{ + // Set up maps hash->iter->view + // + // The intention of this is to be able to map view/viewitems to a node in + // the tree, and then those nodes to a concrete view object + // + // This is such that views can be essentially lazy-loaded - view items + // that are enumerated from a view are akin to placeholders, and a view is + // created only when those nodes are expanded/enumerated themselves + // + self->map_iter_to_view = g_hash_table_new_full( + g_str_hash, + g_str_equal, + g_free, + g_object_unref + ); + self->map_hash_to_iter = g_hash_table_new_full( + g_direct_hash, + g_direct_equal, + g_free, + g_free + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_sh_tree_view_behaviour_constructed( + GObject* object +) +{ + WinTCShTreeViewBehaviour* behaviour = + WINTC_SH_TREE_VIEW_BEHAVIOUR(object); + + if (!behaviour->browser || !behaviour->tree_view) + { + g_critical("%s", "ShTreeViewBehaviour: Must have a browser and view!"); + return; + } + + behaviour->shext_host = + g_object_ref(wintc_sh_browser_get_shext_host(behaviour->browser)); + + // Clear out tree view in case for some reason there's already stuff in it + // + GList* columns = gtk_tree_view_get_columns( + GTK_TREE_VIEW(behaviour->tree_view) + ); + + for (GList* iter = columns; iter; iter = iter->next) + { + gtk_tree_view_remove_column( + GTK_TREE_VIEW(behaviour->tree_view), + GTK_TREE_VIEW_COLUMN(iter->data) + ); + } + + g_list_free(columns); + + // Set up tree view + // + GtkTreeViewColumn* new_column; + GtkCellRenderer* new_cell; + + behaviour->tree_model = + gtk_tree_store_new( + 3, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_UINT + ); + + gtk_tree_view_set_headers_visible( + GTK_TREE_VIEW(behaviour->tree_view), + FALSE + ); + gtk_tree_view_set_model( + GTK_TREE_VIEW(behaviour->tree_view), + GTK_TREE_MODEL(behaviour->tree_model) + ); + + new_column = gtk_tree_view_column_new(); + new_cell = gtk_cell_renderer_pixbuf_new(); + + gtk_tree_view_column_pack_start(new_column, new_cell, FALSE); + gtk_tree_view_column_add_attribute( + new_column, + new_cell, + "icon-name", + COL_ICON_NAME + ); + + new_cell = gtk_cell_renderer_text_new(); + + gtk_tree_view_column_pack_end(new_column, new_cell, TRUE); + gtk_tree_view_column_add_attribute( + new_column, + new_cell, + "text", + COL_ENTRY_NAME + ); + + gtk_tree_view_append_column( + GTK_TREE_VIEW(behaviour->tree_view), + new_column + ); + + // TEST: Attachment to browser to monitor for view switches + g_signal_connect( + behaviour->browser, + "load-changed", + G_CALLBACK(on_browser_load_changed), + behaviour + ); + + (G_OBJECT_CLASS(wintc_sh_tree_view_behaviour_parent_class)) + ->constructed(object); +} + +static void wintc_sh_tree_view_behaviour_dispose( + GObject* object +) +{ + WinTCShTreeViewBehaviour* behaviour = + WINTC_SH_TREE_VIEW_BEHAVIOUR(object); + + g_clear_object(&(behaviour->browser)); + g_clear_object(&(behaviour->map_iter_to_view)); + g_clear_object(&(behaviour->map_hash_to_iter)); + g_clear_object(&(behaviour->shext_host)); + g_clear_object(&(behaviour->tree_view)); + + (G_OBJECT_CLASS(wintc_sh_tree_view_behaviour_parent_class)) + ->dispose(object); +} + +static void wintc_sh_tree_view_behaviour_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCShTreeViewBehaviour* behaviour = + WINTC_SH_TREE_VIEW_BEHAVIOUR(object); + + switch (prop_id) + { + case PROP_BROWSER: + behaviour->browser = g_value_dup_object(value); + break; + + case PROP_TREE_VIEW: + behaviour->tree_view = g_value_dup_object(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +// +// PUBLIC FUNCTIONS +// +WinTCShTreeViewBehaviour* wintc_sh_tree_view_behaviour_new( + GtkTreeView* tree_view, + WinTCShBrowser* browser +) +{ + return WINTC_SH_TREE_VIEW_BEHAVIOUR( + g_object_new( + WINTC_TYPE_SH_TREE_VIEW_BEHAVIOUR, + "browser", browser, + "tree-view", tree_view, + NULL + ) + ); +} + +// +// CALLBACKS +// +static void on_browser_load_changed( + WinTCShBrowser* self, + WinTCShBrowserLoadEvent load_event, + gpointer user_data +) +{ + WinTCShTreeViewBehaviour* behaviour = + WINTC_SH_TREE_VIEW_BEHAVIOUR(user_data); + + WinTCIShextView* current_view = wintc_sh_browser_get_current_view(self); + + if (!current_view || load_event != WINTC_SH_BROWSER_LOAD_STARTED) + { + return; + } + + // Collect up the views back up the tree, until we hit a view we already + // have + // + GError* error = NULL; + const gchar* iter_path = NULL; + GSList* list_views = NULL; + WinTCShextPathInfo path_info = { 0 }; + WinTCIShextView* view = current_view; + + while (view) + { + // Collect this view + // + list_views = g_slist_append(list_views, view); + + // If there is a node in the tree for the view hash, stop here + // + iter_path = + g_hash_table_lookup( + behaviour->map_hash_to_iter, + GUINT_TO_POINTER(wintc_ishext_view_get_unique_hash(view)) + ); + + if (iter_path) + { + break; + } + + // See if there's a parent to step up to + // + if (wintc_ishext_view_has_parent(view)) + { + wintc_ishext_view_get_parent_path(view, &path_info); + + view = + wintc_shext_host_get_view_for_path( + behaviour->shext_host, + &path_info, + &error + ); + + wintc_shext_path_info_free_data(&path_info); + + if (!view) + { + wintc_log_error_and_clear(&error); + } + } + else + { + view = NULL; + } + } + + // Reverse list (bottom up) and append items in the tree + // + // NOTE ON REFERENCES: + // Since the list contains the view owned by the browser as well as views + // instantiated by this function, g_object_ref() needs to only be called on + // the former (so only one ref is owned by the hash table and will get + // binned during dispose()) + // + guint hash; + gboolean new_view; + GtkTreeIter next; + WinTCIShextView* next_view; + GtkTreeIter last = { 0 }; + gboolean should_delete; + + list_views = g_slist_reverse(list_views); + + for (GSList* iter = list_views; iter; iter = iter->next) + { + view = WINTC_ISHEXT_VIEW(iter->data); + + new_view = TRUE; + should_delete = FALSE; + + WINTC_LOG_DEBUG( + "shell: tree - iterate view %s", + wintc_ishext_view_get_display_name(view) + ); + + // Special handling for the first item + // - If there's no iter_path, then we need to create the root node + // - If there is an iter path: + // - If a node exists but no view, map the view + // - If a node exists and already has a view, continue as usual + // but do not add another signal to the view + // + // FOR YOUR INFORMATION: + // The case where a node exists and already has a view can happen when: + // - An already opened view is opened again (going up one parent) + // - A leaf node has gained children (eg. ZIP files opened in the ZIP + // shell extension + // + if (iter == list_views) + { + if (iter_path) + { + gtk_tree_model_get_iter_from_string( + GTK_TREE_MODEL(behaviour->tree_model), + &next, + iter_path + ); + + if ( + g_hash_table_contains( + behaviour->map_iter_to_view, + iter_path + ) + ) + { + WINTC_LOG_DEBUG( + "shell: tree - first iter is a mapped view." + ); + + // Opposite case here, because we are not mapping this + // view, we need to make sure it gets binned if it is not + // owned by the browser + // + // Of course, we can't bin it until we're done with it at + // the end of the iteration + // + if (view != current_view) + { + should_delete = TRUE; + } + + new_view = FALSE; + } + else + { + WINTC_LOG_DEBUG( + "shell: tree - first iter is a view item." + ); + + if (view == current_view) + { + g_object_ref(view); + } + + g_hash_table_insert( + behaviour->map_iter_to_view, + g_strdup(iter_path), + view + ); + } + } + else + { + WINTC_LOG_DEBUG("shell: tree - creating top level."); + + hash = wintc_ishext_view_get_unique_hash(view); + + // Create root node + // + gtk_tree_store_append( + behaviour->tree_model, + &next, + NULL + ); + gtk_tree_store_set( + behaviour->tree_model, + &next, + COL_ICON_NAME, "inode-directory", + COL_ENTRY_NAME, wintc_ishext_view_get_display_name(view), + COL_VIEW_HASH, hash, + -1 + ); + + // Map the hash<->iter + // + if (view == current_view) + { + g_object_ref(view); + } + + g_hash_table_insert( + behaviour->map_hash_to_iter, + GUINT_TO_POINTER(hash), + gtk_tree_model_get_string_from_iter( + GTK_TREE_MODEL(behaviour->tree_model), + &next + ) + ); + g_hash_table_insert( + behaviour->map_iter_to_view, + gtk_tree_model_get_string_from_iter( + GTK_TREE_MODEL(behaviour->tree_model), + &next + ), + view + ); + } + + last = next; // Copy node iter over + } + + // If there's a descendant view below this one, create the node + // immediately + // + if (iter->next) + { + next_view = WINTC_ISHEXT_VIEW(iter->next->data); + + hash = wintc_ishext_view_get_unique_hash(next_view); + + if (next_view == current_view) + { + g_object_ref(next_view); + } + + gtk_tree_store_append( + behaviour->tree_model, + &next, + &last + ); + gtk_tree_store_set( + behaviour->tree_model, + &next, + COL_ICON_NAME, "inode-directory", + COL_ENTRY_NAME, wintc_ishext_view_get_display_name(next_view), + COL_VIEW_HASH, hash, + -1 + ); + + g_hash_table_insert( + behaviour->map_hash_to_iter, + GUINT_TO_POINTER(hash), + gtk_tree_model_get_string_from_iter( + GTK_TREE_MODEL(behaviour->tree_model), + &next + ) + ); + g_hash_table_insert( + behaviour->map_iter_to_view, + gtk_tree_model_get_string_from_iter( + GTK_TREE_MODEL(behaviour->tree_model), + &next + ), + next_view + ); + } + + // Watch for items and refresh if this is a new view + // + if (new_view) + { + g_signal_connect( + view, + "items-added", + G_CALLBACK(on_view_items_added), + behaviour + ); + + wintc_ishext_view_refresh_items(view); + } + + // Delete the view if needed + // + if (should_delete) + { + g_object_unref(view); + } + + // Chain for next iteration + // + last = next; + } + + g_slist_free(list_views); +} + +static void on_view_items_added( + WinTCIShextView* view, + WinTCShextViewItemsAddedData* items_data, + gpointer user_data +) +{ + WinTCShTreeViewBehaviour* behaviour = + WINTC_SH_TREE_VIEW_BEHAVIOUR(user_data); + + GtkTreeIter parent; + GtkTreeIter child; + + WINTC_LOG_DEBUG( + "shell: tree - items added to %p", + GUINT_TO_POINTER(wintc_ishext_view_get_unique_hash(view)) + ); + + // Locate the parent node + // + const gchar* iter_path = + g_hash_table_lookup( + behaviour->map_hash_to_iter, + GUINT_TO_POINTER(wintc_ishext_view_get_unique_hash(view)) + ); + + if ( + !gtk_tree_model_get_iter_from_string( + GTK_TREE_MODEL(behaviour->tree_model), + &parent, + iter_path + ) + ) + { + g_critical("shell: tree - somehow unable to find parent!"); + return; + } + + // Iterate over the items to append to the tree + // + WinTCShextViewItem* view_item; + + for (gint i = 0; i < items_data->num_items; i++) + { + view_item = &(items_data->items[i]); + + // Skip leaf nodes and nodes that already exist + // + if ( + view_item->is_leaf || + g_hash_table_contains( + behaviour->map_hash_to_iter, + GUINT_TO_POINTER(view_item->hash) + ) + ) + { + continue; + } + + WINTC_LOG_DEBUG( + "shell: tree adding item %p", + GUINT_TO_POINTER(view_item->hash) + ); + + gtk_tree_store_append( + behaviour->tree_model, + &child, + &parent + ); + gtk_tree_store_set( + behaviour->tree_model, + &child, + COL_ICON_NAME, "inode-directory", + COL_ENTRY_NAME, view_item->display_name, + COL_VIEW_HASH, view_item->hash, + -1 + ); + + g_hash_table_insert( + behaviour->map_hash_to_iter, + GUINT_TO_POINTER(view_item->hash), + gtk_tree_model_get_string_from_iter( + GTK_TREE_MODEL(behaviour->tree_model), + &child + ) + ); + } +} diff --git a/shared/shell/src/vwcpl.c b/shared/shell/src/vwcpl.c index aed4448..9396ef4 100644 --- a/shared/shell/src/vwcpl.c +++ b/shared/shell/src/vwcpl.c @@ -52,6 +52,10 @@ static void wintc_sh_view_cpl_get_path( WinTCShextPathInfo* path_info ); +static guint wintc_sh_view_cpl_get_unique_hash( + WinTCIShextView* view +); + static gboolean wintc_sh_view_cpl_has_parent( WinTCIShextView* view ); @@ -114,6 +118,7 @@ static void wintc_sh_view_cpl_ishext_view_interface_init( iface->get_display_name = wintc_sh_view_cpl_get_display_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; } @@ -200,6 +205,7 @@ static void wintc_sh_view_cpl_refresh_items( applet->icon_name : "image-missing"; view_item->is_leaf = wintc_sh_cpl_applet_is_executable(applet); + view_item->hash = g_str_hash(applet->exec); view_item->priv = applet; i++; @@ -266,6 +272,13 @@ static void wintc_sh_view_cpl_get_path( ); } +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) ) diff --git a/shared/shell/src/vwdesk.c b/shared/shell/src/vwdesk.c index 6a89688..02d30ef 100644 --- a/shared/shell/src/vwdesk.c +++ b/shared/shell/src/vwdesk.c @@ -17,24 +17,28 @@ static WinTCShextViewItem s_desktop_items[] = { "My Computer", "computer", FALSE, + 0, NULL }, { "My Documents", "folder-documents", FALSE, + 0, NULL }, { "My Network Places", "network-workgroup", FALSE, + 0, NULL }, { "Recycle Bin", "user-trash", FALSE, + 0, NULL } }; @@ -80,6 +84,10 @@ static void wintc_sh_view_desktop_get_path( WinTCShextPathInfo* path_info ); +static guint wintc_sh_view_desktop_get_unique_hash( + WinTCIShextView* view +); + static gboolean wintc_sh_view_desktop_has_parent( WinTCIShextView* view ); @@ -118,6 +126,26 @@ static void wintc_sh_view_desktop_class_init( // whatever // s_desktop_items[0].priv = wintc_sh_path_for_guid(WINTC_SH_GUID_DRIVES); + + // Assign hashes + // + for (gulong i = 0; i < G_N_ELEMENTS(s_desktop_items); i++) + { + // FIXME: Temporary hack until the implementations are finished + // + if (s_desktop_items[i].priv) + { + s_desktop_items[i].hash = g_str_hash(s_desktop_items[i].priv); + } + else + { + gchar* temp = g_strdup_printf("desktop%d", g_random_int()); + + s_desktop_items[i].hash = g_str_hash(temp); + + g_free(temp); + } + } } static void wintc_sh_view_desktop_init( @@ -135,6 +163,7 @@ static void wintc_sh_view_desktop_ishext_view_interface_init( iface->get_display_name = wintc_sh_view_desktop_get_display_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; } @@ -220,6 +249,13 @@ static void wintc_sh_view_desktop_get_path( ); } +static guint wintc_sh_view_desktop_get_unique_hash( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + return g_str_hash(wintc_sh_get_place_path(WINTC_SH_PLACE_DESKTOP)); +} + static gboolean wintc_sh_view_desktop_has_parent( WINTC_UNUSED(WinTCIShextView* view) ) diff --git a/shared/shell/src/vwdrives.c b/shared/shell/src/vwdrives.c index 3d060b9..f3b7442 100644 --- a/shared/shell/src/vwdrives.c +++ b/shared/shell/src/vwdrives.c @@ -12,8 +12,8 @@ // FIXME: Temporary - only item is the drive root atm // static WinTCShextViewItem s_temp_items[] = { - { "/", "drive-harddisk", FALSE, "file:///" }, - { "Control Panel", "preferences-other", FALSE, NULL } + { "/", "drive-harddisk", FALSE, 0, "file:///" }, + { "Control Panel", "preferences-other", FALSE, 0, NULL } }; // @@ -57,6 +57,10 @@ static void wintc_sh_view_drives_get_path( WinTCShextPathInfo* path_info ); +static guint wintc_sh_view_drives_get_unique_hash( + WinTCIShextView* view +); + static gboolean wintc_sh_view_drives_has_parent( WinTCIShextView* view ); @@ -94,6 +98,11 @@ static void wintc_sh_view_drives_class_init( // TEMP: Assign CPL view path // s_temp_items[1].priv = wintc_sh_path_for_guid(WINTC_SH_GUID_CPL); + + // TEMP: Assign hashes + // + s_temp_items[0].hash = g_str_hash("/"); + s_temp_items[1].hash = g_str_hash(s_temp_items[1].priv); } static void wintc_sh_view_drives_init( @@ -111,6 +120,7 @@ static void wintc_sh_view_drives_ishext_view_interface_init( iface->get_display_name = wintc_sh_view_drives_get_display_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; } @@ -197,6 +207,13 @@ static void wintc_sh_view_drives_get_path( ); } +static guint wintc_sh_view_drives_get_unique_hash( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + return g_str_hash(wintc_sh_get_place_path(WINTC_SH_PLACE_DRIVES)); +} + static gboolean wintc_sh_view_drives_has_parent( WINTC_UNUSED(WinTCIShextView* view) ) diff --git a/shared/shell/src/vwfs.c b/shared/shell/src/vwfs.c index 09c56d0..5ca568a 100644 --- a/shared/shell/src/vwfs.c +++ b/shared/shell/src/vwfs.c @@ -61,6 +61,10 @@ static void wintc_sh_view_fs_get_actions_for_view( WinTCIShextView* view ); +static guint wintc_sh_view_fs_get_unique_hash( + WinTCIShextView* view +); + static const gchar* wintc_sh_view_fs_get_display_name( WinTCIShextView* view ); @@ -175,6 +179,7 @@ static void wintc_sh_view_fs_ishext_view_interface_init( iface->get_display_name = wintc_sh_view_fs_get_display_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; } @@ -380,6 +385,7 @@ static void wintc_sh_view_fs_refresh_items( item->display_name = (gchar*) g_steal_pointer(&(iter->data)); item->icon_name = is_dir ? "inode-directory" : "empty"; item->is_leaf = !is_dir; + item->hash = g_str_hash(entry_path); g_free(entry_path); @@ -469,6 +475,15 @@ static void wintc_sh_view_fs_get_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) ) diff --git a/shared/shellext/public/if_view.h b/shared/shellext/public/if_view.h index 6036379..f4559f7 100644 --- a/shared/shellext/public/if_view.h +++ b/shared/shellext/public/if_view.h @@ -60,6 +60,9 @@ struct _WinTCIShextViewInterface WinTCIShextView* view, WinTCShextPathInfo* path_info ); + guint (*get_unique_hash) ( + WinTCIShextView* view + ); gboolean (*has_parent) ( WinTCIShextView* view ); @@ -102,6 +105,9 @@ void wintc_ishext_view_get_path( WinTCIShextView* view, WinTCShextPathInfo* path_info ); +guint wintc_ishext_view_get_unique_hash( + WinTCIShextView* view +); gboolean wintc_ishext_view_has_parent( WinTCIShextView* view ); @@ -130,4 +136,12 @@ void wintc_shext_path_info_move( WinTCShextPathInfo* src ); +guint wintc_shext_path_info_hash( + gconstpointer v +); +gboolean wintc_shext_path_info_equal( + gconstpointer v1, + gconstpointer v2 +); + #endif diff --git a/shared/shellext/public/viewitem.h b/shared/shellext/public/viewitem.h index b399f46..c8e02e2 100644 --- a/shared/shellext/public/viewitem.h +++ b/shared/shellext/public/viewitem.h @@ -13,6 +13,7 @@ typedef struct _WinTCShextViewItem gchar* display_name; gchar* icon_name; gboolean is_leaf; + guint hash; gpointer priv; } WinTCShextViewItem; diff --git a/shared/shellext/src/if_view.c b/shared/shellext/src/if_view.c index 340bfa3..fc74989 100644 --- a/shared/shellext/src/if_view.c +++ b/shared/shellext/src/if_view.c @@ -136,6 +136,16 @@ void wintc_ishext_view_get_path( return iface->get_path(view, path_info); } +guint wintc_ishext_view_get_unique_hash( + WinTCIShextView* view +) +{ + WinTCIShextViewInterface* iface = + WINTC_ISHEXT_VIEW_GET_IFACE(view); + + return iface->get_unique_hash(view); +} + gboolean wintc_ishext_view_has_parent( WinTCIShextView* view ) @@ -228,3 +238,37 @@ void wintc_shext_path_info_move( dst->base_path = g_steal_pointer(&(src->base_path)); dst->extended_path = g_steal_pointer(&(src->extended_path)); } + +guint wintc_shext_path_info_hash( + gconstpointer v +) +{ + const WinTCShextPathInfo* path_info = (WinTCShextPathInfo*) v; + + // Not sure if this reduces the effectiveness of the hash... think it + // should be okay? + // + guint hash1 = g_str_hash( + path_info->base_path ? + path_info->base_path : "" + ); + guint hash2 = g_str_hash( + path_info->extended_path ? + path_info->extended_path : "" + ); + + return hash1 * 33 + hash2; +} + +gboolean wintc_shext_path_info_equal( + gconstpointer v1, + gconstpointer v2 +) +{ + const WinTCShextPathInfo* path_info1 = (WinTCShextPathInfo*) v1; + const WinTCShextPathInfo* path_info2 = (WinTCShextPathInfo*) v2; + + return + g_strcmp0(path_info1->base_path, path_info2->base_path) == 0 && + g_strcmp0(path_info1->extended_path, path_info2->extended_path) == 0; +} diff --git a/shell/cpl/printers/src/vwprntrs.c b/shell/cpl/printers/src/vwprntrs.c index d60d8fe..3618593 100644 --- a/shell/cpl/printers/src/vwprntrs.c +++ b/shell/cpl/printers/src/vwprntrs.c @@ -13,7 +13,7 @@ // FIXME: Temporary, until we display printers // static WinTCShextViewItem s_temp_items[] = { - { "FPO", "printer", TRUE, NULL } + { "FPO", "printer", TRUE, 0, NULL } }; // @@ -57,6 +57,10 @@ static void wintc_cpl_view_printers_get_path( WinTCShextPathInfo* path_info ); +static guint wintc_cpl_view_printers_get_unique_hash( + WinTCIShextView* view +); + static gboolean wintc_cpl_view_printers_has_parent( WinTCIShextView* view ); @@ -89,7 +93,14 @@ G_DEFINE_TYPE_WITH_CODE( static void wintc_cpl_view_printers_class_init( WINTC_UNUSED(WinTCCplViewPrintersClass* klass) -) {} +) +{ + gchar* temp = g_strdup_printf("printers%d", g_random_int()); + + s_temp_items[0].hash = g_str_hash(temp); + + g_free(temp); +} static void wintc_cpl_view_printers_init( WINTC_UNUSED(WinTCCplViewPrinters* self) @@ -106,6 +117,7 @@ static void wintc_cpl_view_printers_ishext_view_interface_init( iface->get_display_name = wintc_cpl_view_printers_get_display_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; } @@ -188,6 +200,13 @@ static void wintc_cpl_view_printers_get_path( ); } +static guint wintc_cpl_view_printers_get_unique_hash( + WINTC_UNUSED(WinTCIShextView* view) +) +{ + return g_str_hash(wintc_sh_get_place_path(WINTC_SH_PLACE_PRINTERS)); +} + static gboolean wintc_cpl_view_printers_has_parent( WINTC_UNUSED(WinTCIShextView* view) ) diff --git a/shell/explorer/src/res/fldrside.ui b/shell/explorer/src/res/fldrside.ui index 8b0d602..9d5ae77 100644 --- a/shell/explorer/src/res/fldrside.ui +++ b/shell/explorer/src/res/fldrside.ui @@ -46,9 +46,17 @@ - + True - False + True + in + + + + True + False + + True diff --git a/shell/explorer/src/sidebars/fldrside.c b/shell/explorer/src/sidebars/fldrside.c index 98e4de6..50696e4 100644 --- a/shell/explorer/src/sidebars/fldrside.c +++ b/shell/explorer/src/sidebars/fldrside.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "../sidebar.h" #include "../window.h" @@ -10,6 +11,15 @@ // FORWARD DECLARATIONS // static void wintc_exp_folders_sidebar_constructed( + GObject* object +); +static void wintc_exp_folders_sidebar_dispose( + GObject* object +); + +static void on_explorer_window_mode_changed( + WinTCExplorerWindow* self, + gpointer user_data ); // @@ -23,6 +33,14 @@ struct _WinTCExpFoldersSidebarClass struct _WinTCExpFoldersSidebar { WinTCExplorerSidebar __parent__; + + // Logic stuff + // + WinTCShTreeViewBehaviour* behaviour_tree; + + // UI + // + GtkWidget* tree_view; }; // @@ -41,6 +59,7 @@ static void wintc_exp_folders_sidebar_class_init( GObjectClass* object_class = G_OBJECT_CLASS(klass); object_class->constructed = wintc_exp_folders_sidebar_constructed; + object_class->dispose = wintc_exp_folders_sidebar_dispose; } static void wintc_exp_folders_sidebar_init( @@ -61,6 +80,13 @@ static void wintc_exp_folders_sidebar_init( ) ); + self->tree_view = + GTK_WIDGET( + g_object_ref( + gtk_builder_get_object(builder, "tree-view") + ) + ); + g_object_unref(builder); } @@ -68,8 +94,57 @@ static void wintc_exp_folders_sidebar_init( // CLASS VIRTUAL METHODS // static void wintc_exp_folders_sidebar_constructed( - WINTC_UNUSED(GObject* object) -) {} + GObject* object +) +{ + WinTCExplorerSidebar* sidebar = + WINTC_EXPLORER_SIDEBAR(object); + WinTCExpFoldersSidebar* sidebar_folders = + WINTC_EXP_FOLDERS_SIDEBAR(object); + + // Potentially may need to delay loading the tree view behaviour depending + // on the state of the explorer window + // + if ( + wintc_explorer_window_get_mode( + WINTC_EXPLORER_WINDOW(sidebar->owner_explorer_wnd) + ) == WINTC_EXPLORER_WINDOW_MODE_LOCAL + ) + { + sidebar_folders->behaviour_tree = + wintc_sh_tree_view_behaviour_new( + GTK_TREE_VIEW(sidebar_folders->tree_view), + wintc_explorer_window_get_browser( + WINTC_EXPLORER_WINDOW(sidebar->owner_explorer_wnd) + ) + ); + } + else + { + g_signal_connect( + sidebar->owner_explorer_wnd, + "mode-changed", + G_CALLBACK(on_explorer_window_mode_changed), + object + ); + } + + (G_OBJECT_CLASS(wintc_exp_folders_sidebar_parent_class)) + ->constructed(object); +} + +static void wintc_exp_folders_sidebar_dispose( + GObject* object +) +{ + WinTCExpFoldersSidebar* sidebar_folders = + WINTC_EXP_FOLDERS_SIDEBAR(object); + + g_clear_object(&(sidebar_folders->behaviour_tree)); + + (G_OBJECT_CLASS(wintc_exp_folders_sidebar_parent_class)) + ->dispose(object); +} // // PUBLIC FUNCTIONS @@ -86,3 +161,34 @@ WinTCExplorerSidebar* wintc_exp_folders_sidebar_new( ) ); } + +// +// CALLBACKS +// +static void on_explorer_window_mode_changed( + WinTCExplorerWindow* self, + gpointer user_data +) +{ + WinTCExpFoldersSidebar* sidebar_folders = + WINTC_EXP_FOLDERS_SIDEBAR(user_data); + + if (sidebar_folders->behaviour_tree) + { + return; + } + + if ( + wintc_explorer_window_get_mode(self) != + WINTC_EXPLORER_WINDOW_MODE_LOCAL + ) + { + return; + } + + sidebar_folders->behaviour_tree = + wintc_sh_tree_view_behaviour_new( + GTK_TREE_VIEW(sidebar_folders->tree_view), + wintc_explorer_window_get_browser(self) + ); +} diff --git a/shell/explorer/src/window.c b/shell/explorer/src/window.c index 36f91cf..ba71cf4 100644 --- a/shell/explorer/src/window.c +++ b/shell/explorer/src/window.c @@ -39,10 +39,10 @@ enum static void wintc_explorer_window_constructed( GObject* object ); -static void wintc_explorer_window_finalize( +static void wintc_explorer_window_dispose( GObject* object ); -static void wintc_explorer_window_dispose( +static void wintc_explorer_window_finalize( GObject* object ); static void wintc_explorer_window_set_property( @@ -473,6 +473,13 @@ GtkWidget* wintc_explorer_window_new( ); } +WinTCShBrowser* wintc_explorer_window_get_browser( + WinTCExplorerWindow* wnd +) +{ + return wnd->browser; +} + void wintc_explorer_window_get_location( WinTCExplorerWindow* wnd, WinTCShextPathInfo* path_info @@ -521,8 +528,9 @@ static void do_navigation( const gchar* specified_path ) { - GError* error = NULL; - const GRegex* regex_looks_webish = NULL; + static GRegex* regex_looks_webish = NULL; + + GError* error = NULL; if (!regex_looks_webish) { diff --git a/shell/explorer/src/window.h b/shell/explorer/src/window.h index 461233f..54a82bc 100644 --- a/shell/explorer/src/window.h +++ b/shell/explorer/src/window.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "application.h" @@ -41,6 +42,9 @@ GtkWidget* wintc_explorer_window_new( const gchar* initial_path ); +WinTCShBrowser* wintc_explorer_window_get_browser( + WinTCExplorerWindow* wnd +); void wintc_explorer_window_get_location( WinTCExplorerWindow* wnd, WinTCShextPathInfo* path_info diff --git a/shell/shext/zip/src/vwzip.c b/shell/shext/zip/src/vwzip.c index d224fe8..1c91819 100644 --- a/shell/shext/zip/src/vwzip.c +++ b/shell/shext/zip/src/vwzip.c @@ -66,10 +66,18 @@ static void wintc_view_zip_get_path( WinTCShextPathInfo* path_info ); +static guint wintc_view_zip_get_unique_hash( + WinTCIShextView* view +); + static gboolean wintc_view_zip_has_parent( WinTCIShextView* view ); +static guint zip_entry_hash( + const gchar* zip_file_path, + const gchar* rel_entry_path +); static gboolean zip_entry_is_in_dir( const gchar* this_dir, const gchar* entry, @@ -169,6 +177,7 @@ static void wintc_view_zip_ishext_view_interface_init( iface->get_display_name = wintc_view_zip_get_display_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; } @@ -323,8 +332,9 @@ static void wintc_view_zip_refresh_items( ); item->display_name = entry_copy + name_offset; - item->icon_name = g_str_has_suffix(entry_name, G_DIR_SEPARATOR_S) ? - "inode-directory" : "empty"; + 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; n_local_entries++; @@ -474,6 +484,19 @@ static void wintc_view_zip_get_path( path_info->extended_path = g_strdup(view_zip->rel_path); } +static guint wintc_view_zip_get_unique_hash( + WinTCIShextView* view +) +{ + WinTCViewZip* view_zip = WINTC_VIEW_ZIP(view); + + return + zip_entry_hash( + (view_zip->zip_uri + strlen("file://")), + view_zip->rel_path + ); +} + static gboolean wintc_view_zip_has_parent( WINTC_UNUSED(WinTCIShextView* view) ) @@ -502,6 +525,21 @@ WinTCIShextView* wintc_view_zip_new( // // PRIVATE FUNCTIONS // +static guint zip_entry_hash( + const gchar* zip_file_path, + const gchar* rel_entry_path +) +{ + guint hash = g_str_hash(zip_file_path); + + if (rel_entry_path) + { + hash = hash * 33 + g_str_hash(rel_entry_path); + } + + return hash; +} + static gboolean zip_entry_is_in_dir( const gchar* this_dir, const gchar* entry, diff --git a/tools/bldutils/depmap/rpm-maps b/tools/bldutils/depmap/rpm-maps index a8e072c..1655167 100644 --- a/tools/bldutils/depmap/rpm-maps +++ b/tools/bldutils/depmap/rpm-maps @@ -14,7 +14,7 @@ gtk3-->rt-->gtk3 lightdm-->bt-->lightdm-gobject-devel lightdm-->rt-->lightdm-gobject msgfmt-->bt,rt-->gettext -networkmanager-->bt-->NetworkManager-libnm +networkmanager-->bt-->NetworkManager-libnm-devel networkmanager-->rt-->NetworkManager plymouth-->bt,rt-->plymouth pulseaudio-->bt-->pulseaudio-libs-devel