mycitadel/view/
mod.rs

1// MyCitadel desktop wallet: bitcoin & RGB wallet based on GTK framework.
2//
3// Written in 2022 by
4//     Dr. Maxim Orlovsky <orlovsky@pandoraprime.ch>
5//
6// Copyright (C) 2022 by Pandora Prime SA, Switzerland.
7//
8// This software is distributed without any warranty. You should have received
9// a copy of the AGPL-3.0 License along with this software. If not, see
10// <https://www.gnu.org/licenses/agpl-3.0-standalone.html>.
11
12pub mod about;
13pub mod devices;
14pub mod launch;
15pub mod psbt;
16pub mod settings;
17pub mod wallet;
18
19use std::path::PathBuf;
20
21use gtk::prelude::*;
22use gtk::{
23    ButtonsType, DialogFlags, FileChooserAction, FileChooserDialog, FileFilter, MessageDialog,
24    MessageType, ResponseType,
25};
26
27pub trait NotificationBoxExt {
28    fn notification_box(&self) -> &gtk::Box;
29    fn main_dialog(&self) -> &gtk::Dialog;
30    fn main_action_button(&self) -> &gtk::Button;
31    fn notification_image(&self) -> &gtk::Image;
32    fn notification_label(&self) -> &gtk::Label;
33
34    fn show_notification(&self) { self.notification_box().show_all(); }
35    fn show_error(&self, msg: &str) {
36        self.main_dialog()
37            .set_response_sensitive(ResponseType::Ok, false);
38        self.main_action_button().set_sensitive(false);
39        self.notification_image()
40            .set_icon_name(Some("dialog-error-symbolic"));
41        self.notification_label().set_label(msg);
42        self.notification_box().show_all();
43    }
44    fn show_info(&self, msg: &str) {
45        self.main_dialog()
46            .set_response_sensitive(ResponseType::Ok, true);
47        self.main_action_button().set_sensitive(true);
48        self.notification_image()
49            .set_icon_name(Some("dialog-information-symbolic"));
50        self.notification_label().set_label(msg);
51        self.notification_box().show_all();
52    }
53    fn show_warning(&self, msg: &str) {
54        self.main_dialog()
55            .set_response_sensitive(ResponseType::Ok, true);
56        self.main_action_button().set_sensitive(true);
57        self.notification_image()
58            .set_icon_name(Some("dialog-warning-symbolic"));
59        self.notification_label().set_label(msg);
60        self.notification_box().show_all();
61    }
62    fn hide_message(&self) {
63        self.main_dialog()
64            .set_response_sensitive(ResponseType::Ok, true);
65        self.main_action_button().set_sensitive(true);
66        self.notification_box().hide()
67    }
68}
69
70pub fn msg_dlg(
71    parent: &impl IsA<gtk::Window>,
72    ty: MessageType,
73    title: &str,
74    message: &str,
75    details: Option<&str>,
76) {
77    let err_dlg = MessageDialog::new(
78        Some(parent),
79        DialogFlags::all(),
80        MessageType::Error,
81        ButtonsType::Close,
82        message,
83    );
84    err_dlg.set_title(title);
85    err_dlg.set_message_type(ty);
86    err_dlg.set_secondary_text(details);
87    err_dlg.run();
88    err_dlg.close();
89}
90
91pub fn error_dlg(
92    parent: &impl IsA<gtk::Window>,
93    title: &str,
94    message: &str,
95    details: Option<&str>,
96) {
97    msg_dlg(parent, MessageType::Error, title, message, details);
98}
99
100pub fn file_dlg(
101    parent: Option<&impl IsA<gtk::Window>>,
102    title: &str,
103    action: FileChooserAction,
104    type_name: &str,
105    mask: &str,
106    default_name: Option<&str>,
107) -> Option<PathBuf> {
108    let button = match action {
109        FileChooserAction::Open => "Open",
110        FileChooserAction::Save => "Save",
111        FileChooserAction::SelectFolder => "Select",
112        FileChooserAction::CreateFolder => "Create",
113        _ => unimplemented!(),
114    };
115
116    let file_dlg =
117        FileChooserDialog::with_buttons(Some(title), parent, action, &[(button, ResponseType::Ok)]);
118    file_dlg.set_default_response(ResponseType::Ok);
119    file_dlg.set_do_overwrite_confirmation(action == FileChooserAction::Save);
120    if let Some(name) = default_name {
121        file_dlg.set_current_name(name);
122    }
123
124    let filter = FileFilter::new();
125    filter.add_pattern(mask);
126    filter.set_name(Some(type_name));
127    file_dlg.add_filter(&filter);
128    file_dlg.set_filter(&filter);
129
130    let resp = file_dlg.run();
131    let path = file_dlg.filename();
132    file_dlg.hide();
133    file_dlg.close();
134    if resp != ResponseType::Ok {
135        return None;
136    }
137    path
138}
139
140pub fn file_open_dlg(
141    parent: Option<&gtk::ApplicationWindow>,
142    title: &str,
143    type_name: &str,
144    mask: &str,
145) -> Option<PathBuf> {
146    file_dlg(
147        parent,
148        title,
149        FileChooserAction::Open,
150        type_name,
151        mask,
152        None,
153    )
154}
155
156pub fn file_save_dlg(
157    parent: Option<&gtk::ApplicationWindow>,
158    title: &str,
159    type_name: &str,
160    mask: &str,
161) -> Option<PathBuf> {
162    file_dlg(
163        parent,
164        title,
165        FileChooserAction::Save,
166        type_name,
167        mask,
168        None,
169    )
170}
171
172pub fn file_create_dlg(
173    parent: Option<&gtk::ApplicationWindow>,
174    title: &str,
175    type_name: &str,
176    mask: &str,
177    default_name: &str,
178) -> Option<PathBuf> {
179    file_dlg(
180        parent,
181        title,
182        FileChooserAction::Save,
183        type_name,
184        mask,
185        Some(default_name),
186    )
187}