deja-dup-team team mailing list archive
-
deja-dup-team team
-
Mailing list archive
-
Message #00084
[Merge] lp:~urbans/deja-dup/deja-dup.nautilus into lp:deja-dup
Urban Skudnik has proposed merging lp:~urbans/deja-dup/deja-dup.nautilus into lp:deja-dup.
Requested reviews:
Michael Terry (mterry): proposals bugreports testing ideas
This branch addresses 'restore missing files' bug [https://bugs.launchpad.net/deja-dup/+bug/540590] by adding assistant for scanning through duplicity's archive and searching for files that were deleted.
ATM Deja Dup has to be manually ran with command --dir-history [directory] to test the new assistant.
--
https://code.launchpad.net/~urbans/deja-dup/deja-dup.nautilus/+merge/29723
Your team Déjà Dup Maintainers is subscribed to branch lp:deja-dup.
=== modified file 'common/Backend.vala'
--- common/Backend.vala 2010-06-05 01:30:41 +0000
+++ common/Backend.vala 2010-07-12 16:58:42 +0000
@@ -37,6 +37,7 @@
public virtual bool is_ready(out string when) {when = null; return true;} // must be callable when nothing is mounted, nothing is prepared
public virtual async void get_envp() throws Error {
+ stdout.printf("get_envp\n");
envp_ready(true, new List<string>());
}
=== modified file 'common/BackendFile.vala'
--- common/BackendFile.vala 2010-06-01 11:17:19 +0000
+++ common/BackendFile.vala 2010-07-12 16:58:42 +0000
@@ -261,6 +261,7 @@
public override async void get_envp() throws Error
{
try {
+ stdout.printf("get_envp mount file\n");
yield mount_file();
}
catch (Error e) {
=== modified file 'common/Duplicity.vala'
--- common/Duplicity.vala 2010-06-12 22:06:51 +0000
+++ common/Duplicity.vala 2010-07-12 16:58:42 +0000
@@ -24,12 +24,23 @@
public class Duplicity : Object
{
+ /*
+ * Vala implementation of various methods for accessing duplicity
+ *
+ * Vala implementation of various methods for accessing duplicity from
+ * vala withot the need of manually running duplicity command.
+ */
+
public signal void done(bool success, bool cancelled);
public signal void raise_error(string errstr, string? detail);
public signal void action_desc_changed(string action);
public signal void action_file_changed(File file, bool actual);
public signal void progress(double percent);
+ /*
+ * Signal emmited when collection dates are retrieved from duplicity
+ */
public signal void collection_dates(List<string>? dates);
+ public signal void listed_current_files(string date, string file);
public signal void question(string title, string msg);
public signal void secondary_desc_changed(string msg);
@@ -208,6 +219,7 @@
}
public void cancel() {
+ stdout.printf("\n\nCanceling duplicity\n\n");
var prev_mode = mode;
mode = Operation.Mode.INVALID;
@@ -694,6 +706,9 @@
void handle_message(DuplicityInstance inst, string[] control_line,
List<string>? data_lines, string user_text)
{
+ /*
+ * Based on duplicity's output handle message as either process data as error, info or warning
+ */
if (control_line.length == 0)
return;
@@ -884,6 +899,9 @@
protected virtual void process_info(string[] firstline, List<string>? data,
string text)
{
+ /*
+ * Pass message to appropriate function considering the type of output
+ */
if (firstline.length > 1) {
switch (firstline[1].to_int()) {
case INFO_DIFF_FILE_NEW:
@@ -909,13 +927,13 @@
set_status(_("Uploadingâ¦"));
break;
case INFO_FILE_STAT:
- process_file_stat(firstline[2], firstline[3]);
+ process_file_stat(firstline[2], firstline[3], data, text);
break;
}
}
}
- void process_file_stat(string date, string file)
+ void process_file_stat(string date, string file, List<string> data, string text)
{
if (mode != Operation.Mode.LIST)
return;
@@ -929,6 +947,7 @@
!gfile.has_prefix(slash_home))
has_non_home_contents = true;
}
+ listed_current_files(date, file);
}
void process_diff_file(string file) {
@@ -981,11 +1000,15 @@
void process_collection_status(List<string>? lines)
{
- // Collection status is a bunch of lines, some of which are indented,
- // which contain information about specific chains. We gather this all up
- // and report back to caller via a signal.
- // We're really only interested in the list of entries in the complete chain,
- // though.
+ stdout.printf("duplicity.process_collection_status\n");
+ /*
+ * Collect output of collection status and return list of dates as strings via a signal
+ *
+ * Duplicity returns collection status as a bunch of lines, some of which are
+ * indented which contain information about specific chains. We gather
+ * this all up and report back to caller via a signal.
+ * We're really only interested in the list of entries in the complete chain.
+ */
var timeval = TimeVal();
var dates = new List<string>();
@@ -1022,7 +1045,7 @@
collection_info = new List<DateInfo?>();
foreach (DateInfo s in infos)
collection_info.append(s); // we want to keep our own copy too
-
+
collection_dates(dates);
}
@@ -1083,6 +1106,7 @@
void disconnect_inst()
{
+ /* Disconnect signals and cancel call to duplicity instance */
if (inst != null) {
inst.done.disconnect(handle_done);
inst.message.disconnect(handle_message);
@@ -1095,13 +1119,21 @@
List<string>? envp_extra = null,
List<string>? argv_entire = null,
File? custom_local = null)
- {
+ {
+ /*
+ * For passed arguments start a new duplicity instance, set duplicity in the right mode and execute command
+ */
+ /* Disconnect instance */
disconnect_inst();
-
+
+ /* Start new duplicity instance */
inst = new DuplicityInstance();
inst.done.connect(handle_done);
+
+ /* As duplicity's data is returned via a signal, handle_message begins post-raw stream processing*/
inst.message.connect(handle_message);
-
+
+ /* Set arguments for call to duplicity */
weak List<string> master_argv = argv_entire == null ? saved_argv : argv_entire;
weak File local_arg = custom_local == null ? local : custom_local;
@@ -1109,7 +1141,8 @@
foreach (string s in master_argv) argv.append(s);
foreach (string s in argv_extra) argv.append(s);
foreach (string s in this.backend_argv) argv.append(s);
-
+
+ /* Set duplicity into right mode */
if (argv_entire == null) {
// add operation, local, and remote args
switch (mode) {
@@ -1136,11 +1169,13 @@
break;
}
}
-
+
+ /* Set enviormental parameters */
var envp = new List<string>();
foreach (string s in saved_envp) envp.append(s);
foreach (string s in envp_extra) envp.append(s);
-
+
+ /* Start duplicity instance */
try {
inst.start(argv, envp, needs_root);
}
=== modified file 'common/DuplicityInstance.vala'
--- common/DuplicityInstance.vala 2010-06-12 22:13:57 +0000
+++ common/DuplicityInstance.vala 2010-07-12 16:58:42 +0000
@@ -276,6 +276,12 @@
async void read_log_lines()
{
+ /*
+ * Process data from stream that is returned by read_log
+ *
+ * As reader returns lines that are outputed by duplicity, read_log_lines makes sure
+ * that data is processed at right speed and passes that data along the chain of functions.
+ */
List<string> stanza = new List<string>();
while (reader != null) {
try {
@@ -324,6 +330,11 @@
async void read_log()
{
+ /*
+ * Asynchronous reading of duplicity's log via stream
+ *
+ * Stream initiated either from log file or pipe
+ */
try {
InputStream stream;
@@ -509,6 +520,10 @@
void process_stanza(List<string> stanza)
{
+ /*
+ * Split the line/stanza that was echoed by stream and pass it forward in a
+ * more structured way via a signal.
+ */
string[] control_line;
split_line(stanza.data, out control_line);
@@ -522,6 +537,9 @@
List<string> grab_stanza_data(List<string> stanza)
{
+ /*
+ * Return only data from stanza that was returned by stream
+ */
var list = new List<string>();
stanza = stanza.next; // skip first control line
foreach (string line in stanza) {
=== modified file 'common/Makefile.am'
--- common/Makefile.am 2010-03-25 21:40:33 +0000
+++ common/Makefile.am 2010-07-12 16:58:42 +0000
@@ -47,6 +47,7 @@
OperationBackup.vala \
OperationRestore.vala \
OperationStatus.vala \
+ OperationFiles.vala \
RecursiveDelete.vala \
RecursiveMove.vala \
RecursiveOp.vala
=== modified file 'common/Operation.vala'
--- common/Operation.vala 2010-02-05 13:44:45 +0000
+++ common/Operation.vala 2010-07-12 16:58:42 +0000
@@ -23,6 +23,15 @@
public abstract class Operation : Object
{
+ /**
+ * Abstract class that abstracts low level operations of duplicity
+ * with specific classes for specific operations
+ *
+ * Abstract class that defines methods and properties that have to be defined
+ * by classes that abstract operations from duplicity. It is generally unnecessary
+ * but it is provided to provide easier development and an abstraction layer
+ * in case Deja Dup project ever replaces its backend.
+ */
public signal void done(bool success, bool cancelled);
public signal void raise_error(string errstr, string? detail);
public signal void action_desc_changed(string action);
@@ -35,13 +44,21 @@
public uint xid {get; construct;}
public bool needs_password {get; private set;}
public Backend backend {get; private set;}
-
+
public enum Mode {
+ /*
+ * Mode of operation of instance
+ *
+ * Every instance of class that inherit its methods and properties from
+ * this class must define in which mode it operates. Based on this Duplicity
+ * attaches appropriate argument.
+ */
INVALID,
BACKUP,
RESTORE,
STATUS,
LIST,
+ FILEHISTORY
}
public Mode mode {get; construct; default = Mode.INVALID;}
@@ -81,7 +98,6 @@
construct
{
dup = new Duplicity(mode);
-
try {
backend = Backend.get_default();
}
@@ -92,8 +108,8 @@
public virtual void start() throws Error
{
- action_desc_changed(_("Preparingâ¦"));
-
+ stdout.printf("\nbase Operation.start()\n\n");
+ action_desc_changed(_("Preparingâ¦"));
if (backend == null) {
done(false, false);
return;
@@ -105,16 +121,19 @@
done(false, false);
return;
}
+ stdout.printf("setting session, encryption needed?\n");
set_session_inhibited(true);
-
// Get encryption passphrase if needed
var client = get_gconf_client();
if (client.get_bool(ENCRYPT_KEY) && passphrase == null) {
+ stdout.printf("nimam passa, lets get it!\n");
needs_password = true;
passphrase_required(); // will call continue_with_passphrase when ready
}
- else
+ else {
+ stdout.printf("nadaljujemo z continue_with_passphrase");
continue_with_passphrase(passphrase);
+ }
}
public void cancel()
@@ -129,6 +148,9 @@
protected virtual void connect_to_dup()
{
+ /*
+ * Connect Deja Dup to signals
+ */
dup.done.connect(operation_finished);
dup.raise_error.connect((d, s, detail) => {raise_error(s, detail);});
dup.action_desc_changed.connect((d, s) => {action_desc_changed(s);});
@@ -141,6 +163,10 @@
public void continue_with_passphrase(string? passphrase)
{
+ /*
+ * Continues with operation after passphrase has been acquired.
+ */
+ stdout.printf("\ncontinue_with_passphrase\n");
needs_password = false;
this.passphrase = passphrase;
try {
@@ -153,6 +179,12 @@
}
void continue_with_envp(DejaDup.Backend b, bool success, List<string>? envp, string? error) {
+ /*
+ * Starts Duplicity backup with added enviroment variables
+ *
+ * Start Duplicity backup process with costum values for enviroment variables.
+ */
+ stdout.printf("continue_with_envp\n");
if (!success) {
if (error != null)
raise_error(error, null);
@@ -200,6 +232,12 @@
protected virtual List<string>? make_argv() throws Error
{
+ /**
+ * Abstract method that prepares arguments that will be sent to duplicity
+ *
+ * Abstract method that will prepare arguments that will be sent to duplicity
+ * and return a list of those arguments.
+ */
return null;
}
=== modified file 'common/OperationBackup.vala'
--- common/OperationBackup.vala 2010-05-29 17:44:34 +0000
+++ common/OperationBackup.vala 2010-07-12 16:58:42 +0000
@@ -34,6 +34,7 @@
protected override void operation_finished(Duplicity dup, bool success, bool cancelled)
{
+ /* If successfully completed, update time of last backup and run base operation_finished */
if (success) {
try {DejaDup.update_last_run_timestamp();}
catch (Error e) {warning("%s\n", e.message);}
=== added file 'common/OperationFiles.vala'
--- common/OperationFiles.vala 1970-01-01 00:00:00 +0000
+++ common/OperationFiles.vala 2010-07-12 16:58:42 +0000
@@ -0,0 +1,46 @@
+using GLib;
+
+namespace DejaDup {
+
+public class OperationFiles : Operation {
+ public signal void listed_current_files(string date, string file);
+ public Time time {public get; public set;}
+ public File source {get; construct;}
+
+ public OperationFiles(uint xid = 0,
+ Time time_in,
+ File source_in) {
+ Object(xid: xid, mode: Mode.LIST, source: source_in);
+ this.time = time_in;
+ }
+
+ protected override void connect_to_dup()
+ {
+ dup.listed_current_files.connect((d, date, file) => {listed_current_files(date, file);});
+ base.connect_to_dup();
+ }
+
+ protected override List<string>? make_argv() throws Error
+ {
+ List<string> argv = new List<string>();
+ //if (time != null) - no need, we don't allow null anyway
+ argv.append("--time=%s".printf(time.format("%s")));
+
+ dup.local = source;
+
+ return argv;
+ }
+
+ public override void start() throws Error
+ {
+ //stdout.printf("operation files.start() with epoch time %s\n", time.format("%c"));
+ stdout.printf("operation files start()\n");
+ base.start();
+ }
+
+ protected override void operation_finished(Duplicity dup, bool success, bool cancelled)
+ {
+ base.operation_finished(dup, success, cancelled);
+ }
+}
+}
\ No newline at end of file
=== modified file 'common/OperationRestore.vala'
--- common/OperationRestore.vala 2010-01-18 23:31:22 +0000
+++ common/OperationRestore.vala 2010-07-12 16:58:42 +0000
@@ -25,6 +25,15 @@
{
public string dest {get; construct;} // Directory user wants to put files in
public string time {get; construct;} // Date user wants to restore to
+
+ /*public Queue<string> restore_queue {
+ get {
+ return this.restore_queue;
+ }
+ set construct {
+ this.restore_queue = value.copy();
+ }}*/
+
string source; // Directory duplicity puts files in
List<string> errors;
private List<File> _restore_files;
@@ -32,7 +41,7 @@
get {
return this._restore_files;
}
- construct {
+ set construct {
foreach (File f in this._restore_files)
f.unref();
this._restore_files = value.copy();
@@ -40,7 +49,9 @@
f.ref();
}
}
-
+
+ //Queue<string>? queue_in = null
+ //, restore_queue: queue_in.copy()
public OperationRestore(string dest_in,
string? time_in = null,
List<File>? files_in = null,
@@ -51,6 +62,7 @@
public override void start() throws Error
{
+ //stdout.printf("mode: %s", this.mode);
action_desc_changed(_("Restoring filesâ¦"));
dup.restore_files = restore_files;
base.start();
@@ -70,12 +82,30 @@
argv.append("--restore-time=%s".printf(time));
dup.local = File.new_for_path(source);
-
return argv;
}
protected override void operation_finished(Duplicity dup, bool success, bool cancelled)
{
+ /*if (this.restore_queue.get_length() > 0) {
+ var restore_file = this.restore_queue.pop_head();
+
+ //var _restore_files = new GLib.List<File>();
+ //restore_files.append(File.new_for_path(restore_file.split(" ")[0]));
+ //this.restore_files.remove(this.restore_files.nth_data(0));
+ var _restore_files = new GLib.List<File>();
+ _restore_files.append(File.new_for_path(restore_file.split(" ")[0]));
+
+ //this.restore_files = _restore_files.copy();
+ dup.restore_files = _restore_files;
+
+ //this.restore_files.append(File.new_for_path(restore_file.split(" ")[0]));
+ this.time = restore_file.split(" ")[1];
+ base.start();
+ return;
+ }*/
+
+
if (success) {
fixup_home_dir();
if (!mv_source_to_dest())
=== modified file 'common/OperationStatus.vala'
--- common/OperationStatus.vala 2009-11-12 21:33:25 +0000
+++ common/OperationStatus.vala 2010-07-12 16:58:42 +0000
@@ -32,8 +32,16 @@
protected override void connect_to_dup()
{
dup.collection_dates.connect((d, dates) => {collection_dates(dates);});
+ dup.collection_dates.connect((d, dates) => {
+ stdout.printf("collection_dates was called\n");
+ });
base.connect_to_dup();
}
+
+ protected override void operation_finished(Duplicity dup, bool success, bool cancelled) {
+ stdout.printf("Operation status.operation_finished\n");
+ base.operation_finished(dup, success, cancelled);
+ }
}
} // end namespace
=== modified file 'configure.ac'
--- configure.ac 2010-06-14 01:06:33 +0000
+++ configure.ac 2010-07-12 16:58:42 +0000
@@ -74,7 +74,9 @@
gio-2.0 >= $GIO_REQ_VER
gconf-2.0
dbus-glib-1
- gnome-keyring-1)
+ gnome-keyring-1
+ gmodule-2.0 >= $GLIB_REQ_VER
+ gee-1.0)
AC_SUBST(DUP_CFLAGS)
AC_SUBST(DUP_LIBS)
@@ -82,7 +84,8 @@
gtk+-2.0 >= $GTK_REQ_VER
gio-2.0 >= $GIO_REQ_VER
gconf-2.0
- unique-1.0)
+ unique-1.0
+ gmodule-2.0 >= $GLIB_REQ_VER)
AC_SUBST(PREF_CFLAGS)
AC_SUBST(PREF_LIBS)
@@ -91,13 +94,15 @@
gio-unix-2.0 >= $GIO_REQ_VER
gconf-2.0
dbus-glib-1
- gnome-keyring-1)
+ gnome-keyring-1
+ gmodule-2.0 >= $GLIB_REQ_VER)
AC_SUBST(COMMON_CFLAGS)
AC_SUBST(COMMON_LIBS)
PKG_CHECK_MODULES(WIDGETS,
gtk+-2.0 >= $GTK_REQ_VER
- gconf-2.0)
+ gconf-2.0
+ gmodule-2.0 >= $GLIB_REQ_VER)
AC_SUBST(WIDGETS_CFLAGS)
AC_SUBST(WIDGETS_LIBS)
=== modified file 'deja-dup/Assistant.vala'
--- deja-dup/Assistant.vala 2010-06-14 01:06:33 +0000
+++ deja-dup/Assistant.vala 2010-07-12 16:58:42 +0000
@@ -61,7 +61,7 @@
bool interrupted_from_hidden = false;
weak List<PageInfo> interrupted;
- weak List<PageInfo> current;
+ public weak List<PageInfo> current;
List<PageInfo> infos;
static const int APPLY = 1;
=== added file 'deja-dup/AssistantDirectoryHistory.vala'
--- deja-dup/AssistantDirectoryHistory.vala 1970-01-01 00:00:00 +0000
+++ deja-dup/AssistantDirectoryHistory.vala 2010-07-12 16:58:42 +0000
@@ -0,0 +1,809 @@
+using GLib;
+using Gee;
+
+public class DeletedFile {
+ /*
+ * Class whose instances hold information and track status of deleted file.
+ *
+ * After providing full file path and time of when was file last seen, instances
+ * can access pretty file name and mark file for restore.
+ *
+ * @param //string// ''name'' Full path name of file
+ * @param //Time// ''deleted'' Information when was file deleted
+ */
+ public string name {get; set;}
+ public Time deleted {get; set;}
+ public bool restore {get; set; default = false;}
+
+ public DeletedFile(string name, Time deleted) {
+ this.name = name;
+ this.deleted = deleted;
+ }
+
+ public string filename() {
+ var splited_fn = this.name.split("/");
+ stdout.printf("filename: %s\n", splited_fn[splited_fn.length-1]);
+ return splited_fn[splited_fn.length-1];
+ }
+
+ public string queue_format() {
+ var file = this.name;
+ var time = this.deleted.format("%s");
+ return @"$file $time";
+ }
+}
+
+public class AssistantDirectoryHistory : AssistantOperation {
+ /*
+ * Assistant for showing deleted files
+ *
+ * Assistant for showing deleted files. Execution flow goes as follows:
+ *
+ * 1. AssistantDirectoryHistory is called with //File// ''list_dir'' directory
+ * 2. //void// do_prepare prepares listfiles_page and runs do_query_collection_dates that initializes query operation for collections dates. Results of query operation are returned to handle_collection_dates in one batch.
+ * 3. handle_collection_dates fills the //PriorityQueue// ''backups_queue'' with //Time// values of backup dates, scans provided //File// ''list_dir'' with files that are currently located in directory and runs do_query_files_at_date
+ * 4. do_query_files_at_date begins query operation for list-current-files at specific times and returns the results to handle_listed_files.
+ * 5. handle_listed_files appends files to the list of deleted files with appropriate controls.
+ * 6. When OperationFiles finishes, query_files_finished releases variables and, if required, calls do_query_files_at_date.
+ * 7. After user selects files that he wishes to restore, he moves to confirmation page and starts the restore operation
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @param //File// ''list_dir'' Directory whose deleted files will be shown.
+ */
+ private File list_directory;
+
+ private bool backups_queue_filled = false;
+ private static int compare_time(Time a, Time b) {
+ /*
+ * Compare function for backups queue
+ *
+ * Default comparing for queue goes from oldest to newest so we use our own
+ * compare function to reverse that ordering and proceed from newest to oldest
+ * since it is far more likely that user deleted file in recent history.
+ *
+ * @param //Time// ''a'', ''b'' Time objects
+ */
+ var a_epoch = a.format("%s").to_int();
+ var b_epoch = b.format("%s").to_int();
+ if (a_epoch < b_epoch)
+ return 1;
+ else if (a_epoch == b_epoch)
+ return 0;
+ else
+ return -1;
+ }
+
+ /*
+ If user moves forward while OperationFiles is runing, code cleanup stops the current operation
+ and stops recursive loop without the need to clear backups_queue. If user decides to go back,
+ OperationFiles will continue with NEXT item in backups_queue.
+ */
+ private bool scan_queue = true;
+ private bool cancel_assistant = false;
+ private PriorityQueue<Time?> backups_queue = new PriorityQueue<Time?>((CompareFunc) compare_time);
+ private PriorityQueue<DeletedFile?> restore_queue = new PriorityQueue<DeletedFile?>();
+
+ private ArrayList<string> allfiles_prev = new ArrayList<string>();
+ private ArrayList<DeletedFile?> file_status = new ArrayList<DeletedFile?>();
+
+ DejaDup.OperationFiles query_op_files;
+ DejaDup.OperationStatus query_op_collection_dates;
+ DejaDup.Operation.State op_state;
+
+ Gtk.Widget page;
+ Gtk.Widget listfiles_page;
+ Gtk.TreeIter deleted_iter;
+ Gtk.TreeIter restoreiter;
+ Gtk.ListStore listmodel;
+ Gtk.ListStore restoremodel;
+
+ /* List files page */
+ Gtk.Label current_scan_date = new Gtk.Label(_("Starting..."));
+ Gtk.Spinner spinner = new Gtk.Spinner();
+
+ /* Confirmation page related widgets */
+ Gtk.Label label;
+ Gtk.Table restore_files_table;
+ /*
+ Gtk.Table in Glade needs to be set at least to 1 at start. When we update
+ our table we start from 0 so that we always resize table to correct size without the need for special case.
+ */
+ int restore_files_table_rows = 0;
+
+ public AssistantDirectoryHistory(File list_dir) {
+ list_directory = list_dir;
+ }
+
+ private ArrayList<string>? files_in_directory() {
+ /*
+ * Function lists all files that are currently located in the directory.
+ *
+ * Function scans directory that was provided to it through
+ * command line children and returns an array of strings populated
+ * by file names of folder and files.
+ */
+ //File directory = File.new_for_path(dirlocation);
+ var file_list = new ArrayList<string> ();
+
+ try {
+ var enumerator = this.list_directory.enumerate_children(FILE_ATTRIBUTE_STANDARD_NAME, 0, null);
+ FileInfo fileinfo;
+ while ((fileinfo = enumerator.next_file(null)) != null){
+ file_list.add(this.list_directory.get_path() + "/" + fileinfo.get_name());
+ }
+ return file_list;
+ } catch(Error err){
+ warning("Error: %s\n", err.message);
+ return null;
+ }
+ }
+
+ /*[CCode (instance_pos = -1)]
+ public void on_restoretoggle(Gtk.CellRendererToggle checkbox, int path) {
+ checkbox.set_active(true);
+ }*/
+
+ Gtk.Widget? make_listfiles_page() {
+ /*
+ * Build list files (introduction) page which shows deleted files.
+ *
+ * Build list files page from a Glade template and attach various dynamic
+ * components to it. Deleted files are dynamically added on-the-fly by
+ * applicable functions.
+ */
+
+ // Hack; we need ''page'' because window needs to be reparented to be shown.
+ var page = new Gtk.Table(1, 1, false);
+ page.name = "listfiles_page";
+ var builder = new Gtk.Builder();
+ try {
+ builder.add_from_file("interface/listfiles-crt-out.ui");
+ builder.connect_signals(this);
+
+ var window = builder.get_object("viewport") as Gtk.Widget;
+ var filelistwindow = builder.get_object("filelistwindow") as Gtk.ScrolledWindow;
+ var status_table = builder.get_object("backup_table") as Gtk.Table;
+ var progress_table = builder.get_object("progress_table") as Gtk.Table;
+
+ /* Add backup and scan information */
+ /* Backup source */
+ Gtk.Widget w = new DejaDup.ConfigLabelLocation();
+ status_table.attach(w, 1, 2, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ /* Spinner */
+ progress_table.attach(this.spinner, 1, 2, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ /* Current date of scan */
+ progress_table.attach(this.current_scan_date, 2, 3, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ this.listmodel = new Gtk.ListStore (3, typeof (bool), typeof (string), typeof (string));
+ var treeview = new Gtk.TreeView.with_model (this.listmodel);
+ //treeview.set_model(this.listmodel);
+
+ /* Toggle code for checkbox */
+ var toggle = new Gtk.CellRendererToggle();
+ toggle.toggled.connect ((toggle, path) => {
+ stdout.printf("%s\n",this.file_status[path.to_int()].name);
+
+ if (!this.file_status[path.to_int()].restore)
+ this.file_status[path.to_int()].restore = true;
+ else
+ this.file_status[path.to_int()].restore = false;
+
+ var tree_path = new Gtk.TreePath.from_string (path);
+ this.listmodel.get_iter (out this.deleted_iter, tree_path);
+ this.listmodel.set(this.deleted_iter, 0, !toggle.active);
+ });
+
+ treeview.insert_column_with_attributes(-1, "", toggle, "active", 0);
+ treeview.insert_column_with_attributes(-1, "File name", new Gtk.CellRendererText(), "text", 1);
+ treeview.insert_column_with_attributes(-1, "Deleted", new Gtk.CellRendererText(), "text", 2);
+
+ treeview.set_headers_visible (true);
+
+ filelistwindow.add_with_viewport(treeview);
+
+ /*
+ we have do_prepare, stupid!
+ this.forward.connect((assist) => {
+ uint pathlenght;
+ string path;
+ string pathr;
+ uint pathlenght2;
+ string path2;
+ string pathr2;
+ assist.current.data.page.path(out pathlenght, out path, out pathr);
+ listfiles_page.path(out pathlenght2, out path2, out pathr2);
+ //stdout.printf("\nFORWARD! name: %s\n %s \n %s\n\n", assist.current.data.page.name, path, path2);
+ if (assist.current.data.page.name == "confirm_page") {
+ stdout.printf("\n\n\nlistfilespage mamo! do magic misko!\n");
+ foreach(var delfile in this.file_status) {
+ if (delfile.restore)
+ {
+ stdout.printf("OFFERING: %s\n\n", delfile.name);
+ //this.restore_queue.push_tail(delfile.queue_format());
+ this.restore_queue.offer(delfile);
+
+ this.restoremodel.append (out this.restoreiter);
+ this.restoremodel.set(this.restoreiter, 0, delfile.name);
+ }
+ }
+ this.backups_queue.clear();
+ }
+ });*/
+ window.reparent(page);
+ return page;
+ } catch (Error err) {
+ warning("Error: %s", err.message);
+ return null;
+ }
+ }
+
+ void add_listfiles_page() {
+ var page = make_listfiles_page();
+ append_page(page);
+ set_page_title(page, _("Deleted Files"));
+ listfiles_page = page;
+ //selectfiles_page = page;
+ }
+
+ //private void deleted_files(Gtk.ListStore listmodel) {
+ // stdout.printf("\n\ndeleted files\n\n");
+
+ // First, the simples - files from directory
+ //var fid = files_in_directory();
+
+ // Second - get collection dates
+ /*if (backup_dates == null) {
+ stdout.printf("backup_dates == null");
+ if (!collection_dates_query_in_progress) {
+ stdout.printf("collection_dates_query_in_progress = false");
+ do_query_collection_dates();
+ }*/
+
+ /*foreach(string date in this.backup_dates) {
+ stdout.printf("%s\n", date);
+ } */
+ // stdout.printf("gremo naprej, timeout ker ni podatkov");
+ /*Timeout.add_seconds(1, () => {
+ deleted_files(listmodel, true);
+ return false;
+ });*/
+ /*return;
+ } else {
+ stdout.printf("we have backup_dates!");
+ foreach(string date in backup_dates){
+ stdout.printf("datum: %s\n", date);
+ }
+ }*/
+
+ // Third
+ //}
+
+ protected override void add_setup_pages() {
+ add_listfiles_page();
+ //add_restorefiles_page();
+ }
+
+ /*void add_restorefiles_page() {
+ var page = make_restorefiles_page();
+ append_page(page);
+ set_page_title(page, _("About to restore"));
+ //selectfiles_page = page;
+ }*/
+
+ protected override void do_prepare(Assistant assist, Gtk.Widget page) {
+ stdout.printf("do_prepare");
+ base.do_prepare(assist, page);
+
+ if (page == listfiles_page) {
+ stdout.printf("listfiles_page");
+
+ if (!scan_queue) {
+ do_query_files_at_date();
+ scan_queue = true;
+ }
+ else {
+ if (op != null && op.needs_password){
+ provide_password();
+ }
+ else if (op == null) {
+ do_query_collection_dates();
+ }
+ }
+ }
+
+ else if (page == confirm_page) {
+ stdout.printf("\nconfirm page\n");
+ scan_queue = false;
+
+ foreach(var delfile in this.file_status) {
+ if (delfile.restore)
+ {
+ stdout.printf("OFFERING: %s\n\n", delfile.name);
+ //stdout.printf("number of relevent rows: %u", this.restore_files_table.nrows);
+ //this.restore_queue.push_tail(delfile.queue_format());
+ this.restore_queue.offer(delfile);
+ stdout.printf("rowz: %u\n", this.restore_files_table.nrows);
+ //stdout.printf(restore_files_table);
+ this.restore_files_table_rows++;
+ this.restore_files_table.resize(this.restore_files_table_rows, 1);
+
+ label = new Gtk.Label(delfile.filename());
+ label.set("xalign", 0.0f);
+ label.show();
+ this.restore_files_table.attach(label, 0, 1, this.restore_files_table_rows-1, this.restore_files_table_rows, Gtk.AttachOptions.FILL, 0, 0, 0);
+ /*this.restoremodel.append (out this.restoreiter);
+ this.restoremodel.set(this.restoreiter, 0, delfile.name);*/
+ }
+ }
+ //this.backups_queue.clear();
+ }
+ else if (page == summary_page) {
+ if (error_occurred)
+ set_page_title(page, _("Restore Failed"));
+ else {
+ set_page_title(page, _("Restore Finished"));
+
+ /* Count the number of files that had to be restored */
+ var numdels = 0; // Number of deleted files
+ foreach(var delfile in this.file_status) {
+ if (delfile.restore) {
+ numdels++;
+ }
+ }
+
+ summary_label.label = ngettext("Your file was successfully restored.",
+ "Your files were successfully restored.",
+ numdels);
+ }
+ }
+ }
+
+ protected void handle_listed_files(DejaDup.OperationFiles op, string date, string file) {
+ /*
+ * Handler for each line returned by duplicity individually
+ *
+ * Duplicity returns each file as a separate line that has to be handled individually.
+ * We therefore check if file in path of directory, if it exists and whether or not it has not been seen before
+ * and attach it to our TreeView model if all conditions are met.
+ *
+ * @param //DejaDup.OperationFiles// ''op'' Operation that is currently running
+ * @param //string// ''date'' Time of last change of file
+ * @param //string// ''file'' Full path of file
+ */
+ string filestr = @"/$file";
+ if (this.list_directory.get_path() in filestr && this.list_directory.get_path() != filestr) {
+ var fileobj = File.new_for_path(filestr);
+
+ if (!fileobj.query_exists(null) && !this.allfiles_prev.contains(filestr)) {
+ if(fileobj.has_parent(this.list_directory)) {
+ var fs = new DeletedFile(filestr, op.time);
+
+ this.file_status.add(fs);
+ //var fileinfoobj = fileobj.query_info("standard::*", FileQueryInfoFlags.NONE, null);
+ //stdout.printf("basename: %s", fileinfoobj.get_display_name());
+
+ this.listmodel.append (out this.deleted_iter);
+ this.listmodel.set (this.deleted_iter, 0, false, 1, fs.filename(), 2, op.time.format("%c"));
+
+ this.allfiles_prev.add(filestr);
+ }
+ /*if (file.query_file_type(0, null) == FileType.DIRECTORY) {
+
+ }
+ else if {
+
+ }*/
+ }
+
+ /*if (!this.allfiles_prev.contains(filestr)) {
+ //[this.list_directory.get_path().length:filestr.length]]
+
+ var fs = new DeletedFile(filestr, op.time);
+ this.file_status.add(fs);
+
+ this.listmodel.append (out this.deleted_iter);
+ this.listmodel.set (this.deleted_iter, 0, false, 1, fs.filename(), 2, op.time.format("%c"));
+
+ this.allfiles_prev.add(filestr);
+ }*/
+ }
+
+ /*if (this.files_at_epoch.has_key(op.time)) {
+ //stdout.printf("%d", this.files_at_epoch.get(op.time));
+ var flist = this.files_at_epoch.get(op.time);
+ flist.add(datefilestr);
+ //this.files_at_epoch.set(op.time, flist);
+ } else {
+ var nlist = new ArrayList<string>();
+ nlist.add(datefilestr);
+ this.files_at_epoch.set(op.time, nlist);
+ }*/
+ }
+
+ protected void handle_collection_dates(DejaDup.OperationStatus op, GLib.List<string>? dates){
+ /*
+ * Handle collection dates
+ *
+ * Collection dates are returned as a single list of strings file timestamps of backup.
+ * Timestamps are in ISO 8601 format and are first read and converted to //Time// objects and then
+ * added to backups_queue.
+ *
+ * @param //DejaDup.OperationStatus// ''op'' Operation currently being run
+ * @param //GLib.List<string>?// ''dates'' ISO 8601 dates of backups.
+ */
+ stdout.printf("\nhandle collection_dates\n");
+ TimeVal tv = TimeVal();
+
+ if (!this.backups_queue_filled) {
+ //foreach(string date in dates){
+ for(var i=dates.length()-1;i > 0;i--){
+ if (tv.from_iso8601(dates.nth(i).data)) {
+ Time t = Time.local(tv.tv_sec);
+ stdout.printf("time of backup: d: %s, tse: %s, cd: %s\n", dates.nth(i).data, t.format("%s"), t.format("%c"));
+ this.backups_queue.offer(t);
+ }
+ }
+ }
+
+ this.allfiles_prev = files_in_directory();
+ this.backups_queue_filled = true;
+
+ this.spinner.start();
+ do_query_files_at_date();
+ }
+
+ protected void do_query_collection_dates() {
+ /*
+ * Initialize query operation for collection dates.
+ *
+ * Initializes query operation and links appropriate signals for when operation
+ * finishes and when it receives duplicity's output.
+ */
+ stdout.printf("\ndo_query_collection_dates\n");
+ realize();
+ var xid = Gdk.x11_drawable_get_xid(this.window);
+
+ query_op_collection_dates = new DejaDup.OperationStatus((uint)xid);
+ query_op_collection_dates.collection_dates.connect(handle_collection_dates);
+ query_op_collection_dates.done.connect(query_collection_dates_finished);
+
+ if (mount_op == null)
+ mount_op = new MountOperationAssistant(this);
+
+ op = query_op_collection_dates;
+ op.backend.mount_op = mount_op;
+ op.passphrase_required.connect(get_passphrase);
+ op.raise_error.connect((o, e, d) => {show_error(e, d);});
+
+ try {
+ stdout.printf("query_collection_dates start\n");
+ query_op_collection_dates.start();
+ }
+ catch (Error e) {
+ warning("%s\n", e.message);
+ show_error(e.message, null); // not really user-friendly text, but ideally this won't happen
+ query_collection_dates_finished(query_op_collection_dates, false, false);
+ }
+ }
+
+ protected void do_query_files_at_date(){
+ /*
+ * Initializes query operation for list-current-files at specific date
+ *
+ * Initializes query operation, updates list files page with current date of scan
+ * in human semi-friendly form and connect appropriate signals.
+ */
+ if (cancel_assistant) {
+ do_close();
+ return;
+ }
+ Time etime = backups_queue.poll();
+
+ /* Update progress */
+ int tepoch = etime.format("%s").to_int();
+ TimeVal ttoday = TimeVal();
+ ttoday.get_current_time();
+ int ttodayi = (int) ttoday.tv_sec;
+
+ string worddiff;
+ int tdiff = (ttodayi - tepoch)/60/60; // Hours
+ if (tdiff / 24 == 0 ) {
+ worddiff = _("Last day");
+ }
+ else if (tdiff / 24 / 7 == 0) {
+ worddiff = _("Last week");
+ }
+ else if (tdiff / 24 / 30 == 0) {
+ worddiff = _("Last month");
+ }
+ else if (tdiff / 24 / 30 > 1 && tdiff / 24 / 30 < 12) {
+ int n = tdiff / 24 / 30;
+ worddiff = _(@"About $n months ago");
+ }
+ else {
+ int n = tdiff / 24 / 30 / 12;
+ worddiff = _(@"About $n years ago");
+ }
+ //stdout.printf("\n\ntepch: %i, ttodayi: %i, tdiff: %i\n\n", tepoch, ttodayi, tdiff);
+ this.current_scan_date.set_text(worddiff);
+
+ stdout.printf("ADH do query, at epoch time: %s\n", etime.format("%c"));
+ if (mount_op == null)
+ mount_op = new MountOperationAssistant(this);
+
+ realize();
+ var xid = Gdk.x11_drawable_get_xid(this.window);
+
+ /* Time object does not support GObject-style construction */
+ query_op_files = new DejaDup.OperationFiles((uint)xid, etime, list_directory);
+ //query_op_files.time = etime;
+ query_op_files.listed_current_files.connect(handle_listed_files);
+ query_op_files.done.connect(query_files_finished);
+
+ op = query_op_files;
+ op.backend.mount_op = mount_op;
+ op.passphrase_required.connect(get_passphrase);
+ op.raise_error.connect((o, e, d) => {show_error(e, d);});
+
+ try {
+ query_op_files.start();
+ }
+ catch (Error e) {
+ warning("%s\n", e.message);
+ show_error(e.message, null); // not really user-friendly text, but ideally this won't happen
+ query_files_finished(query_op_files, false, false);
+ }
+ }
+
+ protected void query_collection_dates_finished(DejaDup.Operation op, bool success, bool cancelled) {
+ op_state = op.get_state();
+ //this.query_op_files = null;
+ query_op_collection_dates = null;
+ op = null;
+
+ if (cancelled) {
+ do_close();
+ //stdout.printf("fail\n");
+ }
+ else if (success)
+ //go_forward();
+ stdout.printf("success\n");
+ }
+
+ protected void query_wrapup() {
+ stdout.printf("\nQUERY WRAPUP\n");
+ //var deleted = new ArrayList<string>();
+
+ /*var iterator = files_at_epoch.map_iterator();
+ var has_next = iterator.first();
+
+ allfiles_prev = iterator.get_value();
+ has_next = iterator.next();
+ while (has_next == true) {
+ allfiles_cur = iterator.get_value();
+ foreach(string pfile in allfiles_prev) {
+ if (!allfiles_cur.contains(pfile)) {
+ deleted.add(pfile);
+ }
+ }
+ allfiles_prev = allfiles_cur;
+ has_next = iterator.next();
+ }
+
+ foreach(var elem in deleted) {
+ stdout.printf("%s", elem);
+ }*/
+ //printlist(deleted);
+ //foreach(var edate in files_at_epoch){
+ //stdout.printf("%s %s\n", edate.key,edate.value[3]);
+
+
+
+ /*foreach(string file in edate.value) {
+ stdout.printf("%s %s\n", edate.key,file);
+ }*/
+ //}
+ }
+
+ protected override void do_cancel() {
+ /*
+ * Mark cancel_assistant as true so that all other operations are blocked.
+ *
+ * do_cancel kills current operation but because we are still running recursion
+ * and at the end of each operation still check if we have anything to deleted,
+ * we need to manually mark entire assistant as canceled so that no further
+ * operations are called.
+ */
+ cancel_assistant = true;
+ base.do_cancel();
+ }
+
+ protected override void apply_finished(DejaDup.Operation op, bool success, bool cancelled)
+ {
+ /*
+ * Ran after assistant finishes applying restore operation.
+ *
+ * After assistant finishes with initial OperationRestore, apply_finished is called. Afterwards we
+ * check if restore_queue is empty and if not, rerun apply function. If it is, we move to
+ * summary page.
+ */
+ //status_icon = null; PRIV PARAMETER - FIXIT!
+ op = null;
+
+ stdout.printf("\n\napply_finished\n\n");
+
+ if (cancelled) {
+ if (success) // stop (resume later) vs cancel
+ Gtk.main_quit();
+ else {
+ stdout.printf("do close!\n");
+ do_close();
+ }
+ }
+ else {
+ if (success) {
+ succeeded = true;
+ stdout.printf("restore queue size: %u", this.restore_queue.size);
+ stdout.printf("what to restore: %s\n", this.restore_queue.peek().name);
+ if (this.restore_queue.size > 0) {
+ stdout.printf("apply finished v ADH; we need more restoring!");
+ base.do_apply();
+ } else {
+ stdout.printf("no more files to restore");
+ go_to_page(summary_page);
+ }
+ }
+ else // show error
+ force_visible(false);
+ }
+ }
+
+ protected void query_files_finished(DejaDup.Operation op, bool success, bool cancelled)
+ {
+ op_state = op.get_state();
+ query_op_files = null;
+ //this.query_op_collection_dates = null;
+ op = null;
+
+ if (backups_queue.size == 0) {
+ this.spinner.stop();
+ this.spinner.destroy();
+ this.current_scan_date.set_text("Done!");
+ scan_queue = false;
+ }
+ else {
+ if (scan_queue)
+ do_query_files_at_date();
+ }
+
+ if (cancelled) {
+ do_close();
+ stdout.printf("fail\n");
+ }
+ else if (success)
+ //go_forward();
+ stdout.printf("success\n");
+ }
+
+ protected override Gtk.Widget? make_confirm_page(){
+ /*
+ * Build confirmation page and add various dynamic elements to Glade template
+ */
+ var page = new Gtk.Table(1, 1, false);
+ page.name = "confirm_page";
+ var builder = new Gtk.Builder();
+ try {
+ //builder.add_from_file("interface/restorefiles.glade");
+ builder.add_from_file("interface/directory_history_confirmation_page.glade");
+ //builder.connect_signals(this);
+
+ var window = builder.get_object("viewport") as Gtk.Widget;
+ var backup_source_properties = builder.get_object("backup_properties") as Gtk.Table;
+ restore_files_table = builder.get_object("restore_files") as Gtk.Table;
+ Gtk.Widget w;
+
+ w = new DejaDup.ConfigLabelLocation();
+ backup_source_properties.attach(w, 1, 2, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ w = new DejaDup.ConfigLabelBool(DejaDup.ENCRYPT_KEY);
+ backup_source_properties.attach(w, 1, 2, 1, 2, Gtk.AttachOptions.FILL, 0, 0, 0);
+
+ //var label = new Gtk.Label("make_confirm_page");
+ //label.set("xalign", 0.0f);
+ //restore_files_table.attach(label, 0, 1, 0, 1, Gtk.AttachOptions.FILL, 0, 0, 0);
+ /*label = new Gtk.Label("meh 2");
+ label.set("xalign", 0.0f);
+ restore_files_table.attach(label, 0, 1, 1, 2, Gtk.AttachOptions.FILL, 0, 0, 0);*/
+
+ //int rows = 2;
+ /*for (var i=0; i<50; i++){
+ label = new Gtk.Label("row %d".printf(rows));
+ label.set("xalign", 0.0f);
+ rows++;
+ restore_files_table.resize(rows, 1);
+ restore_files_table.attach(label, 0, 1, rows, rows+1, Gtk.AttachOptions.FILL, 0, 0, 0);
+ }*/
+
+ //stdout.printf(restore_files_table);
+
+ //label.set("xalign", 0.0f);
+ //restore_files_table.attach(label, 0, 1, 1, 2, Gtk.AttachOptions.FILL, 0, 0, 0);
+ //int rows = 0;
+ //confirm_table = new Gtk.Table(rows, 1, false);
+
+
+ /*var restorefiles = builder.get_object("restorefileswindow") as Gtk.ScrolledWindow;
+
+ this.restoremodel = new Gtk.ListStore(1, typeof(string));
+ var _tree_view = new Gtk.TreeView.with_model(this.restoremodel);
+
+ _tree_view.insert_column_with_attributes(-1, "File name", new Gtk.CellRendererText(), "text", 0);
+
+ _tree_view.set_headers_visible (true);
+ restorefiles.add_with_viewport(_tree_view);*/
+
+ window.reparent(page);
+ return page;
+ } catch (Error err) {
+ warning("Error: %s", err.message);
+ return null;
+ }
+ }
+
+ /*protected override DejaDup.Operation create_op() {
+ realize();
+ stdout.printf("create_op");
+ var xid = Gdk.x11_drawable_get_xid(this.window);
+ return new DejaDup.OperationFiles((uint)xid);
+ }*/
+ protected override DejaDup.Operation create_op()
+ {
+ /*
+ * Creates operation that is then called by do_apply.
+ */
+ stdout.printf("create_op\n");
+ realize();
+ var xid = Gdk.x11_drawable_get_xid(this.window);
+ /* var rest_op = new DejaDup.OperationRestore(restore_location, date,
+ restore_files, (uint)xid);*/
+
+ stdout.printf("resture queue before length: %x\n", this.restore_queue.size);
+
+ var restore_file = this.restore_queue.poll();
+ stdout.printf("resture queue before after: %x\n", this.restore_queue.size);
+ //var restore_file = restore_queue.nth(0);
+ //var restore_file = restore_queue.pop_head();
+ //restore_queue.remove(restore_file);
+ /*
+ OperationRestore usually takes list of file so restore. Since it is high
+ probability that if we will restore multiple files, they will be from different dates,
+ we simply call OperationRestore multiple times with singel date and file.
+ */
+
+ var restore_files = new GLib.List<File>();
+ //restore_files.append(File.new_for_path(restore_file.split(" ")[0]));
+ restore_files.append(File.new_for_path(restore_file.name));
+ //2001-07-15T04:09:38-07:00
+ //restore_file.deleted.format("%FT%T%z"))
+ //stdout.printf("name:%s time: %s", restore_file, restore_file.deleted.format("%FT%T%z"));
+ //restore_file.split(" ")[1]]
+
+ var rest_op = new DejaDup.OperationRestore("/",
+ restore_file.deleted.format("%s"),
+ restore_files,
+ (uint)xid);
+ return rest_op;
+ }
+
+ protected override string get_progress_file_prefix(){
+ return _("Restoring");
+ }
+}
\ No newline at end of file
=== modified file 'deja-dup/AssistantOperation.vala'
--- deja-dup/AssistantOperation.vala 2010-06-14 01:06:33 +0000
+++ deja-dup/AssistantOperation.vala 2010-07-12 16:58:42 +0000
@@ -21,6 +21,23 @@
public abstract class AssistantOperation : Assistant
{
+ /*
+ * Abstract class for implementation of various common pages in assistant
+ *
+ * Abstract class that provides various methods that serve as pages in
+ * assistant. Required methods that all classes that inherit from this
+ * class must implement are create_op, make_confirm_page and
+ * get_progress_file_prefix.
+ *
+ * Pages are shown in the following order:
+ * 1. (Optional) Custom configuration pages
+ * 2. Setup pages
+ * 3. Confirmation page
+ * 4. Password page
+ * 5. Question page
+ * 6. (Required) Progress page
+ * 7. Summary
+ */
protected Gtk.Widget confirm_page {get; private set;}
public signal void closing(bool success);
@@ -79,10 +96,23 @@
closed.connect(do_close);
prepare.connect(do_prepare);
}
-
+
+ /*
+ * Creates confirmation page for particular assistant
+ *
+ * Creates confirmation page that should create confirm_page widget that
+ * is presented for final confirmation.
+ */
protected abstract Gtk.Widget? make_confirm_page();
protected virtual void add_setup_pages() {}
- protected virtual void add_custom_config_pages() {}
+ protected virtual void add_custom_config_pages(){}
+ /*
+ * Creates and calls appropriate operation
+ *
+ * Creates and calls appropriate operation (Backup, Restore, Status, Files)
+ * that is then used to perform various defined tasks on backend. It is
+ * also later connected to various signals.
+ */
protected abstract DejaDup.Operation create_op();
protected abstract string get_progress_file_prefix();
protected virtual void set_op_icon_name() {}
@@ -111,6 +141,11 @@
void show_progress(DejaDup.Operation op, double percent)
{
+ /*
+ * Updates prograss bar
+ *
+ * Updates progress bar with percet provided.
+ */
progress_bar.fraction = percent;
gives_progress = true;
}
@@ -324,6 +359,11 @@
void add_config_pages_if_needed()
{
+ /*
+ * Creates configure pages if required
+ *
+ *
+ */
var client = DejaDup.get_gconf_client();
string val;
try {
@@ -341,6 +381,11 @@
void add_confirm_page()
{
+ /*
+ * Adds confirm page to the sequence of pages
+ *
+ * Adds confirm_page widget to the sequence of pages in assistant.
+ */
var page = make_confirm_page();
if (page == null)
return;
@@ -378,10 +423,10 @@
summary_page = page;
}
- void apply_finished(DejaDup.Operation op, bool success, bool cancelled)
+ protected virtual void apply_finished(DejaDup.Operation op, bool success, bool cancelled)
{
status_icon = null;
- this.op = null;
+ op = null;
if (cancelled) {
if (success) // stop (resume later) vs cancel
@@ -399,8 +444,15 @@
}
}
- void do_apply()
+ protected void do_apply()
{
+ /*
+ * Applies/starts operation that was configured during assistant process and
+ * connect appropriate signals
+ *
+ * Mounts appropriate backend, creates child operation, connects signals to
+ * handler functions and starts operation.
+ */
if (mount_op == null)
mount_op = new MountOperationAssistant(this);
@@ -432,6 +484,17 @@
protected virtual void do_prepare(Assistant assist, Gtk.Widget page)
{
+ /*
+ * Prepare page in assistant
+ *
+ * Prepares every page in assistant for various operations. For example, if
+ * user returns to confirmation page from progress page, it is necessary
+ * to kill running operation. If user advances to progress page, it runs
+ * do_apply and runs the needed operation.
+ *
+ * do_prepare is run when user switches pages and not when pages are built.
+ */
+
if (timeout_id > 0) {
Source.remove(timeout_id);
timeout_id = 0;
@@ -464,11 +527,14 @@
status_icon = null; // hide immediately to seem responsive
}
- void do_cancel()
+ protected virtual void do_cancel()
{
+ stdout.printf("\n\ndo_cancel\n\n");
hide_everything();
- if (op != null)
+ if (op != null) {
+ stdout.printf("\nop != null => op.cancel\n");
op.cancel(); // do_close will happen in done() callback
+ }
else
do_close();
}
@@ -532,24 +598,32 @@
void found_passphrase(GnomeKeyring.Result result, string? str)
{
- if (str != null)
+ stdout.printf("found_passphrase\n");
+ if (str != null) {
+ stdout.printf("mam passphrase\n");
op.continue_with_passphrase(str);
- else
+ }
+ else {
+ stdout.printf("sprasujem za passphrase\n");
ask_passphrase();
+ }
}
protected void get_passphrase()
{
+ stdout.printf("\nget_passphrase\n");
// DEJA_DUP_TESTING only set when we are in test suite
var testing = Environment.get_variable("DEJA_DUP_TESTING");
if (testing == null || testing == "") {
// First, try user's keyring
+ stdout.printf("pass v keyringu\n");
GnomeKeyring.find_password(PASSPHRASE_SCHEMA,
found_passphrase,
"owner", Config.PACKAGE,
"type", "passphrase");
}
else {
+ stdout.printf("\nasking user\n");
// just jump straight to asking user
ask_passphrase();
}
@@ -582,7 +656,7 @@
}
}
- op.continue_with_passphrase(passphrase);
+ //op.continue_with_passphrase(passphrase);
}
void stop_question(Gtk.Dialog dlg, int resp)
=== modified file 'deja-dup/AssistantRestore.vala'
--- deja-dup/AssistantRestore.vala 2010-06-14 01:06:33 +0000
+++ deja-dup/AssistantRestore.vala 2010-07-12 16:58:42 +0000
@@ -315,6 +315,13 @@
protected void handle_collection_dates(DejaDup.OperationStatus op, List<string>? dates)
{
+ /*
+ * Receives list of dates of backups and shows them to user
+ *
+ * After receiving list of dates at which backups were performed function
+ * converts dates to TimeVal structures and later converts them to Time to
+ * time to show them in nicely formate local form.
+ */
var timevals = new List<TimeVal?>();
TimeVal tv = TimeVal();
=== modified file 'deja-dup/Makefile.am'
--- deja-dup/Makefile.am 2010-03-25 21:40:33 +0000
+++ deja-dup/Makefile.am 2010-07-12 16:58:42 +0000
@@ -23,6 +23,8 @@
--vapidir $(top_srcdir)/widgets \
--vapidir $(top_srcdir)/vapi \
--pkg gtk+-2.0 \
+ --pkg gee-1.0 \
+ --pkg gmodule-2.0 \
--pkg gdk-x11-2.0 \
--pkg gio-2.0 \
--pkg gconf-2.0 \
@@ -46,6 +48,7 @@
Assistant.vala \
AssistantBackup.vala \
AssistantOperation.vala \
+ AssistantDirectoryHistory.vala \
AssistantRestore.vala \
MainWindow.vala \
MountOperationAssistant.vala \
=== modified file 'deja-dup/main.vala'
--- deja-dup/main.vala 2010-05-10 09:54:06 +0000
+++ deja-dup/main.vala 2010-07-12 16:58:42 +0000
@@ -27,12 +27,14 @@
static bool show_version = false;
static bool restore_mode = false;
static bool backup_mode = false;
+ static bool dirhistory_mode = false;
static string[] filenames = null;
static const OptionEntry[] options = {
{"version", 0, 0, OptionArg.NONE, ref show_version, N_("Show version"), null},
{"restore", 0, 0, OptionArg.NONE, ref restore_mode, N_("Restore given files"), null},
{"backup", 0, 0, OptionArg.NONE, ref backup_mode, N_("Immediately start a backup"), null},
- {"", 0, 0, OptionArg.FILENAME_ARRAY, ref filenames, null, null}, // remaining
+ {"dir-history", 0, 0, OptionArg.NONE, ref dirhistory_mode, N_("Directory history"), null},
+ {"", 0, 0, OptionArg.FILENAME_ARRAY, ref filenames, null, null}, // remaining
{null}
};
@@ -52,6 +54,19 @@
return false;
}
}
+
+ if (dirhistory_mode) {
+ if (filenames == null) {
+ printerr("%s\n", _("No directory provided"));
+ status = 1;
+ return false;
+ }
+ else if (filenames.length > 1) {
+ printerr("%s\n", _("Only one directory can be shown at once"));
+ status = 1;
+ return false;
+ }
+ }
return true;
}
@@ -110,6 +125,30 @@
toplevel.destroy.connect((t) => {Gtk.main_quit();});
// specifically don't show
}
+ else if (dirhistory_mode){
+ //try {
+ //toplevel = new AssistantDirectoryHistory();
+ /*var assdirhist = new AssistantDirectoryHistory();
+ var intdirhist = new InterfaceDirectoryHistory();
+ var builder = new Gtk.Builder();
+ builder.add_from_file("/home/urbans/Documents/dev/deja-dup.nautilus/interface/sample.ui");
+ builder.connect_signals(assdirhist);
+ //builder.connect_signals(null);
+ var window = builder.get_object("window") as Gtk.Window;*/
+
+ //List<File> file_list = new List<File>();
+
+ //file_list.append(File.new_for_commandline_arg(filenames[i++]));
+ File list_directory = File.new_for_commandline_arg(filenames[0]);
+
+ //return 1;
+ toplevel = new AssistantDirectoryHistory(list_directory);
+ toplevel.destroy.connect((t) => {Gtk.main_quit();});
+ toplevel.show_all();
+ /*} catch (Error e) {
+ stderr.printf("ni ni interfejsa: %s\n", e.message);
+ }*/
+ }
else {
toplevel = new MainWindow();
toplevel.show_all();
=== added directory 'interface'
=== added file 'interface/directory_history_confirmation_page.glade'
--- interface/directory_history_confirmation_page.glade 1970-01-01 00:00:00 +0000
+++ interface/directory_history_confirmation_page.glade 2010-07-12 16:58:42 +0000
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkWindow" id="window1">
+ <child>
+ <object class="GtkViewport" id="viewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkVBox" id="maintable">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkTable" id="backup_properties">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="row_spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="width_request">1</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Backup source</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Encrypted</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">3</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0.029999999329447746</property>
+ <property name="label" translatable="yes">Restoring files:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkTable" id="restore_files">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
=== added file 'interface/listfiles-crt-out.ui'
--- interface/listfiles-crt-out.ui 1970-01-01 00:00:00 +0000
+++ interface/listfiles-crt-out.ui 2010-07-12 16:58:42 +0000
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="deletedfiles">
+ <columns>
+ <!-- column-name Restore -->
+ <column type="gboolean"/>
+ <!-- column-name File -->
+ <column type="gchararray"/>
+ <!-- column-name Deleted -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">True</col>
+ <col id="1" translatable="yes">foo.txt</col>
+ <col id="2" translatable="yes">12. 3. 2009</col>
+ </row>
+ <row>
+ <col id="0">False</col>
+ <col id="1" translatable="yes">bar.txt</col>
+ <col id="2" translatable="yes">19.7.2008</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkWindow" id="window">
+ <property name="default_width">300</property>
+ <property name="default_height">200</property>
+ <signal name="destroy" handler="gtk_main_quit"/>
+ <child>
+ <object class="GtkViewport" id="viewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkTable" id="backup_table">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">3</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="xpad">10</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">Backup source</property>
+ </object>
+ <packing>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0.44999998807907104</property>
+ <property name="xpad">10</property>
+ <property name="ypad">5</property>
+ <property name="label" translatable="yes">Status</property>
+ <property name="width_chars">10</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="progress_table">
+ <property name="visible">True</property>
+ <property name="n_columns">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="filelistwindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
=== added file 'interface/listfiles.ui'
--- interface/listfiles.ui 1970-01-01 00:00:00 +0000
+++ interface/listfiles.ui 2010-07-12 16:58:42 +0000
@@ -0,0 +1,164 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="deletedfiles">
+ <columns>
+ <!-- column-name Restore -->
+ <column type="gboolean"/>
+ <!-- column-name File -->
+ <column type="gchararray"/>
+ <!-- column-name Deleted -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">True</col>
+ <col id="1" translatable="yes">foo.txt</col>
+ <col id="2" translatable="yes">12. 3. 2009</col>
+ </row>
+ <row>
+ <col id="0">False</col>
+ <col id="1" translatable="yes">bar.txt</col>
+ <col id="2" translatable="yes">19.7.2008</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkWindow" id="window">
+ <property name="default_width">300</property>
+ <property name="default_height">200</property>
+ <signal name="destroy" handler="gtk_main_quit"/>
+ <child>
+ <object class="GtkViewport" id="viewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="status">
+ <property name="width_request">65</property>
+ <property name="height_request">21</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Currently </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="timediff">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="text" translatable="yes">Search</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">deletedfiles</property>
+ <property name="reorderable">True</property>
+ <property name="search_column">1</property>
+ <property name="enable_grid_lines">horizontal</property>
+ <property name="enable_tree_lines">True</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="checkboxcolumn">
+ <property name="clickable">True</property>
+ <property name="reorderable">True</property>
+ <property name="sort_indicator">True</property>
+ <child>
+ <object class="GtkCellRendererToggle" id="restoretoggle">
+ <signal name="toggled" handler="assistant_directory_history_on_restoretoggle"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="filenamecolumn">
+ <property name="title">File name</property>
+ <property name="reorderable">True</property>
+ <property name="sort_indicator">True</property>
+ <property name="sort_column_id">1</property>
+ <child>
+ <object class="GtkCellRendererText" id="filenametext"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="datecolumn">
+ <property name="title">Deleted</property>
+ <property name="reorderable">True</property>
+ <property name="sort_indicator">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="datetext"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
=== added file 'interface/restorefiles.glade'
--- interface/restorefiles.glade 1970-01-01 00:00:00 +0000
+++ interface/restorefiles.glade 2010-07-12 16:58:42 +0000
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkWindow" id="window">
+ <child>
+ <object class="GtkViewport" id="viewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">You will restore the following files:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="restorefileswindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Click Forward to proceed</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
=== renamed file 'po/deja-dup.pot' => 'po/deja-dup.pot.THIS'
Follow ups