
The idea is to add some "demo" attribute to a list item inside the <release> tag, since we already decided that (for now at least) we'd keep a strict "intro + list" logics, as we did until now. This demo attribute uses an internal format to specify successive widgets to blink (like a demo path towards a feature). For now, what it allows is: * raise the toolbox, select a tool and blink the tool button. * raise a dockable, blink any widgets in there. Now it is still limited and needs to evolve. In particular: * What happens if the blinked tool button was explicitly removed from Preferences? Should we re-add it for the demo? And once done, should we remove it again? Then should we select back the tool previously selected? * What happens if the dockable widget is not visible? Should we allow changing the settings to be able to demo correctly the new/changed settings? Should it be temporary? If temporary, it can be annoying as you'd still have to look attentively the demo to find back the path to the new settings. If not temporary, some people may dislike we touch their settings. * What if docks are hidden? Should we unhide them, then hide them back after demo time? Also regarding the implementation: originally I wanted to just grab the demo attribute directly from the AppStream metadata file, but I realized that appstream-glib cleans out unknown attribute from the XML. I could then simply parse the file with a generic XML parser, but I found simpler to pre-parse it into a header built within GIMP. I still use appstream-glib at runtime as it takes care of localization for us (though in the same time, we also have the localization in the main po files, so maybe we could just embed the release note strings as well). See appstream-glib report: https://github.com/hughsie/appstream-glib/issues/431
642 lines
23 KiB
C
642 lines
23 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* welcome-dialog.c
|
|
* Copyright (C) 2022 Jehan
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <appstream-glib.h>
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
#include <gdk/gdkwayland.h>
|
|
#endif
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "dialogs-types.h"
|
|
|
|
#include "config/gimpguiconfig.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimp-utils.h"
|
|
|
|
#include "widgets/gimpaction.h"
|
|
#include "widgets/gimpdialogfactory.h"
|
|
#include "widgets/gimphelp-ids.h"
|
|
#include "widgets/gimptoolbox.h"
|
|
#include "widgets/gimpuimanager.h"
|
|
#include "widgets/gimpwidgets-utils.h"
|
|
#include "widgets/gimpwindowstrategy.h"
|
|
|
|
#include "welcome-dialog.h"
|
|
#include "welcome-dialog-data.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
static void welcome_dialog_release_item_activated (GtkListBox *listbox,
|
|
GtkListBoxRow *row,
|
|
gpointer user_data);
|
|
static void welcome_add_link (GtkGrid *grid,
|
|
gint column,
|
|
gint *row,
|
|
const gchar *emoji,
|
|
const gchar *title,
|
|
const gchar *link);
|
|
static void welcome_size_allocate (GtkWidget *welcome_dialog,
|
|
GtkAllocation *allocation,
|
|
gpointer user_data);
|
|
|
|
|
|
GtkWidget *
|
|
welcome_dialog_create (Gimp *gimp)
|
|
{
|
|
GtkWidget *welcome_dialog;
|
|
AsApp *app = NULL;
|
|
const gchar *release_notes = NULL;
|
|
GError *error = NULL;
|
|
|
|
GList *windows;
|
|
|
|
GtkWidget *main_vbox;
|
|
GtkWidget *stack;
|
|
GtkWidget *grid;
|
|
GtkWidget *switcher;
|
|
|
|
GtkWidget *scrolled_window;
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *image;
|
|
GtkWidget *listbox;
|
|
GtkWidget *widget;
|
|
|
|
gchar *release_link;
|
|
gchar *appdata_path;
|
|
gchar *title;
|
|
gchar *markup;
|
|
gchar *release_introduction = NULL;
|
|
GList *release_items = NULL;
|
|
gchar *tmp;
|
|
|
|
gint row;
|
|
|
|
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
|
|
|
|
/* Why I am using gimp_data_directory() then backing out 2 directories
|
|
* instead of directly using gimp_installation_directory () is because
|
|
* the 'datadir' might be customized, so I don't want to hardcode
|
|
* "share". It might be something else.
|
|
*/
|
|
appdata_path = g_build_filename (gimp_data_directory (),
|
|
"..", "..", "metainfo",
|
|
"org.gimp.GIMP.appdata.xml",
|
|
NULL);
|
|
if (! g_file_test (appdata_path, G_FILE_TEST_IS_REGULAR))
|
|
{
|
|
/* This should not happen since we install explicitly this file in
|
|
* metainfo/, but flatpak at least is overriding our install and
|
|
* moving the file to appdata/ (which used to be the legacy
|
|
* location). Hopefully they are the only ones doing it, but just
|
|
* in case, let's make an alternative check in this other
|
|
* location.
|
|
*/
|
|
g_printerr ("%s: AppStream file '%s' is not a regular file.\n",
|
|
G_STRFUNC, appdata_path);
|
|
g_free (appdata_path);
|
|
appdata_path = g_build_filename (gimp_data_directory (),
|
|
"..", "..", "appdata",
|
|
"org.gimp.GIMP.appdata.xml",
|
|
NULL);
|
|
}
|
|
if (g_file_test (appdata_path, G_FILE_TEST_IS_REGULAR))
|
|
{
|
|
AsRelease *release;
|
|
|
|
app = as_app_new ();
|
|
if (as_app_parse_file (app, appdata_path,
|
|
AS_APP_PARSE_FLAG_USE_HEURISTICS,
|
|
&error))
|
|
{
|
|
if ((release = as_app_get_release (app, GIMP_VERSION)) != NULL)
|
|
release_notes = as_release_get_description (release, g_getenv ("LANGUAGE")) ?
|
|
as_release_get_description (release, g_getenv ("LANGUAGE")) :
|
|
as_release_get_description (release, NULL);
|
|
else if (GIMP_MICRO_VERSION % 2 == 0)
|
|
g_printerr ("%s: no <release> tag for version %s in '%s'\n",
|
|
G_STRFUNC, GIMP_VERSION, appdata_path);
|
|
}
|
|
else if (error)
|
|
{
|
|
g_printerr ("%s: %s\n", G_STRFUNC, error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
else
|
|
{
|
|
g_printerr ("%s: failed to load AppStream file '%s'\n", G_STRFUNC, appdata_path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Note that none of the errors here and above should happen.
|
|
* Each of our releases (even micro value) should have a <release>
|
|
* tag. But I am just printing to stderr and half-ignoring the
|
|
* miss because it is not serious enough to break normal GIMP
|
|
* usage.
|
|
*/
|
|
g_printerr ("%s: AppStream file '%s' is not a regular file.\n", G_STRFUNC, appdata_path);
|
|
}
|
|
g_free (appdata_path);
|
|
|
|
/* Translators: the %s string will be the version, e.g. "3.0". */
|
|
title = g_strdup_printf (_("Welcome to GIMP %s"), GIMP_VERSION);
|
|
windows = gimp_get_image_windows (gimp);
|
|
welcome_dialog = gimp_dialog_new (title,
|
|
"gimp-welcome-dialog",
|
|
windows ? windows->data : NULL,
|
|
0, NULL, NULL,
|
|
NULL);
|
|
g_list_free (windows);
|
|
gtk_window_set_position (GTK_WINDOW (welcome_dialog), GTK_WIN_POS_CENTER_ON_PARENT);
|
|
g_free (title);
|
|
|
|
g_signal_connect (welcome_dialog,
|
|
"response",
|
|
G_CALLBACK (gtk_widget_destroy),
|
|
NULL);
|
|
|
|
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (welcome_dialog))),
|
|
main_vbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (main_vbox);
|
|
|
|
stack = gtk_stack_new ();
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), stack, TRUE, TRUE, 0);
|
|
gtk_widget_show (stack);
|
|
|
|
/****************/
|
|
/* Welcome page */
|
|
/****************/
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_stack_add_titled (GTK_STACK (stack), vbox, "welcome",
|
|
"Welcome");
|
|
gtk_widget_show (vbox);
|
|
|
|
image = gtk_image_new_from_icon_name ("gimp-wilber",
|
|
GTK_ICON_SIZE_DIALOG);
|
|
gtk_widget_set_valign (image, GTK_ALIGN_CENTER);
|
|
gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
|
|
g_signal_connect (welcome_dialog,
|
|
"size-allocate",
|
|
G_CALLBACK (welcome_size_allocate),
|
|
image);
|
|
|
|
/* Welcome title. */
|
|
|
|
/* Translators: the %s string will be the version, e.g. "3.0". */
|
|
tmp = g_strdup_printf (_("You installed GIMP %s!"), GIMP_VERSION);
|
|
markup = g_strdup_printf ("<big>%s</big>", tmp);
|
|
g_free (tmp);
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (widget), markup);
|
|
g_free (markup);
|
|
gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
|
|
gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_CENTER);
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), FALSE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
|
|
gtk_widget_show (widget);
|
|
|
|
grid = gtk_grid_new ();
|
|
gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE);
|
|
gtk_grid_set_row_spacing (GTK_GRID (grid), 0);
|
|
gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
|
|
gtk_box_pack_start (GTK_BOX (vbox), grid, TRUE, TRUE, 0);
|
|
gtk_widget_show (grid);
|
|
|
|
/* Welcome message: left */
|
|
|
|
markup = _("GIMP is a Free Software for image authoring and manipulation.\n"
|
|
"Want to know more?");
|
|
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (widget), 30);
|
|
/*gtk_widget_set_size_request (widget, max_width / 2, -1);*/
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
|
|
gtk_widget_set_vexpand (widget, FALSE);
|
|
gtk_widget_set_hexpand (widget, FALSE);
|
|
|
|
/* Making sure the labels are well top aligned to avoid some ugly
|
|
* misalignment if left and right labels have different sizes,
|
|
* but also left-aligned so that the messages are slightly to the left
|
|
* of the emoji/link list below.
|
|
* Following design decisions by Aryeom.
|
|
*/
|
|
gtk_label_set_xalign (GTK_LABEL (widget), 0.0);
|
|
gtk_label_set_yalign (GTK_LABEL (widget), 0.0);
|
|
gtk_widget_set_margin_bottom (widget, 10);
|
|
gtk_label_set_markup (GTK_LABEL (widget), markup);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), widget, 0, 0, 1, 1);
|
|
|
|
gtk_widget_show (widget);
|
|
|
|
row = 1;
|
|
welcome_add_link (GTK_GRID (grid), 0, &row,
|
|
/* "globe with meridians" emoticone in UTF-8. */
|
|
"\xf0\x9f\x8c\x90",
|
|
_("GIMP website"), "https://www.gimp.org/");
|
|
welcome_add_link (GTK_GRID (grid), 0, &row,
|
|
/* "graduation cap" emoticone in UTF-8. */
|
|
"\xf0\x9f\x8e\x93",
|
|
_("Tutorials"),
|
|
"https://www.gimp.org/tutorials/");
|
|
welcome_add_link (GTK_GRID (grid), 0, &row,
|
|
/* "open book" emoticone in UTF-8. */
|
|
"\xf0\x9f\x93\x96",
|
|
_("Documentation"),
|
|
"https://docs.gimp.org/");
|
|
|
|
/* XXX: should we add API docs for plug-in developers once it's
|
|
* properly set up? */
|
|
|
|
/* Welcome message: right */
|
|
|
|
markup = _("GIMP is a Community Software under the GNU general public license v3.\n"
|
|
"Want to contribute?");
|
|
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (widget), 30);
|
|
/*gtk_widget_set_size_request (widget, max_width / 2, -1);*/
|
|
|
|
/* Again the alignments are important. */
|
|
gtk_label_set_xalign (GTK_LABEL (widget), 0.0);
|
|
gtk_widget_set_vexpand (widget, FALSE);
|
|
gtk_widget_set_hexpand (widget, FALSE);
|
|
gtk_label_set_xalign (GTK_LABEL (widget), 0.0);
|
|
gtk_label_set_yalign (GTK_LABEL (widget), 0.0);
|
|
gtk_widget_set_margin_bottom (widget, 10);
|
|
gtk_label_set_markup (GTK_LABEL (widget), markup);
|
|
|
|
gtk_grid_attach (GTK_GRID (grid), widget, 1, 0, 1, 1);
|
|
|
|
gtk_widget_show (widget);
|
|
|
|
row = 1;
|
|
welcome_add_link (GTK_GRID (grid), 1, &row,
|
|
/* "keyboard" emoticone in UTF-8. */
|
|
"\xe2\x8c\xa8",
|
|
_("Contributing"),
|
|
"https://www.gimp.org/develop/");
|
|
welcome_add_link (GTK_GRID (grid), 1, &row,
|
|
/* "love letter" emoticone in UTF-8. */
|
|
"\xf0\x9f\x92\x8c",
|
|
_("Donating"),
|
|
"https://www.gimp.org/donating/");
|
|
|
|
/*****************/
|
|
/* Release Notes */
|
|
/*****************/
|
|
|
|
if (release_notes)
|
|
{
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
|
gtk_stack_add_titled (GTK_STACK (stack), vbox, "release-notes",
|
|
"Release Notes");
|
|
gtk_widget_show (vbox);
|
|
|
|
/* Release note title. */
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
/* Translators: the %s string will be the version, e.g. "3.0". */
|
|
tmp = g_strdup_printf (_("GIMP %s Release Notes"), GIMP_VERSION);
|
|
markup = g_strdup_printf ("<b><big>%s</big></b>", tmp);
|
|
g_free (tmp);
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (widget), markup);
|
|
g_free (markup);
|
|
gtk_label_set_selectable (GTK_LABEL (widget), FALSE);
|
|
gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_CENTER);
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), FALSE);
|
|
gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
|
|
gtk_widget_show (widget);
|
|
|
|
image = gtk_image_new_from_icon_name ("gimp-user-manual",
|
|
GTK_ICON_SIZE_DIALOG);
|
|
gtk_widget_set_valign (image, GTK_ALIGN_START);
|
|
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
|
|
/* Release note contents. */
|
|
|
|
gimp_appstream_to_pango_markups (release_notes,
|
|
&release_introduction,
|
|
&release_items);
|
|
if (release_introduction)
|
|
{
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (widget), release_introduction);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (widget), 70);
|
|
gtk_label_set_selectable (GTK_LABEL (widget), FALSE);
|
|
gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_LEFT);
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
|
|
gtk_widget_show (widget);
|
|
|
|
g_free (release_introduction);
|
|
}
|
|
|
|
if (release_items)
|
|
{
|
|
GList *item;
|
|
|
|
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
|
|
gtk_widget_show (scrolled_window);
|
|
|
|
listbox = gtk_list_box_new ();
|
|
|
|
for (item = release_items; item; item = item->next)
|
|
{
|
|
GtkWidget *row;
|
|
|
|
row = gtk_list_box_row_new ();
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (widget), item->data);
|
|
gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
|
|
gtk_label_set_line_wrap_mode (GTK_LABEL (widget), PANGO_WRAP_WORD);
|
|
gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_LEFT);
|
|
gtk_widget_set_halign (widget, GTK_ALIGN_START);
|
|
gtk_label_set_xalign (GTK_LABEL (widget), 0.0);
|
|
gtk_container_add (GTK_CONTAINER (row), widget);
|
|
|
|
gtk_list_box_insert (GTK_LIST_BOX (listbox), row, -1);
|
|
gtk_widget_show_all (row);
|
|
}
|
|
gtk_container_add (GTK_CONTAINER (scrolled_window), listbox);
|
|
gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox),
|
|
GTK_SELECTION_NONE);
|
|
|
|
g_signal_connect (listbox, "row-activated",
|
|
G_CALLBACK (welcome_dialog_release_item_activated),
|
|
gimp);
|
|
gtk_widget_show (listbox);
|
|
|
|
g_list_free_full (release_items, g_free);
|
|
}
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
if (GIMP_MINOR_VERSION % 2 == 0)
|
|
release_link = g_strdup_printf ("https://www.gimp.org/release-notes/gimp-%d.%d.html",
|
|
GIMP_MAJOR_VERSION, GIMP_MINOR_VERSION);
|
|
else
|
|
release_link = g_strdup ("https://www.gimp.org/");
|
|
|
|
widget = gtk_link_button_new_with_label (release_link, _("Learn more"));
|
|
gtk_widget_show (widget);
|
|
gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
|
|
g_free (release_link);
|
|
|
|
/*****************/
|
|
/* Task switcher */
|
|
/*****************/
|
|
|
|
switcher = gtk_stack_switcher_new ();
|
|
gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher),
|
|
GTK_STACK (stack));
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), switcher, FALSE, FALSE, 0);
|
|
gtk_widget_set_halign (switcher, GTK_ALIGN_CENTER);
|
|
gtk_widget_show (switcher);
|
|
}
|
|
|
|
/**************/
|
|
/* Info label */
|
|
/**************/
|
|
|
|
widget = gtk_label_new (NULL);
|
|
tmp = g_strdup (_("This welcome dialog is only shown at first launch. "
|
|
"You can show it again from the \"Help\" menu."));
|
|
markup = g_strdup_printf ("<small>%s</small>", tmp);
|
|
g_free (tmp);
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (widget), markup);
|
|
gtk_widget_show (widget);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), widget, FALSE, FALSE, 0);
|
|
|
|
g_clear_object (&app);
|
|
|
|
return welcome_dialog;
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_release_item_activated (GtkListBox *listbox,
|
|
GtkListBoxRow *row,
|
|
gpointer user_data)
|
|
{
|
|
Gimp *gimp = user_data;
|
|
GList *blink_script = NULL;
|
|
const gchar *script_string;
|
|
gchar **script_steps;
|
|
gint row_index;
|
|
gint i;
|
|
|
|
row_index = gtk_list_box_row_get_index (row);
|
|
|
|
g_return_if_fail (row_index < n_gimp_welcome_dialog_demo);
|
|
|
|
script_string = gimp_welcome_dialog_demo[row_index];
|
|
|
|
if (script_string == NULL)
|
|
/* Not an error. Some release items have no demos. */
|
|
return;
|
|
|
|
script_steps = g_strsplit (script_string, ",", 0);
|
|
|
|
for (i = 0; script_steps[i]; i++)
|
|
{
|
|
gchar **ids;
|
|
gchar *dockable_id = NULL;
|
|
gchar *widget_id = NULL;
|
|
|
|
ids = g_strsplit (script_steps[i], ":", 2);
|
|
/* Even if the string doesn't contain a second part, it is
|
|
* NULL-terminated, hence the widget_id will simply be NULL, which
|
|
* is fine when you just want to blink a dialog.
|
|
*/
|
|
dockable_id = ids[0];
|
|
widget_id = ids[1];
|
|
|
|
if (g_strcmp0 (dockable_id, "gimp-toolbox") == 0 &&
|
|
widget_id != NULL)
|
|
{
|
|
GimpUIManager *ui_manager;
|
|
GtkWidget *toolbox;
|
|
|
|
/* As a special case, for the toolbox, we don't just raise it,
|
|
* we also select the tool if one was requested.
|
|
*/
|
|
toolbox = gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (gimp)),
|
|
gimp,
|
|
gimp_dialog_factory_get_singleton (),
|
|
gimp_get_monitor_at_pointer (),
|
|
"gimp-toolbox");
|
|
/* Find and activate the tool. */
|
|
if (toolbox &&
|
|
(ui_manager = gimp_dock_get_ui_manager (GIMP_DOCK (toolbox))))
|
|
{
|
|
GimpAction *action;
|
|
|
|
action = gimp_ui_manager_find_action (ui_manager, "tools", widget_id);
|
|
/*"tools-bucket-fill");*/
|
|
gimp_action_activate (GIMP_ACTION (action));
|
|
}
|
|
}
|
|
|
|
/* Blink widget. */
|
|
gimp_blink_dockable (gimp, dockable_id, widget_id, &blink_script);
|
|
|
|
g_strfreev (ids);
|
|
}
|
|
if (blink_script != NULL)
|
|
gimp_blink_play_script (blink_script);
|
|
|
|
g_list_free (blink_script);
|
|
g_strfreev (script_steps);
|
|
}
|
|
|
|
static void
|
|
welcome_add_link (GtkGrid *grid,
|
|
gint column,
|
|
gint *row,
|
|
const gchar *emoji,
|
|
const gchar *title,
|
|
const gchar *link)
|
|
{
|
|
GtkWidget *hbox;
|
|
GtkWidget *button;
|
|
GtkWidget *icon;
|
|
|
|
/* TODO: Aryeom doesn't like the spacing here. There is too much
|
|
* spacing between the link lines and between emojis and links. But we
|
|
* didn't manage to find how to close a bit these 2 spacings in GTK.
|
|
* :-/
|
|
*/
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_grid_attach (grid, hbox, column, *row, 1, 1);
|
|
/* These margin are by design to emphasize a bit the link list by
|
|
* moving them a tiny bit to the right instead of being exactly
|
|
* aligned with the top text.
|
|
*/
|
|
gtk_widget_set_margin_start (hbox, 10);
|
|
gtk_widget_show (hbox);
|
|
|
|
++(*row);
|
|
|
|
icon = gtk_label_new (emoji);
|
|
gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
|
|
gtk_widget_show (icon);
|
|
|
|
button = gtk_link_button_new_with_label (link, title);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_show (button);
|
|
}
|
|
|
|
static void
|
|
welcome_size_allocate (GtkWidget *welcome_dialog,
|
|
GtkAllocation *allocation,
|
|
gpointer user_data)
|
|
{
|
|
GtkWidget *image = GTK_WIDGET (user_data);
|
|
GError *error = NULL;
|
|
GFile *splash_file;
|
|
GdkPixbuf *pixbuf;
|
|
GdkMonitor *monitor;
|
|
GdkRectangle workarea;
|
|
gint min_width;
|
|
gint min_height;
|
|
gint max_width;
|
|
gint max_height;
|
|
gint image_width;
|
|
gint image_height;
|
|
|
|
if (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_PIXBUF)
|
|
return;
|
|
|
|
monitor = gimp_get_monitor_at_pointer ();
|
|
gdk_monitor_get_workarea (monitor, &workarea);
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()))
|
|
{
|
|
/* See the long comment in app/gui/splash.c on why we do this
|
|
* weird stuff for Wayland only.
|
|
* See also #5322.
|
|
*/
|
|
min_width = workarea.width / 8;
|
|
min_height = workarea.height / 8;
|
|
max_width = workarea.width / 4;
|
|
max_height = workarea.height / 4;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
min_width = workarea.width / 4;
|
|
min_height = workarea.height / 4;
|
|
max_width = workarea.width / 2;
|
|
max_height = workarea.height / 2;
|
|
}
|
|
image_width = allocation->width + 20;
|
|
image_height = allocation->height + 20;
|
|
|
|
/* On big monitors, we get very huge images with a lot of empty space.
|
|
* So let's go with a logic so that we want a max and min size
|
|
* (relatively to desktop area), but we also want to avoid too much
|
|
* empty space. This is why we compute first the dialog size without
|
|
* any image in there.
|
|
*/
|
|
image_width = CLAMP (image_width, min_width, max_width);
|
|
image_height = CLAMP (image_height, min_height, max_height);
|
|
|
|
splash_file = gimp_data_directory_file ("images", "gimp-splash.png", NULL);
|
|
pixbuf = gdk_pixbuf_new_from_file_at_scale (g_file_peek_path (splash_file),
|
|
image_width, image_height,
|
|
TRUE, &error);
|
|
if (pixbuf)
|
|
{
|
|
gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
|
|
g_object_unref (pixbuf);
|
|
}
|
|
g_object_unref (splash_file);
|
|
|
|
gtk_widget_show (image);
|
|
|
|
gtk_window_set_resizable (GTK_WINDOW (welcome_dialog), FALSE);
|
|
}
|