Merge branch 'appdialog' into 'main'

Add GtkAppDialog

See merge request GNOME/gtk!5284
This commit is contained in:
Matthias Clasen 2022-12-14 13:24:20 +00:00
commit e3f805f169
15 changed files with 689 additions and 98 deletions

View File

@ -10,6 +10,8 @@
#include <gtk/gtk.h>
static GtkWidget *app_picker;
static void
file_opened (GObject *source,
GAsyncResult *result,
@ -25,12 +27,17 @@ file_opened (GObject *source,
{
g_print ("%s\n", error->message);
g_error_free (error);
gtk_widget_set_sensitive (app_picker, FALSE);
g_object_set_data (G_OBJECT (app_picker), "file", NULL);
return;
}
name = g_file_get_basename (file);
gtk_label_set_label (GTK_LABEL (data), name);
g_free (name);
gtk_widget_set_sensitive (app_picker, TRUE);
g_object_set_data_full (G_OBJECT (app_picker), "file", g_object_ref (file), g_object_unref);
}
static gboolean
@ -65,6 +72,36 @@ open_file (GtkButton *picker,
g_object_unref (dialog);
}
static void
launch_done (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkFileLauncher *launcher = GTK_FILE_LAUNCHER (source);
GError *error = NULL;
if (!gtk_file_launcher_launch_finish (launcher, result, &error))
{
g_print ("%s\n", error->message);
g_error_free (error);
}
}
static void
open_app (GtkButton *picker)
{
GtkWindow *parent = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (picker)));
GtkFileLauncher *launcher;
GFile *file;
file = G_FILE (g_object_get_data (G_OBJECT (picker), "file"));
launcher = gtk_file_launcher_new (file);
gtk_file_launcher_launch (launcher, parent, NULL, launch_done, NULL);
g_object_unref (launcher);
}
GtkWidget *
do_pickers (GtkWidget *do_widget)
{
@ -123,20 +160,17 @@ do_pickers (GtkWidget *do_widget)
gtk_box_append (GTK_BOX (picker), button);
gtk_grid_attach (GTK_GRID (table), picker, 1, 2, 1, 1);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
label = gtk_label_new ("Mail:");
label = gtk_label_new ("Application:");
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
gtk_widget_set_hexpand (label, TRUE);
gtk_grid_attach (GTK_GRID (table), label, 0, 4, 1, 1);
picker = gtk_app_chooser_button_new ("x-scheme-handler/mailto");
gtk_app_chooser_button_set_show_dialog_item (GTK_APP_CHOOSER_BUTTON (picker), TRUE);
G_GNUC_END_IGNORE_DEPRECATIONS
gtk_grid_attach (GTK_GRID (table), label, 0, 3, 1, 1);
gtk_grid_attach (GTK_GRID (table), picker, 1, 3, 1, 1);
app_picker = gtk_button_new_from_icon_name ("emblem-system-symbolic");
gtk_widget_set_halign (app_picker, GTK_ALIGN_END);
gtk_widget_set_sensitive (app_picker, FALSE);
g_signal_connect (app_picker, "clicked", G_CALLBACK (open_app), NULL);
gtk_grid_attach (GTK_GRID (table), app_picker, 1, 4, 1, 1);
}
if (!gtk_widget_get_visible (window))

View File

@ -27,6 +27,8 @@
#include "gtkalertdialog.h"
#include <glib/gi18n-lib.h>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct {
GtkWindow *parent;
GAppLaunchContext *context;
@ -96,6 +98,8 @@ window_handle_exported (GtkWindow *window,
*
* This is the recommended call to be used as it passes information
* necessary for sandbox helpers to parent their dialogs properly.
*
* Deprecated: 4.10: Use [method@Gtk.FileLauncher.launch] instead
*/
void
gtk_show_uri_full (GtkWindow *parent,
@ -142,6 +146,8 @@ gtk_show_uri_full (GtkWindow *parent,
*
* Returns: %TRUE if the URI was shown successfully.
* Otherwise, %FALSE is returned and @error is set
*
* Deprecated: 4.10: Use [method@Gtk.FileLauncher.launch_finish] instead
*/
gboolean
gtk_show_uri_full_finish (GtkWindow *parent,
@ -184,6 +190,8 @@ show_uri_done (GObject *object,
*
* This function launches the default application for showing
* a given uri, or shows an error dialog if that fails.
*
* Deprecated: 4.10: Use [method@Gtk.FileLauncher.launch] instead
*/
void
gtk_show_uri (GtkWindow *parent,

View File

@ -29,7 +29,7 @@
G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_10_FOR(gtk_file_launcher_launch)
void gtk_show_uri_full (GtkWindow *parent,
const char *uri,
guint32 timestamp,
@ -37,12 +37,12 @@ void gtk_show_uri_full (GtkWindow *parent,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_10_FOR(gtk_file_launcher_launch)
gboolean gtk_show_uri_full_finish (GtkWindow *parent,
GAsyncResult *result,
GError **error);
GDK_AVAILABLE_IN_ALL
GDK_DEPRECATED_IN_4_10_FOR(gtk_file_launcher_launch)
void gtk_show_uri (GtkWindow *parent,
const char *uri,
guint32 timestamp);

View File

@ -32,6 +32,7 @@ gtk_deprecated_sources = [
'deprecated/gtkinfobar.c',
'deprecated/gtkliststore.c',
'deprecated/gtkrender.c',
'deprecated/gtkshow.c',
'deprecated/gtkstylecontext.c',
'deprecated/gtktreedatalist.c',
'deprecated/gtktreednd.c',
@ -88,6 +89,7 @@ gtk_deprecated_headers = [
'deprecated/gtkliststore.h',
'deprecated/gtkmessagedialog.h',
'deprecated/gtkrender.h',
'deprecated/gtkshow.h',
'deprecated/gtkstylecontext.h',
'deprecated/gtktreednd.h',
'deprecated/gtktreemodel.h',

View File

@ -130,6 +130,7 @@
#include <gtk/deprecated/gtkfilechooserwidget.h>
#include <gtk/gtkfiledialog.h>
#include <gtk/gtkfilefilter.h>
#include <gtk/gtkfilelauncher.h>
#include <gtk/gtkfilter.h>
#include <gtk/gtkfilterlistmodel.h>
#include <gtk/gtkcustomfilter.h>
@ -237,7 +238,7 @@
#include <gtk/gtkshortcutsshortcut.h>
#include <gtk/gtkshortcutswindow.h>
#include <gtk/gtkshortcuttrigger.h>
#include <gtk/gtkshow.h>
#include <gtk/deprecated/gtkshow.h>
#include <gtk/gtksignallistitemfactory.h>
#include <gtk/gtksingleselection.h>
#include <gtk/gtkslicelistmodel.h>

View File

@ -44,7 +44,7 @@
#include "gtkorientable.h"
#include "gtkscrolledwindow.h"
#include "gtktextview.h"
#include "gtkshow.h"
#include "gtkfilelauncher.h"
#include "gtkmain.h"
#include "gtktogglebutton.h"
#include "gtktypebuiltins.h"
@ -78,7 +78,7 @@
* ![An example GtkAboutDialog](aboutdialog.png)
*
* About dialogs often contain links and email addresses. `GtkAboutDialog`
* displays these as clickable links. By default, it calls [func@Gtk.show_uri]
* displays these as clickable links. By default, it calls [method@Gtk.FileLauncher.launch]
* when a user clicks one. The behaviour can be overridden with the
* [signal@Gtk.AboutDialog::activate-link] signal.
*
@ -361,7 +361,7 @@ gtk_about_dialog_class_init (GtkAboutDialogClass *klass)
* Emitted every time a URL is activated.
*
* Applications may connect to it to override the default behaviour,
* which is to call [func@Gtk.show_uri].
* which is to call [method@Gtk.FileLauncher.launch].
*
* Returns: `TRUE` if the link has been activated
*/
@ -932,7 +932,17 @@ static gboolean
gtk_about_dialog_activate_link (GtkAboutDialog *about,
const char *uri)
{
gtk_show_uri (GTK_WINDOW (about), uri, GDK_CURRENT_TIME);
GtkFileLauncher *launcher;
GFile *file;
file = g_file_new_for_uri (uri);
launcher = gtk_file_launcher_new (file);
gtk_file_launcher_launch (launcher, GTK_WINDOW (about), NULL, NULL, NULL);
g_object_unref (launcher);
g_object_unref (file);
return TRUE;
}

View File

@ -64,7 +64,7 @@
#include "gtkcheckbutton.h"
#include "gtkwindowgroup.h"
#include <glib/gi18n-lib.h>
#include "gtkshow.h"
#include "gtkfilelauncher.h"
#include "gtkmain.h"
#include "gtkscrollable.h"
#include "gtkpopover.h"
@ -1370,68 +1370,14 @@ open_folder_cb (GSimpleAction *action,
GtkWindow *toplevel = GTK_IS_WINDOW (root) ? GTK_WINDOW (root) : NULL;
GFileInfo *info;
GFile *file;
char *uri;
GtkFileLauncher *launcher;
info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), impl->browse_files_popover_item);
file = _gtk_file_info_get_file (info);
#ifdef G_OS_WIN32
uri = g_file_get_uri (file);
gtk_show_uri (toplevel, uri, GDK_CURRENT_TIME);
g_free (uri);
#else
if (gdk_should_use_portal ())
{
g_openuri_portal_open_async (file, TRUE, toplevel, NULL, NULL, NULL);
}
else
{
GDBusConnection *bus;
GVariantBuilder uris_builder;
GVariant *result;
GError *error = NULL;
uri = g_file_get_uri (file);
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_variant_builder_init (&uris_builder, G_VARIANT_TYPE ("as"));
g_variant_builder_add (&uris_builder, "s", uri);
result = g_dbus_connection_call_sync (bus,
FILE_MANAGER_DBUS_NAME,
FILE_MANAGER_DBUS_PATH,
FILE_MANAGER_DBUS_IFACE,
"ShowFolders",
g_variant_new ("(ass)", &uris_builder, ""),
NULL, /* ignore returned type */
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (error)
{
if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER) ||
g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
g_debug ("No " FILE_MANAGER_DBUS_NAME " available");
else
g_warning ("Failed to call ShowFolders: %s", error->message);
g_error_free (error);
}
if (result)
g_variant_unref (result);
else
gtk_show_uri (toplevel, uri, GDK_CURRENT_TIME);
g_free (uri);
}
#endif
launcher = gtk_file_launcher_new (file);
gtk_file_launcher_open_containing_folder (launcher, toplevel, NULL, NULL, NULL);
g_object_unref (launcher);
g_clear_object (&info);
}

502
gtk/gtkfilelauncher.c Normal file
View File

@ -0,0 +1,502 @@
/*
* GTK - The GIMP Toolkit
* Copyright (C) 2022 Red Hat, Inc.
* All rights reserved.
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkfilelauncher.h"
#include "gtkdialogerror.h"
#include "gopenuriportal.h"
#include "deprecated/gtkshow.h"
#include <glib/gi18n-lib.h>
/**
* GtkFileLauncher:
*
* A `GtkFileLauncher` object collects the arguments that are needed to open a uri
* with an application.
*
* Depending on system configuration, user preferences and available APIs, this
* may or may not show an app chooser dialog or launch the default application
* right away.
*
* The operation is started with the [method@Gtk.FileLauncher.launch] function.
* This API follows the GIO async pattern, and the result can be obtained by
* calling [method@Gtk.FileLauncher.launch_finish].
*
* Since: 4.10
*/
/* {{{ GObject implementation */
struct _GtkFileLauncher
{
GObject parent_instance;
GFile *file;
};
enum {
PROP_FILE = 1,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
G_DEFINE_TYPE (GtkFileLauncher, gtk_file_launcher, G_TYPE_OBJECT)
static void
gtk_file_launcher_init (GtkFileLauncher *self)
{
}
static void
gtk_file_launcher_finalize (GObject *object)
{
//GtkFileLauncher *self = GTK_FILE_LAUNCHER (object);
G_OBJECT_CLASS (gtk_file_launcher_parent_class)->finalize (object);
}
static void
gtk_file_launcher_get_property (GObject *object,
unsigned int property_id,
GValue *value,
GParamSpec *pspec)
{
GtkFileLauncher *self = GTK_FILE_LAUNCHER (object);
switch (property_id)
{
case PROP_FILE:
g_value_set_object (value, self->file);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_file_launcher_set_property (GObject *object,
unsigned int property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkFileLauncher *self = GTK_FILE_LAUNCHER (object);
switch (property_id)
{
case PROP_FILE:
gtk_file_launcher_set_file (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_file_launcher_class_init (GtkFileLauncherClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_file_launcher_finalize;
object_class->get_property = gtk_file_launcher_get_property;
object_class->set_property = gtk_file_launcher_set_property;
/**
* GtkFileLauncher:file: (attributes org.gtk.Property.get=gtk_file_launcher_get_file org.gtk.Property.set=gtk_file_launcher_set_file)
*
* The file to launch.
*
* Since: 4.10
*/
properties[PROP_FILE] =
g_param_spec_object ("file", NULL, NULL,
G_TYPE_FILE,
G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
/* }}} */
/* {{{ API: Constructor */
/**
* gtk_file_launcher_new:
* @file: (nullable): the file to open
*
* Creates a new `GtkFileLauncher` object.
*
* Returns: the new `GtkFileLauncher`
*
* Since: 4.10
*/
GtkFileLauncher *
gtk_file_launcher_new (GFile *file)
{
return g_object_new (GTK_TYPE_FILE_LAUNCHER,
"file", file,
NULL);
}
/* }}} */
/* {{{ API: Getters and setters */
/**
* gtk_file_launcher_get_file:
* @self: a `GtkFileLauncher`
*
* Gets the file that will be opened.
*
* Returns: (transfer none) (nullable): the file
*
* Since: 4.10
*/
GFile *
gtk_file_launcher_get_file (GtkFileLauncher *self)
{
g_return_val_if_fail (GTK_IS_FILE_LAUNCHER (self), NULL);
return self->file;
}
/**
* gtk_file_launcher_set_file:
* @self: a `GtkFileLauncher`
* @file: a `GFile`
*
* Sets the file that will be opened.
*
* Since: 4.10
*/
void
gtk_file_launcher_set_file (GtkFileLauncher *self,
GFile *file)
{
g_return_if_fail (GTK_IS_FILE_LAUNCHER (self));
g_return_if_fail (G_IS_FILE (file));
if (!g_set_object (&self->file, file))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]);
}
/* }}} */
/* {{{ Async implementation */
#ifndef G_OS_WIN32
static void
open_done (GObject *source,
GAsyncResult *result,
gpointer data)
{
GTask *task = G_TASK (data);
GError *error = NULL;
if (!g_openuri_portal_open_finish (result, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
#endif
static void
show_folder_done (GObject *source,
GAsyncResult *result,
gpointer data)
{
GDBusConnection *bus = G_DBUS_CONNECTION (source);
GTask *task = G_TASK (data);
GVariant *res;
GError *error = NULL;
res = g_dbus_connection_call_finish (bus, result, &error);
if (res)
g_variant_unref (res);
if (error)
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED, "Cancelled by user");
else
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, "%s", error->message);
g_error_free (error);
}
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
#define FILE_MANAGER_DBUS_NAME "org.freedesktop.FileManager1"
#define FILE_MANAGER_DBUS_IFACE "org.freedesktop.FileManager1"
#define FILE_MANAGER_DBUS_PATH "/org/freedesktop/FileManager1"
static void
show_folder (GtkWindow *parent,
const char *uri,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GDBusConnection *bus;
GVariantBuilder uris_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_STRING_ARRAY);
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
if (!bus)
{
g_task_return_new_error (G_TASK (user_data),
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"Session bus not available");
g_object_unref (G_TASK (user_data));
return;
}
g_variant_builder_add (&uris_builder, "s", uri);
g_dbus_connection_call (bus,
FILE_MANAGER_DBUS_NAME,
FILE_MANAGER_DBUS_PATH,
FILE_MANAGER_DBUS_IFACE,
"ShowFolders",
g_variant_new ("(ass)", &uris_builder, ""),
NULL, /* ignore returned type */
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
show_folder_done,
user_data);
}
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static void
show_uri_done (GObject *source,
GAsyncResult *result,
gpointer data)
{
GtkWindow *parent = GTK_WINDOW (source);
GTask *task = G_TASK (data);
GError *error = NULL;
if (!gtk_show_uri_full_finish (parent, result, &error))
{
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_CANCELLED, "Cancelled by user");
else
g_task_return_new_error (task, GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED, "%s", error->message);
g_error_free (error);
}
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
G_GNUC_END_IGNORE_DEPRECATIONS
/* }}} */
/* {{{ Async API */
/**
* gtk_file_launcher_launch:
* @self: a `GtkFileLauncher`
* @parent: (nullable): the parent `GtkWindow`
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* Launch an application to open the file.
*
* This may present an app chooser dialog to the user.
*
* The @callback will be called when the operation is completed.
* It should call [method@Gtk.FileLauncher.launch_finish] to obtain
* the result.
*
* Since: 4.10
*/
void
gtk_file_launcher_launch (GtkFileLauncher *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_return_if_fail (GTK_IS_FILE_LAUNCHER (self));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_source_tag (task, gtk_file_launcher_launch);
if (self->file == NULL)
{
g_task_return_new_error (task,
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"No file to launch");
return;
}
#ifndef G_OS_WIN32
if (g_openuri_portal_is_available ())
{
g_openuri_portal_open_async (self->file, FALSE, parent, cancellable, open_done, task);
}
else
#endif
{
char *uri = g_file_get_uri (self->file);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_show_uri_full (parent, uri, GDK_CURRENT_TIME, cancellable, show_uri_done, task);
G_GNUC_END_IGNORE_DEPRECATIONS
g_free (uri);
}
}
/**
* gtk_file_launcher_launch_finish:
* @self: a `GtkFileLauncher`
* @result: a `GAsyncResult`
* @error: return location for a [enum@Gtk.DialogError] or [enum@Gio.Error] error
*
* Finishes the [method@Gtk.FileLauncher.launch] call and
* returns the result.
*
* Returns: `TRUE` if an application was launched,
* or `FALSE` and @error is set
*
* Since: 4.10
*/
gboolean
gtk_file_launcher_launch_finish (GtkFileLauncher *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GTK_IS_FILE_LAUNCHER (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_file_launcher_launch, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/**
* gtk_file_launcher_open_containing_folder:
* @self: a `GtkFileLauncher`
* @parent: (nullable): the parent `GtkWindow`
* @cancellable: (nullable): a `GCancellable` to cancel the operation
* @callback: (scope async): a callback to call when the operation is complete
* @user_data: (closure callback): data to pass to @callback
*
* Launch a file manager to show the file in its parent directory.
*
* This is only supported native files. It will fail if @file
* is e.g. a http:// uri.
*
* The @callback will be called when the operation is completed.
* It should call [method@Gtk.FileLauncher.open_containing_folder_finish]
* to obtain the result.
*
* Since: 4.10
*/
void
gtk_file_launcher_open_containing_folder (GtkFileLauncher *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
g_return_if_fail (GTK_IS_FILE_LAUNCHER (self));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_check_cancellable (task, FALSE);
g_task_set_source_tag (task, gtk_file_launcher_open_containing_folder);
if (self->file == NULL)
{
g_task_return_new_error (task,
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"No file to open");
return;
}
if (!g_file_is_native (self->file))
{
g_task_return_new_error (task,
GTK_DIALOG_ERROR, GTK_DIALOG_ERROR_FAILED,
"Operation not supported on non-native files");
return;
}
#ifndef G_OS_WIN32
if (g_openuri_portal_is_available ())
{
g_openuri_portal_open_async (self->file, TRUE, parent, cancellable, open_done, task);
}
else
#endif
{
char *uri = g_file_get_uri (self->file);
show_folder (parent, uri, cancellable, show_folder_done, task);
g_free (uri);
}
}
/**
* gtk_file_launcher_open_containing_folder_finish:
* @self: a `GtkFileLauncher`
* @result: a `GAsyncResult`
* @error: return location for a [enum@Gtk.DialogError] or [enum@Gio.Error] error
*
* Finishes the [method@Gtk.FileLauncher.open_containing_folder]
* call and returns the result.
*
* Returns: `TRUE` if an application was launched,
* or `FALSE` and @error is set
*
* Since: 4.10
*/
gboolean
gtk_file_launcher_open_containing_folder_finish (GtkFileLauncher *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (GTK_IS_FILE_LAUNCHER (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gtk_file_launcher_open_containing_folder, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

68
gtk/gtkfilelauncher.h Normal file
View File

@ -0,0 +1,68 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2022 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/>.
*/
#pragma once
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
G_BEGIN_DECLS
#define GTK_TYPE_FILE_LAUNCHER (gtk_file_launcher_get_type ())
GDK_AVAILABLE_IN_4_10
G_DECLARE_FINAL_TYPE (GtkFileLauncher, gtk_file_launcher, GTK, FILE_LAUNCHER, GObject)
GDK_AVAILABLE_IN_4_10
GtkFileLauncher * gtk_file_launcher_new (GFile *file);
GDK_AVAILABLE_IN_4_10
GFile * gtk_file_launcher_get_file (GtkFileLauncher *self);
GDK_AVAILABLE_IN_4_10
void gtk_file_launcher_set_file (GtkFileLauncher *self,
GFile *file);
GDK_AVAILABLE_IN_4_10
void gtk_file_launcher_launch (GtkFileLauncher *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_launcher_launch_finish (GtkFileLauncher *self,
GAsyncResult *result,
GError **error);
GDK_AVAILABLE_IN_4_10
void gtk_file_launcher_open_containing_folder (GtkFileLauncher *self,
GtkWindow *parent,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GDK_AVAILABLE_IN_4_10
gboolean gtk_file_launcher_open_containing_folder_finish (GtkFileLauncher *self,
GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -40,7 +40,7 @@
#include "gtkshortcut.h"
#include "gtkshortcutcontroller.h"
#include "gtkshortcuttrigger.h"
#include "gtkshow.h"
#include "gtkfilelauncher.h"
#include "gtksnapshot.h"
#include "gtkrenderbackgroundprivate.h"
#include "gtkrenderborderprivate.h"
@ -2102,11 +2102,17 @@ gtk_label_activate_link (GtkLabel *self,
{
GtkWidget *widget = GTK_WIDGET (self);
GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (widget));
GFile *file;
GtkFileLauncher *launcher;
if (!GTK_IS_WINDOW (toplevel))
return FALSE;
gtk_show_uri (GTK_WINDOW (toplevel), uri, GDK_CURRENT_TIME);
file = g_file_new_for_uri (uri);
launcher = gtk_file_launcher_new (file);
gtk_file_launcher_launch (launcher, GTK_WINDOW (toplevel), NULL, NULL, NULL);
g_object_unref (launcher);
g_object_unref (file);
return TRUE;
}
@ -2281,7 +2287,7 @@ gtk_label_class_init (GtkLabelClass *class)
* Gets emitted to activate a URI.
*
* Applications may connect to it to override the default behaviour,
* which is to call gtk_show_uri().
* which is to call [method@Gtk.FileLauncher.launch].
*
* Returns: %TRUE if the link has been activated
*/

View File

@ -37,7 +37,7 @@
* The URI bound to a `GtkLinkButton` can be set specifically using
* [method@Gtk.LinkButton.set_uri].
*
* By default, `GtkLinkButton` calls [func@Gtk.show_uri] when the button
* By default, `GtkLinkButton` calls [method@Gtk.FileLauncher.launch] when the button
* is clicked. This behaviour can be overridden by connecting to the
* [signal@Gtk.LinkButton::activate-link] signal and returning %TRUE from
* the signal handler.
@ -65,7 +65,7 @@
#include "gtkmarshalers.h"
#include "gtkpopovermenu.h"
#include "gtkprivate.h"
#include "gtkshow.h"
#include "gtkfilelauncher.h"
#include "gtksizerequest.h"
#include "gtktooltip.h"
#include "gtkwidgetprivate.h"
@ -198,7 +198,7 @@ gtk_link_button_class_init (GtkLinkButtonClass *klass)
*
* Emitted each time the `GtkLinkButton` is clicked.
*
* The default handler will call [func@Gtk.show_uri] with the URI
* The default handler will call [method@Gtk.FileLauncher.launch] with the URI
* stored inside the [property@Gtk.LinkButton:uri] property.
*
* To override the default behavior, you can connect to the
@ -479,10 +479,17 @@ static gboolean
gtk_link_button_activate_link (GtkLinkButton *link_button)
{
GtkWidget *toplevel;
GFile *file;
GtkFileLauncher *launcher;
toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (link_button)));
gtk_show_uri (GTK_WINDOW (toplevel), link_button->uri, GDK_CURRENT_TIME);
file = g_file_new_for_uri (link_button->uri);
launcher = gtk_file_launcher_new (file);
gtk_file_launcher_launch (launcher, GTK_WINDOW (toplevel), NULL, NULL, NULL);
g_object_unref (launcher);
g_object_unref (file);
gtk_link_button_set_visited (link_button, TRUE);
return TRUE;

View File

@ -34,7 +34,7 @@
#include "gtkprintsettings.h"
#include "gtkpagesetup.h"
#include "gtkprintbackendprivate.h"
#include "gtkshow.h"
#include "gtkfilelauncher.h"
#include <glib/gi18n-lib.h>
#include "gtkwindowprivate.h"
#include "gtkprivate.h"
@ -677,9 +677,12 @@ gtk_print_operation_portal_launch_preview (GtkPrintOperation *op,
GtkWindow *parent,
const char *filename)
{
char *uri;
GFile *file;
GtkFileLauncher *launcher;
uri = g_filename_to_uri (filename, NULL, NULL);
gtk_show_uri (parent, uri, GDK_CURRENT_TIME);
g_free (uri);
file = g_file_new_for_path (filename);
launcher = gtk_file_launcher_new (file);
gtk_file_launcher_launch (launcher, parent, NULL, NULL, NULL);
g_object_unref (launcher);
g_object_unref (file);
}

View File

@ -41,6 +41,7 @@
#include "gtkprinter.h"
#include "gtkprintjob.h"
#include "gtklabel.h"
#include "gtkfilelauncher.h"
#include <glib/gi18n-lib.h>
@ -304,15 +305,17 @@ gtk_print_operation_unix_launch_preview (GtkPrintOperation *op,
if (error != NULL)
{
char * uri;
GFile *file;
GtkFileLauncher *launcher;
g_warning ("Error launching preview: %s", error->message);
g_clear_error (&error);
g_error_free (error);
error = NULL;
uri = g_filename_to_uri (filename, NULL, NULL);
gtk_show_uri (parent, uri, GDK_CURRENT_TIME);
g_free (uri);
file = g_file_new_for_path (filename);
launcher = gtk_file_launcher_new (file);
gtk_file_launcher_launch (launcher, parent, NULL, NULL, NULL);
g_object_unref (launcher);
g_object_unref (file);
}
out:

View File

@ -227,6 +227,7 @@ gtk_public_sources = files([
'gtkfilechooserwidget.c',
'gtkfiledialog.c',
'gtkfilefilter.c',
'gtkfilelauncher.c',
'gtkfilter.c',
'gtkfilterlistmodel.c',
'gtkfixed.c',
@ -345,7 +346,6 @@ gtk_public_sources = files([
'gtkshortcutsshortcut.c',
'gtkshortcutswindow.c',
'gtkshortcuttrigger.c',
'gtkshow.c',
'gtksidebarrow.c',
'gtksignallistitemfactory.c',
'gtksingleselection.c',
@ -478,6 +478,7 @@ gtk_public_headers = files([
'gtkexpression.h',
'gtkfiledialog.h',
'gtkfilefilter.h',
'gtkfilelauncher.h',
'gtkfilter.h',
'gtkfilterlistmodel.h',
'gtkfixed.h',
@ -578,7 +579,6 @@ gtk_public_headers = files([
'gtkshortcutsshortcut.h',
'gtkshortcutswindow.h',
'gtkshortcuttrigger.h',
'gtkshow.h',
'gtksignallistitemfactory.h',
'gtksingleselection.h',
'gtksizegroup.h',

View File

@ -88,6 +88,7 @@ gtk/deprecated/gtkfontbutton.c
gtk/deprecated/gtkfontchooser.c
gtk/deprecated/gtkiconview.c
gtk/deprecated/gtkliststore.c
gtk/deprecated/gtkshow.c
gtk/deprecated/gtkstylecontext.c
gtk/deprecated/gtktreednd.c
gtk/deprecated/gtktreemodel.c
@ -177,6 +178,7 @@ gtk/gtkfilechooserutils.c
gtk/gtkfilechooserwidget.c
gtk/gtkfiledialog.c
gtk/gtkfilefilter.c
gtk/gtkfilelauncher.c
gtk/gtkfilesystemmodel.c
gtk/gtkfilethumbnail.c
gtk/gtkfilter.c
@ -298,7 +300,6 @@ gtk/gtkshortcutssection.c
gtk/gtkshortcutsshortcut.c
gtk/gtkshortcutswindow.c
gtk/gtkshortcuttrigger.c
gtk/gtkshow.c
gtk/gtksidebarrow.c
gtk/gtksignallistitemfactory.c
gtk/gtksingleselection.c