~teejee2008/timeshift/trunk

« back to all changes in this revision

Viewing changes to src/Gtk/RestoreWindow.vala

  • Committer: Tony George
  • Date: 2016-08-13 04:16:47 UTC
  • Revision ID: tony.george.kol@gmail.com-20160813041647-ivf2g6rszt00xco5
Updated project structure

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * MainWindow.vala
 
3
 *
 
4
 * Copyright 2013 Tony George <teejee2008@gmail.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
19
 * MA 02110-1301, USA.
 
20
 *
 
21
 *
 
22
 */
 
23
 
 
24
using Gtk;
 
25
using Gee;
 
26
 
 
27
using TeeJee.Logging;
 
28
using TeeJee.FileSystem;
 
29
using TeeJee.Devices;
 
30
using TeeJee.JsonHelper;
 
31
using TeeJee.ProcessHelper;
 
32
using TeeJee.GtkHelper;
 
33
using TeeJee.System;
 
34
using TeeJee.Misc;
 
35
 
 
36
public class RestoreWindow : Gtk.Dialog{
 
37
        private Box vbox_main;
 
38
        private Box hbox_action;
 
39
        private Notebook notebook;
 
40
 
 
41
    //target device
 
42
    private Label lbl_header_partitions;
 
43
    private RadioButton radio_sys;
 
44
    private RadioButton radio_other;
 
45
    private TreeView tv_partitions;
 
46
        private ScrolledWindow sw_partitions;
 
47
        private TreeViewColumn col_device_target;
 
48
        private TreeViewColumn col_mount;
 
49
        private TreeViewColumn col_fs;
 
50
        private TreeViewColumn col_size;
 
51
        private TreeViewColumn col_dist;
 
52
        private CellRendererCombo cell_mount;
 
53
 
 
54
        //bootloader
 
55
        private Label lbl_header_bootloader;
 
56
        private ComboBox cmb_boot_device;
 
57
        private CheckButton chk_skip_grub_install;
 
58
 
 
59
        //apps
 
60
        private Label lbl_app;
 
61
        private Box vbox_app;
 
62
        private Label lbl_app_message;
 
63
        private TreeView tv_app;
 
64
        private ScrolledWindow sw_app;
 
65
        private TreeViewColumn col_app;
 
66
 
 
67
        //exclude
 
68
        private Label lbl_exclude;
 
69
        private Box vbox_exclude;
 
70
        private LinkButton lnk_default_list;
 
71
        private TreeView tv_exclude;
 
72
        private ScrolledWindow sw_exclude;
 
73
        private TreeViewColumn col_exclude;
 
74
        private Toolbar toolbar_exclude;
 
75
        private ToolButton btn_remove;
 
76
        private ToolButton btn_warning;
 
77
        private ToolButton btn_reset_exclude_list;
 
78
 
 
79
        private MenuToolButton btn_exclude;
 
80
        private Gtk.Menu menu_exclude;
 
81
        private ImageMenuItem menu_exclude_add_file;
 
82
        private ImageMenuItem menu_exclude_add_folder;
 
83
        private ImageMenuItem menu_exclude_add_folder_contents;
 
84
 
 
85
        private MenuToolButton btn_include;
 
86
        private Gtk.Menu menu_include;
 
87
        private ImageMenuItem menu_include_add_file;
 
88
        private ImageMenuItem menu_include_add_folder;
 
89
 
 
90
        private Gee.ArrayList<string> temp_exclude_list;
 
91
 
 
92
        //actions
 
93
        private Button btn_cancel;
 
94
        private Button btn_restore;
 
95
 
 
96
        private Device selected_target = null;
 
97
 
 
98
        public RestoreWindow () {
 
99
                this.title = _("Restore");
 
100
        this.window_position = WindowPosition.CENTER_ON_PARENT;
 
101
        this.set_destroy_with_parent (true);
 
102
                this.set_modal (true);
 
103
        this.set_default_size (550, 500);
 
104
                this.skip_taskbar_hint = true;
 
105
                this.icon = get_app_icon(16);
 
106
 
 
107
            //vbox_main
 
108
        vbox_main = get_content_area ();
 
109
 
 
110
                //notebook
 
111
                notebook = new Notebook ();
 
112
                notebook.margin = 6;
 
113
                vbox_main.pack_start (notebook, true, true, 0);
 
114
 
 
115
                //target device tab -------------------------------------------------
 
116
 
 
117
                //lbl_exclude
 
118
                lbl_exclude = new Label (_("Target"));
 
119
 
 
120
        //vbox_target
 
121
        Box vbox_target = new Box (Orientation.VERTICAL, 6);
 
122
        vbox_target.margin = 6;
 
123
        notebook.append_page (vbox_target, lbl_exclude);
 
124
 
 
125
        //hbox_device
 
126
        Box hbox_device = new Box (Orientation.HORIZONTAL, 6);
 
127
        //hbox_device.margin = 6;
 
128
                vbox_target.add(hbox_device);
 
129
 
 
130
                //lbl_header_partitions
 
131
                lbl_header_partitions = new Gtk.Label((App.mirror_system ? _("Device for Cloning System") : _("Device for Restoring Snapshot")) + ":");
 
132
                lbl_header_partitions.xalign = (float) 0.0;
 
133
                lbl_header_partitions.set_use_markup(true);
 
134
                hbox_device.add(lbl_header_partitions);
 
135
 
 
136
                radio_sys = new RadioButton(null);
 
137
                hbox_device.add(radio_sys);
 
138
                radio_sys.label = "Current System";
 
139
 
 
140
                radio_other = new RadioButton.from_widget(radio_sys);
 
141
                hbox_device.add(radio_other);
 
142
                radio_other.label = "Other Device";
 
143
 
 
144
                if (App.live_system() || App.mirror_system){
 
145
                        radio_other.active = true;
 
146
                        radio_sys.sensitive = false;
 
147
                }
 
148
                else{
 
149
                        radio_sys.sensitive = true;
 
150
                        radio_sys.active = true;
 
151
                }
 
152
 
 
153
                radio_sys.toggled.connect(() => {
 
154
                        sw_partitions.sensitive = radio_other.active;
 
155
 
 
156
                        refresh_tv_partitions();
 
157
 
 
158
                        if (radio_sys.active){
 
159
                                App.restore_target = App.root_device;
 
160
                        }
 
161
                        else{
 
162
                                init_mounts();
 
163
                        }
 
164
 
 
165
                        //tv_partitions_select_target();
 
166
                        cmb_boot_device_select_default();
 
167
                });
 
168
 
 
169
                init_device_list(vbox_target);
 
170
 
 
171
                //bootloader options -------------------------------------------
 
172
 
 
173
                //lbl_header_bootloader
 
174
                lbl_header_bootloader = new Gtk.Label(_("Device for Bootloader Installation") + ":");
 
175
                lbl_header_bootloader.set_use_markup(true);
 
176
                lbl_header_bootloader.xalign = (float) 0.0;
 
177
                vbox_target.add(lbl_header_bootloader);
 
178
 
 
179
                //hbox_grub
 
180
                Box hbox_grub = new Box (Orientation.HORIZONTAL, 6);
 
181
                hbox_grub.margin_right = 6;
 
182
        //hbox_grub.margin_bottom = 6;
 
183
        vbox_target.add (hbox_grub);
 
184
 
 
185
                //cmb_boot_device
 
186
                cmb_boot_device = new ComboBox ();
 
187
                cmb_boot_device.hexpand = true;
 
188
                hbox_grub.add(cmb_boot_device);
 
189
 
 
190
                CellRendererText cell_dev_margin = new CellRendererText ();
 
191
                cell_dev_margin.text = "";
 
192
                cmb_boot_device.pack_start (cell_dev_margin, false);
 
193
 
 
194
                CellRendererPixbuf cell_dev_icon = new CellRendererPixbuf ();
 
195
                cell_dev_icon.stock_id = "gtk-harddisk";
 
196
                cmb_boot_device.pack_start (cell_dev_icon, false);
 
197
 
 
198
                CellRendererText cell_device_grub = new CellRendererText();
 
199
        cmb_boot_device.pack_start(cell_device_grub, false );
 
200
        cmb_boot_device.set_cell_data_func (cell_device_grub, cell_device_grub_render);
 
201
 
 
202
                string tt = "<b>" + _("** Advanced Users **") + "</b>\n\n"+ _("Skips bootloader (re)installation on target device.\nFiles in /boot directory on target partition will remain untouched.\n\nIf you are restoring a system that was bootable previously then it should boot successfully.\nOtherwise the system may fail to boot.");
 
203
 
 
204
                //chk_skip_grub_install
 
205
                chk_skip_grub_install = new CheckButton.with_label(_("Skip bootloader installation (not recommended)"));
 
206
                chk_skip_grub_install.active = false;
 
207
                chk_skip_grub_install.set_tooltip_markup(tt);
 
208
                vbox_target.add (chk_skip_grub_install);
 
209
 
 
210
                chk_skip_grub_install.toggled.connect(()=>{
 
211
                        cmb_boot_device.sensitive = !chk_skip_grub_install.active;
 
212
                });
 
213
 
 
214
        //Exclude Apps tab ---------------------------------------------
 
215
 
 
216
                //lbl_apps
 
217
                lbl_app = new Label (_("Exclude"));
 
218
 
 
219
        //vbox_apps
 
220
        vbox_app = new Box(Gtk.Orientation.VERTICAL, 6);
 
221
        vbox_app.margin = 6;
 
222
        notebook.append_page (vbox_app, lbl_app);
 
223
 
 
224
                //lbl_app_message
 
225
                string msg = _("Select the applications for which current settings should be kept.") + "\n";
 
226
                msg += _("For all other applications, settings will be restored from selected snapshot.");
 
227
                lbl_app_message = new Label (msg);
 
228
                lbl_app_message.xalign = (float) 0.0;
 
229
                vbox_app.add(lbl_app_message);
 
230
 
 
231
                //tv_app-----------------------------------------------
 
232
 
 
233
                //tv_app
 
234
                tv_app = new TreeView();
 
235
                tv_app.get_selection().mode = SelectionMode.MULTIPLE;
 
236
                tv_app.headers_visible = false;
 
237
                tv_app.set_rules_hint (true);
 
238
 
 
239
                //sw_app
 
240
                sw_app = new ScrolledWindow(null, null);
 
241
                sw_app.set_shadow_type (ShadowType.ETCHED_IN);
 
242
                sw_app.add (tv_app);
 
243
                sw_app.expand = true;
 
244
                vbox_app.add(sw_app);
 
245
 
 
246
        //col_app
 
247
                col_app = new TreeViewColumn();
 
248
                col_app.title = _("Application");
 
249
                col_app.expand = true;
 
250
                tv_app.append_column(col_app);
 
251
 
 
252
                CellRendererText cell_app_margin = new CellRendererText ();
 
253
                cell_app_margin.text = "";
 
254
                col_app.pack_start (cell_app_margin, false);
 
255
 
 
256
                CellRendererToggle cell_app_enabled = new CellRendererToggle ();
 
257
                cell_app_enabled.radio = false;
 
258
                cell_app_enabled.activatable = true;
 
259
                col_app.pack_start (cell_app_enabled, false);
 
260
                col_app.set_cell_data_func (cell_app_enabled, cell_app_enabled_render);
 
261
 
 
262
                cell_app_enabled.toggled.connect (cell_app_enabled_toggled);
 
263
 
 
264
                CellRendererText cell_app_text = new CellRendererText ();
 
265
                col_app.pack_start (cell_app_text, false);
 
266
                col_app.set_cell_data_func (cell_app_text, cell_app_text_render);
 
267
 
 
268
        //Advanced tab ---------------------------------------------
 
269
 
 
270
                //lbl_exclude
 
271
                lbl_exclude = new Label (_("Advanced"));
 
272
 
 
273
        //vbox_exclude
 
274
        vbox_exclude = new Box(Gtk.Orientation.VERTICAL, 6);
 
275
        vbox_exclude.margin = 6;
 
276
        notebook.append_page (vbox_exclude, lbl_exclude);
 
277
 
 
278
                //toolbar_exclude ---------------------------------------------------
 
279
 
 
280
        //toolbar_exclude
 
281
                toolbar_exclude = new Gtk.Toolbar ();
 
282
                toolbar_exclude.toolbar_style = ToolbarStyle.BOTH_HORIZ;
 
283
                vbox_exclude.add(toolbar_exclude);
 
284
 
 
285
                string png_exclude = App.share_folder + "/timeshift/images/item-gray.png";
 
286
                string png_include = App.share_folder + "/timeshift/images/item-blue.png";
 
287
 
 
288
                //btn_exclude
 
289
                btn_exclude = new Gtk.MenuToolButton(null,"");
 
290
                toolbar_exclude.add(btn_exclude);
 
291
 
 
292
                btn_exclude.is_important = true;
 
293
                btn_exclude.label = _("Exclude");
 
294
                btn_exclude.set_tooltip_text (_("Exclude"));
 
295
                btn_exclude.set_icon_widget(new Gtk.Image.from_file (png_exclude));
 
296
 
 
297
                //btn_include
 
298
                btn_include = new Gtk.MenuToolButton(null,"");
 
299
                toolbar_exclude.add(btn_include);
 
300
 
 
301
                btn_include.is_important = true;
 
302
                btn_include.label = _("Include");
 
303
                btn_include.set_tooltip_text (_("Include"));
 
304
                btn_include.set_icon_widget(new Gtk.Image.from_file (png_include));
 
305
 
 
306
                //btn_remove
 
307
                btn_remove = new Gtk.ToolButton.from_stock("gtk-remove");
 
308
                toolbar_exclude.add(btn_remove);
 
309
 
 
310
                btn_remove.is_important = true;
 
311
                btn_remove.label = _("Remove");
 
312
                btn_remove.set_tooltip_text (_("Remove selected items"));
 
313
 
 
314
                btn_remove.clicked.connect (btn_remove_clicked);
 
315
 
 
316
                //btn_warning
 
317
                btn_warning = new Gtk.ToolButton.from_stock("gtk-dialog-warning");
 
318
                toolbar_exclude.add(btn_warning);
 
319
 
 
320
                btn_warning.is_important = true;
 
321
                btn_warning.label = _("Warning");
 
322
                btn_warning.set_tooltip_text (_("Warning"));
 
323
 
 
324
                btn_warning.clicked.connect (btn_warning_clicked);
 
325
 
 
326
                //separator
 
327
                var separator = new Gtk.SeparatorToolItem();
 
328
                separator.set_draw (false);
 
329
                separator.set_expand (true);
 
330
                toolbar_exclude.add(separator);
 
331
 
 
332
                //btn_reset_exclude_list
 
333
                btn_reset_exclude_list = new Gtk.ToolButton.from_stock("gtk-refresh");
 
334
                toolbar_exclude.add(btn_reset_exclude_list);
 
335
 
 
336
                btn_reset_exclude_list.is_important = false;
 
337
                btn_reset_exclude_list.label = _("Reset");
 
338
                btn_reset_exclude_list.set_tooltip_text (_("Reset this list to default state"));
 
339
 
 
340
                btn_reset_exclude_list.clicked.connect (btn_reset_exclude_list_clicked);
 
341
 
 
342
                //menu --------------------------------------------------
 
343
 
 
344
        //menu_exclude
 
345
                menu_exclude = new Gtk.Menu();
 
346
                btn_exclude.set_menu(menu_exclude);
 
347
 
 
348
                //menu_exclude_add_file
 
349
                menu_exclude_add_file = new ImageMenuItem.with_label ("");
 
350
                menu_exclude_add_file.label = _("Exclude File(s)");
 
351
                menu_exclude_add_file.set_image(new Gtk.Image.from_file (png_exclude));
 
352
                menu_exclude.append(menu_exclude_add_file);
 
353
 
 
354
                menu_exclude_add_file.activate.connect (menu_exclude_add_files_clicked);
 
355
 
 
356
                //menu_exclude_add_folder
 
357
                menu_exclude_add_folder = new ImageMenuItem.with_label ("");
 
358
                menu_exclude_add_folder.label = _("Exclude Directory");
 
359
                menu_exclude_add_folder.set_image(new Gtk.Image.from_file (png_exclude));
 
360
                menu_exclude.append(menu_exclude_add_folder);
 
361
 
 
362
                menu_exclude_add_folder.activate.connect (menu_exclude_add_folder_clicked);
 
363
 
 
364
                //menu_exclude_add_folder_contents
 
365
                menu_exclude_add_folder_contents = new ImageMenuItem.with_label ("");
 
366
                menu_exclude_add_folder_contents.label = _("Exclude Directory Contents");
 
367
                menu_exclude_add_folder_contents.set_image(new Gtk.Image.from_file (png_exclude));
 
368
                menu_exclude.append(menu_exclude_add_folder_contents);
 
369
 
 
370
                menu_exclude_add_folder_contents.activate.connect (menu_exclude_add_folder_contents_clicked);
 
371
 
 
372
                //menu_include
 
373
                menu_include = new Gtk.Menu();
 
374
                btn_include.set_menu(menu_include);
 
375
 
 
376
                //menu_include_add_file
 
377
                menu_include_add_file = new ImageMenuItem.with_label ("");
 
378
                menu_include_add_file.label = _("Include File(s)");
 
379
                menu_include_add_file.set_image(new Gtk.Image.from_file (png_include));
 
380
                menu_include.append(menu_include_add_file);
 
381
 
 
382
                menu_include_add_file.activate.connect (menu_include_add_files_clicked);
 
383
 
 
384
                //menu_include_add_folder
 
385
                menu_include_add_folder = new ImageMenuItem.with_label ("");
 
386
                menu_include_add_folder.label = _("Include Directory");
 
387
                menu_include_add_folder.set_image(new Gtk.Image.from_file (png_include));
 
388
                menu_include.append(menu_include_add_folder);
 
389
 
 
390
                menu_include_add_folder.activate.connect (menu_include_add_folder_clicked);
 
391
 
 
392
                menu_exclude.show_all();
 
393
                menu_include.show_all();
 
394
 
 
395
                //tv_exclude-----------------------------------------------
 
396
 
 
397
                //tv_exclude
 
398
                tv_exclude = new TreeView();
 
399
                tv_exclude.get_selection().mode = SelectionMode.MULTIPLE;
 
400
                tv_exclude.headers_visible = true;
 
401
                tv_exclude.set_rules_hint (true);
 
402
                //tv_exclude.row_activated.connect(tv_exclude_row_activated);
 
403
 
 
404
                //sw_exclude
 
405
                sw_exclude = new ScrolledWindow(null, null);
 
406
                sw_exclude.set_shadow_type (ShadowType.ETCHED_IN);
 
407
                sw_exclude.add (tv_exclude);
 
408
                sw_exclude.expand = true;
 
409
                vbox_exclude.add(sw_exclude);
 
410
 
 
411
        //col_exclude
 
412
                col_exclude = new TreeViewColumn();
 
413
                col_exclude.title = _("File Pattern");
 
414
                col_exclude.expand = true;
 
415
 
 
416
                CellRendererText cell_exclude_margin = new CellRendererText ();
 
417
                cell_exclude_margin.text = "";
 
418
                col_exclude.pack_start (cell_exclude_margin, false);
 
419
 
 
420
                CellRendererPixbuf cell_exclude_icon = new CellRendererPixbuf ();
 
421
                col_exclude.pack_start (cell_exclude_icon, false);
 
422
                col_exclude.set_attributes(cell_exclude_icon, "pixbuf", 1);
 
423
 
 
424
                CellRendererText cell_exclude_text = new CellRendererText ();
 
425
                col_exclude.pack_start (cell_exclude_text, false);
 
426
                col_exclude.set_cell_data_func (cell_exclude_text, cell_exclude_text_render);
 
427
                cell_exclude_text.editable = true;
 
428
                tv_exclude.append_column(col_exclude);
 
429
 
 
430
                cell_exclude_text.edited.connect (cell_exclude_text_edited);
 
431
 
 
432
                //lnk_default_list
 
433
                lnk_default_list = new LinkButton.with_label("",_("Some locations are excluded by default"));
 
434
                lnk_default_list.xalign = (float) 0.0;
 
435
                lnk_default_list.activate_link.connect(lnk_default_list_activate);
 
436
                vbox_exclude.add(lnk_default_list);
 
437
 
 
438
                //Actions ----------------------------------------------
 
439
 
 
440
                //hbox_action
 
441
        hbox_action = (Box) get_action_area ();
 
442
 
 
443
        //btn_restore
 
444
        btn_restore = new Button();
 
445
        hbox_action.add(btn_restore);
 
446
 
 
447
        btn_restore.set_label (" " + _("Restore"));
 
448
        btn_restore.set_tooltip_text (_("Restore"));
 
449
        Gtk.Image img_restore = new Image.from_stock("gtk-go-forward", Gtk.IconSize.BUTTON);
 
450
                btn_restore.set_image(img_restore);
 
451
        btn_restore.clicked.connect (btn_restore_clicked);
 
452
 
 
453
        //btn_cancel
 
454
        btn_cancel = new Button();
 
455
        hbox_action.add(btn_cancel);
 
456
 
 
457
        btn_cancel.set_label (" " + _("Cancel"));
 
458
        btn_cancel.set_tooltip_text (_("Cancel"));
 
459
        Gtk.Image img_cancel = new Image.from_stock("gtk-cancel", Gtk.IconSize.BUTTON);
 
460
                btn_cancel.set_image(img_cancel);
 
461
        btn_cancel.clicked.connect (btn_cancel_clicked);
 
462
 
 
463
                //initialize -----------------------------------------
 
464
 
 
465
                btn_reset_exclude_list_clicked();
 
466
 
 
467
                refresh_tv_partitions();
 
468
                refresh_cmb_boot_device();
 
469
                //refresh_tv_exclude(); //called by btn_reset_exclude_list_clicked()
 
470
                refresh_tv_apps();
 
471
 
 
472
                sw_partitions.sensitive = radio_other.active;
 
473
 
 
474
                notebook.switch_page.connect_after((page, new_page_index) => {
 
475
                        if (new_page_index == 1){
 
476
                                bool ok = check_and_mount_devices();
 
477
                                if (!ok){
 
478
                                        notebook.set_current_page(0);
 
479
                                        gtk_do_events();
 
480
                                        return;
 
481
                                }
 
482
 
 
483
                                //save current app selections
 
484
                                Gee.ArrayList<string> selected_app_list = new Gee.ArrayList<string>();
 
485
                                foreach(AppExcludeEntry entry in App.exclude_list_apps){
 
486
                                        if (entry.enabled){
 
487
                                                selected_app_list.add(entry.relpath);
 
488
                                        }
 
489
                                }
 
490
 
 
491
                                //refresh the list
 
492
                                App.add_app_exclude_entries();
 
493
 
 
494
                                //restore app selections
 
495
                                foreach(AppExcludeEntry entry in App.exclude_list_apps){
 
496
                                        if (selected_app_list.contains(entry.relpath)){
 
497
                                                entry.enabled = true;
 
498
                                        }
 
499
                                }
 
500
 
 
501
                                //refresh treeview
 
502
                                refresh_tv_apps();
 
503
                        }
 
504
                });
 
505
 
 
506
                set_app_page_state();
 
507
 
 
508
                init_mounts();
 
509
 
 
510
        }
 
511
 
 
512
        private void init_device_list(Gtk.Box box){
 
513
                //tv_partitions
 
514
                tv_partitions = new TreeView();
 
515
                tv_partitions.get_selection().mode = SelectionMode.SINGLE;
 
516
                tv_partitions.set_rules_hint (true);
 
517
                tv_partitions.button_release_event.connect(tv_partitions_button_press_event);
 
518
 
 
519
                //sw_partitions
 
520
                sw_partitions = new ScrolledWindow(null, null);
 
521
                sw_partitions.set_shadow_type (ShadowType.ETCHED_IN);
 
522
                sw_partitions.add (tv_partitions);
 
523
                sw_partitions.expand = true;
 
524
                box.add(sw_partitions);
 
525
 
 
526
                //col_device
 
527
                col_device_target = new TreeViewColumn();
 
528
                col_device_target.title = _("Device");
 
529
                col_device_target.spacing = 1;
 
530
                tv_partitions.append_column(col_device_target);
 
531
 
 
532
                CellRendererPixbuf cell_device_icon = new CellRendererPixbuf();
 
533
                cell_device_icon.xpad = 1;
 
534
                col_device_target.pack_start (cell_device_icon, false);
 
535
                col_device_target.set_attributes(cell_device_icon, "pixbuf", 3);
 
536
 
 
537
                CellRendererText cell_device_target = new CellRendererText ();
 
538
                col_device_target.pack_start (cell_device_target, false);
 
539
                col_device_target.set_cell_data_func (cell_device_target, cell_device_target_render);
 
540
 
 
541
                //col_fs
 
542
                col_fs = new TreeViewColumn();
 
543
                col_fs.title = _("Type");
 
544
                CellRendererText cell_fs = new CellRendererText ();
 
545
                cell_fs.xalign = (float) 0.5;
 
546
                col_fs.pack_start (cell_fs, false);
 
547
                col_fs.set_cell_data_func (cell_fs, cell_fs_render);
 
548
                tv_partitions.append_column(col_fs);
 
549
 
 
550
                //col_mount
 
551
                col_mount = new TreeViewColumn();
 
552
                col_mount.title = _("Mount");
 
553
                cell_mount = new CellRendererCombo();
 
554
                cell_mount.xalign = (float) 0.0;
 
555
                cell_mount.editable = true;
 
556
                cell_mount.width = 70;
 
557
                col_mount.pack_start (cell_mount, false);
 
558
                col_mount.set_cell_data_func (cell_mount, cell_mount_render);
 
559
                tv_partitions.append_column(col_mount);
 
560
 
 
561
                cell_mount.set_property ("text-column", 0);
 
562
                col_mount.add_attribute (cell_mount, "text", 1);
 
563
 
 
564
                //populate combo
 
565
                var model = new Gtk.ListStore(1, typeof(string));
 
566
                cell_mount.model = model;
 
567
 
 
568
                TreeIter iter;
 
569
                model.append(out iter);
 
570
                model.set (iter, 0, "/");
 
571
                model.append(out iter);
 
572
                model.set (iter, 0, "/home");
 
573
                model.append(out iter);
 
574
                model.set (iter, 0, "/boot");
 
575
 
 
576
                cell_mount.changed.connect((path, iter_new) => {
 
577
                        string val;
 
578
                        cell_mount.model.get (iter_new, 0, out val);
 
579
                        model = (Gtk.ListStore) tv_partitions.model;
 
580
                        model.get_iter_from_string (out iter, path);
 
581
                        model.set (iter, 1, val);
 
582
                });
 
583
 
 
584
                cell_mount.edited.connect((path, new_text) => {
 
585
                        model = (Gtk.ListStore) tv_partitions.model;
 
586
                        model.get_iter_from_string (out iter, path);
 
587
                        model.set (iter, 1, new_text);
 
588
                });
 
589
 
 
590
                //col_size
 
591
                col_size = new TreeViewColumn();
 
592
                col_size.title = _("Size");
 
593
                CellRendererText cell_size = new CellRendererText ();
 
594
                cell_size.xalign = (float) 1.0;
 
595
                col_size.pack_start (cell_size, false);
 
596
                col_size.set_cell_data_func (cell_size, cell_size_render);
 
597
                tv_partitions.append_column(col_size);
 
598
 
 
599
                //col_dist
 
600
                col_dist = new TreeViewColumn();
 
601
                col_dist.title = _("System");
 
602
                CellRendererText cell_dist = new CellRendererText ();
 
603
                col_dist.pack_start (cell_dist, false);
 
604
                col_dist.set_cell_data_func (cell_dist, cell_dist_render);
 
605
                tv_partitions.append_column(col_dist);
 
606
 
 
607
                tv_partitions.set_tooltip_column(2);
 
608
        }
 
609
        
 
610
        private void init_mounts(){
 
611
                TreeIter iter;
 
612
                Gtk.ListStore store;
 
613
 
 
614
                App.init_mount_list();
 
615
 
 
616
                if (App.mirror_system){
 
617
                        //default all mount points to root device except /boot
 
618
                        for(int i = App.mount_list.size - 1; i >= 0; i--){
 
619
                                MountEntry mnt = App.mount_list[i];
 
620
                                if (mnt.mount_point != "/boot"){
 
621
                                        App.mount_list.remove_at(i);
 
622
                                }
 
623
                        }
 
624
 
 
625
                        /* Note:
 
626
                         * While cloning the system, /boot is the only mount point that we will leave unchanged (to avoid encrypted systems from breaking)
 
627
                         * All other mounts like /home will be defaulted to target device (to prevent the "cloned" system from using the original device)
 
628
                         * */
 
629
                }
 
630
 
 
631
                //find the root mount point set by user
 
632
                store = (Gtk.ListStore) tv_partitions.model;
 
633
                for (bool next = store.get_iter_first (out iter); next; next = store.iter_next (ref iter)) {
 
634
                        Device pi;
 
635
                        string mount_point;
 
636
                        store.get(iter, 0, out pi);
 
637
                        store.get(iter, 1, out mount_point);
 
638
 
 
639
                        foreach(MountEntry mnt in App.mount_list){
 
640
                                if (mnt.device.device == pi.device){
 
641
                                        store.set(iter, 1, mnt.mount_point, -1);
 
642
                                }
 
643
                        }
 
644
                }
 
645
        }
 
646
 
 
647
        private void set_app_page_state(){
 
648
                if (App.restore_target == null){
 
649
                        lbl_app.sensitive = false;
 
650
                        vbox_app.sensitive = false;
 
651
                }
 
652
                else{
 
653
                        lbl_app.sensitive = true;
 
654
                        vbox_app.sensitive = true;
 
655
                }
 
656
        }
 
657
 
 
658
        private void cell_device_target_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
659
                Device pi;
 
660
                model.get (iter, 0, out pi, -1);
 
661
 
 
662
                string symlink = "";
 
663
                foreach(string sym in pi.symlinks){
 
664
                        if (sym.has_prefix("/dev/mapper/")){
 
665
                                symlink = sym.replace("/dev/mapper/","");
 
666
                        }
 
667
                }
 
668
 
 
669
                string txt = pi.device;
 
670
                
 
671
                if ((App.root_device != null) && (pi.device == App.root_device.device)){
 
672
                        txt += " (" + _("sys") + ")";
 
673
                }
 
674
 
 
675
                if (symlink.length > 0){
 
676
                        txt += " → ";
 
677
                        if (symlink.length > 10){
 
678
                                txt += symlink[0:10] + "...";
 
679
                        }
 
680
                        else{
 
681
                                txt += symlink;
 
682
                        }
 
683
                }
 
684
                
 
685
                Gtk.CellRendererText ctxt = (cell as Gtk.CellRendererText);
 
686
                ctxt.text = txt;
 
687
                set_cell_text_color(ref ctxt);
 
688
        }
 
689
 
 
690
        private void set_cell_text_color(ref CellRendererText cell){
 
691
                string span = "<span>";
 
692
                if (!sw_partitions.sensitive){
 
693
                        span = "<span foreground=\"#585858\">";
 
694
                }
 
695
                cell.markup = span + cell.text + "</span>";
 
696
        }
 
697
 
 
698
        private void cell_fs_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
699
                Device pi;
 
700
                model.get (iter, 0, out pi, -1);
 
701
                (cell as Gtk.CellRendererText).text = pi.type;
 
702
                Gtk.CellRendererText ctxt = (cell as Gtk.CellRendererText);
 
703
                set_cell_text_color(ref ctxt);
 
704
        }
 
705
 
 
706
        private void cell_size_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
707
                Device pi;
 
708
                model.get (iter, 0, out pi, -1);
 
709
                (cell as Gtk.CellRendererText).text = (pi.size_bytes > 0) ? "%s GB".printf(pi.size) : "";
 
710
                Gtk.CellRendererText ctxt = (cell as Gtk.CellRendererText);
 
711
                set_cell_text_color(ref ctxt);
 
712
        }
 
713
 
 
714
        private void cell_mount_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
715
                (cell as Gtk.CellRendererCombo).background = "#F2F5A9";
 
716
        }
 
717
 
 
718
        private void cell_dist_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
719
                Device pi;
 
720
                model.get (iter, 0, out pi, -1);
 
721
                (cell as Gtk.CellRendererText).text = pi.dist_info;
 
722
                Gtk.CellRendererText ctxt = (cell as Gtk.CellRendererText);
 
723
                set_cell_text_color(ref ctxt);
 
724
        }
 
725
 
 
726
 
 
727
        private void cell_device_grub_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
728
                Device dev;
 
729
                model.get (iter, 0, out dev, -1);
 
730
                if (dev.devtype == "disk"){
 
731
                        (cell as Gtk.CellRendererText).markup = "<b>" + dev.description() + " (MBR)</b>";
 
732
                }
 
733
                else{
 
734
                        (cell as Gtk.CellRendererText).markup = dev.description();
 
735
                }
 
736
        }
 
737
 
 
738
        private void cell_exclude_text_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
739
                string pattern;
 
740
                model.get (iter, 0, out pattern, -1);
 
741
                (cell as Gtk.CellRendererText).text = pattern.has_prefix("+ ") ? pattern[2:pattern.length] : pattern;
 
742
        }
 
743
 
 
744
        private void cell_exclude_text_edited (string path, string new_text) {
 
745
                string old_pattern;
 
746
                string new_pattern;
 
747
 
 
748
                TreeIter iter;
 
749
                var model = (Gtk.ListStore) tv_exclude.model;
 
750
                model.get_iter_from_string (out iter, path);
 
751
                model.get (iter, 0, out old_pattern, -1);
 
752
 
 
753
                if (old_pattern.has_prefix("+ ")){
 
754
                        new_pattern = "+ " + new_text;
 
755
                }
 
756
                else{
 
757
                        new_pattern = new_text;
 
758
                }
 
759
                model.set (iter, 0, new_pattern);
 
760
 
 
761
                int index = temp_exclude_list.index_of(old_pattern);
 
762
                temp_exclude_list.insert(index, new_pattern);
 
763
                temp_exclude_list.remove(old_pattern);
 
764
        }
 
765
 
 
766
        private void cell_app_enabled_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
767
                AppExcludeEntry entry;
 
768
                model.get (iter, 0, out entry, -1);
 
769
                (cell as Gtk.CellRendererToggle).active = entry.enabled;
 
770
        }
 
771
 
 
772
        private void cell_app_text_render (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter){
 
773
                AppExcludeEntry entry;
 
774
                model.get (iter, 0, out entry, -1);
 
775
                (cell as Gtk.CellRendererText).text = entry.relpath;
 
776
        }
 
777
 
 
778
        private void cell_app_enabled_toggled (string path){
 
779
                AppExcludeEntry entry;
 
780
                TreeIter iter;
 
781
                var model = (Gtk.ListStore) tv_app.model; //get model
 
782
                model.get_iter_from_string (out iter, path); //get selected iter
 
783
                model.get (iter, 0, out entry, -1); //get entry
 
784
                entry.enabled = !entry.enabled;
 
785
        }
 
786
 
 
787
        private void refresh_cmb_boot_device(){
 
788
                var store = new Gtk.ListStore(1, typeof(Device));
 
789
 
 
790
                //add devices
 
791
                Gee.ArrayList<Device> device_list = new Gee.ArrayList<Device>();
 
792
                foreach(Device di in get_block_devices()) {
 
793
                        device_list.add(di);
 
794
                }
 
795
 
 
796
                //add partitions
 
797
                foreach(Device pi in App.partitions) {
 
798
                        if (!pi.has_linux_filesystem()) { continue; }
 
799
                        if (pi.device.has_prefix("/dev/dm-")) { continue; }
 
800
                        device_list.add(pi);
 
801
                }
 
802
 
 
803
                //sort
 
804
                device_list.sort((a,b) => {
 
805
                                        Device p1 = (Device) a;
 
806
                                        Device p2 = (Device) b;
 
807
 
 
808
                                        return strcmp(p1.device,p2.device);
 
809
                                });
 
810
 
 
811
                TreeIter iter;
 
812
                foreach(Device entry in device_list) {
 
813
                        store.append(out iter);
 
814
                        store.set (iter, 0, entry);
 
815
                }
 
816
 
 
817
                cmb_boot_device.set_model (store);
 
818
                cmb_boot_device_select_default();
 
819
        }
 
820
 
 
821
        private void cmb_boot_device_select_default(){
 
822
                if (App.restore_target == null){
 
823
                        cmb_boot_device.active = -1;
 
824
                        return;
 
825
                }
 
826
 
 
827
                TreeIter iter;
 
828
                var store = (Gtk.ListStore) cmb_boot_device.model;
 
829
                int index = -1;
 
830
 
 
831
                int first_mbr_device_index = -1;
 
832
                for (bool next = store.get_iter_first (out iter); next; next = store.iter_next (ref iter)) {
 
833
                        Device dev;
 
834
                        store.get(iter, 0, out dev);
 
835
 
 
836
                        index++;
 
837
 
 
838
                        if (dev.device == App.restore_target.device[0:8]){
 
839
                                cmb_boot_device.active = index;
 
840
                                break;
 
841
                        }
 
842
 
 
843
                        if ((first_mbr_device_index == -1) && (dev.device.length == "/dev/sdX".length)){
 
844
                                first_mbr_device_index = index;
 
845
                        }
 
846
                }
 
847
 
 
848
                //select first MBR device if not found
 
849
                if (cmb_boot_device.active == -1){
 
850
                        cmb_boot_device.active = first_mbr_device_index;
 
851
                }
 
852
        }
 
853
 
 
854
        private void refresh_tv_partitions(){
 
855
 
 
856
                App.update_partitions();
 
857
 
 
858
                var model = new Gtk.ListStore(4, typeof(Device), typeof(string), typeof(string), typeof(Gdk.Pixbuf));
 
859
                tv_partitions.set_model (model);
 
860
 
 
861
                TreeIter iter;
 
862
                foreach(Device pi in App.partitions) {
 
863
                        if (!pi.has_linux_filesystem()) { continue; }
 
864
                        if (!radio_sys.sensitive && (App.root_device != null) && ((pi.device == App.root_device.device)||(pi.uuid == App.root_device.uuid))) { continue; }
 
865
 
 
866
                        string tt = "";
 
867
                        tt += "%-7s".printf(_("Device")) + "\t: %s\n".printf(pi.full_name_with_alias);
 
868
                        tt += "%-7s".printf(_("UUID")) + "\t: %s\n".printf(pi.uuid);
 
869
                        tt += "%-7s".printf(_("Type")) + "\t: %s\n".printf(pi.fstype);
 
870
                        tt += "%-7s".printf(_("Label")) + "\t: %s\n".printf(pi.label);
 
871
                        tt += "%-7s".printf(_("Size")) + "\t: %s\n".printf((pi.size_bytes > 0) ? "%s GB".printf(pi.size) : "");
 
872
                        tt += "%-7s".printf(_("Used")) + "\t: %s\n".printf((pi.used_bytes > 0) ? "%s GB".printf(pi.used) : "");
 
873
                        tt += "%-7s".printf(_("System")) + "\t: %s".printf(pi.dist_info);
 
874
 
 
875
                        model.append(out iter);
 
876
                        model.set (iter,0,pi,1,"",2,tt);
 
877
 
 
878
                        //set icon ----------------
 
879
 
 
880
                        Gdk.Pixbuf pix_selected = null;
 
881
                        Gdk.Pixbuf pix_device = get_shared_icon("disk","disk.png",16).pixbuf;
 
882
                        Gdk.Pixbuf pix_locked = get_shared_icon("locked","locked.svg",16).pixbuf;
 
883
 
 
884
                        if (pi.type == "luks"){
 
885
                                pix_selected = pix_locked;
 
886
                        }
 
887
                        else{
 
888
                                pix_selected = pix_device;
 
889
                        }
 
890
 
 
891
                        model.set (iter, 3, pix_selected, -1);
 
892
                }
 
893
 
 
894
                tv_partitions_select_target();
 
895
        }
 
896
 
 
897
        private void tv_partitions_select_target(){
 
898
 
 
899
                if (App.restore_target == null){
 
900
                        tv_partitions.get_selection().unselect_all();
 
901
                        return;
 
902
                }
 
903
 
 
904
                TreeIter iter;
 
905
                var store = (Gtk.ListStore) tv_partitions.model;
 
906
 
 
907
                for (bool next = store.get_iter_first (out iter); next; next = store.iter_next (ref iter)) {
 
908
                        Device pi;
 
909
                        string mount_point;
 
910
                        store.get(iter, 0, out pi);
 
911
                        store.get(iter, 1, out mount_point);
 
912
                        if (pi.device == App.restore_target.device){
 
913
                                TreePath path = store.get_path(iter);
 
914
                                tv_partitions.get_selection().select_path(path);
 
915
                        }
 
916
                }
 
917
        }
 
918
 
 
919
        private void refresh_tv_exclude(){
 
920
                var model = new Gtk.ListStore(2, typeof(string), typeof(Gdk.Pixbuf));
 
921
                tv_exclude.model = model;
 
922
 
 
923
                foreach(string path in temp_exclude_list){
 
924
                        tv_exclude_add_item(path);
 
925
                }
 
926
        }
 
927
 
 
928
        private void refresh_tv_apps(){
 
929
                var model = new Gtk.ListStore(1, typeof(AppExcludeEntry));
 
930
                tv_app.model = model;
 
931
 
 
932
                foreach(AppExcludeEntry entry in App.exclude_list_apps){
 
933
                        TreeIter iter;
 
934
                        model.append(out iter);
 
935
                        model.set (iter, 0, entry, -1);
 
936
                }
 
937
        }
 
938
 
 
939
        private void tv_exclude_add_item(string path){
 
940
                Gdk.Pixbuf pix_exclude = null;
 
941
                Gdk.Pixbuf pix_include = null;
 
942
                Gdk.Pixbuf pix_selected = null;
 
943
 
 
944
                try{
 
945
                        pix_exclude = new Gdk.Pixbuf.from_file (App.share_folder + "/timeshift/images/item-gray.png");
 
946
                        pix_include = new Gdk.Pixbuf.from_file (App.share_folder + "/timeshift/images/item-blue.png");
 
947
                }
 
948
        catch(Error e){
 
949
                log_error (e.message);
 
950
            }
 
951
 
 
952
                TreeIter iter;
 
953
                var model = (Gtk.ListStore) tv_exclude.model;
 
954
                model.append(out iter);
 
955
 
 
956
                if (path.has_prefix("+ ")){
 
957
                        pix_selected = pix_include;
 
958
                }
 
959
                else{
 
960
                        pix_selected = pix_exclude;
 
961
                }
 
962
 
 
963
                model.set (iter, 0, path, 1, pix_selected, -1);
 
964
 
 
965
                Adjustment adj = tv_exclude.get_hadjustment();
 
966
                adj.value = adj.upper;
 
967
        }
 
968
 
 
969
        private bool lnk_default_list_activate(){
 
970
                //show message window -----------------
 
971
                var dialog = new ExcludeMessageWindow();
 
972
                dialog.set_transient_for (this);
 
973
                dialog.show_all();
 
974
                dialog.run();
 
975
                dialog.destroy();
 
976
                return true;
 
977
        }
 
978
 
 
979
        private bool tv_partitions_button_press_event(Gdk.EventButton event){
 
980
                TreeIter iter;
 
981
                Gtk.ListStore store;
 
982
                TreeSelection sel;
 
983
                bool iterExists;
 
984
 
 
985
                //get selected target device
 
986
                Device restore_target = null;
 
987
                sel = tv_partitions.get_selection ();
 
988
                store = (Gtk.ListStore) tv_partitions.model;
 
989
                iterExists = store.get_iter_first (out iter);
 
990
                while (iterExists) {
 
991
                        if (sel.iter_is_selected (iter)){
 
992
                                store.get (iter, 0, out restore_target);
 
993
                                break;
 
994
                        }
 
995
                        iterExists = store.iter_next (ref iter);
 
996
                }
 
997
                App.restore_target = restore_target;
 
998
 
 
999
                //select grub device
 
1000
                if (selected_target == null){
 
1001
                        cmb_boot_device_select_default();
 
1002
                }
 
1003
                else if (selected_target.device != restore_target.device){
 
1004
                        cmb_boot_device_select_default();
 
1005
                }
 
1006
                else{
 
1007
                        //target device has not changed - do not reset to default boot device
 
1008
                }
 
1009
                selected_target = restore_target;
 
1010
 
 
1011
                set_app_page_state();
 
1012
 
 
1013
                return false;
 
1014
        }
 
1015
 
 
1016
 
 
1017
        private void menu_exclude_add_files_clicked(){
 
1018
 
 
1019
                var list = browse_files();
 
1020
 
 
1021
                if (list.length() > 0){
 
1022
                        foreach(string path in list){
 
1023
                                if (!temp_exclude_list.contains(path)){
 
1024
                                        temp_exclude_list.add(path);
 
1025
                                        tv_exclude_add_item(path);
 
1026
                                        App.first_snapshot_size = 0; //re-calculate
 
1027
                                }
 
1028
                        }
 
1029
                }
 
1030
        }
 
1031
 
 
1032
        private void menu_exclude_add_folder_clicked(){
 
1033
 
 
1034
                var list = browse_folder();
 
1035
 
 
1036
                if (list.length() > 0){
 
1037
                        foreach(string path in list){
 
1038
 
 
1039
                                path = path + "/";
 
1040
 
 
1041
                                if (!temp_exclude_list.contains(path)){
 
1042
                                        temp_exclude_list.add(path);
 
1043
                                        tv_exclude_add_item(path);
 
1044
                                        App.first_snapshot_size = 0; //re-calculate
 
1045
                                }
 
1046
                        }
 
1047
                }
 
1048
        }
 
1049
 
 
1050
        private void menu_exclude_add_folder_contents_clicked(){
 
1051
 
 
1052
                var list = browse_folder();
 
1053
 
 
1054
                if (list.length() > 0){
 
1055
                        foreach(string path in list){
 
1056
 
 
1057
                                path = path + "/*";
 
1058
 
 
1059
                                if (!temp_exclude_list.contains(path)){
 
1060
                                        temp_exclude_list.add(path);
 
1061
                                        tv_exclude_add_item(path);
 
1062
                                        App.first_snapshot_size = 0; //re-calculate
 
1063
                                }
 
1064
                        }
 
1065
                }
 
1066
        }
 
1067
 
 
1068
        private void menu_include_add_files_clicked(){
 
1069
 
 
1070
                var list = browse_files();
 
1071
 
 
1072
                if (list.length() > 0){
 
1073
                        foreach(string path in list){
 
1074
 
 
1075
                                path = path.has_prefix("+ ") ? path : "+ " + path;
 
1076
 
 
1077
                                if (!temp_exclude_list.contains(path)){
 
1078
                                        temp_exclude_list.add(path);
 
1079
                                        tv_exclude_add_item(path);
 
1080
                                        App.first_snapshot_size = 0; //re-calculate
 
1081
                                }
 
1082
                        }
 
1083
                }
 
1084
        }
 
1085
 
 
1086
        private void menu_include_add_folder_clicked(){
 
1087
 
 
1088
                var list = browse_folder();
 
1089
 
 
1090
                if (list.length() > 0){
 
1091
                        foreach(string path in list){
 
1092
 
 
1093
                                path = path.has_prefix("+ ") ? path : "+ " + path;
 
1094
                                path = path + "/***";
 
1095
 
 
1096
                                if (!temp_exclude_list.contains(path)){
 
1097
                                        temp_exclude_list.add(path);
 
1098
                                        tv_exclude_add_item(path);
 
1099
                                        App.first_snapshot_size = 0; //re-calculate
 
1100
                                }
 
1101
                        }
 
1102
                }
 
1103
        }
 
1104
 
 
1105
        private SList<string> browse_files(){
 
1106
                var dialog = new Gtk.FileChooserDialog(_("Select file(s)"), this, Gtk.FileChooserAction.OPEN,
 
1107
                                                        "gtk-cancel", Gtk.ResponseType.CANCEL,
 
1108
                                                        "gtk-open", Gtk.ResponseType.ACCEPT);
 
1109
                dialog.action = FileChooserAction.OPEN;
 
1110
                dialog.set_transient_for(this);
 
1111
                dialog.local_only = true;
 
1112
                dialog.set_modal (true);
 
1113
                dialog.set_select_multiple (true);
 
1114
 
 
1115
                dialog.run();
 
1116
                var list = dialog.get_filenames();
 
1117
                dialog.destroy ();
 
1118
 
 
1119
                return list;
 
1120
        }
 
1121
 
 
1122
        private SList<string> browse_folder(){
 
1123
                var dialog = new Gtk.FileChooserDialog(_("Select directory"), this, Gtk.FileChooserAction.OPEN,
 
1124
                                                        "gtk-cancel", Gtk.ResponseType.CANCEL,
 
1125
                                                        "gtk-open", Gtk.ResponseType.ACCEPT);
 
1126
                dialog.action = FileChooserAction.SELECT_FOLDER;
 
1127
                dialog.local_only = true;
 
1128
                dialog.set_transient_for(this);
 
1129
                dialog.set_modal (true);
 
1130
                dialog.set_select_multiple (false);
 
1131
 
 
1132
                dialog.run();
 
1133
                var list = dialog.get_filenames();
 
1134
                dialog.destroy ();
 
1135
 
 
1136
                return list;
 
1137
        }
 
1138
 
 
1139
 
 
1140
        private void btn_remove_clicked(){
 
1141
                TreeSelection sel = tv_exclude.get_selection ();
 
1142
                TreeIter iter;
 
1143
                bool iterExists = tv_exclude.model.get_iter_first (out iter);
 
1144
                while (iterExists) {
 
1145
                        if (sel.iter_is_selected (iter)){
 
1146
                                string path;
 
1147
                                tv_exclude.model.get (iter, 0, out path);
 
1148
                                temp_exclude_list.remove(path);
 
1149
                                App.first_snapshot_size = 0; //re-calculate
 
1150
                        }
 
1151
                        iterExists = tv_exclude.model.iter_next (ref iter);
 
1152
                }
 
1153
 
 
1154
                refresh_tv_exclude();
 
1155
        }
 
1156
 
 
1157
        private void btn_warning_clicked(){
 
1158
                string msg = "";
 
1159
                msg += _("By default, any item that was included/excluded at the time of taking the snapshot will be included/excluded.") + " ";
 
1160
                msg += _("Any exclude patterns in the current exclude list will also be excluded.") + " ";
 
1161
                msg += _("To see which files are included in the snapshot use the 'Browse' button on the main window.");
 
1162
 
 
1163
                var dialog = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, msg);
 
1164
                dialog.set_title("Warning");
 
1165
                dialog.set_default_size (200, -1);
 
1166
                dialog.set_transient_for(this);
 
1167
                dialog.set_modal(true);
 
1168
                dialog.run();
 
1169
                dialog.destroy();
 
1170
        }
 
1171
 
 
1172
        private void btn_reset_exclude_list_clicked(){
 
1173
                //create a temp exclude list
 
1174
                temp_exclude_list = new Gee.ArrayList<string>();
 
1175
 
 
1176
                //add all include/exclude items from snapshot list
 
1177
                if (App.snapshot_to_restore != null){
 
1178
                        foreach(string path in App.snapshot_to_restore.exclude_list){
 
1179
                                if (!temp_exclude_list.contains(path) && !App.exclude_list_default.contains(path) && !App.exclude_list_home.contains(path)){
 
1180
                                        temp_exclude_list.add(path);
 
1181
                                }
 
1182
                        }
 
1183
                }
 
1184
 
 
1185
                //add all exclude items from current list
 
1186
                foreach(string path in App.exclude_list_user){
 
1187
                        if (!temp_exclude_list.contains(path) && !App.exclude_list_default.contains(path) && !App.exclude_list_home.contains(path)){
 
1188
 
 
1189
                                if (!path.has_prefix("+ ")){            //don't add include entries from current exclude list
 
1190
                                        temp_exclude_list.add(path);
 
1191
                                }
 
1192
                        }
 
1193
                }
 
1194
 
 
1195
                //refresh treeview
 
1196
                refresh_tv_exclude();
 
1197
        }
 
1198
 
 
1199
        private void btn_restore_clicked(){
 
1200
                //check if backup device is online
 
1201
                if (!check_backup_device_online()) { return; }
 
1202
 
 
1203
                //Note: A successful restore will reboot the system if target device is same as system device
 
1204
 
 
1205
                bool ok = check_and_mount_devices();
 
1206
                if (!ok){
 
1207
                        return;
 
1208
                }
 
1209
 
 
1210
                //save grub install options ----------------------
 
1211
 
 
1212
                App.reinstall_grub2 = !chk_skip_grub_install.active;
 
1213
 
 
1214
                TreeIter iter;
 
1215
                if (App.reinstall_grub2){
 
1216
                        Device entry;
 
1217
                        cmb_boot_device.get_active_iter (out iter);
 
1218
                        TreeModel model = (TreeModel) cmb_boot_device.model;
 
1219
                        model.get(iter, 0, out entry);
 
1220
                        App.grub_device = entry.device;
 
1221
                }
 
1222
                else{
 
1223
                        App.grub_device = "";
 
1224
                }
 
1225
 
 
1226
                //save modified exclude list ----------------------
 
1227
 
 
1228
                App.exclude_list_restore.clear();
 
1229
 
 
1230
                //add default entries
 
1231
                foreach(string path in App.exclude_list_default){
 
1232
                        if (!App.exclude_list_restore.contains(path)){
 
1233
                                App.exclude_list_restore.add(path);
 
1234
                        }
 
1235
                }
 
1236
 
 
1237
                //add app entries
 
1238
                foreach(AppExcludeEntry entry in App.exclude_list_apps){
 
1239
                        if (entry.enabled){
 
1240
                                string pattern = entry.pattern();
 
1241
                                if (!App.exclude_list_restore.contains(pattern)){
 
1242
                                        App.exclude_list_restore.add(pattern);
 
1243
                                }
 
1244
 
 
1245
                                pattern = entry.pattern(true);
 
1246
                                if (!App.exclude_list_restore.contains(pattern)){
 
1247
                                        App.exclude_list_restore.add(pattern);
 
1248
                                }
 
1249
                        }
 
1250
                }
 
1251
 
 
1252
                //add modified user entries
 
1253
                foreach(string path in temp_exclude_list){
 
1254
                        if (!App.exclude_list_restore.contains(path) && !App.exclude_list_home.contains(path)){
 
1255
                                App.exclude_list_restore.add(path);
 
1256
                        }
 
1257
                }
 
1258
 
 
1259
                //add home entries
 
1260
                foreach(string path in App.exclude_list_home){
 
1261
                        if (!App.exclude_list_restore.contains(path)){
 
1262
                                App.exclude_list_restore.add(path);
 
1263
                        }
 
1264
                }
 
1265
 
 
1266
                //exclude timeshift backups
 
1267
                string timeshift_path = "/timeshift/*";
 
1268
                if (!App.exclude_list_restore.contains(timeshift_path)){
 
1269
                        App.exclude_list_restore.add(timeshift_path);
 
1270
                }
 
1271
 
 
1272
                //exclude boot directory if grub install is skipped
 
1273
                if (!App.reinstall_grub2){
 
1274
                        App.exclude_list_restore.add("/boot/*");
 
1275
                }
 
1276
 
 
1277
                //display and confirm mount points ------------
 
1278
 
 
1279
                if (!radio_sys.active){
 
1280
                        if (show_mount_list() != Gtk.ResponseType.OK){
 
1281
                                return;
 
1282
                        }
 
1283
                }
 
1284
 
 
1285
                //last option to quit - show disclaimer ------------
 
1286
 
 
1287
                if (show_disclaimer() == Gtk.ResponseType.YES){
 
1288
                        this.response(Gtk.ResponseType.OK);
 
1289
                }
 
1290
                else{
 
1291
                        this.response(Gtk.ResponseType.CANCEL);
 
1292
                }
 
1293
        }
 
1294
 
 
1295
        private bool check_backup_device_online(){
 
1296
                if (!App.backup_device_online()){
 
1297
                        gtk_messagebox(_("Device Offline"),_("Backup device is not available"), this, true);
 
1298
                        return false;
 
1299
                }
 
1300
                else{
 
1301
                        return true;
 
1302
                }
 
1303
        }
 
1304
 
 
1305
        private bool check_and_mount_devices(){
 
1306
                TreeIter iter;
 
1307
                Gtk.ListStore store;
 
1308
                TreeSelection sel;
 
1309
 
 
1310
                //check if target device selected ---------------
 
1311
 
 
1312
                if (radio_sys.active){
 
1313
                        //we are restoring the current system - no need to mount devices
 
1314
                        App.restore_target = App.root_device;
 
1315
                        return true;
 
1316
                }
 
1317
                else{
 
1318
                        //we are restoring to another disk - mount selected devices
 
1319
 
 
1320
                        App.restore_target = null;
 
1321
                        App.mount_list.clear();
 
1322
                        bool no_mount_points_set_by_user = true;
 
1323
 
 
1324
                        //find the root mount point set by user
 
1325
                        store = (Gtk.ListStore) tv_partitions.model;
 
1326
                        for (bool next = store.get_iter_first (out iter); next; next = store.iter_next (ref iter)) {
 
1327
                                Device pi;
 
1328
                                string mount_point;
 
1329
                                store.get(iter, 0, out pi);
 
1330
                                store.get(iter, 1, out mount_point);
 
1331
 
 
1332
                                if ((mount_point != null) && (mount_point.length > 0)){
 
1333
                                        mount_point = mount_point.strip();
 
1334
                                        no_mount_points_set_by_user = false;
 
1335
 
 
1336
                                        App.mount_list.add(new MountEntry(pi,mount_point));
 
1337
 
 
1338
                                        if (mount_point == "/"){
 
1339
                                                App.restore_target = pi;
 
1340
                                        }
 
1341
                                }
 
1342
                        }
 
1343
 
 
1344
                        if (App.restore_target == null){
 
1345
                                //no root mount point was set by user
 
1346
 
 
1347
                                if (no_mount_points_set_by_user){
 
1348
                                        //user has not set any mount points
 
1349
 
 
1350
                                        //check if a device is selected in treeview
 
1351
                                        sel = tv_partitions.get_selection ();
 
1352
                                        if (sel.count_selected_rows() == 1){
 
1353
                                                //use selected device as the root mount point
 
1354
                                                for (bool next = store.get_iter_first (out iter); next; next = store.iter_next (ref iter)) {
 
1355
                                                        if (sel.iter_is_selected (iter)){
 
1356
                                                                Device pi;
 
1357
                                                                store.get(iter, 0, out pi);
 
1358
                                                                App.restore_target = pi;
 
1359
                                                                App.mount_list.add(new MountEntry(pi,"/"));
 
1360
                                                                break;
 
1361
                                                        }
 
1362
                                                }
 
1363
                                        }
 
1364
                                        else{
 
1365
                                                //no device selected and no mount points set by user
 
1366
                                                string title = _("Select Target Device");
 
1367
                                                string msg = _("Please select the target device from the list");
 
1368
                                                gtk_messagebox(title, msg, this, true);
 
1369
                                                return false;
 
1370
                                        }
 
1371
                                }
 
1372
                                else{
 
1373
                                        //user has set some mount points but not set the root mount point
 
1374
                                        string title = _("Select Root Device");
 
1375
                                        string msg = _("Please select the root device (/)");
 
1376
                                        gtk_messagebox(title, msg, this, true);
 
1377
                                        return false;
 
1378
                                }
 
1379
                        }
 
1380
 
 
1381
                        //check BTRFS subvolume layout --------------
 
1382
 
 
1383
                        if (App.restore_target.type == "btrfs"){
 
1384
                                if (App.check_btrfs_volume(App.restore_target) == false){
 
1385
                                        string title = _("Unsupported Subvolume Layout");
 
1386
                                        string msg = _("The target partition has an unsupported subvolume layout.") + " ";
 
1387
                                        msg += _("Only ubuntu-type layouts with @ and @home subvolumes are currently supported.") + "\n\n";
 
1388
                                        gtk_messagebox(title, msg, this, true);
 
1389
                                        return false;
 
1390
                                }
 
1391
                        }
 
1392
 
 
1393
                        //mount target device -------------
 
1394
 
 
1395
                        bool status = App.mount_target_device(this);
 
1396
                        if (status == false){
 
1397
                                string title = _("Error");
 
1398
                                string msg = _("Failed to mount device") + ": %s".printf(App.restore_target.device);
 
1399
                                gtk_messagebox(title, msg, this, true);
 
1400
                                return false;
 
1401
                        }
 
1402
                }
 
1403
 
 
1404
                //check if grub device selected ---------------
 
1405
 
 
1406
                if (!chk_skip_grub_install.active && cmb_boot_device.active < 0){
 
1407
                        string title =_("Boot device not selected");
 
1408
                        string msg = _("Please select the boot device");
 
1409
                        gtk_messagebox(title, msg, this, true);
 
1410
                        return false;
 
1411
                }
 
1412
 
 
1413
                return true;
 
1414
        }
 
1415
 
 
1416
        private int show_disclaimer(){
 
1417
                string msg = App.disclaimer_pre_restore();
 
1418
                msg += "\n\n";
 
1419
                msg += "<b>" + _("Continue with restore?") + "</b>\n";
 
1420
 
 
1421
                var dialog = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, msg);
 
1422
                dialog.set_title(_("DISCLAIMER"));
 
1423
                dialog.set_default_size (200, -1);
 
1424
                dialog.set_transient_for(this);
 
1425
                dialog.set_modal(true);
 
1426
                int response = dialog.run();
 
1427
                dialog.destroy();
 
1428
                return response;
 
1429
        }
 
1430
 
 
1431
        private int show_mount_list(){
 
1432
                string msg = _("Following mounts will be used for restored system:") + "\n\n";
 
1433
 
 
1434
                int max_mount = _("Mount").length;
 
1435
                int max_dev = _("Device").length;
 
1436
 
 
1437
                foreach(MountEntry mnt in App.mount_list){
 
1438
                        string dev_name = mnt.device.short_name_with_alias;
 
1439
                        if (dev_name.length > max_dev){ max_dev = dev_name.length; }
 
1440
                        if (mnt.mount_point.length > max_mount){ max_mount = mnt.mount_point.length; }
 
1441
                }
 
1442
 
 
1443
                msg += "<tt>";
 
1444
                msg += "<b>";
 
1445
                msg += ("%%-%ds     %%-%ds\n\n".printf(max_dev, max_mount)).printf(_("Device"),_("Mount"));
 
1446
                msg += "</b>";
 
1447
                foreach(MountEntry mnt in App.mount_list){
 
1448
                        msg += ("%%-%ds     %%-%ds\n\n".printf(max_dev, max_mount)).printf(mnt.device.short_name_with_alias, mnt.mount_point);
 
1449
                }
 
1450
                msg += "</tt>";
 
1451
                msg += "\n" + _("Click OK to continue") + "\n";
 
1452
 
 
1453
                var dialog = new Gtk.MessageDialog.with_markup(null, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK_CANCEL, msg);
 
1454
                dialog.set_title(_("Confirm Mounts"));
 
1455
                dialog.set_default_size (200, -1);
 
1456
                dialog.set_transient_for(this);
 
1457
                dialog.set_modal(true);
 
1458
                int response = dialog.run();
 
1459
                dialog.destroy();
 
1460
                return response;
 
1461
        }
 
1462
 
 
1463
        private void btn_cancel_clicked(){
 
1464
                App.unmount_target_device();
 
1465
                this.response(Gtk.ResponseType.CANCEL);
 
1466
                return;
 
1467
        }
 
1468
 
 
1469
}