|
@@ -26,23 +26,39 @@ struct _TextViewerWindow
|
|
|
{
|
|
{
|
|
|
AdwApplicationWindow parent_instance;
|
|
AdwApplicationWindow parent_instance;
|
|
|
|
|
|
|
|
|
|
+ GSettings *settings;
|
|
|
|
|
+
|
|
|
/* Template widgets */
|
|
/* Template widgets */
|
|
|
- AdwHeaderBar *header_bar;
|
|
|
|
|
- GtkTextView *main_text_view;
|
|
|
|
|
- GtkButton *open_button;
|
|
|
|
|
- GtkLabel *cursor_pos;
|
|
|
|
|
|
|
+ AdwHeaderBar *header_bar;
|
|
|
|
|
+ AdwToastOverlay *toast_overlay;
|
|
|
|
|
+ GtkTextView *main_text_view;
|
|
|
|
|
+ GtkButton *open_button;
|
|
|
|
|
+ GtkLabel *cursor_pos;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_FINAL_TYPE (TextViewerWindow, text_viewer_window, ADW_TYPE_APPLICATION_WINDOW)
|
|
G_DEFINE_FINAL_TYPE (TextViewerWindow, text_viewer_window, ADW_TYPE_APPLICATION_WINDOW)
|
|
|
|
|
|
|
|
|
|
+static void
|
|
|
|
|
+text_viewer_window_finalize (GObject *gobject)
|
|
|
|
|
+{
|
|
|
|
|
+ TextViewerWindow *self = TEXT_VIEWER_WINDOW (gobject);
|
|
|
|
|
+
|
|
|
|
|
+ g_clear_object (&self->settings);
|
|
|
|
|
+
|
|
|
|
|
+ G_OBJECT_CLASS (text_viewer_window_parent_class)->finalize (gobject);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static void
|
|
static void
|
|
|
text_viewer_window_class_init (TextViewerWindowClass *klass)
|
|
text_viewer_window_class_init (TextViewerWindowClass *klass)
|
|
|
{
|
|
{
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
|
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
+
|
|
|
|
|
+ gobject_class->finalize = text_viewer_window_finalize;
|
|
|
|
|
|
|
|
gtk_widget_class_set_template_from_resource (
|
|
gtk_widget_class_set_template_from_resource (
|
|
|
- widget_class,
|
|
|
|
|
- "/com/example/TextViewer/text-viewer-window.ui");
|
|
|
|
|
|
|
+ widget_class,
|
|
|
|
|
+ "/com/example/TextViewer/text-viewer-window.ui");
|
|
|
gtk_widget_class_bind_template_child (widget_class,
|
|
gtk_widget_class_bind_template_child (widget_class,
|
|
|
TextViewerWindow,
|
|
TextViewerWindow,
|
|
|
header_bar);
|
|
header_bar);
|
|
@@ -55,6 +71,9 @@ text_viewer_window_class_init (TextViewerWindowClass *klass)
|
|
|
gtk_widget_class_bind_template_child (widget_class,
|
|
gtk_widget_class_bind_template_child (widget_class,
|
|
|
TextViewerWindow,
|
|
TextViewerWindow,
|
|
|
cursor_pos);
|
|
cursor_pos);
|
|
|
|
|
+ gtk_widget_class_bind_template_child (widget_class,
|
|
|
|
|
+ TextViewerWindow,
|
|
|
|
|
+ toast_overlay);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
static void
|
|
@@ -63,16 +82,17 @@ text_viewer_window__update_cursor_position (GtkTextBuffer *buffer,
|
|
|
TextViewerWindow *self)
|
|
TextViewerWindow *self)
|
|
|
{
|
|
{
|
|
|
int cursor_pos = 0;
|
|
int cursor_pos = 0;
|
|
|
|
|
+ GtkTextIter iter;
|
|
|
|
|
+ g_autofree char *cursor_str;
|
|
|
|
|
|
|
|
// Retrieve the value of the "cursor-position" property
|
|
// Retrieve the value of the "cursor-position" property
|
|
|
g_object_get (buffer, "cursor-position", &cursor_pos, NULL);
|
|
g_object_get (buffer, "cursor-position", &cursor_pos, NULL);
|
|
|
|
|
|
|
|
// Construct the text iterator for the position of the cursor
|
|
// Construct the text iterator for the position of the cursor
|
|
|
- GtkTextIter iter;
|
|
|
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor_pos);
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &iter, cursor_pos);
|
|
|
|
|
|
|
|
// Set the new contents of the label
|
|
// Set the new contents of the label
|
|
|
- g_autofree char *cursor_str =
|
|
|
|
|
|
|
+ cursor_str =
|
|
|
g_strdup_printf ("Ln %d, Col %d",
|
|
g_strdup_printf ("Ln %d, Col %d",
|
|
|
gtk_text_iter_get_line (&iter) + 1,
|
|
gtk_text_iter_get_line (&iter) + 1,
|
|
|
gtk_text_iter_get_line_offset (&iter) + 1);
|
|
gtk_text_iter_get_line_offset (&iter) + 1);
|
|
@@ -86,11 +106,14 @@ open_file_complete (GObject *source_object,
|
|
|
TextViewerWindow *self)
|
|
TextViewerWindow *self)
|
|
|
{
|
|
{
|
|
|
GFile *file = G_FILE (source_object);
|
|
GFile *file = G_FILE (source_object);
|
|
|
-
|
|
|
|
|
g_autofree char *contents = NULL;
|
|
g_autofree char *contents = NULL;
|
|
|
gsize length = 0;
|
|
gsize length = 0;
|
|
|
-
|
|
|
|
|
g_autoptr (GError) error = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
|
+ g_autofree char *display_name = NULL;
|
|
|
|
|
+ g_autoptr (GFileInfo) info;
|
|
|
|
|
+ GtkTextBuffer *buffer;
|
|
|
|
|
+ GtkTextIter start;
|
|
|
|
|
+ g_autofree char *success_msg;
|
|
|
|
|
|
|
|
// Complete the asynchronous operation; this function will either
|
|
// Complete the asynchronous operation; this function will either
|
|
|
// give you the contents of the file as a byte array, or will
|
|
// give you the contents of the file as a byte array, or will
|
|
@@ -103,13 +126,11 @@ open_file_complete (GObject *source_object,
|
|
|
&error);
|
|
&error);
|
|
|
|
|
|
|
|
// Query the display name for the file
|
|
// Query the display name for the file
|
|
|
- g_autofree char *display_name = NULL;
|
|
|
|
|
- g_autoptr (GFileInfo) info =
|
|
|
|
|
- g_file_query_info (file,
|
|
|
|
|
- "standard::display-name",
|
|
|
|
|
- G_FILE_QUERY_INFO_NONE,
|
|
|
|
|
- NULL,
|
|
|
|
|
- NULL);
|
|
|
|
|
|
|
+ info = g_file_query_info (file,
|
|
|
|
|
+ "standard::display-name",
|
|
|
|
|
+ G_FILE_QUERY_INFO_NONE,
|
|
|
|
|
+ NULL,
|
|
|
|
|
+ NULL);
|
|
|
if (info != NULL)
|
|
if (info != NULL)
|
|
|
{
|
|
{
|
|
|
display_name =
|
|
display_name =
|
|
@@ -123,35 +144,42 @@ open_file_complete (GObject *source_object,
|
|
|
// In case of error, print a warning to the standard error output
|
|
// In case of error, print a warning to the standard error output
|
|
|
if (error != NULL)
|
|
if (error != NULL)
|
|
|
{
|
|
{
|
|
|
- g_printerr ("Unable to open “%s”: %s\n",
|
|
|
|
|
- g_file_peek_path (file),
|
|
|
|
|
- error->message);
|
|
|
|
|
|
|
+ g_autofree char *msg =
|
|
|
|
|
+ g_strdup_printf ("Unable to open “%s”", display_name);
|
|
|
|
|
+
|
|
|
|
|
+ adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (msg));
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Ensure that the file is encoded with UTF-8
|
|
// Ensure that the file is encoded with UTF-8
|
|
|
if (!g_utf8_validate (contents, length, NULL))
|
|
if (!g_utf8_validate (contents, length, NULL))
|
|
|
{
|
|
{
|
|
|
- g_printerr ("Unable to load the contents of “%s”: "
|
|
|
|
|
- "the file is not encoded with UTF-8\n",
|
|
|
|
|
- g_file_peek_path (file));
|
|
|
|
|
|
|
+ g_autofree char *msg =
|
|
|
|
|
+ g_strdup_printf ("Invalid text encoding for “%s”", display_name);
|
|
|
|
|
+
|
|
|
|
|
+ adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (msg));
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Retrieve the GtkTextBuffer instance that stores the
|
|
// Retrieve the GtkTextBuffer instance that stores the
|
|
|
// text displayed by the GtkTextView widget
|
|
// text displayed by the GtkTextView widget
|
|
|
- GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
|
|
|
|
|
|
|
+ buffer = gtk_text_view_get_buffer (self->main_text_view);
|
|
|
|
|
|
|
|
// Set the text using the contents of the file
|
|
// Set the text using the contents of the file
|
|
|
gtk_text_buffer_set_text (buffer, contents, length);
|
|
gtk_text_buffer_set_text (buffer, contents, length);
|
|
|
|
|
|
|
|
// Reposition the cursor so it's at the start of the text
|
|
// Reposition the cursor so it's at the start of the text
|
|
|
- GtkTextIter start;
|
|
|
|
|
gtk_text_buffer_get_start_iter (buffer, &start);
|
|
gtk_text_buffer_get_start_iter (buffer, &start);
|
|
|
gtk_text_buffer_place_cursor (buffer, &start);
|
|
gtk_text_buffer_place_cursor (buffer, &start);
|
|
|
|
|
|
|
|
// Set the title using the display name
|
|
// Set the title using the display name
|
|
|
gtk_window_set_title (GTK_WINDOW (self), display_name);
|
|
gtk_window_set_title (GTK_WINDOW (self), display_name);
|
|
|
|
|
+
|
|
|
|
|
+ // Show a toast for the successful loading
|
|
|
|
|
+ success_msg =
|
|
|
|
|
+ g_strdup_printf ("Opened “%s”", display_name);
|
|
|
|
|
+
|
|
|
|
|
+ adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (success_msg));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
static void
|
|
@@ -200,18 +228,21 @@ save_file_complete (GObject *source_object,
|
|
|
gpointer user_data)
|
|
gpointer user_data)
|
|
|
{
|
|
{
|
|
|
GFile *file = G_FILE (source_object);
|
|
GFile *file = G_FILE (source_object);
|
|
|
-
|
|
|
|
|
|
|
+ TextViewerWindow *self = user_data;
|
|
|
g_autoptr (GError) error = NULL;
|
|
g_autoptr (GError) error = NULL;
|
|
|
|
|
+ g_autofree char *display_name = NULL;
|
|
|
|
|
+ g_autoptr (GFileInfo) info;
|
|
|
|
|
+ g_autofree char *msg = NULL;
|
|
|
|
|
+
|
|
|
g_file_replace_contents_finish (file, result, NULL, &error);
|
|
g_file_replace_contents_finish (file, result, NULL, &error);
|
|
|
|
|
|
|
|
// Query the display name for the file
|
|
// Query the display name for the file
|
|
|
- g_autofree char *display_name = NULL;
|
|
|
|
|
- g_autoptr (GFileInfo) info =
|
|
|
|
|
- g_file_query_info (file,
|
|
|
|
|
- "standard::display-name",
|
|
|
|
|
- G_FILE_QUERY_INFO_NONE,
|
|
|
|
|
- NULL,
|
|
|
|
|
- NULL);
|
|
|
|
|
|
|
+ info = g_file_query_info (file,
|
|
|
|
|
+ "standard::display-name",
|
|
|
|
|
+ G_FILE_QUERY_INFO_NONE,
|
|
|
|
|
+ NULL,
|
|
|
|
|
+ NULL);
|
|
|
|
|
+
|
|
|
if (info != NULL)
|
|
if (info != NULL)
|
|
|
{
|
|
{
|
|
|
display_name =
|
|
display_name =
|
|
@@ -228,40 +259,49 @@ save_file_complete (GObject *source_object,
|
|
|
display_name,
|
|
display_name,
|
|
|
error->message);
|
|
error->message);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ if (error != NULL)
|
|
|
|
|
+ msg = g_strdup_printf ("Unable to save as “%s”", display_name);
|
|
|
|
|
+ else
|
|
|
|
|
+ msg = g_strdup_printf ("Saved as “%s”", display_name);
|
|
|
|
|
+
|
|
|
|
|
+ adw_toast_overlay_add_toast (self->toast_overlay, adw_toast_new (msg));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
static void
|
|
|
save_file (TextViewerWindow *self,
|
|
save_file (TextViewerWindow *self,
|
|
|
GFile *file)
|
|
GFile *file)
|
|
|
{
|
|
{
|
|
|
- GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
|
|
|
|
|
-
|
|
|
|
|
- // Retrieve the iterator at the start of the buffer
|
|
|
|
|
- GtkTextIter start;
|
|
|
|
|
- gtk_text_buffer_get_start_iter (buffer, &start);
|
|
|
|
|
-
|
|
|
|
|
- // Retrieve the iterator at the end of the buffer
|
|
|
|
|
- GtkTextIter end;
|
|
|
|
|
- gtk_text_buffer_get_end_iter (buffer, &end);
|
|
|
|
|
-
|
|
|
|
|
- // Retrieve all the visible text between the two bounds
|
|
|
|
|
- char *text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
|
|
|
|
|
-
|
|
|
|
|
- // If there is nothing to save, return early
|
|
|
|
|
- if (text == NULL)
|
|
|
|
|
- return;
|
|
|
|
|
-
|
|
|
|
|
- g_autoptr(GBytes) bytes = g_bytes_new_take (text, strlen (text));
|
|
|
|
|
-
|
|
|
|
|
- // Start the asynchronous operation to save the data into the file
|
|
|
|
|
- g_file_replace_contents_bytes_async (file,
|
|
|
|
|
- bytes,
|
|
|
|
|
- NULL,
|
|
|
|
|
- FALSE,
|
|
|
|
|
- G_FILE_CREATE_NONE,
|
|
|
|
|
- NULL,
|
|
|
|
|
- save_file_complete,
|
|
|
|
|
- self);
|
|
|
|
|
|
|
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
|
|
|
|
|
+ GtkTextIter end;
|
|
|
|
|
+ char *text;
|
|
|
|
|
+ g_autoptr(GBytes) bytes;
|
|
|
|
|
+
|
|
|
|
|
+ // Retrieve the iterator at the start of the buffer
|
|
|
|
|
+ GtkTextIter start;
|
|
|
|
|
+ gtk_text_buffer_get_start_iter (buffer, &start);
|
|
|
|
|
+
|
|
|
|
|
+ // Retrieve the iterator at the end of the buffer
|
|
|
|
|
+ gtk_text_buffer_get_end_iter (buffer, &end);
|
|
|
|
|
+
|
|
|
|
|
+ // Retrieve all the visible text between the two bounds
|
|
|
|
|
+ text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
|
|
|
|
|
+
|
|
|
|
|
+ // If there is nothing to save, return early
|
|
|
|
|
+ if (text == NULL)
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ bytes = g_bytes_new_take (text, strlen (text));
|
|
|
|
|
+
|
|
|
|
|
+ // Start the asynchronous operation to save the data into the file
|
|
|
|
|
+ g_file_replace_contents_bytes_async (file,
|
|
|
|
|
+ bytes,
|
|
|
|
|
+ NULL,
|
|
|
|
|
+ FALSE,
|
|
|
|
|
+ G_FILE_CREATE_NONE,
|
|
|
|
|
+ NULL,
|
|
|
|
|
+ save_file_complete,
|
|
|
|
|
+ self);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
static void
|
|
@@ -297,9 +337,12 @@ text_viewer_window__save_file_dialog (GAction *action,
|
|
|
static void
|
|
static void
|
|
|
text_viewer_window_init (TextViewerWindow *self)
|
|
text_viewer_window_init (TextViewerWindow *self)
|
|
|
{
|
|
{
|
|
|
|
|
+ g_autoptr (GSimpleAction) open_action = g_simple_action_new ("open", NULL);
|
|
|
|
|
+ g_autoptr (GSimpleAction) save_action = g_simple_action_new ("save-as", NULL);
|
|
|
|
|
+ GtkTextBuffer *buffer;
|
|
|
|
|
+
|
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
|
|
|
|
|
|
- g_autoptr (GSimpleAction) open_action = g_simple_action_new ("open", NULL);
|
|
|
|
|
g_signal_connect (open_action,
|
|
g_signal_connect (open_action,
|
|
|
"activate",
|
|
"activate",
|
|
|
G_CALLBACK (text_viewer_window__open_file_dialog),
|
|
G_CALLBACK (text_viewer_window__open_file_dialog),
|
|
@@ -307,13 +350,24 @@ text_viewer_window_init (TextViewerWindow *self)
|
|
|
g_action_map_add_action (G_ACTION_MAP (self),
|
|
g_action_map_add_action (G_ACTION_MAP (self),
|
|
|
G_ACTION (open_action));
|
|
G_ACTION (open_action));
|
|
|
|
|
|
|
|
- g_autoptr (GSimpleAction) save_action = g_simple_action_new ("save-as", NULL);
|
|
|
|
|
g_signal_connect (save_action, "activate", G_CALLBACK (text_viewer_window__save_file_dialog), self);
|
|
g_signal_connect (save_action, "activate", G_CALLBACK (text_viewer_window__save_file_dialog), self);
|
|
|
g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (save_action));
|
|
g_action_map_add_action (G_ACTION_MAP (self), G_ACTION (save_action));
|
|
|
|
|
|
|
|
- GtkTextBuffer *buffer = gtk_text_view_get_buffer (self->main_text_view);
|
|
|
|
|
|
|
+ buffer = gtk_text_view_get_buffer (self->main_text_view);
|
|
|
g_signal_connect (buffer,
|
|
g_signal_connect (buffer,
|
|
|
"notify::cursor-position",
|
|
"notify::cursor-position",
|
|
|
G_CALLBACK (text_viewer_window__update_cursor_position),
|
|
G_CALLBACK (text_viewer_window__update_cursor_position),
|
|
|
self);
|
|
self);
|
|
|
|
|
+
|
|
|
|
|
+ self->settings = g_settings_new ("com.example.TextViewer");
|
|
|
|
|
+
|
|
|
|
|
+ g_settings_bind (self->settings, "window-width",
|
|
|
|
|
+ G_OBJECT (self), "default-width",
|
|
|
|
|
+ G_SETTINGS_BIND_DEFAULT);
|
|
|
|
|
+ g_settings_bind (self->settings, "window-height",
|
|
|
|
|
+ G_OBJECT (self), "default-height",
|
|
|
|
|
+ G_SETTINGS_BIND_DEFAULT);
|
|
|
|
|
+ g_settings_bind (self->settings, "window-maximized",
|
|
|
|
|
+ G_OBJECT (self), "maximized",
|
|
|
|
|
+ G_SETTINGS_BIND_DEFAULT);
|
|
|
}
|
|
}
|