gtk2/gtk/gtknativedialog.c

554 lines
15 KiB
C
Raw Normal View History

/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* GTK - The GIMP Toolkit
* gtkfilechoosernative.c: Native File selector dialog
* Copyright (C) 2015, Red Hat, Inc.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtknativedialogprivate.h"
#include "gtkprivate.h"
#include "gtkfilechooserdialog.h"
#include "gtkfilechooserprivate.h"
#include "gtkfilechooserwidget.h"
#include "gtkfilechooserwidgetprivate.h"
#include "gtkfilechooserutils.h"
#include "gtksizerequest.h"
#include "gtktypebuiltins.h"
#include "gtkintl.h"
#include "gtksettings.h"
#include "gtktogglebutton.h"
#include "gtkstylecontext.h"
#include "gtkheaderbar.h"
#include "gtklabel.h"
/**
* SECTION:gtknativedialog
* @Short_description: Integrate with native dialogs
* @Title: GtkNativeDialog
* @See_also: #GtkFileChooserNative, #GtkDialog
*
* Native dialogs are platform dialogs that don't use #GtkDialog or
* #GtkWindow. They are used in order to integrate better with a
* platform, by looking the same as other native applications and
* supporting platform specific features.
*
* The #GtkDialog functions cannot be used on such objects, but we
* need a similar API in order to drive them. The #GtkNativeDialog
* object is an API that allows you to do this. It allows you to set
* various common properties on the dialog, as well as show and hide
* it and get a #GtkNativeDialog::response signal when the user finished
* with the dialog.
*/
typedef struct _GtkNativeDialogPrivate GtkNativeDialogPrivate;
struct _GtkNativeDialogPrivate
{
GtkWindow *transient_for;
char *title;
guint visible : 1;
guint modal : 1;
/* Run state */
gint run_response_id;
GMainLoop *run_loop; /* Non-NULL when in run */
};
enum {
PROP_0,
PROP_TITLE,
PROP_VISIBLE,
PROP_MODAL,
PROP_TRANSIENT_FOR,
LAST_ARG,
};
enum {
RESPONSE,
LAST_SIGNAL
};
static GParamSpec *native_props[LAST_ARG] = { NULL, };
static guint native_signals[LAST_SIGNAL];
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkNativeDialog, gtk_native_dialog, G_TYPE_OBJECT,
G_ADD_PRIVATE (GtkNativeDialog))
static void
gtk_native_dialog_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkNativeDialog *self = GTK_NATIVE_DIALOG (object);
switch (prop_id)
{
case PROP_TITLE:
gtk_native_dialog_set_title (self, g_value_get_string (value));
break;
case PROP_MODAL:
gtk_native_dialog_set_modal (self, g_value_get_boolean (value));
break;
case PROP_VISIBLE:
if (g_value_get_boolean (value))
gtk_native_dialog_show (self);
else
gtk_native_dialog_hide (self);
break;
case PROP_TRANSIENT_FOR:
gtk_native_dialog_set_transient_for (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_native_dialog_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkNativeDialog *self = GTK_NATIVE_DIALOG (object);
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
switch (prop_id)
{
case PROP_TITLE:
g_value_set_string (value, priv->title);
break;
case PROP_MODAL:
g_value_set_boolean (value, priv->modal);
break;
case PROP_VISIBLE:
g_value_set_boolean (value, priv->visible);
break;
case PROP_TRANSIENT_FOR:
g_value_set_object (value, priv->transient_for);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void parent_destroyed (GtkWidget *parent,
GtkNativeDialog *self);
static void
gtk_native_dialog_dispose (GObject *object)
{
GtkNativeDialog *self = GTK_NATIVE_DIALOG (object);
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
if (priv->transient_for)
{
g_signal_handlers_disconnect_by_func (priv->transient_for, parent_destroyed, self);
priv->transient_for = NULL;
}
if (priv->visible)
gtk_native_dialog_hide (self);
G_OBJECT_CLASS (gtk_native_dialog_parent_class)->dispose (object);
}
static void
gtk_native_dialog_finalize (GObject *object)
{
GtkNativeDialog *self = GTK_NATIVE_DIALOG (object);
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_clear_pointer (&priv->title, g_free);
g_clear_object (&priv->transient_for);
G_OBJECT_CLASS (gtk_native_dialog_parent_class)->finalize (object);
}
static void
gtk_native_dialog_class_init (GtkNativeDialogClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_native_dialog_set_property;
gobject_class->get_property = gtk_native_dialog_get_property;
gobject_class->finalize = gtk_native_dialog_finalize;
gobject_class->dispose = gtk_native_dialog_dispose;
/**
* GtkNativeDialog:title:
*
* The title of the dialog window
*/
native_props[PROP_TITLE] =
g_param_spec_string ("title",
P_("Dialog Title"),
P_("The title of the file chooser dialog"),
NULL,
GTK_PARAM_READWRITE);
/**
* GtkNativeDialog:modal:
*
* Whether the window should be modal with respect to its transient parent.
*/
native_props[PROP_MODAL] =
g_param_spec_boolean ("modal",
P_("Modal"),
P_("If TRUE, the dialog is modal (other windows are not usable while this one is up)"),
FALSE,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkNativeDialog:visible:
*
2020-05-28 08:00:03 +00:00
* Whether the window is currently visible.
*/
native_props[PROP_VISIBLE] =
g_param_spec_boolean ("visible",
P_("Visible"),
P_("Whether the dialog is currently visible"),
FALSE,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkNativeDialog:transient-for:
*
* The transient parent of the dialog, or %NULL for none.
*/
native_props[PROP_TRANSIENT_FOR] =
g_param_spec_object ("transient-for",
P_("Transient for Window"),
P_("The transient parent of the dialog"),
GTK_TYPE_WINDOW,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, LAST_ARG, native_props);
/**
* GtkNativeDialog::response:
* @self: the object on which the signal is emitted
* @response_id: the response ID
*
* Emitted when the user responds to the dialog.
*
* When this is called the dialog has been hidden.
*
* If you call gtk_native_dialog_hide() before the user responds to
* the dialog this signal will not be emitted.
*/
native_signals[RESPONSE] =
g_signal_new (I_("response"),
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkNativeDialogClass, response),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
G_TYPE_INT);
}
static void
gtk_native_dialog_init (GtkNativeDialog *self)
{
}
/**
* gtk_native_dialog_show:
* @self: a #GtkNativeDialog
*
* Shows the dialog on the display, allowing the user to interact with
* it. When the user accepts the state of the dialog the dialog will
* be automatically hidden and the #GtkNativeDialog::response signal
* will be emitted.
*
* Multiple calls while the dialog is visible will be ignored.
**/
void
gtk_native_dialog_show (GtkNativeDialog *self)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
GtkNativeDialogClass *klass;
g_return_if_fail (GTK_IS_NATIVE_DIALOG (self));
if (priv->visible)
return;
klass = GTK_NATIVE_DIALOG_GET_CLASS (self);
g_return_if_fail (klass->show != NULL);
klass->show (self);
priv->visible = TRUE;
g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_VISIBLE]);
}
/**
* gtk_native_dialog_hide:
* @self: a #GtkNativeDialog
*
* Hides the dialog if it is visilbe, aborting any interaction. Once this
* is called the #GtkNativeDialog::response signal will not be emitted
* until after the next call to gtk_native_dialog_show().
*
* If the dialog is not visible this does nothing.
**/
void
gtk_native_dialog_hide (GtkNativeDialog *self)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
GtkNativeDialogClass *klass;
g_return_if_fail (GTK_IS_NATIVE_DIALOG (self));
if (!priv->visible)
return;
priv->visible = FALSE;
klass = GTK_NATIVE_DIALOG_GET_CLASS (self);
g_return_if_fail (klass->hide != NULL);
klass->hide (self);
if (priv->run_loop && g_main_loop_is_running (priv->run_loop))
g_main_loop_quit (priv->run_loop);
g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_VISIBLE]);
}
/**
* gtk_native_dialog_destroy:
* @self: a #GtkNativeDialog
*
* Destroys a dialog.
*
* When a dialog is destroyed, it will break any references it holds
* to other objects. If it is visible it will be hidden and any underlying
* window system resources will be destroyed.
*
* Note that this does not release any reference to the object (as opposed to
* destroying a GtkWindow) because there is no reference from the windowing
* system to the #GtkNativeDialog.
**/
void
gtk_native_dialog_destroy (GtkNativeDialog *self)
{
g_return_if_fail (GTK_IS_NATIVE_DIALOG (self));
g_object_run_dispose (G_OBJECT (self));
}
void
_gtk_native_dialog_emit_response (GtkNativeDialog *self,
int response_id)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
priv->visible = FALSE;
g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_VISIBLE]);
g_signal_emit (self, native_signals[RESPONSE], 0, response_id);
}
/**
* gtk_native_dialog_get_visible:
* @self: a #GtkNativeDialog
*
* Determines whether the dialog is visible.
*
* Returns: %TRUE if the dialog is visible
**/
gboolean
gtk_native_dialog_get_visible (GtkNativeDialog *self)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_return_val_if_fail (GTK_IS_NATIVE_DIALOG (self), FALSE);
return priv->visible;
}
/**
* gtk_native_dialog_set_modal:
* @self: a #GtkNativeDialog
* @modal: whether the window is modal
*
* Sets a dialog modal or non-modal. Modal dialogs prevent interaction
* with other windows in the same application. To keep modal dialogs
* on top of main application windows, use
* gtk_native_dialog_set_transient_for() to make the dialog transient for the
* parent; most [window managers][gtk-X11-arch]
* will then disallow lowering the dialog below the parent.
**/
void
gtk_native_dialog_set_modal (GtkNativeDialog *self,
gboolean modal)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_return_if_fail (GTK_IS_NATIVE_DIALOG (self));
modal = modal != FALSE;
if (priv->modal == modal)
return;
priv->modal = modal;
g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_MODAL]);
}
/**
* gtk_native_dialog_get_modal:
* @self: a #GtkNativeDialog
*
* Returns whether the dialog is modal. See gtk_native_dialog_set_modal().
*
* Returns: %TRUE if the dialog is set to be modal
**/
gboolean
gtk_native_dialog_get_modal (GtkNativeDialog *self)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_return_val_if_fail (GTK_IS_NATIVE_DIALOG (self), FALSE);
return priv->modal;
}
/**
* gtk_native_dialog_set_title:
* @self: a #GtkNativeDialog
* @title: title of the dialog
*
* Sets the title of the #GtkNativeDialog.
**/
void
gtk_native_dialog_set_title (GtkNativeDialog *self,
const char *title)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_return_if_fail (GTK_IS_NATIVE_DIALOG (self));
g_free (priv->title);
priv->title = g_strdup (title);
g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_TITLE]);
}
/**
* gtk_native_dialog_get_title:
* @self: a #GtkNativeDialog
*
* Gets the title of the #GtkNativeDialog.
*
introspection: This patch fixes nullable return values fixes for the following symbols in gtk gtk_accel_group_query gtk_accel_group_from_accel_closure gtk_accel_label_get_accel_widget gtk_accessible_get_widget gtk_actionable_get_action_name gtk_app_chooser_get_app_info gtk_app_chooser_button_get_heading gtk_app_chooser_dialog_get_heading gtk_application_get_window_by_id gtk_assistant_get_nth_page gtk_binding_set_find gtk_builder_get_object gtk_builder_lookup_callback_symbol gtk_builder_get_application gtk_button_get_image gtk_cell_area_get_focus_from_sibling gtk_cell_renderer_start_editing gtk_cell_view_get_model gtk_cell_view_get_displayed_row gtk_clipboard_get_owner gtk_container_get_focus_child gtk_container_get_focus_vadjustment gtk_container_get_focus_hadjustment gtk_dialog_get_widget_for_response gtk_drag_get_source_widget gtk_drag_dest_get_target_list gtk_drag_source_get_target_list gtk_entry_completion_get_model gtk_entry_completion_compute_prefix gtk_expander_get_label_widget gtk_file_chooser_get_filename gtk_file_chooser_get_current_folder gtk_file_chooser_get_uri gtk_file_chooser_get_current_folder_uri gtk_file_chooser_get_preview_widget gtk_file_chooser_get_preview_file gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_uri gtk_file_chooser_get_extra_widget gtk_file_chooser_get_filter gtk_file_chooser_native_get_accept_label gtk_file_chooser_native_get_cancel_label gtk_file_filter_get_name gtk_font_chooser_get_font_family gtk_font_chooser_get_font_face gtk_font_chooser_get_font gtk_font_chooser_get_font_desc gtk_font_chooser_get_font_map gtk_frame_get_label gtk_gesture_get_device gtk_gesture_get_window gtk_gl_area_get_error gtk_header_bar_get_title gtk_header_bar_get_subtitle gtk_header_bar_get_custom_title gtk_icon_info_get_filename gtk_icon_view_get_path_at_pos gtk_icon_view_get_model gtk_image_get_pixbuf gtk_image_get_animation gtk_label_get_mnemonic_widget gtk_label_get_attributes gtk_check_version gtk_menu_button_get_popup gtk_menu_button_get_menu_model gtk_menu_button_get_align_widget gtk_menu_button_get_popover gtk_menu_item_get_submenu gtk_menu_item_get_accel_path gtk_native_dialog_get_title gtk_native_dialog_get_transient_for gtk_notebook_get_nth_page gtk_notebook_get_tab_label_text gtk_notebook_get_menu_label gtk_notebook_get_menu_label_text gtk_notebook_get_group_name gtk_notebook_get_action_widget gtk_offscreen_window_get_surface gtk_offscreen_window_get_pixbuf gtk_paned_get_child1 gtk_paned_get_child2 gtk_places_sidebar_get_location gtk_places_sidebar_get_nth_bookmark gtk_plug_get_socket_window gtk_popover_get_default_widget gtk_progress_bar_get_text gtk_recent_filter_get_name gtk_recent_manager_lookup_item gtk_settings_get_default gtk_socket_get_plug_window gtk_stack_sidebar_get_stack gtk_stack_switcher_get_stack gtk_style_context_get_section gtk_style_context_get_parent gtk_style_context_get_frame_clock gtk_test_find_widget gtk_text_buffer_get_mark gtk_text_tag_table_lookup gtk_text_view_get_tabs gtk_text_view_toggle_cursor_visible gtk_text_view_get_window gtk_toolbar_get_nth_item gtk_tool_button_get_label gtk_tool_button_get_icon_name gtk_tool_button_get_label_widget gtk_tool_button_get_icon_widget gtk_tool_palette_get_drop_item gtk_tool_palette_get_drop_group gtk_tree_model_filter_convert_child_path_to_path gtk_tree_model_filter_convert_path_to_child_path gtk_tree_model_sort_convert_child_path_to_path gtk_tree_model_sort_convert_path_to_child_path gtk_tree_view_get_column gtk_tree_view_get_bin_window gtk_tree_view_column_get_widget gtk_tree_view_column_get_tree_view gtk_widget_get_frame_clock gtk_window_group_get_current_device_grab GtkTextBufferSerializeFunc
2015-12-28 20:14:08 +00:00
* Returns: (nullable): the title of the dialog, or %NULL if none has
* been set explicitly. The returned string is owned by the widget
* and must not be modified or freed.
**/
const char *
gtk_native_dialog_get_title (GtkNativeDialog *self)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_return_val_if_fail (GTK_IS_NATIVE_DIALOG (self), NULL);
return priv->title;
}
static void
parent_destroyed (GtkWidget *parent,
GtkNativeDialog *self)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
priv->transient_for = NULL;
}
/**
* gtk_native_dialog_set_transient_for:
* @self: a #GtkNativeDialog
* @parent: (allow-none): parent window, or %NULL
*
* Dialog windows should be set transient for the main application
* window they were spawned from. This allows
* [window managers][gtk-X11-arch] to e.g. keep the
* dialog on top of the main window, or center the dialog over the
* main window.
*
* Passing %NULL for @parent unsets the current transient window.
*/
void
gtk_native_dialog_set_transient_for (GtkNativeDialog *self,
GtkWindow *parent)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_return_if_fail (GTK_IS_NATIVE_DIALOG (self));
if (parent == priv->transient_for)
return;
if (priv->transient_for)
g_signal_handlers_disconnect_by_func (priv->transient_for, parent_destroyed, self);
priv->transient_for = parent;
if (parent)
g_signal_connect (parent, "destroy", G_CALLBACK (parent_destroyed), self);
g_object_notify_by_pspec (G_OBJECT (self), native_props[PROP_TRANSIENT_FOR]);
}
/**
* gtk_native_dialog_get_transient_for:
* @self: a #GtkNativeDialog
*
* Fetches the transient parent for this window. See
* gtk_native_dialog_set_transient_for().
*
introspection: This patch fixes nullable return values fixes for the following symbols in gtk gtk_accel_group_query gtk_accel_group_from_accel_closure gtk_accel_label_get_accel_widget gtk_accessible_get_widget gtk_actionable_get_action_name gtk_app_chooser_get_app_info gtk_app_chooser_button_get_heading gtk_app_chooser_dialog_get_heading gtk_application_get_window_by_id gtk_assistant_get_nth_page gtk_binding_set_find gtk_builder_get_object gtk_builder_lookup_callback_symbol gtk_builder_get_application gtk_button_get_image gtk_cell_area_get_focus_from_sibling gtk_cell_renderer_start_editing gtk_cell_view_get_model gtk_cell_view_get_displayed_row gtk_clipboard_get_owner gtk_container_get_focus_child gtk_container_get_focus_vadjustment gtk_container_get_focus_hadjustment gtk_dialog_get_widget_for_response gtk_drag_get_source_widget gtk_drag_dest_get_target_list gtk_drag_source_get_target_list gtk_entry_completion_get_model gtk_entry_completion_compute_prefix gtk_expander_get_label_widget gtk_file_chooser_get_filename gtk_file_chooser_get_current_folder gtk_file_chooser_get_uri gtk_file_chooser_get_current_folder_uri gtk_file_chooser_get_preview_widget gtk_file_chooser_get_preview_file gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_uri gtk_file_chooser_get_extra_widget gtk_file_chooser_get_filter gtk_file_chooser_native_get_accept_label gtk_file_chooser_native_get_cancel_label gtk_file_filter_get_name gtk_font_chooser_get_font_family gtk_font_chooser_get_font_face gtk_font_chooser_get_font gtk_font_chooser_get_font_desc gtk_font_chooser_get_font_map gtk_frame_get_label gtk_gesture_get_device gtk_gesture_get_window gtk_gl_area_get_error gtk_header_bar_get_title gtk_header_bar_get_subtitle gtk_header_bar_get_custom_title gtk_icon_info_get_filename gtk_icon_view_get_path_at_pos gtk_icon_view_get_model gtk_image_get_pixbuf gtk_image_get_animation gtk_label_get_mnemonic_widget gtk_label_get_attributes gtk_check_version gtk_menu_button_get_popup gtk_menu_button_get_menu_model gtk_menu_button_get_align_widget gtk_menu_button_get_popover gtk_menu_item_get_submenu gtk_menu_item_get_accel_path gtk_native_dialog_get_title gtk_native_dialog_get_transient_for gtk_notebook_get_nth_page gtk_notebook_get_tab_label_text gtk_notebook_get_menu_label gtk_notebook_get_menu_label_text gtk_notebook_get_group_name gtk_notebook_get_action_widget gtk_offscreen_window_get_surface gtk_offscreen_window_get_pixbuf gtk_paned_get_child1 gtk_paned_get_child2 gtk_places_sidebar_get_location gtk_places_sidebar_get_nth_bookmark gtk_plug_get_socket_window gtk_popover_get_default_widget gtk_progress_bar_get_text gtk_recent_filter_get_name gtk_recent_manager_lookup_item gtk_settings_get_default gtk_socket_get_plug_window gtk_stack_sidebar_get_stack gtk_stack_switcher_get_stack gtk_style_context_get_section gtk_style_context_get_parent gtk_style_context_get_frame_clock gtk_test_find_widget gtk_text_buffer_get_mark gtk_text_tag_table_lookup gtk_text_view_get_tabs gtk_text_view_toggle_cursor_visible gtk_text_view_get_window gtk_toolbar_get_nth_item gtk_tool_button_get_label gtk_tool_button_get_icon_name gtk_tool_button_get_label_widget gtk_tool_button_get_icon_widget gtk_tool_palette_get_drop_item gtk_tool_palette_get_drop_group gtk_tree_model_filter_convert_child_path_to_path gtk_tree_model_filter_convert_path_to_child_path gtk_tree_model_sort_convert_child_path_to_path gtk_tree_model_sort_convert_path_to_child_path gtk_tree_view_get_column gtk_tree_view_get_bin_window gtk_tree_view_column_get_widget gtk_tree_view_column_get_tree_view gtk_widget_get_frame_clock gtk_window_group_get_current_device_grab GtkTextBufferSerializeFunc
2015-12-28 20:14:08 +00:00
* Returns: (nullable) (transfer none): the transient parent for this window,
* or %NULL if no transient parent has been set.
**/
GtkWindow *
gtk_native_dialog_get_transient_for (GtkNativeDialog *self)
{
GtkNativeDialogPrivate *priv = gtk_native_dialog_get_instance_private (self);
g_return_val_if_fail (GTK_IS_NATIVE_DIALOG (self), NULL);
return priv->transient_for;
}