]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/mothra/libpanel/entry.c
libpanel: fix text sliding around in libpanel text entry widgets.
[plan9front.git] / sys / src / cmd / mothra / libpanel / entry.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 #include <panel.h>
6 #include "pldefs.h"
7 #include <keyboard.h>
8
9 typedef struct Entry Entry;
10 struct Entry{
11         Rectangle lastr;
12         Rune *entry;
13         char *sentry;
14         int sz, n;
15         int a, b;
16         Point text;
17         void (*hit)(Panel *, char *);
18         Point minsize;
19 };
20 #define SLACK   7       /* enough for one extra rune and ◀ and a nul */
21 void pl_cutentry(Panel *p){
22         Entry *ep;
23
24         ep=p->data;
25         memmove(ep->entry+ep->a, ep->entry+ep->b, (ep->n-ep->b)*sizeof(Rune));
26         ep->n -= ep->b-ep->a;
27         ep->entry[ep->n]=0;
28         ep->b=ep->a;
29 }
30 char *pl_snarfentry(Panel *p){
31         Entry *ep;
32         int n;
33
34         if(p->flags&USERFL)     /* no snarfing from password entry */
35                 return nil;
36         ep=p->data;
37         n=ep->b-ep->a;
38         if(n<1) return nil;
39         return smprint("%.*S", n, ep->entry+ep->a);
40 }
41 void pl_pasteentry(Panel *p, char *s){
42         Entry *ep;
43         int m;
44
45         ep=p->data;
46         m=utflen(s);
47         ep->sz=ep->n+m+100+SLACK;
48         ep->entry=pl_erealloc(ep->entry,ep->sz*sizeof(Rune));
49         memmove(ep->entry+ep->a+m, ep->entry+ep->b, (ep->n-ep->b)*sizeof(Rune));
50         ep->n+=m-(ep->b-ep->a);
51         while(m-- > 0)
52                 s += chartorune(&ep->entry[ep->a++], s);
53         ep->b=ep->a;
54         ep->entry[ep->n]=0;
55         pldraw(p, p->b);
56 }
57 static void drawentry(Panel *p, Rectangle r, Rune *s){
58         Rectangle save;
59         Point tick;
60         Entry *ep;
61         Image *b;
62         int d;
63
64         ep = p->data;
65         b = p->b;
66
67         ep->text = r.min;
68         ep->lastr = r;
69         tick = ep->text;
70         tick.x += runestringnwidth(font, s, ep->a);
71         if(plkbfocus == p)
72                 r.max.x -= TICKW;
73         ep->text.y = r.min.y;
74         if(!ptinrect(tick, r)){
75                 d = 0;
76                 if(tick.x < r.min.x)
77                         d = r.min.x - tick.x;
78                 else if(tick.x > r.max.x)
79                         d = r.max.x - tick.x;
80                 tick.x += d;
81                 ep->text.x += d;
82         }
83         if(plkbfocus == p)
84                 r.max.x += TICKW;
85
86         save = b->clipr;
87         if(!rectclip(&r, save))
88                 return;
89         replclipr(b, b->repl, r);
90         runestring(b, ep->text, pl_black, ZP, font, s);
91         if(plkbfocus == p){
92                 r.min = tick;
93                 if(ep->a != ep->b){
94                         r.max.x = ep->text.x+runestringnwidth(font, s, ep->b);
95                         if(r.max.x < r.min.x){
96                                 d = r.min.x;
97                                 r.min.x = r.max.x;
98                                 r.max.x = d;
99                         }
100                         pl_highlight(b, r);
101                 }else
102                         pl_drawtick(b, r);
103         }
104         replclipr(b, b->repl, save);
105 }
106 void pl_drawentry(Panel *p){
107         Rectangle r;
108         Entry *ep;
109         Rune *s;
110
111         ep=p->data;
112         r=pl_box(p->b, p->r, p->state|BORDER);
113         s=ep->entry;
114         if(p->flags & USERFL){
115                 Rune *p;
116                 s=runestrdup(s);
117                 for(p=s; *p; p++)
118                         *p='*';
119         }
120         drawentry(p, r, s);
121         if(s != ep->entry)
122                 free(s);
123 }
124 int pl_hitentry(Panel *p, Mouse *m){
125         Entry *ep;
126         int i, n, selecting;
127         if((m->buttons&7)==1){
128                 if(plkbfocus != p)
129                         p->state=DOWN;
130                 plgrabkb(p);
131                 ep = p->data;
132                 for(i = 1; i <= ep->n; i++)
133                         if(runestringnwidth(font, ep->entry, i) > m->xy.x-ep->text.x)
134                                 break;
135                 n = i-1;
136                 ep->a = ep->b = n;
137                 pldraw(p, p->b);
138                 selecting = 1;
139                 while(m->buttons&1){
140                         int old;
141                         old=m->buttons;
142                         if(display->bufp > display->buf)
143                                 flushimage(display, 1);
144                         *m=emouse();
145                         p->state=UP;
146                         if((old&7)==1){
147                                 if((m->buttons&7)==3){
148                                         plsnarf(p);
149                                         pl_cutentry(p);
150                                         pldraw(p, p->b);
151                                         ep->b = n = ep->a;
152                                 }
153                                 if(selecting && (m->buttons&7)==1){
154                                         p->state=UP;
155                                         for(i = 0; i < ep->n; i++)
156                                                 if(runestringnwidth(font, ep->entry, i)+TICKW > m->xy.x-ep->text.x)
157                                                         break;
158                                         /*
159                                          * tick is moved towards the mouse pointer dragging the selection
160                                          * after drawing it has to be set so that (a <= b), since
161                                          * the rest of the logic assumes that's always the case
162                                          */
163                                         ep->a = i;
164                                         ep->b = n;
165                                         pldraw(p, p->b);
166                                         if(ep->a > ep->b){
167                                                 ep->a = n;
168                                                 ep->b = i;
169                                         }
170                                 }else
171                                         selecting = 0;
172                                 if((m->buttons&7)==5)
173                                         plpaste(p);
174                         }
175                 }
176                 p->state=UP;
177                 pldraw(p, p->b);
178         }
179         return 0;
180 }
181 void pl_typeentry(Panel *p, Rune c){
182         Entry *ep;
183         ep=p->data;
184         switch(c){
185         case '\n':
186         case '\r':
187                 if(ep->hit) ep->hit(p, plentryval(p));
188                 return;
189         case Kleft:
190                 if(ep->a > 0)
191                         ep->a--;
192                 ep->b=ep->a;
193                 break;
194         case Kright:
195                 if(ep->a<ep->n)
196                         ep->a++;
197                 ep->b = ep->a;
198                 break;
199         case Ksoh:
200                 ep->a=ep->b=0;
201                 break;
202         case Kenq:
203                 ep->a=ep->b=ep->n;
204                 break;
205         case Kesc:
206                 ep->a=0;
207                 ep->b=ep->n;
208                 plsnarf(p);
209                 /* no break */
210         case Kdel:      /* clear */
211                 ep->a = ep->b = ep->n = 0;
212                 *ep->entry = 0;
213                 break;
214         case Knack:     /* ^U: erase line */
215                 ep->a = 0;
216                 pl_cutentry(p);
217                 break;
218         case Kbs:       /* ^H: erase character */
219                 if(ep->a > 0 && ep->a == ep->b)
220                         ep->a--;
221                 /* wet floor */
222                 if(0){
223         case Ketb:      /* ^W: erase word */
224                         while(ep->a>0 && !pl_idchar(ep->entry[ep->a-1]))
225                                 --ep->a;
226                         while(ep->a>0 && pl_idchar(ep->entry[ep->a-1]))
227                                 --ep->a;
228                 }
229                 pl_cutentry(p);
230                 break;
231         default:
232                 if(c < 0x20 || (c & 0xFF00) == KF || (c & 0xFF00) == Spec)
233                         break;
234                 memmove(ep->entry+ep->a+1, ep->entry+ep->b, (ep->n-ep->b)*sizeof(Rune));
235                 ep->n -= ep->b - ep->a - 1;
236                 ep->entry[ep->a++] = c;
237                 ep->b = ep->a;
238                 if(ep->n>ep->sz){
239                         ep->sz = ep->n+100;
240                         ep->entry=pl_erealloc(ep->entry, (ep->sz+SLACK)*sizeof(Rune));
241                 }
242                 ep->entry[ep->n]=0;
243                 break;
244         }
245         pldraw(p, p->b);
246 }
247 Point pl_getsizeentry(Panel *p, Point children){
248         USED(children);
249         return pl_boxsize(((Entry *)p->data)->minsize, p->state);
250 }
251 void pl_childspaceentry(Panel *p, Point *ul, Point *size){
252         USED(p, ul, size);
253 }
254 void pl_freeentry(Panel *p){
255         Entry *ep;
256         ep = p->data;
257         free(ep->entry);
258         free(ep->sentry);
259         ep->entry = nil;
260         ep->sentry = nil;
261 }
262 void plinitentry(Panel *v, int flags, int wid, char *str, void (*hit)(Panel *, char *)){
263         Entry *ep;
264         ep=v->data;
265         v->flags=flags|LEAF;
266         v->state=UP;
267         v->draw=pl_drawentry;
268         v->hit=pl_hitentry;
269         v->type=pl_typeentry;
270         v->getsize=pl_getsizeentry;
271         v->childspace=pl_childspaceentry;
272         ep->minsize=Pt(wid, font->height);
273         v->free=pl_freeentry;
274         v->snarf=pl_snarfentry;
275         v->paste=pl_pasteentry;
276         ep->a = ep->b = 0;
277         ep->n = str ? utflen(str) : 0;
278         ep->sz = ep->n + 100;
279         ep->entry=pl_erealloc(ep->entry, (ep->sz+SLACK)*sizeof(Rune));
280         runesnprint(ep->entry, ep->sz, "%s", str ? str : "");
281         ep->hit=hit;
282         v->kind="entry";
283 }
284 Panel *plentry(Panel *parent, int flags, int wid, char *str, void (*hit)(Panel *, char *)){
285         Panel *v;
286         v=pl_newpanel(parent, sizeof(Entry));
287         plinitentry(v, flags, wid, str, hit);
288         return v;
289 }
290 char *plentryval(Panel *p){
291         Entry *ep;
292         ep=p->data;
293         free(ep->sentry);
294         ep->sentry = smprint("%S", ep->entry);
295         return ep->sentry;
296 }