]> git.decadent.org.uk Git - ion3.git/blob - mod_query/wedln.c
Imported Upstream version 20090110
[ion3.git] / mod_query / wedln.c
1 /*
2  * ion/mod_query/wedln.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10
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>
17
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"
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                               GrAttr a)
54 {
55     if(len==0)
56         return 0;
57     
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);
61     
62     return grbrush_get_text_width(WEDLN_BRUSH(wedln), str, len);
63 }
64
65 #if 0
66 static void dispu(const char* s, int l)
67 {
68     while(l>0){
69         int c=(unsigned char)*s;
70         fprintf(stderr, "%d[%c]", c, *s);
71         s++;
72         l--;
73     }
74     fprintf(stderr, "\n");
75 }
76 #endif
77
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;                                 \
83     }
84
85
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);
93
94 static void init_attr()
95 {
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;
105 }
106
107
108 static void wedln_do_draw_str_box(WEdln *wedln, const WRectangle *geom,
109                                   const char *str, int cursor, 
110                                   int mark, int tx)
111 {
112     int len=strlen(str), ll=0, ty=0;
113     
114     ty=calc_text_y(wedln, geom);
115
116     if(mark<=cursor){
117         if(mark>=0){
118             DSTRSECT(mark, normal);
119             DSTRSECT(cursor-mark, selection);
120         }else{
121             DSTRSECT(cursor, normal);
122         }
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);
129         }
130     }else{
131         DSTRSECT(cursor, normal);
132         ll=str_nextoff(str, 0);
133         DSTRSECT(ll, cursor);
134         DSTRSECT(mark-cursor-ll, selection);
135     }
136     DSTRSECT(len, normal);
137
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);
143     }
144 }
145
146
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)
150 {
151     int tx=0;
152
153     /* Some fonts and Xmb/utf8 routines don't work well with dstart!=0. */
154     dstart=0;
155     
156     if(mark>=0){
157         mark-=vstart+dstart;
158         if(mark<0)
159             mark=0;
160     }
161     
162     point-=vstart+dstart;
163     
164     if(dstart!=0)
165         tx=grbrush_get_text_width(WEDLN_BRUSH(wedln), str+vstart, dstart);
166
167     grbrush_begin(WEDLN_BRUSH(wedln), geom, 
168                   GRBRUSH_AMEND|GRBRUSH_KEEP_ATTR|GRBRUSH_NEED_CLIP);
169     
170     wedln_do_draw_str_box(wedln, geom, str+vstart+dstart, point, mark, tx);
171
172     grbrush_end(WEDLN_BRUSH(wedln));
173 }
174
175
176 static bool wedln_update_cursor(WEdln *wedln, int iw)
177 {
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;
185     
186     if(point<wedln->vstart)
187         wedln->vstart=point;
188     
189     if(wedln->vstart==point)
190         return FALSE;
191     
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);
201         }
202         
203         if(cx<iw)
204             break;
205         
206         l=str_nextoff(str, vstart);
207         if(l==0)
208             break;
209         vstart+=l;
210     }
211     
212     ret=(wedln->vstart!=vstart);
213     wedln->vstart=vstart;
214     
215     return ret;
216 }
217
218
219 /*}}}*/
220
221
222 /*{{{ Size/location calc */
223
224
225 static int get_textarea_height(WEdln *wedln, bool with_spacing)
226 {
227     int w=1, h=1;
228     
229     if(WEDLN_BRUSH(wedln)!=NULL)
230         mod_query_get_minimum_extents(WEDLN_BRUSH(wedln), with_spacing, &w, &h);
231     
232     return h;
233 }
234
235
236 enum{G_NORESET, G_MAX, G_CURRENT};
237
238
239 static void get_geom(WEdln *wedln, int mode, WRectangle *geom)
240 {
241     if(mode==G_MAX)
242         *geom=wedln->input.last_fp.g;
243     else if(mode==G_CURRENT)
244         *geom=REGION_GEOM(wedln);
245 }
246
247
248 static void get_completions_geom(WEdln *wedln, int mode, WRectangle *geom)
249 {
250     get_geom(wedln, mode, geom);
251     geom->x=0;
252     geom->y=0;
253     
254     geom->h-=get_textarea_height(wedln, TRUE);
255     if(geom->h<0)
256         geom->h=0;
257 }
258
259
260 static void get_outer_geom(WEdln *wedln, int mode, WRectangle *geom)
261 {
262     int th;
263     
264     get_geom(wedln, mode, geom);
265     geom->x=0;
266     geom->y=0;
267     
268     th=get_textarea_height(wedln, FALSE);
269     
270     geom->y+=geom->h-th;
271     geom->h=th;
272 }
273
274
275 static void get_inner_geom(WEdln *wedln, int mode, WRectangle *geom)
276 {
277     GrBorderWidths bdw;
278     
279     grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
280     
281     get_outer_geom(wedln, mode, geom);
282     
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);
289 }
290
291
292 static void get_textarea_geom(WEdln *wedln, int mode, WRectangle *geom)
293 {
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);
297 }
298
299
300 static void wedln_calc_size(WEdln *wedln, WRectangle *geom)
301 {
302     int h, th;
303     GrBorderWidths bdw;
304     WRectangle max_geom=*geom, tageom;
305     
306     if(WEDLN_BRUSH(wedln)==NULL)
307         return;
308     
309     if(wedln->prompt!=NULL){
310         wedln->prompt_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
311                                                wedln->prompt, 
312                                                wedln->prompt_len);
313     }
314     
315     if(wedln->info!=NULL){
316         wedln->info_w=grbrush_get_text_width(WEDLN_BRUSH(wedln),
317                                              wedln->info, 
318                                              wedln->info_len);
319     }
320
321     th=get_textarea_height(wedln, wedln->compl_list.strs!=NULL);
322     
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;
330         
331         get_completions_geom(wedln, G_MAX, &g);
332         
333         fit_listing(WEDLN_BRUSH(wedln), &g, &(wedln->compl_list));
334         
335         grbrush_get_border_widths(WEDLN_BRUSH(wedln), &bdw);
336         
337         h=wedln->compl_list.toth;
338         th+=bdw.top+bdw.bottom;
339         
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;
343     }
344     
345     geom->w=max_geom.w;
346     geom->y=max_geom.y+max_geom.h-geom->h;
347     geom->x=max_geom.x;
348
349     tageom=*geom;
350     get_textarea_geom(wedln, G_NORESET, &tageom);
351     wedln_update_cursor(wedln, tageom.w);
352 }
353
354
355 void wedln_size_hints(WEdln *wedln, WSizeHints *hints_ret)
356 {
357     int w=1, h=1;
358     
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);
363     }
364         
365     hints_ret->min_set=TRUE;
366     hints_ret->min_width=w;
367     hints_ret->min_height=h;
368 }
369
370
371 /*}}}*/
372
373
374 /*{{{ Draw */
375
376
377 void wedln_draw_completions(WEdln *wedln, int mode)
378 {
379     WRectangle geom;
380     
381     if(wedln->compl_list.strs!=NULL && WEDLN_BRUSH(wedln)!=NULL){
382         get_completions_geom(wedln, G_CURRENT, &geom);
383         
384         draw_listing(WEDLN_BRUSH(wedln), &geom, &(wedln->compl_list), 
385                      mode, GR_ATTR(selection));
386     }
387 }
388
389
390 void wedln_draw_textarea(WEdln *wedln)    
391 {
392     WRectangle geom;
393     int ty;
394
395     if(WEDLN_BRUSH(wedln)==NULL)
396         return;
397     
398     get_outer_geom(wedln, G_CURRENT, &geom);
399     
400     /*grbrush_begin(WEDLN_BRUSH(wedln), &geom, GRBRUSH_AMEND);*/
401     
402     grbrush_draw_border(WEDLN_BRUSH(wedln), &geom);
403
404     get_inner_geom(wedln, G_CURRENT, &geom);
405
406     ty=calc_text_y(wedln, &geom);
407
408     grbrush_set_attr(WEDLN_BRUSH(wedln), GR_ATTR(prompt));
409    
410     if(wedln->prompt!=NULL){
411         grbrush_draw_string(WEDLN_BRUSH(wedln), geom.x, ty,
412                             wedln->prompt, wedln->prompt_len, TRUE);
413     }
414     
415     if(wedln->info!=NULL){
416         int x=geom.x+geom.w-wedln->info_w;
417         
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));
422     }
423
424     grbrush_unset_attr(WEDLN_BRUSH(wedln), GR_ATTR(prompt));
425         
426     get_textarea_geom(wedln, G_CURRENT, &geom);
427     
428     wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, 0,
429                        wedln->edln.point, wedln->edln.mark);
430
431     /*grbrush_end(WEDLN_BRUSH(wedln));*/
432 }
433
434
435 static void wedln_draw_(WEdln *wedln, bool complete, bool completions)
436 {
437     WRectangle g;
438     int f=(complete ? 0 : GRBRUSH_NO_CLEAR_OK);
439     
440     if(WEDLN_BRUSH(wedln)==NULL)
441         return;
442     
443     get_geom(wedln, G_CURRENT, &g);
444     
445     grbrush_begin(WEDLN_BRUSH(wedln), &g, f);
446     
447     grbrush_set_attr(WEDLN_BRUSH(wedln), REGION_IS_ACTIVE(wedln) 
448                                          ? GR_ATTR(active) 
449                                          : GR_ATTR(inactive));
450
451     if(completions)
452         wedln_draw_completions(wedln, LISTING_DRAW_ALL);
453     
454     wedln_draw_textarea(wedln);
455     
456     grbrush_end(WEDLN_BRUSH(wedln));
457 }
458
459
460 void wedln_draw(WEdln *wedln, bool complete)
461 {
462     wedln_draw_(wedln, complete, TRUE);
463 }
464
465 /*}}} */
466
467
468 /*{{{ Info area */
469
470
471 static void wedln_set_info(WEdln *wedln, const char *info)
472 {
473     WRectangle tageom;
474     char *p;
475     
476     if(wedln->info!=NULL){
477         free(wedln->info);
478         wedln->info=NULL;
479         wedln->info_w=0;
480         wedln->info_len=0;
481     }
482     
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);
491             }
492         }
493     }
494     
495     get_textarea_geom(wedln, G_CURRENT, &tageom);
496     wedln_update_cursor(wedln, tageom.w);
497     
498     wedln_draw_(wedln, FALSE, FALSE);
499 }
500
501
502 /*}}}*/
503
504
505 /*{{{ Completions */
506
507
508 static void wedln_show_completions(WEdln *wedln, char **strs, int nstrs,
509                                    int selected)
510 {
511     int w=REGION_GEOM(wedln).w;
512     int h=REGION_GEOM(wedln).h;
513     
514     if(WEDLN_BRUSH(wedln)==NULL)
515         return;
516     
517     setup_listing(&(wedln->compl_list), strs, nstrs, FALSE);
518     wedln->compl_list.selected_str=selected;
519
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);
523 }
524
525
526 void wedln_hide_completions(WEdln *wedln)
527 {
528     if(wedln->compl_list.strs!=NULL){
529         deinit_listing(&(wedln->compl_list));
530         input_refit((WInput*)wedln);
531     }
532 }
533     
534
535 void wedln_scrollup_completions(WEdln *wedln)
536 {
537     if(wedln->compl_list.strs==NULL)
538         return;
539     if(scrollup_listing(&(wedln->compl_list)))
540         wedln_draw_completions(wedln, LISTING_DRAW_COMPLETE);
541 }
542
543
544 void wedln_scrolldown_completions(WEdln *wedln)
545 {
546     if(wedln->compl_list.strs==NULL)
547         return;
548     if(scrolldown_listing(&(wedln->compl_list)))
549         wedln_draw_completions(wedln, LISTING_DRAW_COMPLETE);
550 }
551
552
553 static int update_nocompl=0;
554
555
556 static void free_completions(char **ptr, int i)
557 {
558     while(i>0){
559         i--;
560         if(ptr[i]!=NULL)
561             free(ptr[i]);
562     }
563     free(ptr);
564 }
565
566
567 bool wedln_do_set_completions(WEdln *wedln, char **ptr, int n, 
568                               char *beg, char *end, int cycle,
569                               bool nosort)
570 {    
571     int sel=-1;
572     
573     if(wedln->compl_beg!=NULL)
574         free(wedln->compl_beg);
575     
576     if(wedln->compl_end!=NULL)
577         free(wedln->compl_end);
578     
579     wedln->compl_beg=beg;
580     wedln->compl_end=end;
581     wedln->compl_current_id=-1;
582     
583     n=edln_do_completions(&(wedln->edln), ptr, n, beg, end, 
584                           !mod_query_config.autoshowcompl, nosort);
585
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--;
591     }
592
593     if(n>1 || (mod_query_config.autoshowcompl && n>0)){
594         wedln_show_completions(wedln, ptr, n, sel);
595         return TRUE;
596     }
597     
598     free_completions(ptr, n);
599     
600     return FALSE;
601 }
602
603
604 void wedln_set_completions(WEdln *wedln, ExtlTab completions, int cycle)
605 {
606     int n=0, i=0;
607     char **ptr=NULL, *beg=NULL, *end=NULL, *p=NULL;
608     
609     n=extl_table_get_n(completions);
610     
611     if(n==0){
612         wedln_hide_completions(wedln);
613         return;
614     }
615     
616     ptr=ALLOC_N(char*, n);
617     if(ptr==NULL)
618         goto allocfail;
619
620     for(i=0; i<n; i++){
621         if(!extl_table_geti_s(completions, i+1, &p)){
622             goto allocfail;
623         }
624         ptr[i]=p;
625     }
626
627     extl_table_gets_s(completions, "common_beg", &beg);
628     extl_table_gets_s(completions, "common_end", &end);
629     
630     if(!wedln_do_set_completions(wedln, ptr, n, beg, end, cycle, FALSE))
631         wedln_hide_completions(wedln);
632
633     return;
634     
635 allocfail:
636     wedln_hide_completions(wedln);
637     free_completions(ptr, i);
638 }
639     
640
641 static void wedln_do_select_completion(WEdln *wedln, int n)
642 {
643     bool redraw=listing_select(&(wedln->compl_list), n);
644     wedln_draw_completions(wedln, redraw);
645
646     update_nocompl++;
647     edln_set_completion(&(wedln->edln), wedln->compl_list.strs[n],
648                         wedln->compl_beg, wedln->compl_end);
649     update_nocompl--;
650 }
651
652
653
654 static ExtlExportedFn *sc_safe_fns[]={
655     (ExtlExportedFn*)&complproxy_set_completions,
656     NULL
657 };
658
659
660 static ExtlSafelist sc_safelist=EXTL_SAFELIST_INIT(sc_safe_fns);
661
662
663 static int wedln_alloc_compl_id(WEdln *wedln)
664 {
665     int id=wedln->compl_waiting_id+1;
666     wedln->compl_waiting_id=maxof(0, wedln->compl_waiting_id+1);
667     return id;
668 }
669
670 static bool wedln_do_call_completor(WEdln *wedln, int id, int cycle)
671 {
672     if(wedln->compl_history_mode){
673         uint n;
674         char **h=NULL;
675         
676         wedln->compl_waiting_id=id;
677
678         n=edln_history_matches(&wedln->edln, &h);
679         
680         if(n==0){
681             wedln_hide_completions(wedln);
682             return FALSE;
683         }
684         
685         if(wedln_do_set_completions(wedln, h, n, NULL, NULL, cycle, TRUE)){
686             wedln->compl_current_id=id;
687             return TRUE;
688         }
689         
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);
695         
696         if(proxy==NULL)
697             return FALSE;
698         
699         /* Set Lua-side to own the proxy: it gets freed by Lua's GC */
700         ((Obj*)proxy)->flags|=OBJ_EXTL_OWNED;
701         
702         if(p==NULL){
703             p="";
704             point=0;
705         }
706         
707         extl_protect(&sc_safelist);
708         extl_call(wedln->completor, "osi", NULL, proxy, p, point);
709         extl_unprotect(&sc_safelist);
710         
711         return TRUE;
712     }
713 }
714
715
716 static void timed_complete(WTimer *tmr, Obj *obj)
717 {
718     WEdln *wedln=(WEdln*)obj;
719     
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);
725     }
726 }
727
728
729 /*EXTL_DOC
730  * Select next completion.
731  */
732 EXTL_EXPORT_MEMBER
733 bool wedln_next_completion(WEdln *wedln)
734 {
735     int n=-1;
736
737     if(wedln->compl_current_id!=wedln->compl_waiting_id)
738         return FALSE;
739     
740     if(wedln->compl_list.nstrs<=0)
741         return FALSE;
742     
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;
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  * Select previous completion.
759  */
760 EXTL_EXPORT_MEMBER
761 bool wedln_prev_completion(WEdln *wedln)
762 {
763     int n=-1;
764
765     if(wedln->compl_current_id!=wedln->compl_waiting_id)
766         return FALSE;
767     
768     if(wedln->compl_list.nstrs<=0)
769         return FALSE;
770     
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;
775     }
776
777     if(n!=wedln->compl_list.selected_str)
778         wedln_do_select_completion(wedln, n);
779     
780     return TRUE;
781 }
782
783
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}.
793  */
794 EXTL_EXPORT_MEMBER
795 void wedln_complete(WEdln *wedln, const char *cycle, const char *mode)
796 {
797     bool valid=TRUE;
798     int cyclei=0;
799     
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;
807         }
808         if(!valid){
809             wedln_set_info(wedln,
810                            (wedln->compl_history_mode
811                             ? TR("history")
812                             : NULL));
813         }
814     }
815     
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;
823         }
824     }
825     
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;
834     
835         if(!wedln_do_call_completor(wedln, wedln_alloc_compl_id(wedln), 
836                                     cyclei)){
837             wedln->compl_waiting_id=oldid;
838         }
839     }
840 }
841
842
843 /*EXTL_DOC
844  * Get history completion mode.
845  */
846 EXTL_EXPORT_MEMBER
847 bool wedln_is_histcompl(WEdln *wedln)
848 {
849     return wedln->compl_history_mode;
850 }
851
852
853 /*}}}*/
854
855
856 /*{{{ Update handler */
857
858
859 static void wedln_update_handler(WEdln *wedln, int from, int flags)
860 {
861     WRectangle geom;
862     
863     if(WEDLN_BRUSH(wedln)==NULL)
864         return;
865     
866     get_textarea_geom(wedln, G_CURRENT, &geom);
867     
868     if(flags&EDLN_UPDATE_NEW)
869         wedln->vstart=0;
870     
871     if(flags&EDLN_UPDATE_MOVED){
872         if(wedln_update_cursor(wedln, geom.w))
873             from=wedln->vstart;
874     }
875     
876     from=maxof(0, from-wedln->vstart);
877
878     wedln_draw_str_box(wedln, &geom, wedln->vstart, wedln->edln.p, from,
879                        wedln->edln.point, wedln->edln.mark);
880     
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);
892         }
893     }
894 }
895
896
897 /*}}}*/
898
899
900 /*{{{ Init, deinit and config update */
901
902
903 static bool wedln_init_prompt(WEdln *wedln, const char *prompt)
904 {
905     char *p;
906     
907     if(prompt!=NULL){
908         p=scat(prompt, "  ");
909     
910         if(p==NULL)
911             return FALSE;
912
913         wedln->prompt=p;
914         wedln->prompt_len=strlen(p);
915     }else{
916         wedln->prompt=NULL;
917         wedln->prompt_len=0;
918     }
919     wedln->prompt_w=0;
920     
921     return TRUE;
922 }
923
924
925 static bool wedln_init(WEdln *wedln, WWindow *par, const WFitParams *fp, 
926                        WEdlnCreateParams *params)
927 {
928     wedln->vstart=0;
929
930     init_attr();
931     
932     if(!wedln_init_prompt(wedln, params->prompt))
933         return FALSE;
934     
935     if(!edln_init(&(wedln->edln), params->dflt)){
936         free(wedln->prompt);
937         return FALSE;
938     }
939     
940     wedln->handler=extl_fn_none();
941     wedln->completor=extl_fn_none();
942     
943     wedln->edln.uiptr=wedln;
944     wedln->edln.ui_update=(EdlnUpdateHandler*)wedln_update_handler;
945
946     wedln->autoshowcompl_timer=NULL;
947     
948     init_listing(&(wedln->compl_list));
949
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;
957     
958     wedln->info=NULL;
959     wedln->info_len=0;
960     wedln->info_w=0;
961     
962     wedln->cycle_bindmap=NULL;
963     
964     if(!input_init((WInput*)wedln, par, fp)){
965         edln_deinit(&(wedln->edln));
966         free(wedln->prompt);
967         return FALSE;
968     }
969
970     window_create_xic(&wedln->input.win);
971     
972     wedln->handler=extl_ref_fn(params->handler);
973     wedln->completor=extl_ref_fn(params->completor);
974
975     region_add_bindmap((WRegion*)wedln, mod_query_wedln_bindmap);
976     
977     return TRUE;
978 }
979
980
981 WEdln *create_wedln(WWindow *par, const WFitParams *fp,
982                     WEdlnCreateParams *params)
983 {
984     CREATEOBJ_IMPL(WEdln, wedln, (p, par, fp, params));
985 }
986
987
988 static void wedln_deinit(WEdln *wedln)
989 {
990     if(wedln->prompt!=NULL)
991         free(wedln->prompt);
992         
993     if(wedln->info!=NULL)
994         free(wedln->info);
995
996     if(wedln->compl_beg!=NULL)
997         free(wedln->compl_beg);
998
999     if(wedln->compl_end!=NULL)
1000         free(wedln->compl_end);
1001
1002     if(wedln->compl_list.strs!=NULL)
1003         deinit_listing(&(wedln->compl_list));
1004
1005     if(wedln->autoshowcompl_timer!=NULL)
1006         destroy_obj((Obj*)wedln->autoshowcompl_timer);
1007
1008     if(wedln->cycle_bindmap!=NULL)
1009         bindmap_destroy(wedln->cycle_bindmap);
1010     
1011     extl_unref_fn(wedln->completor);
1012     extl_unref_fn(wedln->handler);
1013     
1014     edln_deinit(&(wedln->edln));
1015     input_deinit((WInput*)wedln);
1016 }
1017
1018
1019 static void wedln_do_finish(WEdln *wedln)
1020 {
1021     ExtlFn handler;
1022     char *p;
1023     
1024     handler=wedln->handler;
1025     wedln->handler=extl_fn_none();
1026     p=edln_finish(&(wedln->edln));
1027     
1028     region_rqdispose((WRegion*)wedln);
1029     
1030     if(p!=NULL)
1031         extl_call(handler, "s", NULL, p);
1032     
1033     free(p);
1034     extl_unref_fn(handler);
1035 }
1036
1037
1038 /*EXTL_DOC
1039  * Close \var{wedln} and call any handlers.
1040  */
1041 EXTL_EXPORT_MEMBER
1042 void wedln_finish(WEdln *wedln)
1043 {
1044     mainloop_defer_action((Obj*)wedln, (WDeferredAction*)wedln_do_finish);
1045 }
1046
1047
1048 /*}}}*/
1049
1050
1051 /*{{{ The rest */
1052
1053
1054 /*EXTL_DOC
1055  * Request selection from application holding such.
1056  * 
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.
1060  */
1061 EXTL_EXPORT_MEMBER
1062 void wedln_paste(WEdln *wedln)
1063 {
1064     ioncore_request_selection_for(wedln->input.win.win);
1065 }
1066
1067
1068 void wedln_insstr(WEdln *wedln, const char *buf, size_t n)
1069 {
1070     edln_insstr_n(&(wedln->edln), buf, n, TRUE, TRUE);
1071 }
1072
1073
1074 static const char *wedln_style(WEdln *wedln)
1075 {
1076     return "input-edln";
1077 }
1078
1079
1080 /*}}}*/
1081
1082
1083 /*{{{ Dynamic function table and class implementation */
1084
1085
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
1095 };
1096
1097
1098 EXTL_EXPORT
1099 IMPLCLASS(WEdln, WInput, wedln_deinit, wedln_dynfuntab);
1100
1101     
1102 /*}}}*/