Merge branch 'node-editor-autosave' into 'main'

node-editor: Add autosave

See merge request GNOME/gtk!6748
This commit is contained in:
Matthias Clasen 2024-01-13 02:30:06 +00:00
commit df2cdb6913
4 changed files with 254 additions and 23 deletions

View File

@ -19,6 +19,8 @@
#include "config.h"
#include <glib/gstdio.h>
#ifdef HAVE_PANGOFT
#include <pango/pangofc-fontmap.h>
#endif
@ -287,11 +289,63 @@ node_editor_application_class_init (NodeEditorApplicationClass *class)
application_class->open = node_editor_application_open;
}
static void
print_version (void)
{
g_print ("gtk4-node-editor %s%s%s\n",
PACKAGE_VERSION,
g_strcmp0 (PROFILE, "devel") == 0 ? "-" : "",
g_strcmp0 (PROFILE, "devel") == 0 ? VCS_TAG : "");
}
static int
local_options (GApplication *app,
GVariantDict *options,
gpointer data)
{
gboolean version = FALSE;
gboolean reset = FALSE;
g_variant_dict_lookup (options, "version", "b", &version);
if (version)
{
print_version ();
return 0;
}
g_variant_dict_lookup (options, "reset", "b", &reset);
if (reset)
{
char *path;
path = get_autosave_path ("-unsafe");
g_remove (path);
g_free (path);
path = get_autosave_path (NULL);
g_remove (path);
g_free (path);
}
return -1;
}
NodeEditorApplication *
node_editor_application_new (void)
{
return g_object_new (NODE_EDITOR_APPLICATION_TYPE,
NodeEditorApplication *app;
app = g_object_new (NODE_EDITOR_APPLICATION_TYPE,
"application-id", "org.gtk.gtk4.NodeEditor",
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
g_application_add_main_option (G_APPLICATION (app), "version", 0, 0,G_OPTION_ARG_NONE, "Show program version", NULL);
g_application_add_main_option (G_APPLICATION (app), "reset", 0, 0,G_OPTION_ARG_NONE, "Remove autosave content", NULL);
g_signal_connect (app, "handle-local-options", G_CALLBACK (local_options), NULL);
return app;
}

View File

@ -28,6 +28,8 @@
#include "gsk/broadway/gskbroadwayrenderer.h"
#endif
#include <glib/gstdio.h>
#include <cairo.h>
#ifdef CAIRO_HAS_SVG_SURFACE
#include <cairo-svg.h>
@ -64,6 +66,8 @@ struct _NodeEditorWindow
GFileMonitor *file_monitor;
GArray *errors;
guint update_timeout;
};
struct _NodeEditorWindowClass
@ -1101,6 +1105,9 @@ node_editor_window_finalize (GObject *object)
{
NodeEditorWindow *self = (NodeEditorWindow *)object;
if (self->update_timeout)
g_source_remove (self->update_timeout);
g_array_free (self->errors, TRUE);
g_clear_pointer (&self->node, gsk_render_node_unref);
@ -1540,6 +1547,72 @@ edit_action_cb (GtkWidget *widget,
node_editor_window_edit (self, &start);
}
static void
dialog_response (GtkWidget *dialog,
int response_id,
NodeEditorWindow *self)
{
gtk_window_destroy (GTK_WINDOW (dialog));
if (response_id == GTK_RESPONSE_OK)
{
char *path = get_autosave_path ("-unsafe");
char *contents;
gsize len;
if (g_file_get_contents (path, &contents, &len, NULL))
{
gtk_text_buffer_set_text (self->text_buffer, contents, len);
g_free (contents);
}
g_remove (path);
g_free (path);
}
}
static void
node_editor_window_map (GtkWidget *widget)
{
char *path;
GTK_WIDGET_CLASS (node_editor_window_parent_class)->map (widget);
path = get_autosave_path (NULL);
if (g_file_test (path, G_FILE_TEST_EXISTS))
{
g_free (path);
return;
}
g_free (path);
path = get_autosave_path ("-unsafe");
if (g_file_test (path, G_FILE_TEST_EXISTS))
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
GtkWidget *dialog;
dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
"The application may have crashed.\n"
"Restore auto-saved content anyway ?");
gtk_dialog_add_button (GTK_DIALOG (dialog), "Cancel", GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (dialog), "Restore", GTK_RESPONSE_OK);
g_signal_connect (dialog, "response",
G_CALLBACK (dialog_response), widget);
gtk_window_present (GTK_WINDOW (dialog));
G_GNUC_END_IGNORE_DEPRECATIONS
}
g_free (path);
}
static void
node_editor_window_class_init (NodeEditorWindowClass *class)
{
@ -1558,6 +1631,8 @@ node_editor_window_class_init (NodeEditorWindowClass *class)
widget_class->realize = node_editor_window_realize;
widget_class->unrealize = node_editor_window_unrealize;
widget_class->map = node_editor_window_map;
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, text_view);
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, picture);
gtk_widget_class_bind_template_child (widget_class, NodeEditorWindow, renderer_listbox);
@ -1630,6 +1705,114 @@ static GActionEntry win_entries[] = {
{ "open", window_open, NULL, NULL, NULL },
};
char *
get_autosave_path (const char *suffix)
{
char *path;
char *name;
name = g_strconcat ("autosave", suffix, NULL);
path = g_build_filename (g_get_user_cache_dir (), "gtk4-node-editor", name, NULL);
g_free (name);
return path;
}
static void
set_initial_text (NodeEditorWindow *self)
{
char *path;
char *initial_text;
gsize len;
path = get_autosave_path (NULL);
if (g_file_get_contents (path, &initial_text, &len, NULL))
{
gtk_text_buffer_set_text (self->text_buffer, initial_text, len);
g_free (initial_text);
}
else
{
/* Default */
gtk_text_buffer_set_text (self->text_buffer,
"shadow {\n"
" child: texture {\n"
" bounds: 0 0 128 128;\n"
" texture: url(\"resource:///org/gtk/gtk4/node-editor/icons/apps/org.gtk.gtk4.NodeEditor.svg\");\n"
" }\n"
" shadows: rgba(0,0,0,0.5) 0 1 12;\n"
"}\n"
"\n"
"transform {\n"
" child: text {\n"
" color: rgb(46,52,54);\n"
" font: \"Cantarell Bold 11\";\n"
" glyphs: \"GTK Node Editor\";\n"
" offset: 8 14.418;\n"
" }\n"
" transform: translate(0, 140);\n"
"}", -1);
}
g_free (path);
}
static void
autosave_contents (NodeEditorWindow *self)
{
char *path = NULL;
char *dir = NULL;
char *contents;
GtkTextIter start, end;
gtk_text_buffer_get_bounds (self->text_buffer, &start, &end);
contents = gtk_text_buffer_get_text (self->text_buffer, &start, &end, TRUE);
path = get_autosave_path ("-unsafe");
dir = g_path_get_dirname (path);
g_mkdir_with_parents (dir, 0755);
g_file_set_contents (path, contents, -1, NULL);
g_free (dir);
g_free (path);
g_free (contents);
}
static void
mark_autosave_as_safe (void)
{
char *path1 = NULL;
char *path2 = NULL;
path1 = get_autosave_path ("-unsafe");
path2 = get_autosave_path (NULL);
g_rename (path1, path2);
}
static gboolean
update_timeout_cb (gpointer data)
{
NodeEditorWindow *self = data;
self->update_timeout = 0;
mark_autosave_as_safe ();
return G_SOURCE_REMOVE;
}
static void
initiate_autosave (NodeEditorWindow *self)
{
autosave_contents (self);
if (self->update_timeout != 0)
g_source_remove (self->update_timeout);
self->update_timeout = g_timeout_add (100, update_timeout_cb, self);
}
static void
node_editor_window_init (NodeEditorWindow *self)
{
@ -1684,25 +1867,9 @@ node_editor_window_init (NodeEditorWindow *self)
g_signal_connect (self->scale_scale, "notify::value", G_CALLBACK (scale_changed), self);
gtk_text_view_set_buffer (GTK_TEXT_VIEW (self->text_view), self->text_buffer);
/* Default */
gtk_text_buffer_set_text (self->text_buffer,
"shadow {\n"
" child: texture {\n"
" bounds: 0 0 128 128;\n"
" texture: url(\"resource:///org/gtk/gtk4/node-editor/icons/apps/org.gtk.gtk4.NodeEditor.svg\");\n"
" }\n"
" shadows: rgba(0,0,0,0.5) 0 1 12;\n"
"}\n"
"\n"
"transform {\n"
" child: text {\n"
" color: rgb(46,52,54);\n"
" font: \"Cantarell Bold 11\";\n"
" glyphs: \"GTK Node Editor\";\n"
" offset: 8 14.418;\n"
" }\n"
" transform: translate(0, 140);\n"
"}", -1);
set_initial_text (self);
g_signal_connect_swapped (self->text_buffer, "changed", G_CALLBACK (initiate_autosave), self);
if (g_getenv ("GSK_RENDERER"))
{

View File

@ -37,3 +37,5 @@ NodeEditorWindow * node_editor_window_new (NodeEditorApplication
gboolean node_editor_window_load (NodeEditorWindow *self,
GFile *file);
char * get_autosave_path (const char *suffix);

View File

@ -30,6 +30,14 @@ OPTIONS
Show the application help.
``--version``
Show the program version.
``--reset``
Don't restore autosaved content and remove autosave files.
ENVIRONMENT
-----------