Skip to content
API Documentation
BastionGuard™
BackupInProgressDialog.vala
Go to the documentation of this file.
1/*
2 * BackupInProgressDialog.vala
3 *
4 * Copyright 2025-2026 Calogero Scarnà — BastionGuard Backup
5 *
6 * GPL-2.0-or-later
7 *
8 * Modal dialog shown at start-up when another instance of
9 * BastionGuard Backup is already running (typically a scheduled
10 * snapshot, an on-demand backup or a restore in progress).
11 *
12 * The dialog is intentionally simple and self-contained: it does
13 * not require the main window to exist, runs a private GLib main
14 * loop, and lets the application exit cleanly when the user
15 * dismisses it.
16 */
17
18using Gtk;
19
20using TeeJee.Logging;
22using TeeJee.Misc;
23
24public class BackupInProgressDialog : Gtk.Window {
25
26 private GLib.MainLoop? loop = null;
27
28 public BackupInProgressDialog(string lock_message){
29 title = _("BastionGuard Backup");
30 set_default_size(480, -1);
31 resizable = false;
32 modal = true;
33 add_css_class("smb-dialog");
34 // No parent window: this dialog is shown before MainWindow exists.
35
36 // Friendly title and explanation depending on what the running
37 // instance is doing.
38 string heading;
39 string body;
40
41 switch (lock_message){
42 case "backup":
43 case "ondemand":
44 heading = _("A backup is currently in progress");
45 body = _("BastionGuard Backup is currently creating a snapshot in the background.")
46 + "\n\n"
47 + _("Please wait for it to finish, then re-open BastionGuard Backup.");
48 break;
49
50 case "restore":
51 heading = _("A restore is currently in progress");
52 body = _("BastionGuard Backup is currently restoring a snapshot.")
53 + "\n\n"
54 + _("Please wait for it to finish, then re-open BastionGuard Backup.");
55 break;
56
57 case "delete":
58 case "delete-all":
59 heading = _("A snapshot deletion is currently in progress");
60 body = _("BastionGuard Backup is currently deleting one or more snapshots.")
61 + "\n\n"
62 + _("Please wait for it to finish, then re-open BastionGuard Backup.");
63 break;
64
65 default:
66 heading = _("BastionGuard Backup is already running");
67 body = _("Another instance of BastionGuard Backup is currently active.")
68 + "\n\n"
69 + _("Please wait for it to finish, then re-open BastionGuard Backup.");
70 break;
71 }
72
73 build_ui(heading, body);
74 }
75
76 private void build_ui(string heading, string body){
77 var outer = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
78 outer.margin_start = 24;
79 outer.margin_end = 24;
80 outer.margin_top = 24;
81 outer.margin_bottom = 18;
82 set_child(outer);
83
84 // Top row: warning icon + heading
85 var top = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 14);
86 top.margin_bottom = 12;
87 outer.append(top);
88
89 var icon = new Gtk.Image.from_icon_name("dialog-information");
90 icon.pixel_size = 48;
91 icon.valign = Gtk.Align.START;
92 top.append(icon);
93
94 var heading_label = new Gtk.Label("");
95 heading_label.set_markup("<span weight=\"bold\" size=\"large\">%s</span>"
96 .printf(GLib.Markup.escape_text(heading)));
97 heading_label.xalign = 0.0f;
98 heading_label.wrap = true;
99 heading_label.wrap_mode = Pango.WrapMode.WORD;
100 heading_label.hexpand = true;
101 heading_label.valign = Gtk.Align.CENTER;
102 top.append(heading_label);
103
104 // Body
105 var body_label = new Gtk.Label(body);
106 body_label.xalign = 0.0f;
107 body_label.wrap = true;
108 body_label.wrap_mode = Pango.WrapMode.WORD;
109 body_label.margin_bottom = 18;
110 outer.append(body_label);
111
112 // Hint
113 var hint = new Gtk.Label("");
114 hint.set_markup("<i>%s</i>".printf(GLib.Markup.escape_text(
115 _("This window will close when you press OK and BastionGuard Backup will exit."))));
116 hint.xalign = 0.0f;
117 hint.wrap = true;
118 hint.add_css_class("dim-label");
119 hint.margin_bottom = 12;
120 outer.append(hint);
121
122 // OK button
123 var btn_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
124 btn_box.halign = Gtk.Align.END;
125 outer.append(btn_box);
126
127 var btn_ok = new Gtk.Button.with_label(_("OK"));
128 btn_ok.add_css_class("btn-primary");
129 btn_ok.clicked.connect(() => { close_dialog(); });
130 btn_box.append(btn_ok);
131
132 // Closing the window via the X button must also exit the loop.
133 close_request.connect(() => { close_dialog(); return false; });
134 }
135
136 private void close_dialog(){
137 if (loop != null && loop.is_running()){
138 loop.quit();
139 }
140 this.hide();
141 }
142
143 /**
144 * Shows the dialog modally on its own GLib main loop and blocks
145 * until the user dismisses it. Safe to call before the application
146 * has any other windows open.
147 */
148 public void run_modal(){
149 loop = new GLib.MainLoop(null, false);
150 this.present();
151 loop.run();
152 this.destroy();
153 }
154}
#define _(s)
void build_ui(string heading, string body)
BackupInProgressDialog(string lock_message)