/
closedialog.d
219 lines (180 loc) · 6.39 KB
/
closedialog.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
* distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
module gx.tilix.closedialog;
import std.experimental.logger;
import std.format;
import gdkpixbuf.Pixbuf;
import gdk.Event;
import gdk.Keysyms;
import gio.Settings: GSettings = Settings;
import gobject.Value;
import gtk.Box;
import gtk.CellRendererPixbuf;
import gtk.CellRendererText;
import gtk.CheckButton;
import gtk.Dialog;
import gtk.IconInfo;
import gtk.IconTheme;
import gtk.Label;
import gtk.ScrolledWindow;
import gtk.TreeIter;
import gtk.TreeStore;
import gtk.TreeView;
import gtk.TreeViewColumn;
import gtk.Window;
import gx.i18n.l10n;
import gx.gtk.util;
import gx.tilix.common;
import gx.tilix.preferences;
public:
/**
* Prompts the user to confirm that processes can be closed
*/
bool promptCanCloseProcesses(GSettings gsSettings, Window window, ProcessInformation pi) {
if (!gsSettings.getBoolean(SETTINGS_PROMPT_ON_CLOSE_PROCESS_KEY)) return true;
CloseDialog dialog = new CloseDialog(window, pi);
scope(exit) { dialog.destroy();}
dialog.showAll();
int result = dialog.run();
if (result == ResponseType.OK && dialog.futureIgnore) {
gsSettings.setBoolean(SETTINGS_PROMPT_ON_CLOSE_PROCESS_KEY, false);
}
// Weird looking code, exists because of the way hotkeys get interpreted into results, it's
// easier to check if the result is not OK
bool cancelClose = (result != ResponseType.OK);
return !cancelClose;
}
private:
/**
* Dialog that is used to close object when running processes are detected
*/
class CloseDialog: Dialog {
private:
enum MAX_DESCRIPTION = 120;
ProcessInformation processes;
TreeStore ts;
TreeView tv;
CheckButton cbIgnore;
Pixbuf pbTerminal;
void createUI() {
// Create icons
IconTheme iconTheme = new IconTheme();
IconInfo iconInfo = iconTheme.lookupIcon("utilities-terminal", 16, cast(IconLookupFlags) 0);
if (iconInfo !is null) {
pbTerminal = iconInfo.loadIcon();
tracef("Pixbuf width,height = %d,%d", pbTerminal.getWidth(), pbTerminal.getHeight());
} else {
warning("Could not load icon for 'utilities-terminal'");
}
setAllMargins(getContentArea(), 18);
Box box = new Box(Orientation.VERTICAL, 6);
Label lbl = new Label("There are processes still running as shown below, close anyway?");
lbl.setHalign(Align.START);
lbl.setMarginBottom(6);
box.add(lbl);
ts = new TreeStore([GType.STRING, Pixbuf.getType(), GType.STRING]);
loadProcesses();
tv = new TreeView(ts);
tv.addOnKeyRelease(delegate(Event event, Widget) {
uint keyval;
if (event.getKeyval(keyval)) {
switch (keyval) {
case GdkKeysyms.GDK_Escape:
response(GtkResponseType.CANCEL);
break;
case GdkKeysyms.GDK_Return:
response(GtkResponseType.OK);
break;
default:
}
}
return false;
});
tv.setHeadersVisible(false);
CellRendererText crt = new CellRendererText();
crt.setProperty("ellipsize", new Value(PangoEllipsizeMode.END));
TreeViewColumn column = new TreeViewColumn(_("Title"), crt, "text", COLUMNS.NAME);
column.setExpand(true);
tv.appendColumn(column);
CellRendererPixbuf crp = new CellRendererPixbuf();
crp.setProperty("stock-size", 16);
column = new TreeViewColumn(_("Icon"), crp, "pixbuf", COLUMNS.ICON);
column.setExpand(true);
tv.appendColumn(column);
ScrolledWindow sw = new ScrolledWindow(tv);
sw.setShadowType(ShadowType.ETCHED_IN);
sw.setPolicy(PolicyType.NEVER, PolicyType.AUTOMATIC);
sw.setHexpand(true);
sw.setVexpand(true);
sw.setSizeRequest(-1, 300);
box.add(sw);
tv.expandAll();
cbIgnore = new CheckButton(_("Do not show this again"));
box.add(cbIgnore);
getContentArea().add(box);
}
/**
* Load list of processes into treeview, never show Application
* as root, just windows.
*/
void loadProcesses() {
if (processes.source == ProcessInfoSource.APPLICATION) {
foreach(child; processes.children) {
loadProcess(null, child);
}
} else {
loadProcess(null, processes);
}
}
void loadProcess(TreeIter parent, ProcessInformation pi) {
TreeIter current = ts.createIter(parent);
if (pi.source == ProcessInfoSource.TERMINAL) {
ts.setValue(current, COLUMNS.ICON, pbTerminal);
}
switch (pi.source) {
case ProcessInfoSource.WINDOW:
ts.setValue(current, COLUMNS.NAME, format(_("Window (%s)"), pi.description));
break;
case ProcessInfoSource.SESSION:
ts.setValue(current, COLUMNS.NAME, format(_("Session (%s)"), pi.description));
break;
default:
ts.setValue(current, COLUMNS.NAME, pi.description);
break;
}
ts.setValue(current, COLUMNS.UUID, pi.uuid);
foreach(child; pi.children) {
loadProcess(current, child);
}
}
static string getTitle(ProcessInfoSource source) {
final switch (source) {
case ProcessInfoSource.APPLICATION:
return _("Close Application");
case ProcessInfoSource.WINDOW:
return _("Close Window");
case ProcessInfoSource.SESSION:
return _("Close Session");
case ProcessInfoSource.TERMINAL:
return _("Close Session");
}
}
public:
this(Window parent, ProcessInformation processes) {
super(getTitle(processes.source), parent, GtkDialogFlags.MODAL + GtkDialogFlags.USE_HEADER_BAR, [_("OK"), _("Cancel")], [GtkResponseType.OK, GtkResponseType.CANCEL]);
this.processes = processes;
setDefaultResponse(GtkResponseType.OK);
createUI();
}
@property bool futureIgnore() {
return cbIgnore.getActive();
}
}
private:
enum COLUMNS : uint {
NAME = 0,
ICON = 1,
UUID = 2
}