1 // Copyright (c) 2017 Matthew Brennan Jones <matthew.brennan.jones@gmail.com>
2 // Boost Software License - Version 1.0
3 // A simple progress dialog for the D programming language
4 // https://github.com/workhorsy/d-progress-dialog
5 
6 
7 module progress_dialog_kdialog;
8 
9 import progress_dialog : ProgressDialogBase, use_log;
10 
11 
12 class ProgressDialogKDialog : ProgressDialogBase {
13 	import std.process : ProcessPipes;
14 
15 	this(string title, string message) {
16 		super(title, message);
17 	}
18 
19 	override void show(void delegate() cb) {
20 		import std.process : ProcessPipes, ProcessException, pipeProcess, Redirect, tryWait;
21 		import std.algorithm : map;
22 		import std.array : array;
23 		import std.conv : to;
24 		import std..string : split, strip;
25 		import progress_dialog_helpers : programPaths, logProgramOutput;
26 
27 		string[] paths = programPaths(["kdialog"]);
28 		if (paths.length < 1) {
29 			this.fireOnError(new Exception("Failed to find kdialog"));
30 			return;
31 		}
32 
33 		string[] args = [
34 			paths[0],
35 			"--title",
36 			_title,
37 			"100",
38 			"--progressbar",
39 			_message,
40 		];
41 		ProcessPipes pipes;
42 		try {
43 			pipes = pipeProcess(args, Redirect.stdin | Redirect.stdout | Redirect.stderr);
44 		} catch (ProcessException err) {
45 			this.fireOnError(err);
46 			return;
47 		}
48 
49 		// Make sure the program did not terminate
50 		if (tryWait(pipes.pid).terminated) {
51 			this.fireOnError(new Exception("Failed to run kdialog"));
52 			return;
53 		}
54 
55 		// Get the qdbus id to send messages with
56 		_pipes = pipes;
57 		string[] output = _pipes.stdout.byLine.map!(n => n.to!string).array();
58 		_qdbus_id = output[0].split("/ProgressDialog")[0].strip();
59 
60 		try {
61 			cb();
62 			if (use_log) {
63 				logProgramOutput(_pipes);
64 			}
65 		} catch (Throwable err) {
66 			this.fireOnError(err);
67 		}
68 	}
69 
70 	override void setPercent(int percent) {
71 		import std.process : ProcessPipes, ProcessException, pipeProcess, Redirect, tryWait, wait;
72 		import std..string : format;
73 		import std.stdio;
74 		import progress_dialog_helpers : programPaths, logProgramOutput;
75 
76 		string[] paths = programPaths(["qdbus"]);
77 		if (paths.length < 1) {
78 			this.fireOnError(new Exception("Failed to find qdbus"));
79 			return;
80 		}
81 
82 		string[] args = [
83 			paths[0],
84 			_qdbus_id,
85 			"/ProgressDialog",
86 			"Set",
87 			"",
88 			"value",
89 			"%s".format(percent),
90 		];
91 
92 		ProcessPipes pipes;
93 		try {
94 			pipes = pipeProcess(args, Redirect.stdin | Redirect.stdout | Redirect.stderr);
95 		} catch (ProcessException err) {
96 			this.fireOnError(err);
97 			return;
98 		}
99 
100 		if (wait(pipes.pid) != 0) {
101 			this.fireOnError(new Exception("Failed to set kdialog percent"));
102 		}
103 
104 		if (use_log) {
105 			logProgramOutput(pipes);
106 		}
107 	}
108 
109 	override void close() {
110 		import std.process : ProcessPipes, ProcessException, pipeProcess, Redirect, tryWait, wait;
111 		import progress_dialog_helpers : programPaths, logProgramOutput;
112 
113 		this.setPercent(100);
114 
115 		string[] paths = programPaths(["qdbus"]);
116 		if (paths.length < 1) {
117 			this.fireOnError(new Exception("Failed to find qdbus"));
118 			return;
119 		}
120 
121 		string[] args = [
122 			paths[0],
123 			_qdbus_id,
124 			"/ProgressDialog",
125 			"close",
126 		];
127 
128 		ProcessPipes pipes;
129 		try {
130 			pipes = pipeProcess(args, Redirect.stdin | Redirect.stdout | Redirect.stderr);
131 		} catch (ProcessException err) {
132 			this.fireOnError(err);
133 			return;
134 		}
135 
136 		if (wait(pipes.pid) != 0) {
137 			this.fireOnError(new Exception("Failed to close kdialog"));
138 		}
139 
140 		if (use_log) {
141 			logProgramOutput(pipes);
142 		}
143 	}
144 
145 	static bool isSupported() {
146 		import progress_dialog_helpers : programPaths;
147 		return programPaths(["kdialog"]).length > 0 && programPaths(["qdbus"]).length > 0;
148 	}
149 
150 	string _qdbus_id;
151 	ProcessPipes _pipes;
152 }