]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/mothra/libpanel/edit.c
mothra: snarf and paste
[plan9front.git] / sys / src / cmd / mothra / libpanel / edit.c
1 /*
2  * Interface includes:
3  *      void plescroll(Panel *p, int top);
4  *              move the given character position onto the top line
5  *      void plegetsel(Panel *p, int *sel0, int *sel1);
6  *              read the selection back
7  *      int plelen(Panel *p);
8  *              read the length of the text back
9  *      Rune *pleget(Panel *p);
10  *              get a pointer to the text
11  *      void plesel(Panel *p, int sel0, int sel1);
12  *              set the selection -- adjusts hiliting
13  *      void plepaste(Panel *p, Rune *text, int ntext);
14  *              replace the selection with the given text
15  */
16 #include <u.h>
17 #include <libc.h>
18 #include <draw.h>
19 #include <event.h>
20 #include <panel.h>
21 #include "pldefs.h"
22 #include <keyboard.h>
23
24 typedef struct Edit Edit;
25 struct Edit{
26         Point minsize;
27         void (*hit)(Panel *);
28         int sel0, sel1;
29         Textwin *t;
30         Rune *text;
31         int ntext;
32 };
33 void pl_drawedit(Panel *p){
34         Edit *ep;
35         Panel *sb;
36         ep=p->data;
37         if(ep->t==0){
38                 ep->t=twnew(p->b, font, ep->text, ep->ntext);
39                 if(ep->t==0){
40                         fprint(2, "pl_drawedit: can't allocate\n");
41                         exits("no mem");
42                 }
43         }
44         ep->t->b=p->b;
45         twreshape(ep->t, p->r);
46         twhilite(ep->t, ep->sel0, ep->sel1, 1);
47         sb=p->yscroller;
48         if(sb && sb->setscrollbar)
49                 sb->setscrollbar(sb, ep->t->top, ep->t->bot, ep->t->etext-ep->t->text);
50 }
51
52 char *pl_snarfedit(Panel *p){
53         int s0, s1;
54         Rune *t;
55
56         t=pleget(p);
57         plegetsel(p, &s0, &s1);
58         if(t==0 || s0>=s1)
59                 return nil;
60         return smprint("%.*S", s1-s0, t+s0);
61 }
62 void pl_pasteedit(Panel *p, char *s){
63         Rune *t;
64         if(t=runesmprint("%s", s)){
65                 plepaste(p, t, runestrlen(t));
66                 free(t);
67         }
68 }
69
70 /*
71  * Should do double-clicks:
72  *      If ep->sel0==ep->sel1 on entry and the
73  *      call to twselect returns the same selection, then
74  *      expand selections (| marks possible selection points, ... is expanded selection)
75  *      <|...|>                 <> must nest
76  *      (|...|)                 () must nest
77  *      [|...|]                 [] must nest
78  *      {|...|}                 {} must nest
79  *      '|...|'                 no ' in ...
80  *      "|...|"                 no " in ...
81  *      \n|...|\n               either newline may be the corresponding end of text
82  *                              include the trailing newline in the selection
83  *      ...|I...                I and ... are characters satisfying pl_idchar(I)
84  *      ...I|
85  */
86 int pl_hitedit(Panel *p, Mouse *m){
87         Edit *ep;
88         if(m->buttons&1){
89                 plgrabkb(p);
90                 ep=p->data;
91                 ep->t->b=p->b;
92                 twhilite(ep->t, ep->sel0, ep->sel1, 0);
93                 twselect(ep->t, m);
94                 ep->sel0=ep->t->sel0;
95                 ep->sel1=ep->t->sel1;
96                 if((m->buttons&7)==3){
97                         plsnarf(p);
98                         plepaste(p, 0, 0);      /* cut */
99                 }
100                 else if((m->buttons&7)==5)
101                         plpaste(p);
102                 else if(ep->hit)
103                         (*ep->hit)(p);
104         }
105         return 0;
106 }
107 void pl_scrolledit(Panel *p, int dir, int buttons, int num, int den){
108         Edit *ep;
109         Textwin *t;
110         Panel *sb;
111         int index, nline;
112         if(dir!=VERT) return;
113         ep=p->data;
114         t=ep->t;
115         t->b=p->b;
116         switch(buttons){
117         default:
118                 return;
119         case 1:         /* top line moves to mouse position */
120                 nline=(t->r.max.y-t->r.min.y)/t->hgt*num/den;
121                 index=t->top;
122                 while(index!=0 && nline!=0)
123                         if(t->text[--index]=='\n') --nline;
124                 break;
125         case 2:         /* absolute */
126                 index=(t->etext-t->text)*num/den;
127                 break;
128         case 4:         /* mouse points at new top line */
129                 index=twpt2rune(t,
130                         Pt(t->r.min.x, t->r.min.y+(t->r.max.y-t->r.min.y)*num/den));
131                 break;
132         }
133         while(index!=0 && t->text[index-1]!='\n') --index;
134         if(index!=t->top){
135                 twhilite(ep->t, ep->sel0, ep->sel1, 0);
136                 twscroll(t, index);
137                 p->scr.pos.y=t->top;
138                 twhilite(ep->t, ep->sel0, ep->sel1, 1);
139                 sb=p->yscroller;
140                 if(sb && sb->setscrollbar)
141                         sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text);
142         }
143 }
144 void pl_typeedit(Panel *p, Rune c){
145         Edit *ep;
146         Textwin *t;
147         int bot, scrolled;
148         Panel *sb;
149         ep=p->data;
150         t=ep->t;
151         t->b=p->b;
152         twhilite(t, ep->sel0, ep->sel1, 0);
153         switch(c){
154         case Kesc:
155                 plsnarf(p);
156                 plepaste(p, 0, 0);      /* cut */
157                 break;
158         case Kdel:      /* clear */
159                 ep->sel0=0;
160                 ep->sel1=plelen(p);
161                 plepaste(p, 0, 0);      /* cut */
162                 break;
163         case Kbs:       /* ^H: erase character */
164                 if(ep->sel0!=0) --ep->sel0;
165                 twreplace(t, ep->sel0, ep->sel1, 0, 0);
166                 break;
167         case Knack:     /* ^U: erase line */
168                 while(ep->sel0!=0 && t->text[ep->sel0-1]!='\n') --ep->sel0;
169                 twreplace(t, ep->sel0, ep->sel1, 0, 0);
170                 break;
171         case Ketb:      /* ^W: erase word */
172                 while(ep->sel0!=0 && !pl_idchar(t->text[ep->sel0-1])) --ep->sel0;
173                 while(ep->sel0!=0 && pl_idchar(t->text[ep->sel0-1])) --ep->sel0;
174                 twreplace(t, ep->sel0, ep->sel1, 0, 0);
175                 break;
176         default:
177                 if((c & 0xFF00) == KF || (c & 0xFF00) == Spec)
178                         break;
179                 twreplace(t, ep->sel0, ep->sel1, &c, 1);
180                 ++ep->sel0;
181                 break;
182         }
183         ep->sel1=ep->sel0;
184         /*
185          * Scroll up until ep->sel0 is above t->bot.
186          */
187         scrolled=0;
188         do{
189                 bot=t->bot;
190                 if(ep->sel0<=bot) break;
191                 twscroll(t, twpt2rune(t, Pt(t->r.min.x, t->r.min.y+font->height)));
192                 scrolled++;
193         }while(bot!=t->bot);
194         if(scrolled){
195                 sb=p->yscroller;
196                 if(sb && sb->setscrollbar)
197                         sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text);
198         }
199         twhilite(t, ep->sel0, ep->sel1, 1);
200 }
201 Point pl_getsizeedit(Panel *p, Point children){
202         USED(children);
203         return pl_boxsize(((Edit *)p->data)->minsize, p->state);
204 }
205 void pl_childspaceedit(Panel *g, Point *ul, Point *size){
206         USED(g, ul, size);
207 }
208 void pl_freeedit(Panel *p){
209         Edit *ep;
210         ep=p->data;
211         if(ep->t!=nil) twfree(ep->t);
212         ep->t=0;
213 }
214 void plinitedit(Panel *v, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){
215         Edit *ep;
216         ep=v->data;
217         v->flags=flags|LEAF;
218         v->state=UP;
219         v->draw=pl_drawedit;
220         v->hit=pl_hitedit;
221         v->type=pl_typeedit;
222         v->getsize=pl_getsizeedit;
223         v->childspace=pl_childspaceedit;
224         v->free=pl_freeedit;
225         v->snarf=pl_snarfedit;
226         v->paste=pl_pasteedit;
227         v->kind="edit";
228         ep->hit=hit;
229         ep->minsize=minsize;
230         ep->text=text;
231         ep->ntext=ntext;
232         if(ep->t!=0) twfree(ep->t);
233         ep->t=0;
234         ep->sel0=-1;
235         ep->sel1=-1;
236         v->scroll=pl_scrolledit;
237         v->scr.pos=Pt(0,0);
238         v->scr.size=Pt(ntext,0);
239 }
240 Panel *pledit(Panel *parent, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){
241         Panel *v;
242         v=pl_newpanel(parent, sizeof(Edit));
243         ((Edit *)v->data)->t=0;
244         plinitedit(v, flags, minsize, text, ntext, hit);
245         return v;
246 }
247 void plescroll(Panel *p, int top){
248         twscroll(((Edit *)p->data)->t, top);
249 }
250 void plegetsel(Panel *p, int *sel0, int *sel1){
251         Edit *ep;
252         ep=p->data;
253         *sel0=ep->sel0;
254         *sel1=ep->sel1;
255 }
256 int plelen(Panel *p){
257         Textwin *t;
258         t=((Edit *)p->data)->t;
259         if(t==0) return 0;
260         return t->etext-t->text;
261 }
262 Rune *pleget(Panel *p){
263         return ((Edit *)p->data)->t->text;
264 }
265 void plesel(Panel *p, int sel0, int sel1){
266         Edit *ep;
267         ep=p->data;
268         ep->t->b=p->b;
269         twhilite(ep->t, ep->sel0, ep->sel1, 0);
270         ep->sel0=sel0;
271         ep->sel1=sel1;
272         twhilite(ep->t, ep->sel0, ep->sel1, 1);
273 }
274 void plepaste(Panel *p, Rune *text, int ntext){
275         Edit *ep;
276         ep=p->data;
277         ep->t->b=p->b;
278         twhilite(ep->t, ep->sel0, ep->sel1, 0);
279         twreplace(ep->t, ep->sel0, ep->sel1, text, ntext);
280         ep->sel1=ep->sel0+ntext;
281         twhilite(ep->t, ep->sel0, ep->sel1, 1);
282         p->scr.size.y=ep->t->etext-ep->t->text;
283         p->scr.pos.y=ep->t->top;
284 }
285 void plemove(Panel *p, Point d){
286         Edit *ep;
287         ep=p->data;
288         if(ep->t && !eqpt(d, Pt(0,0))) twmove(ep->t, d);
289 }