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