home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 February / CMCD0205.ISO / Linux / gimp-2.2.0.tar.gz / gimp-2.2.0.tar / gimp-2.2.0 / libgimpwidgets / gimpdialog.c < prev    next >
C/C++ Source or Header  |  2004-12-14  |  16KB  |  540 lines

  1. /* LIBGIMP - The GIMP Library
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * gimpdialog.c
  5.  * Copyright (C) 2000-2003 Michael Natterer <mitch@gimp.org>
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Library General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the
  19.  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20.  * Boston, MA 02111-1307, USA.
  21.  */
  22.  
  23. #include "config.h"
  24.  
  25. #include <gtk/gtk.h>
  26.  
  27. #include "gimpwidgetstypes.h"
  28.  
  29. #include "gimpdialog.h"
  30. #include "gimphelpui.h"
  31.  
  32.  
  33. enum
  34. {
  35.   PROP_0,
  36.   PROP_HELP_FUNC,
  37.   PROP_HELP_ID
  38. };
  39.  
  40.  
  41. static void       gimp_dialog_class_init   (GimpDialogClass *klass);
  42. static void       gimp_dialog_init         (GimpDialog      *dialog);
  43.  
  44. static GObject  * gimp_dialog_constructor  (GType            type,
  45.                                             guint            n_params,
  46.                                             GObjectConstructParam *params);
  47. static void       gimp_dialog_set_property (GObject         *object,
  48.                                             guint            property_id,
  49.                                             const GValue    *value,
  50.                                             GParamSpec      *pspec);
  51. static void       gimp_dialog_get_property (GObject         *object,
  52.                                             guint            property_id,
  53.                                             GValue          *value,
  54.                                             GParamSpec      *pspec);
  55.  
  56. static gboolean   gimp_dialog_delete_event (GtkWidget       *widget,
  57.                                             GdkEventAny     *event);
  58. static void       gimp_dialog_close        (GtkDialog       *dialog);
  59. static void       gimp_dialog_help         (GObject         *dialog);
  60.  
  61.  
  62. static GtkDialogClass *parent_class     = NULL;
  63. static gboolean        show_help_button = TRUE;
  64.  
  65.  
  66. GType
  67. gimp_dialog_get_type (void)
  68. {
  69.   static GType dialog_type = 0;
  70.  
  71.   if (! dialog_type)
  72.     {
  73.       static const GTypeInfo dialog_info =
  74.       {
  75.         sizeof (GimpDialogClass),
  76.         (GBaseInitFunc) NULL,
  77.         (GBaseFinalizeFunc) NULL,
  78.         (GClassInitFunc) gimp_dialog_class_init,
  79.         NULL,           /* class_finalize */
  80.         NULL,           /* class_data     */
  81.         sizeof (GimpDialog),
  82.         0,              /* n_preallocs    */
  83.         (GInstanceInitFunc) gimp_dialog_init,
  84.       };
  85.  
  86.       dialog_type = g_type_register_static (GTK_TYPE_DIALOG,
  87.                                             "GimpDialog",
  88.                                             &dialog_info, 0);
  89.     }
  90.  
  91.   return dialog_type;
  92. }
  93.  
  94. static void
  95. gimp_dialog_class_init (GimpDialogClass *klass)
  96. {
  97.   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
  98.   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  99.   GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
  100.  
  101.   parent_class = g_type_class_peek_parent (klass);
  102.  
  103.   object_class->constructor  = gimp_dialog_constructor;
  104.   object_class->set_property = gimp_dialog_set_property;
  105.   object_class->get_property = gimp_dialog_get_property;
  106.  
  107.   widget_class->delete_event = gimp_dialog_delete_event;
  108.  
  109.   dialog_class->close        = gimp_dialog_close;
  110.  
  111.   /**
  112.    * GimpDialog::help-func:
  113.    *
  114.    * Since: GIMP 2.2
  115.    **/
  116.   g_object_class_install_property (object_class, PROP_HELP_FUNC,
  117.                                    g_param_spec_pointer ("help-func", NULL, NULL,
  118.                                                          G_PARAM_READWRITE |
  119.                                                          G_PARAM_CONSTRUCT_ONLY));
  120.  
  121.   /**
  122.    * GimpDialog::help-id:
  123.    *
  124.    * Since: GIMP 2.2
  125.    **/
  126.   g_object_class_install_property (object_class, PROP_HELP_ID,
  127.                                    g_param_spec_string ("help-id", NULL, NULL,
  128.                                                         NULL,
  129.                                                         G_PARAM_READWRITE |
  130.                                                         G_PARAM_CONSTRUCT_ONLY));
  131. }
  132.  
  133. static GObject *
  134. gimp_dialog_constructor (GType                  type,
  135.                          guint                  n_params,
  136.                          GObjectConstructParam *params)
  137. {
  138.   GObject      *object;
  139.   GimpHelpFunc  help_func;
  140.   const gchar  *help_id;
  141.  
  142.   object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
  143.  
  144.   help_func = g_object_get_data (object, "gimp-dialog-help-func");
  145.   help_id   = g_object_get_data (object, "gimp-dialog-help-id");
  146.  
  147.   if (help_func)
  148.     gimp_help_connect (GTK_WIDGET (object), help_func, help_id, object);
  149.  
  150.   if (show_help_button && help_func && help_id)
  151.     {
  152.       GtkDialog *dialog = GTK_DIALOG (object);
  153.       GtkWidget *button = gtk_button_new_from_stock (GTK_STOCK_HELP);
  154.  
  155.       gtk_box_pack_end (GTK_BOX (dialog->action_area), button, FALSE, TRUE, 0);
  156.       gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (dialog->action_area),
  157.                                           button, TRUE);
  158.       gtk_widget_show (button);
  159.  
  160.       g_signal_connect_object (button, "clicked",
  161.                                G_CALLBACK (gimp_dialog_help),
  162.                                dialog, G_CONNECT_SWAPPED);
  163.  
  164.       g_object_set_data (object, "gimp-dialog-help-button", button);
  165.     }
  166.  
  167.   return object;
  168. }
  169.  
  170. static void
  171. gimp_dialog_set_property (GObject      *object,
  172.                           guint         property_id,
  173.                           const GValue *value,
  174.                           GParamSpec   *pspec)
  175. {
  176.   switch (property_id)
  177.     {
  178.     case PROP_HELP_FUNC:
  179.       g_object_set_data (object, "gimp-dialog-help-func",
  180.                          g_value_get_pointer (value));
  181.       break;
  182.     case PROP_HELP_ID:
  183.       g_object_set_data_full (object, "gimp-dialog-help-id",
  184.                               g_value_dup_string (value),
  185.                               (GDestroyNotify) g_free);
  186.       break;
  187.     default:
  188.       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  189.       break;
  190.     }
  191. }
  192.  
  193. static void
  194. gimp_dialog_get_property (GObject    *object,
  195.                           guint       property_id,
  196.                           GValue     *value,
  197.                           GParamSpec *pspec)
  198. {
  199.   switch (property_id)
  200.     {
  201.     case PROP_HELP_FUNC:
  202.       g_value_set_pointer (value, g_object_get_data (object,
  203.                                                      "gimp-dialog-help-func"));
  204.       break;
  205.     case PROP_HELP_ID:
  206.       g_value_set_string (value, g_object_get_data (object,
  207.                                                     "gimp-dialog-help-id"));
  208.       break;
  209.     default:
  210.       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  211.       break;
  212.     }
  213. }
  214.  
  215. static void
  216. gimp_dialog_init (GimpDialog *dialog)
  217. {
  218.   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
  219. }
  220.  
  221. static gboolean
  222. gimp_dialog_delete_event (GtkWidget   *widget,
  223.                           GdkEventAny *event)
  224. {
  225.   return TRUE;
  226. }
  227.  
  228. static void
  229. gimp_dialog_close (GtkDialog *dialog)
  230. {
  231.   /* Synthesize delete_event to close dialog. */
  232.  
  233.   GtkWidget *widget = GTK_WIDGET (dialog);
  234.  
  235.   if (widget->window)
  236.     {
  237.       GdkEvent *event = gdk_event_new (GDK_DELETE);
  238.  
  239.       event->any.window     = g_object_ref (widget->window);
  240.       event->any.send_event = TRUE;
  241.  
  242.       gtk_main_do_event (event);
  243.       gdk_event_free (event);
  244.     }
  245. }
  246.  
  247. static void
  248. gimp_dialog_help (GObject *dialog)
  249. {
  250.   GimpHelpFunc  help_func = g_object_get_data (dialog, "gimp-dialog-help-func");
  251.  
  252.   if (help_func)
  253.     help_func (g_object_get_data (dialog, "gimp-dialog-help-id"), dialog);
  254. }
  255.  
  256.  
  257. /**
  258.  * gimp_dialog_new:
  259.  * @title:        The dialog's title which will be set with
  260.  *                gtk_window_set_title().
  261.  * @role:         The dialog's @role which will be set with
  262.  *                gtk_window_set_role().
  263.  * @parent:       The @parent widget of this dialog.
  264.  * @flags:        The @flags (see the #GtkDialog documentation).
  265.  * @help_func:    The function which will be called if the user presses "F1".
  266.  * @help_id:      The help_id which will be passed to @help_func.
  267.  * @...:          A %NULL-terminated @va_list destribing the
  268.  *                action_area buttons.
  269.  *
  270.  * Creates a new @GimpDialog widget.
  271.  *
  272.  * This function simply packs the action_area arguments passed in "..."
  273.  * into a @va_list variable and passes everything to gimp_dialog_new_valist().
  274.  *
  275.  * For a description of the format of the @va_list describing the
  276.  * action_area buttons see gtk_dialog_new_with_buttons().
  277.  *
  278.  * Returns: A #GimpDialog.
  279.  **/
  280. GtkWidget *
  281. gimp_dialog_new (const gchar    *title,
  282.                  const gchar    *role,
  283.                  GtkWidget      *parent,
  284.                  GtkDialogFlags  flags,
  285.                  GimpHelpFunc    help_func,
  286.                  const gchar    *help_id,
  287.                  ...)
  288. {
  289.   GtkWidget *dialog;
  290.   va_list    args;
  291.  
  292.   g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
  293.   g_return_val_if_fail (title != NULL, NULL);
  294.   g_return_val_if_fail (role != NULL, NULL);
  295.  
  296.   va_start (args, help_id);
  297.  
  298.   dialog = gimp_dialog_new_valist (title, role,
  299.                                    parent, flags,
  300.                                    help_func, help_id,
  301.                                    args);
  302.  
  303.   va_end (args);
  304.  
  305.   return dialog;
  306. }
  307.  
  308. /**
  309.  * gimp_dialog_new_valist:
  310.  * @title:        The dialog's title which will be set with
  311.  *                gtk_window_set_title().
  312.  * @role:         The dialog's @role which will be set with
  313.  *                gtk_window_set_role().
  314.  * @parent:       The @parent widget of this dialog or %NULL.
  315.  * @flags:        The @flags (see the #GtkDialog documentation).
  316.  * @help_func:    The function which will be called if the user presses "F1".
  317.  * @help_id:      The help_id which will be passed to @help_func.
  318.  * @args:         A @va_list destribing the action_area buttons.
  319.  *
  320.  * Creates a new @GimpDialog widget. If a GtkWindow is specified as
  321.  * @parent then the dialog will be made transient for this window.
  322.  *
  323.  * For a description of the format of the @va_list describing the
  324.  * action_area buttons see gtk_dialog_new_with_buttons().
  325.  *
  326.  * Returns: A #GimpDialog.
  327.  **/
  328. GtkWidget *
  329. gimp_dialog_new_valist (const gchar    *title,
  330.                         const gchar    *role,
  331.                         GtkWidget      *parent,
  332.                         GtkDialogFlags  flags,
  333.                         GimpHelpFunc    help_func,
  334.                         const gchar    *help_id,
  335.                         va_list         args)
  336. {
  337.   GtkWidget *dialog;
  338.  
  339.   g_return_val_if_fail (title != NULL, NULL);
  340.   g_return_val_if_fail (role != NULL, NULL);
  341.   g_return_val_if_fail (parent == NULL || GTK_IS_WIDGET (parent), NULL);
  342.  
  343.   dialog = g_object_new (GIMP_TYPE_DIALOG,
  344.                          "title",     title,
  345.                          "role",      role,
  346.                          "modal",     (flags & GTK_DIALOG_MODAL),
  347.                          "help-func", help_func,
  348.                          "help-id",   help_id,
  349.                          NULL);
  350.  
  351.   if (parent)
  352.     {
  353.       if (GTK_IS_WINDOW (parent))
  354.         {
  355.           gtk_window_set_transient_for (GTK_WINDOW (dialog),
  356.                                         GTK_WINDOW (parent));
  357.         }
  358.       else
  359.         {
  360.           gtk_window_set_screen (GTK_WINDOW (dialog),
  361.                                  gtk_widget_get_screen (parent));
  362.         }
  363.  
  364.       if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
  365.         g_signal_connect_object (parent, "destroy",
  366.                                  G_CALLBACK (gimp_dialog_close),
  367.                                  dialog, G_CONNECT_SWAPPED);
  368.     }
  369.  
  370.   gimp_dialog_add_buttons_valist (GIMP_DIALOG (dialog), args);
  371.  
  372.   return dialog;
  373. }
  374.  
  375. /**
  376.  * gimp_dialog_add_buttons_valist:
  377.  * @dialog: The @dialog to add buttons to.
  378.  * @args:   The buttons as va_list.
  379.  *
  380.  * This function is essentially the same as gtk_dialog_add_buttons()
  381.  * except it takes a va_list instead of '...'
  382.  **/
  383. void
  384. gimp_dialog_add_buttons_valist (GimpDialog *dialog,
  385.                                 va_list     args)
  386. {
  387.   const gchar *button_text;
  388.   gint         response_id;
  389.  
  390.   g_return_if_fail (GIMP_IS_DIALOG (dialog));
  391.  
  392.   while ((button_text = va_arg (args, const gchar *)))
  393.     {
  394.       response_id = va_arg (args, gint);
  395.  
  396.       /*  hide the automatically added help button if another one is added  */
  397.       if (response_id == GTK_RESPONSE_HELP)
  398.         {
  399.           GtkWidget *button = g_object_get_data (G_OBJECT (dialog),
  400.                                                  "gimp-dialog-help-button");
  401.           gtk_widget_hide (button);
  402.         }
  403.  
  404.       gtk_dialog_add_button (GTK_DIALOG (dialog), button_text, response_id);
  405.  
  406.       if (response_id == GTK_RESPONSE_OK)
  407.         {
  408.           gtk_dialog_set_default_response (GTK_DIALOG (dialog),
  409.                                            GTK_RESPONSE_OK);
  410.         }
  411.     }
  412. }
  413.  
  414.  
  415. typedef struct
  416. {
  417.   GtkDialog *dialog;
  418.   gint       response_id;
  419.   GMainLoop *loop;
  420.   gboolean   destroyed;
  421. } RunInfo;
  422.  
  423. static void
  424. run_shutdown_loop (RunInfo *ri)
  425. {
  426.   if (g_main_loop_is_running (ri->loop))
  427.     g_main_loop_quit (ri->loop);
  428. }
  429.  
  430. static void
  431. run_unmap_handler (GtkDialog *dialog,
  432.                    RunInfo   *ri)
  433. {
  434.   run_shutdown_loop (ri);
  435. }
  436.  
  437. static void
  438. run_response_handler (GtkDialog *dialog,
  439.                       gint       response_id,
  440.                       RunInfo   *ri)
  441. {
  442.   ri->response_id = response_id;
  443.  
  444.   run_shutdown_loop (ri);
  445. }
  446.  
  447. static gint
  448. run_delete_handler (GtkDialog   *dialog,
  449.                     GdkEventAny *event,
  450.                     RunInfo     *ri)
  451. {
  452.   run_shutdown_loop (ri);
  453.  
  454.   return TRUE; /* Do not destroy */
  455. }
  456.  
  457. static void
  458. run_destroy_handler (GtkDialog *dialog,
  459.                      RunInfo   *ri)
  460. {
  461.   /* shutdown_loop will be called by run_unmap_handler */
  462.  
  463.   ri->destroyed = TRUE;
  464. }
  465.  
  466. /**
  467.  * gimp_dialog_run:
  468.  * @dialog: a #GimpDialog
  469.  *
  470.  * This function does exactly the same as gtk_dialog_run() except it
  471.  * does not make the dialog modal while the #GMainLoop is running.
  472.  *
  473.  * Return value: response ID
  474.  **/
  475. gint
  476. gimp_dialog_run (GimpDialog *dialog)
  477. {
  478.   RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
  479.   gulong  response_handler;
  480.   gulong  unmap_handler;
  481.   gulong  destroy_handler;
  482.   gulong  delete_handler;
  483.  
  484.   g_return_val_if_fail (GIMP_IS_DIALOG (dialog), -1);
  485.  
  486.   g_object_ref (dialog);
  487.  
  488.   gtk_window_present (GTK_WINDOW (dialog));
  489.  
  490.   response_handler = g_signal_connect (dialog, "response",
  491.                                        G_CALLBACK (run_response_handler),
  492.                                        &ri);
  493.   unmap_handler    = g_signal_connect (dialog, "unmap",
  494.                                        G_CALLBACK (run_unmap_handler),
  495.                                        &ri);
  496.   delete_handler   = g_signal_connect (dialog, "delete_event",
  497.                                        G_CALLBACK (run_delete_handler),
  498.                                        &ri);
  499.   destroy_handler  = g_signal_connect (dialog, "destroy",
  500.                                        G_CALLBACK (run_destroy_handler),
  501.                                        &ri);
  502.  
  503.   ri.loop = g_main_loop_new (NULL, FALSE);
  504.  
  505.   GDK_THREADS_LEAVE ();
  506.   g_main_loop_run (ri.loop);
  507.   GDK_THREADS_ENTER ();
  508.  
  509.   g_main_loop_unref (ri.loop);
  510.  
  511.   ri.loop      = NULL;
  512.   ri.destroyed = FALSE;
  513.  
  514.   if (!ri.destroyed)
  515.     {
  516.       g_signal_handler_disconnect (dialog, response_handler);
  517.       g_signal_handler_disconnect (dialog, unmap_handler);
  518.       g_signal_handler_disconnect (dialog, delete_handler);
  519.       g_signal_handler_disconnect (dialog, destroy_handler);
  520.     }
  521.  
  522.   g_object_unref (dialog);
  523.  
  524.   return ri.response_id;
  525. }
  526.  
  527. /**
  528.  * gimp_dialogs_show_help_button:
  529.  * @show: whether a help button should be added when creating a GimpDialog
  530.  *
  531.  * This function is for internal use only.
  532.  *
  533.  * Since: GIMP 2.2
  534.  **/
  535. void
  536. gimp_dialogs_show_help_button (gboolean  show)
  537. {
  538.   show_help_button = show ? TRUE : FALSE;
  539. }
  540.