preparation for mail filtering
[chiroptera.git] / dialogs.d
blobd9395eb02fb98e7f2a786078b9c2174cb23ce564
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 iv.alice;
28 import iv.cmdcon;
29 import iv.strex;
30 import iv.utfutil;
31 import iv.vfs;
33 import chibackend : DynStr;
35 //import account;
36 //import addrbook;
37 //import hitwit;
40 // ////////////////////////////////////////////////////////////////////////// //
41 public final class HintWindow : SubWindow {
42 private DynStr mMessage;
44 this (const(char)[] amessage) {
45 mMessage = amessage;
47 int xwdt = gxTextWidthUtf(amessage)+6;
48 int xhgt = gxTextHeightUtf+4;
49 super(null, 0, 0, xwdt, xhgt);
50 winx = VBufWidth-winw;
51 winy = 0;
52 mType = Type.OnTop;
53 add();
56 override @property int decorationSizeX () const nothrow @safe @nogc { return 3*2; }
57 override @property int decorationSizeY () const nothrow @safe @nogc { return 2*2; }
59 override @property int clientOffsetX () const nothrow @safe @nogc { return 3; }
60 override @property int clientOffsetY () const nothrow @safe @nogc { return 2; }
62 @property void message (DynStr v) {
63 if (mMessage != v) {
64 mMessage = v;
65 if (winx == VBufWidth-winw) {
66 winw = gxTextWidthUtf(v)+6;
67 winx = VBufWidth-winw;
68 } else {
69 winw = gxTextWidthUtf(v)+6;
70 if (winx+winw > VBufWidth) winx = VBufWidth-winw;
72 postScreenRebuild();
76 override void onPaint () {
77 if (closed) return;
78 if (!mMinimized) {
79 setupClip();
80 gxDrawWindow(null, gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
81 gxClipRect.shrinkBy(3, 2);
82 gxDrawTextUtf(x0+3, y0+2, mMessage, gxRGB!(155, 155, 155));
83 } else {
84 drawWindowMinimized();
88 override bool onKey (KeyEvent event) {
89 return false;
92 override bool onMouse (MouseEvent event) {
93 if (closed) return false;
95 int mx, my;
96 event.mouse2xy(mx, my);
98 // start drag?
99 if (!isSubWinDragging && event.type == MouseEventType.buttonPressed && event.button == MouseButton.left) {
100 if (mx >= x0 && my >= y0 && mx < x0+width && my < (!mMinimized ? y0+10 : height)) {
101 startMouseDrag(event);
102 return true;
106 return false;
111 // ////////////////////////////////////////////////////////////////////////// //
113 public class SelectPopBoxWindow : SubWindow {
114 SimpleListBoxWidget lb;
116 void delegate (Account acc) onSelected;
118 this (Account defacc) {
119 if (accounts.length > 0) {
120 int xhgt = 0;
121 int xwdt = 0;
123 lb = new SimpleListBoxWidget(this);
124 bool accFound = false;
125 int defAccIdx = -1;
126 foreach (immutable aidx, Account acc; accounts) {
127 if (cast(Pop3Account)acc) {
128 import std.format : format;
129 string s = "%s <%s>".format(acc.realname, acc.mail);
130 lb.appendItem(s, acc);
131 int w = gxTextWidthUtf(s)+2;
132 if (xwdt < w) xwdt = w;
133 if (acc is defacc) {
134 lb.curidx = lb.length-1;
135 accFound = true;
137 if (acc is defaultAcc) defAccIdx = lb.length-1;
138 xhgt += gxTextHeightUtf;
142 if (xhgt == 0) { super(); return; }
143 if (xhgt > VBufHeight) xhgt = VBufHeight-decorationSizeY;
145 if (!accFound && defAccIdx >= 0) lb.curidx = defAccIdx;
146 if (xwdt > VBufWidth-decorationSizeX) xwdt = VBufWidth-decorationSizeX;
148 super("Select Account", xwdt+decorationSizeX, xhgt+decorationSizeY);
149 lb.ww = clientWidth;
150 lb.wh = clientHeight;
152 lb.onAction = delegate (self) {
153 if (auto acc = cast(Account)lb.itemData(lb.curidx)) {
154 self.parent.close();
155 if (onSelected !is null) onSelected(acc); else vbwin.beep();
156 } else {
157 vbwin.beep();
161 addModal();
162 } else {
163 super();
167 override bool onKey (KeyEvent event) {
168 if (event.pressed) {
169 if (event == "Escape" || event == "C-Q") { close(); return true; }
170 if (event == "Enter") { lb.onAction(lb); return true; }
172 return super.onKey(event);
178 // ////////////////////////////////////////////////////////////////////////// //
180 public class SelectAddressBookWindow : SubWindow {
181 SimpleListBoxWidget lb;
183 void delegate (AddressBookEntry acc) onSelected;
185 this (const(char)[] prefix) {
186 if (abook.length > 0) {
187 int xhgt = 0;
188 int xwdt = 0;
189 auto defae = abookFindByNickFirst(prefix);
190 if (defae is null && prefix.length) {
191 auto mstx = prefix.lastIndexOf('<');
192 if (mstx >= 0) {
193 prefix = prefix[mstx+1..$];
194 if (prefix.length && prefix[$-1] == '>') prefix = prefix[0..$-1];
196 defae = abookFindByMailFirst(prefix);
199 lb = new SimpleListBoxWidget(this);
200 foreach (immutable aidx, AddressBookEntry ae; abook) {
201 import std.format : format;
202 string s;
203 if (ae.realname.length) s = ae.realname~" <"~ae.mail~">"; else s = ae.mail;
204 lb.appendItem(s, ae);
205 int w = gxTextWidthUtf(s)+2;
206 if (xwdt < w) xwdt = w;
207 if (ae is defae) lb.curidx = lb.length-1;
208 xhgt += gxTextHeightUtf;
211 if (xhgt == 0) { super(); return; }
212 if (xhgt > VBufHeight) xhgt = VBufHeight-decorationSizeY;
214 if (xwdt > VBufWidth-decorationSizeX) xwdt = VBufWidth-decorationSizeX;
216 super("Select Recepient", xwdt+decorationSizeX, xhgt+decorationSizeY);
217 lb.ww = clientWidth;
218 lb.wh = clientHeight;
220 lb.onAction = delegate (self) {
221 if (auto ae = cast(AddressBookEntry)lb.itemData(lb.curidx)) {
222 close();
223 if (onSelected !is null) onSelected(ae); else vbwin.beep();
224 } else {
225 vbwin.beep();
229 addModal();
230 } else {
231 super();
235 override bool onKey (KeyEvent event) {
236 if (event.pressed) {
237 if (event == "Escape" || event == "C-Q") { close(); return true; }
238 if (event == "Enter") { lb.onAction(lb); return true; }
240 return super.onKey(event);
246 // ////////////////////////////////////////////////////////////////////////// //
248 public class PostWindow : SubWindow {
249 LineEditWidget from;
250 LineEditWidget to;
251 LineEditWidget subj;
252 EditorWidget ed;
253 string replyto;
254 string references; // of replyto article
255 Account acc;
256 Folder fld;
258 this () {
259 import std.algorithm : max;
261 int wanthgt = VBufHeight-42*2;
262 if (wanthgt < 80) wanthgt = 80;
263 int wantwdt = VBufWidth-64*2;
264 if (wantwdt < 506) wantwdt = 506;
265 super("Compose Mail", /*506*/wantwdt, /*253*/wanthgt);
266 //if (hasWindowClass(this)) return;
268 from = new LineEditWidget(this, "From:");
269 to = new LineEditWidget(this, "To:");
270 subj = new LineEditWidget(this, "Subj:");
271 ed = new EditorWidget(this);
273 setupClientClip();
275 int tw = max(from.titwdt, to.titwdt, subj.titwdt);
276 from.titwdt = tw;
277 to.titwdt = tw;
278 subj.titwdt = tw;
280 from.wy = 0;
281 to.wy = from.wy+from.wh+2;
282 subj.wy = to.wy+to.wh+2;
284 ed.wx = 0;
285 ed.wy = subj.wy+subj.wh+2;
286 ed.ww = gxClipRect.width;
287 ed.wh = gxClipRect.height-ed.wy;
289 add();
292 override bool onKey (KeyEvent event) {
293 if (event.pressed) {
294 if (event == "Escape" && !ed.editor.textChanged) { close(); return true; }
295 if (event == "C-G" || event == "C-C") {
296 if (ed.editor.textChanged) {
297 auto qww = new YesNoWindow("Close?", "Do you really want to close the editor?", true);
298 qww.onYes = () { close(); };
299 qww.addModal();
300 } else {
301 close();
303 return true;
305 if (event == "M-Tab" && activeWidget is to) {
306 auto ae = abookFindByNick(to.str);
307 //if (ae is null) ae = abookFindByMail(to.str);
308 if (ae !is null) {
309 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
310 } else {
311 vbwin.beep();
313 return true;
315 if (event == "C-Space" && activeWidget is to) {
316 auto wae = new SelectAddressBookWindow(to.str);
317 wae.onSelected = delegate (AddressBookEntry ae) {
318 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
320 return true;
322 if ((event == "C-Space") && activeWidget is from) {
323 if (cast(Pop3Account)acc) {
324 auto wacc = new SelectPopBoxWindow(acc);
325 wacc.onSelected = delegate (Account newacc) {
326 acc = newacc;
327 from.str = acc.realname~" <"~acc.mail~">";
330 return true;
332 if (event == "C-Enter") {
333 static bool checkString (string s) nothrow @trusted @nogc {
334 if (s.length == 0) return false;
335 if (s.utflen > 255) return false;
336 return true;
338 if (!checkString(subj.str)) { vbwin.beep(); return true; }
339 if (!checkString(to.str)) { vbwin.beep(); return true; }
340 if (ed.editor[].length == 0) { vbwin.beep(); return true; }
341 auto senddg = delegate () {
342 // build reply article and add it to send queue
343 // check attaches
344 ed.editor.clearAndDisableUndo(); // so removing attaches will not add 'em to undo, lol
345 string[] attnames = ed.extractAttaches();
346 if (attnames) conwriteln("ATTACHES: ", attnames);
347 // build article text
348 auto newart = new Article();
349 newart.markAsStandalone();
350 newart.attachedFileNames = attnames;
351 newart.startText();
352 //foreach (immutable idx; 0..lcount) newart.appendTextLine(ed.editor[idx]);
354 auto mtr = ed.editor[];
355 if (mtr.length) newart.appendTextRange(mtr); else newart.appendTextLine("no text");
357 // clear editor, so it free used memory
358 ed.editor.clearAndDisableUndo();
359 ed.editor.clear();
360 // fix headers
361 newart.subj = subj.str;
362 //newart.inreplyto = replyto;
363 if (replyto.length) newart.replaceHeader("In-Reply-To", replyto);
364 if (references.length) newart.replaceHeader("References", references);
365 newart.replaceHeader("To", encodeq(to.str));
366 if (!acc.addToSendQueue(fld, newart)) { vbwin.beep(); return; }
367 close();
369 auto qww = new YesNoWindow("Send?", "Do you really want to send the message?", true);
370 qww.onYes = senddg;
371 qww.addModal();
372 return true;
375 return super.onKey(event);
381 // ////////////////////////////////////////////////////////////////////////// //
383 public class TitlerWindow : SubWindow {
384 LineEditWidget title;
385 string name;
386 string mail;
387 string folder;
389 void delegate (string name, string mail, string folder, string title) onSelected;
391 this (string aname, string amail, string afolder, string atitle) {
392 int xhgt = gxTextHeightUtf+2;
394 super("Title for "~aname~" <"~amail~">", 506, xhgt+decorationSizeY);
396 title = new LineEditWidget(this, "Title:");
397 title.wh = clientWidth;
399 name = aname;
400 mail = amail;
401 folder = afolder;
402 title.str = atitle;
403 add();
406 override bool onKey (KeyEvent event) {
407 if (event.pressed) {
408 if (event == "Escape" || event == "C-Q") { close(); return true; }
409 if (event == "Enter") {
410 close();
411 if (onSelected !is null) onSelected(name, mail, folder, title.str);
412 return true;
415 return super.onKey(event);