/* * moodialogs.c * * Copyright (C) 2004-2007 by Yevgen Muntyan * * 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.1 of the License, or (at your option) any later version. * * See COPYING file that comes with this distribution. */ #include #include "mooutils/moodialogs.h" #include "mooutils/mooprefs.h" #include "mooutils/mooutils-misc.h" static GtkWidget * create_message_dialog (GtkWindow *parent, GtkMessageType type, const char *text, const char *secondary_text) { GtkWidget *dialog; dialog = gtk_message_dialog_new_with_markup (parent, GTK_DIALOG_MODAL, type, GTK_BUTTONS_NONE, "%s", text); if (secondary_text) gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary_text); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); if (parent && parent->group) gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog)); return dialog; } /* gtkwindow.c */ static void clamp_window_to_rectangle (gint *x, gint *y, gint w, gint h, const GdkRectangle *rect) { /* if larger than the screen, center on the screen. */ if (w > rect->width) *x = rect->x - (w - rect->width) / 2; else if (*x < rect->x) *x = rect->x; else if (*x + w > rect->x + rect->width) *x = rect->x + rect->width - w; if (h > rect->height) *y = rect->y - (h - rect->height) / 2; else if (*y < rect->y) *y = rect->y; else if (*y + h > rect->y + rect->height) *y = rect->y + rect->height - h; } static void position_window (GtkWindow *dialog) { GdkPoint *coord; int screen_width, screen_height, monitor_num; GdkRectangle monitor; GdkScreen *screen; GtkRequisition req; g_signal_handlers_disconnect_by_func (dialog, (gpointer) position_window, NULL); coord = g_object_get_data (G_OBJECT (dialog), "moo-coords"); g_return_if_fail (coord != NULL); screen = gtk_widget_get_screen (GTK_WIDGET (dialog)); g_return_if_fail (screen != NULL); screen_width = gdk_screen_get_width (screen); screen_height = gdk_screen_get_height (screen); monitor_num = gdk_screen_get_monitor_at_point (screen, coord->x, coord->y); gtk_widget_size_request (GTK_WIDGET (dialog), &req); coord->x = coord->x - req.width / 2; coord->y = coord->y - req.height / 2; coord->x = CLAMP (coord->x, 0, screen_width - req.width); coord->y = CLAMP (coord->y, 0, screen_height - req.height); gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); clamp_window_to_rectangle (&coord->x, &coord->y, req.width, req.height, &monitor); gtk_window_move (dialog, coord->x, coord->y); } #ifdef __WIN32__ static void on_hide (GtkWindow *window) { GtkWindow *parent = gtk_window_get_transient_for (window); if (parent && GTK_WIDGET_DRAWABLE (parent)) gtk_window_present (parent); g_signal_handlers_disconnect_by_func (window, (gpointer) on_hide, NULL); } #endif static void moo_position_window_real (GtkWidget *window, GtkWidget *parent, gboolean at_mouse, gboolean at_coords, int x, int y) { GtkWidget *toplevel = NULL; g_return_if_fail (GTK_IS_WINDOW (window)); if (parent) toplevel = gtk_widget_get_toplevel (parent); if (toplevel && !GTK_IS_WINDOW (toplevel)) toplevel = NULL; if (toplevel) { gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (toplevel)); #ifdef __WIN32__ g_signal_handlers_disconnect_by_func (window, (gpointer) on_hide, NULL); g_signal_connect (window, "unmap", G_CALLBACK (on_hide), NULL); #endif } if (toplevel && GTK_WINDOW(toplevel)->group) gtk_window_group_add_window (GTK_WINDOW(toplevel)->group, GTK_WINDOW (window)); if (!at_mouse && !at_coords && parent && GTK_WIDGET_REALIZED (parent)) { if (GTK_IS_WINDOW (parent)) { gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER_ON_PARENT); } else { GdkWindow *parent_window = gtk_widget_get_parent_window (parent); gdk_window_get_origin (parent_window, &x, &y); x += parent->allocation.x; y += parent->allocation.y; x += parent->allocation.width / 2; y += parent->allocation.height / 2; at_coords = TRUE; } } if (at_mouse) { gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE); } else if (at_coords) { GdkPoint *coord = g_new (GdkPoint, 1); coord->x = x; coord->y = y; gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_NONE); g_object_set_data_full (G_OBJECT (window), "moo-coords", coord, g_free); if (!GTK_WIDGET_REALIZED (window)) { g_signal_handlers_disconnect_by_func (window, (gpointer) position_window, NULL); g_signal_connect (window, "realize", G_CALLBACK (position_window), NULL); } else { position_window (GTK_WINDOW (window)); } } } void moo_position_window_at_pointer (GtkWidget *window, GtkWidget *parent) { moo_position_window_real (window, parent, TRUE, FALSE, 0, 0); } void moo_window_set_parent (GtkWidget *window, GtkWidget *parent) { moo_position_window_real (window, parent, FALSE, FALSE, 0, 0); } static void moo_message_dialog (GtkWidget *parent, GtkMessageType type, const char *text, const char *secondary_text, gboolean at_mouse, gboolean at_coords, int x, int y) { GtkWidget *dialog, *toplevel = NULL; if (parent) toplevel = gtk_widget_get_toplevel (parent); if (!toplevel || !GTK_IS_WINDOW (toplevel)) toplevel = NULL; dialog = create_message_dialog (toplevel ? GTK_WINDOW (toplevel) : NULL, type, text, secondary_text); g_return_if_fail (dialog != NULL); moo_position_window_real (dialog, parent, at_mouse, at_coords, x, y); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } void moo_error_dialog (GtkWidget *parent, const char *text, const char *secondary_text) { moo_message_dialog (parent, GTK_MESSAGE_ERROR, text, secondary_text, FALSE, FALSE, 0, 0); } void moo_info_dialog (GtkWidget *parent, const char *text, const char *secondary_text) { moo_message_dialog (parent, GTK_MESSAGE_INFO, text, secondary_text, FALSE, FALSE, 0, 0); } void moo_warning_dialog (GtkWidget *parent, const char *text, const char *secondary_text) { moo_message_dialog (parent, GTK_MESSAGE_WARNING, text, secondary_text, FALSE, FALSE, 0, 0); } gboolean moo_overwrite_file_dialog (GtkWidget *parent, const char *display_name, const char *display_dirname) { int response; GtkWidget *dialog, *button, *toplevel = NULL; g_return_val_if_fail (display_name != NULL, FALSE); if (parent) parent = gtk_widget_get_toplevel (parent); dialog = gtk_message_dialog_new (toplevel ? GTK_WINDOW (toplevel) : NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "A file named \"%s\" already exists. Do you want to replace it?", display_name); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "The file already exists in \"%s\". Replacing it will " "overwrite its contents.", display_dirname); gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); button = gtk_button_new_with_mnemonic ("_Replace"); gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_BUTTON)); gtk_widget_show (button); gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, GTK_RESPONSE_YES); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_YES, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); moo_window_set_parent (dialog, parent); response = gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); return response == GTK_RESPONSE_YES; } const char * moo_font_dialog (GtkWidget *parent, const char *title, const char *start_font, gboolean fixed_width) { GtkWidget *dialog; const char *fontname = NULL; if (fixed_width) g_warning ("%s: choosing fixed width fonts " "only is not implemented", G_STRLOC); dialog = gtk_font_selection_dialog_new (title); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); if (start_font) gtk_font_selection_dialog_set_font_name ( GTK_FONT_SELECTION_DIALOG (dialog), start_font); moo_window_set_parent (dialog, parent); if (GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog))) fontname = gtk_font_selection_dialog_get_font_name ( GTK_FONT_SELECTION_DIALOG (dialog)); gtk_widget_destroy (dialog); return fontname; } typedef struct { char *key_maximized; char *key_width; char *key_height; char *key_x; char *key_y; gboolean remember_position; guint save_size_idle; } PositionInfo; static void position_info_free (PositionInfo *pinfo) { if (pinfo) { if (pinfo->save_size_idle) g_source_remove (pinfo->save_size_idle); g_free (pinfo->key_maximized); g_free (pinfo->key_width); g_free (pinfo->key_height); g_free (pinfo->key_x); g_free (pinfo->key_y); g_free (pinfo); } } static gboolean save_size (GtkWindow *window) { PositionInfo *pinfo; GdkWindowState state; pinfo = g_object_get_data (G_OBJECT (window), "moo-position-info"); g_return_val_if_fail (pinfo != NULL, FALSE); pinfo->save_size_idle = 0; if (!GTK_WIDGET_REALIZED (window)) return FALSE; state = gdk_window_get_state (GTK_WIDGET(window)->window); moo_prefs_set_bool (pinfo->key_maximized, state & GDK_WINDOW_STATE_MAXIMIZED); if (!(state & GDK_WINDOW_STATE_MAXIMIZED)) { int width, height; gtk_window_get_size (GTK_WINDOW (window), &width, &height); moo_prefs_set_int (pinfo->key_width, width); moo_prefs_set_int (pinfo->key_height, height); } if (pinfo->remember_position) { int x, y; gtk_window_get_position (window, &x, &y); moo_prefs_set_int (pinfo->key_x, x); moo_prefs_set_int (pinfo->key_y, y); } return FALSE; } static gboolean configure_event (GtkWindow *window, G_GNUC_UNUSED GdkEventConfigure *event, PositionInfo *pinfo) { if (!pinfo->save_size_idle) pinfo->save_size_idle = moo_idle_add ((GSourceFunc) save_size, window); return FALSE; } static void set_initial_size (GtkWindow *window, PositionInfo *pinfo) { int width, height; width = moo_prefs_get_int (pinfo->key_width); height = moo_prefs_get_int (pinfo->key_height); if (width > 0 && height > 0) gtk_window_set_default_size (window, width, height); if (moo_prefs_get_bool (pinfo->key_maximized)) gtk_window_maximize (window); if (pinfo->remember_position) { int x, y; x = moo_prefs_get_int (pinfo->key_x); y = moo_prefs_get_int (pinfo->key_y); if (x < G_MAXINT && y < G_MAXINT) gtk_window_move (window, x, y); } } void _moo_window_set_remember_size (GtkWindow *window, const char *prefs_key, gboolean remember_position) { PositionInfo *pinfo; g_return_if_fail (prefs_key != NULL); pinfo = g_object_get_data (G_OBJECT (window), "moo-position-info"); if (pinfo) g_signal_handlers_disconnect_by_func (window, (gpointer) configure_event, pinfo); pinfo = g_new0 (PositionInfo, 1); pinfo->key_maximized = moo_prefs_make_key (prefs_key, "maximized", NULL); pinfo->key_width = moo_prefs_make_key (prefs_key, "width", NULL); pinfo->key_height = moo_prefs_make_key (prefs_key, "height", NULL); pinfo->key_x = moo_prefs_make_key (prefs_key, "x", NULL); pinfo->key_y = moo_prefs_make_key (prefs_key, "y", NULL); pinfo->remember_position = remember_position; moo_prefs_create_key (pinfo->key_maximized, MOO_PREFS_STATE, G_TYPE_BOOLEAN, FALSE); moo_prefs_create_key (pinfo->key_width, MOO_PREFS_STATE, G_TYPE_INT, -1); moo_prefs_create_key (pinfo->key_height, MOO_PREFS_STATE, G_TYPE_INT, -1); if (remember_position) { moo_prefs_create_key (pinfo->key_x, MOO_PREFS_STATE, G_TYPE_INT, G_MAXINT); moo_prefs_create_key (pinfo->key_y, MOO_PREFS_STATE, G_TYPE_INT, G_MAXINT); } g_object_set_data_full (G_OBJECT (window), "moo-position-info", pinfo, (GDestroyNotify) position_info_free); set_initial_size (window, pinfo); g_signal_connect (window, "configure-event", G_CALLBACK (configure_event), pinfo); }