mirror of https://github.com/GNOME/gimp.git
1485 lines
54 KiB
C
1485 lines
54 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 <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
#ifdef GDK_WINDOWING_WAYLAND
|
|
#include <gdk/gdkwayland.h>
|
|
#endif
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpconfig/gimpconfig.h"
|
|
#include "libgimpthumb/gimpthumb.h"
|
|
#include "libgimpwidgets/gimpwidgets.h"
|
|
|
|
#include "dialogs-types.h"
|
|
|
|
#include "gimp-version.h"
|
|
|
|
#include "config/gimprc.h"
|
|
|
|
#include "core/gimp.h"
|
|
#include "core/gimp-utils.h"
|
|
#include "core/gimpcontainer.h"
|
|
#include "core/gimpimagefile.h"
|
|
|
|
#include "dialogs/file-open-dialog.h"
|
|
|
|
#include "file/file-open.h"
|
|
|
|
#include "gui/icon-themes.h"
|
|
#include "gui/themes.h"
|
|
|
|
#include "menus/menus.h"
|
|
#include "widgets/gimpdialogfactory.h"
|
|
#include "widgets/gimpprefsbox.h"
|
|
#include "widgets/gimpuimanager.h"
|
|
#include "widgets/gimpwidgets-utils.h"
|
|
|
|
#include "preferences-dialog-utils.h"
|
|
#include "welcome-dialog.h"
|
|
#include "welcome-dialog-data.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
static GtkWidget * welcome_dialog_new (Gimp *gimp,
|
|
GimpConfig *config,
|
|
gboolean show_welcome_page);
|
|
static void welcome_dialog_response (GtkWidget *widget,
|
|
gint response_id,
|
|
GtkWidget *dialog);
|
|
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);
|
|
static void welcome_dialog_create_welcome_page (Gimp *gimp,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox);
|
|
static void welcome_dialog_create_personalize_page (Gimp *gimp,
|
|
GimpConfig *config,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox);
|
|
static void welcome_dialog_create_contribute_page (Gimp *gimp,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox);
|
|
static void welcome_dialog_create_creation_page (Gimp *gimp,
|
|
GimpConfig *config,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox);
|
|
static void welcome_dialog_create_release_page (Gimp *gimp,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox);
|
|
|
|
static void welcome_dialog_new_image_dialog (GtkWidget *button,
|
|
GtkWidget *welcome_dialog);
|
|
static void welcome_dialog_open_image_dialog (GtkWidget *button,
|
|
GtkWidget *welcome_dialog);
|
|
static void welcome_dialog_new_dialog_response (GtkWidget *dialog,
|
|
gint response_id,
|
|
GtkWidget *welcome_dialog);
|
|
static void welcome_dialog_open_dialog_close (GtkWidget *dialog,
|
|
GtkWidget *welcome_dialog);
|
|
static void welcome_open_activated_callback (GtkListBox *listbox,
|
|
GtkListBoxRow *row,
|
|
GtkWidget *welcome_dialog);
|
|
static void welcome_open_images_callback (GtkWidget *button,
|
|
GtkListBox *listbox);
|
|
|
|
static gboolean welcome_scrollable_resize (gpointer data);
|
|
|
|
|
|
static GtkWidget *welcome_dialog;
|
|
|
|
GtkWidget *
|
|
welcome_dialog_create (Gimp *gimp,
|
|
gboolean show_welcome_page)
|
|
{
|
|
GimpConfig *config;
|
|
GimpConfig *config_copy;
|
|
GimpConfig *config_orig;
|
|
|
|
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
|
|
g_return_val_if_fail (GIMP_IS_CONFIG (gimp->edit_config), NULL);
|
|
|
|
if (welcome_dialog)
|
|
return welcome_dialog;
|
|
|
|
/* turn off autosaving while the prefs dialog is open */
|
|
gimp_rc_set_autosave (GIMP_RC (gimp->edit_config), FALSE);
|
|
|
|
config = GIMP_CONFIG (gimp->edit_config);
|
|
config_copy = gimp_config_duplicate (config);
|
|
config_orig = gimp_config_duplicate (config);
|
|
|
|
g_signal_connect_object (config, "notify",
|
|
G_CALLBACK (prefs_config_notify),
|
|
config_copy, 0);
|
|
g_signal_connect_object (config_copy, "notify",
|
|
G_CALLBACK (prefs_config_copy_notify),
|
|
config, 0);
|
|
|
|
g_set_weak_pointer (&welcome_dialog,
|
|
welcome_dialog_new (gimp, config_copy, show_welcome_page));
|
|
|
|
g_object_set_data (G_OBJECT (welcome_dialog), "gimp", gimp);
|
|
|
|
g_object_set_data_full (G_OBJECT (welcome_dialog), "config-copy", config_copy,
|
|
(GDestroyNotify) g_object_unref);
|
|
g_object_set_data_full (G_OBJECT (welcome_dialog), "config-orig", config_orig,
|
|
(GDestroyNotify) g_object_unref);
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (welcome_dialog),
|
|
"gimp-welcome-dialog");
|
|
|
|
return welcome_dialog;
|
|
}
|
|
|
|
static GtkWidget *
|
|
welcome_dialog_new (Gimp *gimp,
|
|
GimpConfig *config,
|
|
gboolean show_welcome_page)
|
|
{
|
|
GtkWidget *dialog;
|
|
GList *windows;
|
|
GtkWidget *switcher;
|
|
GtkWidget *stack;
|
|
GtkWidget *tree_view;
|
|
GtkTreeIter top_iter;
|
|
|
|
GtkWidget *prefs_box;
|
|
GtkWidget *main_vbox;
|
|
|
|
gchar *title;
|
|
|
|
/* 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);
|
|
dialog = gimp_dialog_new (title,
|
|
"gimp-welcome-dialog",
|
|
windows ? windows->data : NULL,
|
|
0, NULL, NULL,
|
|
_("_Close"), GTK_RESPONSE_CLOSE,
|
|
NULL);
|
|
g_list_free (windows);
|
|
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
|
|
g_free (title);
|
|
|
|
gtk_widget_set_margin_start (GTK_WIDGET (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 0);
|
|
gtk_widget_set_margin_end (GTK_WIDGET (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 0);
|
|
|
|
g_signal_connect (dialog, "response",
|
|
G_CALLBACK (welcome_dialog_response),
|
|
dialog);
|
|
|
|
/*****************/
|
|
/* Page Switcher */
|
|
/*****************/
|
|
switcher = gtk_stack_switcher_new ();
|
|
prefs_box = gimp_prefs_box_new ();
|
|
stack = gimp_prefs_box_get_stack (GIMP_PREFS_BOX (prefs_box));
|
|
|
|
gimp_prefs_box_set_header_visible (GIMP_PREFS_BOX (prefs_box), FALSE);
|
|
gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher),
|
|
GTK_STACK (stack));
|
|
gtk_container_set_border_width (GTK_CONTAINER (switcher), 2);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
switcher, FALSE, FALSE, 0);
|
|
gtk_widget_set_halign (switcher, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_visible (switcher, TRUE);
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (prefs_box), 0);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
prefs_box, TRUE, TRUE, 0);
|
|
gtk_widget_set_visible (prefs_box, TRUE);
|
|
|
|
tree_view = gimp_prefs_box_get_tree_view (GIMP_PREFS_BOX (prefs_box));
|
|
/* Hide the side panel selection since we're using GtkStackSwitcher */
|
|
gtk_widget_set_visible (gtk_widget_get_parent (tree_view), FALSE);
|
|
|
|
g_object_set_data (G_OBJECT (dialog), "prefs-box", prefs_box);
|
|
|
|
main_vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box),
|
|
"gimp-wilber",
|
|
_("Welcome"),
|
|
_("Welcome"),
|
|
"gimp-welcome",
|
|
NULL,
|
|
&top_iter);
|
|
gtk_widget_set_margin_top (main_vbox, 0);
|
|
gtk_widget_set_margin_bottom (main_vbox, 0);
|
|
gtk_widget_set_margin_start (main_vbox, 0);
|
|
gtk_widget_set_margin_end (main_vbox, 0);
|
|
|
|
welcome_dialog_create_welcome_page (gimp, dialog, main_vbox);
|
|
gtk_widget_set_visible (main_vbox, TRUE);
|
|
|
|
main_vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box),
|
|
"gimp-wilber",
|
|
_("Personalize"),
|
|
_("Personalize"),
|
|
"gimp-welcome-personalize",
|
|
NULL,
|
|
&top_iter);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
|
|
|
|
welcome_dialog_create_personalize_page (gimp, config, dialog, main_vbox);
|
|
gtk_widget_set_visible (main_vbox, TRUE);
|
|
|
|
main_vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box),
|
|
"gimp-wilber",
|
|
_("Contribute"),
|
|
_("Contribute"),
|
|
"gimp-welcome-contribute",
|
|
NULL,
|
|
&top_iter);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
|
|
|
|
welcome_dialog_create_contribute_page (gimp, dialog, main_vbox);
|
|
gtk_widget_set_visible (main_vbox, TRUE);
|
|
|
|
main_vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box),
|
|
"gimp-wilber",
|
|
_("Create"),
|
|
_("Create"),
|
|
"gimp-welcome-create",
|
|
NULL,
|
|
&top_iter);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
|
|
|
|
welcome_dialog_create_creation_page (gimp, config, dialog, main_vbox);
|
|
gtk_widget_set_visible (main_vbox, TRUE);
|
|
|
|
/* If dialog is set to always show on load, switch to the Create page */
|
|
if (! show_welcome_page)
|
|
gtk_stack_set_visible_child_name (GTK_STACK (stack), "gimp-welcome-create");
|
|
|
|
main_vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box),
|
|
"gimp-wilber",
|
|
_("Release Notes"),
|
|
_("Release Notes"),
|
|
"gimp-welcome-release_notes",
|
|
NULL,
|
|
&top_iter);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
|
|
|
|
welcome_dialog_create_release_page (gimp, dialog, main_vbox);
|
|
gtk_widget_set_visible (main_vbox, TRUE);
|
|
|
|
return dialog;
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_response (GtkWidget *widget,
|
|
gint response_id,
|
|
GtkWidget *dialog)
|
|
{
|
|
Gimp *gimp = g_object_get_data (G_OBJECT (dialog), "gimp");
|
|
GObject *config_copy;
|
|
GList *restart_diff;
|
|
GList *confirm_diff;
|
|
GList *list;
|
|
|
|
config_copy = g_object_get_data (G_OBJECT (dialog), "config-copy");
|
|
|
|
/* destroy config_orig */
|
|
g_object_set_data (G_OBJECT (dialog), "config-orig", NULL);
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (dialog), FALSE);
|
|
|
|
confirm_diff = gimp_config_diff (G_OBJECT (gimp->edit_config),
|
|
config_copy,
|
|
GIMP_CONFIG_PARAM_CONFIRM);
|
|
|
|
g_object_freeze_notify (G_OBJECT (gimp->edit_config));
|
|
|
|
for (list = confirm_diff; list; list = g_list_next (list))
|
|
{
|
|
GParamSpec *param_spec = list->data;
|
|
GValue value = G_VALUE_INIT;
|
|
|
|
g_value_init (&value, param_spec->value_type);
|
|
|
|
g_object_get_property (config_copy,
|
|
param_spec->name, &value);
|
|
g_object_set_property (G_OBJECT (gimp->edit_config),
|
|
param_spec->name, &value);
|
|
|
|
g_value_unset (&value);
|
|
}
|
|
|
|
g_object_thaw_notify (G_OBJECT (gimp->edit_config));
|
|
|
|
g_list_free (confirm_diff);
|
|
|
|
gimp_rc_save (GIMP_RC (gimp->edit_config));
|
|
|
|
/* spit out a solely informational warning about changed values
|
|
* which need restart
|
|
*/
|
|
restart_diff = gimp_config_diff (G_OBJECT (gimp->edit_config),
|
|
G_OBJECT (gimp->config),
|
|
GIMP_CONFIG_PARAM_RESTART);
|
|
|
|
if (restart_diff)
|
|
{
|
|
GString *string;
|
|
|
|
string = g_string_new (_("You will have to restart GIMP for "
|
|
"the following changes to take effect:"));
|
|
g_string_append (string, "\n\n");
|
|
|
|
for (list = restart_diff; list; list = g_list_next (list))
|
|
{
|
|
GParamSpec *param_spec = list->data;
|
|
|
|
/* The first 3 bytes are the bullet unicode character
|
|
* for doing a list (U+2022).
|
|
*/
|
|
g_string_append_printf (string, "\xe2\x80\xa2 %s\n", g_param_spec_get_nick (param_spec));
|
|
}
|
|
|
|
prefs_message (dialog, GTK_MESSAGE_INFO, FALSE, string->str);
|
|
|
|
g_string_free (string, TRUE);
|
|
}
|
|
|
|
g_list_free (restart_diff);
|
|
|
|
gtk_widget_destroy (dialog);
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_create_welcome_page (Gimp *gimp,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox)
|
|
{
|
|
GtkWidget *grid;
|
|
GtkWidget *image;
|
|
GtkWidget *widget;
|
|
|
|
gchar *markup;
|
|
gchar *tmp;
|
|
gint row;
|
|
|
|
/****************/
|
|
/* Welcome page */
|
|
/****************/
|
|
|
|
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 (main_vbox), image, TRUE, TRUE, 0);
|
|
gtk_widget_set_visible (image, TRUE);
|
|
|
|
g_object_set_data (G_OBJECT (welcome_dialog), "welcome-vbox", main_vbox);
|
|
g_signal_connect (welcome_dialog,
|
|
"size-allocate",
|
|
G_CALLBACK (welcome_size_allocate),
|
|
image);
|
|
|
|
/* Welcome title. */
|
|
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 (main_vbox), grid, TRUE, TRUE, 0);
|
|
gtk_widget_set_margin_start (GTK_WIDGET (grid), 12);
|
|
gtk_widget_set_margin_end (GTK_WIDGET (grid), 12);
|
|
gtk_widget_set_visible (grid, TRUE);
|
|
|
|
/* 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_widget_set_margin_bottom (widget, 10);
|
|
gtk_grid_attach (GTK_GRID (grid), widget, 0, 0, 2, 1);
|
|
gtk_widget_set_visible (widget, TRUE);
|
|
|
|
/* Welcome message: left */
|
|
|
|
markup = _("GIMP is 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, 1, 1, 1);
|
|
|
|
gtk_widget_set_visible (widget, TRUE);
|
|
|
|
row = 2;
|
|
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 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, 1, 1, 1);
|
|
|
|
gtk_widget_set_visible (widget, TRUE);
|
|
|
|
row = 2;
|
|
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/");
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_create_personalize_page (Gimp *gimp,
|
|
GimpConfig *config,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox)
|
|
{
|
|
GtkSizeGroup *size_group = NULL;
|
|
GtkWidget *scale;
|
|
GtkListStore *store;
|
|
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *widget;
|
|
GtkWidget *button;
|
|
GtkWidget *grid;
|
|
|
|
GObject *object;
|
|
|
|
gchar **themes;
|
|
gint n_themes;
|
|
|
|
object = G_OBJECT (config);
|
|
|
|
size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
|
|
|
/* Themes */
|
|
vbox = prefs_frame_new (_("Theme"), GTK_CONTAINER (main_vbox), FALSE);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
|
|
gtk_widget_set_halign (GTK_WIDGET (hbox), GTK_ALIGN_START);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (hbox, TRUE);
|
|
|
|
grid = prefs_grid_new (GTK_CONTAINER (hbox));
|
|
button = prefs_enum_combo_box_add (object, "theme-color-scheme", 0, 0,
|
|
_("Color scheme"), GTK_GRID (grid),
|
|
0, size_group);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
|
|
gtk_widget_set_halign (GTK_WIDGET (hbox), GTK_ALIGN_START);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (hbox, TRUE);
|
|
|
|
|
|
/* Icon Theme */
|
|
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
|
|
themes = icon_themes_list_themes (gimp, &n_themes);
|
|
for (gint i = 0; i < n_themes; i++)
|
|
gtk_list_store_insert_with_values (store, NULL,
|
|
-1,
|
|
0, themes[i],
|
|
1, themes[i],
|
|
-1);
|
|
g_strfreev (themes);
|
|
|
|
widget = gimp_prop_string_combo_box_new (object, "icon-theme",
|
|
GTK_TREE_MODEL (store), 0, 1);
|
|
gtk_widget_set_visible (widget, TRUE);
|
|
|
|
grid = prefs_grid_new (GTK_CONTAINER (hbox));
|
|
prefs_widget_add_aligned (widget, _("Icon theme"), GTK_GRID (grid), 0, FALSE,
|
|
size_group);
|
|
g_object_unref (store);
|
|
|
|
/* Reset size group for next set of widgets */
|
|
g_clear_object (&size_group);
|
|
size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
|
|
|
prefs_switch_add (object, "prefer-symbolic-icons",
|
|
_("Use symbolic icons if available"),
|
|
GTK_BOX (hbox), NULL, &button);
|
|
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
|
|
|
|
vbox = prefs_frame_new (_("Icon Scaling"), GTK_CONTAINER (main_vbox), FALSE);
|
|
|
|
prefs_switch_add (object, "override-theme-icon-size",
|
|
_("_Override icon sizes set by the theme"),
|
|
GTK_BOX (vbox), NULL, &button);
|
|
|
|
scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL,
|
|
0.0, 3.0, 1.0);
|
|
/* 'draw_value' updates round_digits. So set it first. */
|
|
gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
|
|
gtk_range_set_round_digits (GTK_RANGE (scale), 0.0);
|
|
gtk_scale_add_mark (GTK_SCALE (scale), 0.0, GTK_POS_BOTTOM,
|
|
_("Small"));
|
|
gtk_scale_add_mark (GTK_SCALE (scale), 1.0, GTK_POS_BOTTOM,
|
|
_("Medium"));
|
|
gtk_scale_add_mark (GTK_SCALE (scale), 2.0, GTK_POS_BOTTOM,
|
|
_("Large"));
|
|
gtk_scale_add_mark (GTK_SCALE (scale), 3.0, GTK_POS_BOTTOM,
|
|
_("Huge"));
|
|
gtk_range_set_value (GTK_RANGE (scale),
|
|
(gdouble) GIMP_GUI_CONFIG (object)->custom_icon_size);
|
|
g_signal_connect (G_OBJECT (scale), "value-changed",
|
|
G_CALLBACK (prefs_icon_size_value_changed),
|
|
GIMP_GUI_CONFIG (object));
|
|
g_signal_connect (G_OBJECT (object), "notify::custom-icon-size",
|
|
G_CALLBACK (prefs_gui_config_notify_icon_size),
|
|
scale);
|
|
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (scale, TRUE);
|
|
|
|
g_object_bind_property (button, "active",
|
|
scale, "sensitive",
|
|
G_BINDING_SYNC_CREATE);
|
|
|
|
vbox = prefs_frame_new (_("Font Scaling"), GTK_CONTAINER (main_vbox), FALSE);
|
|
gimp_help_set_help_data (vbox,
|
|
_("Font scaling will not work with themes using absolute sizes."),
|
|
NULL);
|
|
scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL,
|
|
50, 200, 10);
|
|
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_BOTTOM);
|
|
gtk_scale_add_mark (GTK_SCALE (scale), 50.0, GTK_POS_BOTTOM,
|
|
_("50%"));
|
|
gtk_scale_add_mark (GTK_SCALE (scale), 100.0, GTK_POS_BOTTOM,
|
|
_("100%"));
|
|
gtk_scale_add_mark (GTK_SCALE (scale), 200.0, GTK_POS_BOTTOM,
|
|
_("200%"));
|
|
gtk_range_set_value (GTK_RANGE (scale),
|
|
(gdouble) GIMP_GUI_CONFIG (object)->font_relative_size * 100.0);
|
|
g_signal_connect (G_OBJECT (scale), "value-changed",
|
|
G_CALLBACK (prefs_font_size_value_changed),
|
|
GIMP_GUI_CONFIG (object));
|
|
g_signal_connect (G_OBJECT (object), "notify::font-relative-size",
|
|
G_CALLBACK (prefs_gui_config_notify_font_size),
|
|
scale);
|
|
gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (scale, TRUE);
|
|
|
|
#ifdef HAVE_ISO_CODES
|
|
vbox = prefs_frame_new (_("GUI Language (requires restart)"),
|
|
GTK_CONTAINER (main_vbox), FALSE);
|
|
prefs_language_combo_box_add (object, "language", GTK_BOX (vbox));
|
|
#endif
|
|
|
|
vbox = prefs_frame_new (_("Additional Customizations"), GTK_CONTAINER (main_vbox), FALSE);
|
|
|
|
#ifndef GDK_WINDOWING_QUARTZ
|
|
prefs_switch_add (object, "custom-title-bar",
|
|
_("Merge menu and title bar (requires restart)"),
|
|
GTK_BOX (vbox), size_group, NULL);
|
|
#endif
|
|
|
|
#ifdef CHECK_UPDATE
|
|
if (gimp_version_check_update ())
|
|
{
|
|
prefs_switch_add (object, "check-updates",
|
|
_("Enable check for updates (requires internet)"),
|
|
GTK_BOX (vbox), size_group, NULL);
|
|
}
|
|
#endif
|
|
|
|
prefs_switch_add (object, "toolbox-groups",
|
|
_("Use tool _groups"),
|
|
GTK_BOX (vbox), size_group, NULL);
|
|
|
|
g_clear_object (&size_group);
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_create_creation_page (Gimp *gimp,
|
|
GimpConfig *config,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox)
|
|
{
|
|
GtkWidget *vbox;
|
|
GtkWidget *hbox;
|
|
GtkWidget *button;
|
|
GtkWidget *listbox;
|
|
GtkWidget *toggle;
|
|
gint num_images;
|
|
gint list_count;
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (hbox, TRUE);
|
|
|
|
vbox = prefs_frame_new (_("Create a New Image"), GTK_CONTAINER (hbox),
|
|
FALSE);
|
|
|
|
button = gtk_button_new_with_mnemonic (_("C_reate"));
|
|
/* Balancing the indent from the frame */
|
|
gtk_widget_set_margin_end (button, 12);
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (welcome_dialog_new_image_dialog),
|
|
welcome_dialog);
|
|
|
|
vbox = prefs_frame_new (_("Open an Existing Image"), GTK_CONTAINER (hbox),
|
|
FALSE);
|
|
|
|
button = gtk_button_new_with_mnemonic (_("_Open"));
|
|
gtk_widget_set_margin_end (button, 12);
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (welcome_dialog_open_image_dialog),
|
|
welcome_dialog);
|
|
|
|
/* Recent Files */
|
|
vbox = prefs_frame_new (_("Recent Images"), GTK_CONTAINER (main_vbox),
|
|
FALSE);
|
|
|
|
listbox = gtk_list_box_new ();
|
|
gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox),
|
|
GTK_SELECTION_MULTIPLE);
|
|
gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (listbox),
|
|
FALSE);
|
|
gtk_container_add (GTK_CONTAINER (vbox), listbox);
|
|
gtk_widget_set_visible (listbox, TRUE);
|
|
|
|
num_images = gimp_container_get_n_children (gimp->documents);
|
|
list_count = (num_images <= 8) ? num_images : 8;
|
|
|
|
for (gint i = 0; i < list_count; i++)
|
|
{
|
|
GimpImagefile *imagefile = NULL;
|
|
GtkWidget *row;
|
|
GtkWidget *grid;
|
|
GtkWidget *name_label;
|
|
GtkWidget *thumbnail = NULL;
|
|
GFile *file;
|
|
GimpThumbnail *icon;
|
|
const gchar *name;
|
|
gchar *basename;
|
|
|
|
imagefile = (GimpImagefile *)
|
|
gimp_container_get_child_by_index (gimp->documents, i);
|
|
|
|
file = gimp_imagefile_get_file (imagefile);
|
|
|
|
name = gimp_file_get_utf8_name (file);
|
|
basename = g_path_get_basename (name);
|
|
|
|
/* If the file is not found, remove it and try to
|
|
* load another one if possible */
|
|
if (! g_file_test (name, G_FILE_TEST_IS_REGULAR))
|
|
{
|
|
g_free (basename);
|
|
|
|
if (list_count < num_images)
|
|
list_count++;
|
|
|
|
continue;
|
|
}
|
|
|
|
row = gtk_list_box_row_new ();
|
|
g_object_set_data_full (G_OBJECT (row),
|
|
"file", file,
|
|
NULL);
|
|
|
|
grid = gtk_grid_new ();
|
|
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
|
|
gtk_container_add (GTK_CONTAINER (row), grid);
|
|
|
|
icon = gimp_imagefile_get_thumbnail (imagefile);
|
|
if (icon)
|
|
{
|
|
GdkPixbuf *pixbuf = NULL;
|
|
|
|
pixbuf = gimp_thumbnail_load_thumb (icon, 1, NULL);
|
|
if (! pixbuf)
|
|
pixbuf = gimp_widget_load_icon (grid, GIMP_ICON_DIALOG_QUESTION,
|
|
32);
|
|
|
|
if (pixbuf)
|
|
{
|
|
pixbuf = gdk_pixbuf_scale_simple (pixbuf,
|
|
32, 32,
|
|
GDK_INTERP_BILINEAR);
|
|
|
|
thumbnail = gtk_image_new_from_pixbuf (pixbuf);
|
|
}
|
|
}
|
|
|
|
if (thumbnail)
|
|
gtk_grid_attach (GTK_GRID (grid), thumbnail, 1, 0, 1, 1);
|
|
|
|
name_label = gtk_label_new (basename);
|
|
gtk_label_set_ellipsize (GTK_LABEL (name_label), PANGO_ELLIPSIZE_MIDDLE);
|
|
g_free (basename);
|
|
g_object_set (name_label, "xalign", 0.0, NULL);
|
|
gtk_grid_attach (GTK_GRID (grid), name_label, 2, 0, 1, 1);
|
|
|
|
gtk_widget_show_all (row);
|
|
gtk_list_box_insert (GTK_LIST_BOX (listbox), row, -1);
|
|
}
|
|
|
|
g_signal_connect (listbox, "row-activated",
|
|
G_CALLBACK (welcome_open_activated_callback),
|
|
welcome_dialog);
|
|
|
|
button = gtk_button_new_with_mnemonic (_("O_pen Selected Images"));
|
|
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (welcome_open_images_callback),
|
|
listbox);
|
|
|
|
/* "Always show welcome dialog" checkbox */
|
|
toggle = prefs_check_button_add (G_OBJECT (config), "show-welcome-dialog",
|
|
_("Show on Start "
|
|
"(You can show the Welcome dialog again from the \"Help\" menu)"),
|
|
GTK_BOX (main_vbox));
|
|
gtk_container_child_set (GTK_CONTAINER (main_vbox), toggle,
|
|
"fill", TRUE,
|
|
"pack-type", GTK_PACK_END,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_create_contribute_page (Gimp *gimp,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox)
|
|
{
|
|
GtkWidget *hbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *button;
|
|
GtkWidget *label;
|
|
|
|
gchar *markup;
|
|
gchar *tmp;
|
|
|
|
gtk_box_set_spacing (GTK_BOX (main_vbox), 2);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (hbox, TRUE);
|
|
|
|
tmp = g_strdup_printf (_("Ways to contribute"));
|
|
markup = g_strdup_printf ("<big>%s</big>", tmp);
|
|
g_free (tmp);
|
|
label = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (label), markup);
|
|
g_free (markup);
|
|
gtk_box_set_center_widget (GTK_BOX (hbox), label);
|
|
gtk_widget_set_visible (label, TRUE);
|
|
|
|
vbox = prefs_frame_new (_("Report Bugs"), GTK_CONTAINER (main_vbox), FALSE);
|
|
|
|
tmp = g_strdup_printf (_("As any application, GIMP is not bug-free, so "
|
|
"reporting bugs that you encounter is very "
|
|
"important to the development."));
|
|
label = gtk_label_new (tmp);
|
|
g_free (tmp);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (label), 30);
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (label, TRUE);
|
|
button = gtk_link_button_new_with_label ("https://www.gimp.org/bugs/", _("Report Bugs"));
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
|
|
vbox = prefs_frame_new (_("Write Code"), GTK_CONTAINER (main_vbox), FALSE);
|
|
|
|
tmp = g_strdup_printf (_("Our Developer Website is where you want to start "
|
|
"learning about being a code contributor."));
|
|
label = gtk_label_new (tmp);
|
|
g_free (tmp);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (label), 30);
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (label, TRUE);
|
|
button = gtk_link_button_new_with_label ("https://developer.gimp.org/", _("Write Code"));
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
|
|
vbox = prefs_frame_new (_("Translate"), GTK_CONTAINER (main_vbox), FALSE);
|
|
|
|
tmp = g_strdup_printf (_("Contact the respective translation team for your "
|
|
"language"));
|
|
label = gtk_label_new (tmp);
|
|
g_free (tmp);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (label), 30);
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (label, TRUE);
|
|
button = gtk_link_button_new_with_label ("https://l10n.gnome.org/teams/", _("Translate"));
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
|
|
vbox = prefs_frame_new (_("Donate"), GTK_CONTAINER (main_vbox), FALSE);
|
|
|
|
tmp = g_strdup_printf (_("Donating money is important: it makes GIMP "
|
|
"sustainable."));
|
|
label = gtk_label_new (tmp);
|
|
g_free (tmp);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (label), 30);
|
|
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (label, TRUE);
|
|
button = gtk_link_button_new_with_label ("https://liberapay.com/GIMP/donate", _("Donate via Liberapay"));
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
button = gtk_link_button_new_with_label ("https://www.gimp.org/donating/", _("Other donation options"));
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_create_release_page (Gimp *gimp,
|
|
GtkWidget *welcome_dialog,
|
|
GtkWidget *main_vbox)
|
|
{
|
|
GtkWidget *scrolled_window;
|
|
GtkWidget *hbox;
|
|
GtkWidget *image;
|
|
GtkWidget *listbox;
|
|
GtkWidget *widget;
|
|
|
|
gchar *release_link;
|
|
gchar *markup;
|
|
gchar *tmp;
|
|
|
|
/*****************/
|
|
/* Release Notes */
|
|
/*****************/
|
|
if (gimp_welcome_dialog_n_items > 0)
|
|
{
|
|
gint n_demos = 0;
|
|
|
|
/* 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 (main_vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (hbox, TRUE);
|
|
|
|
/* 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_set_visible (widget, TRUE);
|
|
|
|
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_set_visible (image, TRUE);
|
|
|
|
/* Release note introduction. */
|
|
|
|
if (gimp_welcome_dialog_intro_n_paragraphs)
|
|
{
|
|
GString *introduction = NULL;
|
|
|
|
for (gint i = 0; i < gimp_welcome_dialog_intro_n_paragraphs; i++)
|
|
{
|
|
if (i == 0)
|
|
introduction = g_string_new (_(gimp_welcome_dialog_intro[i]));
|
|
else
|
|
g_string_append_printf (introduction, "\n%s",
|
|
_(gimp_welcome_dialog_intro[i]));
|
|
}
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (widget), introduction->str);
|
|
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 (main_vbox), widget, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (widget, TRUE);
|
|
|
|
g_string_free (introduction, TRUE);
|
|
}
|
|
|
|
/* Release note's change items. */
|
|
|
|
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), scrolled_window, TRUE, TRUE, 0);
|
|
gtk_widget_set_visible (scrolled_window, TRUE);
|
|
|
|
listbox = gtk_list_box_new ();
|
|
|
|
for (gint i = 0; i < gimp_welcome_dialog_n_items; i++)
|
|
{
|
|
GtkWidget *row;
|
|
gchar *markup;
|
|
|
|
/* Add a bold dot for pretty listing. */
|
|
if (i < gimp_welcome_dialog_n_items &&
|
|
gimp_welcome_dialog_demos[i] != NULL)
|
|
{
|
|
markup = g_strdup_printf ("<span weight='ultrabold'>\xe2\x96\xb6</span> %s",
|
|
_((gchar *) gimp_welcome_dialog_items[i]));
|
|
n_demos++;
|
|
}
|
|
else
|
|
{
|
|
markup = g_strdup_printf ("<span weight='ultrabold'>\xe2\x80\xa2</span> %s",
|
|
_((gchar *) gimp_welcome_dialog_items[i]));
|
|
}
|
|
|
|
row = gtk_list_box_row_new ();
|
|
widget = gtk_label_new (NULL);
|
|
gtk_label_set_markup (GTK_LABEL (widget), markup);
|
|
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);
|
|
|
|
g_free (markup);
|
|
}
|
|
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_set_visible (listbox, TRUE);
|
|
|
|
if (n_demos > 0)
|
|
{
|
|
/* A small explicative string to help discoverability of the demo
|
|
* ability.
|
|
*/
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (hbox, TRUE);
|
|
|
|
image = gtk_image_new_from_icon_name ("dialog-information",
|
|
GTK_ICON_SIZE_MENU);
|
|
gtk_widget_set_valign (image, GTK_ALIGN_CENTER);
|
|
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (image, TRUE);
|
|
|
|
widget = gtk_label_new (NULL);
|
|
tmp = g_strdup_printf (_("Click on release items with a %s bullet point to get a tour."),
|
|
"<span weight='ultrabold'>\xe2\x96\xb6</span>");
|
|
markup = g_strdup_printf ("<i>%s</i>", tmp);
|
|
g_free (tmp);
|
|
gtk_label_set_markup (GTK_LABEL (widget), markup);
|
|
g_free (markup);
|
|
gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (widget, TRUE);
|
|
|
|
/* TODO: if a demo changed settings, should we add a "reset"
|
|
* button to get back to previous state?
|
|
*/
|
|
}
|
|
|
|
/* Link to full release notes on web site at the bottom. */
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (hbox, TRUE);
|
|
|
|
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_set_visible (widget, TRUE);
|
|
gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
|
|
g_free (release_link);
|
|
}
|
|
}
|
|
|
|
/* Actions */
|
|
static void
|
|
welcome_dialog_new_image_dialog (GtkWidget *button,
|
|
GtkWidget *welcome_dialog)
|
|
{
|
|
GimpUIManager *manager;
|
|
Gimp *gimp;
|
|
GtkWidget *dialog;
|
|
|
|
gimp = g_object_get_data (G_OBJECT (welcome_dialog), "gimp");
|
|
manager = menus_get_image_manager_singleton (gimp);
|
|
|
|
/* XXX: to avoid code duplication and divergence, we just call the "image-new"
|
|
* action, then we check that the new dialog singleton exists in order to
|
|
* handle its responses.
|
|
*/
|
|
if (gimp_ui_manager_activate_action (manager, "image", "image-new") &&
|
|
(dialog = gimp_dialog_factory_find_widget (gimp_dialog_factory_get_singleton (),
|
|
"gimp-image-new-dialog")))
|
|
{
|
|
gtk_widget_set_visible (welcome_dialog, FALSE);
|
|
g_signal_connect (dialog, "response",
|
|
G_CALLBACK (welcome_dialog_new_dialog_response),
|
|
welcome_dialog);
|
|
g_signal_connect_swapped (dialog, "destroy",
|
|
G_CALLBACK (gtk_widget_destroy),
|
|
welcome_dialog);
|
|
}
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_open_image_dialog (GtkWidget *button,
|
|
GtkWidget *welcome_dialog)
|
|
{
|
|
Gimp *gimp = g_object_get_data (G_OBJECT (welcome_dialog), "gimp");
|
|
GtkWidget *dialog;
|
|
|
|
dialog = file_open_dialog_new (gimp);
|
|
|
|
if (dialog)
|
|
{
|
|
gtk_widget_set_visible (welcome_dialog, FALSE);
|
|
|
|
gtk_window_present (GTK_WINDOW (dialog));
|
|
|
|
g_signal_connect (dialog, "destroy",
|
|
G_CALLBACK (welcome_dialog_open_dialog_close),
|
|
welcome_dialog);
|
|
}
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_new_dialog_response (GtkWidget *dialog,
|
|
gint response_id,
|
|
GtkWidget *welcome_dialog)
|
|
{
|
|
switch (response_id)
|
|
{
|
|
case GTK_RESPONSE_OK:
|
|
/* Don't destroy the welcome dialog directly, because it's possible to get
|
|
* the OK response without the new image dialog closing (in case it
|
|
* triggers a confirm dialog), followed by a GTK_RESPONSE_CANCEL.
|
|
*
|
|
* Let the "destroy" handlers take care of destroying the welcome dialog.
|
|
*/
|
|
break;
|
|
|
|
case GTK_RESPONSE_CANCEL:
|
|
case GTK_RESPONSE_DELETE_EVENT:
|
|
g_signal_handlers_disconnect_by_func (dialog,
|
|
G_CALLBACK (gtk_widget_destroy),
|
|
welcome_dialog);
|
|
gtk_widget_set_visible (welcome_dialog, TRUE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
welcome_dialog_open_dialog_close (GtkWidget *dialog,
|
|
GtkWidget *welcome_dialog)
|
|
{
|
|
GSList *files = NULL;
|
|
|
|
files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (dialog));
|
|
|
|
if (files && welcome_dialog)
|
|
{
|
|
gtk_widget_destroy (welcome_dialog);
|
|
g_slist_free_full (files, (GDestroyNotify) g_object_unref);
|
|
return;
|
|
}
|
|
|
|
if (welcome_dialog)
|
|
gtk_widget_set_visible (welcome_dialog, TRUE);
|
|
}
|
|
|
|
static void
|
|
welcome_open_activated_callback (GtkListBox *listbox,
|
|
GtkListBoxRow *row,
|
|
GtkWidget *welcome_dialog)
|
|
{
|
|
welcome_open_images_callback (NULL, listbox);
|
|
}
|
|
|
|
static void
|
|
welcome_open_images_callback (GtkWidget *button,
|
|
GtkListBox *listbox)
|
|
{
|
|
GList *rows = NULL;
|
|
Gimp *gimp = NULL;
|
|
GError *error = NULL;
|
|
gboolean opened = FALSE;
|
|
GtkWidget *parent;
|
|
|
|
if (! welcome_dialog)
|
|
return;
|
|
|
|
gimp = g_object_get_data (G_OBJECT (welcome_dialog), "gimp");
|
|
|
|
parent = gtk_widget_get_parent (welcome_dialog);
|
|
|
|
rows = gtk_list_box_get_selected_rows (listbox);
|
|
if (rows)
|
|
{
|
|
gtk_widget_set_sensitive (welcome_dialog, FALSE);
|
|
|
|
for (GList *iter = rows; iter; iter = iter->next)
|
|
{
|
|
GFile *file = NULL;
|
|
GimpImage *image = NULL;
|
|
const gchar *name;
|
|
GimpPDBStatusType status;
|
|
|
|
file = g_object_get_data (G_OBJECT (iter->data), "file");
|
|
name = gimp_file_get_utf8_name (file);
|
|
|
|
if (file && g_file_test (name, G_FILE_TEST_IS_REGULAR))
|
|
image = file_open_with_display (gimp, gimp_get_user_context (gimp),
|
|
NULL, file, FALSE, NULL, &status,
|
|
&error);
|
|
|
|
if (! image && status != GIMP_PDB_CANCEL)
|
|
{
|
|
gimp_message (gimp, G_OBJECT (parent), GIMP_MESSAGE_ERROR,
|
|
_("Opening '%s' failed:\n\n%s"),
|
|
gimp_file_get_utf8_name (file), error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
else
|
|
{
|
|
opened = TRUE;
|
|
}
|
|
}
|
|
|
|
g_list_free (rows);
|
|
}
|
|
|
|
/* If no images were successfully opened, leave the dialogue up */
|
|
gtk_widget_set_sensitive (welcome_dialog, TRUE);
|
|
if (opened)
|
|
gtk_widget_destroy (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 < gimp_welcome_dialog_n_items);
|
|
|
|
script_string = gimp_welcome_dialog_demos[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;
|
|
gchar **settings = NULL;
|
|
gchar *settings_value = 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 (widget_id != NULL)
|
|
{
|
|
settings = g_strsplit (widget_id, "=", 2);
|
|
|
|
widget_id = settings[0];
|
|
settings_value = settings[1];
|
|
}
|
|
|
|
/* Allowing white spaces so that the demo in XML metadata can be
|
|
* spaced-out or even on multiple lines for clarity.
|
|
*/
|
|
dockable_id = g_strstrip (dockable_id);
|
|
if (widget_id != NULL)
|
|
widget_id = g_strstrip (widget_id);
|
|
|
|
/* All our dockable IDs start with "gimp-". This allows to write
|
|
* shorter names in the demo script.
|
|
*/
|
|
if (! g_str_has_prefix (dockable_id, "gimp-"))
|
|
{
|
|
gchar *tmp = g_strdup_printf ("gimp-%s", dockable_id);
|
|
|
|
g_free (ids[0]);
|
|
dockable_id = ids[0] = tmp;
|
|
}
|
|
|
|
/* Blink widget. */
|
|
if (g_strcmp0 (dockable_id, "gimp-toolbox") == 0)
|
|
{
|
|
/* All tool button IDs start with "tools-". This allows to
|
|
* write shorter tool names in the demo script.
|
|
*/
|
|
if (widget_id != NULL && ! g_str_has_prefix (widget_id, "tools-"))
|
|
{
|
|
gchar *tmp = g_strdup_printf ("tools-%s", widget_id);
|
|
|
|
g_free (settings[0]);
|
|
widget_id = settings[0] = tmp;
|
|
}
|
|
|
|
gimp_blink_toolbox (gimp, widget_id, &blink_script);
|
|
}
|
|
else
|
|
{
|
|
gimp_blink_dockable (gimp, dockable_id,
|
|
widget_id, settings_value,
|
|
&blink_script);
|
|
}
|
|
|
|
g_strfreev (ids);
|
|
if (settings)
|
|
g_strfreev (settings);
|
|
}
|
|
if (blink_script != NULL)
|
|
{
|
|
GList *windows = gimp_get_image_windows (gimp);
|
|
|
|
/* Losing forcus on the welcome dialog on purpose for the main GUI
|
|
* to be more readable.
|
|
*/
|
|
if (windows)
|
|
gtk_window_present (windows->data);
|
|
|
|
gimp_blink_play_script (blink_script);
|
|
|
|
g_list_free (windows);
|
|
}
|
|
|
|
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_set_visible (hbox, TRUE);
|
|
|
|
++(*row);
|
|
|
|
icon = gtk_label_new (emoji);
|
|
gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (icon, TRUE);
|
|
|
|
button = gtk_link_button_new_with_label (link, title);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
|
|
gtk_widget_set_visible (button, TRUE);
|
|
}
|
|
|
|
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;
|
|
GdkWindow *gdk_window;
|
|
GdkRectangle workarea;
|
|
gint image_width;
|
|
|
|
gdk_window = gtk_widget_get_window (welcome_dialog);
|
|
if (gdk_window)
|
|
monitor = gdk_display_get_monitor_at_window (gdk_display_get_default (), gdk_window);
|
|
else
|
|
monitor = gimp_get_monitor_at_pointer ();
|
|
gdk_monitor_get_workarea (monitor, &workarea);
|
|
|
|
if (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_PIXBUF)
|
|
{
|
|
if (allocation->height > workarea.height - 10 &&
|
|
! g_object_get_data (G_OBJECT (welcome_dialog), "resized-once"))
|
|
{
|
|
g_object_set_data (G_OBJECT (welcome_dialog), "resized-once", GINT_TO_POINTER (TRUE));
|
|
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
|
|
welcome_scrollable_resize, NULL,
|
|
NULL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
image_width = MAX (allocation->width - 2, workarea.width / 4);
|
|
image_width = MIN (image_width, workarea.width / 3);
|
|
/* Splash screens are fullHD. We should not load it bigger.
|
|
* See: https://gitlab.gnome.org/GNOME/gimp-data/-/blob/main/images/README.md#requirements
|
|
*/
|
|
image_width = MIN (image_width, 1920);
|
|
|
|
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, -1,
|
|
TRUE, &error);
|
|
if (pixbuf)
|
|
{
|
|
gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
|
|
g_object_unref (pixbuf);
|
|
}
|
|
else if (error)
|
|
{
|
|
g_printerr ("%s: %s\n", G_STRFUNC, error->message);
|
|
}
|
|
g_object_unref (splash_file);
|
|
g_clear_error (&error);
|
|
|
|
gtk_widget_set_visible (image, TRUE);
|
|
|
|
gtk_window_set_resizable (GTK_WINDOW (welcome_dialog), FALSE);
|
|
}
|
|
|
|
static gboolean
|
|
welcome_scrollable_resize (gpointer data)
|
|
{
|
|
if (welcome_dialog)
|
|
{
|
|
GtkWidget *prefs_box = g_object_get_data (G_OBJECT (welcome_dialog), "prefs-box");
|
|
GtkWidget *main_vbox = g_object_get_data (G_OBJECT (welcome_dialog), "welcome-vbox");
|
|
|
|
/* Make the first page scrollable to prevent height issues on
|
|
* smaller screens */
|
|
gimp_prefs_box_set_page_scrollable (GIMP_PREFS_BOX (prefs_box), main_vbox, TRUE);
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (welcome_dialog));
|
|
}
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|