2 * @file minidialog.c Implementation of the #PidginMiniDialog Gtk widget.
8 * Pidgin is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
31 #include "libpurple/prefs.h"
33 #include "pidgin/minidialog.h"
34 #include "pidgin/pidgin.h"
35 #include "pidgin/pidginstock.h"
37 static void pidgin_mini_dialog_init (PidginMiniDialog
*self
);
38 static void pidgin_mini_dialog_class_init (PidginMiniDialogClass
*klass
);
40 static gpointer pidgin_mini_dialog_parent_class
= NULL
;
43 pidgin_mini_dialog_class_intern_init (gpointer klass
)
45 pidgin_mini_dialog_parent_class
= g_type_class_peek_parent (klass
);
46 pidgin_mini_dialog_class_init ((PidginMiniDialogClass
*) klass
);
50 pidgin_mini_dialog_get_type (void)
52 static GType g_define_type_id
= 0;
53 if (g_define_type_id
== 0)
55 static const GTypeInfo g_define_type_info
= {
56 sizeof (PidginMiniDialogClass
),
58 (GBaseFinalizeFunc
) NULL
,
59 (GClassInitFunc
) pidgin_mini_dialog_class_intern_init
,
60 (GClassFinalizeFunc
) NULL
,
61 NULL
, /* class_data */
62 sizeof (PidginMiniDialog
),
64 (GInstanceInitFunc
) pidgin_mini_dialog_init
,
67 g_define_type_id
= g_type_register_static (GTK_TYPE_VBOX
,
68 "PidginMiniDialog", &g_define_type_info
, 0);
70 return g_define_type_id
;
79 PROP_ENABLE_DESCRIPTION_MARKUP
,
82 } HazeConnectionProperties
;
84 typedef struct _PidginMiniDialogPrivate
91 gboolean enable_description_markup
;
93 guint idle_destroy_cb_id
;
94 } PidginMiniDialogPrivate
;
96 #define PIDGIN_MINI_DIALOG_GET_PRIVATE(dialog) \
97 ((PidginMiniDialogPrivate *) ((dialog)->priv))
99 static PidginMiniDialog
*
100 mini_dialog_new(const gchar
*title
, const gchar
*description
)
102 return g_object_new(PIDGIN_TYPE_MINI_DIALOG
,
104 "description", description
,
109 pidgin_mini_dialog_new(const gchar
*title
,
110 const gchar
*description
,
111 const gchar
*icon_name
)
113 PidginMiniDialog
*mini_dialog
= mini_dialog_new(title
, description
);
114 pidgin_mini_dialog_set_icon_name(mini_dialog
, icon_name
);
119 pidgin_mini_dialog_new_with_custom_icon(const gchar
*title
,
120 const gchar
*description
,
121 GdkPixbuf
*custom_icon
)
123 PidginMiniDialog
*mini_dialog
= mini_dialog_new(title
, description
);
124 pidgin_mini_dialog_set_custom_icon(mini_dialog
, custom_icon
);
129 pidgin_mini_dialog_set_title(PidginMiniDialog
*mini_dialog
,
132 g_object_set(G_OBJECT(mini_dialog
), "title", title
, NULL
);
136 pidgin_mini_dialog_set_description(PidginMiniDialog
*mini_dialog
,
137 const char *description
)
139 g_object_set(G_OBJECT(mini_dialog
), "description", description
, NULL
);
143 pidgin_mini_dialog_enable_description_markup(PidginMiniDialog
*mini_dialog
)
145 g_object_set(G_OBJECT(mini_dialog
), "enable-description-markup", TRUE
, NULL
);
149 pidgin_mini_dialog_links_supported()
151 #if GTK_CHECK_VERSION(2,18,0)
158 void pidgin_mini_dialog_set_link_callback(PidginMiniDialog
*mini_dialog
, GCallback cb
, gpointer user_data
)
160 g_signal_connect(PIDGIN_MINI_DIALOG_GET_PRIVATE(mini_dialog
)->desc
, "activate-link", cb
, user_data
);
164 pidgin_mini_dialog_set_icon_name(PidginMiniDialog
*mini_dialog
,
165 const char *icon_name
)
167 g_object_set(G_OBJECT(mini_dialog
), "icon-name", icon_name
, NULL
);
171 pidgin_mini_dialog_set_custom_icon(PidginMiniDialog
*mini_dialog
, GdkPixbuf
*custom_icon
)
173 g_object_set(G_OBJECT(mini_dialog
), "custom-icon", custom_icon
, NULL
);
176 struct _mini_dialog_button_clicked_cb_data
178 PidginMiniDialog
*mini_dialog
;
179 PidginMiniDialogCallback callback
;
181 gboolean close_dialog_after_click
;
185 pidgin_mini_dialog_get_num_children(PidginMiniDialog
*mini_dialog
)
187 return g_list_length(mini_dialog
->contents
->children
);
191 idle_destroy_cb(GtkWidget
*mini_dialog
)
193 gtk_widget_destroy(mini_dialog
);
198 mini_dialog_button_clicked_cb(GtkButton
*button
,
201 struct _mini_dialog_button_clicked_cb_data
*data
= user_data
;
202 PidginMiniDialogPrivate
*priv
=
203 PIDGIN_MINI_DIALOG_GET_PRIVATE(data
->mini_dialog
);
205 if (data
->close_dialog_after_click
) {
206 /* Set up the destruction callback before calling the clicked callback,
207 * so that if the mini-dialog gets destroyed during the clicked callback
208 * the idle_destroy_cb is correctly removed by _finalize.
210 priv
->idle_destroy_cb_id
=
211 g_idle_add((GSourceFunc
) idle_destroy_cb
, data
->mini_dialog
);
214 if (data
->callback
!= NULL
)
215 data
->callback(data
->mini_dialog
, button
, data
->user_data
);
220 mini_dialog_button_destroy_cb(GtkButton
*button
,
223 struct _mini_dialog_button_clicked_cb_data
*data
= user_data
;
228 mini_dialog_add_button(PidginMiniDialog
*self
,
230 PidginMiniDialogCallback clicked_cb
,
232 gboolean close_dialog_after_click
)
234 PidginMiniDialogPrivate
*priv
= PIDGIN_MINI_DIALOG_GET_PRIVATE(self
);
235 struct _mini_dialog_button_clicked_cb_data
*callback_data
236 = g_new0(struct _mini_dialog_button_clicked_cb_data
, 1);
237 GtkWidget
*button
= gtk_button_new();
238 GtkWidget
*label
= gtk_label_new(NULL
);
240 g_strdup_printf("<span size=\"smaller\">%s</span>", text
);
242 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label
), button_text
);
245 callback_data
->mini_dialog
= self
;
246 callback_data
->callback
= clicked_cb
;
247 callback_data
->user_data
= user_data
;
248 callback_data
->close_dialog_after_click
= close_dialog_after_click
;
249 g_signal_connect(G_OBJECT(button
), "clicked",
250 (GCallback
) mini_dialog_button_clicked_cb
, callback_data
);
251 g_signal_connect(G_OBJECT(button
), "destroy",
252 (GCallback
) mini_dialog_button_destroy_cb
, callback_data
);
254 gtk_misc_set_alignment(GTK_MISC(label
), 0.5, 0.5);
255 gtk_container_add(GTK_CONTAINER(button
), label
);
257 gtk_box_pack_end(GTK_BOX(priv
->buttons
), button
, FALSE
, FALSE
,
259 gtk_widget_show_all(GTK_WIDGET(button
));
263 pidgin_mini_dialog_add_button(PidginMiniDialog
*self
,
265 PidginMiniDialogCallback clicked_cb
,
268 mini_dialog_add_button(self
, text
, clicked_cb
, user_data
, TRUE
);
272 pidgin_mini_dialog_add_non_closing_button(PidginMiniDialog
*self
,
274 PidginMiniDialogCallback clicked_cb
,
277 mini_dialog_add_button(self
, text
, clicked_cb
, user_data
, FALSE
);
281 pidgin_mini_dialog_get_property(GObject
*object
,
286 PidginMiniDialog
*self
= PIDGIN_MINI_DIALOG(object
);
287 PidginMiniDialogPrivate
*priv
= PIDGIN_MINI_DIALOG_GET_PRIVATE(self
);
289 switch (property_id
) {
291 g_value_set_string(value
, gtk_label_get_text(priv
->title
));
293 case PROP_DESCRIPTION
:
294 g_value_set_string(value
, gtk_label_get_text(priv
->desc
));
298 gchar
*icon_name
= NULL
;
300 gtk_image_get_stock(priv
->icon
, &icon_name
, &size
);
301 g_value_set_string(value
, icon_name
);
304 case PROP_CUSTOM_ICON
:
305 g_value_set_object(value
, gtk_image_get_pixbuf(priv
->icon
));
307 case PROP_ENABLE_DESCRIPTION_MARKUP
:
308 g_value_set_boolean(value
, priv
->enable_description_markup
);
311 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
316 mini_dialog_set_title(PidginMiniDialog
*self
,
319 PidginMiniDialogPrivate
*priv
= PIDGIN_MINI_DIALOG_GET_PRIVATE(self
);
321 char *title_esc
= g_markup_escape_text(title
, -1);
322 char *title_markup
= g_strdup_printf(
323 "<span weight=\"bold\" size=\"smaller\">%s</span>",
324 title_esc
? title_esc
: "");
326 gtk_label_set_markup(priv
->title
, title_markup
);
329 g_free(title_markup
);
333 mini_dialog_set_description(PidginMiniDialog
*self
,
334 const char *description
)
336 PidginMiniDialogPrivate
*priv
= PIDGIN_MINI_DIALOG_GET_PRIVATE(self
);
339 char *desc_esc
= priv
->enable_description_markup
? g_strdup(description
) : g_markup_escape_text(description
, -1);
340 char *desc_markup
= g_strdup_printf(
341 "<span size=\"smaller\">%s</span>", desc_esc
);
343 gtk_label_set_markup(priv
->desc
, desc_markup
);
348 gtk_widget_show(GTK_WIDGET(priv
->desc
));
349 g_object_set(G_OBJECT(priv
->desc
), "no-show-all", FALSE
, NULL
);
353 gtk_label_set_text(priv
->desc
, NULL
);
354 gtk_widget_hide(GTK_WIDGET(priv
->desc
));
355 /* make calling show_all() on the minidialog not affect desc
356 * even though it's packed inside it.
358 g_object_set(G_OBJECT(priv
->desc
), "no-show-all", TRUE
, NULL
);
363 pidgin_mini_dialog_set_property(GObject
*object
,
368 PidginMiniDialog
*self
= PIDGIN_MINI_DIALOG(object
);
369 PidginMiniDialogPrivate
*priv
= PIDGIN_MINI_DIALOG_GET_PRIVATE(self
);
371 switch (property_id
) {
373 mini_dialog_set_title(self
, g_value_get_string(value
));
375 case PROP_DESCRIPTION
:
376 mini_dialog_set_description(self
, g_value_get_string(value
));
379 gtk_image_set_from_stock(priv
->icon
, g_value_get_string(value
),
380 gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL
));
382 case PROP_CUSTOM_ICON
:
383 gtk_image_set_from_pixbuf(priv
->icon
, g_value_get_object(value
));
385 case PROP_ENABLE_DESCRIPTION_MARKUP
:
386 priv
->enable_description_markup
= g_value_get_boolean(value
);
389 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
394 pidgin_mini_dialog_finalize(GObject
*object
)
396 PidginMiniDialog
*self
= PIDGIN_MINI_DIALOG(object
);
397 PidginMiniDialogPrivate
*priv
= PIDGIN_MINI_DIALOG_GET_PRIVATE(self
);
399 if (priv
->idle_destroy_cb_id
)
400 g_source_remove(priv
->idle_destroy_cb_id
);
405 purple_prefs_disconnect_by_handle(self
);
407 G_OBJECT_CLASS (pidgin_mini_dialog_parent_class
)->finalize (object
);
411 pidgin_mini_dialog_class_init(PidginMiniDialogClass
*klass
)
413 GObjectClass
*object_class
= G_OBJECT_CLASS(klass
);
414 GParamSpec
*param_spec
;
416 object_class
->get_property
= pidgin_mini_dialog_get_property
;
417 object_class
->set_property
= pidgin_mini_dialog_set_property
;
418 object_class
->finalize
= pidgin_mini_dialog_finalize
;
420 param_spec
= g_param_spec_string("title", "title",
421 "String specifying the mini-dialog's title", NULL
,
422 G_PARAM_STATIC_NAME
| G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
|
424 g_object_class_install_property (object_class
, PROP_TITLE
, param_spec
);
426 param_spec
= g_param_spec_string("description", "description",
427 "Description text for the mini-dialog, if desired", NULL
,
428 G_PARAM_STATIC_NAME
| G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
|
430 g_object_class_install_property (object_class
, PROP_DESCRIPTION
, param_spec
);
432 param_spec
= g_param_spec_string("icon-name", "icon-name",
433 "String specifying the Gtk stock name of the dialog's icon",
435 G_PARAM_STATIC_NAME
| G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
|
437 g_object_class_install_property (object_class
, PROP_ICON_NAME
, param_spec
);
439 param_spec
= g_param_spec_object("custom-icon", "custom-icon",
440 "Pixbuf to use as the dialog's icon",
442 G_PARAM_STATIC_NAME
| G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
|
444 g_object_class_install_property (object_class
, PROP_CUSTOM_ICON
, param_spec
);
446 param_spec
= g_param_spec_boolean("enable-description-markup", "enable-description-markup",
447 "Use GMarkup in the description text", FALSE
,
448 G_PARAM_STATIC_NAME
| G_PARAM_STATIC_NICK
| G_PARAM_STATIC_BLURB
|
450 g_object_class_install_property (object_class
, PROP_ENABLE_DESCRIPTION_MARKUP
, param_spec
);
453 /* 16 is the width of the icon, due to PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL */
454 #define BLIST_WIDTH_OTHER_THAN_LABEL \
455 ((PIDGIN_HIG_BOX_SPACE * 3) + 16)
457 #define BLIST_WIDTH_PREF \
458 (PIDGIN_PREFS_ROOT "/blist/width")
461 blist_width_changed_cb(const char *name
,
466 PidginMiniDialog
*self
= PIDGIN_MINI_DIALOG(data
);
467 PidginMiniDialogPrivate
*priv
= PIDGIN_MINI_DIALOG_GET_PRIVATE(self
);
468 guint blist_width
= GPOINTER_TO_INT(val
);
469 guint label_width
= blist_width
- BLIST_WIDTH_OTHER_THAN_LABEL
;
471 gtk_widget_set_size_request(GTK_WIDGET(priv
->title
), label_width
, -1);
472 gtk_widget_set_size_request(GTK_WIDGET(priv
->desc
), label_width
, -1);
476 pidgin_mini_dialog_init(PidginMiniDialog
*self
)
478 GtkBox
*self_box
= GTK_BOX(self
);
479 guint blist_width
= purple_prefs_get_int(BLIST_WIDTH_PREF
);
480 guint label_width
= blist_width
- BLIST_WIDTH_OTHER_THAN_LABEL
;
482 PidginMiniDialogPrivate
*priv
= g_new0(PidginMiniDialogPrivate
, 1);
485 gtk_container_set_border_width(GTK_CONTAINER(self
), PIDGIN_HIG_BOX_SPACE
);
487 priv
->title_box
= GTK_BOX(gtk_hbox_new(FALSE
, PIDGIN_HIG_BOX_SPACE
));
489 priv
->icon
= GTK_IMAGE(gtk_image_new());
490 gtk_misc_set_alignment(GTK_MISC(priv
->icon
), 0, 0);
492 priv
->title
= GTK_LABEL(gtk_label_new(NULL
));
493 gtk_widget_set_size_request(GTK_WIDGET(priv
->title
), label_width
, -1);
494 gtk_label_set_line_wrap(priv
->title
, TRUE
);
495 gtk_label_set_selectable(priv
->title
, TRUE
);
496 gtk_misc_set_alignment(GTK_MISC(priv
->title
), 0, 0);
498 gtk_box_pack_start(priv
->title_box
, GTK_WIDGET(priv
->icon
), FALSE
, FALSE
, 0);
499 gtk_box_pack_start(priv
->title_box
, GTK_WIDGET(priv
->title
), TRUE
, TRUE
, 0);
501 priv
->desc
= GTK_LABEL(gtk_label_new(NULL
));
502 gtk_widget_set_size_request(GTK_WIDGET(priv
->desc
), label_width
, -1);
503 gtk_label_set_line_wrap(priv
->desc
, TRUE
);
504 gtk_misc_set_alignment(GTK_MISC(priv
->desc
), 0, 0);
505 gtk_label_set_selectable(priv
->desc
, TRUE
);
506 /* make calling show_all() on the minidialog not affect desc even though
507 * it's packed inside it.
509 g_object_set(G_OBJECT(priv
->desc
), "no-show-all", TRUE
, NULL
);
511 purple_prefs_connect_callback(self
, BLIST_WIDTH_PREF
,
512 blist_width_changed_cb
, self
);
514 self
->contents
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
516 priv
->buttons
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
518 gtk_box_pack_start(self_box
, GTK_WIDGET(priv
->title_box
), FALSE
, FALSE
, 0);
519 gtk_box_pack_start(self_box
, GTK_WIDGET(priv
->desc
), FALSE
, FALSE
, 0);
520 gtk_box_pack_start(self_box
, GTK_WIDGET(self
->contents
), TRUE
, TRUE
, 0);
521 gtk_box_pack_start(self_box
, GTK_WIDGET(priv
->buttons
), FALSE
, FALSE
, 0);
523 gtk_widget_show_all(GTK_WIDGET(self
));