[simple-scan/gnome-3-10: 1/8] Rewrite autosaving. Now uses files for scan data, is more efficient in writing to disk and drops dep
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [simple-scan/gnome-3-10: 1/8] Rewrite autosaving. Now uses files for scan data, is more efficient in writing to disk and drops dep
- Date: Thu, 27 Apr 2017 09:14:55 +0000 (UTC)
commit 5774f48e7f8cb7bf603f511c7bbd96cad4d28a20
Author: Robert Ancell <robert ancell canonical com>
Date: Fri Nov 1 12:10:37 2013 -0700
Rewrite autosaving. Now uses files for scan data, is more efficient in writing to disk and drops
dependency on sqlite.
configure.ac | 1 -
src/autosave-manager.vala | 693 ++++++++++++++++-----------------------------
src/book.vala | 5 +-
src/page.vala | 47 +++-
src/simple-scan.vala | 9 +-
src/ui.vala | 30 ++-
6 files changed, 313 insertions(+), 472 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index aa152be..0add5a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,7 +29,6 @@ PKG_CHECK_MODULES(SIMPLE_SCAN, [
cairo
gdk-pixbuf-2.0
gudev-1.0
- sqlite3
])
PKG_CHECK_MODULES(COLORD, [
diff --git a/src/autosave-manager.vala b/src/autosave-manager.vala
index eb8f1c5..b8d9534 100644
--- a/src/autosave-manager.vala
+++ b/src/autosave-manager.vala
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2011 Timo Kluck
- * Author: Timo Kluck <tkluck infty nl>
+ * Authors: Timo Kluck <tkluck infty nl>
+ * Robert Ancell <robert ancell canonical com>
*
* 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
@@ -9,545 +10,353 @@
* license.
*/
-/*
- * We store autosaves in a database named
- * ~/.cache/simple-scan/autosaves/autosaves.db
- * It contains a single table of pages, each containing the process id (pid) of
- * the simple-scan instance that saved it, and a hash of the Book and Page
- * objects corresponding to it. The pixels are saved as a BLOB.
- * Additionally, the autosaves directory contains a number of tiff files that
- * the user can use for manual recovery.
- *
- * At startup, we check whether autosaves.db contains any records
- * with a pid that does not match a current pid for simple-scan. If so, we take
- * ownership by an UPDATE statement changing to our own pid. Then, we
- * recover the book. We're trying our best to avoid the possible race
- * condition if several instances of simple-scan are started simultaneously.
- *
- * At application exit, we delete the records corresponding to our own pid.
- *
- * Important notes:
- * - We enforce that there is only one AutosaveManager instance in a given
- * process by using a create function.
- * - It should be possible to change the book object at runtime, although this
- * is not used in the current implementation so it has not been tested.
- */
-
public class AutosaveManager
{
private static string AUTOSAVE_DIR = Path.build_filename (Environment.get_user_cache_dir (),
"simple-scan", "autosaves");
- private static string AUTOSAVE_NAME = "autosaves";
- private static string AUTOSAVE_EXT = ".db";
- private static string AUTOSAVE_FILENAME = Path.build_filename (AUTOSAVE_DIR, AUTOSAVE_NAME +
AUTOSAVE_EXT);
-
- private static string PID = ((int)(Posix.getpid ())).to_string ();
- private static int number_of_instances = 0;
-
- private Sqlite.Database database_connection;
- private Book _book = null;
+ private static string AUTOSAVE_FILENAME = "autosave.book";
+ private static string AUTOSAVE_PATH = Path.build_filename (AUTOSAVE_DIR, AUTOSAVE_FILENAME);
private uint update_timeout = 0;
- private HashTable<Page, bool> dirty_pages;
+ private HashTable<Page, string> page_filenames;
+
+ private Book book_ = null;
public Book book
{
get
{
- return _book;
+ return book_;
}
set
{
- if (_book != null)
+ if (book_ != null)
{
- for (var i = 0; i < _book.get_n_pages (); i++)
+ for (var i = 0; i < book_.get_n_pages (); i++)
{
- var page = _book.get_page (i);
+ var page = book_.get_page (i);
on_page_removed (page);
}
- _book.page_added.disconnect (on_page_added);
- _book.page_removed.disconnect (on_page_removed);
- _book.reordered.disconnect (on_reordered);
- _book.cleared.disconnect (on_cleared);
+ book_.page_added.disconnect (on_page_added);
+ book_.page_removed.disconnect (on_page_removed);
+ book_.reordered.disconnect (on_changed);
+ book_.cleared.disconnect (on_cleared);
}
- _book = value;
- _book.page_added.connect (on_page_added);
- _book.page_removed.connect (on_page_removed);
- _book.reordered.connect (on_reordered);
- _book.cleared.connect (on_cleared);
- for (var i = 0; i < _book.get_n_pages (); i++)
+ book_ = value;
+ book_.page_added.connect (on_page_added);
+ book_.page_removed.connect (on_page_removed);
+ book_.reordered.connect (on_changed);
+ book_.cleared.connect (on_cleared);
+ for (var i = 0; i < book_.get_n_pages (); i++)
{
- var page = book.get_page (i);
+ var page = book_.get_page (i);
on_page_added (page);
}
}
}
- public static AutosaveManager? create (ref Book book)
+ public AutosaveManager ()
+ {
+ page_filenames = new HashTable<Page, string> (direct_hash, direct_equal);
+ }
+
+ public void load ()
{
- /* compare autosave directories with pids of current instances of simple-scan
- * take ownership of one of the ones that are unowned by renaming to the
- * own pid. Then open the database and fill the book with the pages it
- * contains.
- */
- if (number_of_instances > 0)
- assert_not_reached ();
+ debug ("Loading autosave information");
- var man = new AutosaveManager ();
- number_of_instances++;
+ book.clear ();
+ page_filenames.remove_all ();
+ var file = new KeyFile ();
try
{
- man.database_connection = open_database_connection ();
+ file.load_from_file (AUTOSAVE_PATH, KeyFileFlags.NONE);
}
- catch
+ catch (Error e)
{
- warning ("Could not connect to the autosave database; no autosaves will be kept.");
- return null;
+ warning ("Could not load autosave infomation; not restoring any autosaves");
+ return;
}
-
- bool any_pages_recovered = false;
- try
+ var pages = get_value (file, "simple-scan", "pages");
+ foreach (var page_name in pages.split (" "))
{
- // FIXME: this only works on linux. We can maybe use Gtk.Application and some session bus id
instead?
- string current_pids;
- Process.spawn_command_line_sync ("pidof simple-scan | sed \"s/ /,/g\"", out current_pids);
- current_pids = current_pids.strip ();
- Sqlite.Statement stmt;
- string query = @"
- SELECT process_id, book_hash, book_revision FROM pages
- WHERE NOT process_id IN ($current_pids)
- LIMIT 1
- ";
-
- var result = man.database_connection.prepare_v2 (query, -1, out stmt);
- if (result == Sqlite.OK)
+ debug ("Loading automatically saved page %s", page_name);
+
+ var scan_width = get_integer (file, page_name, "scan-width");
+ var scan_height = get_integer (file, page_name, "scan-height");
+ var rowstride = get_integer (file, page_name, "rowstride");
+ var n_channels = get_integer (file, page_name, "n-channels");
+ var depth = get_integer (file, page_name, "depth");
+ var dpi = get_integer (file, page_name, "dpi");
+ var scan_direction_name = get_value (file, page_name, "scan-direction");
+ ScanDirection scan_direction = ScanDirection.TOP_TO_BOTTOM;
+ switch (scan_direction_name)
{
- while (stmt.step () == Sqlite.ROW)
+ case "TOP_TO_BOTTOM":
+ scan_direction = ScanDirection.TOP_TO_BOTTOM;
+ break;
+ case "LEFT_TO_RIGHT":
+ scan_direction = ScanDirection.LEFT_TO_RIGHT;
+ break;
+ case "BOTTOM_TO_TOP":
+ scan_direction = ScanDirection.BOTTOM_TO_TOP;
+ break;
+ case "RIGHT_TO_LEFT":
+ scan_direction = ScanDirection.RIGHT_TO_LEFT;
+ break;
+ }
+ var color_profile = get_value (file, page_name, "color-profile");
+ if (color_profile == "")
+ color_profile = null;
+ var pixels_filename = get_value (file, page_name, "pixels-filename");
+ var has_crop = get_boolean (file, page_name, "has-crop");
+ var crop_name = get_value (file, page_name, "crop-name");
+ if (crop_name == "")
+ crop_name = null;
+ var crop_x = get_integer (file, page_name, "crop-x");
+ var crop_y = get_integer (file, page_name, "crop-y");
+ var crop_width = get_integer (file, page_name, "crop-width");
+ var crop_height = get_integer (file, page_name, "crop-height");
+
+ uchar[]? pixels = null;
+ if (pixels_filename != "")
+ {
+ var path = Path.build_filename (AUTOSAVE_DIR, pixels_filename);
+ var f = File.new_for_path (path);
+ try
+ {
+ f.load_contents (null, out pixels, null);
+ }
+ catch (Error e)
{
- debug ("Found at least one autosave page, taking ownership");
- var unowned_pid = stmt.column_int (0);
- var book_hash = stmt.column_int (1);
- var book_revision = stmt.column_int (2);
- /* there's a possible race condition here when several instances
- * try to take ownership of the same rows. What would happen is
- * that this operations would affect no rows if another process
- * has taken ownership in the mean time. In that case, recover_book
- * does nothing, so there should be no problem.
- */
- query = @"
- UPDATE pages
- SET process_id = $PID
- WHERE process_id = ?2
- AND book_hash = ?3
- AND book_revision = ?4";
- Sqlite.Statement stmt2;
- result = man.database_connection.prepare_v2(query, -1, out stmt2);
- if (result != Sqlite.OK)
- warning (@"Error preparing statement: $query");
-
- stmt2.bind_int64 (2, unowned_pid);
- stmt2.bind_int64 (3, book_hash);
- stmt2.bind_int64 (4, book_revision);
- result = stmt2.step();
- if (result == Sqlite.DONE)
- {
- any_pages_recovered = true;
- man.recover_book (ref book);
- }
- else
- warning ("Error %d while executing query", result);
+ warning ("Failed to load pixel information");
+ continue;
}
}
- else
- warning ("Error %d while preparing statement", result);
+
+ var page = new Page.from_data (scan_width,
+ scan_height,
+ rowstride,
+ n_channels,
+ depth,
+ dpi,
+ scan_direction,
+ color_profile,
+ pixels,
+ has_crop,
+ crop_name,
+ crop_x,
+ crop_y,
+ crop_width,
+ crop_height);
+ page_filenames.insert (page, pixels_filename);
+ book.append_page (page);
}
- catch (SpawnError e)
+ }
+
+ private string get_value (KeyFile file, string group_name, string key, string default = "")
+ {
+ try
{
- warning ("Could not obtain current process ids; not restoring any autosaves");
+ return file.get_value (group_name, key);
}
-
- man.book = book;
- if (!any_pages_recovered)
+ catch (Error e)
{
- for (var i = 0; i < book.get_n_pages (); i++)
- {
- var page = book.get_page (i);
- man.on_page_added (page);
- }
+ return default;
}
+ }
- return man;
+ private int get_integer (KeyFile file, string group_name, string key, int default = 0)
+ {
+ try
+ {
+ return file.get_integer (group_name, key);
+ }
+ catch (Error e)
+ {
+ return default;
+ }
}
- private AutosaveManager ()
+ private bool get_boolean (KeyFile file, string group_name, string key, bool default = false)
{
- dirty_pages = new HashTable<Page, bool> (direct_hash, direct_equal);
+ try
+ {
+ return file.get_boolean (group_name, key);
+ }
+ catch (Error e)
+ {
+ return default;
+ }
}
public void cleanup ()
{
- debug ("Clean exit; deleting autosave records");
+ debug ("Deleting autosave records");
if (update_timeout > 0)
Source.remove (update_timeout);
update_timeout = 0;
- warn_if_fail (database_connection.exec (@"
- DELETE FROM pages
- WHERE process_id = $PID
- ") == Sqlite.OK);
- }
-
- static Sqlite.Database open_database_connection () throws Error
- {
- var autosaves_dir = File.new_for_path (AUTOSAVE_DIR);
+ Dir dir;
try
{
- autosaves_dir.make_directory_with_parents ();
+ dir = Dir.open (AUTOSAVE_DIR);
}
- catch
- { // the directory already exists
- // pass
+ catch (Error e)
+ {
+ warning ("Failed to delete autosaves: %s", e.message);
+ return;
+ }
+
+ while (true)
+ {
+ var filename = dir.read_name ();
+ if (filename == null)
+ break;
+ var path = Path.build_filename (AUTOSAVE_DIR, filename);
+ FileUtils.unlink (path);
}
- Sqlite.Database connection;
- if (Sqlite.Database.open (AUTOSAVE_FILENAME, out connection) != Sqlite.OK)
- throw new IOError.FAILED ("Could not connect to autosave database");
- string query = @"
- CREATE TABLE IF NOT EXISTS pages (
- id integer PRIMARY KEY,
- process_id integer,
- page_hash integer,
- book_hash integer,
- book_revision integer,
- page_number integer,
- dpi integer,
- width integer,
- height integer,
- depth integer,
- n_channels integer,
- rowstride integer,
- color_profile string,
- crop_x integer,
- crop_y integer,
- crop_width integer,
- crop_height integer,
- scan_direction integer,
- pixels binary
- )";
- var result = connection.exec(query);
- if (result != Sqlite.OK)
- warning ("Error %d while executing query", result);
- return connection;
}
- void on_page_added (Page page)
+ public void on_page_added (Page page)
{
- insert_page (page);
- // TODO: save a tiff file
- page.size_changed.connect (on_page_changed);
- page.scan_direction_changed.connect (on_page_changed);
- page.crop_changed.connect (on_page_changed);
- page.scan_finished.connect (on_page_changed);
+ page.scan_finished.connect (on_scan_finished);
+ page.crop_changed.connect (on_changed);
}
public void on_page_removed (Page page)
{
- page.pixels_changed.disconnect (on_page_changed);
- page.size_changed.disconnect (on_page_changed);
- page.scan_direction_changed.disconnect (on_page_changed);
- page.crop_changed.disconnect (on_page_changed);
- page.scan_finished.connect (on_page_changed);
-
- string query = @"
- DELETE FROM pages
- WHERE process_id = $PID
- AND page_hash = ?2
- AND book_hash = ?3
- AND book_revision = ?4
- ";
- Sqlite.Statement stmt;
- var result = database_connection.prepare_v2 (query, -1, out stmt);
- if (result != Sqlite.OK)
- warning (@"Error $result while preparing query");
- stmt.bind_int64 (2, direct_hash (page));
- stmt.bind_int64 (3, direct_hash (book));
- stmt.bind_int64 (4, cur_book_revision);
-
- result = stmt.step();
- if (result != Sqlite.DONE)
- warning ("Error %d while executing query", result);
- }
+ page.scan_finished.disconnect (on_scan_finished);
+ page.crop_changed.disconnect (on_changed);
- public void on_reordered ()
- {
- for (var i=0; i < book.get_n_pages (); i++)
- {
- var page = book.get_page (i);
- string query = @"
- UPDATE pages SET page_number = ?5
- WHERE process_id = $PID
- AND page_hash = ?2
- AND book_hash = ?3
- AND book_revision = ?4
- ";
- Sqlite.Statement stmt;
- var result = database_connection.prepare_v2 (query, -1, out stmt);
- if (result != Sqlite.OK)
- warning (@"Error $result while preparing query");
-
- stmt.bind_int64 (5, i);
- stmt.bind_int64 (2, direct_hash (page));
- stmt.bind_int64 (3, direct_hash (book));
- stmt.bind_int64 (4, cur_book_revision);
-
- result = stmt.step();
- if (result != Sqlite.DONE)
- warning ("Error %d while executing query", result);
- }
+ var filename = page_filenames.lookup (page);
+ if (filename != null)
+ FileUtils.unlink (filename);
+ page_filenames.remove (page);
}
- public void on_page_changed (Page page)
+ public void on_scan_finished (Page page)
{
- update_page (page);
+ save_pixels (page);
+ save (false);
}
- public void on_needs_saving_changed (Book book)
+ public void on_changed ()
{
- for (var n = 0; n < book.get_n_pages (); n++)
- {
- var page = book.get_page (n);
- update_page (page);
- }
+ save ();
}
- private int cur_book_revision = 0;
-
public void on_cleared ()
{
- cur_book_revision++;
+ page_filenames.remove_all ();
+ save ();
}
- private void insert_page (Page page)
+ private void save (bool do_timeout = true)
{
- debug ("Adding an autosave for a new page");
- string query = @"
- INSERT INTO pages
- (process_id,
- page_hash,
- book_hash,
- book_revision)
- VALUES
- ($PID,
- ?2,
- ?3,
- ?4)
- ";
- Sqlite.Statement stmt;
- var result = database_connection.prepare_v2 (query, -1, out stmt);
- if (result != Sqlite.OK)
- warning (@"Error $result while preparing query");
-
- stmt.bind_int64 (2, direct_hash (page));
- stmt.bind_int64 (3, direct_hash (book));
- stmt.bind_int64 (4, cur_book_revision);
-
- result = stmt.step();
- if (result != Sqlite.DONE)
- warning ("Error %d while executing query", result);
-
- update_page (page);
- }
+ if (update_timeout == 0 && do_timeout)
+ debug ("Waiting to autosave...");
- private void update_page (Page page)
- {
- dirty_pages.insert (page, true);
+ /* Cancel existing timeout */
if (update_timeout > 0)
Source.remove (update_timeout);
- update_timeout = Timeout.add (100, () =>
- {
- var iter = HashTableIter<Page, bool> (dirty_pages);
- Page p;
- bool is_dirty;
- while (iter.next (out p, out is_dirty))
- real_update_page (p);
-
- dirty_pages.remove_all ();
- update_timeout = 0;
-
- return false;
- });
- }
-
- private void real_update_page (Page page)
- {
- debug ("Updating the autosave for a page");
-
- int crop_x;
- int crop_y;
- int crop_width;
- int crop_height;
- page.get_crop (out crop_x, out crop_y, out crop_width, out crop_height);
-
- Sqlite.Statement stmt;
- string query = @"
- UPDATE pages
- SET
- page_number=$(book.get_page_index (page)),
- dpi=$(page.get_dpi ()),
- width=$(page.get_width ()),
- height=$(page.get_height ()),
- depth=$(page.get_depth ()),
- n_channels=$(page.get_n_channels ()),
- rowstride=$(page.get_rowstride ()),
- crop_x=$crop_x,
- crop_y=$crop_y,
- crop_width=$crop_width,
- crop_height=$crop_height,
- scan_direction=$((int)page.get_scan_direction ()),
- color_profile=?1,
- pixels=?2
- WHERE process_id = $PID
- AND page_hash = ?4
- AND book_hash = ?5
- AND book_revision = ?6
- ";
-
- var result = database_connection.prepare_v2 (query, -1, out stmt);
- if (result != Sqlite.OK)
- {
- warning ("Error %d while preparing statement", result);
- return;
- }
-
- stmt.bind_int64 (4, direct_hash (page));
- stmt.bind_int64 (5, direct_hash (book));
- stmt.bind_int64 (6, cur_book_revision);
- result = stmt.bind_text (1, page.get_color_profile () ?? "");
-
- if (result != Sqlite.OK)
- warning ("Error %d while binding text", result);
+ update_timeout = 0;
- if (page.get_pixels () != null)
+ if (do_timeout)
{
- // (-1) is the special value SQLITE_TRANSIENT
- result = stmt.bind_blob (2, page.get_pixels (), page.get_pixels ().length, (DestroyNotify)(-1));
- if (result != Sqlite.OK)
- warning ("Error %d while binding blob", result);
+ update_timeout = Timeout.add (100, () =>
+ {
+ real_save ();
+ update_timeout = 0;
+ return false;
+ });
}
else
- warn_if_fail (stmt.bind_null (2) == Sqlite.OK);
-
- warn_if_fail (stmt.step () == Sqlite.DONE);
+ real_save();
}
- private void recover_book (ref Book book)
+ private void real_save ()
{
- Sqlite.Statement stmt;
- string query = @"
- SELECT process_id,
- page_hash,
- book_hash,
- book_revision,
- page_number,
- dpi,
- width,
- height,
- depth,
- n_channels,
- rowstride,
- color_profile,
- crop_x,
- crop_y,
- crop_width,
- crop_height,
- scan_direction,
- pixels,
- id
- FROM pages
- WHERE process_id = $PID
- AND book_revision = (
- SELECT MAX(book_revision) FROM pages WHERE process_id = $PID
- )
- ORDER BY page_number
- ";
-
- var result = database_connection.prepare_v2 (query, -1, out stmt);
- if (result != Sqlite.OK)
- warning ("Error %d while preparing statement", result);
-
- var first = true;
- while (Sqlite.ROW == stmt.step ())
+ debug ("Autosaving book information");
+
+ var file = new KeyFile ();
+ var page_names = "";
+ for (var i = 0; i < book.get_n_pages (); i++)
{
- debug ("Found a page that needs to be recovered");
- if (first)
- {
- book.clear ();
- first = false;
- }
- var dpi = stmt.column_int (5);
- var width = stmt.column_int (6);
- var height = stmt.column_int (7);
- var depth = stmt.column_int (8);
- var n_channels = stmt.column_int (9);
- var scan_direction = (ScanDirection)stmt.column_int (16);
-
- if (width <= 0 || height <= 0)
+ var page = book.get_page (i);
+
+ /* Skip empty pages */
+ if (!page.has_data ())
continue;
- debug (@"Restoring a page of size $(width) x $(height)");
- var new_page = book.append_page (width, height, dpi, scan_direction);
+ var page_name = "page-%d".printf (i);
+ if (page_names != "")
+ page_names += " ";
+ page_names += page_name;
- if (depth > 0 && n_channels > 0)
- {
- var info = new ScanPageInfo ();
- info.width = width;
- info.height = height;
- info.depth = depth;
- info.n_channels = n_channels;
- info.dpi = dpi;
- info.device = "";
- new_page.set_page_info (info);
- }
+ debug ("Autosaving page %s", page_name);
- new_page.set_color_profile (stmt.column_text (11));
- var crop_x = stmt.column_int (12);
- var crop_y = stmt.column_int (13);
- var crop_width = stmt.column_int (14);
- var crop_height = stmt.column_int (15);
- if (crop_width > 0 && crop_height > 0)
+ file.set_integer (page_name, "scan-width", page.get_scan_width ());
+ file.set_integer (page_name, "scan-height", page.get_scan_height ());
+ file.set_integer (page_name, "rowstride", page.get_rowstride ());
+ file.set_integer (page_name, "n-channels", page.get_n_channels ());
+ file.set_integer (page_name, "depth", page.get_depth ());
+ file.set_integer (page_name, "dpi", page.get_dpi ());
+ switch (page.get_scan_direction ())
{
- new_page.set_custom_crop (crop_width, crop_height);
- new_page.move_crop (crop_x, crop_y);
+ case ScanDirection.TOP_TO_BOTTOM:
+ file.set_value (page_name, "scan-direction", "TOP_TO_BOTTOM");
+ break;
+ case ScanDirection.LEFT_TO_RIGHT:
+ file.set_value (page_name, "scan-direction", "LEFT_TO_RIGHT");
+ break;
+ case ScanDirection.BOTTOM_TO_TOP:
+ file.set_value (page_name, "scan-direction", "BOTTOM_TO_TOP");
+ break;
+ case ScanDirection.RIGHT_TO_LEFT:
+ file.set_value (page_name, "scan-direction", "RIGHT_TO_LEFT");
+ break;
}
-
- uchar[] new_pixels = new uchar[stmt.column_bytes (17)];
- Memory.copy (new_pixels, stmt.column_blob (17), stmt.column_bytes (17));
- new_page.set_pixels (new_pixels);
-
- var id = stmt.column_int (18);
- debug ("Updating autosave to point to our new copy of the page");
- query = @"
- UPDATE pages
- SET page_hash=?1,
- book_hash=?2,
- book_revision=?3
- WHERE id = $id
- ";
-
- Sqlite.Statement stmt2;
- var result2 = database_connection.prepare_v2 (query, -1, out stmt2);
- if (result2 != Sqlite.OK)
- warning (@"Error $result2 while preparing query");
- stmt2.bind_int64 (1, direct_hash (new_page));
- stmt2.bind_int64 (2, direct_hash (book));
- stmt2.bind_int64 (3, cur_book_revision);
-
- result2 = stmt2.step ();
- if (result2 != Sqlite.DONE)
- warning ("Error %d while executing query", result);
+ file.set_value (page_name, "color-profile", page.get_color_profile () ?? "");
+ file.set_value (page_name, "pixels-filename", page_filenames.lookup (page) ?? "");
+ file.set_boolean (page_name, "has-crop", page.has_crop ());
+ int cx, cy, cw, ch;
+ page.get_crop (out cx, out cy, out cw, out ch);
+ file.set_value (page_name, "crop-name", page.get_named_crop () ?? "");
+ file.set_integer (page_name, "crop-x", cx);
+ file.set_integer (page_name, "crop-y", cy);
+ file.set_integer (page_name, "crop-width", cw);
+ file.set_integer (page_name, "crop-height", ch);
+ }
+ file.set_value ("simple-scan", "pages", page_names);
+
+ try
+ {
+ DirUtils.create_with_parents (AUTOSAVE_DIR, 0777);
+ FileUtils.set_contents (AUTOSAVE_PATH, file.to_data ());
}
+ catch (Error e)
+ {
+ warning ("Failed to write autosave: %s", e.message);
+ }
+ }
+
+ private void save_pixels (Page page)
+ {
+ var filename = "%u.pixels".printf (direct_hash (page));
+ var path = Path.build_filename (AUTOSAVE_DIR, filename);
+ page_filenames.insert (page, filename);
+
+ debug ("Autosaving page pixels to %s", path);
- if (first)
- debug ("No pages found to recover");
+ var file = File.new_for_path (path);
+ try
+ {
+ file.replace_contents (page.get_pixels (), null, false, FileCreateFlags.NONE, null);
+ }
+ catch (Error e)
+ {
+ warning ("Failed to autosave page contents: %s", e.message);
+ }
}
}
diff --git a/src/book.vala b/src/book.vala
index a4e6cdd..4427393 100644
--- a/src/book.vala
+++ b/src/book.vala
@@ -52,17 +52,14 @@ public class Book
set_needs_saving (true);
}
- public Page append_page (int width, int height, int dpi, ScanDirection scan_direction)
+ public void append_page (Page page)
{
- var page = new Page (width, height, dpi, scan_direction);
page.pixels_changed.connect (page_changed_cb);
page.crop_changed.connect (page_changed_cb);
pages.append (page);
page_added (page);
set_needs_saving (true);
-
- return page;
}
public void move_page (Page page, uint location)
diff --git a/src/page.vala b/src/page.vala
index f27258c..2153397 100644
--- a/src/page.vala
+++ b/src/page.vala
@@ -81,6 +81,41 @@ public class Page
this.scan_direction = scan_direction;
}
+ public Page.from_data (int width,
+ int n_rows,
+ int rowstride,
+ int n_channels,
+ int depth,
+ int dpi,
+ ScanDirection scan_direction,
+ string? color_profile,
+ uchar[]? pixels,
+ bool has_crop,
+ string? crop_name,
+ int crop_x,
+ int crop_y,
+ int crop_width,
+ int crop_height)
+ {
+ this.width = width;
+ this.n_rows = n_rows;
+ this.expected_rows = n_rows;
+ this.rowstride = rowstride;
+ this.n_channels = n_channels;
+ this.depth = depth;
+ this.dpi = dpi;
+ this.scan_direction = scan_direction;
+ this.color_profile = color_profile;
+ this.pixels = pixels;
+ has_data_ = pixels != null;
+ this.has_crop_ = has_crop;
+ this.crop_name = crop_name;
+ this.crop_x = crop_x;
+ this.crop_y = crop_y;
+ this.crop_width = crop_width;
+ this.crop_height = crop_height;
+ }
+
public void set_page_info (ScanPageInfo info)
{
expected_rows = info.height;
@@ -374,6 +409,11 @@ public class Page
if (!has_crop_)
return;
has_crop_ = false;
+ crop_name = null;
+ crop_x = 0;
+ crop_y = 0;
+ crop_width = 0;
+ crop_height = 0;
crop_changed ();
}
@@ -543,13 +583,6 @@ public class Page
return pixels;
}
- public void set_pixels (uchar[] new_pixels)
- {
- pixels = new_pixels;
- has_data_ = new_pixels != null;
- pixels_changed ();
- }
-
// FIXME: Copied from page-view, should be shared code
private uchar get_sample (uchar[] pixels, int offset, int x, int depth, int n_channels, int channel)
{
diff --git a/src/simple-scan.vala b/src/simple-scan.vala
index 14e70ea..689ab36 100644
--- a/src/simple-scan.vala
+++ b/src/simple-scan.vala
@@ -155,7 +155,8 @@ public class SimpleScan : Gtk.Application
}
}
- page = book.append_page (width, height, dpi, scan_direction);
+ page = new Page (width, height, dpi, scan_direction);
+ book.append_page (page);
if (do_crop)
{
if (named_crop != null)
@@ -275,11 +276,7 @@ public class SimpleScan : Gtk.Application
private void remove_empty_page ()
{
var page = book.get_page ((int) book.get_n_pages () - 1);
-
- /* Remove a failed page */
- if (page.has_data ())
- page.finish ();
- else
+ if (!page.has_data ())
book.delete_page (page);
}
diff --git a/src/ui.vala b/src/ui.vala
index 075c97d..f3e34d7 100644
--- a/src/ui.vala
+++ b/src/ui.vala
@@ -109,7 +109,18 @@ public class UserInterface
load ();
- autosave_manager = AutosaveManager.create (ref book);
+ autosave_manager = new AutosaveManager ();
+ autosave_manager.book = book;
+ autosave_manager.load ();
+
+ if (book.get_n_pages () == 0)
+ {
+ add_default_page ();
+ book.set_needs_saving (false);
+ }
+ else
+ book_view.select_page (book.get_page (0));
+ book.needs_saving_changed.connect (needs_saving_cb);
}
~UserInterface ()
@@ -318,10 +329,11 @@ public class UserInterface
private void add_default_page ()
{
- var page = book.append_page (default_page_width,
- default_page_height,
- default_page_dpi,
- default_page_scan_direction);
+ var page = new Page (default_page_width,
+ default_page_height,
+ default_page_dpi,
+ default_page_scan_direction);
+ book.append_page (page);
book_view.select_page (page);
}
@@ -1183,8 +1195,7 @@ public class UserInterface
window.destroy ();
- if (autosave_manager != null)
- autosave_manager.cleanup ();
+ autosave_manager.cleanup ();
return true;
}
@@ -1519,11 +1530,6 @@ public class UserInterface
window.maximize ();
}
- if (book.get_n_pages () == 0)
- add_default_page ();
- book.set_needs_saving (false);
- book.needs_saving_changed.connect (needs_saving_cb);
-
progress_dialog = new ProgressBarDialog (window, _("Saving document..."));
book.saving.connect (book_saving_cb);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]