]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libcontrol/entry.c
libcontrol: primitive text entry cut and paste with mouse
[plan9front.git] / sys / src / libcontrol / entry.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <control.h>
8
9 typedef struct Entry Entry;
10
11 struct Entry
12 {
13         Control;
14         int             border;
15         CFont   *font;
16         CImage  *image;
17         CImage  *textcolor;
18         CImage  *bordercolor;
19         Rune            *text;
20         int             ntext;
21         int             cursor;
22         int             align;
23         int             hasfocus;
24         int             lastbut;
25 };
26
27 enum{
28         EAlign,
29         EBorder,
30         EBordercolor,
31         EData,
32         EFocus,
33         EFont,
34         EFormat,
35         EHide,
36         EImage,
37         ERect,
38         EReveal,
39         EShow,
40         ESize,
41         ETextcolor,
42         EValue,
43 };
44
45 static char *cmds[] = {
46         [EAlign] =                      "align",
47         [EBorder] =             "border",
48         [EBordercolor] =        "bordercolor",
49         [EData] =                       "data",
50         [EFocus] =              "focus",
51         [EFont] =                       "font",
52         [EFormat] =             "format",
53         [EHide] =                       "hide",
54         [EImage] =              "image",
55         [ERect] =                       "rect",
56         [EReveal] =             "reveal",
57         [EShow] =                       "show",
58         [ESize] =                       "size",
59         [ETextcolor] =          "textcolor",
60         [EValue] =                      "value",
61         nil
62 };
63
64 static void
65 entryfree(Control *c)
66 {
67         Entry *e;
68
69         e = (Entry *)c;
70         _putctlfont(e->font);
71         _putctlimage(e->image);
72         _putctlimage(e->textcolor);
73         _putctlimage(e->bordercolor);
74         free(e->text);
75 }
76
77 static Point
78 entrypoint(Entry *e, int c)
79 {
80         Point p;
81         Rectangle r;
82
83         r = e->rect;
84         if(e->border > 0)
85                 r = insetrect(r, e->border);
86         p = _ctlalignpoint(r,
87                 runestringnwidth(e->font->font, e->text, e->ntext),
88                 e->font->font->height, e->align);
89         if(c > e->ntext)
90                 c = e->ntext;
91         p.x += runestringnwidth(e->font->font, e->text, c);
92         return p;
93 }
94
95 static void
96 entryshow(Entry *e)
97 {
98         Rectangle r, dr;
99         Point p;
100
101         if (e->hidden)
102                 return;
103         r = e->rect;
104         draw(e->screen, r, e->image->image, nil, e->image->image->r.min);
105         if(e->border > 0){
106                 border(e->screen, r, e->border, e->bordercolor->image, e->bordercolor->image->r.min);
107                 dr = insetrect(r, e->border);
108         }else
109                 dr = r;
110         p = entrypoint(e, 0);
111         _string(e->screen, p, e->textcolor->image,
112                 ZP, e->font->font, nil, e->text, e->ntext,
113                 dr, nil, ZP, SoverD);
114         if(e->hasfocus){
115                 p = entrypoint(e, e->cursor);
116                 r.min = p;
117                 r.max.x = p.x+1;
118                 r.max.y = p.y+e->font->font->height;
119                 if(rectclip(&r, dr))
120                         draw(e->screen, r, e->textcolor->image, nil, ZP);
121         }
122         flushimage(display, 1);
123 }
124
125 static void
126 entrysetpoint(Entry *e, Point cp)
127 {
128         Point p;
129         int i;
130
131         if(!ptinrect(cp, insetrect(e->rect, e->border)))
132                 return;
133         p = entrypoint(e, 0);
134         for(i=0; i<e->ntext; i++){
135                 p.x += runestringnwidth(e->font->font, e->text+i, 1);
136                 if(p.x > cp.x)
137                         break;
138         }
139         e->cursor = i;
140         entryshow(e);
141 }
142
143 static void
144 entrycut(Entry *e)
145 {
146         _ctlputsnarf(e->text);
147         e->cursor = 0;
148         e->ntext = 0;
149         e->text[0] = L'\0';
150 }
151
152 static void
153 entrypaste(Entry *e)
154 {
155         Rune *s;
156         int n;
157
158         s = _ctlgetsnarf();
159         if(s == nil)
160                 return;
161         n = runestrlen(s);
162         e->text = ctlrealloc(e->text, (e->ntext+n+1)*sizeof(Rune));
163         memmove(e->text+e->cursor+n, e->text+e->cursor,
164                 (e->ntext+1-e->cursor)*sizeof(Rune));
165         memmove(e->text+e->cursor, s, n*sizeof(Rune));
166         e->cursor += n;
167         e->ntext += n;
168 }
169
170 static void
171 entrymouse(Control *c, Mouse *m)
172 {
173         Entry *e;
174
175         e = (Entry*)c;
176         if(m->buttons==1 && e->lastbut==0){
177                 entrysetpoint(e, m->xy);
178         } else if(m->buttons==3 && e->lastbut!=3){
179                 entrycut(e);
180                 entryshow(e);
181         } else if(m->buttons==5 && e->lastbut!=5){
182                 entrypaste(e);
183                 entryshow(e);
184         }
185         e->lastbut = m->buttons;
186 }
187
188 static void
189 entryctl(Control *c, CParse *cp)
190 {
191         int cmd;
192         Rectangle r;
193         Entry *e;
194         Rune *rp;
195
196         e = (Entry*)c;
197         cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
198         switch(cmd){
199         default:
200                 ctlerror("%q: unrecognized message '%s'", e->name, cp->str);
201                 break;
202         case EAlign:
203                 _ctlargcount(e, cp, 2);
204                 e->align = _ctlalignment(cp->args[1]);
205                 break;
206         case EBorder:
207                 _ctlargcount(e, cp, 2);
208                 if(cp->iargs[1] < 0)
209                         ctlerror("%q: bad border: %c", e->name, cp->str);
210                 e->border = cp->iargs[1];
211                 break;
212         case EBordercolor:
213                 _ctlargcount(e, cp, 2);
214                 _setctlimage(e, &e->bordercolor, cp->args[1]);
215                 break;
216         case EData:
217                 _ctlargcount(e, cp, 1);
218                 chanprint(e->data, "%S", e->text);
219                 break;
220         case EFocus:
221                 _ctlargcount(e, cp, 2);
222                 e->hasfocus = cp->iargs[1];
223                 e->lastbut = 0;
224                 entryshow(e);
225                 break;
226         case EFont:
227                 _ctlargcount(e, cp, 2);
228                 _setctlfont(e, &e->font, cp->args[1]);
229                 break;
230         case EFormat:
231                 _ctlargcount(e, cp, 2);
232                 e->format = ctlstrdup(cp->args[1]);
233                 break;
234         case EHide:
235                 _ctlargcount(e, cp, 1);
236                 e->hidden = 1;
237                 break;
238         case EImage:
239                 _ctlargcount(e, cp, 2);
240                 _setctlimage(e, &e->image, cp->args[1]);
241                 break;
242         case ERect:
243                 _ctlargcount(e, cp, 5);
244                 r.min.x = cp->iargs[1];
245                 r.min.y = cp->iargs[2];
246                 r.max.x = cp->iargs[3];
247                 r.max.y = cp->iargs[4];
248                 if(Dx(r)<=0 || Dy(r)<=0)
249                         ctlerror("%q: bad rectangle: %s", e->name, cp->str);
250                 e->rect = r;
251                 break;
252         case EReveal:
253                 _ctlargcount(e, cp, 1);
254                 e->hidden = 0;
255                 entryshow(e);
256                 break;
257         case EShow:
258                 _ctlargcount(e, cp, 1);
259                 entryshow(e);
260                 break;
261         case ESize:
262                 if (cp->nargs == 3)
263                         r.max = Pt(0x7fffffff, 0x7fffffff);
264                 else{
265                         _ctlargcount(e, cp, 5);
266                         r.max.x = cp->iargs[3];
267                         r.max.y = cp->iargs[4];
268                 }
269                 r.min.x = cp->iargs[1];
270                 r.min.y = cp->iargs[2];
271                 if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
272                         ctlerror("%q: bad sizes: %s", e->name, cp->str);
273                 e->size.min = r.min;
274                 e->size.max = r.max;
275                 break;
276         case ETextcolor:
277                 _ctlargcount(e, cp, 2);
278                 _setctlimage(e, &e->textcolor, cp->args[1]);
279                 break;
280         case EValue:
281                 _ctlargcount(e, cp, 2);
282                 rp = _ctlrunestr(cp->args[1]);
283                 if(runestrcmp(rp, e->text) != 0){
284                         free(e->text);
285                         e->text = rp;
286                         e->ntext = runestrlen(e->text);
287                         e->cursor = e->ntext;
288                         entryshow(e);
289                 }else
290                         free(rp);
291                 break;
292         }
293 }
294
295 static void
296 entrykey(Entry *e, Rune r)
297 {
298         char *p;
299
300         switch(r){
301         default:
302                 e->text = ctlrealloc(e->text, (e->ntext+1+1)*sizeof(Rune));
303                 memmove(e->text+e->cursor+1, e->text+e->cursor,
304                         (e->ntext+1-e->cursor)*sizeof(Rune));
305                 e->text[e->cursor++] = r;
306                 e->ntext++;
307                 break;
308         case L'\n':     /* newline: return value */
309                 p = _ctlstrrune(e->text);
310                 chanprint(e->event, e->format, e->name, p);
311                 free(p);
312                 return;
313         case L'\b':
314                 if(e->cursor > 0){
315                         memmove(e->text+e->cursor-1, e->text+e->cursor,
316                                 (e->ntext+1-e->cursor)*sizeof(Rune));
317                         e->cursor--;
318                         e->ntext--;
319                 }
320                 break;
321         case Kright:
322                 if(e->cursor < e->ntext)
323                         e->cursor++;
324                 break;
325         case Kleft:
326                 if(e->cursor > 0)
327                         e->cursor--;
328                 break;
329         case 0x01:      /* control A: beginning of line */
330                 e->cursor = 0;
331                 break;
332         case 0x05:      /* control E: end of line */
333                 e->cursor = e->ntext;
334                 break;
335         case 0x15:      /* control U: kill line */
336                 e->cursor = 0;
337                 e->ntext = 0;
338                 break;
339         case 0x16:      /* control V: paste (append snarf buffer) */
340                 entrypaste(e);
341                 break;
342         }
343         e->text[e->ntext] = L'\0';
344 }
345
346 static void
347 entrykeys(Control *c, Rune *rp)
348 {
349         Entry *e;
350         int i;
351
352         e = (Entry *)c;
353         for(i=0; rp[i]!=L'\0'; i++)
354                 entrykey(e, rp[i]);
355         entryshow(e);
356 }
357
358 Control*
359 createentry(Controlset *cs, char *name)
360 {
361         Entry *e;
362
363         e = (Entry*) _createctl(cs, "entry", sizeof(Entry), name);
364         e->text = ctlmalloc(sizeof(Rune));
365         e->ntext = 0;
366         e->image = _getctlimage("white");
367         e->textcolor = _getctlimage("black");
368         e->bordercolor = _getctlimage("black");
369         e->font = _getctlfont("font");
370         e->format = ctlstrdup("%q: value %q");
371         e->border = 0;
372         e->ctl = entryctl;
373         e->mouse = entrymouse;
374         e->key = entrykeys;
375         e->exit = entryfree;
376         return (Control *)e;
377 }