[ease] [editor] Added threaded saving with progress.
- From: Nate Stedman <natesm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ease] [editor] Added threaded saving with progress.
- Date: Sat, 7 Aug 2010 21:01:42 +0000 (UTC)
commit 7edd2a366c5bf069f440e3f57f6839fed96bb74e
Author: Nate Stedman <natesm gmail com>
Date: Sat Aug 7 05:58:19 2010 -0400
[editor] Added threaded saving with progress.
- New Dialogs.Progress class
- New Archiver class with modified code removed from temp
- Saving is threaded if supported, falls back if not
- Progress bar is a little glitchy - some times does not
advance.
data/ui/progress-dialog.ui | 76 ++++++++++++++++
ease-core/Makefile.am | 2 +
ease-core/ease-archiver.vala | 161 +++++++++++++++++++++++++++++++++++
ease-core/ease-dialog-progress.vala | 129 ++++++++++++++++++++++++++++
ease-core/ease-document.vala | 4 +-
ease-core/ease-temp.vala | 69 ---------------
ease/ease-editor-window.vala | 2 +-
7 files changed, 371 insertions(+), 72 deletions(-)
---
diff --git a/data/ui/progress-dialog.ui b/data/ui/progress-dialog.ui
new file mode 100644
index 0000000..ac3216d
--- /dev/null
+++ b/data/ui/progress-dialog.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="dialog">
+ <property name="width_request">350</property>
+ <property name="border_width">5</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="vbox">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkVBox" id="vbox-top">
+ <property name="visible">True</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="progress">
+ <property name="height_request">30</property>
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="action-area">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">cancel</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/ease-core/Makefile.am b/ease-core/Makefile.am
index 490b206..2c8921a 100644
--- a/ease-core/Makefile.am
+++ b/ease-core/Makefile.am
@@ -14,11 +14,13 @@ AM_CPPFLAGS = \
libease_core_0_3_la_SOURCES = \
ease-actor.vala \
ease-animated-zoom-slider.vala \
+ ease-archiver.vala \
ease-background.vala \
ease-background-widget.vala \
ease-cairo-actor.vala \
ease-cairo-element.vala \
ease-color.vala \
+ ease-dialog-progress.vala \
ease-dialogs.vala \
ease-document.vala \
ease-element.vala \
diff --git a/ease-core/ease-archiver.vala b/ease-core/ease-archiver.vala
new file mode 100644
index 0000000..b45b123
--- /dev/null
+++ b/ease-core/ease-archiver.vala
@@ -0,0 +1,161 @@
+/* Ease, a GTK presentation application
+ Copyright (C) 2010 Nate Stedman
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+internal class Ease.Archiver : GLib.Object
+{
+ private string temp_path;
+ private string filename;
+ private Dialogs.Progress dialog;
+ private unowned Thread thread;
+ private bool async = true;
+ private int total_size = 0;
+
+ private static GLib.List<Archiver> archivers = new GLib.List<Archiver>();
+
+ private const int ARCHIVE_BUFFER = 4096;
+ private const string LABEL_MARKUP = "<large>%s</large>".printf(LABEL_TEXT);
+ private const string LABEL_TEXT = _("Saving %s");
+
+ internal Archiver(string temp, string fname, Dialogs.Progress dlog)
+ {
+ temp_path = temp;
+ filename = fname;
+ dialog = dlog;
+ archivers.append(this);
+
+ if (!Thread.supported())
+ {
+ // fall back on non-async archiving
+ async = false;
+ archive_real();
+ return;
+ }
+
+ // this is a little redundant, probably not a huge perf hit though
+ recursive_directory(temp_path, null, (path, full_path) => {
+ Posix.Stat st;
+ Posix.stat(full_path, out st);
+ total_size += (int)st.st_size;
+ });
+
+ dialog.set_label("Hello World");
+ dialog.show();
+ thread = Thread.create(archive_real, true);
+ }
+
+ /**
+ * Does the actual archiving of a directory.
+ */
+ private void* archive_real()
+ {
+ // create a writable archive
+ var archive = new Archive.Write();
+ var buffer = new char[ARCHIVE_BUFFER];
+
+ // set archive format
+ archive.set_format_pax_restricted();
+ archive.set_compression_none();
+
+ // open file
+ if (archive.open_filename(filename) == Archive.Result.FAILED)
+ {
+ throw new Error(0, 0, "Error opening %s", filename);
+ }
+
+ // open the temporary directory
+ var dir = GLib.Dir.open(temp_path, 0);
+
+ // error if the temporary directory has disappeared
+ if (dir == null)
+ {
+ throw new FileError.NOENT(
+ _("Temporary directory doesn't exist: %s"), temp_path);
+ }
+
+ // add files
+ recursive_directory(temp_path, null, (path, full_path) => {
+ // create an archive entry for the file
+ var entry = new Archive.Entry();
+ entry.set_pathname(path);
+ entry.set_perm(0644);
+ Posix.Stat st;
+ Posix.stat(full_path, out st);
+ entry.copy_stat(st);
+ arc_fail(archive.write_header(entry), archive);
+
+ double size = (double)st.st_size;
+ double size_frac = size / total_size;
+
+ // write the file
+ var fd = Posix.open(full_path, Posix.O_RDONLY);
+ var len = Posix.read(fd, buffer, sizeof(char) * ARCHIVE_BUFFER);
+ while(len > 0)
+ {
+ archive.write_data(buffer, len);
+ len = Posix.read(fd, buffer, sizeof(char) * ARCHIVE_BUFFER);
+ lock (dialog) dialog.add_fraction(size_frac * (len / size));
+ }
+ Posix.close(fd);
+ arc_fail(archive.finish_entry(), archive);
+ });
+
+ // close the archive
+ arc_fail(archive.close(), archive);
+
+ // destroy the progress dialog in async mode
+ lock (dialog) if (async) dialog.destroy();
+
+ // stop tracking this archiver
+ lock (archivers) { archivers.remove(this); }
+
+ return null;
+ }
+
+ /**
+ * Produces an error if a libarchive error occurs.
+ */
+ private static void arc_fail(Archive.Result result, Archive.Archive archive)
+ {
+ if (result != Archive.Result.OK) critical(archive.error_string());
+ }
+}
+
+namespace Ease
+{
+ /**
+ * Asynchronously (if supported) creates an archive from a temporary
+ * directory. Otherwise, falls back on synchronous archiving.
+ *
+ * archive() uses libarchive to create a tarball of the temporary directory.
+ *
+ * @param temp_path The path of the temporary directory.
+ * @param filename The filename of the archive to save to.
+ * @param title The title of the progress dialog.
+ * @param win The window to display a progress dialog modal for.
+ */
+ internal static void archive(string temp_path,
+ string filename,
+ string title,
+ Gtk.Window? win) throws Error
+ {
+ // create a progress dialog
+ var dialog = new Dialogs.Progress(title, false, 1, win);
+
+ // archive away!
+ var arc = new Archiver(temp_path, filename, dialog);
+ }
+}
diff --git a/ease-core/ease-dialog-progress.vala b/ease-core/ease-dialog-progress.vala
new file mode 100644
index 0000000..3f5fc0f
--- /dev/null
+++ b/ease-core/ease-dialog-progress.vala
@@ -0,0 +1,129 @@
+/* Ease, a GTK presentation application
+ Copyright (C) 2010 Nate Stedman
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * Controls a dialog displaying a progress bar, and optionally a cancel button
+ * and label.
+ *
+ * Note that this class is not a subclass of Gtk.Dialog.
+ */
+public class Ease.Dialogs.Progress : GLib.Object
+{
+ private const string UI_FILE = "progress-dialog.ui";
+
+ private Gtk.Dialog dialog;
+ private Gtk.Button cancel;
+ private Gtk.Label label;
+ private Gtk.ProgressBar progress;
+ private double max_val;
+
+ /**
+ * Creates a progress dialog.
+ *
+ * @param title The title of the dialog.
+ * @param cancellable If the dialog should display a cancel button.
+ * @param max The maximum value of the dialog.
+ * @param modal The window the dialog should be modal for, or null.
+ */
+ public Progress(string title, bool cancellable, double max,
+ Gtk.Window? modal)
+ {
+ max_val = max;
+
+ var builder = new Gtk.Builder();
+ try
+ {
+ builder.add_from_file(data_path(Path.build_filename(Temp.UI_DIR,
+ UI_FILE)));
+ }
+ catch (Error e) { error("Error loading UI: %s", e.message); }
+
+ // get builder objects
+ dialog = builder.get_object("dialog") as Gtk.Dialog;
+ cancel = builder.get_object("cancel") as Gtk.Button;
+ label = builder.get_object("label") as Gtk.Label;
+ progress = builder.get_object("progress") as Gtk.ProgressBar;
+
+ // set basic stuff
+ dialog.title = title;
+ cancel.visible = cancellable;
+ }
+
+ /**
+ * Shows the progress dialog.
+ */
+ public void show()
+ {
+ dialog.show();
+ }
+
+ /**
+ * Hides the progress dialog.
+ */
+ public void destroy()
+ {
+ dialog.destroy();
+ }
+
+ /**
+ * Sets (or unsets with null) the label of this dialog. Markup allowed.
+ */
+ public void set_label(string? str)
+ {
+ if (str == null)
+ {
+ label.hide();
+ return;
+ }
+ label.set_markup(str);
+ label.show_all();
+ }
+
+ /**
+ * Sets the dialog's progress to a value relative to the maximum, specified
+ * in the constructor.
+ */
+ public void set(double val)
+ {
+ progress.set_fraction(val / max_val);
+ }
+
+ /**
+ * Sets the dialog's progress to a fraction between 0 and 1.
+ */
+ public void set_fraction(double val)
+ {
+ progress.set_fraction(val);
+ }
+
+ /**
+ * Adds a value, relative to the maximum specified in the constructor, to
+ * the progress bar.
+ */
+ public void add(double val)
+ {
+ progress.set_fraction(progress.get_fraction() + val / max_val);
+ }
+
+ /**
+ * Adds a fractional value to the progress bar.
+ */
+ public void add_fraction(double val)
+ {
+ progress.set_fraction(progress.get_fraction() + val);
+ }
+}
diff --git a/ease-core/ease-document.vala b/ease-core/ease-document.vala
index 39a2495..58e8886 100644
--- a/ease-core/ease-document.vala
+++ b/ease-core/ease-document.vala
@@ -194,7 +194,7 @@ public class Ease.Document : GLib.Object, UndoSource
append_slide(slide);
}
- public void to_json() throws GLib.Error
+ public void to_json(Gtk.Window? window) throws GLib.Error
{
var root = new Json.Node(Json.NodeType.OBJECT);
var obj = new Json.Object();
@@ -223,7 +223,7 @@ public class Ease.Document : GLib.Object, UndoSource
generator.to_file(Path.build_filename(path, JSON_FILE));
// archive
- Temp.archive(path, filename);
+ archive(path, filename, _("Saving Document"), window);
}
/**
diff --git a/ease-core/ease-temp.vala b/ease-core/ease-temp.vala
index cc35ad5..0d197cb 100644
--- a/ease-core/ease-temp.vala
+++ b/ease-core/ease-temp.vala
@@ -177,75 +177,6 @@ public static class Ease.Temp : Object
}
/**
- * Creates an archive from a temporary directory.
- *
- * archive() uses libarchive to create a tarball of the temporary directory.
- *
- * @param temp_path The path of the temporary directory.
- * @param filename The filename of the archive to save to.
- */
- internal static void archive(string temp_path, string filename) throws Error
- {
- // create a writable archive
- var archive = new Archive.Write();
- var buffer = new char[ARCHIVE_BUFFER];
-
- // set archive format
- archive.set_format_pax_restricted();
- archive.set_compression_none();
-
- // open file
- if (archive.open_filename(filename) == Archive.Result.FAILED)
- {
- throw new Error(0, 0, "Error opening %s", filename);
- }
-
- // open the temporary directory
- var dir = GLib.Dir.open(temp_path, 0);
-
- // error if the temporary directory has disappeared
- if (dir == null)
- {
- throw new FileError.NOENT(
- _("Temporary directory doesn't exist: %s"), temp_path);
- }
-
- // add files
- recursive_directory(temp_path, null, (path, full_path) => {
- // create an archive entry for the file
- var entry = new Archive.Entry();
- entry.set_pathname(path);
- entry.set_perm(0644);
- Posix.Stat st;
- Posix.stat(full_path, out st);
- entry.copy_stat(st);
- arc_fail(archive.write_header(entry), archive);
-
- // write the file
- var fd = Posix.open(full_path, Posix.O_RDONLY);
- var len = Posix.read(fd, buffer, sizeof(char) * ARCHIVE_BUFFER);
- while(len > 0)
- {
- archive.write_data(buffer, len);
- len = Posix.read(fd, buffer, sizeof(char) * ARCHIVE_BUFFER);
- }
- Posix.close(fd);
- arc_fail(archive.finish_entry(), archive);
- });
-
- // close the archive
- arc_fail(archive.close(), archive);
- }
-
- /**
- * Produces an error if a libarchive error occurs.
- */
- private static void arc_fail(Archive.Result result, Archive.Archive archive)
- {
- if (result != Archive.Result.OK) error(archive.error_string());
- }
-
- /**
* Deletes all temporary directories created by this instance of Ease.
* Call when exiting.
*/
diff --git a/ease/ease-editor-window.vala b/ease/ease-editor-window.vala
index 702b6fd..521cd7d 100644
--- a/ease/ease-editor-window.vala
+++ b/ease/ease-editor-window.vala
@@ -560,7 +560,7 @@ internal class Ease.EditorWindow : Gtk.Window
try
{
- document.to_json();
+ document.to_json(this);
last_saved = 0;
}
catch (GLib.Error e)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]