Releasing version 3-2014010505
[notion/jeffpc.git] / mod_query / wedln.c
blob1c43db2a7cf3402241ce00bf80bf25741e2c71c7
1 /*
2 * ion/mod_query/wedln.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #include <libtu/objp.h>
12 #include <libtu/minmax.h>
13 #include <libtu/setparam.h>
14 #include <libextl/extl.h>
15 #include <libmainloop/defer.h>
16 #include <libmainloop/signal.h>
18 #include <ioncore/common.h>
19 #include <ioncore/global.h>
20 #include <ioncore/strings.h>
21 #include <ioncore/xic.h>
22 #include <ioncore/selection.h>
23 #include <ioncore/event.h>
24 #include <ioncore/regbind.h>
25 #include <ioncore/gr-util.h>
26 #include <ioncore/sizehint.h>
27 #include <ioncore/resize.h>
28 #include "edln.h"
29 #include "wedln.h"
30 #include "inputp.h"
31 #include "complete.h"
32 #include "main.h"
35 #define WEDLN_BRUSH(X) ((X)->input.brush)
38 /*{{{ Drawing primitives */
41 static int calc_text_y(WEdln *wedln, const WRectangle *geom)
43 GrFontExtents fnte;
45 grbrush_get_font_extents(WEDLN_BRUSH(wedln), &fnte);
47 return geom->y+geom->h/2-fnte.max_height/2+fnte.baseline;
51 static int wedln_draw_strsect(WEdln *wedln, const WRectangle *geom,
52 int x, int y, const char *str, int len,
53 GrAttr a)
55 if(len==0)
56 return 0;
58 grbrush_set_attr(WEDLN_BRUSH(wedln), a);
59 grbrush_draw_string(WEDLN_BRUSH(wedln), x, y, str, len, TRUE);
60 grbrush_unset_attr(WEDLN_BRUSH(wedln), a);
62 return grbrush_get_text_width(WEDLN_BRUSH(wedln), str, len);
65 #if 0
66 static void dispu(const char* s, int l)
68 while(l>0){
69 int c=(unsigned char)*s;
70 fprintf(stderr, "%d[%c]", c, *s);
71 s++;
72 l--;
74 fprintf(stderr, "\n");
76 #endif
78 #define DSTRSECT(LEN, A) \
79 if(LEN>0){ \
80 tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty, \
81 str, LEN, GR_ATTR(A)); \
82 str+=LEN; len-=LEN; \
86 GR_DEFATTR(active);
87 GR_DEFATTR(inactive);
88 GR_DEFATTR(normal);
89 GR_DEFATTR(selection);
90 GR_DEFATTR(cursor);
91 GR_DEFATTR(prompt);
92 GR_DEFATTR(info);
94 static void init_attr()
96 GR_ALLOCATTR_BEGIN;
97 GR_ALLOCATTR(active);
98 GR_ALLOCATTR(inactive);
99 GR_ALLOCATTR(normal);
100 GR_ALLOCATTR(selection);
101 GR_ALLOCATTR(cursor);
102 GR_ALLOCATTR(prompt);
103 GR_ALLOCATTR(info);
104 GR_ALLOCATTR_END;
108 static void wedln_do_draw_str_box(WEdln *wedln, const WRectangle *geom,
109 const char *str, int cursor,
110 int mark, int tx)
112 int len=strlen(str), ll=0, ty=0;
114 ty=calc_text_y(wedln, geom);
116 if(mark<=cursor){
117 if(mark>=0){
118 DSTRSECT(mark, normal);
119 DSTRSECT(cursor-mark, selection);
120 }else{
121 DSTRSECT(cursor, normal);
123 if(len==0){
124 tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty,
125 " ", 1, GR_ATTR(cursor));
126 }else{
127 ll=str_nextoff(str, 0);
128 DSTRSECT(ll, cursor);
130 }else{
131 DSTRSECT(cursor, normal);
132 ll=str_nextoff(str, 0);
133 DSTRSECT(ll, cursor);
134 DSTRSECT(mark-cursor-ll, selection);
136 DSTRSECT(len, normal);
138 if(tx<geom->w){
139 WRectangle g=*geom;
140 g.x+=tx;
141 g.w-=tx;
142 grbrush_clear_area(WEDLN_BRUSH(wedln), &g);
147 static void wedln_draw_str_box(WEdln *wedln, const WRectangle *geom,
148 int vstart, const char *str,
149 int dstart, int point, int mark)
151 int tx=0;
153 /* Some fonts and Xmb/utf8 routines don't work well with dstart!=0. */
154 dstart=0;
156 if(mark>=0){
157 mark-=vstart+dstart;
158 if(mark<0)
159 mark=0;
162 point-=vstart+dstart;
164 if(dstart!=0)
165 tx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, dstart);
167 grbrush_begin(WEDLN_BRUSH(wedln), geom,
168 GRBRUSH_AMEND|GRBRUSH_KEEP_ATTR|GRBRUSH_NEED_CLIP);
170 wedln_do_draw_str_box(wedln, geom, str+vstart+dstart, point, mark, tx);
172 grbrush_end(WEDLN_BRUSH(wedln));
176 static bool wedln_update_cursor(WEdln *wedln, int iw)
178 int cx, l;
179 int vstart=wedln->vstart;
180 int point=wedln->edln.point;
181 int len=wedln->edln.psize;
182 int mark=wedln->edln.mark;
183 const char *str=wedln->edln.p;
184 bool ret;
186 if(point<wedln->vstart)
187 wedln->vstart=point;
189 if(wedln->vstart==point)
190 return FALSE;
192 while(vstart<point){
193 if(point==len){
194 cx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart,
195 point-vstart);
196 cx+=grbrush_get_text_width(WEDLN_BRUSH(wedln), " ", 1);
197 }else{
198 int nxt=str_nextoff(str, point);
199 cx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart,
200 point-vstart+nxt);
203 if(cx<iw)
204 break;
206 l=str_nextoff(str, vstart);
207 if(l==0)
208 break;
209 vstart+=l;
212 ret=(wedln->vstart!=vstart);
213 wedln->vstart=vstart;
215 return ret;
219 /*}}}*/
222 /*{{{ Size/location calc */
225 static int get_textarea_height(WEdln *wedln, bool with_spacing)
227 int w=1, h=1;
229 if(WEDLN_BRUSH(wedln)!=NULL)
230 mod_query_get_minimum_extents(WEDLN_BRUSH(wedln), with_spacing, &w, &h);
232 return h;
236 enum{G_NORESET, G_MAX, G_CURRENT};
239 static void get_geom(WEdln *wedln, int mode, WRectangle *geom)
241 if(mode==G_MAX)
242 *geom=wedln->input.last_fp.g;
243 else if(mode==G_CURRENT)
244 *geom=REGION_GEOM(wedln);
248 static void get_completions_geom(WEdln *wedln, int mode, WRectangle *geom)
250 get_geom(wedln, mode, geom);
251 geom->x=0;
252 geom->y=0;
254 geom->h-=get_textarea_height(wedln, TRUE);
255 if(geom->h<0)
256 geom->h=0;
260 static void get_outer_geom(WEdln *wedln, int mode, WRectangle *geom)
262 int th;
264 get_geom(wedln, mode, geom);
265 geom->x=0;
266 geom->y=0;
268 th=get_textarea_height(wedln, FALSE);
270 geom->y+=geom->h-th;
271 geom->h=th;
275 static void get_inner_geom(WEdln *wedln, int mode, WRectangle *geom)
277 GrBorderWidths bdw;
279 grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
281 get_outer_geom(wedln, mode, geom);
283 geom->x+=bdw.left;
284 geom->w-=bdw.left+bdw.right;
285 geom->y+=bdw.top;
286 geom->h-=bdw.top+bdw.bottom;
287 geom->w=maxof(0, geom->w);
288 geom->h=maxof(0, geom->h);
292 static void get_textarea_geom(WEdln *wedln, int mode, WRectangle *geom)
294 get_inner_geom(wedln, mode, geom);
295 geom->x+=wedln->prompt_w;
296 geom->w=maxof(0, geom->w - wedln->prompt_w - wedln->info_w);
300 static void wedln_calc_size(WEdln *wedln, WRectangle *geom)
302 int h, th;
303 GrBorderWidths bdw;
304 WRectangle max_geom=*geom, tageom;
306 if(WEDLN_BRUSH(wedln)==NULL)
307 return;
309 if(wedln->prompt!=NULL){
310 wedln->prompt_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
311 wedln->prompt,
312 wedln->prompt_len);
315 if(wedln->info!=NULL){
316 wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
317 wedln->info,
318 wedln->info_len);
321 th=get_textarea_height(wedln, wedln->compl_list.strs!=NULL);
323 if(wedln->compl_list.strs==NULL){
324 if(max_geom.h<th || !(wedln->input.last_fp.mode&REGION_FIT_BOUNDS))
325 geom->h=max_geom.h;
326 else
327 geom->h=th;
328 }else{
329 WRectangle g;
331 get_completions_geom(wedln, G_MAX, &g);
333 fit_listing(WEDLN_BRUSH(wedln), &g, &(wedln->compl_list));
335 grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
337 h=wedln->compl_list.toth;
338 th+=bdw.top+bdw.bottom;
340 if(h+th>max_geom.h || !(wedln->input.last_fp.mode&REGION_FIT_BOUNDS))
341 h=max_geom.h-th;
342 geom->h=h+th;
345 geom->w=max_geom.w;
346 geom->y=max_geom.y+max_geom.h-geom->h;
347 geom->x=max_geom.x;
349 tageom=*geom;
350 get_textarea_geom(wedln, G_NORESET, &tageom);
351 wedln_update_cursor(wedln, tageom.w);
355 void wedln_size_hints(WEdln *wedln, WSizeHints *hints_ret)
357 int w=1, h=1;
359 if(WEDLN_BRUSH(wedln)!=NULL){
360 mod_query_get_minimum_extents(WEDLN_BRUSH(wedln), FALSE, &w, &h);
361 w+=wedln->prompt_w+wedln->info_w;
362 w+=grbrush_get_text_width(WEDLN_BRUSH(wedln), "xxxxxxxxxx", 10);
365 hints_ret->min_set=TRUE;
366 hints_ret->min_width=w;
367 hints_ret->min_height=h;
371 /*}}}*/
374 /*{{{ Draw */
377 void wedln_draw_completions(WEdln *wedln, int mode)
379 WRectangle geom;
381 if(wedln->compl_list.strs!=NULL && WEDLN_BRUSH(wedln)!=NULL){
382 get_completions_geom(wedln, G_CURRENT, &geom);
384 draw_listing(WEDLN_BRUSH(wedln), &geom, &(wedln->compl_list),
385 mode, GR_ATTR(selection));
390 void wedln_draw_textarea(WEdln *wedln)
392 WRectangle geom;
393 int ty;
395 if(WEDLN_BRUSH(wedln)==NULL)
396 return;
398 get_outer_geom(wedln, G_CURRENT, &geom);
400 /*grbrush_begin(WEDLN_BRUSH(wedln), &geom, GRBRUSH_AMEND);*/
402 grbrush_draw_border(WEDLN_BRUSH(wedln), &geom);
404 get_inner_geom(wedln, G_CURRENT, &geom);
406 ty=calc_text_y(wedln, &geom);
408 grbrush_set_attr(WEDLN_BRUSH(wedln), GR_ATTR(prompt));
410 if(wedln->prompt!=NULL){
411 grbrush_draw_string(WEDLN_BRUSH(wedln), geom.x, ty,
412 wedln->prompt, wedln->prompt_len, TRUE);
415 if(wedln->info!=NULL){
416 int x=geom.x+geom.w-wedln->info_w;
418 grbrush_set_attr(WEDLN_BRUSH(wedln), GR_ATTR(info));
419 grbrush_draw_string(WEDLN_BRUSH(wedln), x, ty,
420 wedln->info, wedln->info_len, TRUE);
421 grbrush_unset_attr(WEDLN_BRUSH(wedln), GR_ATTR(info));
424 grbrush_unset_attr(WEDLN_BRUSH(wedln), GR_ATTR(prompt));
426 get_textarea_geom(wedln, G_CURRENT, &geom);
428 wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, 0,
429 wedln->edln.point, wedln->edln.mark);
431 /*grbrush_end(WEDLN_BRUSH(wedln));*/
435 static void wedln_draw_(WEdln *wedln, bool complete, bool completions)
437 WRectangle g;
438 int f=(complete ? 0 : GRBRUSH_NO_CLEAR_OK);
440 if(WEDLN_BRUSH(wedln)==NULL)
441 return;
443 get_geom(wedln, G_CURRENT, &g);
445 grbrush_begin(WEDLN_BRUSH(wedln), &g, f);
447 grbrush_set_attr(WEDLN_BRUSH(wedln), REGION_IS_ACTIVE(wedln)
448 ? GR_ATTR(active)
449 : GR_ATTR(inactive));
451 if(completions)
452 wedln_draw_completions(wedln, LISTING_DRAW_ALL);
454 wedln_draw_textarea(wedln);
456 grbrush_end(WEDLN_BRUSH(wedln));
460 void wedln_draw(WEdln *wedln, bool complete)
462 wedln_draw_(wedln, complete, TRUE);
465 /*}}} */
468 /*{{{ Info area */
471 static void wedln_set_info(WEdln *wedln, const char *info)
473 WRectangle tageom;
474 char *p;
476 if(wedln->info!=NULL){
477 free(wedln->info);
478 wedln->info=NULL;
479 wedln->info_w=0;
480 wedln->info_len=0;
483 if(info!=NULL){
484 wedln->info=scat3(" [", info, "]");
485 if(wedln->info!=NULL){
486 wedln->info_len=strlen(wedln->info);
487 if(WEDLN_BRUSH(wedln)!=NULL){
488 wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
489 wedln->info,
490 wedln->info_len);
495 get_textarea_geom(wedln, G_CURRENT, &tageom);
496 wedln_update_cursor(wedln, tageom.w);
498 wedln_draw_(wedln, FALSE, FALSE);
502 /*}}}*/
505 /*{{{ Completions */
508 static void wedln_show_completions(WEdln *wedln, char **strs, int nstrs,
509 int selected)
511 int w=REGION_GEOM(wedln).w;
512 int h=REGION_GEOM(wedln).h;
514 if(WEDLN_BRUSH(wedln)==NULL)
515 return;
517 setup_listing(&(wedln->compl_list), strs, nstrs, FALSE);
518 wedln->compl_list.selected_str=selected;
520 input_refit((WInput*)wedln);
521 if(w==REGION_GEOM(wedln).w && h==REGION_GEOM(wedln).h)
522 wedln_draw_completions(wedln, LISTING_DRAW_COMPLETE);
526 void wedln_hide_completions(WEdln *wedln)
528 if(wedln->compl_list.strs!=NULL){
529 deinit_listing(&(wedln->compl_list));
530 input_refit((WInput*)wedln);
535 void wedln_scrollup_completions(WEdln *wedln)
537 if(wedln->compl_list.strs==NULL)
538 return;
539 if(scrollup_listing(&(wedln->compl_list)))
540 wedln_draw_completions(wedln, LISTING_DRAW_COMPLETE);
544 void wedln_scrolldown_completions(WEdln *wedln)
546 if(wedln->compl_list.strs==NULL)
547 return;
548 if(scrolldown_listing(&(wedln->compl_list)))
549 wedln_draw_completions(wedln, LISTING_DRAW_COMPLETE);
553 static int update_nocompl=0;
556 static void free_completions(char **ptr, int i)
558 while(i>0){
559 i--;
560 if(ptr[i]!=NULL)
561 free(ptr[i]);
563 free(ptr);
567 bool wedln_do_set_completions(WEdln *wedln, char **ptr, int n,
568 char *beg, char *end, int cycle,
569 bool nosort)
571 int sel=-1;
573 if(wedln->compl_beg!=NULL)
574 free(wedln->compl_beg);
576 if(wedln->compl_end!=NULL)
577 free(wedln->compl_end);
579 wedln->compl_beg=beg;
580 wedln->compl_end=end;
581 wedln->compl_current_id=-1;
583 n=edln_do_completions(&(wedln->edln), ptr, n, beg, end,
584 !mod_query_config.autoshowcompl, nosort);
586 if(mod_query_config.autoshowcompl && n>0 && cycle!=0){
587 update_nocompl++;
588 sel=(cycle>0 ? 0 : n-1);
589 edln_set_completion(&(wedln->edln), ptr[sel], beg, end);
590 update_nocompl--;
593 if(n>1 || (mod_query_config.autoshowcompl && n>0)){
594 wedln_show_completions(wedln, ptr, n, sel);
595 return TRUE;
598 free_completions(ptr, n);
600 return FALSE;
604 void wedln_set_completions(WEdln *wedln, ExtlTab completions, int cycle)
606 int n=0, i=0;
607 char **ptr=NULL, *beg=NULL, *end=NULL, *p=NULL;
609 n=extl_table_get_n(completions);
611 if(n==0){
612 wedln_hide_completions(wedln);
613 return;
616 ptr=ALLOC_N(char*, n);
617 if(ptr==NULL)
618 goto allocfail;
620 for(i=0; i<n; i++){
621 if(!extl_table_geti_s(completions, i+1, &p)){
622 goto allocfail;
624 ptr[i]=p;
627 extl_table_gets_s(completions, "common_beg", &beg);
628 extl_table_gets_s(completions, "common_end", &end);
630 if(!wedln_do_set_completions(wedln, ptr, n, beg, end, cycle, FALSE))
631 wedln_hide_completions(wedln);
633 return;
635 allocfail:
636 wedln_hide_completions(wedln);
637 free_completions(ptr, i);
641 static void wedln_do_select_completion(WEdln *wedln, int n)
643 bool redraw=listing_select(&(wedln->compl_list), n);
644 wedln_draw_completions(wedln, redraw);
646 update_nocompl++;
647 edln_set_completion(&(wedln->edln), wedln->compl_list.strs[n],
648 wedln->compl_beg, wedln->compl_end);
649 update_nocompl--;
654 static ExtlExportedFn *sc_safe_fns[]={
655 (ExtlExportedFn*)&complproxy_set_completions,
656 NULL
660 static ExtlSafelist sc_safelist=EXTL_SAFELIST_INIT(sc_safe_fns);
663 static int wedln_alloc_compl_id(WEdln *wedln)
665 int id=wedln->compl_waiting_id+1;
666 wedln->compl_waiting_id=maxof(0, wedln->compl_waiting_id+1);
667 return id;
670 static bool wedln_do_call_completor(WEdln *wedln, int id, int cycle)
672 if(wedln->compl_history_mode){
673 uint n;
674 char **h=NULL;
676 wedln->compl_waiting_id=id;
678 n=edln_history_matches(&wedln->edln, &h);
680 if(n==0){
681 wedln_hide_completions(wedln);
682 return FALSE;
685 if(wedln_do_set_completions(wedln, h, n, NULL, NULL, cycle, TRUE)){
686 wedln->compl_current_id=id;
687 return TRUE;
690 return FALSE;
691 }else{
692 const char *p=wedln->edln.p;
693 int point=wedln->edln.point;
694 WComplProxy *proxy=create_complproxy(wedln, id, cycle);
696 if(proxy==NULL)
697 return FALSE;
699 /* Set Lua-side to own the proxy: it gets freed by Lua's GC */
700 ((Obj*)proxy)->flags|=OBJ_EXTL_OWNED;
702 if(p==NULL){
703 p="";
704 point=0;
707 extl_protect(&sc_safelist);
708 extl_call(wedln->completor, "osi", NULL, proxy, p, point);
709 extl_unprotect(&sc_safelist);
711 return TRUE;
716 static void timed_complete(WTimer *tmr, Obj *obj)
718 WEdln *wedln=(WEdln*)obj;
720 if(wedln!=NULL){
721 int id=wedln->compl_timed_id;
722 wedln->compl_timed_id=-1;
723 if(id==wedln->compl_waiting_id && id>=0)
724 wedln_do_call_completor((WEdln*)obj, id, FALSE);
729 /*EXTL_DOC
730 * Select next completion.
732 EXTL_EXPORT_MEMBER
733 bool wedln_next_completion(WEdln *wedln)
735 int n=-1;
737 if(wedln->compl_current_id!=wedln->compl_waiting_id)
738 return FALSE;
740 if(wedln->compl_list.nstrs<=0)
741 return FALSE;
743 if(wedln->compl_list.selected_str<0 ||
744 wedln->compl_list.selected_str+1>=wedln->compl_list.nstrs){
745 n=0;
746 }else{
747 n=wedln->compl_list.selected_str+1;
750 if(n!=wedln->compl_list.selected_str)
751 wedln_do_select_completion(wedln, n);
753 return TRUE;
757 /*EXTL_DOC
758 * Select previous completion.
760 EXTL_EXPORT_MEMBER
761 bool wedln_prev_completion(WEdln *wedln)
763 int n=-1;
765 if(wedln->compl_current_id!=wedln->compl_waiting_id)
766 return FALSE;
768 if(wedln->compl_list.nstrs<=0)
769 return FALSE;
771 if(wedln->compl_list.selected_str<=0){
772 n=wedln->compl_list.nstrs-1;
773 }else{
774 n=wedln->compl_list.selected_str-1;
777 if(n!=wedln->compl_list.selected_str)
778 wedln_do_select_completion(wedln, n);
780 return TRUE;
784 /*EXTL_DOC
785 * Call completion handler with the text between the beginning of line and
786 * current cursor position, or select next/previous completion from list if in
787 * auto-show-completions mode and \var{cycle} is set to \codestr{next} or
788 * \codestr{prev}, respectively.
789 * The \var{mode} may be \codestr{history} or \codestr{normal}. If it is
790 * not set, the previous mode is used. Normally next entry is not cycled to
791 * despite the setting of \var{cycle} if mode switch occurs. To override
792 * this, use \codestr{next-always} and \codestr{prev-always} for \var{cycle}.
794 EXTL_EXPORT_MEMBER
795 void wedln_complete(WEdln *wedln, const char *cycle, const char *mode)
797 bool valid=TRUE;
798 int cyclei=0;
800 if(mode!=NULL){
801 if(strcmp(mode, "history")==0){
802 valid=wedln->compl_history_mode;
803 wedln->compl_history_mode=TRUE;
804 }else if(strcmp(mode, "normal")==0){
805 valid=!wedln->compl_history_mode;
806 wedln->compl_history_mode=FALSE;
808 if(!valid){
809 wedln_set_info(wedln,
810 (wedln->compl_history_mode
811 ? TR("history")
812 : NULL));
816 if(cycle!=NULL){
817 if((valid && strcmp(cycle, "next")==0) ||
818 strcmp(cycle, "next-always")==0){
819 cyclei=1;
820 }else if((valid && strcmp(cycle, "prev")==0) ||
821 strcmp(cycle, "prev-always")==0){
822 cyclei=-1;
826 if(valid && cyclei!=0 && mod_query_config.autoshowcompl &&
827 wedln->compl_list.nstrs>0){
828 if(cyclei>0)
829 wedln_next_completion(wedln);
830 else
831 wedln_prev_completion(wedln);
832 }else{
833 int oldid=wedln->compl_waiting_id;
835 if(!wedln_do_call_completor(wedln, wedln_alloc_compl_id(wedln),
836 cyclei)){
837 wedln->compl_waiting_id=oldid;
843 /*EXTL_DOC
844 * Get history completion mode.
846 EXTL_EXPORT_MEMBER
847 bool wedln_is_histcompl(WEdln *wedln)
849 return wedln->compl_history_mode;
853 /*}}}*/
856 /*{{{ Update handler */
859 static void wedln_update_handler(WEdln *wedln, int from, int flags)
861 WRectangle geom;
863 if(WEDLN_BRUSH(wedln)==NULL)
864 return;
866 get_textarea_geom(wedln, G_CURRENT, &geom);
868 if(flags&EDLN_UPDATE_NEW)
869 wedln->vstart=0;
871 if(flags&EDLN_UPDATE_MOVED){
872 if(wedln_update_cursor(wedln, geom.w))
873 from=wedln->vstart;
876 from=maxof(0, from-wedln->vstart);
878 wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, from,
879 wedln->edln.point, wedln->edln.mark);
881 if(update_nocompl==0 &&
882 mod_query_config.autoshowcompl &&
883 flags&EDLN_UPDATE_CHANGED){
884 wedln->compl_current_id=-1; /* invalidate */
885 if(wedln->autoshowcompl_timer==NULL)
886 wedln->autoshowcompl_timer=create_timer();
887 if(wedln->autoshowcompl_timer!=NULL){
888 wedln->compl_timed_id=wedln_alloc_compl_id(wedln);
889 timer_set(wedln->autoshowcompl_timer,
890 mod_query_config.autoshowcompl_delay,
891 timed_complete, (Obj*)wedln);
897 /*}}}*/
900 /*{{{ Init, deinit and config update */
903 static bool wedln_init_prompt(WEdln *wedln, const char *prompt)
905 char *p;
907 if(prompt!=NULL){
908 p=scat(prompt, " ");
910 if(p==NULL)
911 return FALSE;
913 wedln->prompt=p;
914 wedln->prompt_len=strlen(p);
915 }else{
916 wedln->prompt=NULL;
917 wedln->prompt_len=0;
919 wedln->prompt_w=0;
921 return TRUE;
925 static bool wedln_init(WEdln *wedln, WWindow *par, const WFitParams *fp,
926 WEdlnCreateParams *params)
928 wedln->vstart=0;
930 init_attr();
932 if(!wedln_init_prompt(wedln, params->prompt))
933 return FALSE;
935 if(!edln_init(&(wedln->edln), params->dflt)){
936 free(wedln->prompt);
937 return FALSE;
940 wedln->handler=extl_fn_none();
941 wedln->completor=extl_fn_none();
943 wedln->edln.uiptr=wedln;
944 wedln->edln.ui_update=(EdlnUpdateHandler*)wedln_update_handler;
946 wedln->autoshowcompl_timer=NULL;
948 init_listing(&(wedln->compl_list));
950 wedln->compl_waiting_id=-1;
951 wedln->compl_current_id=-1;
952 wedln->compl_timed_id=-1;
953 wedln->compl_beg=NULL;
954 wedln->compl_end=NULL;
955 wedln->compl_tab=FALSE;
956 wedln->compl_history_mode=FALSE;
958 wedln->info=NULL;
959 wedln->info_len=0;
960 wedln->info_w=0;
962 wedln->cycle_bindmap=NULL;
964 if(!input_init((WInput*)wedln, par, fp)){
965 edln_deinit(&(wedln->edln));
966 free(wedln->prompt);
967 return FALSE;
970 window_create_xic(&wedln->input.win);
972 wedln->handler=extl_ref_fn(params->handler);
973 wedln->completor=extl_ref_fn(params->completor);
975 region_add_bindmap((WRegion*)wedln, mod_query_wedln_bindmap);
977 return TRUE;
981 WEdln *create_wedln(WWindow *par, const WFitParams *fp,
982 WEdlnCreateParams *params)
984 CREATEOBJ_IMPL(WEdln, wedln, (p, par, fp, params));
988 static void wedln_deinit(WEdln *wedln)
990 if(wedln->prompt!=NULL)
991 free(wedln->prompt);
993 if(wedln->info!=NULL)
994 free(wedln->info);
996 if(wedln->compl_beg!=NULL)
997 free(wedln->compl_beg);
999 if(wedln->compl_end!=NULL)
1000 free(wedln->compl_end);
1002 if(wedln->compl_list.strs!=NULL)
1003 deinit_listing(&(wedln->compl_list));
1005 if(wedln->autoshowcompl_timer!=NULL)
1006 destroy_obj((Obj*)wedln->autoshowcompl_timer);
1008 if(wedln->cycle_bindmap!=NULL)
1009 bindmap_destroy(wedln->cycle_bindmap);
1011 extl_unref_fn(wedln->completor);
1012 extl_unref_fn(wedln->handler);
1014 edln_deinit(&(wedln->edln));
1015 input_deinit((WInput*)wedln);
1019 static void wedln_do_finish(WEdln *wedln)
1021 ExtlFn handler;
1022 char *p;
1024 handler=wedln->handler;
1025 wedln->handler=extl_fn_none();
1026 p=edln_finish(&(wedln->edln));
1028 region_rqdispose((WRegion*)wedln);
1030 if(p!=NULL)
1031 extl_call(handler, "s", NULL, p);
1033 free(p);
1034 extl_unref_fn(handler);
1038 /*EXTL_DOC
1039 * Close \var{wedln} and call any handlers.
1041 EXTL_EXPORT_MEMBER
1042 void wedln_finish(WEdln *wedln)
1044 mainloop_defer_action((Obj*)wedln, (WDeferredAction*)wedln_do_finish);
1048 /*}}}*/
1051 /*{{{ The rest */
1054 /*EXTL_DOC
1055 * Request selection from application holding such.
1057 * Note that this function is asynchronous; the selection will not
1058 * actually be inserted before Ion receives it. This will be no
1059 * earlier than Ion return to its main loop.
1061 EXTL_EXPORT_MEMBER
1062 void wedln_paste(WEdln *wedln)
1064 ioncore_request_selection_for(wedln->input.win.win);
1068 void wedln_insstr(WEdln *wedln, const char *buf, size_t n)
1070 edln_insstr_n(&(wedln->edln), buf, n, TRUE, TRUE);
1074 static const char *wedln_style(WEdln *wedln)
1076 return "input-edln";
1080 /*}}}*/
1083 /*{{{ Dynamic function table and class implementation */
1086 static DynFunTab wedln_dynfuntab[]={
1087 {window_draw, wedln_draw},
1088 {input_calc_size, wedln_calc_size},
1089 {input_scrollup, wedln_scrollup_completions},
1090 {input_scrolldown, wedln_scrolldown_completions},
1091 {window_insstr, wedln_insstr},
1092 {(DynFun*)input_style, (DynFun*)wedln_style},
1093 {region_size_hints, wedln_size_hints},
1094 END_DYNFUNTAB
1098 EXTL_EXPORT
1099 IMPLCLASS(WEdln, WInput, wedln_deinit, wedln_dynfuntab);
1102 /*}}}*/