sqlite: more schema changes; removed message hashes (it wasn't a very bright idea...
[chiroptera.git] / dialogs.d
blob4b472a67b50658ca60c37f192b4d9bad2163ae73
1 /* E-Mail Client
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module dialogs /*is aliced*/;
18 private:
20 import core.time;
22 import arsd.simpledisplay;
24 import egfx;
25 import egui;
27 import maildb;
29 import iv.alice;
30 import iv.cmdcon;
31 import iv.strex;
32 import iv.utfutil;
33 import iv.vfs;
35 import account;
36 import addrbook;
37 import folder;
38 import hitwit;
41 // ////////////////////////////////////////////////////////////////////////// //
42 public class SelectPopBoxWindow : SubWindow {
43 SimpleListBoxWidget lb;
45 void delegate (Account acc) onSelected;
47 this (Account defacc) {
48 if (accounts.length > 0) {
49 int xhgt = 0;
50 int xwdt = 0;
52 lb = new SimpleListBoxWidget(this);
53 bool accFound = false;
54 int defAccIdx = -1;
55 foreach (immutable aidx, Account acc; accounts) {
56 if (cast(Pop3Account)acc) {
57 import std.format : format;
58 string s = "%s <%s>".format(acc.realname, acc.mail);
59 lb.appendItem(s, acc);
60 int w = gxTextWidthUtf(s)+2;
61 if (xwdt < w) xwdt = w;
62 if (acc is defacc) {
63 lb.curidx = lb.length-1;
64 accFound = true;
66 if (acc is defaultAcc) defAccIdx = lb.length-1;
67 xhgt += gxTextHeightUtf;
71 if (xhgt == 0) { super(); return; }
72 if (xhgt > VBufHeight) xhgt = VBufHeight-decorationSizeY;
74 if (!accFound && defAccIdx >= 0) lb.curidx = defAccIdx;
75 if (xwdt > VBufWidth-decorationSizeX) xwdt = VBufWidth-decorationSizeX;
77 super("Select Account", xwdt+decorationSizeX, xhgt+decorationSizeY);
78 lb.ww = clientWidth;
79 lb.wh = clientHeight;
81 lb.onAction = delegate (self) {
82 if (auto acc = cast(Account)lb.itemData(lb.curidx)) {
83 self.parent.close();
84 if (onSelected !is null) onSelected(acc); else vbwin.beep();
85 } else {
86 vbwin.beep();
90 addModal();
91 } else {
92 super();
96 override bool onKey (KeyEvent event) {
97 if (event.pressed) {
98 if (event == "Escape" || event == "C-Q") { close(); return true; }
99 if (event == "Enter") { lb.onAction(lb); return true; }
101 return super.onKey(event);
106 // ////////////////////////////////////////////////////////////////////////// //
107 public class SelectAddressBookWindow : SubWindow {
108 SimpleListBoxWidget lb;
110 void delegate (AddressBookEntry acc) onSelected;
112 this (const(char)[] prefix) {
113 if (abook.length > 0) {
114 int xhgt = 0;
115 int xwdt = 0;
116 auto defae = abookFindByNickFirst(prefix);
117 if (defae is null && prefix.length) {
118 auto mstx = prefix.lastIndexOf('<');
119 if (mstx >= 0) {
120 prefix = prefix[mstx+1..$];
121 if (prefix.length && prefix[$-1] == '>') prefix = prefix[0..$-1];
123 defae = abookFindByMailFirst(prefix);
126 lb = new SimpleListBoxWidget(this);
127 foreach (immutable aidx, AddressBookEntry ae; abook) {
128 import std.format : format;
129 string s;
130 if (ae.realname.length) s = ae.realname~" <"~ae.mail~">"; else s = ae.mail;
131 lb.appendItem(s, ae);
132 int w = gxTextWidthUtf(s)+2;
133 if (xwdt < w) xwdt = w;
134 if (ae is defae) lb.curidx = lb.length-1;
135 xhgt += gxTextHeightUtf;
138 if (xhgt == 0) { super(); return; }
139 if (xhgt > VBufHeight) xhgt = VBufHeight-decorationSizeY;
141 if (xwdt > VBufWidth-decorationSizeX) xwdt = VBufWidth-decorationSizeX;
143 super("Select Recepient", xwdt+decorationSizeX, xhgt+decorationSizeY);
144 lb.ww = clientWidth;
145 lb.wh = clientHeight;
147 lb.onAction = delegate (self) {
148 if (auto ae = cast(AddressBookEntry)lb.itemData(lb.curidx)) {
149 close();
150 if (onSelected !is null) onSelected(ae); else vbwin.beep();
151 } else {
152 vbwin.beep();
156 addModal();
157 } else {
158 super();
162 override bool onKey (KeyEvent event) {
163 if (event.pressed) {
164 if (event == "Escape" || event == "C-Q") { close(); return true; }
165 if (event == "Enter") { lb.onAction(lb); return true; }
167 return super.onKey(event);
172 // ////////////////////////////////////////////////////////////////////////// //
173 public class PostWindow : SubWindow {
174 LineEditWidget from;
175 LineEditWidget to;
176 LineEditWidget subj;
177 EditorWidget ed;
178 string replyto;
179 string references; // of replyto article
180 Account acc;
181 Folder fld;
183 this () {
184 import std.algorithm : max;
186 int wanthgt = VBufHeight-42*2;
187 if (wanthgt < 80) wanthgt = 80;
188 int wantwdt = VBufWidth-64*2;
189 if (wantwdt < 506) wantwdt = 506;
190 super("Compose Mail", /*506*/wantwdt, /*253*/wanthgt);
191 //if (hasWindowClass(this)) return;
193 from = new LineEditWidget(this, "From:");
194 to = new LineEditWidget(this, "To:");
195 subj = new LineEditWidget(this, "Subj:");
196 ed = new EditorWidget(this);
198 setupClientClip();
200 int tw = max(from.titwdt, to.titwdt, subj.titwdt);
201 from.titwdt = tw;
202 to.titwdt = tw;
203 subj.titwdt = tw;
205 from.wy = 0;
206 to.wy = from.wy+from.wh+2;
207 subj.wy = to.wy+to.wh+2;
209 ed.wx = 0;
210 ed.wy = subj.wy+subj.wh+2;
211 ed.ww = gxClipRect.width;
212 ed.wh = gxClipRect.height-ed.wy;
214 add();
217 override bool onKey (KeyEvent event) {
218 if (event.pressed) {
219 if (event == "Escape" && !ed.editor.textChanged) { close(); return true; }
220 if (event == "C-G" || event == "C-C") {
221 if (ed.editor.textChanged) {
222 auto qww = new YesNoWindow("Close?", "Do you really want to close the editor?", true);
223 qww.onYes = () { close(); };
224 qww.addModal();
225 } else {
226 close();
228 return true;
230 if (event == "M-Tab" && activeWidget is to) {
231 auto ae = abookFindByNick(to.str);
232 //if (ae is null) ae = abookFindByMail(to.str);
233 if (ae !is null) {
234 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
235 } else {
236 vbwin.beep();
238 return true;
240 if (event == "C-Space" && activeWidget is to) {
241 auto wae = new SelectAddressBookWindow(to.str);
242 wae.onSelected = delegate (AddressBookEntry ae) {
243 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
245 return true;
247 if ((event == "C-Space") && activeWidget is from) {
248 if (cast(Pop3Account)acc) {
249 auto wacc = new SelectPopBoxWindow(acc);
250 wacc.onSelected = delegate (Account newacc) {
251 acc = newacc;
252 from.str = acc.realname~" <"~acc.mail~">";
255 return true;
257 if (event == "C-Enter") {
258 static bool checkString (string s) nothrow @trusted @nogc {
259 if (s.length == 0) return false;
260 if (s.utflen > 255) return false;
261 return true;
263 if (!checkString(subj.str)) { vbwin.beep(); return true; }
264 if (!checkString(to.str)) { vbwin.beep(); return true; }
265 if (ed.editor[].length == 0) { vbwin.beep(); return true; }
266 auto senddg = delegate () {
267 // build reply article and add it to send queue
268 // check attaches
269 ed.editor.clearAndDisableUndo(); // so removing attaches will not add 'em to undo, lol
270 string[] attnames = ed.extractAttaches();
271 if (attnames) conwriteln("ATTACHES: ", attnames);
272 // build article text
273 auto newart = new Article();
274 newart.markAsStandalone();
275 newart.attachedFileNames = attnames;
276 newart.startText();
277 //foreach (immutable idx; 0..lcount) newart.appendTextLine(ed.editor[idx]);
279 auto mtr = ed.editor[];
280 if (mtr.length) newart.appendTextRange(mtr); else newart.appendTextLine("no text");
282 // clear editor, so it free used memory
283 ed.editor.clearAndDisableUndo();
284 ed.editor.clear();
285 // fix headers
286 newart.subj = subj.str;
287 //newart.inreplyto = replyto;
288 if (replyto.length) newart.replaceHeader("In-Reply-To", replyto);
289 if (references.length) newart.replaceHeader("References", references);
290 newart.replaceHeader("To", encodeq(to.str));
291 if (!acc.addToSendQueue(fld, newart)) { vbwin.beep(); return; }
292 close();
294 auto qww = new YesNoWindow("Send?", "Do you really want to send the message?", true);
295 qww.onYes = senddg;
296 qww.addModal();
297 return true;
300 return super.onKey(event);
305 // ////////////////////////////////////////////////////////////////////////// //
306 public class TitlerWindow : SubWindow {
307 LineEditWidget title;
308 string name;
309 string mail;
310 string folder;
312 void delegate (string name, string mail, string folder, string title) onSelected;
314 this (string aname, string amail, string afolder, string atitle) {
315 int xhgt = gxTextHeightUtf+2;
317 super("Title for "~aname~" <"~amail~">", 506, xhgt+decorationSizeY);
319 title = new LineEditWidget(this, "Title:");
320 title.wh = clientWidth;
322 name = aname;
323 mail = amail;
324 folder = afolder;
325 title.str = atitle;
326 add();
329 override bool onKey (KeyEvent event) {
330 if (event.pressed) {
331 if (event == "Escape" || event == "C-Q") { close(); return true; }
332 if (event == "Enter") {
333 close();
334 if (onSelected !is null) onSelected(name, mail, folder, title.str);
335 return true;
338 return super.onKey(event);
343 // ////////////////////////////////////////////////////////////////////////// //
344 public final class HintWindow : SubWindow {
345 private string mMessage;
347 this (string amessage) {
348 mMessage = amessage;
350 int xwdt = gxTextWidthUtf(amessage)+6;
351 int xhgt = gxTextHeightUtf+4;
352 super(null, 0, 0, xwdt, xhgt);
353 winx = VBufWidth-winw;
354 winy = 0;
355 mType = Type.OnTop;
356 add();
359 override @property int decorationSizeX () const nothrow @safe @nogc { return 3*2; }
360 override @property int decorationSizeY () const nothrow @safe @nogc { return 2*2; }
362 override @property int clientOffsetX () const nothrow @safe @nogc { return 3; }
363 override @property int clientOffsetY () const nothrow @safe @nogc { return 2; }
365 @property void message (string v) {
366 if (mMessage != v) {
367 mMessage = v;
368 if (winx == VBufWidth-winw) {
369 winw = gxTextWidthUtf(v)+6;
370 winx = VBufWidth-winw;
371 } else {
372 winw = gxTextWidthUtf(v)+6;
373 if (winx+winw > VBufWidth) winx = VBufWidth-winw;
375 postScreenRebuild();
379 override void onPaint () {
380 if (closed) return;
381 if (!mMinimized) {
382 setupClip();
383 gxDrawWindow(null, gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
384 gxClipRect.shrinkBy(3, 2);
385 gxDrawTextUtf(x0+3, y0+2, mMessage, gxRGB!(155, 155, 155));
386 } else {
387 drawWindowMinimized();
391 override bool onKey (KeyEvent event) {
392 return false;
395 override bool onMouse (MouseEvent event) {
396 if (closed) return false;
398 int mx, my;
399 event.mouse2xy(mx, my);
401 // start drag?
402 if (!isSubWinDragging && event.type == MouseEventType.buttonPressed && event.button == MouseButton.left) {
403 if (mx >= x0 && my >= y0 && mx < x0+width && my < (!mMinimized ? y0+10 : height)) {
404 startMouseDrag(event);
405 return true;
409 return false;