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*/;
35 // ////////////////////////////////////////////////////////////////////////// //
36 public final class HintWindow
: SubWindow
{
37 private DynStr mMessage
;
39 this (const(char)[] amessage
) {
42 int xwdt
= gxTextWidthUtf(amessage
)+6;
43 int xhgt
= gxTextHeightUtf
+4;
44 super(null, GxSize(xwdt
, xhgt
));
45 x0
= screenWidth
-width
;
51 override @property int decorationSizeX () const nothrow @safe @nogc { return 3*2; }
52 override @property int decorationSizeY () const nothrow @safe @nogc { return 2*2; }
54 override @property int clientOffsetX () const nothrow @safe @nogc { return decorationSizeX
/2; }
55 override @property int clientOffsetY () const nothrow @safe @nogc { return decorationSizeY
/2; }
57 @property void message (DynStr v
) {
60 if (x0
== screenWidth
-width
) {
61 width
= gxTextWidthUtf(v
)+6;
62 x0
= screenWidth
-width
;
64 width
= gxTextWidthUtf(v
)+6;
65 if (x0
+width
> screenWidth
) x0
= screenWidth
-width
;
71 override void onPaint () {
74 immutable string oldtitle
= mWinTitle
;
75 scope(exit
) mWinTitle
= oldtitle
;
76 if (!minimised
) mWinTitle
= null;
79 if (!minimised
&& mMessage
.length
) {
82 gxClipRect
.shrinkBy(3, 2);
83 gxDrawTextUtf(x0
+3, y0
+2, mMessage
, getColor("text"));
88 // prevent any default keyboard handling
89 override bool onKeySink (KeyEvent event
) {
95 // ////////////////////////////////////////////////////////////////////////// //
96 public final class MessageWindow
: SubWindow
{
97 private DynStr mMessage
;
98 private uint lastBarWidth
;
100 this (const(char)[] amessage
) {
103 int xwdt
= gxTextWidthUtf(amessage
)+decorationSizeX
;
104 int xhgt
= gxTextHeightUtf
+decorationSizeY
;
105 super(null, GxSize(xwdt
, xhgt
));
111 override @property int decorationSizeX () const nothrow @safe @nogc { return 24; }
112 override @property int decorationSizeY () const nothrow @safe @nogc { return 16; }
114 override @property int clientOffsetX () const nothrow @safe @nogc { return decorationSizeX
/2; }
115 override @property int clientOffsetY () const nothrow @safe @nogc { return decorationSizeY
/2; }
117 // returns `true` if need redraw
118 bool setProgress (const(char)[] v
, uint curr
, uint total
) {
123 int xwdt
= gxTextWidthUtf(v
)+decorationSizeX
;
124 int xhgt
= gxTextHeightUtf
+decorationSizeY
;
125 if (xwdt
> width || xhgt
> height
) {
133 if (total
) barWidth
= curr
*cast(uint)(width
-2)/total
;
134 if (barWidth
!= lastBarWidth
) res
= true;
135 lastBarWidth
= barWidth
;
139 override void onPaint () {
142 immutable string oldtitle
= mWinTitle
;
143 scope(exit
) mWinTitle
= oldtitle
;
144 if (!minimised
) mWinTitle
= null;
147 if (!minimised
&& mMessage
.length
) {
150 if (lastBarWidth
> 0) {
151 auto rc
= GxRect(x0
+1, y0
+1, lastBarWidth
, height
-2);
152 gxFillRect(rc
, getColor("bar-back"));
155 //gxClipRect.shrinkBy(3, 2);
156 immutable tw
= gxTextWidthUtf(mMessage
);
157 immutable th
= gxTextHeightUtf();
158 gxDrawTextUtf(x0
+(width
-tw
)/2, y0
+(height
-th
)/2, mMessage
, getColor("text"));
163 // prevent any default keyboard handling
164 override bool onKeySink (KeyEvent event
) {
170 // ////////////////////////////////////////////////////////////////////////// //
171 public final class ProgressWindow
: SubWindow
{
172 ProgressBarWidget pbar
;
173 private enum PadX
= 24;
174 private enum PadY
= 8;
176 this (string amessage
) {
177 int xwdt
= gxTextWidthUtf(amessage
)+PadX
*2;
178 int xhgt
= gxTextHeightUtf
+PadY
*2;
179 super(null, GxSize(xwdt
, xhgt
));
182 pbar
= new ProgressBarWidget(rootWidget
, amessage
);
183 pbar
.size
= rootWidget
.size
;
189 override @property int decorationSizeX () const nothrow @safe @nogc { return 0; }
190 override @property int decorationSizeY () const nothrow @safe @nogc { return 0; }
192 override @property int clientOffsetX () const nothrow @safe @nogc { return 0; }
193 override @property int clientOffsetY () const nothrow @safe @nogc { return 0; }
195 // returns `true` if need redraw
196 bool setProgress (const(char)[] v
, uint curr
, uint total
) {
199 if (pbar
.text
!= v
) {
201 int xwdt
= gxTextWidthUtf(v
)+PadX
*2;
202 int xhgt
= gxTextHeightUtf
+PadY
*2;
203 if (xwdt
> width || xhgt
> height
) {
204 if (xwdt
< width
) xwdt
= width
;
205 if (xhgt
< height
) xhgt
= height
;
207 rootWidget
.size
= GxSize(xwdt
, xhgt
);
208 pbar
.size
= rootWidget
.size
;
213 if (pbar
.setCurrentTotal(cast(int)curr
, cast(int)total
)) res
= true;
217 // prevent any default keyboard handling
218 override bool onKeySink (KeyEvent event
) {
224 // ////////////////////////////////////////////////////////////////////////// //
226 public class SelectPopBoxWindow : SubWindow {
227 SimpleListBoxWidget lb;
229 void delegate (Account acc) onSelected;
231 this (Account defacc) {
232 if (accounts.length > 0) {
236 lb = new SimpleListBoxWidget(this);
237 bool accFound = false;
239 foreach (immutable aidx, Account acc; accounts) {
240 if (cast(Pop3Account)acc) {
241 import std.format : format;
242 string s = "%s <%s>".format(acc.realname, acc.mail);
243 lb.appendItem(s, acc);
244 int w = gxTextWidthUtf(s)+2;
245 if (xwdt < w) xwdt = w;
247 lb.curidx = lb.length-1;
250 if (acc is defaultAcc) defAccIdx = lb.length-1;
251 xhgt += gxTextHeightUtf;
255 if (xhgt == 0) { super(); return; }
256 if (xhgt > screenHeight) xhgt = screenHeight-decorationSizeY;
258 if (!accFound && defAccIdx >= 0) lb.curidx = defAccIdx;
259 if (xwdt > screenWidth-decorationSizeX) xwdt = screenWidth-decorationSizeX;
261 super("Select Account", xwdt+decorationSizeX, xhgt+decorationSizeY);
263 lb.wh = clientHeight;
265 lb.onAction = delegate (self) {
266 if (auto acc = cast(Account)lb.itemData(lb.curidx)) {
268 if (onSelected !is null) onSelected(acc); else vbwin.beep();
280 override bool onKey (KeyEvent event) {
282 if (event == "Escape" || event == "C-Q") { close(); return true; }
283 if (event == "Enter") { lb.onAction(lb); return true; }
285 return super.onKey(event);
291 // ////////////////////////////////////////////////////////////////////////// //
293 public class SelectAddressBookWindow : SubWindow {
294 SimpleListBoxWidget lb;
296 void delegate (AddressBookEntry acc) onSelected;
298 this (const(char)[] prefix) {
299 if (abook.length > 0) {
302 auto defae = abookFindByNickFirst(prefix);
303 if (defae is null && prefix.length) {
304 auto mstx = prefix.lastIndexOf('<');
306 prefix = prefix[mstx+1..$];
307 if (prefix.length && prefix[$-1] == '>') prefix = prefix[0..$-1];
309 defae = abookFindByMailFirst(prefix);
312 lb = new SimpleListBoxWidget(this);
313 foreach (immutable aidx, AddressBookEntry ae; abook) {
314 import std.format : format;
316 if (ae.realname.length) s = ae.realname~" <"~ae.mail~">"; else s = ae.mail;
317 lb.appendItem(s, ae);
318 int w = gxTextWidthUtf(s)+2;
319 if (xwdt < w) xwdt = w;
320 if (ae is defae) lb.curidx = lb.length-1;
321 xhgt += gxTextHeightUtf;
324 if (xhgt == 0) { super(); return; }
325 if (xhgt > screenHeight) xhgt = screenHeight-decorationSizeY;
327 if (xwdt > screenWidth-decorationSizeX) xwdt = screenWidth-decorationSizeX;
329 super("Select Recepient", xwdt+decorationSizeX, xhgt+decorationSizeY);
331 lb.wh = clientHeight;
333 lb.onAction = delegate (self) {
334 if (auto ae = cast(AddressBookEntry)lb.itemData(lb.curidx)) {
336 if (onSelected !is null) onSelected(ae); else vbwin.beep();
348 override bool onKey (KeyEvent event) {
350 if (event == "Escape" || event == "C-Q") { close(); return true; }
351 if (event == "Enter") { lb.onAction(lb); return true; }
353 return super.onKey(event);
359 // ////////////////////////////////////////////////////////////////////////// //
361 public class PostWindow : SubWindow {
367 string references; // of replyto article
372 import std.algorithm : max;
374 int wanthgt = screenHeight-42*2;
375 if (wanthgt < 80) wanthgt = 80;
376 int wantwdt = screenWidth-64*2;
377 if (wantwdt < 506) wantwdt = 506;
378 super("Compose Mail", /*506*/wantwdt, /*253*/wanthgt);
379 //if (hasWindowClass(this)) return;
381 from = new LineEditWidget(this, "From:");
382 to = new LineEditWidget(this, "To:");
383 subj = new LineEditWidget(this, "Subj:");
384 ed = new EditorWidget(this);
388 int tw = max(from.titwdt, to.titwdt, subj.titwdt);
394 to.wy = from.wy+from.wh+2;
395 subj.wy = to.wy+to.wh+2;
398 ed.wy = subj.wy+subj.wh+2;
399 ed.ww = gxClipRect.width;
400 ed.wh = gxClipRect.height-ed.wy;
405 override bool onKey (KeyEvent event) {
407 if (event == "Escape" && !ed.editor.textChanged) { close(); return true; }
408 if (event == "C-G" || event == "C-C") {
409 if (ed.editor.textChanged) {
410 auto qww = new YesNoWindow("Close?", "Do you really want to close the editor?", true);
411 qww.onYes = () { close(); };
418 if (event == "M-Tab" && activeWidget is to) {
419 auto ae = abookFindByNick(to.str);
420 //if (ae is null) ae = abookFindByMail(to.str);
422 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
428 if (event == "C-Space" && activeWidget is to) {
429 auto wae = new SelectAddressBookWindow(to.str);
430 wae.onSelected = delegate (AddressBookEntry ae) {
431 if (ae.realname.length) to.str = ae.realname~" <"~ae.mail~">"; else to.str = ae.mail;
435 if ((event == "C-Space") && activeWidget is from) {
436 if (cast(Pop3Account)acc) {
437 auto wacc = new SelectPopBoxWindow(acc);
438 wacc.onSelected = delegate (Account newacc) {
440 from.str = acc.realname~" <"~acc.mail~">";
445 if (event == "C-Enter") {
446 static bool checkString (string s) nothrow @trusted @nogc {
447 if (s.length == 0) return false;
448 if (s.utflen > 255) return false;
451 if (!checkString(subj.str)) { vbwin.beep(); return true; }
452 if (!checkString(to.str)) { vbwin.beep(); return true; }
453 if (ed.editor[].length == 0) { vbwin.beep(); return true; }
454 auto senddg = delegate () {
455 // build reply article and add it to send queue
457 ed.editor.clearAndDisableUndo(); // so removing attaches will not add 'em to undo, lol
458 string[] attnames = ed.extractAttaches();
459 if (attnames) conwriteln("ATTACHES: ", attnames);
460 // build article text
461 auto newart = new Article();
462 newart.markAsStandalone();
463 newart.attachedFileNames = attnames;
465 //foreach (immutable idx; 0..lcount) newart.appendTextLine(ed.editor[idx]);
467 auto mtr = ed.editor[];
468 if (mtr.length) newart.appendTextRange(mtr); else newart.appendTextLine("no text");
470 // clear editor, so it free used memory
471 ed.editor.clearAndDisableUndo();
474 newart.subj = subj.str;
475 //newart.inreplyto = replyto;
476 if (replyto.length) newart.replaceHeader("In-Reply-To", replyto);
477 if (references.length) newart.replaceHeader("References", references);
478 newart.replaceHeader("To", encodeq(to.str));
479 if (!acc.addToSendQueue(fld, newart)) { vbwin.beep(); return; }
482 auto qww = new YesNoWindow("Send?", "Do you really want to send the message?", true);
488 return super.onKey(event);
494 // ////////////////////////////////////////////////////////////////////////// //
495 public class TitlerWindow
: SubWindow
{
496 LineEditWidget edtTitle
;
497 LineEditWidget fromName
;
498 LineEditWidget fromMail
;
499 //LineEditWidget fromTag;
505 bool delegate (const(char)[] name
, const(char)[] mail
, const(char)[] folder
, const(char)[] title
) onSelected
;
507 override void createWidgets () {
508 new SpacerWidget(rootWidget
, 2);
511 fromName
= new LineEditWidget(rootWidget
, "Name:");
514 new SpacerWidget(rootWidget
, 2);
515 fromMail
= new LineEditWidget(rootWidget
, "Mail:");
518 new SpacerWidget(rootWidget
, 2);
519 edtTitle
= new LineEditWidget(rootWidget
, "Title:");
522 (new HBoxWidget
).enter{
523 with (new HotLabelWidget("&Name:", LabelWidget
.HAlign
.Right
)) { width
= width
+2; hsizeId
= "editors"; }
525 fromName
= new LineEditWidget();
530 (new HBoxWidget
).enter{
531 with (new HotLabelWidget("&Mail:", LabelWidget
.HAlign
.Right
)) { width
= width
+2; hsizeId
= "editors"; }
533 fromMail
= new LineEditWidget();
538 (new HBoxWidget
).enter{
539 with (new HotLabelWidget("&Title:", LabelWidget
.HAlign
.Right
)) { width
= width
+2; hsizeId
= "editors"; }
541 edtTitle
= new LineEditWidget();
546 new SpacerWidget(rootWidget
, 2);
550 edtTitle
.str = title
;
560 this (const(char)[] aname
, const(char)[] amail
, const(char)[] afolder
, const(char)[] atitle
) {
566 DynStr caption
= "Title for ";
571 super(caption
.getData
);
574 override bool onKeyBubble (KeyEvent event
) {
576 if (event
== "Escape" || event
== "C-Q") { close(); return true; }
577 if (event
== "Enter") {
578 if (onSelected
!is null) {
579 if (!onSelected(fromName
.str, fromMail
.str, folder
, edtTitle
.str)) return true;
585 return super.onKeyBubble(event
);
590 // ////////////////////////////////////////////////////////////////////////// //
591 public class TagOptionsWindow
: SubWindow
{
592 LabelWidget optPath
; // real path
593 LineEditWidget optMonthes
;
594 CheckboxWidget optThreaded
;
595 CheckboxWidget optAttaches
;
598 void delegate (const(char)[] tagname
) onUpdated
;
600 override void createWidgets () {
601 optPath
= new LabelWidget(rootWidget
, "", LabelWidget
.HAlign
.Center
);
605 optMonthes
= new LineEditWidget(rootWidget
, "Monthes:");
607 optMonthes
.width
= optMonthes
.titwdt
+64;
609 (new HBoxWidget
).enter{
610 with (new HotLabelWidget("&Monthes:", LabelWidget
.HAlign
.Right
)) { width
= width
+2; /*hsizeId = "editors";*/ }
612 optMonthes
= new LineEditWidget();
613 //optMonthes.flex = 1;
614 optMonthes
.width
= gxTextWidthUtf("96669");
615 //new SpringWidget(1);
619 optThreaded
= new CheckboxWidget(rootWidget
, "&Threaded");
620 optThreaded
.flex
= 1;
622 optAttaches
= new CheckboxWidget(rootWidget
, "&Attaches");
623 optAttaches
.flex
= 1;
627 immutable bool goodTag
= (chiroGetTagUid(tagname
.getData
) != 0);
629 import std
.conv
: to
;
631 DynStr path
= chiroGetTagMonthLimitEx(tagname
.getData
, out val
, defval
:6);
632 //conwriteln("TAGNAME=<", tagname.getData, ">; val=", val, "; path=<", path.getData, ">");
633 optPath
.text
= path
.getData
;
634 optMonthes
.str = val
.to
!string
;
635 optPath
.width
= gxTextWidthUtf(optPath
.text
)+4;
639 foreach (auto row
; dbView
.statement(`
640 SELECT threading AS trd, noattaches AS noatt FROM tagnames WHERE tag=:tag LIMIT 1
641 ;`).bindConstText(":tag", tagname
.getData
).range
)
643 optThreaded
.checked
= (row
.trd
!int == 1);
644 optAttaches
.checked
= (row
.noatt
!int == 0);
647 optThreaded
.enabled
= false;
648 optAttaches
.enabled
= false;
651 optMonthes
.killTextOnChar
= true;
653 minWinSize
.w
= optPath
.width
;
659 this (const(char)[] atagname
) {
662 DynStr caption
= "options for '";
665 super(caption
.getData
);
668 override bool onKeyBubble (KeyEvent event
) {
670 if (event
== "Escape" || event
== "C-Q") { close(); return true; }
671 if (event
== "Enter") {
674 auto vv
= optMonthes
.str.xstrip
;
675 if (vv
.length
== 0) {
678 import std
.conv
: to
;
680 if (mv
< -1) mv
= -666; else if (mv
== 0) mv
= -1;
682 } catch (Exception
) {
685 if (mv
< -2) return true;
686 import core
.stdc
.stdio
: snprintf
;
687 char[1024] xname
= void;
688 const(char)[] tn
= tagname
.getData
;
689 auto xlen
= snprintf(xname
.ptr
, xname
.sizeof
, "/mainpane/msgview/monthlimit%s%.*s",
690 (tn
.length
&& tn
[0] != '/' ?
"/".ptr
: "".ptr
), cast(uint)tn
.length
, tn
.ptr
);
693 if (xname
[0..xlen
] != "/mainpane/msgview/monthlimit") {
694 chiroDeleteOption(xname
[0..xlen
]);
698 chiroSetOption(xname
[0..xlen
], mv
);
700 // fix threading and attaches
701 if (optThreaded
.enabled
) {
704 SET threading=:trd, noattaches=:noatt
707 .bindConstText(":name", tagname
.getData())
708 .bind(":trd", (optThreaded
.checked ?
1 : 0))
709 .bind(":noatt", (optAttaches
.checked ?
0 : 1))
712 if (onUpdated
!is null) onUpdated(tagname
.getData
);
717 return super.onKeyBubble(event
);