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
;
32 import chibackend
/*: DynStr*/;
39 // ////////////////////////////////////////////////////////////////////////// //
40 public final class HintWindow
: SubWindow
{
41 private DynStr mMessage
;
43 this (const(char)[] amessage
) {
46 int xwdt
= gxTextWidthUtf(amessage
)+6;
47 int xhgt
= gxTextHeightUtf
+4;
48 super(null, GxSize(xwdt
, xhgt
));
49 x0
= screenWidth
-width
;
55 override @property int decorationSizeX () const nothrow @safe @nogc { return 3*2; }
56 override @property int decorationSizeY () const nothrow @safe @nogc { return 2*2; }
58 override @property int clientOffsetX () const nothrow @safe @nogc { return decorationSizeX
/2; }
59 override @property int clientOffsetY () const nothrow @safe @nogc { return decorationSizeY
/2; }
61 @property void message (DynStr v
) {
64 if (x0
== screenWidth
-width
) {
65 width
= gxTextWidthUtf(v
)+6;
66 x0
= screenWidth
-width
;
68 width
= gxTextWidthUtf(v
)+6;
69 if (x0
+width
> screenWidth
) x0
= screenWidth
-width
;
75 override void onPaint () {
80 //gxDrawWindow(winrect, null, gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
81 gxDrawWindow(winrect
, null,
83 getColor("title-text"),
84 getColor("title-back"),
86 //getColor(gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
87 gxClipRect
.shrinkBy(3, 2);
88 gxDrawTextUtf(x0
+3, y0
+2, mMessage
, getColor("text")/*gxRGB!(155, 155, 155)*/);
90 drawWindowMinimized();
95 // prevent any default keyboard handling
96 override bool onKeySink (KeyEvent event
) {
102 // ////////////////////////////////////////////////////////////////////////// //
103 public final class MessageWindow
: SubWindow
{
104 private DynStr mMessage
;
105 private uint lastBarWidth
;
107 this (const(char)[] amessage
) {
110 int xwdt
= gxTextWidthUtf(amessage
)+decorationSizeX
;
111 int xhgt
= gxTextHeightUtf
+decorationSizeY
;
112 super(null, GxSize(xwdt
, xhgt
));
118 override @property int decorationSizeX () const nothrow @safe @nogc { return 24; }
119 override @property int decorationSizeY () const nothrow @safe @nogc { return 16; }
121 override @property int clientOffsetX () const nothrow @safe @nogc { return decorationSizeX
/2; }
122 override @property int clientOffsetY () const nothrow @safe @nogc { return decorationSizeY
/2; }
124 // returns `true` if need redraw
125 bool setProgress (const(char)[] v
, uint curr
, uint total
) {
130 int xwdt
= gxTextWidthUtf(v
)+decorationSizeX
;
131 int xhgt
= gxTextHeightUtf
+decorationSizeY
;
132 if (xwdt
> width || xhgt
> height
) {
140 if (total
) barWidth
= curr
*cast(uint)(width
-2)/total
;
141 if (barWidth
!= lastBarWidth
) res
= true;
142 lastBarWidth
= barWidth
;
145 import core.stdc.stdio : snprintf;
146 char[128] buf = void;
148 recalcHintWindow.message = msg;
150 auto len = snprintf(buf.ptr, buf.sizeof, "%.*s: %u", cast(uint)msg.length, msg.ptr, curr*100U/total);
151 recalcHintWindow.message = buf[0..len];
156 override void onPaint () {
161 //gxDrawWindow(winrect, null, gxRGB!(255, 255, 255), gxRGB!(0, 0, 0), gxRGB!(255, 255, 255), gxRGB!(0, 0, 80));
162 gxDrawWindow(winrect
, null,
164 getColor("title-text"),
165 getColor("title-back"),
167 if (lastBarWidth
> 0) {
168 auto rc
= GxRect(x0
+1, y0
+1, lastBarWidth
, height
-2);
169 gxFillRect(rc
, getColor("bar-back")/*gxRGB!(90, 90, 180)*/);
172 //gxClipRect.shrinkBy(3, 2);
173 immutable tw
= gxTextWidthUtf(mMessage
);
174 immutable th
= gxTextHeightUtf();
175 gxDrawTextUtf(x0
+(width
-tw
)/2, y0
+(height
-th
)/2, mMessage
, getColor("text"));
177 drawWindowMinimized();
182 // prevent any default keyboard handling
183 override bool onKeySink (KeyEvent event
) {
189 // ////////////////////////////////////////////////////////////////////////// //
190 public final class ProgressWindow
: SubWindow
{
191 ProgressBarWidget pbar
;
192 private enum PadX
= 24;
193 private enum PadY
= 8;
195 this (string amessage
) {
196 int xwdt
= gxTextWidthUtf(amessage
)+PadX
*2;
197 int xhgt
= gxTextHeightUtf
+PadY
*2;
198 super(null, GxSize(xwdt
, xhgt
));
201 pbar
= new ProgressBarWidget(rootWidget
, amessage
);
202 pbar
.size
= rootWidget
.size
;
208 override @property int decorationSizeX () const nothrow @safe @nogc { return 0; }
209 override @property int decorationSizeY () const nothrow @safe @nogc { return 0; }
211 override @property int clientOffsetX () const nothrow @safe @nogc { return 0; }
212 override @property int clientOffsetY () const nothrow @safe @nogc { return 0; }
214 // returns `true` if need redraw
215 bool setProgress (const(char)[] v
, uint curr
, uint total
) {
218 if (pbar
.text
!= v
) {
220 int xwdt
= gxTextWidthUtf(v
)+PadX
*2;
221 int xhgt
= gxTextHeightUtf
+PadY
*2;
222 if (xwdt
> width || xhgt
> height
) {
223 if (xwdt
< width
) xwdt
= width
;
224 if (xhgt
< height
) xhgt
= height
;
226 rootWidget
.size
= GxSize(xwdt
, xhgt
);
227 pbar
.size
= rootWidget
.size
;
232 if (pbar
.setCurrentTotal(cast(int)curr
, cast(int)total
)) res
= true;
236 override void onPaint () {
242 drawWindowMinimized();
247 // prevent any default keyboard handling
248 override bool onKeySink (KeyEvent event
) {
254 // ////////////////////////////////////////////////////////////////////////// //
256 public class SelectPopBoxWindow : SubWindow {
257 SimpleListBoxWidget lb;
259 void delegate (Account acc) onSelected;
261 this (Account defacc) {
262 if (accounts.length > 0) {
266 lb = new SimpleListBoxWidget(this);
267 bool accFound = false;
269 foreach (immutable aidx, Account acc; accounts) {
270 if (cast(Pop3Account)acc) {
271 import std.format : format;
272 string s = "%s <%s>".format(acc.realname, acc.mail);
273 lb.appendItem(s, acc);
274 int w = gxTextWidthUtf(s)+2;
275 if (xwdt < w) xwdt = w;
277 lb.curidx = lb.length-1;
280 if (acc is defaultAcc) defAccIdx = lb.length-1;
281 xhgt += gxTextHeightUtf;
285 if (xhgt == 0) { super(); return; }
286 if (xhgt > screenHeight) xhgt = screenHeight-decorationSizeY;
288 if (!accFound && defAccIdx >= 0) lb.curidx = defAccIdx;
289 if (xwdt > screenWidth-decorationSizeX) xwdt = screenWidth-decorationSizeX;
291 super("Select Account", xwdt+decorationSizeX, xhgt+decorationSizeY);
293 lb.wh = clientHeight;
295 lb.onAction = delegate (self) {
296 if (auto acc = cast(Account)lb.itemData(lb.curidx)) {
298 if (onSelected !is null) onSelected(acc); else vbwin.beep();
310 override bool onKey (KeyEvent event) {
312 if (event == "Escape" || event == "C-Q") { close(); return true; }
313 if (event == "Enter") { lb.onAction(lb); return true; }
315 return super.onKey(event);
321 // ////////////////////////////////////////////////////////////////////////// //
323 public class SelectAddressBookWindow : SubWindow {
324 SimpleListBoxWidget lb;
326 void delegate (AddressBookEntry acc) onSelected;
328 this (const(char)[] prefix) {
329 if (abook.length > 0) {
332 auto defae = abookFindByNickFirst(prefix);
333 if (defae is null && prefix.length) {
334 auto mstx = prefix.lastIndexOf('<');
336 prefix = prefix[mstx+1..$];
337 if (prefix.length && prefix[$-1] == '>') prefix = prefix[0..$-1];
339 defae = abookFindByMailFirst(prefix);
342 lb = new SimpleListBoxWidget(this);
343 foreach (immutable aidx, AddressBookEntry ae; abook) {
344 import std.format : format;
346 if (ae.realname.length) s = ae.realname~" <"~ae.mail~">"; else s = ae.mail;
347 lb.appendItem(s, ae);
348 int w = gxTextWidthUtf(s)+2;
349 if (xwdt < w) xwdt = w;
350 if (ae is defae) lb.curidx = lb.length-1;
351 xhgt += gxTextHeightUtf;
354 if (xhgt == 0) { super(); return; }
355 if (xhgt > screenHeight) xhgt = screenHeight-decorationSizeY;
357 if (xwdt > screenWidth-decorationSizeX) xwdt = screenWidth-decorationSizeX;
359 super("Select Recepient", xwdt+decorationSizeX, xhgt+decorationSizeY);
361 lb.wh = clientHeight;
363 lb.onAction = delegate (self) {
364 if (auto ae = cast(AddressBookEntry)lb.itemData(lb.curidx)) {
366 if (onSelected !is null) onSelected(ae); else vbwin.beep();
378 override bool onKey (KeyEvent event) {
380 if (event == "Escape" || event == "C-Q") { close(); return true; }
381 if (event == "Enter") { lb.onAction(lb); return true; }
383 return super.onKey(event);
389 // ////////////////////////////////////////////////////////////////////////// //
391 public class PostWindow : SubWindow {
397 string references; // of replyto article
402 import std.algorithm : max;
404 int wanthgt = screenHeight-42*2;
405 if (wanthgt < 80) wanthgt = 80;
406 int wantwdt = screenWidth-64*2;
407 if (wantwdt < 506) wantwdt = 506;
408 super("Compose Mail", /*506*/wantwdt, /*253*/wanthgt);
409 //if (hasWindowClass(this)) return;
411 from = new LineEditWidget(this, "From:");
412 to = new LineEditWidget(this, "To:");
413 subj = new LineEditWidget(this, "Subj:");
414 ed = new EditorWidget(this);
418 int tw = max(from.titwdt, to.titwdt, subj.titwdt);
424 to.wy = from.wy+from.wh+2;
425 subj.wy = to.wy+to.wh+2;
428 ed.wy = subj.wy+subj.wh+2;
429 ed.ww = gxClipRect.width;
430 ed.wh = gxClipRect.height-ed.wy;
435 override bool onKey (KeyEvent event) {
437 if (event == "Escape" && !ed.editor.textChanged) { close(); return true; }
438 if (event == "C-G" || event == "C-C") {
439 if (ed.editor.textChanged) {
440 auto qww = new YesNoWindow("Close?", "Do you really want to close the editor?", true);
441 qww.onYes = () { close(); };
448 if (event == "M-Tab" && activeWidget is to) {
449 auto ae = abookFindByNick(to.str);
450 //if (ae is null) ae = abookFindByMail(to.str);
452 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
458 if (event == "C-Space" && activeWidget is to) {
459 auto wae = new SelectAddressBookWindow(to.str);
460 wae.onSelected = delegate (AddressBookEntry ae) {
461 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
465 if ((event == "C-Space") && activeWidget is from) {
466 if (cast(Pop3Account)acc) {
467 auto wacc = new SelectPopBoxWindow(acc);
468 wacc.onSelected = delegate (Account newacc) {
470 from.str = acc.realname~" <"~acc.mail~">";
475 if (event == "C-Enter") {
476 static bool checkString (string s) nothrow @trusted @nogc {
477 if (s.length == 0) return false;
478 if (s.utflen > 255) return false;
481 if (!checkString(subj.str)) { vbwin.beep(); return true; }
482 if (!checkString(to.str)) { vbwin.beep(); return true; }
483 if (ed.editor[].length == 0) { vbwin.beep(); return true; }
484 auto senddg = delegate () {
485 // build reply article and add it to send queue
487 ed.editor.clearAndDisableUndo(); // so removing attaches will not add 'em to undo, lol
488 string[] attnames = ed.extractAttaches();
489 if (attnames) conwriteln("ATTACHES: ", attnames);
490 // build article text
491 auto newart = new Article();
492 newart.markAsStandalone();
493 newart.attachedFileNames = attnames;
495 //foreach (immutable idx; 0..lcount) newart.appendTextLine(ed.editor[idx]);
497 auto mtr = ed.editor[];
498 if (mtr.length) newart.appendTextRange(mtr); else newart.appendTextLine("no text");
500 // clear editor, so it free used memory
501 ed.editor.clearAndDisableUndo();
504 newart.subj = subj.str;
505 //newart.inreplyto = replyto;
506 if (replyto.length) newart.replaceHeader("In-Reply-To", replyto);
507 if (references.length) newart.replaceHeader("References", references);
508 newart.replaceHeader("To", encodeq(to.str));
509 if (!acc.addToSendQueue(fld, newart)) { vbwin.beep(); return; }
512 auto qww = new YesNoWindow("Send?", "Do you really want to send the message?", true);
518 return super.onKey(event);
524 // ////////////////////////////////////////////////////////////////////////// //
525 public class TitlerWindow
: SubWindow
{
526 LineEditWidget edtTitle
;
527 LineEditWidget fromName
;
528 LineEditWidget fromMail
;
529 //LineEditWidget fromTag;
534 bool delegate (const(char)[] name
, const(char)[] mail
, const(char)[] folder
, const(char)[] title
) onSelected
;
536 this (const(char)[] aname
, const(char)[] amail
, const(char)[] afolder
, const(char)[] atitle
) {
537 import std
.algorithm
: max
;
538 int xhgt
= gxTextHeightUtf
+2;
540 DynStr caption
= "Title for ";
545 super(caption
.getData
.idup
/*, GxSize(506, xhgt+decorationSizeY)*/);
547 new SpacerWidget(rootWidget
, 2);
548 fromName
= new LineEditWidget(rootWidget
, "Name:");
551 new SpacerWidget(rootWidget
, 2);
552 fromMail
= new LineEditWidget(rootWidget
, "Mail:");
555 new SpacerWidget(rootWidget
, 2);
556 edtTitle
= new LineEditWidget(rootWidget
, "Title:");
558 new SpacerWidget(rootWidget
, 2);
560 fromName
.str = aname
;
561 fromMail
.str = amail
;
562 edtTitle
.str = atitle
;
565 int twdt
= fromName
.titwdt
;
566 if (twdt
< fromMail
.titwdt
) twdt
= fromMail
.titwdt
;
567 if (twdt
< edtTitle
.titwdt
) twdt
= edtTitle
.titwdt
;
568 fromName
.titwdt
= twdt
;
569 fromMail
.titwdt
= twdt
;
570 edtTitle
.titwdt
= twdt
;
580 override bool onKeyBubble (KeyEvent event
) {
582 if (event
== "Escape" || event
== "C-Q") { close(); return true; }
583 if (event
== "Enter") {
584 if (onSelected
!is null) {
585 if (!onSelected(fromName
.str, fromMail
.str, folder
, edtTitle
.str)) return true;
591 return super.onKeyBubble(event
);
596 // ////////////////////////////////////////////////////////////////////////// //
597 public class TagOptionsWindow
: SubWindow
{
598 LabelWidget optPath
; // real path
599 LineEditWidget optMonthes
;
600 CheckboxWidget optThreaded
;
601 CheckboxWidget optAttaches
;
604 void delegate (const(char)[] tagname
) onUpdated
;
606 this (const(char)[] atagname
) {
609 DynStr caption
= "options for '";
612 super(caption
.getData
.idup
/*, GxSize(706, 64)*/);
614 optPath
= new LabelWidget(rootWidget
, "", LabelWidget
.HAlign
.Center
);
617 optMonthes
= new LineEditWidget(rootWidget
, "Monthes:");
619 optMonthes
.width
= optMonthes
.titwdt
+64;
621 optThreaded
= new CheckboxWidget(rootWidget
, "&Threaded");
622 optThreaded
.flex
= 1;
624 optAttaches
= new CheckboxWidget(rootWidget
, "&Attaches");
625 optAttaches
.flex
= 1;
630 optPath = new LabelWidget(this, "", LabelWidget.HAlign.Center);
632 optPath.ww = clientWidth;
634 optMonthes = new LineEditWidget(this, "Monthes:");
635 optMonthes.wy = optPath.wy+optPath.wh+2;
636 optMonthes.ww = clientWidth;
638 optThreaded = new CheckboxWidget(this, "&Threaded");
639 optThreaded.ww = clientWidth;
640 optThreaded.wy = optMonthes.wy+optMonthes.wh+2;
642 optAttaches = new CheckboxWidget(this, "&Attaches");
643 optAttaches.ww = clientWidth;
644 optAttaches.wy = optThreaded.wy+optThreaded.wh+2;
646 activeWidget = optMonthes;
649 immutable bool goodTag
= (chiroGetTagUid(tagname
.getData
) != 0);
651 import std
.conv
: to
;
653 DynStr path
= chiroGetTagMonthLimitEx(tagname
.getData
, out val
, defval
:6);
654 //conwriteln("TAGNAME=<", tagname.getData, ">; val=", val, "; path=<", path.getData, ">");
655 optPath
.text
= path
.getData
;
656 optMonthes
.str = val
.to
!string
;
657 optPath
.width
= gxTextWidthUtf(optPath
.text
)+4;
661 foreach (auto row
; dbView
.statement(`
662 SELECT threading AS trd, noattaches AS noatt FROM tagnames WHERE tag=:tag LIMIT 1
663 ;`).bindConstText(":tag", atagname
).range
)
665 optThreaded
.checked
= (row
.trd
!int == 1);
666 optAttaches
.checked
= (row
.noatt
!int == 0);
669 optThreaded
.enabled
= false;
670 optAttaches
.enabled
= false;
673 optMonthes
.killTextOnChar
= true;
675 minWinSize
.w
= optPath
.width
;
681 override bool onKeyBubble (KeyEvent event
) {
683 if (event
== "Escape" || event
== "C-Q") { close(); return true; }
684 if (event
== "Enter") {
687 auto vv
= optMonthes
.str.xstrip
;
688 if (vv
.length
== 0) {
691 import std
.conv
: to
;
693 if (mv
< -1) mv
= -666; else if (mv
== 0) mv
= -1;
695 } catch (Exception
) {
698 if (mv
< -2) return true;
699 import core
.stdc
.stdio
: snprintf
;
700 char[1024] xname
= void;
701 const(char)[] tn
= tagname
.getData
;
702 auto xlen
= snprintf(xname
.ptr
, xname
.sizeof
, "/mainpane/msgview/monthlimit%s%.*s",
703 (tn
.length
&& tn
[0] != '/' ?
"/".ptr
: "".ptr
), cast(uint)tn
.length
, tn
.ptr
);
706 if (xname
[0..xlen
] != "/mainpane/msgview/monthlimit") {
707 chiroDeleteOption(xname
[0..xlen
]);
711 chiroSetOption(xname
[0..xlen
], mv
);
713 // fix threading and attaches
714 if (optThreaded
.enabled
) {
717 SET threading=:trd, noattaches=:noatt
720 .bindConstText(":name", tagname
.getData())
721 .bind(":trd", (optThreaded
.checked ?
1 : 0))
722 .bind(":noatt", (optAttaches
.checked ?
0 : 1))
725 if (onUpdated
!is null) onUpdated(tagname
.getData
);
730 return super.onKeyBubble(event
);