1
1
mirror of https://gitlab.gnome.org/GNOME/gimp.git synced 2025-10-06 01:12:40 +02:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Jehan
68ac064cf2 app: make sure the Messages tab is displayed when the "Show on Start"…
… option is checked in the Welcome dialog.

The logic right now is:

* On a new version of GIMP, always show the "Welcome" tab.
* In other cases, when there are new messages, the "Team Messages" tab
  will be shown in front, whether or not the "Show on Start" option is
  checked.
* The rest of the time, if "Show on Start" is checked, the "Create" tab
  is shown (or the whole Welcome dialog is simply not shown at all
  otherwise).
2025-09-23 00:33:12 +02:00
Jehan
fb37c5cdd2 app: add "conditions" on messages so that they only show on relevant installations.
For instance, we could have messages showing only on Windows, or only on
the MSIX package but not the installer (with the build ID), or only on
specific versions, or version ranges, or finally only on development
releases, or nightly builds.
2025-09-23 00:33:12 +02:00
Jehan
53008ec00a app: new infrastructure to pass messages to GIMP installations.
This simply reuses the gimp_versions.json file, which is loaded no more
than once a week on Windows and macOS. It doesn't do any additional HTTP
request.
Furthermore it stays a read-only request (not sending any information to
upstream which is anyway a static website).

I had the idea of this feature for quite some time actually, but lately
I came to realize it would have been very handy right now if it had been
implemented. Indeed we have this issue where GIMP is crashing on
Norwegian Bokmål or Turkish (#12626) and this has lasted for a few
months now.  Unfortunately, while some people would know where to find
us (on the bug tracker), most people would just be stuck and frustrated
with a broken GIMP without any information. It would have been great if
we had been able to at least display a message to Windows users and give
them some workaround advice, or at least some info for them to be a bit
patient.

We won't over-use this feature, but for the few off times where we could
need it, it would be extremely useful IMO.

What remains to be done:

* Split a bit the logic so that this can work also on Linux, or on the
  store version for Windows, i.e. when we don't actually check for new
  versions (yet we could still check for messages).
* Add a new settings so that people can disable this message check
  independently to the version check? Though it doesn't bring much to be
  fair, since if it does not do any more requests with 1 or 2 checks.
* I need to implement some condition logic for messages which need to be
  seen only on specific OSes or with specific versions, etc.
* Maybe implement some localization for messages?
2025-09-23 00:33:12 +02:00
6 changed files with 659 additions and 8 deletions

View File

@@ -129,6 +129,12 @@ enum
PROP_LAST_RELEASE_COMMENT,
PROP_LAST_REVISION,
PROP_LAST_KNOWN_RELEASE,
PROP_LAST_MESSAGE_ID,
PROP_MESSAGES,
PROP_MESSAGE_TITLES,
PROP_MESSAGE_IMAGES,
PROP_MESSAGE_DATES,
PROP_N_NEW_MESSAGES,
#ifdef G_OS_WIN32
PROP_WIN32_POINTER_INPUT_API,
#endif
@@ -700,6 +706,43 @@ gimp_core_config_class_init (GimpCoreConfigClass *klass)
0, G_MAXINT, 0,
GIMP_PARAM_STATIC_STRINGS);
GIMP_CONFIG_PROP_STRING (object_class, PROP_LAST_MESSAGE_ID,
"last-message-id",
"ID of the last message from core team",
LAST_KNOWN_RELEASE_BLURB,
NULL,
GIMP_PARAM_STATIC_STRINGS);
/* These are messages from the official GIMP team. We don't want to
* store these in gimprc. We only store the last message ID. Yet we
* want to keep these in a property for passing them through.
*/
g_object_class_install_property (object_class, PROP_MESSAGES,
g_param_spec_boxed ("messages", NULL, NULL,
G_TYPE_STRV,
GIMP_PARAM_STATIC_STRINGS |
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_MESSAGE_TITLES,
g_param_spec_boxed ("message-titles", NULL, NULL,
G_TYPE_STRV,
GIMP_PARAM_STATIC_STRINGS |
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_MESSAGE_IMAGES,
g_param_spec_boxed ("message-images", NULL, NULL,
G_TYPE_STRV,
GIMP_PARAM_STATIC_STRINGS |
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_MESSAGE_DATES,
g_param_spec_boxed ("message-dates", NULL, NULL,
G_TYPE_STRV,
GIMP_PARAM_STATIC_STRINGS |
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_N_NEW_MESSAGES,
g_param_spec_int ("n-new-messages", NULL, NULL,
0, G_MAXINT, 0,
GIMP_PARAM_STATIC_STRINGS |
GIMP_PARAM_READWRITE));
GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SAVE_DOCUMENT_HISTORY,
"save-document-history",
"Save document history",
@@ -857,6 +900,12 @@ gimp_core_config_init (GimpCoreConfig *config)
gimp_color_set_alpha (red, 0.5);
config->quick_mask_color = red;
config->last_message_id = NULL;
config->messages = NULL;
config->message_titles = NULL;
config->message_images = NULL;
config->message_dates = NULL;
config->n_new_messages = 0;
config->default_image = g_object_new (GIMP_TYPE_TEMPLATE,
"name", "Default Image",
@@ -921,6 +970,12 @@ gimp_core_config_finalize (GObject *object)
g_clear_pointer (&core_config->last_release_comment, g_free);
g_clear_pointer (&core_config->config_version, g_free);
g_clear_pointer (&core_config->last_message_id, g_free);
g_strfreev (core_config->messages);
g_strfreev (core_config->message_titles);
g_strfreev (core_config->message_images);
g_strfreev (core_config->message_dates);
g_clear_object (&core_config->default_image);
g_clear_object (&core_config->default_grid);
g_clear_object (&core_config->color_management);
@@ -1157,6 +1212,24 @@ gimp_core_config_set_property (GObject *object,
g_set_str (&core_config->last_known_release, version);
}
break;
case PROP_LAST_MESSAGE_ID:
g_set_str (&core_config->last_message_id, g_value_get_string (value));
break;
case PROP_MESSAGES:
core_config->messages = g_value_dup_boxed (value);
break;
case PROP_MESSAGE_TITLES:
core_config->message_titles = g_value_dup_boxed (value);
break;
case PROP_MESSAGE_IMAGES:
core_config->message_images = g_value_dup_boxed (value);
break;
case PROP_MESSAGE_DATES:
core_config->message_dates = g_value_dup_boxed (value);
break;
case PROP_N_NEW_MESSAGES:
core_config->n_new_messages = g_value_get_int (value);
break;
case PROP_CONFIG_VERSION:
g_set_str (&core_config->config_version,
g_value_get_string (value));
@@ -1431,6 +1504,24 @@ gimp_core_config_get_property (GObject *object,
case PROP_LAST_KNOWN_RELEASE:
g_value_set_string (value, core_config->last_known_release);
break;
case PROP_LAST_MESSAGE_ID:
g_value_set_string (value, core_config->last_message_id);
break;
case PROP_MESSAGES:
g_value_set_boxed (value, core_config->messages);
break;
case PROP_MESSAGE_TITLES:
g_value_set_boxed (value, core_config->message_titles);
break;
case PROP_MESSAGE_IMAGES:
g_value_set_boxed (value, core_config->message_images);
break;
case PROP_MESSAGE_DATES:
g_value_set_boxed (value, core_config->message_dates);
break;
case PROP_N_NEW_MESSAGES:
g_value_set_int (value, core_config->n_new_messages);
break;
case PROP_CONFIG_VERSION:
g_value_set_string (value, core_config->config_version);
break;

View File

@@ -118,6 +118,13 @@ struct _GimpCoreConfig
gchar *last_release_comment;
gint last_revision;
gchar *last_message_id;
gchar **messages;
gchar **message_titles;
gchar **message_images;
gchar **message_dates;
gint n_new_messages;
gchar *config_version;
};

View File

@@ -208,7 +208,7 @@ dialogs_welcome_get (GimpDialogFactory *factory,
GimpUIManager *ui_manager,
gint view_size)
{
return welcome_dialog_create (context->gimp, TRUE);
return welcome_dialog_create (context->gimp, TRUE, FALSE);
}
GtkWidget *

View File

@@ -66,7 +66,8 @@
static GtkWidget * welcome_dialog_new (Gimp *gimp,
GimpConfig *config,
gboolean show_welcome_page);
gboolean show_welcome_page,
gboolean show_messages_page);
static void welcome_dialog_response (GtkWidget *widget,
gint response_id,
GtkWidget *dialog);
@@ -96,6 +97,10 @@ static void welcome_dialog_create_creation_page (Gimp *gimp,
GimpConfig *config,
GtkWidget *welcome_dialog,
GtkWidget *main_vbox);
static void welcome_dialog_create_messages_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);
@@ -133,12 +138,16 @@ static void welcome_dialog_open_image_accelerator (GtkAccelGroup *accel_grou
static gboolean welcome_scrollable_resize (gpointer data);
static void welcome_dialog_messages_notified (GimpCoreConfig *config,
const GParamSpec *pspec,
GtkWidget *dialog);
static GtkWidget *welcome_dialog;
GtkWidget *
welcome_dialog_create (Gimp *gimp,
gboolean show_welcome_page)
gboolean show_welcome_page,
gboolean show_messages_page)
{
GimpConfig *config;
GimpConfig *config_copy;
@@ -165,7 +174,7 @@ welcome_dialog_create (Gimp *gimp,
config, 0);
g_set_weak_pointer (&welcome_dialog,
welcome_dialog_new (gimp, config_copy, show_welcome_page));
welcome_dialog_new (gimp, config_copy, show_welcome_page, show_messages_page));
g_object_set_data (G_OBJECT (welcome_dialog), "gimp", gimp);
@@ -183,7 +192,8 @@ welcome_dialog_create (Gimp *gimp,
static GtkWidget *
welcome_dialog_new (Gimp *gimp,
GimpConfig *config,
gboolean show_welcome_page)
gboolean show_welcome_page,
gboolean show_messages_page)
{
GtkWidget *dialog;
GList *windows;
@@ -202,6 +212,8 @@ welcome_dialog_new (Gimp *gimp,
GdkModifierType accel_mods;
gchar **accels;
gchar **messages = NULL;
/* 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);
@@ -302,9 +314,22 @@ welcome_dialog_new (Gimp *gimp,
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)
if (! show_welcome_page && ! show_messages_page)
gtk_stack_set_visible_child_name (GTK_STACK (stack), "gimp-welcome-create");
g_object_get (gimp->config, "messages", &messages, NULL);
g_object_set_data (G_OBJECT (dialog), "gimp", gimp);
g_object_set_data (G_OBJECT (dialog), "show-welcome", GINT_TO_POINTER (show_welcome_page));
g_object_set_data (G_OBJECT (dialog), "show-messages", GINT_TO_POINTER (show_messages_page));
g_object_set_data (G_OBJECT (dialog), "stack", stack);
if (messages && g_strv_length (messages) > 0)
welcome_dialog_messages_notified (gimp->config, NULL, dialog);
else
g_signal_connect (gimp->config, "notify::messages",
(GCallback) welcome_dialog_messages_notified,
dialog);
g_strfreev (messages);
if (gimp_welcome_dialog_n_items > 0)
{
main_vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box),
@@ -962,6 +987,126 @@ welcome_dialog_create_contribute_page (Gimp *gimp,
gtk_widget_set_visible (button, TRUE);
}
static void
welcome_dialog_create_messages_page (Gimp *gimp,
GimpConfig *config,
GtkWidget *welcome_dialog,
GtkWidget *main_vbox)
{
GtkWidget *label;
gchar *markup;
gchar *text;
gchar **messages;
gchar **titles;
gchar **images;
gchar **dates;
gint n_new_messages;
g_object_get (config,
"messages", &messages,
"message-titles", &titles,
"message-images", &images,
"message-dates", &dates,
"n-new-messages", &n_new_messages,
NULL);
if (n_new_messages > 0)
{
text = g_strdup_printf (ngettext ("You have a new message from the GIMP team:",
"You have %d new messages from the GIMP team:",
n_new_messages),
n_new_messages);
markup = g_strdup_printf ("<big><b>%s</b></big>", text);
label = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (label), markup);
g_free (text);
g_free (markup);
gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
gtk_widget_set_visible (label, TRUE);
}
for (gint i = 0; messages[i]; i++)
{
GtkWidget *frame;
GtkWidget *frame_label;
if (i == n_new_messages && i != 0)
{
GtkWidget *separator;
separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
gtk_box_pack_start (GTK_BOX (main_vbox), separator, FALSE, FALSE, 0);
gtk_widget_set_visible (separator, TRUE);
text = g_strdup_printf ("Older messages:");
markup = g_strdup_printf ("<big><b>%s</b></big>", text);
label = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (label), markup);
g_free (text);
g_free (markup);
gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
gtk_widget_set_visible (label, TRUE);
}
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_set_visible (frame, TRUE);
frame_label = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
gtk_frame_set_label_widget (GTK_FRAME (frame), frame_label);
gtk_widget_set_visible (frame_label, TRUE);
markup = g_strdup_printf ("<u>%s</u>", titles[i]);
label = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (label), markup);
gtk_box_pack_start (GTK_BOX (frame_label), label, FALSE, FALSE, 0);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_START);
gtk_widget_set_visible (label, TRUE);
g_free (markup);
text = g_markup_escape_text (dates[i], -1);
markup = g_strdup_printf ("<i>%s</i>", text);
label = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (label), markup);
gtk_box_pack_start (GTK_BOX (frame_label), label, FALSE, FALSE, 0);
gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
gtk_widget_set_valign (label, GTK_ALIGN_START);
gtk_widget_set_visible (label, TRUE);
g_free (text);
g_free (markup);
/* Note: I don't use a GtkTextView on purpose because it doesn't
* support links in pango markup.
*/
label = gtk_label_new (NULL);
gtk_label_set_markup (GTK_LABEL (label), messages[i]);
gtk_widget_set_margin_top (label, 8);
gtk_widget_set_margin_bottom (label, 14);
gtk_widget_set_margin_start (label, 8);
gtk_widget_set_margin_end (label, 8);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_set_valign (label, GTK_ALIGN_START);
gtk_widget_set_hexpand (label, TRUE);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR);
gtk_label_set_track_visited_links (GTK_LABEL (label), TRUE);
gtk_label_set_max_width_chars (GTK_LABEL (label), 80);
gtk_label_set_width_chars (GTK_LABEL (label), 80);
gtk_label_set_selectable (GTK_LABEL (label), TRUE);
gtk_label_set_lines (GTK_LABEL (label), -1);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_container_add (GTK_CONTAINER (frame), label);
gtk_widget_set_visible (label, TRUE);
}
g_strfreev (messages);
g_strfreev (titles);
g_strfreev (images);
g_strfreev (dates);
}
static void
welcome_dialog_create_release_page (Gimp *gimp,
GtkWidget *welcome_dialog,
@@ -1554,3 +1699,41 @@ welcome_dialog_open_image_accelerator (GtkAccelGroup *accel_group,
if (gimp_ui_manager_activate_action (manager, "file", action_name))
gtk_widget_destroy (welcome_dialog);
}
static void
welcome_dialog_messages_notified (GimpCoreConfig *config,
const GParamSpec *pspec,
GtkWidget *dialog)
{
Gimp *gimp;
GtkWidget *main_vbox;
GtkWidget *prefs_box;
GtkWidget *stack;
GtkTreeIter top_iter;
gboolean show_welcome;
gboolean show_messages;
g_signal_handlers_disconnect_by_func (config,
(GCallback) welcome_dialog_messages_notified,
dialog);
gimp = g_object_get_data (G_OBJECT (dialog), "gimp");
prefs_box = g_object_get_data (G_OBJECT (dialog), "prefs-box");
stack = g_object_get_data (G_OBJECT (dialog), "stack");
show_welcome = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (dialog), "show-welcome"));
show_messages = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (dialog), "show-messages"));
main_vbox = gimp_prefs_box_add_page (GIMP_PREFS_BOX (prefs_box),
"dialog-information",
_("Team Messages"),
_("Team Messages"),
"gimp-welcome-messages",
NULL, &top_iter);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
welcome_dialog_create_messages_page (gimp, GIMP_CONFIG (gimp->config), dialog, main_vbox);
gtk_widget_set_visible (main_vbox, TRUE);
if (config->n_new_messages > 0 && ((pspec != NULL && ! show_welcome) ||
(pspec == NULL && show_messages)))
gtk_stack_set_visible_child_name (GTK_STACK (stack), "gimp-welcome-messages");
}

View File

@@ -22,4 +22,5 @@
GtkWidget * welcome_dialog_create (Gimp *gimp,
gboolean show_welcome_page);
gboolean show_welcome_page,
gboolean show_messages_page);

View File

@@ -55,6 +55,19 @@ static gboolean gimp_update_known (GimpCoreConfig *config,
gint64 release_timestamp,
gint build_revision,
const gchar *comment);
static gboolean gimp_update_get_highest (JsonParser *parser,
gchar **highest_version,
gint64 *release_timestamp,
gint *build_revision,
gchar **build_comment,
gboolean unstable);
static gboolean gimp_update_get_messages (JsonParser *parser,
gchar **top_message_id,
gchar ***titles,
gchar ***messages,
gchar ***images,
gchar ***dates,
gint *new_messages);
static void gimp_check_updates_process (const gchar *source,
gchar *file_contents,
gsize file_length,
@@ -67,6 +80,9 @@ static void gimp_check_updates_callback (GObject *source,
static void gimp_update_about_dialog (GimpCoreConfig *config,
const GParamSpec *pspec,
gpointer user_data);
static void gimp_update_welcome_dialog (GimpCoreConfig *config,
const GParamSpec *pspec,
gpointer user_data);
static const gchar * gimp_get_version_url (void);
@@ -327,6 +343,291 @@ gimp_update_get_highest (JsonParser *parser,
return (*highest_version != NULL);
}
static gboolean
gimp_update_get_messages (JsonParser *parser,
gchar **top_message_id,
gchar ***titles,
gchar ***messages,
gchar ***images,
gchar ***dates,
gint *new_messages)
{
GStrvBuilder *titles_builder;
GStrvBuilder *messages_builder;
GStrvBuilder *images_builder;
GStrvBuilder *dates_builder;
JsonPath *path;
JsonNode *result;
JsonArray *messages_array;
gchar *new_top_message_id = NULL;
const gchar *path_str;
const gchar *build_category;
GError *error = NULL;
gint i;
gboolean is_new_message = TRUE;
g_return_val_if_fail (top_message_id != NULL, FALSE);
g_return_val_if_fail (titles != NULL, FALSE);
g_return_val_if_fail (messages != NULL, FALSE);
g_return_val_if_fail (images != NULL, FALSE);
g_return_val_if_fail (dates != NULL, FALSE);
g_return_val_if_fail (new_messages != NULL, FALSE);
*titles = NULL;
*messages = NULL;
*images = NULL;
*dates = NULL;
*new_messages = 0;
path_str = "$['MESSAGES'][*]";
path = json_path_new ();
if (! json_path_compile (path, path_str, &error))
{
g_warning ("%s: path compilation failed: %s\n",
G_STRFUNC, error->message);
g_clear_error (&error);
g_object_unref (path);
return FALSE;
}
result = json_path_match (path, json_parser_get_root (parser));
if (! JSON_NODE_HOLDS_ARRAY (result))
{
g_printerr ("%s: match for \"%s\" is not a JSON array.\n",
G_STRFUNC, path_str);
g_object_unref (path);
return FALSE;
}
titles_builder = g_strv_builder_new ();
messages_builder = g_strv_builder_new ();
images_builder = g_strv_builder_new ();
dates_builder = g_strv_builder_new ();
#if defined(GIMP_RELEASE)
#if defined(GIMP_UNSTABLE)
build_category = "devel";
#else
build_category = "stable";
#endif
#else
build_category = "nightly";
#endif
messages_array = json_node_get_array (result);
for (i = 0; i < (gint) json_array_get_length (messages_array); i++)
{
JsonObject *msg;
msg = json_array_get_object_element (messages_array, i);
if (! json_object_has_member (msg, "title") ||
! json_object_has_member (msg, "message") ||
! json_object_has_member (msg, "id") ||
! json_object_has_member (msg, "date"))
{
/* JSON file data bug. */
g_printerr ("%s: message %d misses mandatory elements.\n",
G_STRFUNC, i);
continue;
}
else
{
const gchar *id;
const gchar *date;
GDateTime *datetime;
gchar *str;
id = json_object_get_string_member (msg, "id");
if (json_object_has_member (msg, "conditions"))
{
JsonArray *conditions;
gboolean valid_message = FALSE;
gint j;
conditions = json_object_get_array_member (msg, "conditions");
/* The "conditions" is a list where if one condition
* matches, then the message is considered valid.
*/
for (j = 0; j < (gint) json_array_get_length (conditions); j++)
{
gchar *condition;
gchar *token;
condition = g_strdup (json_array_get_string_element (conditions, j));
/* The format of conditions is:
* "[os]:[build-id]:[stable|devel|nightly]:[ver]"
* - Any part can be replaced by "*" to mean it
* matches any value.
* - Any missing section means "*" (so for instance,
* you could just have a
* "windows:org.gimp.GIMP_official" condition.
* - The "[ver]" can be an exact version, e.g.
* "3.0.4", but it can also ">3.0.4" or "<=3.0.4" or
* again ">=3.0.0,<=3.0.4" for any version in between
* these. I.e. a comma-separated list of version
* comparisons.
*/
token = strtok (condition, ":");
if (g_strcmp0 (token, "*") == 0 ||
g_strcmp0 (token, GIMP_BUILD_PLATFORM_FAMILY) == 0)
{
token = strtok (NULL, ":");
if (token == NULL)
{
valid_message = TRUE;
break;
}
if (g_strcmp0 (token, "*") == 0 ||
g_strcmp0 (token, GIMP_BUILD_ID) == 0)
{
token = strtok (NULL, ":");
if (token == NULL)
{
valid_message = TRUE;
break;
}
if (g_strcmp0 (token, "*") == 0 ||
g_strcmp0 (token, build_category) == 0)
{
gchar *vertok;
token = strtok (NULL, ":");
if (token == NULL && g_strcmp0 (token, "*") == 0)
{
valid_message = TRUE;
break;
}
valid_message = TRUE;
vertok = strtok (token, ",");
while (vertok != NULL)
{
if (strlen (vertok) > 2 &&
vertok[1] == '=')
{
if (vertok[0] == '>' &&
gimp_version_cmp (vertok + 2, NULL) > 0)
{
valid_message = FALSE;
break;
}
else if (vertok[0] == '<' &&
gimp_version_cmp (vertok + 2, NULL) < 0)
{
valid_message = FALSE;
break;
}
}
else if (strlen (vertok) > 1 &&
(vertok[0] == '>' || vertok[0] == '<'))
{
if (vertok[0] == '>' &&
gimp_version_cmp (vertok + 1, NULL) >= 0)
{
valid_message = FALSE;
break;
}
else if (vertok[0] == '<' &&
gimp_version_cmp (vertok + 1, NULL) <= 0)
{
valid_message = FALSE;
break;
}
}
else if (strlen (vertok) > 0 &&
gimp_version_cmp (vertok, NULL) != 0)
{
valid_message = FALSE;
break;
}
if (! valid_message)
break;
vertok = strtok (NULL, ",");
}
}
}
}
g_free (condition);
if (valid_message)
break;
}
if (! valid_message)
continue;
}
if (*top_message_id &&
is_new_message &&
g_strcmp0 (id, *top_message_id) == 0)
/* Below messages were all shown in previous runs. */
is_new_message = FALSE;
if (is_new_message)
(*new_messages)++;
date = json_object_get_string_member (msg, "date");
str = g_strdup_printf ("%s 00:00:00Z", date);
datetime = g_date_time_new_from_iso8601 (str, NULL);
g_free (str);
if (datetime)
{
gchar *date = g_date_time_format (datetime, "%x");
g_strv_builder_add (dates_builder, date);
g_free (date);
}
else
{
/* JSON file data bug. */
g_printerr ("%s: date for message %d not properly formatted: %s\n",
G_STRFUNC, i, date);
continue;
}
if (new_top_message_id == NULL)
new_top_message_id = g_strdup (id);
}
g_strv_builder_add (titles_builder, json_object_get_string_member (msg, "title"));
g_strv_builder_add (messages_builder, json_object_get_string_member (msg, "message"));
if (json_object_has_member (msg, "image"))
g_strv_builder_add (images_builder, json_object_get_string_member (msg, "image"));
else
g_strv_builder_add (images_builder, "");
}
*titles = g_strv_builder_end (titles_builder);
*messages = g_strv_builder_end (messages_builder);
*images = g_strv_builder_end (images_builder);
*dates = g_strv_builder_end (dates_builder);
json_node_unref (result);
g_object_unref (path);
g_strv_builder_unref (titles_builder);
g_strv_builder_unref (messages_builder);
g_strv_builder_unref (images_builder);
g_strv_builder_unref (dates_builder);
if (new_top_message_id)
{
g_free (*top_message_id);
*top_message_id = new_top_message_id;
}
return (g_strv_length (*titles) != 0);
}
static void
gimp_check_updates_process (const gchar *source,
gchar *file_contents,
@@ -337,6 +638,14 @@ gimp_check_updates_process (const gchar *source,
gchar *build_comment = NULL;
gint64 release_timestamp = 0;
gint build_revision = 0;
gchar *last_message_id = NULL;
gchar **titles = NULL;
gchar **messages = NULL;
gchar **images = NULL;
gchar **dates = NULL;
gint new_messages = 0;
GError *error = NULL;
JsonParser *parser;
@@ -387,8 +696,41 @@ gimp_check_updates_process (const gchar *source,
gimp_update_known (config, last_version, release_timestamp, build_revision, build_comment);
g_object_get (config,
"last-message-id", &last_message_id,
NULL);
if (gimp_update_get_messages (parser, &last_message_id,
&titles, &messages, &images, &dates,
&new_messages))
{
g_object_set (config,
"messages", messages,
"message-titles", titles,
"message-images", images,
"message-dates", dates,
"n-new-messages", new_messages,
NULL);
if (config->last_known_release == NULL)
/* Only update the last message ID if we didn't pop-up the About
* dialog to announce a new release. Indeed in such a case, we
* won't display the new message(s) and therefore the user
* likely won't get knowledge of it (unless they were to later
* open the Welcome dialog on their own.
*/
g_object_set (config,
"last-message-id", last_message_id,
NULL);
}
g_clear_pointer (&last_version, g_free);
g_clear_pointer (&build_comment, g_free);
g_clear_pointer (&last_message_id, g_free);
g_strfreev (titles);
g_strfreev (messages);
g_strfreev (images);
g_object_unref (parser);
g_free (file_contents);
}
@@ -448,6 +790,30 @@ gimp_update_about_dialog (GimpCoreConfig *config,
}
}
static void
gimp_update_welcome_dialog (GimpCoreConfig *config,
const GParamSpec *pspec,
gpointer user_data)
{
#ifndef GIMP_CONSOLE_COMPILATION
Gimp *gimp = user_data;
#endif
g_signal_handlers_disconnect_by_func (config,
(GCallback) gimp_update_welcome_dialog,
user_data);
if (config->last_known_release == NULL &&
config->n_new_messages > 0)
{
#ifndef GIMP_CONSOLE_COMPILATION
gtk_widget_show (welcome_dialog_create (gimp, FALSE, TRUE));
#else
g_printerr (_("You have new messages from the GIMP team."));
#endif
}
}
static const gchar *
gimp_get_version_url (void)
{
@@ -519,7 +885,7 @@ gimp_update_auto_check (GimpCoreConfig *config,
* dialog to always appear on load, show the Create page on start.
*/
if (! gimp->no_interface)
gtk_widget_set_visible (welcome_dialog_create (gimp, is_update),
gtk_widget_set_visible (welcome_dialog_create (gimp, is_update, FALSE),
TRUE);
/* Do not check for new updates when GIMP was just updated. */
@@ -562,6 +928,9 @@ gimp_update_auto_check (GimpCoreConfig *config,
g_signal_connect (config, "notify::last-known-release",
(GCallback) gimp_update_about_dialog,
gimp);
g_signal_connect (config, "notify::n-new-messages",
(GCallback) gimp_update_welcome_dialog,
gimp);
gimp_update_check (config);