Enhancement: Fixes #376, explorer/shell - implement default tree view behaviour

This commit is contained in:
Rory Fewell
2024-11-20 20:39:35 +00:00
parent c1a57641bc
commit 6beac99459
19 changed files with 1042 additions and 16 deletions

View File

@@ -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

View File

@@ -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
);

View File

@@ -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
);

View File

@@ -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
)

680
shared/shell/src/trevwbeh.c Normal file
View File

@@ -0,0 +1,680 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <wintc/comgtk.h>
#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
)
);
}
}

View File

@@ -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)
)

View File

@@ -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)
)

View File

@@ -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)
)

View File

@@ -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)
)

View File

@@ -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

View File

@@ -13,6 +13,7 @@ typedef struct _WinTCShextViewItem
gchar* display_name;
gchar* icon_name;
gboolean is_leaf;
guint hash;
gpointer priv;
} WinTCShextViewItem;

View File

@@ -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;
}

View File

@@ -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)
)

View File

@@ -46,9 +46,17 @@
</packing>
</child>
<child>
<object class="GtkTreeView">
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="tree-view">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>

View File

@@ -1,6 +1,7 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <wintc/comgtk.h>
#include <wintc/shell.h>
#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)
);
}

View File

@@ -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)
{

View File

@@ -3,6 +3,7 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <wintc/shell.h>
#include <wintc/shellext.h>
#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

View File

@@ -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,

View File

@@ -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