metacity-dialog.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity dialog process */
00004 
00005 /* 
00006  * Copyright (C) 2001 Havoc Pennington
00007  * Copyright (C) 2004 Elijah Newren
00008  * 
00009  * This program is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU General Public License as
00011  * published by the Free Software Foundation; either version 2 of the
00012  * License, or (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful, but
00015  * WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * General Public License for more details.
00018  * 
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00022  * 02111-1307, USA.
00023  */
00024 
00025 #include <config.h>
00026 #include <gtk/gtk.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <unistd.h>
00030 
00031 #include <libintl.h>
00032 #define _(x) dgettext (GETTEXT_PACKAGE, x)
00033 #define N_(x) x
00034 
00035 #include <gdk/gdkx.h>
00036 #include <X11/Xatom.h>
00037 
00038 static Window
00039 window_from_string (const char *str)
00040 {
00041   char *end;
00042   unsigned long l;
00043 
00044   end = NULL;
00045   
00046   l = strtoul (str, &end, 16);
00047 
00048   if (end == NULL || end == str)
00049     {
00050       g_printerr (_("Could not parse \"%s\" as an integer"),
00051                   str);
00052       return None;
00053     }
00054 
00055   if (*end != '\0')
00056     {
00057       g_printerr (_("Did not understand trailing characters \"%s\" in string \"%s\""),
00058                   end, str);
00059       return None;
00060     }
00061 
00062   return l;
00063 }
00064 
00065 static void
00066 on_realize (GtkWidget *dialog,
00067             void      *data)
00068 {
00069   const char *parent_str = data;
00070   Window xwindow;
00071 
00072   xwindow = window_from_string (parent_str);
00073 
00074   gdk_error_trap_push ();
00075   XSetTransientForHint (gdk_display, GDK_WINDOW_XID (dialog->window),
00076                         xwindow);
00077   XSync (gdk_display, False);
00078   gdk_error_trap_pop ();
00079 }
00080 
00081 static int
00082 kill_window_question (const char *window_name,
00083                       const char *parent_str,
00084                       guint32     timestamp)
00085 {
00086   GtkWidget *dialog;
00087   char *str, *tmp;
00088 
00089   tmp = g_markup_escape_text (window_name, -1);
00090   str = g_strdup_printf (_("\"%s\" is not responding."), tmp);
00091   g_free (tmp);
00092   dialog = gtk_message_dialog_new (NULL, 0,
00093                                    GTK_MESSAGE_WARNING,
00094                                    GTK_BUTTONS_NONE,
00095                                    "<big><b>%s</b></big>\n\n<i>%s</i>",
00096                                    str,
00097                                    _("You may choose to wait a short while "
00098                                    "for it to continue or force the application "
00099                                    "to quit entirely."));
00100   g_free (str);
00101   gtk_window_set_icon_name (GTK_WINDOW (dialog), "stock_dialog-warning");
00102 
00103   gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE);
00104   gtk_label_set_line_wrap (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE);
00105   
00106   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
00107                           _("_Wait"),
00108                           GTK_RESPONSE_REJECT,
00109                           _("_Force Quit"),
00110                           GTK_RESPONSE_ACCEPT,
00111                           NULL);
00112   
00113   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT);
00114 
00115   g_signal_connect (G_OBJECT (dialog), "realize",
00116                     G_CALLBACK (on_realize), (char*) parent_str);
00117   
00118   gtk_widget_realize (dialog);
00119   gdk_x11_window_set_user_time (dialog->window, timestamp);
00120 
00121   /* return our PID, then window ID that should be killed */
00122   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
00123     g_print ("%d\n%s\n", (int) getpid (), parent_str);
00124   else
00125     g_print ("%d\n0x0\n", (int) getpid ());
00126 
00127   return 0;
00128 }
00129 
00130 static char*
00131 latin1_to_utf8 (const char *text)
00132 {
00133   GString *str;
00134   const char *p;
00135   
00136   str = g_string_new ("");
00137 
00138   p = text;
00139   while (*p)
00140     {
00141       g_string_append_unichar (str, *p);
00142       ++p;
00143     }
00144 
00145   return g_string_free (str, FALSE);
00146 }
00147 
00148 enum
00149 {
00150   COLUMN_TITLE,
00151   COLUMN_CLASS,
00152   COLUMN_LAST
00153 };
00154 
00155 static GtkWidget*
00156 create_lame_apps_list (char **lame_apps)
00157 {
00158   GtkTreeSelection *selection;
00159   GtkCellRenderer *cell;
00160   GtkWidget *tree_view;
00161   GtkTreeViewColumn *column;
00162   GtkListStore *model;
00163   GtkTreeIter iter;
00164   int i;
00165   
00166   model = gtk_list_store_new (COLUMN_LAST,
00167                               G_TYPE_STRING,
00168                               G_TYPE_STRING);
00169   
00170   tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
00171 
00172   g_object_unref (G_OBJECT (model));
00173   
00174   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
00175 
00176   gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
00177                                GTK_SELECTION_NONE);
00178 
00179   i = 0;
00180   while (lame_apps[i])
00181     {
00182       char *s;
00183       
00184       gtk_list_store_append (model, &iter);
00185 
00186       /* window class is latin-1 */
00187       s = latin1_to_utf8 (lame_apps[i+1]);
00188       
00189       gtk_list_store_set (model,
00190                           &iter,
00191                           COLUMN_TITLE, lame_apps[i],
00192                           COLUMN_CLASS, s,
00193                           -1);
00194 
00195       g_free (s);
00196       
00197       i += 2;
00198     }
00199   
00200   cell = gtk_cell_renderer_text_new ();
00201   
00202   g_object_set (G_OBJECT (cell),
00203                 "xpad", 2,
00204                 NULL);
00205   
00206   column = gtk_tree_view_column_new_with_attributes (_("Title"),
00207                                                      cell,
00208                                                      "text", COLUMN_TITLE,
00209                                                      NULL);
00210 
00211   gtk_tree_view_column_set_sort_column_id (column, COLUMN_TITLE);
00212   
00213   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
00214                                GTK_TREE_VIEW_COLUMN (column));
00215 
00216   cell = gtk_cell_renderer_text_new ();
00217   
00218   column = gtk_tree_view_column_new_with_attributes (_("Class"),
00219                                                      cell,
00220                                                      "text", COLUMN_CLASS,
00221                                                      NULL);
00222 
00223   gtk_tree_view_column_set_sort_column_id (column, COLUMN_CLASS);
00224   
00225   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
00226                                GTK_TREE_VIEW_COLUMN (column));
00227 
00228   return tree_view;
00229 }
00230 
00231 static int
00232 warn_about_no_sm_support (char    **lame_apps,
00233                           guint32   timestamp)
00234 {
00235   GtkWidget *dialog;
00236   GtkWidget *list;
00237   GtkWidget *sw;
00238   GtkWidget *button;
00239       
00240   dialog = gtk_message_dialog_new (NULL,
00241                                    0,
00242                                    GTK_MESSAGE_WARNING,
00243                                    GTK_BUTTONS_NONE,
00244                                    _("These windows do not support \"save current setup\" and will have to be restarted manually next time you log in."));
00245   gtk_window_set_icon_name (GTK_WINDOW (dialog), "stock_dialog-warning");
00246   
00247   g_signal_connect (G_OBJECT (dialog),
00248                     "response",
00249                     G_CALLBACK (gtk_main_quit),
00250                     NULL);
00251 
00252   /* Wait 4 minutes then force quit, so we don't wait around all night */
00253   g_timeout_add (4 * 60 * 1000, (GSourceFunc) gtk_main_quit, NULL);
00254 
00255   button = gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
00256   gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
00257   list = create_lame_apps_list (lame_apps);
00258 
00259   sw = gtk_scrolled_window_new (NULL, NULL);
00260   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
00261                                   GTK_POLICY_AUTOMATIC,
00262                                   GTK_POLICY_AUTOMATIC);
00263   gtk_container_set_border_width (GTK_CONTAINER (sw), 3);
00264       
00265   gtk_container_add (GTK_CONTAINER (sw), list);
00266 
00267   /* sw as geometry widget */
00268   gtk_window_set_geometry_hints (GTK_WINDOW (dialog),
00269                                  sw, NULL, 0);
00270 
00271   gtk_window_set_resizable (GTK_WINDOW(dialog), TRUE);
00272 
00273   /* applies to geometry widget; try to avoid scrollbars,
00274    * but don't make the window huge
00275    */
00276   gtk_window_set_default_size (GTK_WINDOW (dialog),
00277                                400, 225);
00278 
00279   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
00280                       sw,
00281                       TRUE, TRUE, 0);
00282   
00283   gtk_window_stick (GTK_WINDOW (dialog));
00284 
00285   gtk_widget_realize (dialog);
00286   gdk_x11_window_set_user_time (dialog->window, timestamp);
00287 
00288   gtk_widget_grab_focus (button);
00289   gtk_widget_show_all (dialog);
00290 
00291   gtk_main ();
00292   
00293   return 0;
00294 }
00295 
00296 static int
00297 error_about_command (const char *gconf_key,
00298                      const char *command,
00299                      const char *error,
00300                      guint32     timestamp)
00301 {
00302   GtkWidget *dialog;
00303 
00304   /* FIXME offer to change the value of the command's gconf key */
00305 
00306   if (*command != '\0')
00307     dialog = gtk_message_dialog_new (NULL, 0,
00308                                      GTK_MESSAGE_ERROR,
00309                                      GTK_BUTTONS_CLOSE,
00310                                      _("There was an error running \"%s\":\n"
00311                                        "%s."),
00312                                      command, error);
00313   else
00314     dialog = gtk_message_dialog_new (NULL, 0,
00315                                      GTK_MESSAGE_ERROR,
00316                                      GTK_BUTTONS_CLOSE,
00317                                      "%s", error);
00318   gtk_window_set_icon_name (GTK_WINDOW (dialog), "stock_dialog-error");
00319   
00320   gtk_widget_realize (dialog);
00321   gdk_x11_window_set_user_time (dialog->window, timestamp);
00322 
00323   gtk_dialog_run (GTK_DIALOG (dialog));
00324 
00325   gtk_widget_destroy (dialog);
00326   
00327   return 0;
00328 }
00329 
00330 static gchar *screen = NULL;
00331 static gchar *timestamp_string = NULL;
00332 static gboolean isset_kill_window_question = FALSE;
00333 static gboolean isset_warn_about_no_sm_support = FALSE;
00334 static gboolean isset_command_failed_error = FALSE;
00335 static gchar **remaining_args;
00336 
00337 static const GOptionEntry options[] = {
00338   { "screen", 0, 0, G_OPTION_ARG_STRING, &screen, NULL, NULL},
00339   { "timestamp", 0, 0, G_OPTION_ARG_STRING, &timestamp_string, NULL, NULL},
00340   { "kill-window-question", 'k', 0, G_OPTION_ARG_NONE, 
00341     &isset_kill_window_question, NULL, NULL},
00342   { "warn-about-no-sm-support", 'w', 0, G_OPTION_ARG_NONE, 
00343     &isset_warn_about_no_sm_support, NULL, NULL},
00344   { "command-failed-error", 'c', 0, G_OPTION_ARG_NONE, 
00345     &isset_command_failed_error, NULL, NULL},
00346   { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, 
00347     &remaining_args, NULL, NULL},
00348   { NULL}
00349 };
00350 
00351 int
00352 main (int argc, char **argv)
00353 {
00354   GOptionContext *ctx;
00355   guint32 timestamp = 0;
00356   gint num_args = 0;
00357 
00358   bindtextdomain (GETTEXT_PACKAGE, METACITY_LOCALEDIR);
00359   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
00360   textdomain (GETTEXT_PACKAGE);
00361 
00362   gtk_init (&argc, &argv);
00363 
00364   ctx = g_option_context_new ("- Dialogs for metacity. "
00365                           "This program is intented for use by metacity only.");
00366   g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
00367   g_option_context_parse (ctx, &argc, &argv, NULL);
00368   g_option_context_free (ctx);
00369     
00370   if (timestamp_string != NULL)
00371     {
00372       timestamp = strtoul (timestamp_string, NULL, 10);
00373     }
00374 
00375   if (remaining_args != NULL)
00376     {
00377       num_args = g_strv_length (remaining_args);
00378     }
00379 
00380   if ((isset_kill_window_question && isset_warn_about_no_sm_support) ||
00381       (isset_kill_window_question && isset_command_failed_error) ||
00382       (isset_warn_about_no_sm_support && isset_command_failed_error) ||
00383       timestamp == 0) 
00384     {
00385       g_printerr ("bad args to metacity-dialog\n");
00386       return 1;
00387     }
00388 
00389   else if (isset_kill_window_question)
00390     {
00391       if (num_args < 2)
00392         {
00393           g_printerr ("bad args to metacity-dialog\n");
00394           return 1;
00395         } 
00396       else 
00397         {
00398           return kill_window_question (remaining_args[0], 
00399                           remaining_args[1], timestamp);
00400         }
00401     }
00402 
00403   else if (isset_warn_about_no_sm_support)
00404     {
00405       /* argc must be even because we want title-class pairs */
00406       if (num_args == 0 || (num_args % 2) != 0)
00407         {
00408           g_printerr ("bad args to metacity-dialog\n");
00409           return 1;
00410         } 
00411       else 
00412         {
00413           return warn_about_no_sm_support (&remaining_args[0], timestamp);
00414         }
00415     }
00416 
00417   else if (isset_command_failed_error)
00418     {
00419       /* the args are the gconf key of the failed command, the text of
00420        * the command, and the error message
00421        */
00422       if (num_args != 3)
00423         {
00424           g_printerr ("bad args to metacity-dialog\n");
00425           return 1;
00426         } 
00427       else 
00428         {
00429           return error_about_command (remaining_args[0], 
00430                           remaining_args[1], remaining_args[2], timestamp);
00431         }
00432     }
00433   else
00434     {
00435       g_printerr ("bad args to metacity-dialog\n");
00436       return 1;
00437     }
00438 }

Generated on Sat Aug 23 22:04:18 2008 for metacity by  doxygen 1.5.5