Skip to content
Commits on Source (26)
News in 6.2.0, 2022-11-02
-------------------------
* Remove deprecated API.
* Add some new API (see the reference documentation).
* Small improvements.
News in 6.1.2, 2022-07-12
-------------------------
* API stability is no longer guaranteed.
......
......@@ -8,6 +8,8 @@ The API is now **constantly unstable**, there can be API breaks for new
versions. As a consequence, it is highly recommended to **bundle** Tepl with the
program, and for the program to depend on a precise version of Tepl.
The library soname is adapted for each release.
Dependencies
------------
......
......@@ -209,4 +209,25 @@
</listitem>
</itemizedlist>
</chapter>
<chapter id="api-breaks-during-tepl-6">
<title>API breaks during Tepl 6</title>
<para>
There are also ABI breaks not affecting the API, so just re-compiling the
program is sufficient in that case.
</para>
<para>
Tepl 6.0 to Tepl 6.2:
</para>
<itemizedlist>
<listitem>
<para>
The <code>tepl_utils_override_font()</code> function has been renamed
to <link linkend="tepl-utils-override-font-string">tepl_utils_override_font_string()</link>.
</para>
</listitem>
</itemizedlist>
</chapter>
</part>
......@@ -96,7 +96,8 @@
<refsect1>
<title>API stability</title>
<para>
The API is now constantly unstable.
The API is now constantly unstable. The library soname is adapted for each
release.
</para>
<para>
......
......@@ -632,9 +632,10 @@ tepl_utils_list_box_get_row_at_index_with_filter
tepl_utils_list_box_get_filtered_children
tepl_utils_override_font_description
tepl_utils_override_font_string
tepl_utils_override_font
tepl_utils_get_titled_component
tepl_utils_binding_transform_func_smart_bool
tepl_utils_can_use_gsettings_schema
tepl_utils_can_use_gsettings_key
</SECTION>
<SECTION>
......
......@@ -6,7 +6,7 @@
project(
'tepl', 'c',
meson_version: '>= 0.53',
version: '6.1.2',
version: '6.2.0', # Don't forget to increment lt_current too.
default_options: ['warning_level=2']
)
......@@ -14,29 +14,17 @@ GNOME = import('gnome')
PKG_CONFIG = import('pkgconfig')
I18N = import('i18n')
# Libtool versioning
# Libtool versioning (for the soname).
# See https://www.gnu.org/software/libtool/manual/html_node/Versioning.html for
# the official docs.
#
# For development releases (if the minor package version is odd), keep the same
# Libtool version.
# For Tepl needs and to keep things simple, for *each* release (each release is
# now only a development snapshot):
#
# For a new minor stable release (when incrementing the minor package version
# to an even number), apply the following algorithm step by step:
# 1. If the library source code has changed at all since the last
# update, then increment REVISION.
# 2. If any exported functions or data have been added, removed, or
# changed since the last update, increment CURRENT and set REVISION
# to 0.
# 3. If any exported functions or data have been added since the last
# public release, increment AGE.
# 4. If any exported functions or data have been removed since the last
# public release, set AGE to 0.
#
# When incrementing the API version (usually for a new major package version),
# set CURRENT, REVISION and AGE to 0 since it's like a new library.
lt_current = 0
lt_revision = 0
lt_age = 0
TEPL_LT_VERSION = '@0@.@1@.@2@'.format(lt_current, lt_revision, lt_age)
# Just increment CURRENT.
# REVISION and AGE are kept to 0.
lt_current = 1
TEPL_LT_VERSION = '@0@.0.0'.format(lt_current)
# API version, used for parallel installability.
TEPL_API_VERSION = '6'
......
tepl/tepl-io-error-info-bar.c
testsuite/test-file.c
tests/unit-tests/test-file.c
......@@ -87,7 +87,6 @@ tepl_public_c_files = [
TEPL_PRIVATE_HEADERS = [
'tepl-close-confirm-dialog-single.h',
'tepl-icu.h',
'tepl-io-error-info-bar.h',
'tepl-metadata-attic.h',
'tepl-metadata-parser.h',
'tepl-window-actions-edit.h',
......@@ -98,7 +97,6 @@ TEPL_PRIVATE_HEADERS = [
tepl_private_c_files = [
'tepl-close-confirm-dialog-single.c',
'tepl-icu.c',
'tepl-io-error-info-bar.c',
'tepl-metadata-attic.c',
'tepl-metadata-parser.c',
'tepl-window-actions-edit.c',
......
......@@ -3,6 +3,7 @@
*/
#include "tepl-info-bar.h"
#include "tepl-utils.h"
/**
* SECTION:info-bar
......@@ -492,7 +493,7 @@ tepl_info_bar_add_primary_message (TeplInfoBar *info_bar,
g_return_if_fail (TEPL_IS_INFO_BAR (info_bar));
g_return_if_fail (primary_msg != NULL);
primary_msg_escaped = g_markup_escape_text (primary_msg, -1);
primary_msg_escaped = tepl_utils_markup_escape_text (primary_msg);
primary_markup = g_strdup_printf ("<b>%s</b>", primary_msg_escaped);
primary_label = tepl_info_bar_create_label ();
gtk_label_set_markup (primary_label, primary_markup);
......@@ -500,8 +501,9 @@ tepl_info_bar_add_primary_message (TeplInfoBar *info_bar,
g_free (primary_msg_escaped);
gtk_widget_show (GTK_WIDGET (primary_label));
gtk_container_add (GTK_CONTAINER (info_bar->priv->vgrid_alongside_icon),
GTK_WIDGET (primary_label));
tepl_info_bar_add_content_widget (info_bar,
GTK_WIDGET (primary_label),
TEPL_INFO_BAR_LOCATION_ALONGSIDE_ICON);
}
/**
......@@ -525,7 +527,7 @@ tepl_info_bar_add_secondary_message (TeplInfoBar *info_bar,
g_return_if_fail (TEPL_IS_INFO_BAR (info_bar));
g_return_if_fail (secondary_msg != NULL);
secondary_msg_escaped = g_markup_escape_text (secondary_msg, -1);
secondary_msg_escaped = tepl_utils_markup_escape_text (secondary_msg);
secondary_markup = g_strdup_printf ("<small>%s</small>", secondary_msg_escaped);
secondary_label = tepl_info_bar_create_label ();
gtk_label_set_markup (secondary_label, secondary_markup);
......@@ -533,8 +535,9 @@ tepl_info_bar_add_secondary_message (TeplInfoBar *info_bar,
g_free (secondary_msg_escaped);
gtk_widget_show (GTK_WIDGET (secondary_label));
gtk_container_add (GTK_CONTAINER (info_bar->priv->vgrid_alongside_icon),
GTK_WIDGET (secondary_label));
tepl_info_bar_add_content_widget (info_bar,
GTK_WIDGET (secondary_label),
TEPL_INFO_BAR_LOCATION_ALONGSIDE_ICON);
}
/**
......@@ -708,9 +711,7 @@ tepl_info_bar_create_label (void)
* See:
* https://wiki.gnome.org/HowDoI/Labels
*
* There is also a safety net in tepl_tab_add_info_bar() which calls
* gtk_widget_set_size_request() on the GtkInfoBar, to set a minimum
* width.
* There is also a safety net with _tepl_info_bar_set_size_request().
*/
gtk_label_set_width_chars (label, 30);
......
/* SPDX-FileCopyrightText: 2005 - Paolo Maggi
* SPDX-FileCopyrightText: 2016 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "config.h"
#include "tepl-io-error-info-bar.h"
#include <glib/gi18n-lib.h>
#include "tepl-utils.h"
struct _TeplIoErrorInfoBarPrivate
{
gint something;
};
/* Verbose error reporting for file I/O operations (load, save, etc.). */
G_DEFINE_TYPE_WITH_PRIVATE (TeplIoErrorInfoBar, _tepl_io_error_info_bar, TEPL_TYPE_INFO_BAR)
static gboolean
is_recoverable_error (const GError *error)
{
gboolean is_recoverable = FALSE;
if (error->domain == G_IO_ERROR)
{
switch (error->code)
{
case G_IO_ERROR_PERMISSION_DENIED:
case G_IO_ERROR_NOT_FOUND:
case G_IO_ERROR_HOST_NOT_FOUND:
case G_IO_ERROR_TIMED_OUT:
case G_IO_ERROR_NOT_MOUNTABLE_FILE:
case G_IO_ERROR_NOT_MOUNTED:
case G_IO_ERROR_BUSY:
is_recoverable = TRUE;
break;
default:
break;
}
}
return is_recoverable;
}
static void
parse_error (const GError *error,
GFile *location,
const gchar *uri_for_display,
gchar **primary_text,
gchar **secondary_text)
{
g_assert (error != NULL);
g_assert (primary_text != NULL);
g_assert (secondary_text != NULL);
*primary_text = NULL;
*secondary_text = NULL;
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
{
*primary_text = g_strdup_printf (_("Could not find the file “%s”."),
uri_for_display);
*secondary_text = g_strdup (_("Please check that you typed the "
"location correctly and try again."));
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED) &&
location != NULL)
{
gchar *uri_scheme;
uri_scheme = g_file_get_uri_scheme (location);
/* Translators: %s is a URI scheme (like for example
* http:, ftp:, etc.).
*/
*secondary_text = g_strdup_printf (_("Unable to handle “%s:” locations."),
uri_scheme);
g_free (uri_scheme);
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE) ||
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
{
*secondary_text = g_strdup (_("The location of the file cannot be accessed."));
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY))
{
*primary_text = g_strdup_printf (_("“%s” is a directory."), uri_for_display);
*secondary_text = g_strdup (_("Please check that you typed the "
"location correctly and try again."));
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME))
{
*primary_text = g_strdup_printf (_("“%s” is not a valid location."), uri_for_display);
*secondary_text = g_strdup (_("Please check that you typed the "
"location correctly and try again."));
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND))
{
/* This case can be hit for user-typed strings like "foo" due to
* the code that guesses web addresses when there's no initial "/".
* But this case is also hit for legitimate web addresses when
* the proxy is set up wrong.
*/
gchar *uri = NULL;
gchar *host = NULL;
if (location != NULL)
{
uri = g_file_get_uri (location);
}
if (uri != NULL)
{
tepl_utils_decode_uri (uri, NULL, NULL, &host, NULL, NULL);
}
if (host != NULL)
{
gchar *host_utf8;
host_utf8 = g_utf8_make_valid (host, -1);
/* Translators: %s is a hostname. */
*secondary_text = g_strdup_printf (_("Host “%s” could not be found. Please check that "
"your proxy settings are correct and try again."),
host_utf8);
g_free (host_utf8);
}
else
{
/* Use the same string as INVALID_HOST. */
*secondary_text = g_strdup_printf (_("Hostname was invalid. Please check that you "
"typed the location correctly and try again."));
}
g_free (uri);
g_free (host);
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE))
{
*secondary_text = g_strdup_printf (_("“%s” is not a regular file."),
uri_for_display);
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
{
*secondary_text = g_strdup (_("Connection timed out. Please try again."));
}
else
{
*secondary_text = g_strdup_printf (_("Unexpected error: %s"), error->message);
}
}
static void
set_io_loading_error (TeplIoErrorInfoBar *info_bar,
gboolean recoverable_error)
{
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Cancel"),
GTK_RESPONSE_CANCEL);
if (recoverable_error)
{
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Retry"),
GTK_RESPONSE_OK);
}
}
static void
set_conversion_error (TeplIoErrorInfoBar *info_bar,
gboolean edit_anyway)
{
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Retry"),
GTK_RESPONSE_OK);
if (edit_anyway)
{
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("Edit Any_way"),
GTK_RESPONSE_YES);
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
}
else
{
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR);
}
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Cancel"),
GTK_RESPONSE_CANCEL);
}
static void
_tepl_io_error_info_bar_class_init (TeplIoErrorInfoBarClass *klass)
{
}
static void
_tepl_io_error_info_bar_init (TeplIoErrorInfoBar *info_bar)
{
info_bar->priv = _tepl_io_error_info_bar_get_instance_private (info_bar);
}
TeplIoErrorInfoBar *
_tepl_io_error_info_bar_new (void)
{
return g_object_new (TEPL_TYPE_IO_ERROR_INFO_BAR, NULL);
}
void
_tepl_io_error_info_bar_set_loading_error (TeplIoErrorInfoBar *info_bar,
GtkSourceFileLoader *loader,
const GError *error)
{
GFile *location;
const GtkSourceEncoding *encoding;
gchar *uri_for_display;
gchar *primary_text = NULL;
gchar *secondary_text = NULL;
gboolean edit_anyway = FALSE;
gboolean convert_error = FALSE;
g_return_if_fail (TEPL_IS_IO_ERROR_INFO_BAR (info_bar));
g_return_if_fail (GTK_SOURCE_IS_FILE_LOADER (loader));
g_return_if_fail (error != NULL);
g_return_if_fail (error->domain == GTK_SOURCE_FILE_LOADER_ERROR ||
error->domain == G_IO_ERROR ||
error->domain == G_CONVERT_ERROR);
location = gtk_source_file_loader_get_location (loader);
encoding = gtk_source_file_loader_get_encoding (loader);
if (location != NULL)
{
uri_for_display = g_file_get_parse_name (location);
}
else
{
/* FIXME ugly. "stdin" should not be hardcoded here. It should
* be set to @loader at the place where we know that we are
* loading from stdin.
*/
uri_for_display = g_strdup ("stdin");
}
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_LINKS))
{
secondary_text = g_strdup (_("The number of followed links is limited and the "
"actual file could not be found within this limit."));
}
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
{
secondary_text = g_strdup (_("You do not have the permissions necessary to open the file."));
}
/* FIXME can the G_IO_ERROR_INVALID_DATA error happen with
* GtkSourceFileLoader?
*/
else if ((g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA) && encoding == NULL) ||
g_error_matches (error,
GTK_SOURCE_FILE_LOADER_ERROR,
GTK_SOURCE_FILE_LOADER_ERROR_ENCODING_AUTO_DETECTION_FAILED))
{
secondary_text = g_strdup (_("Unable to detect the character encoding.\n"
"Please check that you are not trying to open a binary file.\n"
"Select a character encoding from the menu and try again."));
convert_error = TRUE;
}
else if (g_error_matches (error,
GTK_SOURCE_FILE_LOADER_ERROR,
GTK_SOURCE_FILE_LOADER_ERROR_CONVERSION_FALLBACK))
{
primary_text = g_strdup_printf (_("There was a problem opening the file “%s”."), uri_for_display);
secondary_text = g_strdup (_("The file you opened has some invalid characters. "
"If you continue editing this file you could corrupt it.\n"
"You can also choose another character encoding and try again."));
edit_anyway = TRUE;
convert_error = TRUE;
}
/* FIXME can the G_IO_ERROR_INVALID_DATA error happen with
* GtkSourceFileLoader?
*/
else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA) &&
encoding != NULL)
{
gchar *encoding_name;
encoding_name = gtk_source_encoding_to_string (encoding);
primary_text = g_strdup_printf (_("Could not open the file “%s” using the “%s” character encoding."),
uri_for_display,
encoding_name);
secondary_text = g_strdup (_("Please check that you are not trying to open a binary file.\n"
"Select a different character encoding from the menu and try again."));
convert_error = TRUE;
g_free (encoding_name);
}
else
{
parse_error (error, location, uri_for_display, &primary_text, &secondary_text);
}
if (primary_text == NULL)
{
primary_text = g_strdup_printf (_("Could not open the file “%s”."), uri_for_display);
}
if (convert_error)
{
set_conversion_error (info_bar, edit_anyway);
}
else
{
set_io_loading_error (info_bar, is_recoverable_error (error));
}
tepl_info_bar_add_primary_message (TEPL_INFO_BAR (info_bar),
primary_text);
if (secondary_text != NULL)
{
tepl_info_bar_add_secondary_message (TEPL_INFO_BAR (info_bar),
secondary_text);
}
g_free (uri_for_display);
g_free (primary_text);
g_free (secondary_text);
}
/* SPDX-FileCopyrightText: 2016 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef TEPL_IO_ERROR_INFO_BAR_H
#define TEPL_IO_ERROR_INFO_BAR_H
#include <gtksourceview/gtksource.h>
#include "tepl-info-bar.h"
G_BEGIN_DECLS
#define TEPL_TYPE_IO_ERROR_INFO_BAR (_tepl_io_error_info_bar_get_type ())
#define TEPL_IO_ERROR_INFO_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEPL_TYPE_IO_ERROR_INFO_BAR, TeplIoErrorInfoBar))
#define TEPL_IO_ERROR_INFO_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEPL_TYPE_IO_ERROR_INFO_BAR, TeplIoErrorInfoBarClass))
#define TEPL_IS_IO_ERROR_INFO_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEPL_TYPE_IO_ERROR_INFO_BAR))
#define TEPL_IS_IO_ERROR_INFO_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEPL_TYPE_IO_ERROR_INFO_BAR))
#define TEPL_IO_ERROR_INFO_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEPL_TYPE_IO_ERROR_INFO_BAR, TeplIoErrorInfoBarClass))
typedef struct _TeplIoErrorInfoBar TeplIoErrorInfoBar;
typedef struct _TeplIoErrorInfoBarClass TeplIoErrorInfoBarClass;
typedef struct _TeplIoErrorInfoBarPrivate TeplIoErrorInfoBarPrivate;
struct _TeplIoErrorInfoBar
{
TeplInfoBar parent;
TeplIoErrorInfoBarPrivate *priv;
};
struct _TeplIoErrorInfoBarClass
{
TeplInfoBarClass parent_class;
};
G_GNUC_INTERNAL
GType _tepl_io_error_info_bar_get_type (void);
G_GNUC_INTERNAL
TeplIoErrorInfoBar * _tepl_io_error_info_bar_new (void);
G_GNUC_INTERNAL
void _tepl_io_error_info_bar_set_loading_error (TeplIoErrorInfoBar *info_bar,
GtkSourceFileLoader *loader,
const GError *error);
G_END_DECLS
#endif /* TEPL_IO_ERROR_INFO_BAR_H */
/* SPDX-FileCopyrightText: 2005 - Paolo Maggi
* SPDX-FileCopyrightText: 2020 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-FileCopyrightText: 2020-2022 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "config.h"
#include "tepl-io-error-info-bars.h"
#include <glib/gi18n-lib.h>
#include "tepl-utils.h"
/**
* SECTION:io-error-info-bars
......@@ -15,6 +16,19 @@
* Verbose error reporting for file I/O operations.
*/
static gchar *
get_filename_for_display (GFile *location)
{
gchar *parse_name;
gchar *filename_for_display;
parse_name = g_file_get_parse_name (location);
filename_for_display = tepl_utils_replace_home_dir_with_tilde (parse_name);
g_free (parse_name);
return filename_for_display;
}
/**
* tepl_io_error_info_bar_file_already_open:
* @location: the #GFile already open in another window.
......@@ -31,31 +45,66 @@ TeplInfoBar *
tepl_io_error_info_bar_file_already_open (GFile *location)
{
TeplInfoBar *info_bar;
gchar *uri;
gchar *primary_msg;
gchar *filename;
gchar *filename_in_italic;
gchar *app_name_escaped;
gchar *primary_text;
gchar *primary_text_in_bold;
GtkLabel *primary_label;
gchar *secondary_msg;
g_return_val_if_fail (G_IS_FILE (location), NULL);
g_return_val_if_fail (g_get_application_name () != NULL, NULL);
info_bar = tepl_info_bar_new ();
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
tepl_info_bar_set_icon_from_message_type (info_bar, TRUE);
/* Note that below the markup plus translatable strings is not handled
* perfectly. But "perfect is the enemy of good". This should be:
* "foo%sbar" with foo and bar both escaped.
*/
filename = get_filename_for_display (location);
filename_in_italic = g_markup_printf_escaped ("<i>%s</i>", filename);
app_name_escaped = tepl_utils_markup_escape_text (g_get_application_name ());
/* Translators: the first %s is a filename and the second %s is an
* application name.
*/
primary_text = g_strdup_printf (_("This file (%s) is already open in another %s window."),
filename_in_italic,
app_name_escaped);
primary_text_in_bold = g_strdup_printf ("<b>%s</b>", primary_text);
primary_label = tepl_info_bar_create_label ();
gtk_label_set_markup (primary_label, primary_text_in_bold);
gtk_widget_show (GTK_WIDGET (primary_label));
tepl_info_bar_add_content_widget (info_bar,
GTK_WIDGET (primary_label),
TEPL_INFO_BAR_LOCATION_ALONGSIDE_ICON);
secondary_msg = g_strdup_printf (_("%s opened this instance of the file in a non-editable way. "
"Do you want to edit it anyway?"),
g_get_application_name ());
tepl_info_bar_add_secondary_message (info_bar, secondary_msg);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Edit Anyway"),
GTK_RESPONSE_YES);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Don’t Edit"),
_("_Keep Read-Only"),
GTK_RESPONSE_CANCEL);
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
uri = g_file_get_parse_name (location);
primary_msg = g_strdup_printf (_("This file “%s” is already open in another window."), uri);
tepl_info_bar_add_primary_message (info_bar, primary_msg);
g_free (uri);
g_free (primary_msg);
tepl_info_bar_add_secondary_message (info_bar, _("Do you want to edit it anyway?"));
g_free (filename);
g_free (filename_in_italic);
g_free (app_name_escaped);
g_free (primary_text);
g_free (primary_text_in_bold);
g_free (secondary_msg);
return info_bar;
}
......@@ -72,13 +121,12 @@ tepl_io_error_info_bar_file_already_open (GFile *location)
* Returns: (transfer floating): the newly created #TeplInfoBar.
* Since: 5.0
*/
/* TODO add another possible action: save as? */
TeplInfoBar *
tepl_io_error_info_bar_cant_create_backup (GFile *location,
const GError *error)
{
TeplInfoBar *info_bar;
gchar *uri;
gchar *filename;
gchar *primary_msg;
const gchar *secondary_msg;
......@@ -86,21 +134,13 @@ tepl_io_error_info_bar_cant_create_backup (GFile *location,
g_return_val_if_fail (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP), NULL);
info_bar = tepl_info_bar_new ();
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("S_ave Anyway"),
GTK_RESPONSE_YES);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Don’t Save"),
GTK_RESPONSE_CANCEL);
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
tepl_info_bar_set_icon_from_message_type (info_bar, TRUE);
uri = g_file_get_parse_name (location);
primary_msg = g_strdup_printf (_("Could not create a backup file while saving “%s”"), uri);
filename = get_filename_for_display (location);
primary_msg = g_strdup_printf (_("Could not create a backup file while saving “%s”."), filename);
tepl_info_bar_add_primary_message (info_bar, primary_msg);
g_free (uri);
g_free (filename);
g_free (primary_msg);
secondary_msg = _("Could not back up the old copy of the file before saving the new one. "
......@@ -117,6 +157,14 @@ tepl_io_error_info_bar_cant_create_backup (GFile *location,
g_free (error_msg);
}
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Save Anyway"),
GTK_RESPONSE_YES);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Don’t Save"),
GTK_RESPONSE_CANCEL);
return info_bar;
}
......@@ -130,7 +178,7 @@ tepl_io_error_info_bar_cant_create_backup (GFile *location,
* actions:
* - Depending on @document_modified, "Reload" or "Drop changes and reload":
* %GTK_RESPONSE_OK.
* - A close button as added with gtk_info_bar_set_show_close_button().
* - Ignore: %GTK_RESPONSE_CLOSE.
*
* Returns: (transfer floating): the newly created #TeplInfoBar.
* Since: 5.0
......@@ -140,27 +188,43 @@ tepl_io_error_info_bar_externally_modified (GFile *location,
gboolean document_modified)
{
TeplInfoBar *info_bar;
gchar *uri;
gchar *filename;
gchar *primary_msg;
const gchar *secondary_msg;
const gchar *button_text;
g_return_val_if_fail (G_IS_FILE (location), NULL);
info_bar = tepl_info_bar_new ();
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
tepl_info_bar_set_icon_from_message_type (info_bar, TRUE);
uri = g_file_get_parse_name (location);
primary_msg = g_strdup_printf (_("The file “%s” changed on disk."), uri);
filename = get_filename_for_display (location);
primary_msg = g_strdup_printf (_("The file “%s” changed on disk."), filename);
tepl_info_bar_add_primary_message (info_bar, primary_msg);
g_free (uri);
g_free (filename);
g_free (primary_msg);
if (document_modified)
{
secondary_msg = _("This document has unsaved modifications. "
"Do you want to drop your changes and reload the file?");
}
else
{
secondary_msg = _("Do you want to reload the file?");
}
tepl_info_bar_add_secondary_message (info_bar, secondary_msg);
button_text = document_modified ? _("Drop Changes and _Reload") : _("_Reload");
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
button_text,
GTK_RESPONSE_OK);
gtk_info_bar_set_show_close_button (GTK_INFO_BAR (info_bar), TRUE);
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Ignore"),
GTK_RESPONSE_CLOSE);
return info_bar;
}
......@@ -181,33 +245,34 @@ TeplInfoBar *
tepl_io_error_info_bar_invalid_characters (GFile *location)
{
TeplInfoBar *info_bar;
gchar *uri;
gchar *filename;
gchar *primary_msg;
const gchar *secondary_msg;
g_return_val_if_fail (G_IS_FILE (location), NULL);
info_bar = tepl_info_bar_new ();
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("S_ave Anyway"),
GTK_RESPONSE_YES);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Don’t Save"),
GTK_RESPONSE_CANCEL);
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
tepl_info_bar_set_icon_from_message_type (info_bar, TRUE);
uri = g_file_get_parse_name (location);
primary_msg = g_strdup_printf (_("Some invalid characters have been detected while saving “%s”."), uri);
filename = get_filename_for_display (location);
primary_msg = g_strdup_printf (_("Some invalid characters have been detected while saving “%s”."),
filename);
tepl_info_bar_add_primary_message (info_bar, primary_msg);
g_free (uri);
g_free (filename);
g_free (primary_msg);
secondary_msg = _("If you continue saving this file you can corrupt the document. "
"Save anyway?");
tepl_info_bar_add_secondary_message (info_bar, secondary_msg);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Save Anyway"),
GTK_RESPONSE_YES);
gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
_("_Don’t Save"),
GTK_RESPONSE_CANCEL);
return info_bar;
}
......@@ -5,6 +5,7 @@
#include "config.h"
#include "tepl-space-drawer-prefs.h"
#include <glib/gi18n-lib.h>
#include "tepl-utils.h"
/**
* SECTION:space-drawer-prefs
......@@ -204,7 +205,7 @@ create_subtitle_label (const gchar *str)
gchar *str_in_bold;
GtkWidget *label;
str_escaped = g_markup_escape_text (str, -1);
str_escaped = tepl_utils_markup_escape_text (str);
str_in_bold = g_strdup_printf ("<b>%s</b>", str_escaped);
label = gtk_label_new (str_in_bold);
......
......@@ -3,7 +3,7 @@
* SPDX-FileCopyrightText: 2000, 2002 - Chema Celorio, Paolo Maggi
* SPDX-FileCopyrightText: 2003-2005 - Paolo Maggi
*
* SPDX-FileCopyrightText: 2016-2020 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-FileCopyrightText: 2016-2022 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
......@@ -1148,24 +1148,6 @@ tepl_utils_override_font_string (GtkWidget *widget,
}
}
/**
* tepl_utils_override_font:
* @widget: a #GtkWidget.
* @font_str: (nullable): a string representation of a #PangoFontDescription, or
* %NULL to undo the effect of previous calls to this function on @widget.
*
* The old name for tepl_utils_override_font_string().
*
* Since: 6.0
* Deprecated: 6.2
*/
void
tepl_utils_override_font (GtkWidget *widget,
const gchar *font_str)
{
tepl_utils_override_font_string (widget, font_str);
}
/**
* tepl_utils_get_titled_component:
* @title: the title.
......@@ -1280,3 +1262,76 @@ tepl_utils_binding_transform_func_smart_bool (GBinding *binding,
return FALSE;
}
/**
* tepl_utils_can_use_gsettings_schema:
* @schema_id: a #GSettings schema ID.
*
* Checks that a #GSettings schema exists.
*
* Especially useful for external #GSettings (provided by another application
* for instance).
*
* Returns: %TRUE if a #GSettings instance can be created with @schema_id.
* %FALSE otherwise (in that case the program would crash).
* Since: 6.2
*/
gboolean
tepl_utils_can_use_gsettings_schema (const gchar *schema_id)
{
GSettingsSchemaSource *source;
GSettingsSchema *schema;
g_return_val_if_fail (schema_id != NULL, FALSE);
source = g_settings_schema_source_get_default ();
if (source == NULL)
{
return FALSE;
}
schema = g_settings_schema_source_lookup (source, schema_id, TRUE);
if (schema == NULL)
{
return FALSE;
}
g_settings_schema_unref (schema);
return TRUE;
}
/**
* tepl_utils_can_use_gsettings_key:
* @settings: a #GSettings object.
* @key: the key to introspect.
*
* Especially useful for external #GSettings (provided by another application
* for instance).
*
* See also: tepl_utils_can_use_gsettings_schema() which is typically used
* before this function.
*
* Returns: whether the #GSettings key exists.
* Since: 6.2
*/
gboolean
tepl_utils_can_use_gsettings_key (GSettings *settings,
const gchar *key)
{
GSettingsSchema *schema = NULL;
gboolean can_use;
g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_object_get (settings,
"settings-schema", &schema,
NULL);
g_return_val_if_fail (schema != NULL, FALSE);
can_use = g_settings_schema_has_key (schema, key);
g_settings_schema_unref (schema);
return can_use;
}
/* SPDX-FileCopyrightText: 2016-2020 - Sébastien Wilmet <swilmet@gnome.org>
/* SPDX-FileCopyrightText: 2016-2022 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
......@@ -151,11 +151,12 @@ gboolean tepl_utils_binding_transform_func_smart_bool (GBinding *binding,
GValue *to_value,
gpointer user_data);
/* Deprecated */
_TEPL_EXTERN
gboolean tepl_utils_can_use_gsettings_schema (const gchar *schema_id);
_TEPL_EXTERN
void tepl_utils_override_font (GtkWidget *widget,
const gchar *font_str) G_GNUC_DEPRECATED_FOR (tepl_utils_override_font_string);
gboolean tepl_utils_can_use_gsettings_key (GSettings *settings,
const gchar *key);
G_END_DECLS
......
/* SPDX-FileCopyrightText: 2016, 2020 - Sébastien Wilmet <swilmet@gnome.org>
/* SPDX-FileCopyrightText: 2016-2022 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <tepl/tepl.h>
#include "tepl/tepl-io-error-info-bar.h"
#include <stdlib.h>
static void
......@@ -69,23 +68,14 @@ progress_cb (GtkButton *button,
}
static void
add_io_loading_error_info_bar (TeplTab *tab,
GError *error)
already_open_cb (GtkButton *button,
TeplTab *tab)
{
GFile *location;
GtkSourceFile *file;
GtkSourceBuffer *buffer;
GtkSourceFileLoader *loader;
TeplIoErrorInfoBar *info_bar;
location = g_file_new_for_path ("/home/seb/test.c");
file = gtk_source_file_new ();
gtk_source_file_set_location (file, location);
buffer = gtk_source_buffer_new (NULL);
loader = gtk_source_file_loader_new (buffer, file);
TeplInfoBar *info_bar;
info_bar = _tepl_io_error_info_bar_new ();
_tepl_io_error_info_bar_set_loading_error (info_bar, loader, error);
location = g_file_new_for_path ("/home/user/file");
info_bar = tepl_io_error_info_bar_file_already_open (location);
g_signal_connect (info_bar,
"response",
......@@ -96,38 +86,72 @@ add_io_loading_error_info_bar (TeplTab *tab,
gtk_widget_show (GTK_WIDGET (info_bar));
g_object_unref (location);
g_object_unref (file);
g_object_unref (buffer);
g_object_unref (loader);
}
static void
permission_denied_cb (GtkButton *button,
TeplTab *tab)
cant_create_backup_cb (GtkButton *button,
TeplTab *tab)
{
GError *error = g_error_new (G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "blah");
add_io_loading_error_info_bar (tab, error);
GFile *location;
GError *error;
TeplInfoBar *info_bar;
location = g_file_new_for_path ("/home/user/file");
error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, "error message");
info_bar = tepl_io_error_info_bar_cant_create_backup (location, error);
g_signal_connect (info_bar,
"response",
G_CALLBACK (info_bar_response_cb),
NULL);
tepl_tab_add_info_bar (tab, GTK_INFO_BAR (info_bar));
gtk_widget_show (GTK_WIDGET (info_bar));
g_object_unref (location);
g_error_free (error);
}
static void
not_found_cb (GtkButton *button,
TeplTab *tab)
externally_modified_cb (GtkButton *button,
TeplTab *tab)
{
GError *error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "blah");
add_io_loading_error_info_bar (tab, error);
g_error_free (error);
GFile *location;
TeplInfoBar *info_bar;
location = g_file_new_for_path ("/home/user/file");
info_bar = tepl_io_error_info_bar_externally_modified (location, TRUE);
g_signal_connect (info_bar,
"response",
G_CALLBACK (info_bar_response_cb),
NULL);
tepl_tab_add_info_bar (tab, GTK_INFO_BAR (info_bar));
gtk_widget_show (GTK_WIDGET (info_bar));
g_object_unref (location);
}
static void
conversion_fallback_cb (GtkButton *button,
TeplTab *tab)
invalid_characters_cb (GtkButton *button,
TeplTab *tab)
{
GError *error = g_error_new (GTK_SOURCE_FILE_LOADER_ERROR,
GTK_SOURCE_FILE_LOADER_ERROR_CONVERSION_FALLBACK,
"blah");
add_io_loading_error_info_bar (tab, error);
g_error_free (error);
GFile *location;
TeplInfoBar *info_bar;
location = g_file_new_for_path ("/home/user/file");
info_bar = tepl_io_error_info_bar_invalid_characters (location);
g_signal_connect (info_bar,
"response",
G_CALLBACK (info_bar_response_cb),
NULL);
tepl_tab_add_info_bar (tab, GTK_INFO_BAR (info_bar));
gtk_widget_show (GTK_WIDGET (info_bar));
g_object_unref (location);
}
static GtkWidget *
......@@ -136,9 +160,10 @@ create_side_panel (TeplTab *tab)
GtkGrid *vgrid;
GtkWidget *basic;
GtkWidget *progress;
GtkWidget *permission_denied;
GtkWidget *not_found;
GtkWidget *conversion_fallback;
GtkWidget *already_open;
GtkWidget *cant_create_backup;
GtkWidget *externally_modified;
GtkWidget *invalid_characters;
vgrid = GTK_GRID (gtk_grid_new ());
gtk_orientable_set_orientation (GTK_ORIENTABLE (vgrid), GTK_ORIENTATION_VERTICAL);
......@@ -160,27 +185,35 @@ create_side_panel (TeplTab *tab)
tab,
0);
permission_denied = gtk_button_new_with_label ("Permission denied");
gtk_container_add (GTK_CONTAINER (vgrid), permission_denied);
g_signal_connect_object (permission_denied,
already_open = gtk_button_new_with_label ("Already open");
gtk_container_add (GTK_CONTAINER (vgrid), already_open);
g_signal_connect_object (already_open,
"clicked",
G_CALLBACK (already_open_cb),
tab,
0);
cant_create_backup = gtk_button_new_with_label ("Can't create backup");
gtk_container_add (GTK_CONTAINER (vgrid), cant_create_backup);
g_signal_connect_object (cant_create_backup,
"clicked",
G_CALLBACK (permission_denied_cb),
G_CALLBACK (cant_create_backup_cb),
tab,
0);
not_found = gtk_button_new_with_label ("Not found");
gtk_container_add (GTK_CONTAINER (vgrid), not_found);
g_signal_connect_object (not_found,
externally_modified = gtk_button_new_with_label ("Externally modified");
gtk_container_add (GTK_CONTAINER (vgrid), externally_modified);
g_signal_connect_object (externally_modified,
"clicked",
G_CALLBACK (not_found_cb),
G_CALLBACK (externally_modified_cb),
tab,
0);
conversion_fallback = gtk_button_new_with_label ("Conversion fallback");
gtk_container_add (GTK_CONTAINER (vgrid), conversion_fallback);
g_signal_connect_object (conversion_fallback,
invalid_characters = gtk_button_new_with_label ("Invalid characters");
gtk_container_add (GTK_CONTAINER (vgrid), invalid_characters);
g_signal_connect_object (invalid_characters,
"clicked",
G_CALLBACK (conversion_fallback_cb),
G_CALLBACK (invalid_characters_cb),
tab,
0);
......@@ -212,9 +245,9 @@ create_window_content (void)
return GTK_WIDGET (hgrid);
}
gint
main (gint argc,
gchar **argv)
int
main (int argc,
char **argv)
{
GtkWidget *window;
......
subdir('interactive-tests')
subdir('other-tests')
subdir('unit-tests')
executable(
'test-utils-gsettings',
'test-utils-gsettings.c',
dependencies: TEPL_STATIC_DEP
)
/* SPDX-FileCopyrightText: 2022 - Sébastien Wilmet <swilmet@gnome.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include <tepl/tepl.h>
#include <stdlib.h>
#define SETTINGS_DESKTOP_INTERFACE "org.gnome.desktop.interface"
#define SETTINGS_NOT_FOUND "one.this.not.probably"
#define SETTINGS_KEY_MONOSPACE_FONT_NAME "monospace-font-name"
#define SETTINGS_KEY_NOT_FOUND "probably-not-found"
static void
print_can_use_schema (const gchar *schema_id)
{
gboolean can_use;
can_use = tepl_utils_can_use_gsettings_schema (schema_id);
g_print ("Can use '%s': %s\n",
schema_id,
can_use ? "yes" : "no");
}
static void
print_can_use_key (const gchar *schema_id,
const gchar *key)
{
GSettings *settings;
gboolean can_use;
settings = g_settings_new (schema_id);
can_use = tepl_utils_can_use_gsettings_key (settings, key);
g_print ("Can use '%s' -> key '%s': %s\n",
schema_id,
key,
can_use ? "yes" : "no");
}
int
main (void)
{
print_can_use_schema (SETTINGS_DESKTOP_INTERFACE);
print_can_use_schema (SETTINGS_NOT_FOUND);
g_print ("\n");
print_can_use_key (SETTINGS_DESKTOP_INTERFACE, SETTINGS_KEY_MONOSPACE_FONT_NAME);
print_can_use_key (SETTINGS_DESKTOP_INTERFACE, SETTINGS_KEY_NOT_FOUND);
// To test that it actually crashes:
//print_can_use_key (SETTINGS_NOT_FOUND, SETTINGS_KEY_NOT_FOUND);
return EXIT_SUCCESS;
}