1 /* Invisible Vector Library
2 * simple FlexBox-based TUI engine
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv
.egtui
.editor
.dialogs
/*is aliced*/;
25 import iv
.egtui
.parser
;
28 /* history ids for editor dialogs:
32 * dlg-tabsize-tabsize"
36 * search-and-replace dialog, search pattern
39 * search-and-replace dialog, replace pattern
43 // ///////////////////////////////////////////////////////////////////////// //
44 int dialogFileModified (const(char)[] filename
, bool def
, const(char)[] query
="File was modified. Save it?") {
46 if (filename
.length
> ttyw
-10) {
49 filename
= filename
[$-(ttyw
-13)..$];
53 caption
: "File was modified!"
59 text
: `\C${query}\n\C<${dotsdots}${filename}>`
66 button
: { id
: "bttan" caption
: "&tan" }
67 spacer
: { width
: 1 } // this hack just inserts space
68 button
: { id
: "btona" caption
: "o&na" }
73 auto ctx
= FuiContext
.create();
74 //ctx.maxDimensions = FuiSize(ttyw, ttyh);
75 ctx
.parse
!(dotsdots
, filename
, query
)(laydesc
);
77 ctx
.focused
= ctx
[def ?
"bttan" : "btona"];
78 auto res
= ctx
.modalDialog
;
79 if (res
>= 0) return (ctx
.itemId(res
) == "bttan" ?
1 : 0);
84 // ///////////////////////////////////////////////////////////////////////// //
86 int dialogLineNumber (FuiHistoryManager dghisman
, int defval
=-1) {
88 caption
: "Select line number"
93 text
: `\RLine &number: `
98 id
: "dlg-linenum-lnum"
106 button
: { id
: "btok" caption
: " O&K " default }
111 auto ctx
= FuiContext
.create();
113 int edGetNum (int item
) {
114 if (auto edl
= ctx
.itemAs
!"editline"(item
)) {
116 if (ed
is null) return -1;
119 while (!rng
.empty
&& rng
.front
<= ' ') rng
.popFront();
120 if (rng
.empty ||
!rng
.front
.isdigit
) return -1;
121 while (!rng
.empty
&& rng
.front
.isdigit
) {
122 num
= num
*10+rng
.front
-'0';
125 while (!rng
.empty
&& rng
.front
<= ' ') rng
.popFront();
126 return (rng
.empty ? num
: -1);
131 int validate (FuiContext ctx
, int item
) {
132 ctx
.setEnabled(ctx
["btok"], edGetNum(item
) > 0);
136 //ctx.maxDimensions = FuiSize(ttyw, ttyh);
137 ctx
.parse
!(validate
)(laydesc
);
138 ctx
.dialogHistoryManager
= dghisman
;
140 validate(ctx
, ctx
["dlg-linenum-lnum"]);
141 //ctx.focused = ctx["dlg-linenum-lnum"];
143 import std
.conv
: to
;
144 with (ctx
.itemAs
!"editline"("dlg-linenum-lnum")) ed
.setNewText(defval
.to
!string
);
145 validate(ctx
, ctx
["dlg-linenum-lnum"]);
147 auto res
= ctx
.modalDialog
;
149 auto ln
= edGetNum(ctx
["dlg-linenum-lnum"]);
151 if (auto hisman
= ctx
.dialogHistoryManager
) {
152 import std
.conv
: to
;
153 hisman
.add("dlg-linenum-lnum", ln
.to
!string
);
162 // ///////////////////////////////////////////////////////////////////////// //
163 // <=0: invalid number
164 int dialogTabSize (FuiHistoryManager dghisman
, int defval
) {
166 caption
: "Select Tab Size"
171 text
: `\R&Tab size: `
172 dest
: "dlg-tabsize-tabsize"
176 id
: "dlg-tabsize-tabsize"
184 button
: { id
: "btok" caption
: " O&K " default }
189 auto ctx
= FuiContext
.create();
191 int edGetNum (int item
) {
192 if (auto edl
= ctx
.itemAs
!"editline"(item
)) {
194 if (ed
is null) return -1;
197 while (!rng
.empty
&& rng
.front
<= ' ') rng
.popFront();
198 if (rng
.empty ||
!rng
.front
.isdigit
) return -1;
199 while (!rng
.empty
&& rng
.front
.isdigit
) {
200 num
= num
*10+rng
.front
-'0';
203 while (!rng
.empty
&& rng
.front
<= ' ') rng
.popFront();
204 return (rng
.empty ? num
: -1);
209 int validate (FuiContext ctx
, int item
) {
210 auto num
= edGetNum(item
);
211 ctx
.setEnabled(ctx
["btok"], (num
> 0 && num
<= 32));
215 //ctx.maxDimensions = FuiSize(ttyw, ttyh);
216 ctx
.parse
!(validate
)(laydesc
);
217 ctx
.dialogHistoryManager
= dghisman
;
219 validate(ctx
, ctx
["dlg-tabsize-tabsize"]);
220 //ctx.focused = ctx["dlg-tabsize-tabsize"];
222 import std
.conv
: to
;
223 with (ctx
.itemAs
!"editline"("dlg-tabsize-tabsize")) ed
.setNewText(defval
.to
!string
);
224 validate(ctx
, ctx
["dlg-tabsize-tabsize"]);
226 auto res
= ctx
.modalDialog
;
228 auto ts
= edGetNum(ctx
["dlg-tabsize-tabsize"]);
230 if (auto hisman
= ctx
.dialogHistoryManager
) {
231 import std
.conv
: to
;
232 hisman
.add("dlg-tabsize-tabsize", ts
.to
!string
);
241 // ///////////////////////////////////////////////////////////////////////// //
243 struct SearchReplaceOptions
{
244 // WARNING! keep in sync with window layout!
249 const(char)[] search
;
250 const(char)[] replace
;
251 bool inselenabled
= true; // "in selection" enabled
261 bool dialogSearchReplace (FuiHistoryManager dghisman
, ref SearchReplaceOptions opts
) {
266 label
: { caption
: "&Search string:" dest
: "dlg-srr-edsearch" }
267 editline
: { align: expand id
: "dlg-srr-edsearch" text
: "$searchstr" on
-action
: validate utfuck
: $utfuck
}
269 label
: { caption
: "Re&placement string:" dest
: "dlg-srr-edreplace" }
270 editline
: { align: expand id
: "dlg-srr-edreplace" text
: "$replacestr" on
-action
: validate utfuck
: $utfuck
}
278 radio
: { caption
: "No&rmal" bind
-var
: opttype
}
279 radio
: { caption
: "Re&gular expression" bind
-var
: opttype
}
283 checkbox
: { caption
: "Cas&e sensitive" bind
-var
: optci
}
284 checkbox
: { caption
: "&Backwards" bind
-var
: optback
}
285 checkbox
: { caption
: "&Whole words" bind
-var
: optword
}
286 checkbox
: { caption
: "In se&lection" id
: "cbinsel" bind
-var
: optsel
}
287 checkbox
: { caption
: "S&kip comments" id
: "cbnocom" bind
-var
: optnocom
}
296 button
: { id
: "btok" caption
: " O&K " default }
297 button
: { id
: "btcancel" caption
: "&Cancel" }
302 bool utfuck
= opts
.utfuck
;
303 int opttype
= opts
.type
;
304 bool optci
= opts
.casesens
;
305 bool optback
= opts
.backwards
;
306 bool optword
= opts
.wholeword
;
307 bool optsel
= opts
.inselection
;
308 bool optnocom
= opts
.nocomments
;
309 auto searchstr
= opts
.search
;
310 auto replacestr
= opts
.replace
;
312 auto ctx
= FuiContext
.create();
314 int validate (FuiContext ctx
, int item
=-1) {
316 if (auto edl
= ctx
.itemAs
!"editline"("dlg-srr-edsearch")) {
317 if (edl
.ed
.textsize
== 0) ok
= false;
319 ctx
.setEnabled(ctx
["btok"], ok
);
320 ctx
.setEnabled(ctx
["cbinsel"], opts
.inselenabled
);
326 //opttype = SearchReplaceOptions.Type.Regex;
328 ctx
.parse
!(opttype
, optci
, optback
, optsel
, searchstr
, replacestr
, validate
, utfuck
, optword
, optnocom
)(laydesc
);
329 ctx
.dialogHistoryManager
= dghisman
;
331 if (ctx
.layprops(0).position
.w
< ttyw
/3*2) {
332 ctx
.layprops(0).minSize
.w
= ttyw
/3*2;
336 auto res
= ctx
.modalDialog
;
337 if (ctx
.itemId(res
) == "btok") {
338 opts
.type
= cast(SearchReplaceOptions
.Type
)(opttype
>= SearchReplaceOptions
.Type
.min
&& opttype
<= SearchReplaceOptions
.Type
.max ? opttype
: 0);
339 opts
.casesens
= optci
;
340 opts
.backwards
= optback
;
341 opts
.wholeword
= optword
;
342 opts
.inselection
= optsel
;
343 opts
.nocomments
= optnocom
;
344 opts
.search
= ctx
.editlineGetText(ctx
["dlg-srr-edsearch"]);
345 opts
.replace
= ctx
.editlineGetText(ctx
["dlg-srr-edreplace"]);
346 if (auto hisman
= ctx
.dialogHistoryManager
) {
347 hisman
.add("dlg-srr-edsearch", opts
.search
);
348 hisman
.add("dlg-srr-edreplace", opts
.replace
);
356 // ///////////////////////////////////////////////////////////////////////// //
357 enum DialogRepPromptResult
{
364 DialogRepPromptResult
dialogReplacePrompt (int sy
=-1) {
366 caption
: "Confirm replace"
369 label
: { align: expand caption
: `\CPattern found. What to do?` }
376 button
: { id
: "btreplace" caption
: " &Replace " default }
377 button
: { id
: "btall" caption
: "A&ll" }
378 button
: { id
: "btskip" caption
: "&Skip" }
379 button
: { id
: "btcancel" caption
: "&Cancel" }
384 auto ctx
= FuiContext
.create();
387 if (sy
>= 0 && sy
< ttyh
) {
388 if (sy
+1+ctx
.layprops(0).position
.h
< ttyh
-1) {
389 ctx
.layprops(0).position
.y
= sy
+1;
390 } else if (sy
-1-ctx
.layprops(0).position
.h
>= 0) {
391 ctx
.layprops(0).position
.y
= sy
-1-ctx
.layprops(0).position
.h
;
394 auto res
= ctx
.modalDialog
;
395 if (res
< 0) return DialogRepPromptResult
.Cancel
;
396 auto rid
= ctx
.itemId(res
);
397 if (rid
== "btreplace") return DialogRepPromptResult
.Replace
;
398 if (rid
== "btall") return DialogRepPromptResult
.All
;
399 if (rid
== "btskip") return DialogRepPromptResult
.Skip
;
400 return DialogRepPromptResult
.Cancel
;
404 // ///////////////////////////////////////////////////////////////////////// //
406 int dialogCodePage (int curcp
) {
408 caption
: "Select codepage"
426 button
: { id
: "btok" caption
: " O&K " default }
427 button
: { id
: "btcancel" caption
: "&Cancel" }
432 auto ctx
= FuiContext
.create();
435 if (curcp
< 0) curcp
= 0; else if (curcp
> 3) curcp
= 3;
436 ctx
.listboxItemSetCurrent(ctx
["lbcp"], curcp
);
437 //ctx.setDialogPalette(TuiPaletteError);
438 auto res
= ctx
.modalDialog
;
439 if (ctx
.itemId(res
) != "btok") return -1;
440 return ctx
.listboxItemCurrent(ctx
["lbcp"]);
444 // ///////////////////////////////////////////////////////////////////////// //
445 // return -1 on escape or index
446 // tries to show it under (or above) (winx, winy), so the line itself is visible
447 int dialogSelectAC(T
: const(char)[]) (T
[] items
, int winx
, int winy
, int idx
=0, int maxhgt
=-1) {
448 if (items
.length
== 0) return -1;
450 if (maxhgt
< 0 || maxhgt
> ttyh
) maxhgt
= ttyh
;
451 if (maxhgt
< 3) maxhgt
= 3;
454 if (maxwdt
< 3) maxwdt
= 3;
458 foreach (const s
; items
) if (s
.length
> maxlen
) maxlen
= cast(int)s
.length
;
459 if (maxlen
> ttyw
-4) maxlen
= ttyw
-4;
461 int pgsize
= cast(int)items
.length
;
462 if (pgsize
> maxhgt
-2) pgsize
= maxhgt
-2;
465 winx
= (ttyw
-(maxlen
+4))/2;
466 if (winx
< 0) winx
= 0;
469 winy
= (ttyh
-(pgsize
+2))/2;
470 if (winy
< 0) winy
= 0;
475 // no room to show it at the bottom?
476 if (y0
+pgsize
+1 > ttyh
) {
478 // no room to show it at the top? center it then
479 if (y0
< 0 || y0
>= ttyh
) y0
= (ttyh
-(pgsize
+2))/2;
481 if (x0
+maxlen
+4 > ttyw
) x0
= ttyw
-maxlen
-4;
485 int winhgt
= pgsize
+2;
486 int winwdt
= maxlen
+4;
489 //caption: "Completions"
504 auto ctx
= FuiContext
.create();
505 ctx
.parse
!(winhgt
, winwdt
, maxwdt
, maxhgt
)(laydesc
);
508 auto lbi
= ctx
["lbac"];
510 foreach (const s
; items
) ctx
.listboxItemAdd(lbi
, s
);
511 //ctx.listboxItemSetCurrent(lbi, cast(int)items.length-1);
514 ctx
.layprops(0).position
.x
= x0
;
515 ctx
.layprops(0).position
.y
= y0
;
516 auto res
= ctx
.modalDialog
!false; // don't center
517 if (res
< 0) return -1;
518 return ctx
.listboxItemCurrent(lbi
);