From 3f72523dfa9fcbea86538b37130b863fc805e7ec Mon Sep 17 00:00:00 2001 From: Ettore Perazzoli Date: Wed, 7 Mar 2001 23:48:18 +0000 Subject: Added the ability for components to specify drag/drop types, and started some work to get the EStorageSetView to use them. Still in progress, nothing really happens for the user yet. svn path=/trunk/; revision=8589 --- shell/ChangeLog | 51 ++++++++++ shell/Evolution-ShellComponent.idl | 4 +- shell/e-component-registry.c | 19 +++- shell/e-folder-type-registry.c | 84 ++++++++++++++-- shell/e-folder-type-registry.h | 12 ++- shell/e-storage-set-view.c | 194 ++++++++++++++++++++++++++++++++++++- shell/evolution-shell-component.c | 71 +++++++++++++- shell/evolution-shell-component.h | 4 + 8 files changed, 425 insertions(+), 14 deletions(-) diff --git a/shell/ChangeLog b/shell/ChangeLog index 9c0c7d4be5..36a54a7c1b 100644 --- a/shell/ChangeLog +++ b/shell/ChangeLog @@ -1,3 +1,54 @@ +2001-03-07 Ettore Perazzoli + + * e-component-registry.c (register_type): New args + @num_exported_dnd_types, @exported_dnd_types, + @num_accepted_dnd_types, @accepted_dnd_types. Pass them to + `e_folder_type_registry_register_type()'. + (register_component): Updated accordingly, getting the values from + the CORBA `GNOME::Evolution::FolderType' struct. + + * e-folder-type-registry.c: New members `accepted_dnd_types' and + `exported_dnd_types' in `struct _FolderType'. + (e_folder_type_registry_register_type): New args + @num_accepted_dnd_types, @accepted_dnd_types, + @num_exported_dnd_types, @exported_dnd_types. + (register_folder_type): Likewise. + (folder_type_new): Likewise. Use them to initialize + `accepted_dnd_types' and `exported_dnd_types' in the newly created + `FolderType' struct. + (e_folder_type_registry_get_accepted_dnd_types_for_type): New. + (e_folder_type_registry_get_exported_dnd_types_for_type): New. + + * e-storage-set-view.c: New #defined constant `DRAG_RESISTANCE'. + New members `drag_x', `drag_y', `drag_column', `drag_row' in + `EStorageSetViewPrivate'. + (init): Initialize them. + (button_press_event): New function, overriding for the + `::button_press_event' method. + (motion_notify_event): New function, overriding for the + `::motion_notify_event' method. + (class_init): Install them. + + * evolution-shell-component.c + (duplicate_null_terminated_string_array): New helper function. + (evolution_shell_component_construct): Copy the values for + `accepted_dnd_types' and `exported_dnd_types' from the original + folder type list using it. + (destroy): Free the `exported_dnd_types' and `accepted_dnd_types' + members too. + (fill_corba_sequence_from_null_terminated_string_array): New + helper function. + (impl_ShellComponent__get_supported_types): Also set the + `exported_dnd_types' and `accepted_dnd_types' values in the folder + type elements we return. + + * evolution-shell-component.h: Added `accepted_dnd_types' and + `exported_dnd_types' members to `EvolutionShellComponentFolderType'. + + * Evolution-ShellComponent.idl: Renamed `accepted_dnd_mime_types' + to `accepted_dnd_types' and `exported_dnd_mime_types' to + `exported_dnd_types'. These are not necessarily MIME types. + 2001-03-05 Christopher James Lahey * e-storage-set-view.c: Override cursor_activated signal instead diff --git a/shell/Evolution-ShellComponent.idl b/shell/Evolution-ShellComponent.idl index 989aa06827..174f7fafdb 100644 --- a/shell/Evolution-ShellComponent.idl +++ b/shell/Evolution-ShellComponent.idl @@ -18,8 +18,8 @@ module Evolution { string name; string icon_name; - sequence accepted_dnd_mime_types; - sequence exported_dnd_mime_types; + sequence accepted_dnd_types; + sequence exported_dnd_types; }; typedef sequence FolderTypeList; diff --git a/shell/e-component-registry.c b/shell/e-component-registry.c index 89638fa14c..f175722ab7 100644 --- a/shell/e-component-registry.c +++ b/shell/e-component-registry.c @@ -106,6 +106,10 @@ static gboolean register_type (EComponentRegistry *component_registry, const char *name, const char *icon_name, + int num_exported_dnd_types, + const char **exported_dnd_types, + int num_accepted_dnd_types, + const char **accepted_dnd_types, Component *handler) { EComponentRegistryPrivate *priv; @@ -116,7 +120,12 @@ register_type (EComponentRegistry *component_registry, folder_type_registry = e_shell_get_folder_type_registry (priv->shell); g_assert (folder_type_registry != NULL); - if (! e_folder_type_registry_register_type (folder_type_registry, name, icon_name)) { + if (! e_folder_type_registry_register_type (folder_type_registry, + name, icon_name, + num_exported_dnd_types, + exported_dnd_types, + num_accepted_dnd_types, + accepted_dnd_types)) { g_warning ("Trying to register duplicate folder type -- %s", name); return FALSE; } @@ -175,7 +184,13 @@ register_component (EComponentRegistry *component_registry, type = supported_types->_buffer + i; - if (! register_type (component_registry, type->name, type->icon_name, component)) { + if (! register_type (component_registry, + type->name, type->icon_name, + type->exported_dnd_types._length, + (const char **) type->exported_dnd_types._buffer, + type->accepted_dnd_types._length, + (const char **) type->accepted_dnd_types._buffer, + component)) { g_warning ("Cannot register type `%s' for component %s", type->name, component->id); } else { diff --git a/shell/e-folder-type-registry.c b/shell/e-folder-type-registry.c index b73deb4b06..ebd258b61e 100644 --- a/shell/e-folder-type-registry.c +++ b/shell/e-folder-type-registry.c @@ -42,6 +42,9 @@ struct _FolderType { char *name; char *icon_name; + GList *exported_dnd_types; /* char * */ + GList *accepted_dnd_types; /* char * */ + EvolutionShellComponentClient *handler; /* The icon, standard (48x48) and mini (16x16) versions. */ @@ -59,16 +62,34 @@ struct _EFolderTypeRegistryPrivate { static FolderType * folder_type_new (const char *name, - const char *icon_name) + const char *icon_name, + int num_exported_dnd_types, + const char **exported_dnd_types, + int num_accepted_dnd_types, + const char **accepted_dnd_types) { FolderType *new; char *icon_path; + int i; new = g_new (FolderType, 1); new->name = g_strdup (name); new->icon_name = g_strdup (icon_name); - new->handler = NULL; + + new->exported_dnd_types = NULL; + for (i = 0; i < num_exported_dnd_types; i++) + new->exported_dnd_types = g_list_prepend (new->exported_dnd_types, + g_strdup (exported_dnd_types[i])); + new->exported_dnd_types = g_list_reverse (new->exported_dnd_types); + + new->accepted_dnd_types = NULL; + for (i = 0; i < num_accepted_dnd_types; i++) + new->accepted_dnd_types = g_list_prepend (new->accepted_dnd_types, + g_strdup (accepted_dnd_types[i])); + new->accepted_dnd_types = g_list_reverse (new->accepted_dnd_types); + + new->handler = NULL; icon_path = e_shell_get_icon_path (icon_name, FALSE); if (icon_path == NULL) @@ -124,7 +145,11 @@ get_folder_type (EFolderTypeRegistry *folder_type_registry, static gboolean register_folder_type (EFolderTypeRegistry *folder_type_registry, const char *name, - const char *icon_name) + const char *icon_name, + int num_exported_dnd_types, + const char **exported_dnd_types, + int num_accepted_dnd_types, + const char **accepted_dnd_types) { EFolderTypeRegistryPrivate *priv; FolderType *folder_type; @@ -135,7 +160,9 @@ register_folder_type (EFolderTypeRegistry *folder_type_registry, if (get_folder_type (folder_type_registry, name) != NULL) return FALSE; - folder_type = folder_type_new (name, icon_name); + folder_type = folder_type_new (name, icon_name, + num_exported_dnd_types, exported_dnd_types, + num_accepted_dnd_types, accepted_dnd_types); g_hash_table_insert (priv->name_to_type, folder_type->name, folder_type); return TRUE; @@ -247,14 +274,20 @@ e_folder_type_registry_new (void) gboolean e_folder_type_registry_register_type (EFolderTypeRegistry *folder_type_registry, const char *type_name, - const char *icon_name) + const char *icon_name, + int num_exported_dnd_types, + const char **exported_dnd_types, + int num_accepted_dnd_types, + const char **accepted_dnd_types) { g_return_val_if_fail (folder_type_registry != NULL, FALSE); g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), FALSE); g_return_val_if_fail (type_name != NULL, FALSE); g_return_val_if_fail (icon_name != NULL, FALSE); - return register_folder_type (folder_type_registry, type_name, icon_name); + return register_folder_type (folder_type_registry, type_name, icon_name, + num_exported_dnd_types, exported_dnd_types, + num_accepted_dnd_types, accepted_dnd_types); } gboolean @@ -362,6 +395,45 @@ e_folder_type_registry_get_handler_for_type (EFolderTypeRegistry *folder_type_re return folder_type->handler; } + +GList * +e_folder_type_registry_get_exported_dnd_types_for_type (EFolderTypeRegistry *folder_type_registry, + const char *type_name) +{ + const FolderType *folder_type; + + g_return_val_if_fail (folder_type_registry != NULL, NULL); + g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL); + g_return_val_if_fail (type_name != NULL, NULL); + + folder_type = get_folder_type (folder_type_registry, type_name); + if (folder_type == NULL) { + g_warning ("e_folder_type_registry_get_exported_dnd_types_for_type() -- Unknown type `%s'", type_name); + return NULL; + } + + return folder_type->exported_dnd_types; +} + +GList * +e_folder_type_registry_get_accepted_dnd_types_for_type (EFolderTypeRegistry *folder_type_registry, + const char *type_name) +{ + const FolderType *folder_type; + + g_return_val_if_fail (folder_type_registry != NULL, NULL); + g_return_val_if_fail (E_IS_FOLDER_TYPE_REGISTRY (folder_type_registry), NULL); + g_return_val_if_fail (type_name != NULL, NULL); + + folder_type = get_folder_type (folder_type_registry, type_name); + if (folder_type == NULL) { + g_warning ("e_folder_type_registry_get_accepted_dnd_types_for_type() -- Unknown type `%s'", type_name); + return NULL; + } + + return folder_type->accepted_dnd_types; +} + E_MAKE_TYPE (e_folder_type_registry, "EFolderTypeRegistry", EFolderTypeRegistry, class_init, init, PARENT_TYPE) diff --git a/shell/e-folder-type-registry.h b/shell/e-folder-type-registry.h index f4a1905f52..f34db31811 100644 --- a/shell/e-folder-type-registry.h +++ b/shell/e-folder-type-registry.h @@ -62,7 +62,11 @@ EFolderTypeRegistry *e_folder_type_registry_new (void); gboolean e_folder_type_registry_register_type (EFolderTypeRegistry *folder_type_registry, const char *type_name, - const char *icon_name); + const char *icon_name, + int num_exported_dnd_types, + const char **exported_dnd_types, + int num_accepted_dnd_types, + const char **accepted_dnd_types); gboolean e_folder_type_registry_set_handler_for_type (EFolderTypeRegistry *folder_type_registry, const char *type_name, EvolutionShellComponentClient *handler); @@ -77,6 +81,12 @@ const char *e_folder_type_registry_get_icon_name_for_type (E EvolutionShellComponentClient *e_folder_type_registry_get_handler_for_type (EFolderTypeRegistry *folder_type_registry, const char *type_name); +GList *e_folder_type_registry_get_exported_dnd_types_for_type (EFolderTypeRegistry *folder_type_registry, + const char *type_name); +GList *e_folder_type_registry_get_accepted_dnd_types_for_type (EFolderTypeRegistry *folder_type_registry, + const char *type_name); + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/shell/e-storage-set-view.c b/shell/e-storage-set-view.c index 8dcc988234..dc299cb787 100644 --- a/shell/e-storage-set-view.c +++ b/shell/e-storage-set-view.c @@ -45,6 +45,10 @@ static char *list [] = { }; #endif +#define DRAG_RESISTANCE 3 /* FIXME hardcoded in ETable to this value as + * well, and there is no way for us to use the + * same value as it's not exported. */ + #define ETABLE_SPEC " \ \ \ @@ -71,6 +75,9 @@ struct _EStorageSetViewPrivate { const char *selected_row_path; gboolean show_folders; + + int drag_x, drag_y; + int drag_column, drag_row; }; @@ -254,6 +261,100 @@ marshal_NONE__GDKDRAGCONTEXT_STRING_STRING_STRING (GtkObject *object, /* DnD selection setup stuff. */ +/* This will create an array of GtkTargetEntries from the specified list of DND + types. The type name will *not* be allocated in the list, as this is + supposed to be used only temporarily to set up the cell as a drag source. */ +static GtkTargetEntry * +create_target_entries_from_dnd_type_list (GList *dnd_types, + int *num_entries_return) +{ + GtkTargetEntry *entries; + GList *p; + int num_entries; + int i; + + if (dnd_types == NULL) + return NULL; + + num_entries = g_list_length (dnd_types); + if (num_entries == 0) + return NULL; + + entries = g_new (GtkTargetEntry, num_entries); + + for (p = dnd_types, i = 0; p != NULL; p = p->next, i++) { + const char *dnd_type; + + g_assert (i < num_entries); + + dnd_type = (const char *) p->data; + + entries[i].target = (char *) dnd_type; + entries[i].flags = 0; + entries[i].info = i; + } + + *num_entries_return = num_entries; + return entries; +} + +static void +free_target_entries (GtkTargetEntry *entries) +{ + g_assert (entries != NULL); + + /* The target names are not strdup()ed so a simple free will do. */ + g_free (entries); +} + +static GtkTargetList * +create_target_list_for_row (EStorageSetView *storage_set_view, + int row) +{ + EStorageSetViewPrivate *priv; + GtkTargetList *target_list; + EFolderTypeRegistry *folder_type_registry; + ETreePath *folder_node_path; + EFolder *folder; + const char *folder_path; + const char *folder_type; + GList *exported_dnd_types; + GtkTargetEntry *target_entries; + int num_target_entries; + + priv = storage_set_view->priv; + + target_list = gtk_target_list_new (NULL, 0); + + folder_type_registry = e_storage_set_get_folder_type_registry (priv->storage_set); + + folder_node_path = e_tree_model_node_at_row (priv->etree_model, row); + g_assert (folder_node_path != NULL); + + folder_path = e_tree_model_node_get_data (priv->etree_model, folder_node_path); + g_assert (folder_path != NULL); + + folder = e_storage_set_get_folder (priv->storage_set, folder_path); + g_assert (folder != NULL); + + folder_type = e_folder_get_type_string (folder); + + exported_dnd_types = e_folder_type_registry_get_exported_dnd_types_for_type (folder_type_registry, + folder_type); + if (exported_dnd_types == NULL) + return NULL; + + target_entries = create_target_entries_from_dnd_type_list (exported_dnd_types, + &num_target_entries); + g_assert (target_entries != NULL); + + target_list = gtk_target_list_new (target_entries, num_target_entries); + + free_target_entries (target_entries); + + return NULL; +} + static void set_uri_list_selection (EStorageSetView *storage_set_view, GtkSelectionData *selection_data) @@ -439,7 +540,87 @@ destroy (GtkObject *object) } -/* ETable methods */ +/* GtkWidget methods. */ + +static int +button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + EStorageSetView *storage_set_view; + EStorageSetViewPrivate *priv; + ETable *table; + int row, column; + + storage_set_view = E_STORAGE_SET_VIEW (widget); + priv = storage_set_view->priv; + + table = E_TABLE (widget); + + /* FIXME correct? */ + if (GTK_WIDGET_CLASS (parent_class)->button_press_event != NULL) + (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event); + + if (event->button != 1) + return FALSE; + + e_table_get_cell_at (table, event->x, event->y, &row, &column); + + g_print ("e-storage-set-view.c::button_press_event() -- row %d column %d\n", row, column); + + priv->drag_x = event->x; + priv->drag_y = event->y; + priv->drag_column = column; + priv->drag_row = row; + + /* FIXME correct? */ + return TRUE; +} + +static int +motion_notify_event (GtkWidget *widget, + GdkEventMotion *event) +{ + EStorageSetView *storage_set_view; + EStorageSetViewPrivate *priv; + ETable *table; + GtkTargetList *target_list; + GdkDragAction actions; + GdkDragContext *context; + + storage_set_view = E_STORAGE_SET_VIEW (widget); + priv = storage_set_view->priv; + + table = E_TABLE (widget); + + /* FIXME correct? */ + if (GTK_WIDGET_CLASS (parent_class)->motion_notify_event != NULL) + (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event); + + /* FIXME correct? */ + if (! (event->state & GDK_BUTTON1_MASK)) + return FALSE; + + if (ABS (priv->drag_x - event->x) < DRAG_RESISTANCE + && ABS (priv->drag_y - event->y) < DRAG_RESISTANCE) + return FALSE; + + target_list = create_target_list_for_row (storage_set_view, priv->drag_row); + if (target_list == NULL) + return FALSE; + + actions = GDK_ACTION_MOVE | GDK_ACTION_COPY; + + context = e_table_drag_begin (table, + priv->drag_row, priv->drag_column, + target_list, actions, + 1, (GdkEvent *) event); + gtk_drag_set_icon_default (context); + + return FALSE; +} + + +/* ETable methods. */ static void table_drag_begin (ETable *etable, @@ -870,6 +1051,7 @@ static void class_init (EStorageSetViewClass *klass) { GtkObjectClass *object_class; + GtkWidgetClass *widget_class; ETableClass *etable_class; parent_class = gtk_type_class (e_table_get_type ()); @@ -877,6 +1059,10 @@ class_init (EStorageSetViewClass *klass) object_class = GTK_OBJECT_CLASS (klass); object_class->destroy = destroy; + widget_class = GTK_WIDGET_CLASS (klass); + widget_class->button_press_event = button_press_event; + widget_class->motion_notify_event = motion_notify_event; + etable_class = E_TABLE_CLASS (klass); etable_class->right_click = right_click; etable_class->cursor_activated = cursor_activated; @@ -936,6 +1122,10 @@ init (EStorageSetView *storage_set_view) priv->type_name_to_pixbuf = g_hash_table_new (g_str_hash, g_str_equal); priv->selected_row_path = NULL; priv->show_folders = TRUE; + priv->drag_x = 0; + priv->drag_y = 0; + priv->drag_column = 0; + priv->drag_row = 0; storage_set_view->priv = priv; } @@ -1117,6 +1307,7 @@ e_storage_set_view_construct (EStorageSetView *storage_set_view, ETABLE_SPEC, NULL); gtk_object_unref (GTK_OBJECT (extras)); +#if 0 e_table_drag_source_set (E_TABLE (storage_set_view), GDK_BUTTON1_MASK, source_drag_types, num_source_drag_types, GDK_ACTION_MOVE | GDK_ACTION_COPY); @@ -1124,6 +1315,7 @@ e_storage_set_view_construct (EStorageSetView *storage_set_view, e_table_drag_dest_set (E_TABLE (storage_set_view), GTK_DEST_DEFAULT_ALL, source_drag_types, num_source_drag_types, GDK_ACTION_MOVE | GDK_ACTION_COPY); +#endif gtk_object_ref (GTK_OBJECT (storage_set)); priv->storage_set = storage_set; diff --git a/shell/evolution-shell-component.c b/shell/evolution-shell-component.c index 63f5fb7254..0e28981ef6 100644 --- a/shell/evolution-shell-component.c +++ b/shell/evolution-shell-component.c @@ -60,6 +60,63 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; + +/* Helper functions. */ + +/* Notice that, if passed a NULL pointer, this string will construct a + zero-element NULL-terminated string array instead of returning NULL itself + (i.e. it will return a pointer to a single g_malloc()ed NULL pointer). */ +static char ** +duplicate_null_terminated_string_array (char *array[]) +{ + char **new; + int count; + int i; + + if (array == NULL) { + count = 0; + } else { + for (count = 0; array[count] != NULL; count++) + ; + } + + new = g_new (char *, count + 1); + + for (i = 0; i < count; i++) + new[i] = g_strdup (array[i]); + new[count] = NULL; + + return new; +} + +/* The following will create a CORBA sequence of strings from the specified + * NULL-terminated array, without duplicating the strings. */ +static void +fill_corba_sequence_from_null_terminated_string_array (CORBA_sequence_CORBA_string *corba_sequence, + char **array) +{ + int count; + int i; + + g_assert (corba_sequence != NULL); + g_assert (array != NULL); + + /* We won't be reallocating the strings, so we don't want them to be + freed when the sequence is freed. */ + CORBA_sequence_set_release (corba_sequence, FALSE); + + count = 0; + while (array[count] != NULL) + count++; + + corba_sequence->_maximum = count; + corba_sequence->_length = count; + corba_sequence->_buffer = CORBA_sequence_CORBA_string_allocbuf (count); + + for (i = 0; i < count; i++) + corba_sequence->_buffer[i] = (CORBA_char *) array[i]; +} + /* CORBA interface implementation. */ @@ -93,6 +150,11 @@ impl_ShellComponent__get_supported_types (PortableServer_Servant servant, corba_folder_type = folder_type_list->_buffer + i; corba_folder_type->name = CORBA_string_dup (folder_type->name); corba_folder_type->icon_name = CORBA_string_dup (folder_type->icon_name); + + fill_corba_sequence_from_null_terminated_string_array (& corba_folder_type->accepted_dnd_types, + folder_type->accepted_dnd_types); + fill_corba_sequence_from_null_terminated_string_array (& corba_folder_type->exported_dnd_types, + folder_type->exported_dnd_types); } return folder_type_list; @@ -333,6 +395,9 @@ destroy (GtkObject *object) g_free (folder_type->name); g_free (folder_type->icon_name); + g_strfreev (folder_type->exported_dnd_types); + g_strfreev (folder_type->accepted_dnd_types); + g_free (folder_type); } g_list_free (priv->folder_types); @@ -439,8 +504,10 @@ evolution_shell_component_construct (EvolutionShellComponent *shell_component, continue; new = g_new (EvolutionShellComponentFolderType, 1); - new->name = g_strdup (folder_types[i].name); - new->icon_name = g_strdup (folder_types[i].icon_name); + new->name = g_strdup (folder_types[i].name); + new->icon_name = g_strdup (folder_types[i].icon_name); + new->accepted_dnd_types = duplicate_null_terminated_string_array (folder_types[i].accepted_dnd_types); + new->exported_dnd_types = duplicate_null_terminated_string_array (folder_types[i].exported_dnd_types); priv->folder_types = g_list_prepend (priv->folder_types, new); } diff --git a/shell/evolution-shell-component.h b/shell/evolution-shell-component.h index 0f5e57b9af..b64f3ae524 100644 --- a/shell/evolution-shell-component.h +++ b/shell/evolution-shell-component.h @@ -98,6 +98,10 @@ typedef void (* EvolutionShellComponentPopulateFolderContextMenu) (EvolutionShel struct _EvolutionShellComponentFolderType { char *name; char *icon_name; + + /* The following are NULL-terminated arrays. */ + char **accepted_dnd_types; + char **exported_dnd_types; }; typedef struct _EvolutionShellComponentFolderType EvolutionShellComponentFolderType; -- cgit v1.2.3