diff --git a/base/logonui/src/welcome/ui.c b/base/logonui/src/welcome/ui.c index 064d5a7..1a5b0c7 100644 --- a/base/logonui/src/welcome/ui.c +++ b/base/logonui/src/welcome/ui.c @@ -117,7 +117,7 @@ static void wintc_welcome_ui_remove( static void wintc_welcome_ui_change_state( WinTCWelcomeUI* welcome_ui, - WinTCGinaState next_state + WinTCGinaState next_state ); static void wintc_welcome_ui_internal_add( WinTCWelcomeUI* welcome_ui, @@ -811,7 +811,10 @@ static gboolean on_timeout_delay_done( ) ) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear( + &error, + wintc_widget_get_toplevel_window(GTK_WIDGET(welcome_ui)) + ); wintc_welcome_ui_change_state( welcome_ui, diff --git a/shared/comgtk/CMakeLists.txt b/shared/comgtk/CMakeLists.txt index e415e3c..33c4ad8 100644 --- a/shared/comgtk/CMakeLists.txt +++ b/shared/comgtk/CMakeLists.txt @@ -41,10 +41,14 @@ add_library( src/builder.c public/builder.h public/debug.h + src/clipbd.c + public/clipbd.h src/container.c public/container.h src/defprocs.c public/defprocs.h + src/delegate.c + public/delegate.h src/errors.c public/errors.h src/icons.c diff --git a/shared/comgtk/public/clipbd.h b/shared/comgtk/public/clipbd.h new file mode 100644 index 0000000..387fdb8 --- /dev/null +++ b/shared/comgtk/public/clipbd.h @@ -0,0 +1,22 @@ +/** @file */ + +#ifndef __COMGTK_CLIPBD_H__ +#define __COMGTK_CLIPBD_H__ + +#include + +// +// PUBLIC FUNCTIONS +// + +/** + * Clears the contents of the clipboard regardless of whether the clipboard + * is owned by someone else or not. + * + * @param clipboard The clipboard. + */ +void wintc_clipboard_true_clear( + GtkClipboard* clipboard +); + +#endif diff --git a/shared/comgtk/public/delegate.h b/shared/comgtk/public/delegate.h new file mode 100644 index 0000000..2ea0dcf --- /dev/null +++ b/shared/comgtk/public/delegate.h @@ -0,0 +1,20 @@ +/** @file */ + +#ifndef __COMGTK_DELEGATE_H__ +#define __COMGTK_DELEGATE_H__ + +#include + +// +// PUBLIC FUNCTIONS +// + +/** + * GCopyFunc stub for g_strdup. + */ +gpointer wintc_copyfunc_strdup( + gconstpointer src, + gpointer user_data +); + +#endif diff --git a/shared/comgtk/public/errors.h b/shared/comgtk/public/errors.h index c0d55a1..294d753 100644 --- a/shared/comgtk/public/errors.h +++ b/shared/comgtk/public/errors.h @@ -4,6 +4,7 @@ #define __COMGTK_ERRORS_H__ #include +#include #define WINTC_GENERAL_ERROR wintc_general_error_quark() @@ -28,9 +29,11 @@ typedef enum * GError object. * * @param error A reference to the GError storage location. + * @param wnd The window to own the dialog, if applicable. */ void wintc_display_error_and_clear( - GError** error + GError** error, + GtkWindow* wnd ); GQuark wintc_general_error_quark(void); @@ -50,9 +53,11 @@ void wintc_log_error_and_clear( * clears the associated GError object. * * @param error A reference to the GError storage location. + * @param wnd The window to own the dialog, if applicable. */ void wintc_nice_error_and_clear( - GError** error + GError** error, + GtkWindow* wnd ); #endif diff --git a/shared/comgtk/public/libapi.h.in b/shared/comgtk/public/libapi.h.in index 93820d1..215c3cd 100644 --- a/shared/comgtk/public/libapi.h.in +++ b/shared/comgtk/public/libapi.h.in @@ -5,9 +5,11 @@ #include "@LIB_HEADER_DIR@/application.h" #include "@LIB_HEADER_DIR@/assets.h" #include "@LIB_HEADER_DIR@/builder.h" +#include "@LIB_HEADER_DIR@/clipbd.h" #include "@LIB_HEADER_DIR@/container.h" #include "@LIB_HEADER_DIR@/debug.h" #include "@LIB_HEADER_DIR@/defprocs.h" +#include "@LIB_HEADER_DIR@/delegate.h" #include "@LIB_HEADER_DIR@/errors.h" #include "@LIB_HEADER_DIR@/icons.h" #include "@LIB_HEADER_DIR@/list.h" diff --git a/shared/comgtk/public/window.h b/shared/comgtk/public/window.h index 36e68fd..c836a07 100644 --- a/shared/comgtk/public/window.h +++ b/shared/comgtk/public/window.h @@ -18,4 +18,15 @@ void wintc_focus_window( GtkWindow* window ); +/** + * Gets the top-level window that owns the widget, if there is no window, then + * NULL will be returned. + * + * @param widget The widget. + * @return The top-level window that owns the widget or NULL. + */ +GtkWindow* wintc_widget_get_toplevel_window( + GtkWidget* widget +); + #endif diff --git a/shared/comgtk/src/clipbd.c b/shared/comgtk/src/clipbd.c new file mode 100644 index 0000000..8082bb7 --- /dev/null +++ b/shared/comgtk/src/clipbd.c @@ -0,0 +1,17 @@ +#include + +#include "../public/clipbd.h" + +// +// PUBLIC FUNCTIONS +// +void wintc_clipboard_true_clear( + GtkClipboard* clipboard +) +{ + gtk_clipboard_clear(clipboard); + + // If we don't own the clipboard, then replace it with nothing + // + gtk_clipboard_set_text(clipboard, "", 0); +} diff --git a/shared/comgtk/src/delegate.c b/shared/comgtk/src/delegate.c new file mode 100644 index 0000000..db7e3c1 --- /dev/null +++ b/shared/comgtk/src/delegate.c @@ -0,0 +1,15 @@ +#include + +#include "../public/delegate.h" +#include "../public/shorthand.h" + +// +// PUBLIC FUNCTIONS +// +gpointer wintc_copyfunc_strdup( + gconstpointer src, + WINTC_UNUSED(gpointer user_data) +) +{ + return g_strdup((const gchar*) src); +} diff --git a/shared/comgtk/src/errors.c b/shared/comgtk/src/errors.c index a10f8fa..4e4e0fa 100644 --- a/shared/comgtk/src/errors.c +++ b/shared/comgtk/src/errors.c @@ -15,13 +15,14 @@ G_DEFINE_QUARK(wintc-general-error-quark, wintc_general_error) // PUBLIC FUNCTIONS // void wintc_display_error_and_clear( - GError** error + GError** error, + GtkWindow* wnd ) { RETURN_IF_NO_ERROR(error) wintc_messagebox_show( - NULL, + wnd, (*error)->message, "", GTK_BUTTONS_OK, @@ -43,7 +44,8 @@ void wintc_log_error_and_clear( } void wintc_nice_error_and_clear( - GError** error + GError** error, + GtkWindow* wnd ) { RETURN_IF_NO_ERROR(error) @@ -63,7 +65,7 @@ void wintc_nice_error_and_clear( if (message != NULL) { wintc_messagebox_show( - NULL, + wnd, message, "", GTK_BUTTONS_OK, @@ -74,6 +76,6 @@ void wintc_nice_error_and_clear( } else { - wintc_display_error_and_clear(error); + wintc_display_error_and_clear(error, wnd); } } diff --git a/shared/comgtk/src/window.c b/shared/comgtk/src/window.c index 1f16141..7ba8a8c 100644 --- a/shared/comgtk/src/window.c +++ b/shared/comgtk/src/window.c @@ -15,3 +15,17 @@ void wintc_focus_window( GDK_CURRENT_TIME ); } + +GtkWindow* wintc_widget_get_toplevel_window( + GtkWidget* widget +) +{ + GtkWidget* toplevel = gtk_widget_get_toplevel(widget); + + if (GTK_IS_WINDOW(toplevel)) + { + return GTK_WINDOW(toplevel); + } + + return NULL; +} diff --git a/shared/msgina/src/authwnd.c b/shared/msgina/src/authwnd.c index 970de6e..68a86b3 100644 --- a/shared/msgina/src/authwnd.c +++ b/shared/msgina/src/authwnd.c @@ -140,8 +140,8 @@ static void wintc_gina_auth_window_init( &err_bannerx ); - wintc_display_error_and_clear(&err_banner); - wintc_display_error_and_clear(&err_bannerx); + wintc_display_error_and_clear(&err_banner, NULL); + wintc_display_error_and_clear(&err_bannerx, NULL); // Set up branding box // @@ -594,7 +594,7 @@ static gboolean on_timeout_delay_done( ) ) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, GTK_WINDOW(window)); wintc_gina_auth_window_change_state( window, diff --git a/shared/shell/CMakeLists.txt b/shared/shell/CMakeLists.txt index ecedecc..07f8d2c 100644 --- a/shared/shell/CMakeLists.txt +++ b/shared/shell/CMakeLists.txt @@ -44,6 +44,10 @@ add_library( public/dialog.h src/error.c public/error.h + src/fsclipbd.c + public/fsclipbd.h + src/fsop.c + public/fsop.h src/icnvwbeh.c public/icnvwbeh.h src/resources.c diff --git a/shared/shell/public/error.h b/shared/shell/public/error.h index dde806a..bb5827d 100644 --- a/shared/shell/public/error.h +++ b/shared/shell/public/error.h @@ -7,7 +7,8 @@ typedef enum { - WINTC_SHELL_ERROR_PLACEHOLDER // TEMP + WINTC_SHELL_ERROR_CLIPBOARD_EMPTY, + WINTC_SHELL_ERROR_CLIPBOARD_GET_FAILED } WinTCShellError; GQuark wintc_shell_error_quark(void); diff --git a/shared/shell/public/fsclipbd.h b/shared/shell/public/fsclipbd.h new file mode 100644 index 0000000..0116968 --- /dev/null +++ b/shared/shell/public/fsclipbd.h @@ -0,0 +1,31 @@ +#ifndef __SHELL_FSCLIPBD_H__ +#define __SHELL_FSCLIPBD_H__ + +#include + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_SH_FS_CLIPBOARD (wintc_sh_fs_clipboard_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCShFSClipboard, + wintc_sh_fs_clipboard, + WINTC, + SH_FS_CLIPBOARD, + GObject +) + +// +// PUBLIC FUNCTIONS +// +WinTCShFSClipboard* wintc_sh_fs_clipboard_new(void); + +gboolean wintc_sh_fs_clipboard_paste( + WinTCShFSClipboard* fs_clipboard, + const gchar* dest, + GtkWindow* wnd, + GError** error +); + +#endif diff --git a/shared/shell/public/fsop.h b/shared/shell/public/fsop.h new file mode 100644 index 0000000..d4f0150 --- /dev/null +++ b/shared/shell/public/fsop.h @@ -0,0 +1,45 @@ +#ifndef __SHELL_FSOP_H__ +#define __SHELL_FSOP_H__ + +#include +#include + +// +// PUBLIC ENUMS +// +typedef enum +{ + WINTC_SH_FS_OPERATION_INVALID, + WINTC_SH_FS_OPERATION_COPY, + WINTC_SH_FS_OPERATION_MOVE, + WINTC_SH_FS_OPERATION_TRASH +} WinTCShFSOperationKind; + +// +// GTK OOP BOILERPLATE +// +#define WINTC_TYPE_SH_FS_OPERATION (wintc_sh_fs_operation_get_type()) + +G_DECLARE_FINAL_TYPE( + WinTCShFSOperation, + wintc_sh_fs_operation, + WINTC, + SH_FS_OPERATION, + GObject +) + +// +// PUBLIC FUNCTIONS +// +WinTCShFSOperation* wintc_sh_fs_operation_new( + GList* files, + const gchar* dest, + WinTCShFSOperationKind operation_kind +); + +void wintc_sh_fs_operation_do( + WinTCShFSOperation* fs_operation, + GtkWindow* wnd +); + +#endif diff --git a/shared/shell/public/libapi.h.in b/shared/shell/public/libapi.h.in index c893f9e..765c9c2 100644 --- a/shared/shell/public/libapi.h.in +++ b/shared/shell/public/libapi.h.in @@ -5,6 +5,7 @@ #include "@LIB_HEADER_DIR@/cpl.h" #include "@LIB_HEADER_DIR@/dialog.h" #include "@LIB_HEADER_DIR@/error.h" +#include "@LIB_HEADER_DIR@/fsclipbd.h" #include "@LIB_HEADER_DIR@/icnvwbeh.h" #include "@LIB_HEADER_DIR@/nmspace.h" #include "@LIB_HEADER_DIR@/trevwbeh.h" diff --git a/shared/shell/src/fsclipbd.c b/shared/shell/src/fsclipbd.c new file mode 100644 index 0000000..ab2f883 --- /dev/null +++ b/shared/shell/src/fsclipbd.c @@ -0,0 +1,440 @@ +#include +#include +#include +#include + +#include "../public/error.h" +#include "../public/fsclipbd.h" +#include "../public/fsop.h" + +// +// PRIVATE ENUMS +// +enum +{ + PROP_CAN_PASTE = 1 +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_sh_fs_clipboard_constructed( + GObject* object +); +static void wintc_sh_fs_clipboard_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +); + +static void wintc_sh_fs_clipboard_update_state( + WinTCShFSClipboard* fs_clipboard +); + +static void cb_clipboard_contents_received( + GtkClipboard* clipboard, + GtkSelectionData* selection_data, + gpointer user_data +); +static void cb_clipboard_targets_received( + GtkClipboard* clipboard, + GdkAtom* atoms, + gint n_atoms, + gpointer user_data +); + +static void on_clipboard_owner_change( + GtkClipboard* self, + GdkEventOwnerChange* event, + gpointer user_data +); +static void on_fs_operation_done( + WinTCShFSOperation* self, + gpointer user_data +); + +// +// STATIC DATA +// +static GdkAtom S_ATOM_TEXT_URI_LIST; +static GdkAtom S_ATOM_X_SPECIAL_GNOME_COPIED_FILES; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +typedef struct _WinTCShFSClipboard +{ + GObject __parent__; + + GtkClipboard* clipboard; + GList* list_uris; + WinTCShFSOperationKind operation_kind; + GdkAtom preferred_target; +} WinTCShFSClipboard; + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCShFSClipboard, + wintc_sh_fs_clipboard, + G_TYPE_OBJECT +) + +static void wintc_sh_fs_clipboard_class_init( + WinTCShFSClipboardClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = wintc_sh_fs_clipboard_constructed; + object_class->get_property = wintc_sh_fs_clipboard_get_property; + + g_object_class_install_property( + object_class, + PROP_CAN_PASTE, + g_param_spec_boolean( + "can-paste", + "CanPaste", + "Determines whether the clipboard has pastable content.", + FALSE, + G_PARAM_READABLE + ) + ); + + S_ATOM_TEXT_URI_LIST = + gdk_atom_intern_static_string("text/uri-list"); + S_ATOM_X_SPECIAL_GNOME_COPIED_FILES = + gdk_atom_intern_static_string("x-special/gnome-copied-files"); +} + +static void wintc_sh_fs_clipboard_init( + WinTCShFSClipboard* self +) +{ + WINTC_LOG_DEBUG("shell: fs clipbd - creating clipboard"); + + self->clipboard = + gtk_clipboard_get_default(gdk_display_get_default()); + + g_signal_connect( + self->clipboard, + "owner-change", + G_CALLBACK(on_clipboard_owner_change), + self + ); +} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_sh_fs_clipboard_constructed( + GObject* object +) +{ + WinTCShFSClipboard* fs_clipboard = WINTC_SH_FS_CLIPBOARD(object); + + wintc_sh_fs_clipboard_update_state(fs_clipboard); + + (G_OBJECT_CLASS(wintc_sh_fs_clipboard_parent_class)) + ->constructed(object); +} + +static void wintc_sh_fs_clipboard_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +) +{ + WinTCShFSClipboard* fs_clipboard = WINTC_SH_FS_CLIPBOARD(object); + + switch (prop_id) + { + case PROP_CAN_PASTE: + g_value_set_boolean( + value, + !!(fs_clipboard->list_uris) + ); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +// +// PUBLIC FUNCTIONS +// +WinTCShFSClipboard* wintc_sh_fs_clipboard_new(void) +{ + // We are using a singleton instance here, but as far as programs are + // concerned it's an ordinary object they own a reference to + // + // This gives some flexibility if any changes are needed in future, but + // since the clipboard is a shared resource it makes sense to only hold + // one instance + // + static WinTCShFSClipboard* singleton_clipboard = NULL; + + if (!singleton_clipboard) + { + singleton_clipboard = + WINTC_SH_FS_CLIPBOARD( + g_object_new( + WINTC_TYPE_SH_FS_CLIPBOARD, + NULL + ) + ); + } + + g_object_ref(singleton_clipboard); + + return singleton_clipboard; +} + +gboolean wintc_sh_fs_clipboard_paste( + WinTCShFSClipboard* fs_clipboard, + const gchar* dest, + GtkWindow* wnd, + GError** error +) +{ + if (!(fs_clipboard->list_uris)) + { + g_set_error( + error, + wintc_shell_error_quark(), + WINTC_SHELL_ERROR_CLIPBOARD_EMPTY, + "%s", + "There are no files on the clipboard." // FIXME: Localise + ); + + return FALSE; + } + + // Attempt to paste these files + // + WinTCShFSOperation* fs_operation = + wintc_sh_fs_operation_new( + fs_clipboard->list_uris, + dest, + fs_clipboard->operation_kind + ); + + g_signal_connect( + fs_operation, + "done", + G_CALLBACK(on_fs_operation_done), + NULL + ); + + wintc_sh_fs_operation_do( + fs_operation, + wnd + ); + + // If the operation is move (aka 'Cut'), we should clear the clipboard + // + if (fs_clipboard->operation_kind == WINTC_SH_FS_OPERATION_MOVE) + { + wintc_clipboard_true_clear(fs_clipboard->clipboard); + } + + return TRUE; +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_sh_fs_clipboard_update_state( + WinTCShFSClipboard* fs_clipboard +) +{ + WINTC_LOG_DEBUG("shell: fsclipbd - update state"); + + fs_clipboard->operation_kind = WINTC_SH_FS_OPERATION_INVALID; + fs_clipboard->preferred_target = GDK_NONE; + + if (fs_clipboard->list_uris) + { + g_clear_list( + &(fs_clipboard->list_uris), + (GDestroyNotify) g_free + ); + } + + g_object_notify( + G_OBJECT(fs_clipboard), + "can-paste" + ); + + gtk_clipboard_request_targets( + fs_clipboard->clipboard, + cb_clipboard_targets_received, + fs_clipboard + ); +} + +// +// CALLBACKS +// +static void cb_clipboard_contents_received( + WINTC_UNUSED(GtkClipboard* clipboard), + GtkSelectionData* selection_data, + gpointer user_data +) +{ + WinTCShFSClipboard* fs_clipboard = WINTC_SH_FS_CLIPBOARD(user_data); + + WINTC_LOG_DEBUG("shell: fsclipbd - contents received, parsing"); + + // Parse the contents + // + const guchar* data; + const guchar* data_end; + gint len = 0; + + data = gtk_selection_data_get_data_with_length( + selection_data, + &len + ); + data_end = data + len; + + if (len < 0) // No data on clipboard + { + WINTC_LOG_DEBUG("shell: fsclipbd - apparently the contents are empty"); + return; + } + + if (fs_clipboard->preferred_target == S_ATOM_X_SPECIAL_GNOME_COPIED_FILES) + { + if (g_ascii_strncasecmp((const gchar*) data, "copy\n", 5) == 0) + { + fs_clipboard->operation_kind = WINTC_SH_FS_OPERATION_COPY; + data += 5; + } + else if (g_ascii_strncasecmp((const gchar*) data, "cut\n", 4) == 0) + { + fs_clipboard->operation_kind = WINTC_SH_FS_OPERATION_MOVE; + data += 4; + } + } + + while (data < data_end) + { + // Search for next \n + // + const guchar* next_lf = memchr(data, '\n', data_end - data); + + if (!next_lf) // gnome-copied-files has no final \n + { + next_lf = data_end; + } + + // Work out how much we need to slice + // + gint copy_len = next_lf - data; + + if (fs_clipboard->preferred_target == S_ATOM_TEXT_URI_LIST) + { + copy_len--; // text/uri-list uses \r\n rather than just \n + } + + // Slice and store the URI in our list + // + gchar* buf = g_malloc(copy_len + 1); + + memcpy(buf, data, copy_len); + buf[copy_len] = 0; + + fs_clipboard->list_uris = + g_list_append(fs_clipboard->list_uris, buf); + + // Iter + // + data = next_lf + 1; + } + + g_object_notify( + G_OBJECT(fs_clipboard), + "can-paste" + ); +} + +static void cb_clipboard_targets_received( + GtkClipboard* clipboard, + GdkAtom* atoms, + gint n_atoms, + gpointer user_data +) +{ +#define K_N_ATOMS_SEARCH 2 + + static GdkAtom s_atoms_prioritised[K_N_ATOMS_SEARCH]; + + if (s_atoms_prioritised[0] == GDK_NONE) + { + s_atoms_prioritised[0] = S_ATOM_X_SPECIAL_GNOME_COPIED_FILES; + s_atoms_prioritised[1] = S_ATOM_TEXT_URI_LIST; + } + + // Look for the atoms + // + WinTCShFSClipboard* fs_clipboard = WINTC_SH_FS_CLIPBOARD(user_data); + + WINTC_LOG_DEBUG("shell: fsclipbd - targets received, parsing"); + + for (gint i = 0; i < K_N_ATOMS_SEARCH; i++) + { + GdkAtom lookfor = s_atoms_prioritised[i]; + + for (gint j = 0; j < n_atoms; j++) + { + GdkAtom lookat = atoms[j]; + + if (lookat == lookfor) + { + fs_clipboard->preferred_target = lookfor; + goto doneloop; + } + } + } + +doneloop: + if (fs_clipboard->preferred_target == GDK_NONE) + { + WINTC_LOG_DEBUG("shell: fsclipbd - no supported formats"); + return; + } + + gtk_clipboard_request_contents( + clipboard, + fs_clipboard->preferred_target, + cb_clipboard_contents_received, + fs_clipboard + ); +} + +static void on_clipboard_owner_change( + WINTC_UNUSED(GtkClipboard* self), + WINTC_UNUSED(GdkEventOwnerChange* event), + gpointer user_data +) +{ + WINTC_LOG_DEBUG("shell: fsclipbd - owner changed"); + + wintc_sh_fs_clipboard_update_state( + WINTC_SH_FS_CLIPBOARD(user_data) + ); +} + +static void on_fs_operation_done( + WinTCShFSOperation* self, + WINTC_UNUSED(gpointer user_data) +) +{ + g_object_unref(self); +} diff --git a/shared/shell/src/fsop.c b/shared/shell/src/fsop.c new file mode 100644 index 0000000..0b62483 --- /dev/null +++ b/shared/shell/src/fsop.c @@ -0,0 +1,832 @@ +#include +#include +#include +#include + +#include "../public/fsop.h" + +#define K_TIMEOUT_SHOW_UI 1 + +// +// PRIVATE ENUMS +// +enum +{ + PROP_LIST_FILES = 1, + PROP_DESTINATION, + PROP_OPERATION +}; + +enum +{ + SIGNAL_DONE = 0, + N_SIGNALS +}; + +// +// FORWARD DECLARATIONS +// +static void wintc_sh_fs_operation_constructed( + GObject* object +); +static void wintc_sh_fs_operation_dispose( + GObject* object +); +static void wintc_sh_fs_operation_finalize( + GObject* object +); +static void wintc_sh_fs_operation_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +); +static void wintc_sh_fs_operation_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +); + +static void wintc_sh_fs_operation_step( + WinTCShFSOperation* fs_operation +); +static void wintc_sh_fs_operation_update_progress_text( + WinTCShFSOperation* fs_operation +); + +static GFile* get_g_file_for_copymove( + const gchar* src_path, + const gchar* dest_path +); +static GFile* get_g_file_for_target( + const gchar* target +); + +static void cb_async_file_op( + GObject* source_object, + GAsyncResult* res, + gpointer data +); +static void cb_file_progress_update_ui( + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data +); +static gboolean cb_timeout_show_ui( + gpointer user_data +); + +static void on_button_cancel_clicked( + GtkButton* self, + gpointer user_data +); + +// +// STATIC DATA +// +static gint wintc_sh_fs_operation_signals[N_SIGNALS] = { 0 }; + +// +// GTK OOP CLASS/INSTANCE DEFINITIONS +// +typedef struct _WinTCShFSOperation +{ + GObject __parent__; + + // Operation details + // + GCancellable* cancellable; + gchar* dest; + GFile* dest_file; + gboolean done; + guint id_timeout_show_ui; + GList* iter_op; + GList* list_files; + gint num_done; + gint num_files; + WinTCShFSOperationKind operation_kind; + + // UI + // + GtkWidget* wnd_progress; + GtkWidget* button_cancel; + GtkWidget* label_eta; + GtkWidget* label_filename; + GtkWidget* label_locations; + GtkWidget* prg_copymove; + + // Track the current top most window, either the owner window (eg. + // explorer) or the progress window, if it's visible + // + GtkWidget* wnd_topmost; +} WinTCShFSOperation; + +// +// GTK TYPE DEFINITIONS & CTORS +// +G_DEFINE_TYPE( + WinTCShFSOperation, + wintc_sh_fs_operation, + G_TYPE_OBJECT +) + +static void wintc_sh_fs_operation_class_init( + WinTCShFSOperationClass* klass +) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = wintc_sh_fs_operation_constructed; + object_class->dispose = wintc_sh_fs_operation_dispose; + object_class->finalize = wintc_sh_fs_operation_finalize; + object_class->get_property = wintc_sh_fs_operation_get_property; + object_class->set_property = wintc_sh_fs_operation_set_property; + + g_object_class_install_property( + object_class, + PROP_LIST_FILES, + g_param_spec_pointer( + "list-files", + "ListFiles", + "The list of files to operate on.", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + ) + ); + g_object_class_install_property( + object_class, + PROP_DESTINATION, + g_param_spec_string( + "destination", + "Destination", + "The destination for the files.", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + ) + ); + g_object_class_install_property( + object_class, + PROP_OPERATION, + g_param_spec_int( + "operation", + "Operation", + "The operation to perform.", + WINTC_SH_FS_OPERATION_INVALID, + WINTC_SH_FS_OPERATION_TRASH, + WINTC_SH_FS_OPERATION_INVALID, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY + ) + ); + + wintc_sh_fs_operation_signals[SIGNAL_DONE] = + g_signal_new( + "done", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0 + ); +} + +static void wintc_sh_fs_operation_init( + WINTC_UNUSED(WinTCShFSOperation* self) +) {} + +// +// CLASS VIRTUAL METHODS +// +static void wintc_sh_fs_operation_constructed( + GObject* object +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(object); + + // Check things are set + // + if (!(fs_operation->list_files) || !(fs_operation->dest)) + { + fs_operation->operation_kind = WINTC_SH_FS_OPERATION_INVALID; + } + + // Copying to trash is invalid + // + // FIXME: scheme check probs needs to be case insensitive? + // + if ( + fs_operation->operation_kind == WINTC_SH_FS_OPERATION_COPY && + g_str_has_prefix(fs_operation->dest, "trash://") + ) + { + fs_operation->operation_kind = WINTC_SH_FS_OPERATION_INVALID; + } + + // Moving to trash is just trashing + // + // FIXME: Ditto + // + if ( + fs_operation->operation_kind == WINTC_SH_FS_OPERATION_MOVE && + g_str_has_prefix(fs_operation->dest, "trash://") + ) + { + fs_operation->operation_kind = WINTC_SH_FS_OPERATION_TRASH; + } +} + +static void wintc_sh_fs_operation_dispose( + GObject* object +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(object); + + g_clear_object(&(fs_operation->cancellable)); + g_clear_object(&(fs_operation->dest_file)); + + if (fs_operation->wnd_progress) + { + gtk_widget_destroy(fs_operation->wnd_progress); + fs_operation->wnd_progress = NULL; + } + + (G_OBJECT_CLASS(wintc_sh_fs_operation_parent_class))->dispose(object); +} + +static void wintc_sh_fs_operation_finalize( + GObject* object +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(object); + + g_free(fs_operation->dest); + g_list_free_full(fs_operation->list_files, (GDestroyNotify) g_free); + + (G_OBJECT_CLASS(wintc_sh_fs_operation_parent_class))->finalize(object); +} + +static void wintc_sh_fs_operation_get_property( + GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(object); + + switch (prop_id) + { + case PROP_LIST_FILES: + g_value_set_pointer(value, fs_operation->list_files); + break; + + case PROP_DESTINATION: + g_value_set_string(value, fs_operation->dest); + break; + + case PROP_OPERATION: + g_value_set_int(value, fs_operation->operation_kind); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void wintc_sh_fs_operation_set_property( + GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(object); + + switch (prop_id) + { + case PROP_LIST_FILES: + fs_operation->list_files = g_list_copy_deep( + g_value_get_pointer(value), + (GCopyFunc) wintc_copyfunc_strdup, + NULL + ); + fs_operation->num_files = g_list_length(fs_operation->list_files); + + break; + + case PROP_DESTINATION: + fs_operation->dest = g_value_dup_string(value); + fs_operation->dest_file = get_g_file_for_target( + fs_operation->dest + ); + break; + + case PROP_OPERATION: + fs_operation->operation_kind = g_value_get_int(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +// +// PUBLIC FUNCTIONS +// +WinTCShFSOperation* wintc_sh_fs_operation_new( + GList* files, + const gchar* dest, + WinTCShFSOperationKind operation_kind +) +{ + return WINTC_SH_FS_OPERATION( + g_object_new( + WINTC_TYPE_SH_FS_OPERATION, + "list-files", files, + "destination", dest, + "operation", operation_kind, + NULL + ) + ); +} + +void wintc_sh_fs_operation_do( + WinTCShFSOperation* fs_operation, + GtkWindow* wnd +) +{ + if (fs_operation->operation_kind == WINTC_SH_FS_OPERATION_INVALID) + { + g_critical("%s", "shell: fs op: invalid operation"); + return; + } + + if (fs_operation->iter_op) + { + g_critical("%s", "shell fs op: operation already in progress"); + return; + } + + if (fs_operation->done) + { + g_critical("%s", "shell: fs op: operation already complete"); + return; + } + + // Create new cancellable for this op + // + fs_operation->cancellable = g_cancellable_new(); + + // Spawn timeout for UI + // + fs_operation->id_timeout_show_ui = + g_timeout_add_seconds( + K_TIMEOUT_SHOW_UI, + (GSourceFunc) cb_timeout_show_ui, + fs_operation + ); + + // Set up the operation for stepping + // + fs_operation->iter_op = fs_operation->list_files; + fs_operation->wnd_topmost = GTK_WIDGET(wnd); + + wintc_sh_fs_operation_step(fs_operation); +} + +// +// PRIVATE FUNCTIONS +// +static void wintc_sh_fs_operation_step( + WinTCShFSOperation* fs_operation +) +{ + // Update UI if needed + // + if (fs_operation->wnd_progress) + { + wintc_sh_fs_operation_update_progress_text(fs_operation); + } + + // Spawn the file operation + // + GFile* dest_file = fs_operation->dest_file; + const gchar* src_path = (gchar*) fs_operation->iter_op->data; + GFile* src_file = get_g_file_for_target(src_path); + + g_object_ref(dest_file); + + if ( + ( + fs_operation->operation_kind == WINTC_SH_FS_OPERATION_COPY || + fs_operation->operation_kind == WINTC_SH_FS_OPERATION_MOVE + ) && + g_file_test( + fs_operation->dest, + G_FILE_TEST_IS_DIR + ) + ) + { + g_object_unref(dest_file); + + dest_file = + get_g_file_for_copymove( + src_path, + fs_operation->dest + ); + } + + switch (fs_operation->operation_kind) + { + case WINTC_SH_FS_OPERATION_COPY: + WINTC_LOG_DEBUG( + "shell: fsop - copy %s to %s", + (gchar*) fs_operation->iter_op->data, + fs_operation->dest + ); + + g_file_copy_async( + src_file, + dest_file, + G_FILE_COPY_NONE, + G_PRIORITY_DEFAULT, + fs_operation->cancellable, + cb_file_progress_update_ui, + fs_operation, + (GAsyncReadyCallback) cb_async_file_op, + fs_operation + ); + + break; + + case WINTC_SH_FS_OPERATION_MOVE: + WINTC_LOG_DEBUG( + "shell: fsop - move %s to %s", + (gchar*) fs_operation->iter_op->data, + fs_operation->dest + ); + + g_file_move_async( + src_file, + dest_file, + G_FILE_COPY_NONE, + G_PRIORITY_DEFAULT, + fs_operation->cancellable, + cb_file_progress_update_ui, + fs_operation, + (GAsyncReadyCallback) cb_async_file_op, + fs_operation + ); + + break; + + case WINTC_SH_FS_OPERATION_TRASH: + WINTC_LOG_DEBUG( + "shell: fsop - trash %s", + (gchar*) fs_operation->iter_op->data + ); + + g_file_trash_async( + src_file, + G_PRIORITY_DEFAULT, + NULL, // FIXME: Cancellable, should use this! + (GAsyncReadyCallback) cb_async_file_op, + fs_operation + ); + + break; + + default: + g_critical("%s", "shell: fs op - cannot step, unknown op"); + break; + } + + g_object_unref(dest_file); +} + +static void wintc_sh_fs_operation_update_progress_text( + WinTCShFSOperation* fs_operation +) +{ + const gchar* src_path = fs_operation->iter_op->data; + + gchar* dst_dir = g_path_get_basename(fs_operation->dest); + gchar* src_dir = g_path_get_dirname(src_path); + gchar* src_file = g_path_get_basename(src_path); + + // FIXME: Localise + gchar* fmt_locations = + g_strdup_printf("From '%s' to '%s'", src_dir, dst_dir); + + gtk_label_set_text( + GTK_LABEL(fs_operation->label_filename), + src_file + ); + gtk_label_set_text( + GTK_LABEL(fs_operation->label_locations), + fmt_locations + ); + + g_free(dst_dir); + g_free(src_dir); + g_free(src_file); + g_free(fmt_locations); +} + +static GFile* get_g_file_for_copymove( + const gchar* src_path, + const gchar* dest_path +) +{ + GFile* ret; + + gchar* filename = g_path_get_basename(src_path); + gchar* new_dest = g_build_path( + G_DIR_SEPARATOR_S, + dest_path, + filename, + NULL + ); + + ret = get_g_file_for_target(new_dest); + + g_free(filename); + g_free(new_dest); + + return ret; +} + +static GFile* get_g_file_for_target( + const gchar* target +) +{ + // If the string starts with / it's a local path, otherwise treat it like + // a URI + // + if (strchr(target, G_DIR_SEPARATOR) == target) + { + return g_file_new_for_path(target); + } + else + { + return g_file_new_for_uri(target); + } +} + +// +// CALLBACKS +// +static void cb_async_file_op( + GObject* source_object, + GAsyncResult* res, + gpointer data +) +{ + GFile* file = (GFile*) source_object; + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(data); + + // Finish up this op + // + GError* error = NULL; + gboolean proceed = TRUE; + gboolean success; + + switch (fs_operation->operation_kind) + { + case WINTC_SH_FS_OPERATION_COPY: + success = g_file_copy_finish(file, res, &error); + break; + + case WINTC_SH_FS_OPERATION_MOVE: + success = g_file_move_finish(file, res, &error); + break; + + case WINTC_SH_FS_OPERATION_TRASH: + success = g_file_trash_finish(file, res, &error); + break; + + default: + g_critical("%s", "shell: fs op - impossible finish situation?"); + return; + } + + g_object_unref(file); + + if (!success) + { + // + // FIXME: Handle properly, depending on what the error is it could be + // possible to request user action for things like: + // + // - overwrite file when moving/copying? + // - trashing impossible on file system, delete instead? + // - merge directories? + // + // etc. - see copy/move/trash GLib documentation for what needs + // to be handled + // + + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + proceed = FALSE; + } + else + { + if (fs_operation->id_timeout_show_ui) + { + g_source_remove(fs_operation->id_timeout_show_ui); + } + + wintc_display_error_and_clear( + &error, + GTK_WINDOW(fs_operation->wnd_topmost) + ); + + fs_operation->id_timeout_show_ui = + g_timeout_add_seconds( + K_TIMEOUT_SHOW_UI, + (GSourceFunc) cb_timeout_show_ui, + fs_operation + ); + } + } + + // Proceed to next op? + // + if (proceed) + { + fs_operation->iter_op = fs_operation->iter_op->next; + fs_operation->num_done++; + } + else + { + fs_operation->iter_op = NULL; + } + + if (!(fs_operation->iter_op)) + { + fs_operation->done = TRUE; + + if (fs_operation->id_timeout_show_ui) + { + g_source_remove(fs_operation->id_timeout_show_ui); + fs_operation->id_timeout_show_ui = 0; + } + + if (fs_operation->wnd_progress) + { + gtk_widget_hide(fs_operation->wnd_progress); + } + + g_signal_emit( + fs_operation, + wintc_sh_fs_operation_signals[SIGNAL_DONE], + 0 + ); + + return; + } + + wintc_sh_fs_operation_step(fs_operation); +} + +static void cb_file_progress_update_ui( + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(user_data); + + // Skip if no window to update + // + if (!(fs_operation->wnd_progress)) + { + return; + } + + // Update progress percentage + // + gdouble pct_file = (gdouble) current_num_bytes / total_num_bytes; + gdouble pct_single = 1.0f / fs_operation->num_files; + gdouble pct_items = pct_single * fs_operation->num_done; + gdouble pct_total = pct_items + (pct_single * pct_file); + + gtk_progress_bar_set_fraction( + GTK_PROGRESS_BAR(fs_operation->prg_copymove), + pct_total + ); +} + +static gboolean cb_timeout_show_ui( + gpointer user_data +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(user_data); + + GtkBuilder* builder = + gtk_builder_new_from_resource( + "/uk/oddmatics/wintc/shell/dlgfsop.ui" + ); + + wintc_ctl_install_default_styles(); + wintc_lc_builder_preprocess_widget_text(builder); + + wintc_builder_get_objects( + builder, + "main-wnd", &(fs_operation->wnd_progress), + "button-cancel", &(fs_operation->button_cancel), + "label-eta", &(fs_operation->label_eta), + "label-filename", &(fs_operation->label_filename), + "label-locations", &(fs_operation->label_locations), + "prg-copymove", &(fs_operation->prg_copymove), + NULL + ); + + g_object_ref(fs_operation->wnd_progress); + + g_object_unref(builder); + + // Connect signals + // + g_signal_connect( + fs_operation->button_cancel, + "clicked", + G_CALLBACK(on_button_cancel_clicked), + fs_operation + ); + + // Set up window + // + GApplication* app = g_application_get_default(); + + if (app) + { + gtk_window_set_application( + GTK_WINDOW(fs_operation->wnd_progress), + GTK_APPLICATION(app) + ); + } + + switch (fs_operation->operation_kind) + { + case WINTC_SH_FS_OPERATION_COPY: + gtk_window_set_title( + GTK_WINDOW(fs_operation->wnd_progress), + "Copying..." + ); + break; + + case WINTC_SH_FS_OPERATION_MOVE: + gtk_window_set_title( + GTK_WINDOW(fs_operation->wnd_progress), + "Moving..." + ); + break; + + case WINTC_SH_FS_OPERATION_TRASH: + gtk_window_set_title( + GTK_WINDOW(fs_operation->wnd_progress), + "Deleting..." + ); + break; + + default: + g_warning("%s", "shell: fsop - don't know op for progress"); + break; + } + + gtk_window_set_modal( + GTK_WINDOW(fs_operation->wnd_progress), + TRUE + ); + gtk_window_set_transient_for( + GTK_WINDOW(fs_operation->wnd_progress), + GTK_WINDOW(fs_operation->wnd_topmost) + ); + + wintc_sh_fs_operation_update_progress_text(fs_operation); + + gtk_widget_show_all( + fs_operation->wnd_progress + ); + + fs_operation->id_timeout_show_ui = 0; + fs_operation->wnd_topmost = fs_operation->wnd_progress; + + return G_SOURCE_REMOVE; +} + +static void on_button_cancel_clicked( + WINTC_UNUSED(GtkButton* self), + gpointer user_data +) +{ + WinTCShFSOperation* fs_operation = WINTC_SH_FS_OPERATION(user_data); + + g_cancellable_cancel(fs_operation->cancellable); +} diff --git a/shared/shell/src/icnvwbeh.c b/shared/shell/src/icnvwbeh.c index fe14cc2..2667b4f 100644 --- a/shared/shell/src/icnvwbeh.c +++ b/shared/shell/src/icnvwbeh.c @@ -4,6 +4,7 @@ #include #include "../public/browser.h" +#include "../public/fsclipbd.h" #include "../public/icnvwbeh.h" // @@ -43,6 +44,11 @@ static void wintc_sh_icon_view_behaviour_update_view( WinTCShIconViewBehaviour* behaviour ); +static void action_paste_operation( + GSimpleAction* action, + GVariant* parameter, + gpointer user_data +); static void action_view_operation( GSimpleAction* action, GVariant* parameter, @@ -87,6 +93,13 @@ static void on_current_view_refreshing( static GSimpleAction* s_action_noop = NULL; static GActionEntry s_actions[] = { + { + .name = "paste-op", + .activate = action_paste_operation, + .parameter_type = "i", + .state = NULL, + .change_state = NULL + }, { .name = "view-op", .activate = action_view_operation, @@ -119,6 +132,10 @@ struct _WinTCShIconViewBehaviour gulong sigid_items_added; gulong sigid_items_removed; gulong sigid_refreshing; + + // Misc. stuff for actions + // + WinTCShFSClipboard* fs_clipboard; }; // @@ -184,6 +201,10 @@ static void wintc_sh_icon_view_behaviour_constructed( return; } + // Spawn the FS clipboard + // + behaviour->fs_clipboard = wintc_sh_fs_clipboard_new(); + // Set up view model // behaviour->list_model = @@ -226,6 +247,19 @@ static void wintc_sh_icon_view_behaviour_constructed( G_ACTION_GROUP(action_group) ); + // Bind special action states + // + GAction* action_paste_op = + g_action_map_lookup_action(G_ACTION_MAP(action_group), "paste-op"); + + g_object_bind_property( + behaviour->fs_clipboard, + "can-paste", + action_paste_op, + "enabled", + G_BINDING_DEFAULT + ); + g_object_unref(action_group); // Attach stuff to view @@ -296,6 +330,7 @@ static void wintc_sh_icon_view_behaviour_dispose( g_clear_object(&(behaviour->current_view)); g_clear_object(&(behaviour->browser)); g_clear_object(&(behaviour->icon_view)); + g_clear_object(&(behaviour->fs_clipboard)); (G_OBJECT_CLASS(wintc_sh_icon_view_behaviour_parent_class)) ->dispose(object); @@ -418,6 +453,27 @@ static void wintc_sh_icon_view_behaviour_update_view( // // CALLBACKS // +static void action_paste_operation( + WINTC_UNUSED(GSimpleAction* action), + GVariant* parameter, + gpointer user_data +) +{ + WinTCShIconViewBehaviour* behaviour = + WINTC_SH_ICON_VIEW_BEHAVIOUR(user_data); + + // Forward to normal view op + // + g_action_group_activate_action( + gtk_widget_get_action_group( + behaviour->icon_view, + "control" + ), + "view-op", + parameter + ); +} + static void action_view_operation( WINTC_UNUSED(GSimpleAction* action), GVariant* parameter, @@ -481,6 +537,9 @@ static void action_view_operation( WinTCIShextView* view = wintc_sh_browser_get_current_view( behaviour->browser ); + GtkWindow* wnd = wintc_widget_get_toplevel_window( + behaviour->icon_view + ); operation = wintc_ishext_view_spawn_operation( @@ -492,23 +551,18 @@ static void action_view_operation( if (!operation) { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear( + &error, + wnd + ); return; } // Execute! // - GtkWidget* toplevel = gtk_widget_get_toplevel(behaviour->icon_view); - GtkWindow* wnd = NULL; - - if (GTK_IS_WINDOW(toplevel)) - { - wnd = GTK_WINDOW(toplevel); - } - if (!(operation->func) (view, operation, wnd, &error)) { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear(&error, wnd); } g_free(operation); @@ -708,7 +762,10 @@ static void on_icon_view_item_activated( if (error) { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear( + &error, + wintc_widget_get_toplevel_window(GTK_WIDGET(self)) + ); } } } diff --git a/shared/shell/src/res/dlgfsop.ui b/shared/shell/src/res/dlgfsop.ui new file mode 100644 index 0000000..2629aed --- /dev/null +++ b/shared/shell/src/res/dlgfsop.ui @@ -0,0 +1,149 @@ + + + + False + False + + + + True + False + horizontal + + + + True + False + vertical + + + + True + False + 25 + 236 + + + False + False + 0 + + + + + True + False + filename + 0.0 + + + + + False + False + 1 + + + + + True + False + From 'A' to 'B' + 0.0 + + + + + False + False + 2 + + + + + True + False + + + + + False + False + 3 + + + + + True + False + + + 0.0 + + + + + False + False + 4 + + + + + + + False + False + 0 + + + + + + True + False + vertical + + + + Cancel + True + True + True + + + False + False + end + 0 + + + + + + + False + False + 1 + + + + + + diff --git a/shared/shell/src/res/menufs.ui b/shared/shell/src/res/menufs.ui index 0735414..2a06b57 100644 --- a/shared/shell/src/res/menufs.ui +++ b/shared/shell/src/res/menufs.ui @@ -9,12 +9,14 @@
- control.no-op + control.paste-op Paste + 6 - control.no-op + control.paste-op Paste Shortcut + 7
diff --git a/shared/shell/src/res/resources.xml b/shared/shell/src/res/resources.xml index 3b9e2af..76a0151 100644 --- a/shared/shell/src/res/resources.xml +++ b/shared/shell/src/res/resources.xml @@ -3,6 +3,7 @@ dlgabout.ui + dlgfsop.ui menuctx.ui diff --git a/shared/shell/src/vwfs.c b/shared/shell/src/vwfs.c index c96b4d5..9882c44 100644 --- a/shared/shell/src/vwfs.c +++ b/shared/shell/src/vwfs.c @@ -6,6 +6,7 @@ #include #include +#include "../public/fsclipbd.h" #include "../public/vwfs.h" // @@ -104,6 +105,12 @@ static gboolean shopr_open( GtkWindow* wnd, GError** error ); +static gboolean shopr_paste( + WinTCIShextView* view, + WinTCShextOperation* operation, + GtkWindow* wnd, + GError** error +); static void on_file_monitor_changed( GFileMonitor* self, @@ -133,7 +140,8 @@ struct _WinTCShViewFS GFileMonitor* fs_monitor; GHashTable* fs_map_entries; - WinTCShextHost* shext_host; + WinTCShFSClipboard* fs_clipboard; + WinTCShextHost* shext_host; }; // @@ -191,8 +199,11 @@ static void wintc_sh_view_fs_class_init( } static void wintc_sh_view_fs_init( - WINTC_UNUSED(WinTCShViewFS* self) -) {} + WinTCShViewFS* self +) +{ + self->fs_clipboard = wintc_sh_fs_clipboard_new(); +} static void wintc_sh_view_fs_ishext_view_interface_init( WinTCIShextViewInterface* iface @@ -220,6 +231,7 @@ static void wintc_sh_view_fs_dispose( { WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(object); + g_clear_object(&(view_fs->fs_clipboard)); g_clear_object(&(view_fs->fs_monitor)); g_clear_object(&(view_fs->shext_host)); @@ -634,12 +646,6 @@ static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( WINTC_UNUSED(GError** error) ) { - if (operation_id > WINTC_SHEXT_KNOWN_OP_OPEN) - { - g_critical("Not implemented %s", __func__); - return NULL; - } - // Spawn op // WinTCShextOperation* ret = g_new(WinTCShextOperation, 1); @@ -651,6 +657,10 @@ static WinTCShextOperation* wintc_sh_view_fs_spawn_operation( ret->priv = targets; break; + case WINTC_SHEXT_KNOWN_OP_PASTE: + ret->func = shopr_paste; + break; + default: g_free(ret); g_critical("%s", "shell: fs - invalid op"); @@ -819,6 +829,23 @@ static gboolean shopr_open( return success; } +static gboolean shopr_paste( + WinTCIShextView* view, + WINTC_UNUSED(WinTCShextOperation* operation), + GtkWindow* wnd, + GError** error +) +{ + WinTCShViewFS* view_fs = WINTC_SH_VIEW_FS(view); + + return wintc_sh_fs_clipboard_paste( + view_fs->fs_clipboard, + view_fs->path, + wnd, + error + ); +} + static void on_file_monitor_changed( WINTC_UNUSED(GFileMonitor* self), GFile* file, diff --git a/shell/cpl/desk/src/pagedesk.c b/shell/cpl/desk/src/pagedesk.c index 683a02b..636593b 100644 --- a/shell/cpl/desk/src/pagedesk.c +++ b/shell/cpl/desk/src/pagedesk.c @@ -293,7 +293,7 @@ static void refresh_wallpaper_list( if (!wnd->list_wallpapers) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, GTK_WINDOW(wnd)); return; } @@ -483,7 +483,7 @@ static void on_listbox_wallpapers_row_selected( if (!wnd->pixbuf_wallpaper) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, GTK_WINDOW(wnd)); return; } diff --git a/shell/cpl/desk/src/window.c b/shell/cpl/desk/src/window.c index 540acaf..c8d350d 100644 --- a/shell/cpl/desk/src/window.c +++ b/shell/cpl/desk/src/window.c @@ -245,7 +245,7 @@ static void action_apply( if (!wintc_cpl_desk_settings_apply(wnd->settings, &error)) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, GTK_WINDOW(wnd)); } } @@ -297,7 +297,7 @@ static gboolean on_window_map_event( if (!wintc_cpl_desk_settings_load(wnd->settings, &error)) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, GTK_WINDOW(wnd)); return FALSE; } diff --git a/shell/explorer/src/window.c b/shell/explorer/src/window.c index b0ecc3a..6c4b401 100644 --- a/shell/explorer/src/window.c +++ b/shell/explorer/src/window.c @@ -73,9 +73,10 @@ static void do_navigation_local( WinTCExplorerWindow* wnd, const gchar* specified_path ); -static void prepare_new_location( +static gboolean prepare_new_location( const gchar* specified_path, - WinTCShextPathInfo* local_path_info + WinTCShextPathInfo* local_path_info, + GError** error ); static void switch_mode_to( WinTCExplorerWindow* wnd, @@ -701,7 +702,7 @@ static void do_navigation( if (!regex_looks_webish) { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear(&error, GTK_WINDOW(wnd)); return; } } @@ -785,10 +786,19 @@ static void do_navigation_local( &(wnd->local_path) ); - prepare_new_location( - specified_path, - &path_info - ); + if ( + !prepare_new_location( + specified_path, + &path_info, + &error + ) + ) + { + wintc_nice_error_and_clear(&error, GTK_WINDOW(wnd)); + wintc_shext_path_info_free_data(&path_info); + + return; + } // Attempt the navigation // @@ -807,24 +817,23 @@ static void do_navigation_local( } else { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, GTK_WINDOW(wnd)); } wintc_shext_path_info_free_data(&path_info); } -static void prepare_new_location( +static gboolean prepare_new_location( const gchar* specified_path, - WinTCShextPathInfo* local_path_info + WinTCShextPathInfo* local_path_info, + GError** error ) { - GError* error = NULL; - const GRegex* regex_uri_scheme = wintc_regex_uri_scheme(&error); + const GRegex* regex_uri_scheme = wintc_regex_uri_scheme(error); if (!regex_uri_scheme) { - wintc_nice_error_and_clear(&error); - return; + return FALSE; } // If the path starts with '::' then assume it's a GUID and shouldn't be @@ -836,7 +845,7 @@ static void prepare_new_location( local_path_info->base_path = g_strdup(specified_path); - return; + return TRUE; } // If the path starts with a leading slash '/' then assume an absolute file @@ -849,7 +858,7 @@ static void prepare_new_location( local_path_info->base_path = g_strdup_printf("file://%s", specified_path); - return; + return TRUE; } // If the path is a URL, handle it here @@ -869,7 +878,7 @@ static void prepare_new_location( local_path_info->base_path = g_strdup(specified_path); - return; + return TRUE; } // Here we assume it's a relative path @@ -878,6 +887,8 @@ static void prepare_new_location( g_free(local_path_info->extended_path); local_path_info->extended_path = g_strdup(specified_path); + + return TRUE; } static void switch_mode_to( diff --git a/shell/taskband/src/start/personal.c b/shell/taskband/src/start/personal.c index d267f3a..345572a 100644 --- a/shell/taskband/src/start/personal.c +++ b/shell/taskband/src/start/personal.c @@ -868,7 +868,7 @@ static void refresh_personal_menu( if (!garcon_menu_load(all_entries, NULL, &error)) { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear(&error, NULL); return; } @@ -970,7 +970,7 @@ static void on_button_power_clicked( if (!wintc_launch_action(GPOINTER_TO_INT(user_data), &error)) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, NULL); } } @@ -997,7 +997,7 @@ static void on_event_box_userpic_clicked( "Cannot edit user pic yet!" ); - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, NULL); } static void on_menu_item_launcher_activate( @@ -1027,7 +1027,7 @@ static void on_menu_item_launcher_activate( if (error) { - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, NULL); } } diff --git a/tools/resvwr/src/window.c b/tools/resvwr/src/window.c index d740763..9d81d48 100644 --- a/tools/resvwr/src/window.c +++ b/tools/resvwr/src/window.c @@ -239,6 +239,7 @@ static void wintc_resvwr_window_refresh( GMenuModel* menu; GMenuModel* menubar; GtkWidget* main_box; + GtkWidget* main_wnd; GtkWidget* page; GtkWidget* toolbar; @@ -246,6 +247,7 @@ static void wintc_resvwr_window_refresh( builder, "label-title", &label_title, "main-box", &main_box, + "main-wnd", &main_wnd, "menu", &menu, "menubar", &menubar, "page", &page, @@ -260,6 +262,28 @@ static void wintc_resvwr_window_refresh( main_box ); } + else if (main_wnd) + { + GtkWidget* box_wrapper = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + GtkWidget* child = gtk_bin_get_child(GTK_BIN(main_wnd)); + + g_object_ref(child); + + gtk_container_remove( + GTK_CONTAINER(main_wnd), + child + ); + gtk_container_add( + GTK_CONTAINER(box_wrapper), + child + ); + gtk_container_add( + GTK_CONTAINER(wnd->scrollwnd_view), + box_wrapper + ); + + g_object_unref(child); + } else if (menu || menubar) { GtkWidget* box_wrapper = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); diff --git a/windows/notepad/src/window.c b/windows/notepad/src/window.c index 14d1fff..cb88da2 100644 --- a/windows/notepad/src/window.c +++ b/windows/notepad/src/window.c @@ -523,6 +523,8 @@ static void action_notimpl( WINTC_UNUSED(gpointer user_data) ) { + WinTCNotepadWindow* wnd = WINTC_NOTEPAD_WINDOW(user_data); + GError* error = NULL; g_set_error( @@ -533,7 +535,7 @@ static void action_notimpl( "Action not implemented." ); - wintc_nice_error_and_clear(&error); + wintc_nice_error_and_clear(&error, GTK_WINDOW(wnd)); } static void action_about( @@ -744,7 +746,10 @@ static void action_open( ) ) { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear( + &error, + GTK_WINDOW(wnd) + ); } break; @@ -770,13 +775,19 @@ static void action_open( } else { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear( + &error, + GTK_WINDOW(wnd) + ); } } } else { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear( + &error, + GTK_WINDOW(wnd) + ); } g_free(file_contents); @@ -893,12 +904,12 @@ static void action_save_as( } else { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear(&error, GTK_WINDOW(wnd)); } } else { - wintc_display_error_and_clear(&error); + wintc_display_error_and_clear(&error, GTK_WINDOW(wnd)); } g_free(file_contents);