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