Skip to content
API Documentation
BastionGuard™
BackupWindow.vala
Go to the documentation of this file.
1/*
2 * BackupWindow.vala
3 *
4 * Copyright 2012-2018 Tony George <teejeetech@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
23using Gtk;
24using Gee;
25
26using TeeJee.Logging;
31using TeeJee.System;
32using TeeJee.Misc;
33
34class BackupWindow : Gtk.Window {
35
36 public signal void request_close();
37
38 private Gtk.Box root_box;
39 private Gtk.Box sidebar;
40 private Gtk.Box content_box;
41 private Gtk.Box header_box;
42 private Gtk.Box body_box;
43 private Gtk.Box bbox_action;
44 private Gtk.Stack page_stack;
45 private int current_page = -1;
46
47 // sidebar nav
48 private Gtk.Button btn_nav_estimate;
49 private Gtk.Button btn_nav_location;
50 private Gtk.Button btn_nav_backup;
51 private Gtk.Button btn_nav_finish;
52 private Gtk.Button? current_nav_btn = null;
53
54 private Gtk.Label lbl_page_title;
55
56 // tabs
61
62 // actions
63 private Gtk.Button btn_prev;
64 private Gtk.Button btn_next;
65 private Gtk.Button btn_cancel;
66 private Gtk.Button btn_pause;
67 private Gtk.Button btn_close;
68
69 private uint tmr_init;
70 private int def_width = 980;
71 private int def_height = 680;
72 private bool success = false;
73 private bool embedded_mode = false;
74
75 public BackupWindow(bool embedded = false) {
76 embedded_mode = embedded;
77
78 log_debug("BackupWindow: BackupWindow()");
79
80 this.title = _("Create Snapshot");
81 if (!embedded_mode) this.modal = true;
82 this.set_default_size(def_width, def_height);
83 this.set_default_size(def_width, def_height);
84 this.set_icon_name("bastionguard-backup");
85 if (!embedded_mode) this.close_request.connect(on_close_request);
86
87 if (!embedded_mode) this.set_titlebar(HeaderFactory.create_main_headerbar(this, 220));
88
89 build_ui();
92
93 if (!embedded_mode)
94 show();
95
96 tmr_init = Timeout.add(100, init_delayed);
97
98 log_debug("BackupWindow: BackupWindow(): exit");
99 }
100
101 private void build_ui() {
102
103 root_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
104 root_box.hexpand = true;
105 root_box.vexpand = true;
106 root_box.halign = Gtk.Align.FILL;
107 root_box.valign = Gtk.Align.FILL;
108 set_child(root_box);
109
112
113 root_box.append(sidebar);
114 root_box.append(content_box);
115 }
116
117 private void build_sidebar() {
118
119 sidebar = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
120 sidebar.set_size_request(220, -1);
121 sidebar.get_style_context().add_class("sidebar");
122 sidebar.hexpand = false;
123 sidebar.vexpand = true;
124 sidebar.halign = Gtk.Align.FILL;
125 sidebar.valign = Gtk.Align.FILL;
126
127 var sidebar_header = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 8);
128 sidebar_header.get_style_context().add_class("sidebar-header");
129 sidebar.append(sidebar_header);
130
131 Gtk.Widget logo_widget;
132 try {
133 logo_widget = new Gtk.Image.from_pixbuf(IconManager.lookup("bastionguard-backup", 48));
134 }
135 catch (Error e) {
136 logo_widget = themed_icon("document-save-symbolic", 48);
137 }
138
139 var title = new Gtk.Label(_("BastionGuard™\nBackup"));
140 title.xalign = 0.0f;
141 title.get_style_context().add_class("sidebar-title");
142
143 sidebar_header.append(logo_widget);
144 sidebar_header.append(title);
145
146 sidebar.append(new Gtk.Box(Gtk.Orientation.VERTICAL, 0));
147
148 btn_nav_estimate = make_nav_button("system-search-symbolic", _("Estimate"));
149 btn_nav_location = make_nav_button("drive-harddisk-symbolic", _("Location"));
150 btn_nav_backup = make_nav_button("document-save-symbolic", _("Backup"));
151 btn_nav_finish = make_nav_button("emblem-ok-symbolic", _("Finish"));
152
153 // flow lineare
154 btn_nav_estimate.sensitive = false;
155 btn_nav_location.sensitive = false;
156 btn_nav_backup.sensitive = false;
157 btn_nav_finish.sensitive = false;
158
159 var spacer = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
160 spacer.vexpand = true;
161 sidebar.append(spacer);
162 }
163
164 private Gtk.Button make_nav_button(string icon_name, string label_text) {
165
166 var btn = new Gtk.Button();
167 btn.get_style_context().add_class("sidebar-button");
168 btn.hexpand = true;
169 btn.margin_start = 8;
170 btn.margin_end = 8;
171 btn.margin_top = 2;
172 btn.margin_bottom = 2;
173
174 var row = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 10);
175 row.margin_start = 8;
176 row.margin_end = 8;
177 row.margin_top = 8;
178 row.margin_bottom = 8;
179
180 var img = themed_icon(icon_name, 18);
181 var lbl = new Gtk.Label(label_text);
182 lbl.xalign = 0.0f;
183
184 row.append(img);
185 row.append(lbl);
186
187 btn.set_child(row);
188 sidebar.append(btn);
189
190 return btn;
191 }
192
193 private void build_content() {
194
195 content_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
196 content_box.get_style_context().add_class("content");
197 content_box.hexpand = true;
198 content_box.vexpand = true;
199 content_box.halign = Gtk.Align.FILL;
200 content_box.valign = Gtk.Align.FILL;
201
202 header_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
203 header_box.margin_start = 20;
204 header_box.margin_end = 20;
205 header_box.margin_top = 20;
206 header_box.margin_bottom = 10;
207
208 lbl_page_title = new Gtk.Label(_("Create Snapshot"));
209 lbl_page_title.xalign = 0.0f;
210 lbl_page_title.get_style_context().add_class("title");
212
213 body_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
214 body_box.margin_start = 14;
215 body_box.margin_end = 14;
216 body_box.margin_bottom = 12;
217 body_box.hexpand = true;
218 body_box.vexpand = true;
219
220 page_stack = new Gtk.Stack();
221 page_stack.hexpand = true;
222 page_stack.vexpand = true;
223 page_stack.halign = Gtk.Align.FILL;
224 page_stack.valign = Gtk.Align.FILL;
225
226 body_box.append(page_stack);
227
228 content_box.append(header_box);
229 content_box.append(body_box);
230 }
231
232 private Gtk.Widget wrap_page(Gtk.Widget child) {
233 var card = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
234 card.get_style_context().add_class("card");
235 card.hexpand = true;
236 card.vexpand = true;
237 card.margin_start = 6;
238 card.margin_end = 6;
239 card.margin_top = 6;
240 card.margin_bottom = 6;
241 card.append(child);
242 return card;
243 }
244
245 private void build_pages() {
246
247 Gtk.Label label;
248
249 label = new Gtk.Label(_("Estimate"));
250 estimate_box = new EstimateBox(this);
251 estimate_box.margin_start = 12;
252 estimate_box.margin_end = 12;
253 estimate_box.margin_top = 12;
254 estimate_box.margin_bottom = 12;
255 page_stack.add_named(wrap_page(estimate_box), "estimate");
256
257 label = new Gtk.Label(_("Location"));
259 backup_dev_box.margin_start = 12;
260 backup_dev_box.margin_end = 12;
261 backup_dev_box.margin_top = 12;
262 backup_dev_box.margin_bottom = 12;
263 page_stack.add_named(wrap_page(backup_dev_box), "location");
264
265 label = new Gtk.Label(_("Backup"));
266 backup_box = new BackupBox(this);
267 backup_box.margin_start = 12;
268 backup_box.margin_end = 12;
269 backup_box.margin_top = 12;
270 backup_box.margin_bottom = 12;
271 page_stack.add_named(wrap_page(backup_box), "backup");
272
273 label = new Gtk.Label(_("Finish"));
275 backup_finish_box.margin_start = 12;
276 backup_finish_box.margin_end = 12;
277 backup_finish_box.margin_top = 12;
278 backup_finish_box.margin_bottom = 12;
279 page_stack.add_named(wrap_page(backup_finish_box), "finish");
280 }
281
282 private bool init_delayed() {
283
284 if (tmr_init > 0) {
285 Source.remove(tmr_init);
286 tmr_init = 0;
287 }
288
289 go_first();
290 return false;
291 }
292
293 private bool on_close_request() {
294 save_changes();
295 return false;
296 }
297
298 private void save_changes() {
300 }
301
302 private void create_actions() {
303
304 var hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
305 content_box.append(hbox);
306
307 var bbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 8);
308 bbox.margin_start = 12;
309 bbox.margin_end = 12;
310 bbox.margin_top = 12;
311 bbox.margin_bottom = 12;
312 bbox.spacing = 6;
313 bbox.hexpand = true;
314 hbox.append(bbox);
315
316 bbox_action = bbox;
317
318 #if GTK3_18
319 bbox.halign = Gtk.Align.CENTER;
320 #endif
321
322 Gtk.SizeGroup size_group = null;
323
324 btn_prev = add_button(bbox, _("Previous"), "", size_group, null);
325 btn_prev.get_style_context().add_class("btn-back");
326 btn_prev.clicked.connect(() => {
327 go_prev();
328 });
329
330 btn_next = add_button(bbox, _("Next"), "", size_group, null);
331 btn_next.get_style_context().add_class("btn-next");
332 btn_next.clicked.connect(() => {
333 go_next();
334 });
335
336 btn_close = add_button(bbox, _("Close"), "", size_group, null);
337 btn_close.get_style_context().add_class("btn-finish");
338 btn_close.clicked.connect(() => {
339 save_changes();
340 if (embedded_mode)
342 else
343 this.destroy();
344 });
345
346 btn_cancel = add_button(bbox, _("Cancel"), "", size_group, null);
347 btn_cancel.get_style_context().add_class("btn-cancel");
348 btn_cancel.clicked.connect(() => {
349 if (App.task != null) {
350 App.task.stop(AppStatus.CANCELLED);
351 }
352 if (embedded_mode)
354 else
355 this.destroy();
356 });
357
358 btn_pause = add_button(bbox, _("Pause"), "", size_group, null);
359 btn_pause.clicked.connect(() => {
360 if (App.task != null) {
361 if (AppStatus.PAUSED == App.task.status) {
362 App.task.resume();
363 this.backup_box.resume();
364 this.btn_pause.set_label(_("Pause"));
365 }
366 else {
367 App.task.pause();
368 this.backup_box.pause();
369 this.btn_pause.set_label(_("Resume"));
370 }
371 }
372 });
373
375 }
376
377 private void action_buttons_set_initial_visibility(bool visible) {
378
379 btn_prev.visible = visible;
380 btn_next.visible = visible;
381 btn_close.visible = visible;
382 btn_cancel.visible = visible;
383 btn_pause.visible = visible;
384 }
385
386 // navigation
387
388 private void go_first() {
389
390 if (App.btrfs_mode) {
391 set_page(Tabs.BACKUP);
392 }
393 else {
394 if (Main.first_snapshot_size == 0) {
395 set_page(Tabs.ESTIMATE);
396 }
397 else if (!App.repo.available() || !App.repo.has_space()) {
398 set_page(Tabs.BACKUP_DEVICE);
399 }
400 else {
401 set_page(Tabs.BACKUP);
402 }
403 }
404
406 }
407
408 private void go_prev() {
409
410 switch (current_page) {
411 case Tabs.ESTIMATE:
412 break;
413
414 case Tabs.BACKUP_DEVICE:
415 if (!App.btrfs_mode && Main.first_snapshot_size == 0) {
416 set_page(Tabs.ESTIMATE);
417 }
418 break;
419
420 case Tabs.BACKUP:
421 if (!App.btrfs_mode && (!App.repo.available() || !App.repo.has_space())) {
422 set_page(Tabs.BACKUP_DEVICE);
423 }
424 else if (!App.btrfs_mode && Main.first_snapshot_size == 0) {
425 set_page(Tabs.ESTIMATE);
426 }
427 break;
428
429 case Tabs.BACKUP_FINISH:
430 break;
431 }
432
434 }
435
436 private void go_next() {
437
438 if (!validate_current_tab()) {
439 return;
440 }
441
442 switch (current_page) {
443 case Tabs.ESTIMATE:
444 set_page(Tabs.BACKUP_DEVICE);
445 break;
446
447 case Tabs.BACKUP_DEVICE:
448 set_page(Tabs.BACKUP);
449 break;
450
451 case Tabs.BACKUP:
452 set_page(Tabs.BACKUP_FINISH);
453 break;
454
455 case Tabs.BACKUP_FINISH:
456 destroy();
457 break;
458 }
459
461 }
462
463 private void initialize_tab() {
464
465 if (current_page < 0) {
466 return;
467 }
468
469 log_msg("");
470 log_debug("page: %d".printf(current_page));
471
473
474 switch (current_page) {
475 case Tabs.ESTIMATE:
476 btn_prev.hide();
477 btn_next.hide();
478 btn_close.hide();
479 btn_cancel.show();
480 btn_pause.hide();
481 break;
482
483 case Tabs.BACKUP:
484 btn_prev.hide();
485 btn_next.hide();
486 btn_close.hide();
487 btn_cancel.show();
488 btn_pause.show();
489 break;
490
491 case Tabs.BACKUP_DEVICE:
492 btn_prev.show();
493 btn_next.show();
494 btn_close.show();
495 btn_cancel.hide();
496 btn_pause.hide();
497
498 btn_prev.sensitive = false;
499 btn_next.sensitive = true;
500 btn_close.sensitive = true;
501 break;
502
503 case Tabs.BACKUP_FINISH:
504 btn_prev.hide();
505 btn_next.hide();
506 btn_close.show();
507 btn_close.sensitive = true;
508 btn_cancel.hide();
509 btn_pause.hide();
510 break;
511 }
512
513 switch (current_page) {
514 case Tabs.ESTIMATE:
518 go_next();
519 return;
520
521 case Tabs.BACKUP_DEVICE:
525 go_next();
526 return;
527
528 case Tabs.BACKUP:
532 go_next();
533 return;
534
535 case Tabs.BACKUP_FINISH:
538
540
541 if (App.repo.status_code == SnapshotLocationStatus.HAS_SNAPSHOTS_NO_SPACE) {
542 this.hide();
543 gtk_messagebox(App.repo.status_message, App.repo.status_details, this, true);
544 if (embedded_mode)
546 else
547 this.destroy();
548 }
549 else {
551 wait_and_close_window(1000, this);
552 }
553 break;
554 }
555 }
556
557 private bool validate_current_tab() {
558
559 if (current_page == Tabs.BACKUP_DEVICE) {
560 if (!App.repo.available() || !App.repo.has_space()) {
561 gtk_messagebox(
564 this,
565 true
566 );
567 return false;
568 }
569 }
570
571 return true;
572 }
573
574 private void clear_sidebar_selection() {
575 btn_nav_estimate.get_style_context().remove_class("active-sidebar-button");
576 btn_nav_location.get_style_context().remove_class("active-sidebar-button");
577 btn_nav_backup.get_style_context().remove_class("active-sidebar-button");
578 btn_nav_finish.get_style_context().remove_class("active-sidebar-button");
579 }
580
581 private void set_sidebar_active(Gtk.Button btn) {
582 btn.get_style_context().add_class("active-sidebar-button");
583 current_nav_btn = btn;
584 }
585
586 private void update_sidebar_state() {
587
589
590 switch (current_page) {
591 case Tabs.ESTIMATE:
593 break;
594 case Tabs.BACKUP_DEVICE:
596 break;
597 case Tabs.BACKUP:
599 break;
600 case Tabs.BACKUP_FINISH:
602 break;
603 }
604 }
605
606 private void update_page_title() {
607
608 switch (current_page) {
609 case Tabs.ESTIMATE:
610 lbl_page_title.label = _("Estimate");
611 break;
612 case Tabs.BACKUP_DEVICE:
613 lbl_page_title.label = _("Backup Location");
614 break;
615 case Tabs.BACKUP:
616 lbl_page_title.label = _("Create Snapshot");
617 break;
618 case Tabs.BACKUP_FINISH:
619 lbl_page_title.label = _("Finished");
620 break;
621 }
622 }
623
624 private void set_page(int page) {
625 current_page = page;
626 switch (page) {
627 case Tabs.ESTIMATE:
628 page_stack.set_visible_child_name("estimate");
629 break;
630 case Tabs.BACKUP_DEVICE:
631 page_stack.set_visible_child_name("location");
632 break;
633 case Tabs.BACKUP:
634 page_stack.set_visible_child_name("backup");
635 break;
636 case Tabs.BACKUP_FINISH:
637 page_stack.set_visible_child_name("finish");
638 break;
639 }
640 }
641
642 public enum Tabs {
643 ESTIMATE = 0,
644 BACKUP_DEVICE = 1,
645 BACKUP = 2,
646 BACKUP_FINISH = 3
647 }
648
649 public Gtk.Widget detach_root_widget() {
650 set_child(null);
651 return root_box;
652 }
653
654 // ── Icon theme support ─────────────────────────────────────────────────
655 private Gee.ArrayList<Gtk.Image> themed_img_list = new Gee.ArrayList<Gtk.Image>();
656 private Gee.ArrayList<string> themed_img_name = new Gee.ArrayList<string>();
657 private Gee.ArrayList<int> themed_img_size = new Gee.ArrayList<int>();
658
659 private Gtk.Image themed_icon(string icon_name, int size) {
660 var img = IconManager.lookup_image(icon_name, size);
661 themed_img_list.add(img);
662 themed_img_name.add(icon_name);
663 themed_img_size.add(size);
664 return img;
665 }
666
667 public void reload_themed_icons() {
668 for (int i = 0; i < themed_img_list.size; i++) {
669 var img = themed_img_list[i];
670 var name = themed_img_name[i];
671 var sz = themed_img_size[i];
672 var pix = IconManager.lookup(name, sz);
673 if (pix != null) {
674 img.set_from_pixbuf(pix);
675 } else {
676 img.set_from_icon_name(name);
677 }
678 }
679 }
680
681}
Main App
AppStatus
#define _(s)
SnapshotLocationStatus
void pause(AppStatus status_to_update=AppStatus.PAUSED)
void stop(AppStatus status_to_update=AppStatus.CANCELLED)
void resume(AppStatus status_to_update=AppStatus.RUNNING)
AppStatus status
void resume()
bool take_snapshot()
BackupBox(Gtk.Window _parent_window)
void pause()
BackupDeviceBox(Gtk.Window _parent_window)
BackupFinishBox(Gtk.Window _parent_window)
void update_message(bool success)
Gtk.Widget wrap_page(Gtk.Widget child)
Gtk.Button btn_nav_finish
Gtk.Button btn_nav_backup
Gtk.Button btn_pause
Gtk.Button make_nav_button(string icon_name, string label_text)
void create_actions()
Gtk.Box bbox_action
BackupWindow(bool embedded=false)
Gtk.? Button current_nav_btn
Gtk.Box header_box
Gtk.Button btn_next
Gee.ArrayList< string > themed_img_name
Gtk.Label lbl_page_title
Gtk.Button btn_prev
void set_sidebar_active(Gtk.Button btn)
Gee.ArrayList< Gtk.Image > themed_img_list
EstimateBox estimate_box
void reload_themed_icons()
Gtk.Stack page_stack
Gtk.Button btn_nav_estimate
Gee.ArrayList< int > themed_img_size
BackupBox backup_box
Gtk.Box content_box
BackupFinishBox backup_finish_box
void initialize_tab()
BackupDeviceBox backup_dev_box
bool on_close_request()
bool validate_current_tab()
Gtk.Button btn_cancel
Gtk.Widget detach_root_widget()
Gtk.Button btn_close
signal void request_close()
Gtk.Image themed_icon(string icon_name, int size)
void clear_sidebar_selection()
void update_page_title()
void action_buttons_set_initial_visibility(bool visible)
void set_page(int page)
Gtk.Button btn_nav_location
void update_sidebar_state()
void estimate_system_size()
EstimateBox(Gtk.Window _parent_window)
static Gtk.HeaderBar create_main_headerbar(Gtk.Window win, int left_width=215)
static Gdk.? Pixbuf lookup(string icon_name, int icon_size, bool symbolic=false, bool use_hardcoded=false, int scale=1)
static Gtk.Image lookup_image(string icon_name, int icon_size, bool symbolic=false, bool use_hardcoded=false)
Definition Main.vala:39
static uint64 first_snapshot_size
Definition Main.vala:123
SnapshotRepo repo
Definition Main.vala:70
RsyncTask task
Definition Main.vala:160
bool btrfs_mode
Definition Main.vala:52
void cron_job_update()
Definition Main.vala:4463
string status_details
bool has_space(uint64 needed=0)
string status_message
SnapshotLocationStatus status_code
void wait_and_close_window(int milliseconds, Gtk.Window window)
void log_debug(string message)
void log_msg(string message, bool highlight=false)