/* moko-dialog.c * * Authored (in part) by Rob Bradford * * Copyright (C) 2007 OpenMoko Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser Public License as published by * the Free Software Foundation; version 2 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser Public License for more details. * * Also contains code directly derived from GTK+ (gtk/gtkdialog.c) with the * following Copyright notice: * * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. * * Derivation Copyright (C) 2007 OpenMoko Inc. * Derivation Authored by Rob Bradford G_DEFINE_TYPE (MokoDialog, moko_dialog, MOKO_TYPE_WINDOW) #define DIALOG_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), MOKO_TYPE_DIALOG, MokoDialogPrivate)) typedef struct _MokoDialogPrivate MokoDialogPrivate; struct _MokoDialogPrivate { GtkWidget *hbox; GtkWidget *label; GtkWidget *eventbox; GtkWidget *closebutton; gint response_id; GMainLoop *loop; gboolean destroyed; }; enum { RESPONSE, CLOSE, LAST_SIGNAL }; static guint moko_dialog_signals[LAST_SIGNAL]; static void moko_dialog_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void moko_dialog_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void moko_dialog_dispose (GObject *object) { if (G_OBJECT_CLASS (moko_dialog_parent_class)->dispose) G_OBJECT_CLASS (moko_dialog_parent_class)->dispose (object); } static void moko_dialog_finalize (GObject *object) { G_OBJECT_CLASS (moko_dialog_parent_class)->finalize (object); } static void moko_dialog_class_init (MokoDialogClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (MokoDialogPrivate)); object_class->get_property = moko_dialog_get_property; object_class->set_property = moko_dialog_set_property; object_class->dispose = moko_dialog_dispose; object_class->finalize = moko_dialog_finalize; moko_dialog_signals[RESPONSE] = g_signal_new (("response"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MokoDialogClass, response), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); } static void moko_dialog_init (MokoDialog *self) { MokoDialogPrivate* priv = DIALOG_PRIVATE(self); GtkWidget* parent; /* Most of this was borrowed from GTK */ /** * Set up a "Title Bar" - this should really be done in the window manager * theme... */ /* The primary vbox holds the contents of the dialog and the action_area */ self->vbox = gtk_vbox_new (FALSE, 6); gtk_container_add (GTK_CONTAINER (self), self->vbox); /* Create the hbox at the top */ priv->hbox = gtk_hbox_new(FALSE, 0); /* Add an eventbox to said hbox */ priv->eventbox = gtk_event_box_new (); gtk_box_pack_start (GTK_BOX (priv->hbox), priv->eventbox, TRUE, TRUE, 0); gtk_widget_set_name (priv->eventbox, "mokodialogwindow-title-labelbox"); /* Create the label for the top */ priv->label = gtk_label_new (NULL); gtk_widget_set_name (priv->label, "mokodialogwindow-title-label"); /* Add to the eventbox */ gtk_container_add (GTK_CONTAINER (priv->eventbox), priv->label); /* Add this hbox to the start of vbox */ gtk_box_pack_start (GTK_BOX (self->vbox), priv->hbox, FALSE, FALSE, 0); /** * Now get back to the proper parts of the dialog */ /* Create the action_area and put it into the main vbox */ self->action_area = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (self->action_area), GTK_BUTTONBOX_END); gtk_box_pack_start (GTK_BOX (self->vbox), self->action_area, FALSE, TRUE, 0); /* Mark it as a dialog window */ gtk_window_set_type_hint (GTK_WINDOW (self), GDK_WINDOW_TYPE_HINT_DIALOG); /* Center on parent?, yessir */ gtk_window_set_position (GTK_WINDOW (self), GTK_WIN_POS_CENTER_ON_PARENT); /* Setup the relationship between this window and its parent */ parent = moko_application_get_main_window(moko_application_get_instance()); if (parent) { gtk_window_set_transient_for(GTK_WINDOW(self), GTK_WINDOW (parent) ); gtk_window_set_modal(GTK_WINDOW(self), TRUE ); gtk_window_set_destroy_with_parent(GTK_WINDOW(self), TRUE ); } gtk_widget_show_all (GTK_WIDGET (self->vbox)); } void moko_dialog_set_title (MokoDialog* self, const gchar* title) { MokoDialogPrivate* priv = DIALOG_PRIVATE(self); gtk_label_set_text (GTK_LABEL (priv->label), title); gtk_window_set_title (GTK_WINDOW(self), title); } static void button_clicked_cb (GtkButton *button, gpointer *user_data) { MokoDialog *self = MOKO_DIALOG (user_data); gint response_id; response_id = moko_dialog_get_button_response_id (self, button); moko_dialog_response (self, response_id); } static void response_id_free_cb (gpointer data) { g_slice_free (gint, data); } void moko_dialog_set_button_response_id (MokoDialog *self, GtkButton *button, gint response_id) { gint* data = g_slice_new (gint); *data = response_id; g_object_set_data_full (G_OBJECT (button), "moko-dialog-response-id", data, response_id_free_cb); } gint moko_dialog_get_button_response_id (MokoDialog *self, GtkButton *button) { gint *data = NULL; data = g_object_get_data (G_OBJECT (button), "moko-dialog-response-id"); if (data == NULL) return GTK_RESPONSE_NONE; else return *data; } void moko_dialog_add_button_widget (MokoDialog *self, GtkButton *button, gint response_id) { gint cur_response_id; cur_response_id = moko_dialog_get_button_response_id (self, button); if (cur_response_id == GTK_RESPONSE_NONE) moko_dialog_set_button_response_id (self, button, response_id); g_signal_connect (button, "clicked", (GCallback)button_clicked_cb, self); gtk_box_pack_end (GTK_BOX (self->action_area), GTK_WIDGET (button), FALSE, TRUE, 0); } GtkWidget* moko_dialog_add_button (MokoDialog *self, const gchar *text, gint response_id) { GtkWidget *button; button = gtk_button_new_from_stock (text); moko_dialog_add_button_widget (self, GTK_BUTTON (button), response_id); gtk_widget_show (button); return button; } GtkWidget* moko_dialog_add_button_secondary (MokoDialog *self, gchar *text, gint response_id) { GtkWidget *button; button = gtk_button_new_from_stock (text); moko_dialog_add_button_widget (self, GTK_BUTTON (button), response_id); gtk_widget_show (button); moko_dialog_set_button_secondary (self, GTK_BUTTON (button), TRUE); return button; } void moko_dialog_set_button_secondary (MokoDialog *self, GtkButton *button, gboolean is_secondary) { gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (self->action_area), GTK_WIDGET (button), is_secondary); } gboolean moko_dialog_get_button_secondary (MokoDialog *self, GtkButton *button) { return gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (self->action_area), GTK_WIDGET (button)); } void moko_dialog_add_buttons (MokoDialog *self, gchar *first_button_text, ...) { va_list args; const gchar* text; gint response_id; va_start (args, first_button_text); if (first_button_text == NULL) return; text = first_button_text; response_id = va_arg (args, gint); while (text != NULL) { moko_dialog_add_button (self, text, response_id); text = va_arg (args, gchar*); if (text == NULL) break; response_id = va_arg (args, int); } va_end (args); } void moko_dialog_response (MokoDialog *self, gint response_id) { g_signal_emit (self, moko_dialog_signals[RESPONSE], 0, response_id); } GtkWidget* moko_dialog_new (void) { return g_object_new (MOKO_TYPE_DIALOG, NULL); } /* Check all buttons have the correct style */ static void check_button_style_cb (GtkWidget *widget, GtkButtonBox *box) { if (GTK_IS_BUTTON (widget) && gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (box), widget)) gtk_widget_set_name (GTK_WIDGET (widget), "mokostylusbutton-white"); else gtk_widget_set_name (GTK_WIDGET (widget), "mokostylusbutton-black"); } static void check_button_styles (MokoDialog *self) { gtk_container_forall (GTK_CONTAINER (self->action_area), (GtkCallback) (check_button_style_cb), self->action_area); #if 0 GList *children, *l; /* * the button box doesn't seem to return secondary children from * gtk_container_get_children * * maybe a gtk+ bug? */ children = gtk_container_get_children (GTK_CONTAINER (self->action_area)); for (l = children; (l = g_list_next (l)); ) { if (GTK_IS_BUTTON (l->data) && gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (self->action_area), l->data)) gtk_widget_set_name (GTK_WIDGET (l->data), "mokostylusbutton-white"); else gtk_widget_set_name (GTK_WIDGET (l->data), "mokostylusbutton-black"); if (GTK_IS_CONTAINER (l->data)) l = g_list_concat (l, gtk_container_get_children (GTK_CONTAINER (l->data))); } #endif } /* Code beyond this point directly derived from gtk+ (gtkdialog.c) */ static void shutdown_loop (MokoDialog *self) { MokoDialogPrivate *priv = DIALOG_PRIVATE (self); if (g_main_loop_is_running (priv->loop)) g_main_loop_quit (priv->loop); } static void run_unmap_handler (MokoDialog *self, gpointer data) { shutdown_loop (self); } static void run_response_handler (MokoDialog *self, gint response_id, gpointer data) { MokoDialogPrivate *priv = DIALOG_PRIVATE (self); priv->response_id = response_id; shutdown_loop (self); } static gint run_delete_handler (MokoDialog *self, GdkEventAny *event, gpointer data) { shutdown_loop (self); return TRUE; /* Do not destroy */ } static void run_destroy_handler (MokoDialog *self, gpointer data) { MokoDialogPrivate *priv = DIALOG_PRIVATE (self); /* shutdown_loop will be called by run_unmap_handler */ priv->destroyed = TRUE; } gint moko_dialog_run (MokoDialog *self) { MokoDialogPrivate *priv = DIALOG_PRIVATE (self); priv->response_id = GTK_RESPONSE_NONE; priv->loop = NULL; priv->destroyed = FALSE; gboolean was_modal; gulong response_handler; gulong unmap_handler; gulong destroy_handler; gulong delete_handler; g_object_ref (self); /* check we have the correct styles for OpenMoko */ check_button_styles (self); was_modal = GTK_WINDOW (self)->modal; if (!was_modal) gtk_window_set_modal (GTK_WINDOW (self), TRUE); if (!GTK_WIDGET_VISIBLE (self)) gtk_widget_show (GTK_WIDGET (self)); response_handler = g_signal_connect (self, "response", G_CALLBACK (run_response_handler), NULL); unmap_handler = g_signal_connect (self, "unmap", G_CALLBACK (run_unmap_handler), NULL); delete_handler = g_signal_connect (self, "delete-event", G_CALLBACK (run_delete_handler), NULL); destroy_handler = g_signal_connect (self, "destroy", G_CALLBACK (run_destroy_handler), NULL); priv->loop = g_main_loop_new (NULL, FALSE); GDK_THREADS_LEAVE (); g_main_loop_run (priv->loop); GDK_THREADS_ENTER (); g_main_loop_unref (priv->loop); priv->loop = NULL; if (!priv->destroyed) { if (!was_modal) gtk_window_set_modal (GTK_WINDOW(self), FALSE); g_signal_handler_disconnect (self, response_handler); g_signal_handler_disconnect (self, unmap_handler); g_signal_handler_disconnect (self, delete_handler); g_signal_handler_disconnect (self, destroy_handler); } g_object_unref (self); return priv->response_id; }