diff --git a/shared/comgtk/public/list.h b/shared/comgtk/public/list.h index 9d9dee2..43e10dd 100644 --- a/shared/comgtk/public/list.h +++ b/shared/comgtk/public/list.h @@ -9,6 +9,18 @@ // PUBLIC FUNCTIONS // +/** + * Appends one list to the end of another. + * + * @param list The list. + * @param other_list The list to append. + * @return The list. + */ +GList* wintc_list_append_list( + GList* list, + GList* other_list +); + /** * Appends an item to the end of a list, if an item of equal value already * exists in the list it will be removed and freed. diff --git a/shared/comgtk/src/list.c b/shared/comgtk/src/list.c index 82569cf..b802d53 100644 --- a/shared/comgtk/src/list.c +++ b/shared/comgtk/src/list.c @@ -5,6 +5,43 @@ // // PUBLIC FUNCTIONS // +GList* wintc_list_append_list( + GList* list, + GList* other_list +) +{ + // If either list is NULL, return the other one + // (this also returns NULL if both are NULL) + // + if (!list) + { + return other_list; + } + else if (!other_list) + { + return list; + } + + // Invalid op if other list is not the head + // + if (other_list->prev) + { + g_critical( + "%s", + "comgtk: cannot append list to list, other list is not the head" + ); + + return list; + } + + GList* tail = g_list_last(list); + + tail->next = other_list; + other_list->prev = tail; + + return list; +} + GList* wintc_list_distinct_append( GList* list, gpointer data, diff --git a/shared/shell/CMakeLists.txt b/shared/shell/CMakeLists.txt index bc03923..a463e6a 100644 --- a/shared/shell/CMakeLists.txt +++ b/shared/shell/CMakeLists.txt @@ -55,13 +55,15 @@ add_library( public/fsop.h src/icnvwbeh.c public/icnvwbeh.h + src/newmenu.c + public/newmenu.h + src/nmspace.c + public/nmspace.h src/resources.c public/sound.h src/sound.c src/trevwbeh.c public/trevwbeh.h - src/nmspace.c - public/nmspace.h src/vwcpl.c public/vwcpl.h src/vwdesk.c diff --git a/shared/shell/public/libapi.h.in b/shared/shell/public/libapi.h.in index f6612c1..d57680c 100644 --- a/shared/shell/public/libapi.h.in +++ b/shared/shell/public/libapi.h.in @@ -9,6 +9,7 @@ #include "@LIB_HEADER_DIR@/fsclipbd.h" #include "@LIB_HEADER_DIR@/fsop.h" #include "@LIB_HEADER_DIR@/icnvwbeh.h" +#include "@LIB_HEADER_DIR@/newmenu.h" #include "@LIB_HEADER_DIR@/nmspace.h" #include "@LIB_HEADER_DIR@/sound.h" #include "@LIB_HEADER_DIR@/trevwbeh.h" diff --git a/shared/shell/public/newmenu.h b/shared/shell/public/newmenu.h new file mode 100644 index 0000000..34f74f3 --- /dev/null +++ b/shared/shell/public/newmenu.h @@ -0,0 +1,18 @@ +#ifndef __SHELL_NEWMENU_H__ +#define __SHELL_NEWMENU_H__ + +#include + +// +// PUBLIC FUNCTIONS +// +GMenuModel* wintc_sh_new_menu_get_menu(void); + +gboolean wintc_sh_new_menu_create_file( + const gchar* path, + gint op_id, + guint* hash_new_file, + GError** error +); + +#endif diff --git a/shared/shell/public/vwdesk.h b/shared/shell/public/vwdesk.h index b1b5fb0..5adef16 100644 --- a/shared/shell/public/vwdesk.h +++ b/shared/shell/public/vwdesk.h @@ -22,6 +22,8 @@ GType wintc_sh_view_desktop_get_type(void) G_GNUC_CONST; // // PUBLIC FUNCTIONS // -WinTCIShextView* wintc_sh_view_desktop_new(void); +WinTCIShextView* wintc_sh_view_desktop_new( + WinTCShextHost* shext_host +); #endif diff --git a/shared/shell/src/icnvwbeh.c b/shared/shell/src/icnvwbeh.c index c518fdf..6511de0 100644 --- a/shared/shell/src/icnvwbeh.c +++ b/shared/shell/src/icnvwbeh.c @@ -586,7 +586,7 @@ static void action_view_operation( // Execute! // - if (!(operation->func) (view, operation, wnd, &error)) + if (!(operation->func) (operation->view, operation, wnd, &error)) { wintc_display_error_and_clear(&error, wnd); } diff --git a/shared/shell/src/newmenu.c b/shared/shell/src/newmenu.c new file mode 100644 index 0000000..a14acf6 --- /dev/null +++ b/shared/shell/src/newmenu.c @@ -0,0 +1,417 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../public/fsop.h" +#include "../public/newmenu.h" + +// +// PRIVATE ENUMS +// +enum +{ + WINTC_SH_NEW_OP_NEW_FOLDER = 80, + WINTC_SH_NEW_OP_NEW_SHORTCUT, + WINTC_SH_NEW_OP_FIRST_TEMPLATE +}; + +// +// PRIVATE STRUCTURES +// +typedef struct _WinTCShNewTemplate +{ + gchar* filename; + gchar* name; +} WinTCShNewTemplate; + +// +// FORWARD DECLARATIONS +// +static void wintc_sh_new_menu_update_templates(void); + +static void clear_new_template( + WinTCShNewTemplate* template +); + +// +// STATIC DATA +// +static GList* S_LIST_TEMPLATES = NULL; + +// +// PUBLIC FUNCTIONS +// +GMenuModel* wintc_sh_new_menu_get_menu(void) +{ + static GMenuModel* s_menu = NULL; + + if (s_menu) + { + return s_menu; + } + + wintc_sh_new_menu_update_templates(); + + // Create the menu + // + GtkBuilder* builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/shell/menunew.ui" + ); + + wintc_lc_builder_preprocess_widget_text(builder); + + s_menu = + G_MENU_MODEL( + g_object_ref( + gtk_builder_get_object(builder, "menu-model") + ) + ); + + // Populate templates + // + GMenu* section_new_templates = + G_MENU( + gtk_builder_get_object( + builder, + "section-new-templates" + ) + ); + + gint view_op_id = WINTC_SHEXT_OP_NEW + 2; + + for (GList* iter = S_LIST_TEMPLATES; iter; iter = iter->next) + { + WinTCShNewTemplate* template = + (WinTCShNewTemplate*) iter->data; + + // Cap off menu items + // + if (!WINTC_SHEXT_OP_IS_NEW_OP(view_op_id)) + { + break; + } + + // Create the menu item + // + GIcon* icon = g_themed_icon_new(template->filename); + GMenuItem* menu_item = g_menu_item_new(NULL, NULL); + + g_menu_item_set_label(menu_item, template->name); + g_menu_item_set_icon(menu_item, icon); + + g_menu_item_set_action_and_target( + menu_item, + "control.view-op", + "i", + view_op_id + ); + + g_menu_append_item( + section_new_templates, + menu_item + ); + + view_op_id++; + + g_object_unref(icon); + g_object_unref(menu_item); + } + + g_object_unref(builder); + + return s_menu; +} + +gboolean wintc_sh_new_menu_create_file( + const gchar* path, + gint op_id, + guint* hash_new_file, + GError** error +) +{ + // + // FIXME: Localisation needed in the names this func uses + // + + // Handle Folder and Shortcut as special cases + // + // FIXME: Not doing shortcuts just yet because it requires a wizard + // + gboolean is_folder = op_id == WINTC_SH_NEW_OP_NEW_FOLDER; + + GFile* file; + guint hash; + GError* local_error = NULL; + gchar* name; + const gchar* name_type; + gchar* new_path; + gboolean success; + WinTCShNewTemplate* template; + + // FIXME: Drop out of shortcut handling for now + // + if (op_id == WINTC_SH_NEW_OP_NEW_SHORTCUT) + { + g_critical( + "%s", + "shell: new - shortcuts not implemented" + ); + return FALSE; + } + + if (is_folder) + { + name_type = "Folder"; + } + else + { + template = + (WinTCShNewTemplate*) + g_list_nth_data( + S_LIST_TEMPLATES, + op_id - WINTC_SH_NEW_OP_FIRST_TEMPLATE + ); + + name_type = template->name; + } + + for (gint attempt = 0; attempt < 100; attempt++) + { + if (attempt) + { + name = + g_strdup_printf( + "New %s (%d)", + name_type, + attempt + ); + } + else + { + name = + g_strdup_printf( + "New %s", + name_type + ); + } + + new_path = g_build_path(G_DIR_SEPARATOR_S, path, name, NULL); + hash = g_str_hash(new_path); + file = g_file_new_for_path(new_path); + + if (is_folder) + { + success = + g_file_make_directory( + file, + NULL, + &local_error + ); + } + else + { + gchar* template_path = g_build_path( + G_DIR_SEPARATOR_S, + WINTC_ASSETS_DIR, + "templates", + template->filename, + NULL + ); + GFile* template_file = g_file_new_for_path(template_path); + + success = + g_file_copy ( + template_file, + file, + G_FILE_COPY_NONE, + NULL, + NULL, + NULL, + &local_error + ); + + g_free(template_path); + g_object_unref(template_file); + } + + g_free(new_path); + g_free(name); + g_object_unref(file); + + if (success) + { + WINTC_SAFE_REF_SET(hash_new_file, hash); + return TRUE; + } + else + { + if (local_error->code == G_IO_ERROR_EXISTS) + { + g_clear_error(&local_error); + continue; + } + else + { + g_propagate_error(error, local_error); + return FALSE; + } + } + } + + // FIXME: Set error here + return FALSE; +} + +// +// PRIVATE FUNCTIONS +// +static void clear_new_template( + WinTCShNewTemplate* template +) +{ + g_free(template->filename); + g_free(template->name); + g_free(template); +} + +static void wintc_sh_new_menu_update_templates(void) +{ + GList* templates = + wintc_sh_fs_get_names_as_list( + WINTC_ASSETS_DIR G_DIR_SEPARATOR_S "templates", + FALSE, + G_FILE_TEST_IS_REGULAR, + FALSE, + NULL + ); + + g_clear_list( + &(S_LIST_TEMPLATES), + (GDestroyNotify) clear_new_template + ); + + for (GList* iter = templates; iter; iter = iter->next) + { + const gchar* filename = (gchar*) iter->data; + + // Pull out the MIME type parts from filename + // + const gchar* p_mime_base_end = strstr(filename, "-"); + + if (!p_mime_base_end) + { + g_warning( + "shell: fs - doesn't look like valid MIME: %s", + filename + ); + + continue; + } + + // Build the file path... + // + gchar* mime_base = wintc_substr(filename, p_mime_base_end); + gchar* mime_spec = wintc_substr(p_mime_base_end + 1, NULL); + gchar* mime_specfn = g_strdup_printf("%s.xml", mime_spec); + + gchar* mime_path = + g_build_path( + G_DIR_SEPARATOR_S, + G_DIR_SEPARATOR_S, + "usr", +#ifdef WINTC_PKGMGR_BSDPKG + "local", +#endif + "share", + "mime", + mime_base, + mime_specfn, + NULL + ); + + // Attempt to parse the MIME XML + // + xmlDocPtr xml_mime = xmlParseFile(mime_path); + + g_free(mime_base); + g_free(mime_spec); + g_free(mime_specfn); + g_free(mime_path); + + if (!xml_mime) + { + WINTC_LOG_DEBUG( + "shell: fs - could not get XML for %s", + filename + ); + + continue; + } + + // Retrieve the name + // + xmlNodePtr xml_root = xmlDocGetRootElement(xml_mime); + + for (xmlNodePtr node = xml_root->children; node; node = node->next) + { + // We're looking for + // + xmlChar* node_lang; + + if (node->type != XML_ELEMENT_NODE) + { + continue; + } + + if (g_strcmp0((gchar*) node->name, "comment") != 0) + { + continue; + } + + if ((node_lang = xmlNodeGetLang(node))) + { + xmlFree(node_lang); + continue; + } + + // Create the new template item + // + WinTCShNewTemplate* template = + g_new(WinTCShNewTemplate, 1); + + xmlChar* mime_name = xmlNodeGetContent(node); + + template->filename = g_strdup(filename); + template->name = g_strdup((gchar*) mime_name); + + xmlFree(mime_name); + + S_LIST_TEMPLATES = + g_list_prepend( + S_LIST_TEMPLATES, + template + ); + + break; + } + + xmlFreeDoc(xml_mime); + } + + if (S_LIST_TEMPLATES) + { + S_LIST_TEMPLATES = + g_list_reverse(S_LIST_TEMPLATES); + } + + g_list_free_full( + templates, + (GDestroyNotify) g_free + ); +} diff --git a/shared/shell/src/nmspace.c b/shared/shell/src/nmspace.c index 2ba828e..48969bc 100644 --- a/shared/shell/src/nmspace.c +++ b/shared/shell/src/nmspace.c @@ -81,9 +81,9 @@ void wintc_sh_init_namespace_tree( // CALLBACKS // static WinTCIShextView* factory_view_by_guid_cb( - WINTC_UNUSED(WinTCShextHost* shext_host), - WINTC_UNUSED(WinTCShextViewAssoc assoc), - const gchar* assoc_str, + WinTCShextHost* shext_host, + WINTC_UNUSED(WinTCShextViewAssoc assoc), + const gchar* assoc_str, WINTC_UNUSED(const WinTCShextPathInfo* url) ) { @@ -97,7 +97,7 @@ static WinTCIShextView* factory_view_by_guid_cb( } else if (g_ascii_strcasecmp(assoc_str, WINTC_SH_GUID_DESKTOP) == 0) { - return wintc_sh_view_desktop_new(); + return wintc_sh_view_desktop_new(shext_host); } else if (g_ascii_strcasecmp(assoc_str, WINTC_SH_GUID_DRIVES) == 0) { diff --git a/shared/shell/src/res/menudesk.ui b/shared/shell/src/res/menudesk.ui new file mode 100644 index 0000000..fda68c6 --- /dev/null +++ b/shared/shell/src/res/menudesk.ui @@ -0,0 +1,23 @@ + + + +
+ + control.no-op + Paste + + + control.no-op + Paste Shortcut + +
+
+
+ + control.view-op + Properties + 14 + +
+
+
diff --git a/shared/shell/src/res/menufs.ui b/shared/shell/src/res/menufs.ui index 3f403b0..e3f0d14 100644 --- a/shared/shell/src/res/menufs.ui +++ b/shared/shell/src/res/menufs.ui @@ -19,29 +19,7 @@ 7 -
- - New - -
- - control.view-op - Folder - inode-directory - 80 - - - control.view-op - Shortcut - emblem-symbolic-link - 100 - -
- -
-
-
-
+
control.no-op diff --git a/shared/shell/src/res/menunew.ui b/shared/shell/src/res/menunew.ui new file mode 100644 index 0000000..2505e02 --- /dev/null +++ b/shared/shell/src/res/menunew.ui @@ -0,0 +1,21 @@ + + +
+ + control.view-op + Folder + inode-directory + 80 + + + control.view-op + Shortcut + emblem-symbolic-link + 81 + +
+ +
+
+
+
diff --git a/shared/shell/src/res/resources.xml b/shared/shell/src/res/resources.xml index 76a0151..bf7d0ea 100644 --- a/shared/shell/src/res/resources.xml +++ b/shared/shell/src/res/resources.xml @@ -7,8 +7,10 @@ menuctx.ui + menudesk.ui menufs.ui menufslf.ui menufsvw.ui + menunew.ui diff --git a/shared/shell/src/trevwbeh.c b/shared/shell/src/trevwbeh.c index 7b500c7..956ae49 100644 --- a/shared/shell/src/trevwbeh.c +++ b/shared/shell/src/trevwbeh.c @@ -19,7 +19,6 @@ enum COLUMN_ICON_NAME = 0, COLUMN_ENTRY_NAME, COLUMN_VIEW_HASH, - COLUMN_MAPPED_VIEW, N_COLUMNS }; @@ -87,6 +86,7 @@ struct _WinTCShTreeViewBehaviour GtkTreeStore* tree_model; GHashTable* map_hash_to_row; + GHashTable* map_hash_to_view; GtkWidget* tree_view; }; @@ -154,6 +154,24 @@ static void wintc_sh_tree_view_behaviour_init( NULL, (GDestroyNotify) clear_tree_row_reference ); + + // Set up map for hash->view + // + // We store the mapping from hash to view in a separate table because + // previously they were stored in a column on the tree nodes - problem is + // when it comes time to delete a node, we would have to go bottom-up to + // ensure they're all collected and unref their views (if a parent node is + // deleted first, the child nodes get binned before we can clean up) + // + // Much easier to just use a second hash table instead of doing this work + // + self->map_hash_to_view = + g_hash_table_new_full( + g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) g_object_unref + ); } // @@ -268,6 +286,7 @@ static void wintc_sh_tree_view_behaviour_dispose( g_clear_object(&(behaviour->tree_view)); g_hash_table_unref(g_steal_pointer(&(behaviour->map_hash_to_row))); + g_hash_table_unref(g_steal_pointer(&(behaviour->map_hash_to_view))); (G_OBJECT_CLASS(wintc_sh_tree_view_behaviour_parent_class)) ->dispose(object); @@ -329,15 +348,24 @@ static void wintc_sh_tree_view_behaviour_node_add_items( GtkTreeIter child; WinTCShextViewItem* view_item; + // Get the node's view + // + guint node_hash; WinTCIShextView* view = NULL; gtk_tree_model_get( GTK_TREE_MODEL(behaviour->tree_model), node, - COLUMN_MAPPED_VIEW, &view, + COLUMN_VIEW_HASH, &node_hash, -1 ); + view = + g_hash_table_lookup( + behaviour->map_hash_to_view, + GUINT_TO_POINTER(node_hash) + ); + for (GList* iter = items; iter; iter = iter->next) { view_item = iter->data; @@ -525,6 +553,7 @@ static void wintc_sh_tree_view_behaviour_update_view( { if (row_ref) { + guint node_hash; WinTCIShextView* node_view = NULL; GtkTreePath* tree_path; @@ -540,10 +569,16 @@ static void wintc_sh_tree_view_behaviour_update_view( gtk_tree_model_get( GTK_TREE_MODEL(behaviour->tree_model), &next, - COLUMN_MAPPED_VIEW, &node_view, + COLUMN_VIEW_HASH, &node_hash, -1 ); + node_view = + g_hash_table_lookup( + behaviour->map_hash_to_view, + GUINT_TO_POINTER(node_hash) + ); + gtk_tree_path_free(tree_path); if (node_view) @@ -577,11 +612,10 @@ static void wintc_sh_tree_view_behaviour_update_view( g_object_ref(view); } - gtk_tree_store_set( - behaviour->tree_model, - &next, - COLUMN_MAPPED_VIEW, view, - -1 + g_hash_table_insert( + behaviour->map_hash_to_view, + GUINT_TO_POINTER(node_hash), + view ); } } @@ -604,11 +638,10 @@ static void wintc_sh_tree_view_behaviour_update_view( COLUMN_ICON_NAME, wintc_ishext_view_get_icon_name(view), COLUMN_ENTRY_NAME, wintc_ishext_view_get_display_name(view), COLUMN_VIEW_HASH, hash, - COLUMN_MAPPED_VIEW, view, -1 ); - // Map the hash->row + // Map the hash->row and hash->view // GtkTreePath* tree_path = gtk_tree_model_get_path( @@ -629,6 +662,11 @@ static void wintc_sh_tree_view_behaviour_update_view( tree_path ) ); + g_hash_table_insert( + behaviour->map_hash_to_view, + GUINT_TO_POINTER(hash), + view + ); gtk_tree_path_free(tree_path); } @@ -661,11 +699,10 @@ static void wintc_sh_tree_view_behaviour_update_view( COLUMN_ICON_NAME, wintc_ishext_view_get_icon_name(next_view), COLUMN_ENTRY_NAME, wintc_ishext_view_get_display_name(next_view), COLUMN_VIEW_HASH, hash, - COLUMN_MAPPED_VIEW, next_view, -1 ); - // Map the hash->row + // Map the hash->row and hash->view // GtkTreePath* tree_path = gtk_tree_model_get_path( @@ -681,6 +718,11 @@ static void wintc_sh_tree_view_behaviour_update_view( tree_path ) ); + g_hash_table_insert( + behaviour->map_hash_to_view, + GUINT_TO_POINTER(hash), + next_view + ); gtk_tree_path_free(tree_path); } @@ -740,27 +782,25 @@ static void clear_tree_row_reference( GtkTreeRowReference* row_ref ) { - WinTCIShextView* view = NULL; - GtkTreeModel* tree_model = gtk_tree_row_reference_get_model(row_ref); GtkTreePath* tree_path = gtk_tree_row_reference_get_path(row_ref); - GtkTreeIter row; + if (tree_path) + { + GtkTreeIter row; - gtk_tree_model_get_iter( - tree_model, - &row, - tree_path - ); + gtk_tree_model_get_iter( + tree_model, + &row, + tree_path + ); - gtk_tree_model_get( - tree_model, - &row, - COLUMN_MAPPED_VIEW, &view, - -1 - ); + gtk_tree_store_remove( + GTK_TREE_STORE(tree_model), + &row + ); + } - g_clear_object(&view); gtk_tree_path_free(tree_path); gtk_tree_row_reference_free(row_ref); } @@ -844,50 +884,30 @@ static void on_view_items_removed( // Track down and bin via the maps // - GtkTreeIter iter; - for (GList* upd_iter = update->data; upd_iter; upd_iter = upd_iter->next) { guint item_hash = GPOINTER_TO_UINT(upd_iter->data); - // Nav to iter - // - GtkTreeRowReference* row_ref; - GtkTreePath* tree_path; - - row_ref = - g_hash_table_lookup( + if ( + !g_hash_table_contains( behaviour->map_hash_to_row, GUINT_TO_POINTER(item_hash) - ); - - if (!row_ref) + ) + ) { continue; } - tree_path = - gtk_tree_row_reference_get_path(row_ref); - - gtk_tree_model_get_iter( - GTK_TREE_MODEL(behaviour->tree_model), - &iter, - tree_path - ); - - gtk_tree_path_free(tree_path); - - // Delete node and mapping (row ref and view collected up via + // Delete mappings (row ref and node collected up via // clear_tree_row_reference()) // - gtk_tree_store_remove( - behaviour->tree_model, - &iter - ); - g_hash_table_remove( behaviour->map_hash_to_row, GUINT_TO_POINTER(item_hash) ); + g_hash_table_remove( + behaviour->map_hash_to_view, + GUINT_TO_POINTER(item_hash) + ); } } diff --git a/shared/shell/src/vwdesk.c b/shared/shell/src/vwdesk.c index c498fff..0107a20 100644 --- a/shared/shell/src/vwdesk.c +++ b/shared/shell/src/vwdesk.c @@ -1,62 +1,22 @@ #include #include +#include #include #include +#include +#include "../public/newmenu.h" #include "../public/vwdesk.h" +#define K_ORDER_FS_ITEM 1717 + // // PRIVATE ENUMS // enum { - PROP_ICON_NAME = 1 -}; - -// -// STATIC DATA -// - -// FIXME: Temp -// -static GHashTable* s_desktop_map = NULL; - -// FIXME: LAZY AGAIN! Use shlang!!!!!! Temporary as well cos the user can -// toggle which items are present -// -static WinTCShextViewItem s_desktop_items[] = { - { - "My Computer", - "computer", - FALSE, - 0, - WINTC_SHEXT_VIEW_ITEM_DEFAULT, - NULL - }, - { - "My Documents", - "folder-documents", - FALSE, - 0, - WINTC_SHEXT_VIEW_ITEM_DEFAULT, - NULL - }, - { - "My Network Places", - "network-workgroup", - FALSE, - 0, - WINTC_SHEXT_VIEW_ITEM_DEFAULT, - NULL, - }, - { - "Recycle Bin", - "user-trash", - FALSE, - 0, - WINTC_SHEXT_VIEW_ITEM_DEFAULT, - NULL, - } + PROP_ICON_NAME = 1, + PROP_SHEXT_HOST }; // @@ -66,12 +26,24 @@ static void wintc_sh_view_desktop_ishext_view_interface_init( WinTCIShextViewInterface* iface ); +static void wintc_sh_view_desktop_constructed( + GObject* object +); +static void wintc_sh_view_desktop_dispose( + GObject* object +); static void wintc_sh_view_desktop_get_property( GObject* object, guint prop_id, GValue* value, GParamSpec* pspec ); +static void wintc_sh_view_desktop_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); static gboolean wintc_sh_view_desktop_activate_item( WinTCIShextView* view, @@ -124,6 +96,87 @@ static WinTCShextOperation* wintc_sh_view_desktop_spawn_operation( GError** error ); +static gint wintc_sh_view_desktop_get_item_order( + guint item_hash +); +static GList* wintc_sh_view_desktop_filter_op_targets( + WinTCShViewDesktop* view_desk, + GList* targets +); +static WinTCShextViewItem* wintc_sh_view_desktop_get_view_item( + WinTCShViewDesktop* view_desk, + guint item_hash +); +static void wintc_sh_view_desktop_real_refresh_items( + WinTCShViewDesktop* view_desk +); + +static gboolean shopr_properties( + WinTCIShextView* view, + WinTCShextOperation* operation, + GtkWindow* wnd, + GError** error +); + +static void on_view_user_desktop_items_added( + WinTCIShextView* view, + WinTCShextViewItemsUpdate* update, + gpointer user_data +); +static void on_view_user_desktop_items_removed( + WinTCIShextView* view, + WinTCShextViewItemsUpdate* update, + gpointer user_data +); +static void on_view_user_desktop_refreshing( + WinTCIShextView* view, + gpointer user_data +); + +// +// STATIC DATA +// +static GHashTable* S_DESKTOP_MAP = NULL; +static GHashTable* S_DESKTOP_ORDER = NULL; + +// FIXME: LAZY AGAIN! Use shlang!!!!!! Temporary as well cos the user can +// toggle which items are present +// +static WinTCShextViewItem S_DESKTOP_ITEMS[] = { + { + "My Documents", + "folder-documents", + FALSE, + 0, + WINTC_SHEXT_VIEW_ITEM_DEFAULT, + NULL + }, + { + "My Computer", + "computer", + FALSE, + 0, + WINTC_SHEXT_VIEW_ITEM_DEFAULT, + NULL + }, + { + "My Network Places", + "network-workgroup", + FALSE, + 0, + WINTC_SHEXT_VIEW_ITEM_DEFAULT, + NULL, + }, + { + "Recycle Bin", + "user-trash", + FALSE, + 0, + WINTC_SHEXT_VIEW_ITEM_DEFAULT, + NULL, + } +}; + // // GLIB OOP CLASS/INSTANCE DEFINITIONS // @@ -135,6 +188,11 @@ struct _WinTCShViewDesktopClass struct _WinTCShViewDesktop { GObject __parent__; + + // State + // + WinTCShextHost* shext_host; + WinTCIShextView* view_user_desktop; }; // @@ -154,36 +212,46 @@ static void wintc_sh_view_desktop_class_init( WinTCShViewDesktopClass* klass ) { - s_desktop_map = g_hash_table_new(g_direct_hash, g_direct_equal); + S_DESKTOP_MAP = g_hash_table_new(g_direct_hash, g_direct_equal); + S_DESKTOP_ORDER = g_hash_table_new(g_direct_hash, g_direct_equal); // Assign GUID paths to built-in desktop items - kind of rubbish but // whatever // - s_desktop_items[0].priv = wintc_sh_path_for_guid(WINTC_SH_GUID_DRIVES); + S_DESKTOP_ITEMS[1].priv = wintc_sh_path_for_guid(WINTC_SH_GUID_DRIVES); // Assign hashes // - for (gulong i = 0; i < G_N_ELEMENTS(s_desktop_items); i++) + 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) + if (S_DESKTOP_ITEMS[i].priv) { - s_desktop_items[i].hash = g_str_hash(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); + S_DESKTOP_ITEMS[i].hash = g_str_hash(temp); g_free(temp); } g_hash_table_insert( - s_desktop_map, - GUINT_TO_POINTER(s_desktop_items[i].hash), - &(s_desktop_items[i]) + S_DESKTOP_MAP, + GUINT_TO_POINTER(S_DESKTOP_ITEMS[i].hash), + &(S_DESKTOP_ITEMS[i]) + ); + + // Map the item order (for compare_items) + // + g_hash_table_insert( + S_DESKTOP_ORDER, + GUINT_TO_POINTER(S_DESKTOP_ITEMS[i].hash), + GINT_TO_POINTER(i + 1) // Add 1 to simplify later, so NULL/0 means + // it's an FS item ); } @@ -191,13 +259,28 @@ static void wintc_sh_view_desktop_class_init( // GObjectClass* object_class = G_OBJECT_CLASS(klass); + object_class->constructed = wintc_sh_view_desktop_constructed; + object_class->dispose = wintc_sh_view_desktop_dispose; object_class->get_property = wintc_sh_view_desktop_get_property; + object_class->set_property = wintc_sh_view_desktop_set_property; g_object_class_override_property( object_class, PROP_ICON_NAME, "icon-name" ); + + g_object_class_install_property( + object_class, + PROP_SHEXT_HOST, + g_param_spec_object( + "shext-host", + "ShextHost", + "The shell extension host instance.", + WINTC_TYPE_SHEXT_HOST, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY + ) + ); } static void wintc_sh_view_desktop_init( @@ -228,6 +311,76 @@ static void wintc_sh_view_desktop_ishext_view_interface_init( // // CLASS VIRTUAL METHODS // +static void wintc_sh_view_desktop_constructed( + GObject* object +) +{ + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(object); + + (G_OBJECT_CLASS(wintc_sh_view_desktop_parent_class)) + ->constructed(object); + + // Create the backing desktop view + // + GError* error = NULL; + WinTCShextPathInfo path_info = { 0 }; + + path_info.base_path = + g_strdup_printf( + "file://%s", + g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP) + ); + + view_desk->view_user_desktop = + wintc_shext_host_get_view_for_path( + view_desk->shext_host, + &path_info, + &error + ); + + wintc_shext_path_info_free_data(&path_info); + + if (!(view_desk->view_user_desktop)) + { + wintc_log_error_and_clear(&error); + return; + } + + // Link up with desktop view + // + g_signal_connect( + view_desk->view_user_desktop, + "items-added", + G_CALLBACK(on_view_user_desktop_items_added), + view_desk + ); + g_signal_connect( + view_desk->view_user_desktop, + "items-removed", + G_CALLBACK(on_view_user_desktop_items_removed), + view_desk + ); + g_signal_connect( + view_desk->view_user_desktop, + "refreshing", + G_CALLBACK(on_view_user_desktop_refreshing), + view_desk + ); +} + +static void wintc_sh_view_desktop_dispose( + GObject* object +) +{ + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(object); + + g_clear_object(&(view_desk->shext_host)); + g_clear_object(&(view_desk->view_user_desktop)); + + (G_OBJECT_CLASS(wintc_sh_view_desktop_parent_class)) + ->dispose(object); +} + static void wintc_sh_view_desktop_get_property( GObject* object, guint prop_id, @@ -252,25 +405,58 @@ static void wintc_sh_view_desktop_get_property( } } +static void wintc_sh_view_desktop_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(object); + + switch (prop_id) + { + case PROP_SHEXT_HOST: + view_desk->shext_host = g_value_dup_object(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + // // INTERFACE METHODS (WinTCIShextView) // static gboolean wintc_sh_view_desktop_activate_item( - WINTC_UNUSED(WinTCIShextView* view), + WinTCIShextView* view, guint item_hash, WinTCShextPathInfo* path_info, GError** error ) { + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(view); + WINTC_SAFE_REF_CLEAR(error); WinTCShextViewItem* item = - (WinTCShextViewItem*) - g_hash_table_lookup( - s_desktop_map, - GUINT_TO_POINTER(item_hash) - ); + wintc_sh_view_desktop_get_view_item(view_desk, item_hash); + // If this isn't one of the desktop items, forward to the FS view + // + if (!item) + { + return wintc_ishext_view_activate_item( + view_desk->view_user_desktop, + item_hash, + path_info, + error + ); + } + + // This is ours, deal with it + // if (!(item->priv)) { g_critical("%s", "shell: desk view can't activate item, no target"); @@ -283,13 +469,30 @@ static gboolean wintc_sh_view_desktop_activate_item( } static gint wintc_sh_view_desktop_compare_items( - WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(guint item_hash1), - WINTC_UNUSED(guint item_hash2) + WinTCIShextView* view, + guint item_hash1, + guint item_hash2 ) { - // FIXME: Proper implementation - return -1; + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(view); + + gint order_item1 = wintc_sh_view_desktop_get_item_order(item_hash1); + gint order_item2 = wintc_sh_view_desktop_get_item_order(item_hash2); + + // If they're both FS items, then forward onto the fs view + // + if (order_item1 == order_item2 && order_item1 == K_ORDER_FS_ITEM) + { + return wintc_ishext_view_compare_items( + view_desk->view_user_desktop, + item_hash1, + item_hash2 + ); + } + + return + order_item1 < order_item2 ? -1 : + (order_item1 > order_item2 ? 1 : 0); } static const gchar* wintc_sh_view_desktop_get_display_name( @@ -308,27 +511,88 @@ static const gchar* wintc_sh_view_desktop_get_icon_name( } static GList* wintc_sh_view_desktop_get_items( - WINTC_UNUSED(WinTCIShextView* view) + WinTCIShextView* view ) { - return g_hash_table_get_values(s_desktop_map); + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(view); + + // Aggregate the lists + // + return wintc_list_append_list( + g_hash_table_get_values(S_DESKTOP_MAP), + wintc_ishext_view_get_items(view_desk->view_user_desktop) + ); } static GMenuModel* wintc_sh_view_desktop_get_operations_for_item( - WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(guint item_hash) + WinTCIShextView* view, + guint item_hash ) { - g_warning("%s Not Implemented", __func__); - return NULL; + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(view); + + // Check if this is one of ours + // + WinTCShextViewItem* item = + wintc_sh_view_desktop_get_view_item(view_desk, item_hash); + + if (item) + { + g_warning("%s Not Implemented", __func__); + return NULL; + } + + // Assume it's for the FS view + // + return wintc_ishext_view_get_operations_for_item( + view_desk->view_user_desktop, + item_hash + ); } static GMenuModel* wintc_sh_view_desktop_get_operations_for_view( WINTC_UNUSED(WinTCIShextView* view) ) { - g_warning("%s Not Implemented", __func__); - return NULL; + GtkBuilder* builder; + GMenuModel* menu; + + builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/shell/menudesk.ui" + ); + + wintc_lc_builder_preprocess_widget_text(builder); + + menu = + G_MENU_MODEL( + g_object_ref( + gtk_builder_get_object(builder, "menu") + ) + ); + + // Populate New submenu + // + GMenu* section_new = + G_MENU( + gtk_builder_get_object( + builder, + "section-new" + ) + ); + + GMenuModel* menu_new = wintc_sh_new_menu_get_menu(); + + g_menu_insert_submenu( + section_new, + 0, + "New", + menu_new + ); + + g_object_unref(builder); + + return menu; } static void wintc_sh_view_desktop_get_parent_path( @@ -365,45 +629,290 @@ static void wintc_sh_view_desktop_refresh_items( WinTCIShextView* view ) { + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(view); + WINTC_LOG_DEBUG("%s", "shell: refresh desktop view"); + // Refresh the user desktop view, this will in turn cause the desktop to + // refresh as well + // + wintc_ishext_view_refresh_items( + view_desk->view_user_desktop + ); +} + +static WinTCShextOperation* wintc_sh_view_desktop_spawn_operation( + WinTCIShextView* view, + gint operation_id, + GList* targets, + GError** error +) +{ + WinTCShViewDesktop* view_desk = WINTC_SH_VIEW_DESKTOP(view); + + // Spawn op + // + WinTCShextOperation* ret = NULL; + + if (targets) + { + targets = + wintc_sh_view_desktop_filter_op_targets( + view_desk, + targets + ); + + // Check the first item to determine whether it's one of our + // desktop items or to forward the whole lot to the FS view + // + if ( + wintc_sh_view_desktop_get_item_order( + GPOINTER_TO_UINT(targets->data) + ) == K_ORDER_FS_ITEM + ) + { + return wintc_ishext_view_spawn_operation( + view_desk->view_user_desktop, + operation_id, + targets, + error + ); + } + else + { + // FIXME: Implement this + // + g_critical( + "%s", + "shell: desktop - spawn op not implemented for item" + ); + } + + } + else + { + ret = g_new(WinTCShextOperation, 1); + + ret->view = view; + + switch (operation_id) + { + case WINTC_SHEXT_KNOWN_OP_PROPERTIES: + // FIXME: Handle for view items + // + ret->func = shopr_properties; + break; + + default: + g_clear_pointer(&ret, (GDestroyNotify) g_free); + + if (WINTC_SHEXT_OP_IS_NEW_OP(operation_id)) + { + return wintc_ishext_view_spawn_operation( + view_desk->view_user_desktop, + operation_id, + NULL, + error + ); + } + + g_critical("%s", "shell: desktop - invalid op"); + + break; + } + } + + g_clear_list(&targets, NULL); + + return ret; +} + +// +// PUBLIC FUNCTIONS +// +WinTCIShextView* wintc_sh_view_desktop_new( + WinTCShextHost* shext_host +) +{ + return WINTC_ISHEXT_VIEW( + g_object_new( + WINTC_TYPE_SH_VIEW_DESKTOP, + "shext-host", shext_host, + NULL + ) + ); +} + +// +// PRIVATE FUNCTIONS +// +static gint wintc_sh_view_desktop_get_item_order( + guint item_hash +) +{ + gint order = + GPOINTER_TO_INT( + g_hash_table_lookup( + S_DESKTOP_ORDER, + GUINT_TO_POINTER(item_hash) + ) + ); + + if (!order) + { + return K_ORDER_FS_ITEM; + } + + return order; +} + +static WinTCShextViewItem* wintc_sh_view_desktop_get_view_item( + WINTC_UNUSED(WinTCShViewDesktop* view_desk), + guint item_hash +) +{ + return + (WinTCShextViewItem*) + g_hash_table_lookup( + S_DESKTOP_MAP, + GUINT_TO_POINTER(item_hash) + ); +} + +static GList* wintc_sh_view_desktop_filter_op_targets( + WINTC_UNUSED(WinTCShViewDesktop* view_desk), + GList* targets +) +{ + if (!targets) + { + return NULL; + } + + // + // Targets must be filtered in case there's a mix of desktop items and FS + // view items + // + // This is based on the first item in the view targets list, which SHOULD + // be the one the user right clicked on in the first place + // + + // We can make this easy by using the existing 'get_item_order' function + // because it determines whether the item is ours or not + // + gint order = + wintc_sh_view_desktop_get_item_order( + GPOINTER_TO_UINT(targets->data) + ); + + if (order == K_ORDER_FS_ITEM) + { + // Ensure all other items are FS items + // + GList* iter = targets; + GList* next = NULL; + + while (iter) + { + order = + wintc_sh_view_desktop_get_item_order( + GPOINTER_TO_UINT(iter->data) + ); + + next = iter->next; + + if (order != K_ORDER_FS_ITEM) + { + targets = g_list_delete_link(targets, iter); + } + + iter = next; + } + } + else + { + // Delete everything other than the first item + // + GList* remaining = g_list_remove_link(targets, targets); + + g_list_free(remaining); + } + + return targets; +} + +static void wintc_sh_view_desktop_real_refresh_items( + WinTCShViewDesktop* view_desk +) +{ + WinTCIShextView* view = WINTC_ISHEXT_VIEW(view_desk); + _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); + GList* items = g_hash_table_get_values(S_DESKTOP_MAP); update.data = items; - update.done = TRUE; + update.done = FALSE; // The FS view will always follow behind _wintc_ishext_view_items_added(view, &update); g_list_free(items); } -static WinTCShextOperation* wintc_sh_view_desktop_spawn_operation( +// +// CALLBACKS +// +static gboolean shopr_properties( WINTC_UNUSED(WinTCIShextView* view), - WINTC_UNUSED(gint operation_id), - WINTC_UNUSED(GList* targets), - WINTC_UNUSED(GError** error) + WINTC_UNUSED(WinTCShextOperation* operation), + WINTC_UNUSED(GtkWindow* wnd), + GError** error ) { - g_critical("Not implemented %s", __func__); - return NULL; + // FIXME: Clash with WINE + return wintc_launch_command("desk.cpl", error); } -// -// PUBLIC FUNCTIONS -// -WinTCIShextView* wintc_sh_view_desktop_new(void) +static void on_view_user_desktop_items_added( + WINTC_UNUSED(WinTCIShextView* view), + WinTCShextViewItemsUpdate* update, + gpointer user_data +) { - return WINTC_ISHEXT_VIEW( - g_object_new( - WINTC_TYPE_SH_VIEW_DESKTOP, - NULL - ) + // Just forward + // + _wintc_ishext_view_items_added( + WINTC_ISHEXT_VIEW(user_data), + update + ); +} + +static void on_view_user_desktop_items_removed( + WINTC_UNUSED(WinTCIShextView* view), + WinTCShextViewItemsUpdate* update, + gpointer user_data +) +{ + // Just forward + // + _wintc_ishext_view_items_removed( + WINTC_ISHEXT_VIEW(user_data), + update + ); +} + +static void on_view_user_desktop_refreshing( + WINTC_UNUSED(WinTCIShextView* view), + gpointer user_data +) +{ + // Only worry about refreshing us + // + wintc_sh_view_desktop_real_refresh_items( + WINTC_SH_VIEW_DESKTOP(user_data) ); } diff --git a/shared/shell/src/vwfs.c b/shared/shell/src/vwfs.c index dd56171..c81d986 100644 --- a/shared/shell/src/vwfs.c +++ b/shared/shell/src/vwfs.c @@ -1,15 +1,13 @@ #include #include -#include -#include #include #include -#include #include #include #include "../public/fsclipbd.h" #include "../public/fsop.h" +#include "../public/newmenu.h" #include "../public/vwfs.h" // @@ -22,21 +20,6 @@ enum PROP_ICON_NAME }; -enum -{ - WINTC_SH_VIEW_FS_OP_NEW_FOLDER = 80, - WINTC_SH_VIEW_FS_OP_NEW_SHORTCUT = 100 -}; - -// -// PRIVATE STRUCTURES -// -typedef struct _WinTCShViewFSNewTemplate -{ - gchar* filename; - gchar* name; -} WinTCShViewFSNewTemplate; - // // FORWARD DECLARATIONS // @@ -114,9 +97,6 @@ static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( GError** error ); -static void clear_new_template( - WinTCShViewFSNewTemplate* template -); static void clear_view_item( WinTCShextViewItem* item ); @@ -143,9 +123,6 @@ static WinTCShextViewItem* wintc_sh_view_fs_get_view_item( WinTCShViewFS* view_fs, guint item_hash ); -static void wintc_sh_view_fs_update_new_templates( - WinTCShViewFS* view_fs -); static gboolean shopr_delete( WinTCIShextView* view, @@ -208,7 +185,6 @@ struct _WinTCShViewFS WinTCShFSClipboard* fs_clipboard; WinTCShextHost* shext_host; - GList* list_new_templates; guint next_new_hash; // For flagging a view item we just made as new }; @@ -306,11 +282,6 @@ static void wintc_sh_view_fs_dispose( g_clear_object(&(view_fs->fs_monitor)); g_clear_object(&(view_fs->shext_host)); - g_clear_list( - &(view_fs->list_new_templates), - (GDestroyNotify) clear_new_template - ); - (G_OBJECT_CLASS(wintc_sh_view_fs_parent_class))->dispose(object); } @@ -556,11 +527,9 @@ static GMenuModel* wintc_sh_view_fs_get_operations_for_item( } static GMenuModel* wintc_sh_view_fs_get_operations_for_view( - WinTCIShextView* view + WINTC_UNUSED(WinTCIShextView* view) ) { - WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); - GtkBuilder* builder; GMenuModel* menu; @@ -584,51 +553,18 @@ static GMenuModel* wintc_sh_view_fs_get_operations_for_view( G_MENU( gtk_builder_get_object( builder, - "section-new-templates" + "section-new" ) ); - gint view_op_id = WINTC_SHEXT_OP_NEW + 1; + GMenuModel* menu_new = wintc_sh_new_menu_get_menu(); - wintc_sh_view_fs_update_new_templates(view_fs); - - for (GList* iter = view_fs->list_new_templates; iter; iter = iter->next) - { - WinTCShViewFSNewTemplate* template = - (WinTCShViewFSNewTemplate*) iter->data; - - // Cap off menu items - // - if (!WINTC_SHEXT_OP_IS_NEW_OP(view_op_id)) - { - break; - } - - // Create the menu item - // - GIcon* icon = g_themed_icon_new(template->filename); - GMenuItem* menu_item = g_menu_item_new(NULL, NULL); - - g_menu_item_set_label(menu_item, template->name); - g_menu_item_set_icon(menu_item, icon); - - g_menu_item_set_action_and_target( - menu_item, - "control.view-op", - "i", - view_op_id - ); - - g_menu_append_item( - section_new_templates, - menu_item - ); - - view_op_id++; - - g_object_unref(icon); - g_object_unref(menu_item); - } + g_menu_insert_submenu( + section_new_templates, + 0, + "New", + menu_new + ); g_object_unref(builder); @@ -808,7 +744,7 @@ static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( WinTCIShextView* view, gint operation_id, GList* targets, - WINTC_UNUSED(GError** error) + WINTC_UNUSED(GError** error) ) { WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); @@ -817,6 +753,8 @@ static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( // WinTCShextOperation* ret = g_new(WinTCShextOperation, 1); + ret->view = view; + switch (operation_id) { case WINTC_SHEXT_KNOWN_OP_OPEN: @@ -846,7 +784,7 @@ static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( } else { - g_free(g_steal_pointer(&ret)); + g_clear_pointer(&ret, g_free); g_critical("%s", "shell: fs - invalid op"); } @@ -879,15 +817,6 @@ WinTCIShextView* wintc_sh_view_fs_new( // // PRIVATE FUNCTIONS // -static void clear_new_template( - WinTCShViewFSNewTemplate* template -) -{ - g_free(template->filename); - g_free(template->name); - g_free(template); -} - static void clear_view_item( WinTCShextViewItem* item ) @@ -1078,144 +1007,6 @@ static WinTCShextViewItem* wintc_sh_view_fs_get_view_item( ); } -static void wintc_sh_view_fs_update_new_templates( - WinTCShViewFS* view_fs -) -{ - GList* templates = - wintc_sh_fs_get_names_as_list( - WINTC_ASSETS_DIR G_DIR_SEPARATOR_S "templates", - FALSE, - G_FILE_TEST_IS_REGULAR, - FALSE, - NULL - ); - - g_clear_list( - &(view_fs->list_new_templates), - (GDestroyNotify) clear_new_template - ); - - for (GList* iter = templates; iter; iter = iter->next) - { - const gchar* filename = (gchar*) iter->data; - - // Pull out the MIME type parts from filename - // - const gchar* p_mime_base_end = strstr(filename, "-"); - - if (!p_mime_base_end) - { - g_warning( - "shell: fs - doesn't look like valid MIME: %s", - filename - ); - - continue; - } - - // Build the file path... - // - gchar* mime_base = wintc_substr(filename, p_mime_base_end); - gchar* mime_spec = wintc_substr(p_mime_base_end + 1, NULL); - gchar* mime_specfn = g_strdup_printf("%s.xml", mime_spec); - - gchar* mime_path = - g_build_path( - G_DIR_SEPARATOR_S, - G_DIR_SEPARATOR_S, - "usr", -#ifdef WINTC_PKGMGR_BSDPKG - "local", -#endif - "share", - "mime", - mime_base, - mime_specfn, - NULL - ); - - // Attempt to parse the MIME XML - // - xmlDocPtr xml_mime = xmlParseFile(mime_path); - - g_free(mime_base); - g_free(mime_spec); - g_free(mime_specfn); - g_free(mime_path); - - if (!xml_mime) - { - WINTC_LOG_DEBUG( - "shell: fs - could not get XML for %s", - filename - ); - - continue; - } - - // Retrieve the name - // - xmlNodePtr xml_root = xmlDocGetRootElement(xml_mime); - - for (xmlNodePtr node = xml_root->children; node; node = node->next) - { - // We're looking for - // - xmlChar* node_lang; - - if (node->type != XML_ELEMENT_NODE) - { - continue; - } - - if (g_strcmp0((gchar*) node->name, "comment") != 0) - { - continue; - } - - if ((node_lang = xmlNodeGetLang(node))) - { - xmlFree(node_lang); - continue; - } - - // Create the new template item - // - WinTCShViewFSNewTemplate* template = - g_new(WinTCShViewFSNewTemplate, 1); - - xmlChar* mime_name = xmlNodeGetContent(node); - - template->filename = g_strdup(filename); - template->name = g_strdup((gchar*) mime_name); - - xmlFree(mime_name); - - view_fs->list_new_templates = - g_list_prepend( - view_fs->list_new_templates, - template - ); - - break; - } - - xmlFreeDoc(xml_mime); - } - - if (view_fs->list_new_templates) - { - view_fs->list_new_templates = - g_list_reverse(view_fs->list_new_templates); - } - - g_list_free_full( - templates, - (GDestroyNotify) g_free - ); -} - // // CALLBACKS // @@ -1252,128 +1043,14 @@ static gboolean shopr_new( GError** error ) { - // - // FIXME: Localisation needed in the names this func uses - // - WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); - // Folder creation is ID 80, above that and we're dealing with a MIME - // template - // - gint op = GPOINTER_TO_INT(operation->priv); - gboolean is_folder = op == WINTC_SH_VIEW_FS_OP_NEW_FOLDER; - - GFile* file; - guint hash; - gchar* path; - GError* local_error = NULL; - gchar* name; - const gchar* name_type; - gboolean success; - WinTCShViewFSNewTemplate* template; - - if (is_folder) - { - name_type = "Folder"; - } - else - { - template = - (WinTCShViewFSNewTemplate*) - g_list_nth_data( - view_fs->list_new_templates, - op - WINTC_SH_VIEW_FS_OP_NEW_FOLDER - 1 - ); - - name_type = template->name; - } - - for (gint attempt = 0; attempt < 100; attempt++) - { - if (attempt) - { - name = - g_strdup_printf( - "New %s (%d)", - name_type, - attempt - ); - } - else - { - name = - g_strdup_printf( - "New %s", - name_type - ); - } - - path = g_build_path(G_DIR_SEPARATOR_S, view_fs->path, name, NULL); - hash = g_str_hash(path); - file = g_file_new_for_path(path); - - if (is_folder) - { - success = - g_file_make_directory( - file, - NULL, - &local_error - ); - } - else - { - gchar* template_path = g_build_path( - G_DIR_SEPARATOR_S, - WINTC_ASSETS_DIR, - "templates", - template->filename, - NULL - ); - GFile* template_file = g_file_new_for_path(template_path); - - success = - g_file_copy ( - template_file, - file, - G_FILE_COPY_NONE, - NULL, - NULL, - NULL, - &local_error - ); - - g_free(template_path); - g_object_unref(template_file); - } - - g_free(path); - g_free(name); - g_object_unref(file); - - if (success) - { - view_fs->next_new_hash = hash; - return TRUE; - } - else - { - if (local_error->code == G_IO_ERROR_EXISTS) - { - g_clear_error(&local_error); - continue; - } - else - { - g_propagate_error(error, local_error); - return FALSE; - } - } - } - - // FIXME: Set error here - return FALSE; + return wintc_sh_new_menu_create_file( + view_fs->path, + GPOINTER_TO_INT(operation->priv), + &(view_fs->next_new_hash), + error + ); } static gboolean shopr_open( diff --git a/shared/shellext/public/viewops.h b/shared/shellext/public/viewops.h index 6d36922..550ee21 100644 --- a/shared/shellext/public/viewops.h +++ b/shared/shellext/public/viewops.h @@ -61,6 +61,7 @@ typedef gboolean (*WinTCShextOperationFunc) ( struct _WinTCShextOperation { WinTCShextOperationFunc func; + WinTCIShextView* view; gpointer priv; }; diff --git a/shared/shellext/src/if_view.c b/shared/shellext/src/if_view.c index b76fa75..91e3499 100644 --- a/shared/shellext/src/if_view.c +++ b/shared/shellext/src/if_view.c @@ -371,7 +371,7 @@ void wintc_shext_path_info_demangle_uri( // if (!path_info->base_path) { - path_info->base_path = g_strdup(uri); + path_info->base_path = g_uri_unescape_string(uri, NULL); } } diff --git a/shell/cpl/desk/src/application.c b/shell/cpl/desk/src/application.c index bea1775..e21015b 100644 --- a/shell/cpl/desk/src/application.c +++ b/shell/cpl/desk/src/application.c @@ -64,10 +64,18 @@ static void wintc_cpl_desk_application_activate( GApplication* application ) { - GtkWidget* new_window = - wintc_cpl_desk_window_new(WINTC_CPL_DESK_APPLICATION(application)); + static GtkWidget* wnd = NULL; - gtk_widget_show_all(new_window); + if (!wnd) + { + wnd = + wintc_cpl_desk_window_new( + WINTC_CPL_DESK_APPLICATION(application) + ); + } + + gtk_widget_show_all(wnd); + gtk_window_present(GTK_WINDOW(wnd)); } static void wintc_cpl_desk_application_startup(