Enhancement: Fixes #555, shell - desktop FS view

This commit is contained in:
Rory Fewell
2026-01-18 22:10:46 +00:00
parent a675dcfabc
commit d2edd38e30
19 changed files with 1255 additions and 527 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
#ifndef __SHELL_NEWMENU_H__
#define __SHELL_NEWMENU_H__
#include <glib.h>
//
// 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

View File

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

View File

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

417
shared/shell/src/newmenu.c Normal file
View File

@@ -0,0 +1,417 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <wintc/comgtk.h>
#include <wintc/shcommon.h>
#include <wintc/shellext.h>
#include <wintc/shlang.h>
#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 <comment>
//
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
);
}

View File

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

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="menu">
<section>
<item>
<attribute name="action">control.no-op</attribute>
<attribute name="label">Paste</attribute>
</item>
<item>
<attribute name="action">control.no-op</attribute>
<attribute name="label">Paste Shortcut</attribute>
</item>
</section>
<section id="section-new" />
<section>
<item>
<attribute name="action">control.view-op</attribute>
<attribute name="label">Properties</attribute>
<attribute name="target" type="i">14</attribute>
</item>
</section>
</menu>
</interface>

View File

@@ -19,29 +19,7 @@
<attribute name="target" type="i">7</attribute>
</item>
</section>
<section>
<submenu>
<attribute name="label">New</attribute>
<section>
<item>
<attribute name="action">control.view-op</attribute>
<attribute name="label">Folder</attribute>
<attribute name="icon">inode-directory</attribute>
<attribute name="target" type="i">80</attribute>
</item>
<item>
<attribute name="action">control.view-op</attribute>
<attribute name="label">Shortcut</attribute>
<attribute name="icon">emblem-symbolic-link</attribute>
<attribute name="target" type="i">100</attribute>
</item>
</section>
<section id="section-new-templates">
</section>
</submenu>
</section>
<section id="section-new" />
<section>
<item>
<attribute name="action">control.no-op</attribute>

View File

@@ -0,0 +1,21 @@
<interface>
<menu id="menu-model">
<section>
<item>
<attribute name="action">control.view-op</attribute>
<attribute name="label">Folder</attribute>
<attribute name="icon">inode-directory</attribute>
<attribute name="target" type="i">80</attribute>
</item>
<item>
<attribute name="action">control.view-op</attribute>
<attribute name="label">Shortcut</attribute>
<attribute name="icon">emblem-symbolic-link</attribute>
<attribute name="target" type="i">81</attribute>
</item>
</section>
<section id="section-new-templates">
</section>
</menu>
</interface>

View File

@@ -7,8 +7,10 @@
<!-- MENUS -->
<file>menuctx.ui</file>
<file>menudesk.ui</file>
<file>menufs.ui</file>
<file>menufslf.ui</file>
<file>menufsvw.ui</file>
<file>menunew.ui</file>
</gresource>
</gresources>

View File

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

View File

@@ -1,62 +1,22 @@
#include <glib.h>
#include <wintc/comgtk.h>
#include <wintc/exec.h>
#include <wintc/shcommon.h>
#include <wintc/shellext.h>
#include <wintc/shlang.h>
#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)
);
}

View File

@@ -1,15 +1,13 @@
#include <gio/gio.h>
#include <glib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <wintc/comgtk.h>
#include <wintc/exec.h>
#include <wintc/shcommon.h>
#include <wintc/shellext.h>
#include <wintc/shlang.h>
#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 <comment>
//
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(

View File

@@ -61,6 +61,7 @@ typedef gboolean (*WinTCShextOperationFunc) (
struct _WinTCShextOperation
{
WinTCShextOperationFunc func;
WinTCIShextView* view;
gpointer priv;
};

View File

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

View File

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