*** empty log message ***
[bpt/emacs.git] / src / gtkutil.c
index 54cb43b..b8d37df 100644 (file)
@@ -1,5 +1,5 @@
 /* Functions for creating and updating GTK widgets.
-   Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -199,7 +199,7 @@ malloc_widget_value ()
     }
   else
     {
-      wv = (widget_value *) malloc (sizeof (widget_value));
+      wv = (widget_value *) xmalloc (sizeof (widget_value));
       malloc_cpt++;
     }
   memset (wv, 0, sizeof (widget_value));
@@ -322,43 +322,39 @@ xg_get_image_for_pixmap (f, img, widget, old_widget)
   GdkPixmap *gmask;
   GdkDisplay *gdpy;
 
-  /* If we are on a one bit display, let GTK do all the image handling.
+  /* If we have a file, let GTK do all the image handling.
      This seems to be the only way to make insensitive and activated icons
-     look good.  */
-  if (x_screen_planes (f) == 1)
-    {
-      Lisp_Object specified_file = Qnil;
-      Lisp_Object tail;
-      extern Lisp_Object QCfile;
-
-      for (tail = XCDR (img->spec);
-          NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
-          tail = XCDR (XCDR (tail)))
-       if (EQ (XCAR (tail), QCfile))
-         specified_file = XCAR (XCDR (tail));
-
-       if (STRINGP (specified_file))
-         {
+     look good in all cases.  */
+  Lisp_Object specified_file = Qnil;
+  Lisp_Object tail;
+  Lisp_Object file;
+  extern Lisp_Object QCfile;
 
-           Lisp_Object file = Qnil;
-           struct gcpro gcpro1;
-           GCPRO1 (file);
+  for (tail = XCDR (img->spec);
+       NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
+       tail = XCDR (XCDR (tail)))
+    if (EQ (XCAR (tail), QCfile))
+      specified_file = XCAR (XCDR (tail));
 
-           file = x_find_image_file (specified_file);
-           /* We already loaded the image once before calling this
-              function, so this should not fail.  */
-           xassert (STRINGP (file) != 0);
+  /* We already loaded the image once before calling this
+     function, so this only fails if the image file has been removed.
+     In that case, use the pixmap already loaded.  */
 
-           if (! old_widget)
-             old_widget = GTK_IMAGE (gtk_image_new_from_file (SSDATA (file)));
-           else
-             gtk_image_set_from_file (old_widget, SSDATA (file));
+  if (STRINGP (specified_file)
+      && STRINGP (file = x_find_image_file (specified_file)))
+    {
+      if (! old_widget)
+        old_widget = GTK_IMAGE (gtk_image_new_from_file (SSDATA (file)));
+      else
+        gtk_image_set_from_file (old_widget, SSDATA (file));
 
-           UNGCPRO;
-           return GTK_WIDGET (old_widget);
-         }
+      return GTK_WIDGET (old_widget);
     }
 
+  /* No file, do the image handling ourselves.  This will look very bad
+     on a monochrome display, and sometimes bad on all displays with
+     certain themes.  */
+
   gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
   gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap);
   gmask = img->mask ? gdk_pixmap_foreign_new_for_display (gdpy, img->mask) : 0;
@@ -1155,6 +1151,27 @@ create_dialog (wv, select_cb, deactivate_cb)
 /***********************************************************************
                       File dialog functions
  ***********************************************************************/
+/* Return non-zero if the old file selection dialog is being used.
+   Return zero if not.  */
+
+int
+xg_uses_old_file_dialog ()
+{
+#ifdef HAVE_GTK_FILE_BOTH
+  extern int x_use_old_gtk_file_dialog;
+  return x_use_old_gtk_file_dialog;
+#else /* ! HAVE_GTK_FILE_BOTH */
+
+#ifdef HAVE_GTK_FILE_SELECTION_NEW
+  return 1;
+#else
+  return 0;
+#endif
+
+#endif /* ! HAVE_GTK_FILE_BOTH */
+}
+
+
 /* Function that is called when the file dialog pops down.
    W is the dialog widget, RESPONSE is the response code.
    USER_DATA is what we passed in to g_signal_connect (pointer to int).  */
@@ -1199,6 +1216,59 @@ xg_get_file_name_from_chooser (w)
   return gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
 }
 
+/* Callback called when the "Show hidden files" toggle is pressed.
+   WIDGET is the toggle widget, DATA is the file chooser dialog.  */
+
+static void
+xg_toggle_visibility_cb (widget, data)
+     GtkWidget *widget;
+     gpointer data;
+{
+  GtkFileChooser *dialog = GTK_FILE_CHOOSER (data);
+  gboolean visible;
+  g_object_get (G_OBJECT (dialog), "show-hidden", &visible, NULL);
+  g_object_set (G_OBJECT (dialog), "show-hidden", !visible, NULL);
+}
+
+
+/* Callback called when a property changes in a file chooser.
+   GOBJECT is the file chooser dialog, ARG1 describes the property.
+   USER_DATA is the toggle widget in the file chooser dialog.
+   We use this to update the "Show hidden files" toggle when the user
+   changes that property by right clicking in the file list.  */
+
+static void
+xg_toggle_notify_cb (gobject, arg1, user_data)
+     GObject *gobject;
+     GParamSpec *arg1;
+     gpointer user_data;
+{
+  extern int x_gtk_show_hidden_files;
+
+  if (strcmp (arg1->name, "show-hidden") == 0)
+    {
+      GtkFileChooser *dialog = GTK_FILE_CHOOSER (gobject);
+      GtkWidget *wtoggle = GTK_WIDGET (user_data);
+      gboolean visible, toggle_on;
+
+      g_object_get (G_OBJECT (gobject), "show-hidden", &visible, NULL);
+      toggle_on = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wtoggle));
+
+      if (!!visible != !!toggle_on)
+        {
+          g_signal_handlers_block_by_func (G_OBJECT (wtoggle),
+                                           G_CALLBACK (xg_toggle_visibility_cb),
+                                           gobject);
+          gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), visible);
+          g_signal_handlers_unblock_by_func
+            (G_OBJECT (wtoggle),
+             G_CALLBACK (xg_toggle_visibility_cb),
+             gobject);
+        }
+      x_gtk_show_hidden_files = visible;
+    }
+}
+
 /* Read a file name from the user using a file chooser dialog.
    F is the current frame.
    PROMPT is a prompt to show to the user.  May not be NULL.
@@ -1218,11 +1288,14 @@ xg_get_file_with_chooser (f, prompt, default_filename,
      int mustmatch_p, only_dir_p;
      xg_get_file_func *func;
 {
-  GtkWidget *filewin;
+  char message[1024];
+
+  GtkWidget *filewin, *wtoggle, *wbox, *wmessage;
   GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
   GtkFileChooserAction action = (mustmatch_p ?
                                  GTK_FILE_CHOOSER_ACTION_OPEN :
                                  GTK_FILE_CHOOSER_ACTION_SAVE);
+  extern int x_gtk_show_hidden_files;
 
   if (only_dir_p)
     action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
@@ -1235,6 +1308,33 @@ xg_get_file_with_chooser (f, prompt, default_filename,
                                          NULL);
   gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (filewin), TRUE);
 
+  wbox = gtk_vbox_new (FALSE, 0);
+  gtk_widget_show (wbox);
+  wtoggle = gtk_check_button_new_with_label ("Show hidden files.");
+  
+  if (x_gtk_show_hidden_files) 
+    {
+      g_object_set (G_OBJECT (filewin), "show-hidden", TRUE, NULL);
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wtoggle), TRUE);
+    }
+  gtk_widget_show (wtoggle);
+  g_signal_connect (G_OBJECT (wtoggle), "clicked",
+                    G_CALLBACK (xg_toggle_visibility_cb), filewin);
+  g_signal_connect (G_OBJECT (filewin), "notify",
+                    G_CALLBACK (xg_toggle_notify_cb), wtoggle);
+
+  message[0] = '\0';
+  if (action != GTK_FILE_CHOOSER_ACTION_SAVE)
+    strcat (message, "\nType C-l to display a file name text entry box.\n");
+  strcat (message, "\nIf you don't like this file selector, customize "
+          "use-file-dialog\nto turn it off, or type C-x C-f to visit files.");
+
+  wmessage = gtk_label_new (message);
+  gtk_widget_show (wmessage);
+  gtk_box_pack_start (GTK_BOX (wbox), wtoggle, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (wbox), wmessage, FALSE, FALSE, 0);
+  gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (filewin), wbox);
+
   if (default_filename)
     {
       Lisp_Object file;
@@ -1343,7 +1443,6 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
   char *fn = 0;
   int filesel_done = 0;
   xg_get_file_func func;
-  extern int x_use_old_gtk_file_dialog;
 
 #if defined (HAVE_GTK_AND_PTHREAD) && defined (__SIGRTMIN)
   /* I really don't know why this is needed, but without this the GLIBC add on
@@ -1354,7 +1453,7 @@ xg_get_file_name (f, prompt, default_filename, mustmatch_p, only_dir_p)
 
 #ifdef HAVE_GTK_FILE_BOTH
 
-  if (x_use_old_gtk_file_dialog)
+  if (xg_uses_old_file_dialog ())
     w = xg_get_file_with_selection (f, prompt, default_filename,
                                     mustmatch_p, only_dir_p, &func);
   else
@@ -3139,11 +3238,28 @@ xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole)
    the GtkImage with a new image.  */
 #define XG_TOOL_BAR_IMAGE_DATA "emacs-tool-bar-image"
 
+/* The key for storing the latest modifiers so the activate callback can
+   get them.  */
+#define XG_TOOL_BAR_LAST_MODIFIER "emacs-tool-bar-modifier"
+
+
 /* Callback function invoked when a tool bar item is pressed.
    W is the button widget in the tool bar that got pressed,
    CLIENT_DATA is an integer that is the index of the button in the
    tool bar.  0 is the first button.  */
 
+static gboolean
+xg_tool_bar_button_cb (widget, event, user_data)
+    GtkWidget      *widget;
+    GdkEventButton *event;
+    gpointer        user_data;
+{
+  g_object_set_data (G_OBJECT (user_data), XG_TOOL_BAR_LAST_MODIFIER,
+                     (gpointer) event->state);
+  return FALSE;
+}
+
+
 static void
 xg_tool_bar_callback (w, client_data)
      GtkWidget *w;
@@ -3151,6 +3267,8 @@ xg_tool_bar_callback (w, client_data)
 {
   /* The EMACS_INT cast avoids a warning. */
   int idx = (int) (EMACS_INT) client_data;
+  int mod = (int) g_object_get_data (G_OBJECT (w), XG_TOOL_BAR_LAST_MODIFIER);
+
   FRAME_PTR f = (FRAME_PTR) g_object_get_data (G_OBJECT (w), XG_FRAME_DATA);
   Lisp_Object key, frame;
   struct input_event event;
@@ -3171,7 +3289,10 @@ xg_tool_bar_callback (w, client_data)
   event.kind = TOOL_BAR_EVENT;
   event.frame_or_window = frame;
   event.arg = key;
-  event.modifiers = 0;  /* These are not available.  */
+  /* Convert between the modifier bits GDK uses and the modifier bits
+     Emacs uses.  This assumes GDK an X masks are the same, which they are when
+     this is written.  */
+  event.modifiers = x_x_to_emacs_modifiers (FRAME_X_DISPLAY_INFO (f), mod);
   kbd_buffer_store_event (&event);
 }
 
@@ -3189,6 +3310,10 @@ xg_tool_bar_detach_callback (wbox, w, client_data)
      gpointer client_data;
 {
   FRAME_PTR f = (FRAME_PTR) client_data;
+  extern int x_gtk_whole_detached_tool_bar;
+
+  g_object_set (G_OBJECT (w), "show-arrow", !x_gtk_whole_detached_tool_bar,
+               NULL);
 
   if (f)
     {
@@ -3219,6 +3344,7 @@ xg_tool_bar_attach_callback (wbox, w, client_data)
      gpointer client_data;
 {
   FRAME_PTR f = (FRAME_PTR) client_data;
+  g_object_set (G_OBJECT (w), "show-arrow", TRUE, NULL);
 
   if (f)
     {
@@ -3256,9 +3382,7 @@ xg_tool_bar_help_callback (w, event, client_data)
   Lisp_Object help, frame;
 
   if (! GTK_IS_BUTTON (w))
-    {
-      return FALSE;
-    }
+    return FALSE;
 
   if (! f || ! f->n_tool_bar_items || NILP (f->tool_bar_items))
     return FALSE;
@@ -3493,54 +3617,63 @@ update_frame_tool_bar (f)
       if (! wicon)
         {
           GtkWidget *w = xg_get_image_for_pixmap (f, img, x->widget, NULL);
+          GtkToolItem *ti = gtk_tool_button_new (w, "");
 
           gtk_misc_set_padding (GTK_MISC (w), hmargin, vmargin);
 
+          gtk_toolbar_insert (GTK_TOOLBAR (x->toolbar_widget),
+                              ti,
+                              i);
           /* The EMACS_INT cast avoids a warning. */
-          gtk_toolbar_append_item (GTK_TOOLBAR (x->toolbar_widget),
-                                   0, 0, 0,
-                                   w,
-                                   GTK_SIGNAL_FUNC (xg_tool_bar_callback),
-                                   (gpointer) (EMACS_INT) i);
+          g_signal_connect (GTK_WIDGET (ti), "clicked",
+                            GTK_SIGNAL_FUNC (xg_tool_bar_callback),
+                            (gpointer) (EMACS_INT) i);
+
+          gtk_widget_show (GTK_WIDGET (ti));
+          gtk_widget_show (GTK_WIDGET (w));
 
           /* Save the image so we can see if an update is needed when
              this function is called again.  */
           g_object_set_data (G_OBJECT (w), XG_TOOL_BAR_IMAGE_DATA,
                              (gpointer)img->pixmap);
 
+          g_object_set_data (G_OBJECT (ti), XG_FRAME_DATA, (gpointer)f);
+
           /* Catch expose events to overcome an annoying redraw bug, see
              comment for xg_tool_bar_item_expose_callback.  */
-          g_signal_connect (G_OBJECT (w),
+          g_signal_connect (G_OBJECT (ti),
                             "expose-event",
                             G_CALLBACK (xg_tool_bar_item_expose_callback),
                             0);
 
-          /* We must set sensitive on the button that is the parent
-             of the GtkImage parent.  Go upwards until we find the button.  */
+          gtk_widget_set_sensitive (GTK_WIDGET (ti), enabled_p);
+          gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (ti), FALSE);
+          
           while (! GTK_IS_BUTTON (w))
             w = gtk_widget_get_parent (w);
 
-          if (w)
-            {
-              /* Save the frame in the button so the xg_tool_bar_callback
-                 can get at it.  */
-              g_object_set_data (G_OBJECT (w), XG_FRAME_DATA, (gpointer)f);
-              gtk_widget_set_sensitive (w, enabled_p);
-
-              /* Use enter/leave notify to show help.  We use the events
-                 rather than the GtkButton specific signals "enter" and
-                 "leave", so we can have only one callback.  The event
-                 will tell us what kind of event it is.  */
-              /* The EMACS_INT cast avoids a warning. */
-              g_signal_connect (G_OBJECT (w),
-                                "enter-notify-event",
-                                G_CALLBACK (xg_tool_bar_help_callback),
-                                (gpointer) (EMACS_INT) i);
-              g_signal_connect (G_OBJECT (w),
-                                "leave-notify-event",
-                                G_CALLBACK (xg_tool_bar_help_callback),
-                                (gpointer) (EMACS_INT) i);
-            }
+          /* Callback to save modifyer mask (Shift/Control, etc).  GTK makes
+             no distinction based on modifiers in the activate callback,
+             so we have to do it ourselves.  */
+          g_signal_connect (w, "button-release-event",
+                            GTK_SIGNAL_FUNC (xg_tool_bar_button_cb),
+                            ti);
+
+          g_object_set_data (G_OBJECT (w), XG_FRAME_DATA, (gpointer)f);
+
+          /* Use enter/leave notify to show help.  We use the events
+             rather than the GtkButton specific signals "enter" and
+             "leave", so we can have only one callback.  The event
+             will tell us what kind of event it is.  */
+          /* The EMACS_INT cast avoids a warning. */
+          g_signal_connect (G_OBJECT (w),
+                            "enter-notify-event",
+                            G_CALLBACK (xg_tool_bar_help_callback),
+                            (gpointer) (EMACS_INT) i);
+          g_signal_connect (G_OBJECT (w),
+                            "leave-notify-event",
+                            G_CALLBACK (xg_tool_bar_help_callback),
+                            (gpointer) (EMACS_INT) i);
         }
       else
         {