Re: New treeviewized GtkFileSelection and GTK_SELECTION_MULTIPLE



On Mon, Feb 04, 2002 at 03:16:58PM -0800, Manish Singh wrote:
> On Mon, Feb 04, 2002 at 06:00:44PM -0500, Owen Taylor wrote:
> > 
> > Manish Singh <yosh gimp org> writes:
> > 
> > > A bit of an addendum:
> > > 
> > > > Proposed API additions:
> > > > 
> > > > 1) gtk_file_selection_set_selection_mode (): simply a wrapper around
> > > >    gtk_tree_selection_set_mode () for the fs->file_list
> > > 
> > > Also, a corresponding get_file_selection_get_selection_mode ()
> > >  
> > > > 2) gtk_file_selection_get_selections (): Returns an array of filenames,
> > > >    freed by the caller.
> > > 
> > > It should also return whatever the user typed in the entry widget, and
> > > prepend the directory names to all the filenames. I'm not sure I like the
> > > function name, anyone have any better suggestions?
> > 
> >  * On reflection, as much as I don't like adding API at this point, I think
> >    we need to do this. Multiple file selection is used in various current
> >    users of GTK+-2.0 (the GIMP, libbonoboui/bonobo/bonobo-list) and probably
> >    a elsewhere not using GTK+-2.0 yet, so currently we have
> >    a reasonably large functionality regression.
> > 
> >  * Adding the API explicitely is definitely better than adding the API
> >    implicitely by making it work if you poke into the internals in a certain
> >    way.
> > 
> >  * I think it's probably worth spending the effort to make the filename
> >    in that's put into entry not the first item among the selected items,
> >    but the most recently selected item. (More exactly, the first newly
> >    added item when the selection changes.)
> >   
> >    Unfortunately, the only clean way to do this that I (or Jonathan) could
> >    think of is to to keep track of the current selection and do diffs
> >    in selection::changed.
> > 
> > The proposed API looks fine, except that I'd like to see;
> > 
> >  gtk_file_selection_set/get_select_multiple (GtkFileSelection *filesel, gboolean select_multiple);
> > 
> > Then reusing the selection enum, since the "NONE" selection mode
> > doesn't make sense.
> 
> All sounds fine to me, I'll come up with a patch tonight.

Patch is attached. The diff stuff logic isn't quite finished yet, but the
API is there and should be working.

-Yosh
Index: gtkfilesel.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkfilesel.c,v
retrieving revision 1.101
diff -u -p -r1.101 gtkfilesel.c
--- gtkfilesel.c	2002/02/11 21:17:11	1.101
+++ gtkfilesel.c	2002/02/14 23:18:03
@@ -396,13 +396,18 @@ static void gtk_file_selection_abort    
 static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
 						    gchar                  *current_dir);
 
-static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
+static void gtk_file_selection_multiple_changed  (GtkTreeSelection  *selection,
+						  gpointer           user_data);
+
+static void gtk_file_selection_create_dir  (GtkWidget *widget, gpointer data);
 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
 
 
+static GtkDialogClass *parent_class = NULL;
 
-static GtkWindowClass *parent_class = NULL;
+static GQuark quark_selected_names = 0;
+static GQuark quark_last_selected = 0;
 
 /* Saves errno when something cmpl does fails. */
 static gint cmpl_errno;
@@ -505,8 +510,11 @@ gtk_file_selection_class_init (GtkFileSe
   gobject_class = (GObjectClass*) class;
   object_class = (GtkObjectClass*) class;
   widget_class = (GtkWidgetClass*) class;
+
+  parent_class = g_type_class_peek_parent (class);
 
-  parent_class = gtk_type_class (GTK_TYPE_DIALOG);
+  quark_selected_names = g_quark_from_static_string ("gtk-fs-selected-names");
+  quark_last_selected = g_quark_from_static_string ("gtk-fs-last_selected");
 
   gobject_class->finalize = gtk_file_selection_finalize;
   gobject_class->set_property = gtk_file_selection_set_property;
@@ -1815,7 +1823,7 @@ gtk_file_selection_update_history_menu (
 }
 
 static gchar *
-get_real_filename (gchar *filename)
+get_real_filename (gchar    *filename)
 {
 #ifdef G_WITH_CYGWIN
   /* Check to see if the selection was a drive selector */
@@ -1823,7 +1831,7 @@ get_real_filename (gchar *filename)
     {
       /* It is... map it to a CYGWIN32 drive */
       gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
-      g_free(filename);
+      g_free (filename);
       return temp_filename;
     }
 #else
@@ -2089,6 +2097,207 @@ gtk_file_selection_abort (GtkFileSelecti
 
   if (fs->selection_entry)
     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
+}
+
+void
+gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
+					gboolean          select_multiple)
+{
+  GtkTreeSelection *sel;
+  GtkSelectionMode mode;
+
+  g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
+
+  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
+
+  mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
+
+  gtk_tree_selection_set_mode (sel, mode);
+
+  g_signal_handlers_disconnect_matched (sel, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+      					NULL, filesel);
+  if (select_multiple)
+    g_signal_connect (sel, "changed",
+		      G_CALLBACK (gtk_file_selection_multiple_changed),
+		      filesel);
+  else
+    g_signal_connect (sel, "changed",
+		      G_CALLBACK (gtk_file_selection_file_changed),
+		      filesel);
+}
+
+gboolean
+gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
+{
+  GtkTreeSelection *sel;
+
+  g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
+
+  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
+  return !!(gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
+}
+
+static void
+multiple_changed_foreach (GtkTreeModel *model,
+			  GtkTreePath  *path,
+			  GtkTreeIter  *iter,
+			  gpointer      data)
+{
+  GPtrArray *names = data;
+  gchar *filename;
+
+  gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
+
+  g_ptr_array_add (names, filename);
+}
+
+static void
+free_selected_names (gpointer data)
+{
+  g_ptr_array_free ((GPtrArray *) data, TRUE);
+}
+
+#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
+#define ms_strcmp(a, b) strcmp(a, b)
+#else
+#define ms_strcmp(a, b) g_strcasecmp(a, b)
+#endif
+
+static void
+gtk_file_selection_multiple_changed  (GtkTreeSelection *selection,
+				      gpointer          user_data)
+{
+  GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
+  GPtrArray *new_names, *old_names;
+  gchar *filename, *old_filename;
+  gint i, index = 0;
+
+  new_names = g_ptr_array_sized_new (8);
+
+  gtk_tree_selection_selected_foreach (selection,
+				       multiple_changed_foreach,
+				       new_names);
+  /* nothing selected */
+  if (new_names->len == 0)
+    {
+      g_ptr_array_free (new_names, TRUE);
+      g_object_set_qdata (G_OBJECT (selection), quark_selected_names, NULL);
+      return;
+    }
+
+  if (new_names->len != 1)
+    {
+      old_names = g_object_get_qdata (G_OBJECT (selection),
+				      quark_selected_names);
+
+      if (old_names != NULL)
+	{
+	  /* FIXME: unfinished logic here, need to get the cases for
+	   * multiremoval correct
+	   */
+	  if (new_names->len > old_names->len)
+	    {
+	      if (ms_strcmp (g_ptr_array_index (old_names, old_names->len - 1),
+			     g_ptr_array_index (new_names, new_names->len - 1)) < 0)
+		index = new_names->len - 1;
+	      else
+		{
+		  for (i = 0; i < old_names->len; i++)
+		    {
+		      if (ms_strcmp (g_ptr_array_index (old_names, i),
+				     g_ptr_array_index (new_names, i)) != 0)
+			{
+			  index = i;
+			  break;
+			}
+		    }
+		}
+	    }
+	  else if (new_names->len < old_names->len)
+  	    {
+	    }
+	  else
+	    {
+	    }
+	}
+      else
+	{
+	  old_filename = g_object_get_qdata (G_OBJECT (selection),
+					     quark_last_selected);
+
+	  if (ms_strcmp (old_filename, g_ptr_array_index (new_names, 0)) == 0)
+	    index = new_names->len - 1;
+	  else
+	    index = 0;
+	}
+    }
+  else
+    index = 0;
+
+  g_object_set_qdata_full (G_OBJECT (selection), quark_selected_names,
+			   new_names, free_selected_names);
+
+  filename = g_ptr_array_index (new_names, index);
+  g_object_set_qdata_full (G_OBJECT (selection), quark_last_selected,
+      			   g_strdup (filename), g_free);
+
+  old_filename = filename;
+  filename = get_real_filename (filename);
+
+  gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
+
+  if (filename != old_filename)
+    g_free (filename);
+}
+
+gchar **
+gtk_file_selection_get_selections (GtkFileSelection *filesel)
+{
+  GtkTreeSelection *sel;
+  GPtrArray *names;
+  gchar **selections;
+  gchar *filename, *dirname, *current;
+  gint i, j;
+
+  g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
+
+  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
+
+  names = g_object_get_qdata (G_OBJECT (sel), quark_selected_names);
+
+  selections = g_new (gchar *, names->len + 2);
+
+  filename = g_strdup (gtk_file_selection_get_filename (filesel));
+
+  if (strlen (filename) == 0)
+    return NULL;
+
+  selections[0] = filename;
+  j = 1;
+
+  if (names != NULL)
+    {
+      dirname = g_path_get_dirname (filename);
+
+      for (i = 0; i < names->len; i++, j++)
+	{
+	  current = g_build_filename (dirname, g_ptr_array_index (names, i),
+				      NULL);
+
+	  if (ms_strcmp (current, filename) == 0)
+	    {
+	      g_free (current);
+	      selections = g_realloc (selections, names->len + 1);
+	      j--;
+	    }
+	  else
+	    selections[j] = current;
+	}
+    }
+
+  selections[j] = NULL;
+
+  return selections;
 }
 
 /**********************************************************************/
Index: gtkfilesel.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkfilesel.h,v
retrieving revision 1.13
diff -u -p -r1.13 gtkfilesel.h
--- gtkfilesel.h	2001/03/23 23:39:24	1.13
+++ gtkfilesel.h	2002/02/14 23:18:03
@@ -74,7 +74,6 @@ struct _GtkFileSelection
   
   GtkWidget *button_area;
   GtkWidget *action_area;
-  
 };
 
 struct _GtkFileSelectionClass
@@ -100,20 +99,15 @@ void	   gtk_file_selection_complete		  (
 void       gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel);
 void       gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel);
 
+gchar**    gtk_file_selection_get_selections      (GtkFileSelection *filesel);
 
+void       gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
+						   gboolean          select_multiple);
+gboolean   gtk_file_selection_get_select_multiple (GtkFileSelection *filesel);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
 
 
 #endif /* __GTK_FILESEL_H__ */
-
-
-
-
-
-
-
-
-
-


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]