]> git.decadent.org.uk Git - ion3.git/blob - mod_query/wedln.c
[svn-inject] Installing original source of ion3
[ion3.git] / mod_query / wedln.c
1 /*
2  * ion/mod_query/wedln.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <string.h>
13
14 #include <libtu/objp.h>
15 #include <libtu/minmax.h>
16 #include <libtu/setparam.h>
17 #include <libextl/extl.h>
18 #include <libmainloop/defer.h>
19 #include <libmainloop/signal.h>
20
21 #include <ioncore/common.h>
22 #include <ioncore/global.h>
23 #include <ioncore/strings.h>
24 #include <ioncore/xic.h>
25 #include <ioncore/selection.h>
26 #include <ioncore/event.h>
27 #include <ioncore/regbind.h>
28 #include "edln.h"
29 #include "wedln.h"
30 #include "inputp.h"
31 #include "complete.h"
32 #include "main.h"
33
34
35 #define WEDLN_BRUSH(X) ((X)->input.brush)
36
37
38 /*{{{ Drawing primitives */
39
40
41 static int calc_text_y(WEdln *wedln, const WRectangle *geom)
42 {
43     GrFontExtents fnte;
44         
45     grbrush_get_font_extents(WEDLN_BRUSH(wedln), &fnte);
46         
47     return geom->y+geom->h/2-fnte.max_height/2+fnte.baseline;
48 }
49
50
51 static int wedln_draw_strsect(WEdln *wedln, const WRectangle *geom, 
52                               int x, int y, const char *str, int len,
53                               const char *attr)
54 {
55     if(len==0)
56         return 0;
57     
58     grbrush_draw_string(WEDLN_BRUSH(wedln), x, y, str, len, TRUE, attr);
59     
60     return grbrush_get_text_width(WEDLN_BRUSH(wedln), str, len);
61 }
62
63 #if 0
64 static void dispu(const char* s, int l)
65 {
66     while(l>0){
67         int c=(unsigned char)*s;
68         fprintf(stderr, "%d[%c]", c, *s);
69         s++;
70         l--;
71     }
72     fprintf(stderr, "\n");
73 }
74 #endif
75
76 #define DSTRSECT(LEN, INV) \
77     if(LEN>0){tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty, str, LEN, INV); \
78      str+=LEN; len-=LEN;}
79
80
81 static void wedln_do_draw_str_box(WEdln *wedln, const WRectangle *geom,
82                                   const char *str, int cursor, 
83                                   int mark, int tx)
84 {
85     int len=strlen(str), ll=0, ty=0;
86     const char *normalstyle=(REGION_IS_ACTIVE(wedln)
87                              ? "active-normal" : "inactive-normal");
88     const char *selectionstyle=(REGION_IS_ACTIVE(wedln)
89                                 ? "active-selection" : "inactive-selection");
90     const char *cursorstyle=(REGION_IS_ACTIVE(wedln)
91                              ? "active-cursor" : "inactive-cursor");
92     
93     /*if(tx<geom->w){
94         WRectangle g=*geom;
95         g.x+=tx;
96         g.w-=tx;
97         grbrush_clear_area(WEDLN_BRUSH(wedln), &g);
98     }*/
99     
100     ty=calc_text_y(wedln, geom);
101
102     if(mark<=cursor){
103         if(mark>=0){
104             DSTRSECT(mark, normalstyle);
105             DSTRSECT(cursor-mark, selectionstyle);
106         }else{
107             DSTRSECT(cursor, normalstyle);
108         }
109         if(len==0){
110             tx+=wedln_draw_strsect(wedln, geom, geom->x+tx, ty,
111                                    " ", 1, cursorstyle);
112         }else{
113             ll=str_nextoff(str, 0);
114             DSTRSECT(ll, cursorstyle);
115         }
116     }else{
117         DSTRSECT(cursor, normalstyle);
118         ll=str_nextoff(str, 0);
119         DSTRSECT(ll, cursorstyle);
120         DSTRSECT(mark-cursor-ll, selectionstyle);
121     }
122     DSTRSECT(len, normalstyle);
123
124     if(tx<geom->w){
125         WRectangle g=*geom;
126         g.x+=tx;
127         g.w-=tx;
128         grbrush_clear_area(WEDLN_BRUSH(wedln), &g);
129     }
130 }
131
132
133 static void wedln_draw_str_box(WEdln *wedln, const WRectangle *geom,
134                                int vstart, const char *str,
135                                int dstart, int point, int mark)
136 {
137     int tx=0;
138
139     /* Some fonts and Xmb/utf8 routines don't work well with dstart!=0. */
140     dstart=0;
141     
142     if(mark>=0){
143         mark-=vstart+dstart;
144         if(mark<0)
145             mark=0;
146     }
147     
148     point-=vstart+dstart;
149     
150     if(dstart!=0)
151         tx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, dstart);
152
153     grbrush_begin(WEDLN_BRUSH(wedln), geom, GRBRUSH_AMEND|GRBRUSH_NEED_CLIP);
154     
155     wedln_do_draw_str_box(wedln, geom, str+vstart+dstart, point, mark, tx);
156
157     grbrush_end(WEDLN_BRUSH(wedln));
158 }
159
160
161 static bool wedln_update_cursor(WEdln *wedln, int iw)
162 {
163     int cx, l;
164     int vstart=wedln->vstart;
165     int point=wedln->edln.point;
166     int len=wedln->edln.psize;
167     int mark=wedln->edln.mark;
168     const char *str=wedln->edln.p;
169     bool ret;
170     
171     if(point<wedln->vstart)
172         wedln->vstart=point;
173     
174     if(wedln->vstart==point)
175         return FALSE;
176     
177     while(vstart<point){
178         if(point==len){
179             cx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, 
180                                       point-vstart);
181             cx+=grbrush_get_text_width(WEDLN_BRUSH(wedln), " ", 1);
182         }else{
183             int nxt=str_nextoff(str, point);
184             cx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, 
185                                       point-vstart+nxt);
186         }
187         
188         if(cx<iw)
189             break;
190         
191         l=str_nextoff(str, vstart);
192         if(l==0)
193             break;
194         vstart+=l;
195     }
196     
197     ret=(wedln->vstart!=vstart);
198     wedln->vstart=vstart;
199     
200     return ret;
201 }
202
203
204 /*}}}*/
205
206
207 /*{{{ Size/location calc */
208
209
210 static int get_textarea_height(WEdln *wedln, bool with_spacing)
211 {
212     GrBorderWidths bdw;
213     GrFontExtents fnte;
214     
215     grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
216     grbrush_get_font_extents(WEDLN_BRUSH(wedln), &fnte);
217     
218     return (fnte.max_height+bdw.top+bdw.bottom+
219             (with_spacing ? bdw.spacing : 0));
220 }
221
222
223 enum{G_NORESET, G_MAX, G_CURRENT};
224
225
226 static void get_geom(WEdln *wedln, int mode, WRectangle *geom)
227 {
228     if(mode==G_MAX)
229         *geom=wedln->input.last_fp.g;
230     else if(mode==G_CURRENT)
231         *geom=REGION_GEOM(wedln);
232 }
233
234
235 static void get_completions_geom(WEdln *wedln, int mode, WRectangle *geom)
236 {
237     get_geom(wedln, mode, geom);
238     geom->x=0;
239     geom->y=0;
240     
241     geom->h-=get_textarea_height(wedln, TRUE);
242     if(geom->h<0)
243         geom->h=0;
244 }
245
246
247 static void get_outer_geom(WEdln *wedln, int mode, WRectangle *geom)
248 {
249     int th;
250     
251     get_geom(wedln, mode, geom);
252     geom->x=0;
253     geom->y=0;
254     
255     th=get_textarea_height(wedln, FALSE);
256     
257     geom->y+=geom->h-th;
258     geom->h=th;
259 }
260
261
262 static void get_inner_geom(WEdln *wedln, int mode, WRectangle *geom)
263 {
264     GrBorderWidths bdw;
265     
266     grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
267     
268     get_outer_geom(wedln, mode, geom);
269     
270     geom->x+=bdw.left;
271     geom->w-=bdw.left+bdw.right;
272     geom->y+=bdw.top;
273     geom->h-=bdw.top+bdw.bottom;
274     geom->w=maxof(0, geom->w);
275     geom->h=maxof(0, geom->h);
276 }
277
278
279 static void get_textarea_geom(WEdln *wedln, int mode, WRectangle *geom)
280 {
281     get_inner_geom(wedln, mode, geom);
282     geom->x+=wedln->prompt_w;
283     geom->w=maxof(0, geom->w - wedln->prompt_w - wedln->info_w);
284 }
285
286
287 static void wedln_calc_size(WEdln *wedln, WRectangle *geom)
288 {
289     int h, th;
290     GrBorderWidths bdw;
291     WRectangle max_geom=*geom, tageom;
292     
293     if(WEDLN_BRUSH(wedln)==NULL)
294         return;
295     
296     if(wedln->prompt!=NULL){
297         wedln->prompt_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
298                                                wedln->prompt, 
299                                                wedln->prompt_len);
300     }
301     
302     if(wedln->info!=NULL){
303         wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
304                                              wedln->info, 
305                                              wedln->info_len);
306     }
307
308     th=get_textarea_height(wedln, wedln->compl_list.strs!=NULL);
309     
310     if(wedln->compl_list.strs==NULL){
311         if(max_geom.h<th || !(wedln->input.last_fp.mode&REGION_FIT_BOUNDS))
312             geom->h=max_geom.h;
313         else
314             geom->h=th;
315     }else{
316         WRectangle g;
317         
318         get_completions_geom(wedln, G_MAX, &g);
319         
320         fit_listing(WEDLN_BRUSH(wedln), &g, &(wedln->compl_list));
321         
322         grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
323         
324         h=wedln->compl_list.toth;
325         th+=bdw.top+bdw.bottom;
326         
327         if(h+th>max_geom.h || !(wedln->input.last_fp.mode&REGION_FIT_BOUNDS))
328             h=max_geom.h-th;
329         geom->h=h+th;
330     }
331     
332     geom->w=max_geom.w;
333     geom->y=max_geom.y+max_geom.h-geom->h;
334     geom->x=max_geom.x;
335
336     tageom=*geom;
337     get_textarea_geom(wedln, G_NORESET, &tageom);
338     wedln_update_cursor(wedln, tageom.w);
339 }
340
341
342 /*}}}*/
343
344
345 /*{{{ Draw */
346
347
348 void wedln_draw_completions(WEdln *wedln, bool complete)
349 {
350     WRectangle geom;
351     
352     if(wedln->compl_list.strs!=NULL && WEDLN_BRUSH(wedln)!=NULL){
353         const char *style=(REGION_IS_ACTIVE(wedln) 
354                            ? "active" 
355                            : "inactive");
356         const char *selstyle=(REGION_IS_ACTIVE(wedln) 
357                               ? "active-selection" 
358                               : "inactive-selection");
359         
360         get_completions_geom(wedln, G_CURRENT, &geom);
361         
362         draw_listing(WEDLN_BRUSH(wedln), &geom, &(wedln->compl_list), 
363                      complete, style, selstyle);
364     }
365 }
366
367
368 void wedln_draw_textarea(WEdln *wedln)    
369 {
370     WRectangle geom;
371     int ty;
372     const char *style=(REGION_IS_ACTIVE(wedln) ? "active" : "inactive");
373
374     if(WEDLN_BRUSH(wedln)==NULL)
375         return;
376     
377     get_outer_geom(wedln, G_CURRENT, &geom);
378     
379     /*grbrush_begin(WEDLN_BRUSH(wedln), &geom, GRBRUSH_AMEND);*/
380     
381     grbrush_draw_border(WEDLN_BRUSH(wedln), &geom, style);
382
383     get_inner_geom(wedln, G_CURRENT, &geom);
384
385     ty=calc_text_y(wedln, &geom);
386
387     if(wedln->prompt!=NULL){
388         const char *promptstyle=(REGION_IS_ACTIVE(wedln) 
389                                  ? "active-prompt" 
390                                  : "inactive-prompt");
391         grbrush_draw_string(WEDLN_BRUSH(wedln), geom.x, ty,
392                             wedln->prompt, wedln->prompt_len, TRUE, 
393                             promptstyle);
394     }
395     
396     if(wedln->info!=NULL){
397         int x=geom.x+geom.w-wedln->info_w;
398         const char *promptstyle=(REGION_IS_ACTIVE(wedln) 
399                                  ? "active-prompt-info" 
400                                  : "inactive-prompt-info");
401         grbrush_draw_string(WEDLN_BRUSH(wedln), x, ty,
402                             wedln->info, wedln->info_len, TRUE, 
403                             promptstyle);
404     }
405         
406     get_textarea_geom(wedln, G_CURRENT, &geom);
407     
408     wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, 0,
409                        wedln->edln.point, wedln->edln.mark);
410
411     /*grbrush_end(WEDLN_BRUSH(wedln));*/
412 }
413
414
415 void wedln_draw(WEdln *wedln, bool complete)
416 {
417     WRectangle g;
418     int f=(complete ? 0 : GRBRUSH_NO_CLEAR_OK);
419     
420     if(WEDLN_BRUSH(wedln)==NULL)
421         return;
422     
423     get_geom(wedln, G_CURRENT, &g);
424     
425     grbrush_begin(WEDLN_BRUSH(wedln), &g, f);
426     
427     wedln_draw_completions(wedln, FALSE);
428     wedln_draw_textarea(wedln);
429     
430     grbrush_end(WEDLN_BRUSH(wedln));
431 }
432
433
434 /*}}} */
435
436
437 /*{{{ Info area */
438
439
440 static void wedln_set_info(WEdln *wedln, const char *info)
441 {
442     WRectangle tageom;
443     char *p;
444     
445     if(wedln->info!=NULL){
446         free(wedln->info);
447         wedln->info=NULL;
448         wedln->info_w=0;
449         wedln->info_len=0;
450     }
451     
452     if(info!=NULL){
453         wedln->info=scat3("  [", info, "]");
454         if(wedln->info!=NULL){
455             wedln->info_len=strlen(wedln->info);
456             if(WEDLN_BRUSH(wedln)!=NULL){
457                 wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
458                                                      wedln->info, 
459                                                      wedln->info_len);
460             }
461         }
462     }
463     
464     get_textarea_geom(wedln, G_CURRENT, &tageom);
465     wedln_update_cursor(wedln, tageom.w);
466     
467     wedln_draw_textarea(wedln);
468 }
469
470
471 /*}}}*/
472
473
474 /*{{{ Completions */
475
476
477 static void wedln_show_completions(WEdln *wedln, char **strs, int nstrs,
478                                    int selected)
479 {
480     int w=REGION_GEOM(wedln).w;
481     int h=REGION_GEOM(wedln).h;
482     
483     if(WEDLN_BRUSH(wedln)==NULL)
484         return;
485     
486     setup_listing(&(wedln->compl_list), strs, nstrs, FALSE);
487     wedln->compl_list.selected_str=selected;
488
489     input_refit((WInput*)wedln);
490     if(w==REGION_GEOM(wedln).w && h==REGION_GEOM(wedln).h)
491         wedln_draw_completions(wedln, TRUE);
492 }
493
494
495 void wedln_hide_completions(WEdln *wedln)
496 {
497     if(wedln->compl_list.strs!=NULL){
498         deinit_listing(&(wedln->compl_list));
499         input_refit((WInput*)wedln);
500     }
501 }
502     
503
504 void wedln_scrollup_completions(WEdln *wedln)
505 {
506     if(wedln->compl_list.strs==NULL)
507         return;
508     if(scrollup_listing(&(wedln->compl_list)))
509         wedln_draw_completions(wedln, TRUE);
510 }
511
512
513 void wedln_scrolldown_completions(WEdln *wedln)
514 {
515     if(wedln->compl_list.strs==NULL)
516         return;
517     if(scrolldown_listing(&(wedln->compl_list)))
518         wedln_draw_completions(wedln, TRUE);
519 }
520
521
522 static int update_nocompl=0;
523
524
525 static void free_completions(char **ptr, int i)
526 {
527     while(i>0){
528         i--;
529         free(ptr[i]);
530     }
531     free(ptr);
532 }
533
534
535 bool wedln_do_set_completions(WEdln *wedln, char **ptr, int n, 
536                               char *beg, char *end, int cycle,
537                               bool nosort)
538 {    
539     int sel=-1;
540     
541     if(wedln->compl_beg!=NULL)
542         free(wedln->compl_beg);
543     
544     if(wedln->compl_end!=NULL)
545         free(wedln->compl_end);
546     
547     wedln->compl_beg=beg;
548     wedln->compl_end=end;
549     wedln->compl_current_id=-1;
550     
551     n=edln_do_completions(&(wedln->edln), ptr, n, beg, end, 
552                           !mod_query_config.autoshowcompl, nosort);
553
554     if(mod_query_config.autoshowcompl && n>0 && cycle!=0){
555         update_nocompl++;
556         sel=(cycle>0 ? 0 : n-1);
557         edln_set_completion(&(wedln->edln), ptr[sel], beg, end);
558         update_nocompl--;
559     }
560
561     if(n>1 || (mod_query_config.autoshowcompl && n>0)){
562         wedln_show_completions(wedln, ptr, n, sel);
563         return TRUE;
564     }
565     
566     free_completions(ptr, n);
567     
568     return FALSE;
569 }
570
571
572 void wedln_set_completions(WEdln *wedln, ExtlTab completions, int cycle)
573 {
574     int n=0, i=0;
575     char **ptr=NULL, *beg=NULL, *end=NULL, *p=NULL;
576     
577     n=extl_table_get_n(completions);
578     
579     if(n==0){
580         wedln_hide_completions(wedln);
581         return;
582     }
583     
584     ptr=ALLOC_N(char*, n);
585     if(ptr==NULL)
586         goto allocfail;
587
588     for(i=0; i<n; i++){
589         if(!extl_table_geti_s(completions, i+1, &p)){
590             goto allocfail;
591         }
592         ptr[i]=p;
593     }
594
595     extl_table_gets_s(completions, "common_beg", &beg);
596     extl_table_gets_s(completions, "common_end", &end);
597     
598     if(wedln_do_set_completions(wedln, ptr, n, beg, end, cycle, FALSE))
599         return;
600     
601 allocfail:
602     wedln_hide_completions(wedln);
603     free_completions(ptr, i);
604     while(i>0){
605         i--;
606         /* edln_do_completions may NULL things */
607         if(ptr[i]!=NULL)
608             free(ptr[i]);
609     }
610     free(ptr);
611 }
612     
613
614 static void wedln_do_select_completion(WEdln *wedln, int n)
615 {
616     bool complredraw=listing_select(&(wedln->compl_list), n);
617     wedln_draw_completions(wedln, complredraw);
618
619     update_nocompl++;
620     edln_set_completion(&(wedln->edln), wedln->compl_list.strs[n],
621                         wedln->compl_beg, wedln->compl_end);
622     update_nocompl--;
623 }
624
625
626
627 static ExtlExportedFn *sc_safe_fns[]={
628     (ExtlExportedFn*)&complproxy_set_completions,
629     NULL
630 };
631
632
633 static ExtlSafelist sc_safelist=EXTL_SAFELIST_INIT(sc_safe_fns);
634
635
636 static int wedln_alloc_compl_id(WEdln *wedln)
637 {
638     int id=wedln->compl_waiting_id+1;
639     wedln->compl_waiting_id=maxof(0, wedln->compl_waiting_id+1);
640     return id;
641 }
642
643 static bool wedln_do_call_completor(WEdln *wedln, int id, int cycle)
644 {
645     if(wedln->compl_history_mode){
646         uint n;
647         char **h=NULL;
648         
649         wedln->compl_waiting_id=id;
650
651         n=edln_history_matches(&wedln->edln, &h);
652         
653         if(n==0){
654             wedln_hide_completions(wedln);
655             return FALSE;
656         }
657         
658         if(wedln_do_set_completions(wedln, h, n, NULL, NULL, cycle, TRUE)){
659             wedln->compl_current_id=id;
660             return TRUE;
661         }
662         
663         return FALSE;
664     }else{
665         const char *p=wedln->edln.p;
666         int point=wedln->edln.point;
667         WComplProxy *proxy=create_complproxy(wedln, id, cycle);
668         
669         if(proxy==NULL)
670             return FALSE;
671         
672         /* Set Lua-side to own the proxy: it gets freed by Lua's GC */
673         ((Obj*)proxy)->flags|=OBJ_EXTL_OWNED;
674         
675         if(p==NULL){
676             p="";
677             point=0;
678         }
679         
680         extl_protect(&sc_safelist);
681         extl_call(wedln->completor, "osi", NULL, proxy, p, point);
682         extl_unprotect(&sc_safelist);
683         
684         return TRUE;
685     }
686 }
687
688
689 static void timed_complete(WTimer *tmr, Obj *obj)
690 {
691     WEdln *wedln=(WEdln*)obj;
692     
693     if(wedln!=NULL){
694         int id=wedln->compl_timed_id;
695         wedln->compl_timed_id=-1;
696         if(id==wedln->compl_waiting_id && id>=0)
697             wedln_do_call_completor((WEdln*)obj, id, FALSE);
698     }
699 }
700
701
702 /*EXTL_DOC
703  * Select next completion.
704  */
705 EXTL_EXPORT_MEMBER
706 bool wedln_next_completion(WEdln *wedln)
707 {
708     int n=-1;
709
710     if(wedln->compl_current_id!=wedln->compl_waiting_id)
711         return FALSE;
712     
713     if(wedln->compl_list.nstrs<=0)
714         return FALSE;
715     
716     if(wedln->compl_list.selected_str<0 ||
717        wedln->compl_list.selected_str+1>=wedln->compl_list.nstrs){
718         n=0;
719     }else{
720         n=wedln->compl_list.selected_str+1;
721     }
722     
723     if(n!=wedln->compl_list.selected_str)
724         wedln_do_select_completion(wedln, n);
725     
726     return TRUE;
727 }
728
729
730 /*EXTL_DOC
731  * Select previous completion.
732  */
733 EXTL_EXPORT_MEMBER
734 bool wedln_prev_completion(WEdln *wedln)
735 {
736     int n=-1;
737
738     if(wedln->compl_current_id!=wedln->compl_waiting_id)
739         return FALSE;
740     
741     if(wedln->compl_list.nstrs<=0)
742         return FALSE;
743     
744     if(wedln->compl_list.selected_str<=0){
745         n=wedln->compl_list.nstrs-1;
746     }else{
747         n=wedln->compl_list.selected_str-1;
748     }
749
750     if(n!=wedln->compl_list.selected_str)
751         wedln_do_select_completion(wedln, n);
752     
753     return TRUE;
754 }
755
756
757 /*EXTL_DOC
758  * Call completion handler with the text between the beginning of line and
759  * current cursor position, or select next/previous completion from list if in
760  * auto-show-completions mode and \var{cycle} is set to ``next'' or ``prev'',
761  * respectively. The \var{mode} may be ``history'' or ``normal''. If it is 
762  * not set, the previous mode is used. Normally next entry is not cycled to
763  * despite the setting of \var{cycle} if mode switch occurs. To override
764  * this, use ``next-always'' and ``prev-always'' for \var{cycle}.
765  */
766 EXTL_EXPORT_MEMBER
767 void wedln_complete(WEdln *wedln, const char *cycle, const char *mode)
768 {
769     bool valid=TRUE;
770     int cyclei=0;
771     
772     if(mode!=NULL){
773         if(strcmp(mode, "history")==0){
774             valid=wedln->compl_history_mode;
775             wedln->compl_history_mode=TRUE;
776         }else if(strcmp(mode, "normal")==0){
777             valid=!wedln->compl_history_mode;
778             wedln->compl_history_mode=FALSE;
779         }
780         if(!valid){
781             wedln_set_info(wedln,
782                            (wedln->compl_history_mode
783                             ? TR("history")
784                             : NULL));
785         }
786     }
787     
788     if(cycle!=NULL){
789         if((valid && strcmp(cycle, "next")==0) || 
790            strcmp(cycle, "next-always")==0){
791             cyclei=1;
792         }else if((valid && strcmp(cycle, "prev")==0) || 
793                  strcmp(cycle, "prev-always")==0){
794             cyclei=-1;
795         }
796     }
797     
798     if(valid && cyclei!=0 && mod_query_config.autoshowcompl && 
799        wedln->compl_list.nstrs>0){
800         if(cyclei>0)
801             wedln_next_completion(wedln);
802         else
803             wedln_prev_completion(wedln);
804     }else{
805         int oldid=wedln->compl_waiting_id;
806     
807         if(!wedln_do_call_completor(wedln, wedln_alloc_compl_id(wedln), 
808                                     cyclei)){
809             wedln->compl_waiting_id=oldid;
810         }
811     }
812 }
813
814
815 /*EXTL_DOC
816  * Get history completion mode.
817  */
818 EXTL_EXPORT_MEMBER
819 bool wedln_is_histcompl(WEdln *wedln)
820 {
821     return wedln->compl_history_mode;
822 }
823
824
825 /*}}}*/
826
827
828 /*{{{ Update handler */
829
830
831 static void wedln_update_handler(WEdln *wedln, int from, int flags)
832 {
833     WRectangle geom;
834     
835     if(WEDLN_BRUSH(wedln)==NULL)
836         return;
837     
838     get_textarea_geom(wedln, G_CURRENT, &geom);
839     
840     if(flags&EDLN_UPDATE_NEW)
841         wedln->vstart=0;
842     
843     if(flags&EDLN_UPDATE_MOVED){
844         if(wedln_update_cursor(wedln, geom.w))
845             from=wedln->vstart;
846     }
847     
848     from=maxof(0, from-wedln->vstart);
849
850     wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, from,
851                        wedln->edln.point, wedln->edln.mark);
852     
853     if(update_nocompl==0 &&
854        mod_query_config.autoshowcompl && 
855        flags&EDLN_UPDATE_CHANGED){
856         wedln->compl_current_id=-1; /* invalidate */
857         if(wedln->autoshowcompl_timer==NULL)
858             wedln->autoshowcompl_timer=create_timer();
859         if(wedln->autoshowcompl_timer!=NULL){
860             wedln->compl_timed_id=wedln_alloc_compl_id(wedln);
861             timer_set(wedln->autoshowcompl_timer, 
862                       mod_query_config.autoshowcompl_delay,
863                       timed_complete, (Obj*)wedln);
864         }
865     }
866 }
867
868
869 /*}}}*/
870
871
872 /*{{{ Init, deinit and config update */
873
874
875 static bool wedln_init_prompt(WEdln *wedln, const char *prompt)
876 {
877     char *p;
878     
879     if(prompt!=NULL){
880         p=scat(prompt, "  ");
881     
882         if(p==NULL)
883             return FALSE;
884
885         wedln->prompt=p;
886         wedln->prompt_len=strlen(p);
887     }else{
888         wedln->prompt=NULL;
889         wedln->prompt_len=0;
890     }
891     wedln->prompt_w=0;
892     
893     return TRUE;
894 }
895
896
897 static bool wedln_init(WEdln *wedln, WWindow *par, const WFitParams *fp, 
898                        WEdlnCreateParams *params)
899 {
900     wedln->vstart=0;
901
902     if(!wedln_init_prompt(wedln, params->prompt))
903         return FALSE;
904     
905     if(!edln_init(&(wedln->edln), params->dflt)){
906         free(wedln->prompt);
907         return FALSE;
908     }
909     
910     wedln->handler=extl_fn_none();
911     wedln->completor=extl_fn_none();
912     
913     wedln->edln.uiptr=wedln;
914     wedln->edln.ui_update=(EdlnUpdateHandler*)wedln_update_handler;
915
916     wedln->autoshowcompl_timer=NULL;
917     
918     init_listing(&(wedln->compl_list));
919
920     wedln->compl_waiting_id=-1;
921     wedln->compl_current_id=-1;
922     wedln->compl_timed_id=-1;
923     wedln->compl_beg=NULL;
924     wedln->compl_end=NULL;
925     wedln->compl_tab=FALSE;
926     wedln->compl_history_mode=FALSE;
927     
928     wedln->info=NULL;
929     wedln->info_len=0;
930     wedln->info_w=0;
931     
932     wedln->cycle_bindmap=NULL;
933     
934     if(!input_init((WInput*)wedln, par, fp)){
935         edln_deinit(&(wedln->edln));
936         free(wedln->prompt);
937         return FALSE;
938     }
939
940     window_create_xic(&wedln->input.win);
941     
942     wedln->handler=extl_ref_fn(params->handler);
943     wedln->completor=extl_ref_fn(params->completor);
944
945     region_add_bindmap((WRegion*)wedln, mod_query_wedln_bindmap);
946     
947     return TRUE;
948 }
949
950
951 WEdln *create_wedln(WWindow *par, const WFitParams *fp,
952                     WEdlnCreateParams *params)
953 {
954     CREATEOBJ_IMPL(WEdln, wedln, (p, par, fp, params));
955 }
956
957
958 static void wedln_deinit(WEdln *wedln)
959 {
960     if(wedln->prompt!=NULL)
961         free(wedln->prompt);
962         
963     if(wedln->info!=NULL)
964         free(wedln->info);
965
966     if(wedln->compl_beg!=NULL)
967         free(wedln->compl_beg);
968
969     if(wedln->compl_end!=NULL)
970         free(wedln->compl_end);
971
972     if(wedln->compl_list.strs!=NULL)
973         deinit_listing(&(wedln->compl_list));
974
975     if(wedln->autoshowcompl_timer!=NULL)
976         destroy_obj((Obj*)wedln->autoshowcompl_timer);
977
978     if(wedln->cycle_bindmap!=NULL)
979         bindmap_destroy(wedln->cycle_bindmap);
980     
981     extl_unref_fn(wedln->completor);
982     extl_unref_fn(wedln->handler);
983     
984     edln_deinit(&(wedln->edln));
985     input_deinit((WInput*)wedln);
986 }
987
988
989 static void wedln_do_finish(WEdln *wedln)
990 {
991     ExtlFn handler;
992     char *p;
993     
994     handler=wedln->handler;
995     wedln->handler=extl_fn_none();
996     p=edln_finish(&(wedln->edln));
997     
998     if(region_manager_allows_destroying((WRegion*)wedln))
999        destroy_obj((Obj*)wedln);
1000     
1001     if(p!=NULL)
1002         extl_call(handler, "s", NULL, p);
1003     
1004     free(p);
1005     extl_unref_fn(handler);
1006 }
1007
1008
1009 /*EXTL_DOC
1010  * Close \var{wedln} and call any handlers.
1011  */
1012 EXTL_EXPORT_MEMBER
1013 void wedln_finish(WEdln *wedln)
1014 {
1015     mainloop_defer_action((Obj*)wedln, (WDeferredAction*)wedln_do_finish);
1016 }
1017
1018
1019 /*}}}*/
1020
1021
1022 /*{{{ The rest */
1023
1024
1025 /*EXTL_DOC
1026  * Request selection from application holding such.
1027  * 
1028  * Note that this function is asynchronous; the selection will not
1029  * actually be inserted before Ion receives it. This will be no
1030  * earlier than Ion return to its main loop.
1031  */
1032 EXTL_EXPORT_MEMBER
1033 void wedln_paste(WEdln *wedln)
1034 {
1035     ioncore_request_selection_for(wedln->input.win.win);
1036 }
1037
1038
1039 void wedln_insstr(WEdln *wedln, const char *buf, size_t n)
1040 {
1041     edln_insstr_n(&(wedln->edln), buf, n, TRUE, TRUE);
1042 }
1043
1044
1045 static const char *wedln_style(WEdln *wedln)
1046 {
1047     return "input-edln";
1048 }
1049
1050
1051 /*}}}*/
1052
1053
1054 /*{{{ Dynamic function table and class implementation */
1055
1056
1057 static DynFunTab wedln_dynfuntab[]={
1058     {window_draw, wedln_draw},
1059     {input_calc_size, wedln_calc_size},
1060     {input_scrollup, wedln_scrollup_completions},
1061     {input_scrolldown, wedln_scrolldown_completions},
1062     {window_insstr, wedln_insstr},
1063     {(DynFun*)input_style, (DynFun*)wedln_style},
1064     END_DYNFUNTAB
1065 };
1066
1067
1068 EXTL_EXPORT
1069 IMPLCLASS(WEdln, WInput, wedln_deinit, wedln_dynfuntab);
1070
1071     
1072 /*}}}*/