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*/;
22 import arsd
.simpledisplay
;
33 import chibackend
/*: DynStr*/;
40 // ////////////////////////////////////////////////////////////////////////// //
41 public final class HintWindow
: SubWindow
{
42 private DynStr mMessage
;
44 this (const(char)[] amessage
) {
47 int xwdt
= gxTextWidthUtf(amessage
)+6;
48 int xhgt
= gxTextHeightUtf
+4;
49 super(null, GxSize(xwdt
, xhgt
));
50 x0
= screenWidth
-width
;
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 decorationSizeX
/2; }
60 override @property int clientOffsetY () const nothrow @safe @nogc { return decorationSizeY
/2; }
62 @property void message (DynStr v
) {
65 if (x0
== screenWidth
-width
) {
66 width
= gxTextWidthUtf(v
)+6;
67 x0
= screenWidth
-width
;
69 width
= gxTextWidthUtf(v
)+6;
70 if (x0
+width
> screenWidth
) x0
= screenWidth
-width
;
76 override void onPaint () {
81 //gxDrawWindow(winrect, null, gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
82 gxDrawWindow(winrect
, null,
84 getColor("title-text"),
85 getColor("title-back"),
87 //getColor(gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
88 gxClipRect
.shrinkBy(3, 2);
89 gxDrawTextUtf(x0
+3, y0
+2, mMessage
, getColor("text")/*gxRGB!(155, 155, 155)*/);
91 drawWindowMinimized();
96 // prevent any default keyboard handling
97 override bool onKeySink (KeyEvent event
) {
103 // ////////////////////////////////////////////////////////////////////////// //
104 public final class MessageWindow
: SubWindow
{
105 private DynStr mMessage
;
106 private uint lastBarWidth
;
108 this (const(char)[] amessage
) {
111 int xwdt
= gxTextWidthUtf(amessage
)+decorationSizeX
;
112 int xhgt
= gxTextHeightUtf
+decorationSizeY
;
113 super(null, GxSize(xwdt
, xhgt
));
119 override @property int decorationSizeX () const nothrow @safe @nogc { return 24; }
120 override @property int decorationSizeY () const nothrow @safe @nogc { return 16; }
122 override @property int clientOffsetX () const nothrow @safe @nogc { return decorationSizeX
/2; }
123 override @property int clientOffsetY () const nothrow @safe @nogc { return decorationSizeY
/2; }
125 // returns `true` if need redraw
126 bool setProgress (const(char)[] v
, uint curr
, uint total
) {
131 int xwdt
= gxTextWidthUtf(v
)+decorationSizeX
;
132 int xhgt
= gxTextHeightUtf
+decorationSizeY
;
133 if (xwdt
> width || xhgt
> height
) {
141 if (total
) barWidth
= curr
*cast(uint)(width
-2)/total
;
142 if (barWidth
!= lastBarWidth
) res
= true;
143 lastBarWidth
= barWidth
;
146 import core.stdc.stdio : snprintf;
147 char[128] buf = void;
149 recalcHintWindow.message = msg;
151 auto len = snprintf(buf.ptr, buf.sizeof, "%.*s: %u", cast(uint)msg.length, msg.ptr, curr*100U/total);
152 recalcHintWindow.message = buf[0..len];
157 override void onPaint () {
162 //gxDrawWindow(winrect, null, gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
163 gxDrawWindow(winrect
, null,
165 getColor("title-text"),
166 getColor("title-back"),
168 if (lastBarWidth
> 0) {
169 auto rc
= GxRect(x0
+1, y0
+1, lastBarWidth
, height
-2);
170 gxFillRect(rc
, getColor("bar-back")/*gxRGB!(90, 90, 180)*/);
173 //gxClipRect.shrinkBy(3, 2);
174 immutable tw
= gxTextWidthUtf(mMessage
);
175 immutable th
= gxTextHeightUtf();
176 gxDrawTextUtf(x0
+(width
-tw
)/2, y0
+(height
-th
)/2, mMessage
, getColor("text"));
178 drawWindowMinimized();
183 // prevent any default keyboard handling
184 override bool onKeySink (KeyEvent event
) {
190 // ////////////////////////////////////////////////////////////////////////// //
192 public class SelectPopBoxWindow : SubWindow {
193 SimpleListBoxWidget lb;
195 void delegate (Account acc) onSelected;
197 this (Account defacc) {
198 if (accounts.length > 0) {
202 lb = new SimpleListBoxWidget(this);
203 bool accFound = false;
205 foreach (immutable aidx, Account acc; accounts) {
206 if (cast(Pop3Account)acc) {
207 import std.format : format;
208 string s = "%s <%s>".format(acc.realname, acc.mail);
209 lb.appendItem(s, acc);
210 int w = gxTextWidthUtf(s)+2;
211 if (xwdt < w) xwdt = w;
213 lb.curidx = lb.length-1;
216 if (acc is defaultAcc) defAccIdx = lb.length-1;
217 xhgt += gxTextHeightUtf;
221 if (xhgt == 0) { super(); return; }
222 if (xhgt > screenHeight) xhgt = screenHeight-decorationSizeY;
224 if (!accFound && defAccIdx >= 0) lb.curidx = defAccIdx;
225 if (xwdt > screenWidth-decorationSizeX) xwdt = screenWidth-decorationSizeX;
227 super("Select Account", xwdt+decorationSizeX, xhgt+decorationSizeY);
229 lb.wh = clientHeight;
231 lb.onAction = delegate (self) {
232 if (auto acc = cast(Account)lb.itemData(lb.curidx)) {
234 if (onSelected !is null) onSelected(acc); else vbwin.beep();
246 override bool onKey (KeyEvent event) {
248 if (event == "Escape" || event == "C-Q") { close(); return true; }
249 if (event == "Enter") { lb.onAction(lb); return true; }
251 return super.onKey(event);
257 // ////////////////////////////////////////////////////////////////////////// //
259 public class SelectAddressBookWindow : SubWindow {
260 SimpleListBoxWidget lb;
262 void delegate (AddressBookEntry acc) onSelected;
264 this (const(char)[] prefix) {
265 if (abook.length > 0) {
268 auto defae = abookFindByNickFirst(prefix);
269 if (defae is null && prefix.length) {
270 auto mstx = prefix.lastIndexOf('<');
272 prefix = prefix[mstx+1..$];
273 if (prefix.length && prefix[$-1] == '>') prefix = prefix[0..$-1];
275 defae = abookFindByMailFirst(prefix);
278 lb = new SimpleListBoxWidget(this);
279 foreach (immutable aidx, AddressBookEntry ae; abook) {
280 import std.format : format;
282 if (ae.realname.length) s = ae.realname~" <"~ae.mail~">"; else s = ae.mail;
283 lb.appendItem(s, ae);
284 int w = gxTextWidthUtf(s)+2;
285 if (xwdt < w) xwdt = w;
286 if (ae is defae) lb.curidx = lb.length-1;
287 xhgt += gxTextHeightUtf;
290 if (xhgt == 0) { super(); return; }
291 if (xhgt > screenHeight) xhgt = screenHeight-decorationSizeY;
293 if (xwdt > screenWidth-decorationSizeX) xwdt = screenWidth-decorationSizeX;
295 super("Select Recepient", xwdt+decorationSizeX, xhgt+decorationSizeY);
297 lb.wh = clientHeight;
299 lb.onAction = delegate (self) {
300 if (auto ae = cast(AddressBookEntry)lb.itemData(lb.curidx)) {
302 if (onSelected !is null) onSelected(ae); else vbwin.beep();
314 override bool onKey (KeyEvent event) {
316 if (event == "Escape" || event == "C-Q") { close(); return true; }
317 if (event == "Enter") { lb.onAction(lb); return true; }
319 return super.onKey(event);
325 // ////////////////////////////////////////////////////////////////////////// //
327 public class PostWindow : SubWindow {
333 string references; // of replyto article
338 import std.algorithm : max;
340 int wanthgt = screenHeight-42*2;
341 if (wanthgt < 80) wanthgt = 80;
342 int wantwdt = screenWidth-64*2;
343 if (wantwdt < 506) wantwdt = 506;
344 super("Compose Mail", /*506*/wantwdt, /*253*/wanthgt);
345 //if (hasWindowClass(this)) return;
347 from = new LineEditWidget(this, "From:");
348 to = new LineEditWidget(this, "To:");
349 subj = new LineEditWidget(this, "Subj:");
350 ed = new EditorWidget(this);
354 int tw = max(from.titwdt, to.titwdt, subj.titwdt);
360 to.wy = from.wy+from.wh+2;
361 subj.wy = to.wy+to.wh+2;
364 ed.wy = subj.wy+subj.wh+2;
365 ed.ww = gxClipRect.width;
366 ed.wh = gxClipRect.height-ed.wy;
371 override bool onKey (KeyEvent event) {
373 if (event == "Escape" && !ed.editor.textChanged) { close(); return true; }
374 if (event == "C-G" || event == "C-C") {
375 if (ed.editor.textChanged) {
376 auto qww = new YesNoWindow("Close?", "Do you really want to close the editor?", true);
377 qww.onYes = () { close(); };
384 if (event == "M-Tab" && activeWidget is to) {
385 auto ae = abookFindByNick(to.str);
386 //if (ae is null) ae = abookFindByMail(to.str);
388 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
394 if (event == "C-Space" && activeWidget is to) {
395 auto wae = new SelectAddressBookWindow(to.str);
396 wae.onSelected = delegate (AddressBookEntry ae) {
397 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
401 if ((event == "C-Space") && activeWidget is from) {
402 if (cast(Pop3Account)acc) {
403 auto wacc = new SelectPopBoxWindow(acc);
404 wacc.onSelected = delegate (Account newacc) {
406 from.str = acc.realname~" <"~acc.mail~">";
411 if (event == "C-Enter") {
412 static bool checkString (string s) nothrow @trusted @nogc {
413 if (s.length == 0) return false;
414 if (s.utflen > 255) return false;
417 if (!checkString(subj.str)) { vbwin.beep(); return true; }
418 if (!checkString(to.str)) { vbwin.beep(); return true; }
419 if (ed.editor[].length == 0) { vbwin.beep(); return true; }
420 auto senddg = delegate () {
421 // build reply article and add it to send queue
423 ed.editor.clearAndDisableUndo(); // so removing attaches will not add 'em to undo, lol
424 string[] attnames = ed.extractAttaches();
425 if (attnames) conwriteln("ATTACHES: ", attnames);
426 // build article text
427 auto newart = new Article();
428 newart.markAsStandalone();
429 newart.attachedFileNames = attnames;
431 //foreach (immutable idx; 0..lcount) newart.appendTextLine(ed.editor[idx]);
433 auto mtr = ed.editor[];
434 if (mtr.length) newart.appendTextRange(mtr); else newart.appendTextLine("no text");
436 // clear editor, so it free used memory
437 ed.editor.clearAndDisableUndo();
440 newart.subj = subj.str;
441 //newart.inreplyto = replyto;
442 if (replyto.length) newart.replaceHeader("In-Reply-To", replyto);
443 if (references.length) newart.replaceHeader("References", references);
444 newart.replaceHeader("To", encodeq(to.str));
445 if (!acc.addToSendQueue(fld, newart)) { vbwin.beep(); return; }
448 auto qww = new YesNoWindow("Send?", "Do you really want to send the message?", true);
454 return super.onKey(event);
460 // ////////////////////////////////////////////////////////////////////////// //
461 public class TitlerWindow
: SubWindow
{
462 LineEditWidget edtTitle
;
463 LineEditWidget fromName
;
464 LineEditWidget fromMail
;
465 //LineEditWidget fromTag;
470 bool delegate (const(char)[] name
, const(char)[] mail
, const(char)[] folder
, const(char)[] title
) onSelected
;
472 this (const(char)[] aname
, const(char)[] amail
, const(char)[] afolder
, const(char)[] atitle
) {
473 import std
.algorithm
: max
;
474 int xhgt
= gxTextHeightUtf
+2;
476 DynStr caption
= "Title for ";
481 super(caption
.getData
.idup
/*, GxSize(506, xhgt+decorationSizeY)*/);
483 new SpacerWidget(rootWidget
, 2);
484 fromName
= new LineEditWidget(rootWidget
, "Name:");
487 new SpacerWidget(rootWidget
, 2);
488 fromMail
= new LineEditWidget(rootWidget
, "Mail:");
491 new SpacerWidget(rootWidget
, 2);
492 edtTitle
= new LineEditWidget(rootWidget
, "Title:");
494 new SpacerWidget(rootWidget
, 2);
496 fromName
.str = aname
;
497 fromMail
.str = amail
;
498 edtTitle
.str = atitle
;
501 int twdt
= fromName
.titwdt
;
502 if (twdt
< fromMail
.titwdt
) twdt
= fromMail
.titwdt
;
503 if (twdt
< edtTitle
.titwdt
) twdt
= edtTitle
.titwdt
;
504 fromName
.titwdt
= twdt
;
505 fromMail
.titwdt
= twdt
;
506 edtTitle
.titwdt
= twdt
;
516 override bool onKeyBubble (KeyEvent event
) {
518 if (event
== "Escape" || event
== "C-Q") { close(); return true; }
519 if (event
== "Enter") {
520 if (onSelected
!is null) {
521 if (!onSelected(fromName
.str, fromMail
.str, folder
, edtTitle
.str)) return true;
527 return super.onKeyBubble(event
);
532 // ////////////////////////////////////////////////////////////////////////// //
533 public class TagOptionsWindow
: SubWindow
{
534 LabelWidget optPath
; // real path
535 LineEditWidget optMonthes
;
536 CheckboxWidget optThreaded
;
537 CheckboxWidget optAttaches
;
540 void delegate (const(char)[] tagname
) onUpdated
;
542 this (const(char)[] atagname
) {
545 DynStr caption
= "options for '";
548 super(caption
.getData
.idup
/*, GxSize(706, 64)*/);
550 optPath
= new LabelWidget(rootWidget
, "", LabelWidget
.HAlign
.Center
);
553 optMonthes
= new LineEditWidget(rootWidget
, "Monthes:");
555 optMonthes
.width
= optMonthes
.titwdt
+64;
557 optThreaded
= new CheckboxWidget(rootWidget
, "&Threaded");
558 optThreaded
.flex
= 1;
560 optAttaches
= new CheckboxWidget(rootWidget
, "&Attaches");
561 optAttaches
.flex
= 1;
566 optPath = new LabelWidget(this, "", LabelWidget.HAlign.Center);
568 optPath.ww = clientWidth;
570 optMonthes = new LineEditWidget(this, "Monthes:");
571 optMonthes.wy = optPath.wy+optPath.wh+2;
572 optMonthes.ww = clientWidth;
574 optThreaded = new CheckboxWidget(this, "&Threaded");
575 optThreaded.ww = clientWidth;
576 optThreaded.wy = optMonthes.wy+optMonthes.wh+2;
578 optAttaches = new CheckboxWidget(this, "&Attaches");
579 optAttaches.ww = clientWidth;
580 optAttaches.wy = optThreaded.wy+optThreaded.wh+2;
582 activeWidget = optMonthes;
585 immutable bool goodTag
= (chiroGetTagUid(tagname
.getData
) != 0);
587 import std
.conv
: to
;
589 DynStr path
= chiroGetTagMonthLimitEx(tagname
.getData
, out val
, defval
:6);
590 //conwriteln("TAGNAME=<", tagname.getData, ">; val=", val, "; path=<", path.getData, ">");
591 optPath
.text
= path
.getData
;
592 optMonthes
.str = val
.to
!string
;
593 optPath
.width
= gxTextWidthUtf(optPath
.text
)+4;
597 foreach (auto row
; dbView
.statement(`
598 SELECT threading AS trd, noattaches AS noatt FROM tagnames WHERE tag=:tag LIMIT 1
599 ;`).bindConstText(":tag", atagname
).range
)
601 optThreaded
.checked
= (row
.trd
!int == 1);
602 optAttaches
.checked
= (row
.noatt
!int == 0);
605 optThreaded
.enabled
= false;
606 optAttaches
.enabled
= false;
609 optMonthes
.killTextOnChar
= true;
611 minWinSize
.w
= optPath
.width
;
617 override bool onKeyBubble (KeyEvent event
) {
619 if (event
== "Escape" || event
== "C-Q") { close(); return true; }
620 if (event
== "Enter") {
623 auto vv
= optMonthes
.str.xstrip
;
624 if (vv
.length
== 0) {
627 import std
.conv
: to
;
629 if (mv
< -1) mv
= -666; else if (mv
== 0) mv
= -1;
631 } catch (Exception
) {
634 if (mv
< -2) return true;
635 import core
.stdc
.stdio
: snprintf
;
636 char[1024] xname
= void;
637 const(char)[] tn
= tagname
.getData
;
638 auto xlen
= snprintf(xname
.ptr
, xname
.sizeof
, "/mainpane/msgview/monthlimit%s%.*s",
639 (tn
.length
&& tn
[0] != '/' ?
"/".ptr
: "".ptr
), cast(uint)tn
.length
, tn
.ptr
);
642 if (xname
[0..xlen
] != "/mainpane/msgview/monthlimit") {
643 chiroDeleteOption(xname
[0..xlen
]);
647 chiroSetOption(xname
[0..xlen
], mv
);
649 // fix threading and attaches
650 if (optThreaded
.enabled
) {
653 SET threading=:trd, noattaches=:noatt
656 .bindConstText(":name", tagname
.getData())
657 .bind(":trd", (optThreaded
.checked ?
1 : 0))
658 .bind(":noatt", (optAttaches
.checked ?
0 : 1))
661 if (onUpdated
!is null) onUpdated(tagname
.getData
);
666 return super.onKeyBubble(event
);