1 /* Copyright (C) 2002-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "chardata.h"
31 #include "fontforgeui.h"
32 #include "gkeysym.h"
33 #include "langfreq.h"
34 #include "sftextfieldP.h"
35 #include "splinefill.h"
36 #include "splineutil.h"
37 #include "tottfgpos.h"
38 #include "ustring.h"
39 #include "utype.h"
40 
41 #include <math.h>
42 
43 static GBox sftextarea_box = GBOX_EMPTY; /* Don't initialize here */
44 static int sftextarea_inited = false;
45 static FontInstance *sftextarea_font;
46 
47 static unichar_t nullstr[] = { 0 },
48 	newlinestr[] = { '\n', 0 }, tabstr[] = { '\t', 0 };
49 
50 static int SFTextArea_Show(SFTextArea *st, int pos);
51 static void GTPositionGIC(SFTextArea *st);
52 
SFTextAreaRefigureLines(SFTextArea * st,int start_of_change,int end_of_change)53 static void SFTextAreaRefigureLines(SFTextArea *st, int start_of_change,
54 	int end_of_change) {
55 
56     if ( st->vsb!=NULL && st->li.lines==NULL )
57 	GScrollBarSetBounds(&st->vsb->g,0,0,st->g.inner.height);
58 
59     LayoutInfoRefigureLines(&st->li,start_of_change,end_of_change,
60 	    st->g.inner.width);
61 
62     if ( st->hsb!=NULL )
63 	GScrollBarSetBounds(&st->hsb->g,0,st->li.xmax,st->g.inner.width);
64     if ( st->vsb!=NULL && st->li.lcnt>0 )
65 	GScrollBarSetBounds(&st->vsb->g,0,st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh,st->g.inner.height);
66 }
67 
SFTextAreaChanged(SFTextArea * st,int src)68 static void SFTextAreaChanged(SFTextArea *st,int src) {
69     GEvent e;
70 
71     e.type = et_controlevent;
72     e.w = st->g.base;
73     e.u.control.subtype = et_textchanged;
74     e.u.control.g = &st->g;
75     e.u.control.u.tf_changed.from_pulldown = src;
76     if ( st->g.handle_controlevent != NULL )
77 	(st->g.handle_controlevent)(&st->g,&e);
78     else
79 	GDrawPostEvent(&e);
80 }
81 
SFTextAreaFocusChanged(SFTextArea * st,int gained)82 static void SFTextAreaFocusChanged(SFTextArea *st,int gained) {
83     GEvent e;
84 
85     e.type = et_controlevent;
86     e.w = st->g.base;
87     e.u.control.subtype = et_textfocuschanged;
88     e.u.control.g = &st->g;
89     e.u.control.u.tf_focus.gained_focus = gained;
90     if ( st->g.handle_controlevent != NULL )
91 	(st->g.handle_controlevent)(&st->g,&e);
92     else
93 	GDrawPostEvent(&e);
94 }
95 
_SFTextAreaReplace(SFTextArea * st,const unichar_t * str)96 static void _SFTextAreaReplace(SFTextArea *st, const unichar_t *str) {
97 
98     st->sel_oldstart = st->sel_start;
99     st->sel_oldend = st->sel_end;
100     st->sel_oldbase = st->sel_base;
101     st->sel_start += LayoutInfoReplace(&st->li,str,st->sel_start,st->sel_end,
102 	    st->g.inner.width);
103     st->sel_end = st->sel_start;
104 
105     if ( st->hsb!=NULL )
106 	GScrollBarSetBounds(&st->hsb->g,0,st->li.xmax,st->g.inner.width);
107     if ( st->vsb!=NULL && st->li.lcnt>0 )
108 	GScrollBarSetBounds(&st->vsb->g,0,st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh,st->g.inner.height);
109 }
110 
SFTextArea_Replace(SFTextArea * st,const unichar_t * str)111 static void SFTextArea_Replace(SFTextArea *st, const unichar_t *str) {
112     _SFTextAreaReplace(st,str);
113     SFTextArea_Show(st,st->sel_start);
114 }
115 
SFTextAreaFindLine(SFTextArea * st,int pos)116 static int SFTextAreaFindLine(SFTextArea *st, int pos) {
117     int i;
118 
119     for ( i=0; i+1<st->li.lcnt; ++i )
120 	if ( pos<st->li.lineheights[i+1].start_pos )
121     break;
122 
123 return( i );
124 }
125 
PSTComponentCount(PST * pst)126 static int PSTComponentCount(PST *pst) {
127     int cnt=0;
128     char *pt = pst->u.lig.components;
129 
130     for (;;) {
131 	while ( *pt==' ' ) ++pt;
132 	if ( *pt=='\0' )
133 return( cnt );
134 	while ( *pt!=' ' && *pt!='\0' ) ++pt;
135 	++cnt;
136     }
137 }
138 
PSTLigComponentCount(SplineChar * sc)139 static int PSTLigComponentCount(SplineChar *sc) {
140     PST *pst;
141     int lcnt, ltemp;
142 
143     lcnt = 0;
144     for ( pst=sc->possub; pst!=NULL ; pst=pst->next ) {
145 	/* Find out the number of components. Note that ffi might be f+f+i or ff+i */
146 	/*  so find the max */
147 	if ( pst->type==pst_ligature ) {
148 	    ltemp = PSTComponentCount(pst);
149 	    if ( ltemp>lcnt )
150 		lcnt = ltemp;
151 	}
152     }
153 return( lcnt );
154 }
155 
SFTextAreaGetOffsetFromXPos(SFTextArea * st,int i,int xpos)156 static int SFTextAreaGetOffsetFromXPos(SFTextArea *st,int i,int xpos) {
157     int p, x, xend, pos, j, l, r2l, lcnt;
158     struct opentype_str **line;
159     PST *pst;
160 
161     if ( i<0 )
162 return( 0 );
163     if ( i>=st->li.lcnt )
164 return( u_strlen(st->li.text));
165 
166     p = st->li.lineheights[i].p;
167     if ( st->li.paras[p].para[0]!=NULL &&
168 	    ScriptIsRightToLeft( ((struct fontlist *) (st->li.paras[p].para[0]->fl))->script )) {
169 	x = st->li.xmax - st->li.lineheights[i].linelen;
170 	r2l = true;
171     } else {
172 	x = 0;
173 	r2l = false;
174     }
175 
176     line = st->li.lines[i];
177     if ( line[0]==NULL ) {
178 	pos = st->li.lineheights[i].start_pos;
179     } else {
180 	for ( j=0; line[j]!=NULL; ++j ) {
181 	    xend = x + line[j]->advance_width + line[j]->vr.h_adv_off;
182 	    if ( xpos<xend ) {
183 		double scale;
184 		xpos -= x; xend -= x;
185 		/* Check for ligature carets */
186 		for ( pst=line[j]->sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
187 		if ( pst!=NULL && pst->u.lcaret.cnt==0 )
188 		    pst = NULL;
189 		lcnt = 0;
190 		if ( pst==NULL ) {
191 		    lcnt = PSTLigComponentCount(line[j]->sc);
192 		    if ( lcnt<=1 ) {
193 			if ( xpos>xend/2 )
194 			    ++j;
195 		    } else {
196 			if ( line[j+1]!=NULL && xpos>(2*lcnt-1)*xend/lcnt ) {
197 			    ++j;
198 			    lcnt = 0;
199 			}
200 		    }
201 		} else {
202 		    FontData *fd = ((struct fontlist *) (line[j]->fl))->fd;
203 		    scale = fd->pointsize*st->li.dpi / (72.0*(fd->sf->ascent+fd->sf->descent));
204 		    if ( xpos-pst->u.lcaret.carets[ pst->u.lcaret.cnt-1 ]*scale > xend-xpos ) {
205 			++j;
206 			pst = NULL;
207 		    }
208 		}
209 		if ( line[j]==NULL ) {
210 		    pos = line[j-1]->orig_index + ((struct fontlist *) (line[j-1]->fl))->start +1;
211 		} else
212 		    pos = line[j]->orig_index + ((struct fontlist *) (line[j]->fl))->start;
213 		if ( pst!=NULL ) {
214 		    for ( l=0; l<pst->u.lcaret.cnt; ++l )
215 			if (( l==0 && xpos < scale*pst->u.lcaret.carets[0]-xpos ) ||
216 				(l!=0 && xpos-scale*pst->u.lcaret.carets[l-1] < scale*pst->u.lcaret.carets[0]-xpos ))
217 		    break;
218 		    if ( r2l )
219 			l = pst->u.lcaret.cnt-l;
220 		    pos += l;
221 		} else if ( lcnt>=2 ) {
222 		    /* Ok it's a ligature with lcnt components. Assume each has the */
223 		    /*  length */
224 		    l = (xpos+xend/(2*lcnt))/(xend/lcnt);
225 		    if ( r2l )
226 			l = lcnt-1-l;
227 		    pos += l;
228 		}
229 	break;
230 	    }
231 	    x = xend;
232 	}
233 	if ( line[j]==NULL )
234 	    pos = line[j-1]->orig_index + ((struct fontlist *) (line[j-1]->fl))->start +1;
235     }
236 return( pos );
237 }
238 
SFTextAreaGetXPosFromOffset(SFTextArea * st,int l,int pos)239 static int SFTextAreaGetXPosFromOffset(SFTextArea *st,int l,int pos) {
240     int j, scpos, lcnt, x;
241     struct opentype_str **line;
242     PST *pst;
243 
244     if ( l<0 || l>= st->li.lcnt )
245 return( 0 );
246     if ( st->li.lines[0]==NULL || pos < st->li.lineheights[l].start_pos )
247 return( 0 );
248 
249     line = st->li.lines[l];
250     x = 0;
251     for ( j=0; line[j]!=NULL; ++j ) {
252 	scpos = line[j]->orig_index + ((struct fontlist *) (line[j]->fl))->start;
253 	if ( scpos==pos )
254 return( x );
255 	for ( pst=line[j]->sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
256 	if ( pst!=NULL && pst->u.lcaret.cnt==0 )
257 	    pst = NULL;
258 	if ( pst!=NULL && pos>scpos && pos<=scpos+pst->u.lcaret.cnt ) {
259 	    FontData *fd = ((struct fontlist *) (line[j]->fl))->fd;
260 	    double scale = fd->pointsize*st->li.dpi / (72.0*(fd->sf->ascent+fd->sf->descent));
261 return( x + rint(scale*pst->u.lcaret.carets[pos-scpos-1]) );
262 	}
263 	x += line[j]->advance_width + line[j]->vr.h_adv_off;
264     }
265     /* Ok, maybe they didn't specify lig carets. Check if we are within a ligature */
266     x=0;
267     for ( j=0; line[j]!=NULL; ++j ) {
268 	scpos = line[j]->orig_index + ((struct fontlist *) (line[j]->fl))->start;
269 	if ( scpos==pos )
270 return( x );
271 	lcnt = PSTLigComponentCount(line[j]->sc);
272 	if ( pos>scpos && pos<scpos+lcnt ) {
273 	    int wid = line[j]->advance_width + line[j]->vr.h_adv_off;
274 return( x + (pos-scpos)*wid/lcnt );
275 	}
276 	x += line[j]->advance_width + line[j]->vr.h_adv_off;
277     }
278 return( x );
279 }
280 
SFTextArea_EndPage(SFTextArea * st)281 static int SFTextArea_EndPage(SFTextArea *st) {
282     int endpage;
283 
284     for ( endpage=1; st->li.lcnt-endpage>=0 &&
285 	    st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh
286 		-st->li.lineheights[st->li.lcnt-endpage].y <= st->g.inner.height;
287 	    ++endpage );
288     if ( (endpage-=2) < 1 ) endpage = 1;
289 return( endpage );
290 }
291 
SFTextArea_Show(SFTextArea * st,int pos)292 static int SFTextArea_Show(SFTextArea *st, int pos) {
293     int i, xoff, loff, x, xlen;
294     int refresh=false, endpage, page;
295 
296     if ( pos < 0 ) pos = 0;
297     if ( pos > u_strlen(st->li.text)) pos = u_strlen(st->li.text);
298     i = SFTextAreaFindLine(st,pos);
299 
300     loff = st->loff_top;
301     for ( page=1; st->loff_top+page<st->li.lcnt && st->li.lineheights[st->loff_top+page].y-st->li.lineheights[st->loff_top].y<=st->g.inner.height;
302 	    ++page );
303     if ( --page < 1 ) page = 1;
304     /* a page starting at loff_top may have a different number of lines than */
305     /*  a page ending at lcnt */
306     endpage = SFTextArea_EndPage(st);
307     if ( i<loff || i>=st->loff_top+page)
308 	loff = i-page/4;
309     if ( loff > st->li.lcnt-endpage )
310 	loff = st->li.lcnt-endpage;
311     if ( loff<0 ) loff = 0;
312     if ( st->li.lcnt==0 || st->li.lineheights[st->li.lcnt-1].y<st->g.inner.height )
313 	loff = 0;
314 
315     xoff = st->xoff_left;
316     x = 0;
317     if ( i<st->li.lcnt ) {
318 	x = SFTextAreaGetXPosFromOffset(st,i,pos);
319 	xlen = st->li.lineheights[i].linelen;
320 	if ( xlen< st->g.inner.width )
321 	    xoff = 0;
322 	else if ( x<xoff+4 || x>=xoff+st->g.inner.width-4 ) {
323 	    xoff = x - xlen/4;
324 	    if ( xoff<0 ) xoff = 0;
325 	}
326     }
327 
328     if ( xoff!=st->xoff_left ) {
329 	st->xoff_left = xoff;
330 	if ( st->hsb!=NULL )
331 	    GScrollBarSetPos(&st->hsb->g,xoff);
332 	refresh = true;
333     }
334     if ( loff!=st->loff_top ) {
335 	st->loff_top = loff;
336 	if ( st->vsb!=NULL )
337 	    GScrollBarSetPos(&st->vsb->g,st->li.lineheights[loff].y);
338 	refresh = true;
339     }
340     GTPositionGIC(st);
341 return( refresh );
342 }
343 
genunicodedata(void * _gt,int32 * len)344 static void *genunicodedata(void *_gt,int32 *len) {
345     SFTextArea *st = _gt;
346     unichar_t *temp;
347     *len = st->sel_end-st->sel_start + 1;
348     temp = malloc((*len+2)*sizeof(unichar_t));
349     temp[0] = 0xfeff;		/* KDE expects a byte order flag */
350     u_strncpy(temp+1,st->li.text+st->sel_start,st->sel_end-st->sel_start);
351     temp[*len+1] = 0;
352 return( temp );
353 }
354 
genutf8data(void * _gt,int32 * len)355 static void *genutf8data(void *_gt,int32 *len) {
356     SFTextArea *st = _gt;
357     unichar_t *temp =u_copyn(st->li.text+st->sel_start,st->sel_end-st->sel_start);
358     char *ret = u2utf8_copy(temp);
359     free(temp);
360     *len = strlen(ret);
361 return( ret );
362 }
363 
ddgenunicodedata(void * _gt,int32 * len)364 static void *ddgenunicodedata(void *_gt,int32 *len) {
365     void *temp = genunicodedata(_gt,len);
366     SFTextArea *st = _gt;
367     _SFTextAreaReplace(st,nullstr);
368     _ggadget_redraw(&st->g);
369 return( temp );
370 }
371 
genlocaldata(void * _gt,int32 * len)372 static void *genlocaldata(void *_gt,int32 *len) {
373     SFTextArea *st = _gt;
374     unichar_t *temp =u_copyn(st->li.text+st->sel_start,st->sel_end-st->sel_start);
375     char *ret = u2def_copy(temp);
376     free(temp);
377     *len = strlen(ret);
378 return( ret );
379 }
380 
ddgenlocaldata(void * _gt,int32 * len)381 static void *ddgenlocaldata(void *_gt,int32 *len) {
382     void *temp = genlocaldata(_gt,len);
383     SFTextArea *st = _gt;
384     _SFTextAreaReplace(st,nullstr);
385     _ggadget_redraw(&st->g);
386 return( temp );
387 }
388 
noop(void * _st)389 static void noop(void *_st) {
390 }
391 
SFTextAreaGrabPrimarySelection(SFTextArea * st)392 static void SFTextAreaGrabPrimarySelection(SFTextArea *st) {
393     int ss = st->sel_start, se = st->sel_end;
394 
395     GDrawGrabSelection(st->g.base,sn_primary);
396     st->sel_start = ss; st->sel_end = se;
397     GDrawAddSelectionType(st->g.base,sn_primary,"text/plain;charset=ISO-10646-UCS-4",st,st->sel_end-st->sel_start,
398 	    sizeof(unichar_t),
399 	    genunicodedata,noop);
400     GDrawAddSelectionType(st->g.base,sn_primary,"UTF8_STRING",st,3*(st->sel_end-st->sel_start),
401 	    sizeof(unichar_t),
402 	    genutf8data,noop);
403     GDrawAddSelectionType(st->g.base,sn_primary,"STRING",st,st->sel_end-st->sel_start,sizeof(char),
404 	    genlocaldata,noop);
405 }
406 
SFTextAreaGrabDDSelection(SFTextArea * st)407 static void SFTextAreaGrabDDSelection(SFTextArea *st) {
408 
409     GDrawGrabSelection(st->g.base,sn_drag_and_drop);
410     GDrawAddSelectionType(st->g.base,sn_drag_and_drop,"text/plain;charset=ISO-10646-UCS-4",st,st->sel_end-st->sel_start,
411 	    sizeof(unichar_t),
412 	    ddgenunicodedata,noop);
413     GDrawAddSelectionType(st->g.base,sn_drag_and_drop,"STRING",st,st->sel_end-st->sel_start,sizeof(char),
414 	    ddgenlocaldata,noop);
415 }
416 
SFTextAreaGrabSelection(SFTextArea * st,enum selnames sel)417 static void SFTextAreaGrabSelection(SFTextArea *st, enum selnames sel ) {
418 
419     if ( st->sel_start!=st->sel_end ) {
420 	unichar_t *temp;
421 	char *ctemp;
422 	int i;
423 	uint16 *u2temp;
424 
425 	GDrawGrabSelection(st->g.base,sel);
426 	temp = malloc((st->sel_end-st->sel_start + 2)*sizeof(unichar_t));
427 	temp[0] = 0xfeff;		/* KDE expects a byte order flag */
428 	u_strncpy(temp+1,st->li.text+st->sel_start,st->sel_end-st->sel_start);
429 	ctemp = u2utf8_copy(temp);
430 	GDrawAddSelectionType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-4",temp,u_strlen(temp),
431 		sizeof(unichar_t),
432 		NULL,NULL);
433 	u2temp = malloc((st->sel_end-st->sel_start + 2)*sizeof(uint16));
434 	for ( i=0; temp[i]!=0; ++i )
435 	    u2temp[i] = temp[i];
436 	u2temp[i] = 0;
437 	GDrawAddSelectionType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-2",u2temp,u_strlen(temp),
438 		2,
439 		NULL,NULL);
440 	GDrawAddSelectionType(st->g.base,sel,"UTF8_STRING",ctemp,strlen(ctemp),sizeof(char),
441 		NULL,NULL);
442 	GDrawAddSelectionType(st->g.base,sel,"STRING",u2def_copy(temp),u_strlen(temp),sizeof(char),
443 		NULL,NULL);
444     }
445 }
446 
SFTextAreaSelBackword(unichar_t * text,int start)447 static int SFTextAreaSelBackword(unichar_t *text,int start) {
448     unichar_t ch;
449 
450     if ( start==0 )
451 return( 0 ); /* Can't go back */;
452 
453     ch = text[start-1];
454     if ( isalnum(ch) || ch=='_' ) {
455 	int i;
456 	for ( i=start-1; i>=0 && (isalnum(text[i]) || text[i]=='_') ; --i );
457 	start = i+1;
458     } else {
459 	int i;
460 	for ( i=start-1; i>=0 && !isalnum(text[i]) && text[i]!='_' ; --i );
461 	start = i+1;
462     }
463 return( start );
464 }
465 
SFTextAreaSelForeword(unichar_t * text,int end)466 static int SFTextAreaSelForeword(unichar_t *text,int end) {
467     unichar_t ch = text[end];
468 
469     if ( ch=='\0' )
470 	/* Nothing */;
471     else if ( isalnum(ch) || ch=='_' ) {
472 	int i;
473 	for ( i=end; isalnum(text[i]) || text[i]=='_' ; ++i );
474 	end = i;
475     } else {
476 	int i;
477 	for ( i=end; !isalnum(text[i]) && text[i]!='_' && text[i]!='\0' ; ++i );
478 	end = i;
479     }
480 return( end );
481 }
482 
SFTextAreaSelectWord(SFTextArea * st,int mid,int16 * start,int16 * end)483 static void SFTextAreaSelectWord(SFTextArea *st,int mid, int16 *start, int16 *end) {
484     unichar_t *text = st->li.text;
485     unichar_t ch = text[mid];
486 
487     if ( ch=='\0' )
488 	*start = *end = mid;
489     else if ( isspace(ch) ) {
490 	int i;
491 	for ( i=mid; isspace(text[i]); ++i );
492 	*end = i;
493 	for ( i=mid-1; i>=0 && isspace(text[i]) ; --i );
494 	*start = i+1;
495     } else if ( isalnum(ch) || ch=='_' ) {
496 	int i;
497 	for ( i=mid; isalnum(text[i]) || text[i]=='_' ; ++i );
498 	*end = i;
499 	for ( i=mid-1; i>=0 && (isalnum(text[i]) || text[i]=='_') ; --i );
500 	*start = i+1;
501     } else {
502 	int i;
503 	for ( i=mid; !isalnum(text[i]) && text[i]!='_' && text[i]!='\0' ; ++i );
504 	*end = i;
505 	for ( i=mid-1; i>=0 && !isalnum(text[i]) && text[i]!='_' ; --i );
506 	*start = i+1;
507     }
508 }
509 
SFTextAreaSelectWords(SFTextArea * st,int last)510 static void SFTextAreaSelectWords(SFTextArea *st,int last) {
511     int16 ss, se;
512     SFTextAreaSelectWord(st,st->sel_base,&st->sel_start,&st->sel_end);
513     if ( last!=st->sel_base ) {
514 	SFTextAreaSelectWord(st,last,&ss,&se);
515 	if ( ss<st->sel_start ) st->sel_start = ss;
516 	if ( se>st->sel_end ) st->sel_end = se;
517     }
518 }
519 
SFTextAreaPaste(SFTextArea * st,enum selnames sel)520 static void SFTextAreaPaste(SFTextArea *st,enum selnames sel) {
521     if ( GDrawSelectionHasType(st->g.base,sel,"UTF8_STRING") ||
522 	    GDrawSelectionHasType(st->g.base,sel,"text/plain;charset=UTF-8")) {
523 	unichar_t *temp; char *ctemp;
524 	int32 len;
525 	if ( GDrawSelectionHasType(st->g.base,sel,"UTF8_STRING") )
526 	    ctemp = GDrawRequestSelection(st->g.base,sel,"UTF8_STRING",&len);
527 	else
528 	    ctemp = GDrawRequestSelection(st->g.base,sel,"text/plain;charset=UTF-8",&len);
529 	if ( ctemp!=NULL ) {
530 	    temp = utf82u_copyn(ctemp,strlen(ctemp));
531 	    SFTextArea_Replace(st,temp);
532 	    free(ctemp); free(temp);
533 	}
534     } else if ( GDrawSelectionHasType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-4")) {
535 	unichar_t *temp;
536 	int32 len;
537 	temp = GDrawRequestSelection(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-4",&len);
538 	/* Bug! I don't handle byte reversed selections. But I don't think there should be any anyway... */
539 	if ( temp!=NULL )
540 	    SFTextArea_Replace(st,temp[0]==0xfeff?temp+1:temp);
541 	free(temp);
542     } else if ( GDrawSelectionHasType(st->g.base,sel,"Unicode") ||
543 	    GDrawSelectionHasType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-2")) {
544 	unichar_t *temp;
545 	uint16 *temp2;
546 	int32 len;
547 	temp2 = GDrawRequestSelection(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-2",&len);
548 	if ( temp2==NULL || len==0 )
549 	    temp2 = GDrawRequestSelection(st->g.base,sel,"Unicode",&len);
550 	if ( temp2!=NULL ) {
551 	    int i;
552 	    temp = malloc((len/2+1)*sizeof(unichar_t));
553 	    for ( i=0; temp2[i]!=0; ++i )
554 		temp[i] = temp2[i];
555 	    temp[i] = 0;
556 	    SFTextArea_Replace(st,temp[0]==0xfeff?temp+1:temp);
557 	    free(temp);
558 	}
559 	free(temp2);
560     } else if ( GDrawSelectionHasType(st->g.base,sel,"STRING")) {
561 	unichar_t *temp; char *ctemp;
562 	int32 len;
563 	ctemp = GDrawRequestSelection(st->g.base,sel,"STRING",&len);
564 	if ( ctemp!=NULL ) {
565 	    temp = def2u_copy(ctemp);
566 	    SFTextArea_Replace(st,temp);
567 	    free(ctemp); free(temp);
568 	}
569     }
570 }
571 
sftextarea_editcmd(GGadget * g,enum editor_commands cmd)572 static int sftextarea_editcmd(GGadget *g,enum editor_commands cmd) {
573     SFTextArea *st = (SFTextArea *) g;
574     int i;
575 
576     switch ( cmd ) {
577       case ec_selectall:
578 	st->sel_start = 0;
579 	st->sel_end = u_strlen(st->li.text);
580 return( true );
581       case ec_clear:
582 	SFTextArea_Replace(st,nullstr);
583 return( true );
584       case ec_cut:
585 	SFTextAreaGrabSelection(st,sn_clipboard);
586 	SFTextArea_Replace(st,nullstr);
587 return( true );
588       case ec_copy:
589 	SFTextAreaGrabSelection(st,sn_clipboard);
590 return( true );
591       case ec_paste:
592 	SFTextAreaPaste(st,sn_clipboard);
593 	SFTextArea_Show(st,st->sel_start);
594 return( true );
595       case ec_undo:
596 	if ( st->li.oldtext!=NULL ) {
597 	    unichar_t *temp = st->li.text;
598 	    struct fontlist *ofl = st->li.fontlist;
599 	    int16 s;
600 	    st->li.text = st->li.oldtext; st->li.oldtext = temp;
601 	    st->li.fontlist = st->li.oldfontlist; st->li.oldfontlist = ofl;
602 	    s = st->sel_start; st->sel_start = st->sel_oldstart; st->sel_oldstart = s;
603 	    s = st->sel_end; st->sel_end = st->sel_oldend; st->sel_oldend = s;
604 	    s = st->sel_base; st->sel_base = st->sel_oldbase; st->sel_oldbase = s;
605 	    for ( i=0; i<st->li.pcnt; ++i )
606 		free( st->li.paras[i].para);
607 	    free(st->li.paras); st->li.paras = NULL; st->li.pcnt = 0; st->li.pmax = 0;
608 	    for ( i=0; i<st->li.lcnt; ++i )
609 		free( st->li.lines[i]);
610 	    free( st->li.lines );
611 	    free( st->li.lineheights );
612 	    st->li.lines = NULL; st->li.lineheights = NULL; st->li.lcnt = 0;
613 	    SFTextAreaRefigureLines(st, 0, -1);
614 	    SFTextArea_Show(st,st->sel_end);
615 	}
616 return( true );
617       case ec_redo:		/* Hmm. not sure */ /* we don't do anything */
618 return( true );			/* but probably best to return success */
619       case ec_backword:
620         if ( st->sel_start==st->sel_end && st->sel_start!=0 ) {
621 	    st->sel_start = SFTextAreaSelBackword(st->li.text,st->sel_start);
622 	}
623 	SFTextArea_Replace(st,nullstr);
624 return( true );
625       case ec_deleteword:
626         if ( st->sel_start==st->sel_end && st->sel_start!=0 )
627 	    SFTextAreaSelectWord(st,st->sel_start,&st->sel_start,&st->sel_end);
628 	SFTextArea_Replace(st,nullstr);
629 return( true );
630     }
631 return( false );
632 }
633 
_sftextarea_editcmd(GGadget * g,enum editor_commands cmd)634 static int _sftextarea_editcmd(GGadget *g,enum editor_commands cmd) {
635     if ( sftextarea_editcmd(g,cmd)) {
636 	_ggadget_redraw(g);
637 	GTPositionGIC((SFTextArea *) g);
638 return( true );
639     }
640 return( false );
641 }
642 
GTBackPos(SFTextArea * st,int pos,int ismeta)643 static int GTBackPos(SFTextArea *st,int pos, int ismeta) {
644     int newpos/*, xloc, l*/;
645 
646     if ( ismeta )
647 	newpos = SFTextAreaSelBackword(st->li.text,pos);
648     else
649 	newpos = pos-1;
650     if ( newpos==-1 ) newpos = 0;
651 return( newpos );
652 }
653 
GTForePos(SFTextArea * st,int pos,int ismeta)654 static int GTForePos(SFTextArea *st,int pos, int ismeta) {
655     int newpos=pos/*, xloc, l*/;
656 
657     if ( ismeta )
658 	newpos = SFTextAreaSelForeword(st->li.text,pos);
659     else {
660 	if ( st->li.text[pos]!=0 )
661 	    newpos = pos+1;
662     }
663 return( newpos );
664 }
665 
SFTextAreaImport(SFTextArea * st)666 static void SFTextAreaImport(SFTextArea *st) {
667     char *cret = gwwv_open_filename(_("Open"),NULL,
668 	    "*.txt",NULL);
669     unichar_t *str;
670 
671     if ( cret==NULL )
672 return;
673     str = _GGadgetFileToUString(cret,65536);
674     if ( str==NULL ) {
675 	ff_post_error(_("Could not open"),_("Could not open %.100s"),cret);
676 	free(cret);
677 return;
678     }
679     free(cret);
680     SFTextArea_Replace(st,str);
681     free(str);
682 }
683 
SFTextAreaInsertRandom(SFTextArea * st)684 static void SFTextAreaInsertRandom(SFTextArea *st) {
685     LayoutInfo *li = &st->li;
686     struct fontlist *fl, *prev;
687     char **scriptlangs;
688     int i,cnt;
689     uint32 script, lang;
690     char *utf8_str;
691     unichar_t *str;
692     int start, pos;
693     struct lang_frequencies **freq;
694 
695     for ( fl=li->fontlist, prev = NULL; fl!=NULL && fl->start<=st->sel_start ; prev=fl, fl=fl->next );
696     if ( prev==NULL )
697 return;
698     scriptlangs = SFScriptLangs(prev->fd->sf,&freq);
699     if ( scriptlangs==NULL || scriptlangs[0]==NULL ) {
700 	ff_post_error(_("No letters in font"), _("No letters in font"));
701 	free(scriptlangs);
702 	free(freq);
703 return;
704     }
705     for ( cnt=0; scriptlangs[cnt]!=NULL; ++cnt );
706     i = ff_choose(_("Text from script"),(const char **) scriptlangs,cnt,0,_("Insert random text in the specified script"));
707     if ( i==-1 )
708 return;
709     pos = strlen(scriptlangs[i])-10;
710     script = (scriptlangs[i][pos+0]<<24) |
711 	     (scriptlangs[i][pos+1]<<16) |
712 	     (scriptlangs[i][pos+2]<<8 ) |
713 	     (scriptlangs[i][pos+3]    );
714     lang = (scriptlangs[i][pos+5]<<24) |
715 	   (scriptlangs[i][pos+6]<<16) |
716 	   (scriptlangs[i][pos+7]<<8 ) |
717 	   (scriptlangs[i][pos+8]    );
718 
719     utf8_str = RandomParaFromScriptLang(script,lang,prev->fd->sf,freq[i]);
720     str = utf82u_copy(utf8_str);
721 
722     start = st->sel_start;
723     SFTextArea_Replace(st,str);
724     SFTFSetScriptLang(&st->g,start,start+u_strlen(str),script,lang);
725 
726     free(str);
727     free(utf8_str);
728     for ( i=0; scriptlangs[i]!=NULL; ++i )
729 	free(scriptlangs[i]);
730     free(scriptlangs);
731     free(freq);
732 }
733 
SFTextAreaSave(SFTextArea * st)734 static void SFTextAreaSave(SFTextArea *st) {
735     char *cret = gwwv_save_filename(_("Save"),NULL, "*.txt");
736     FILE *file;
737     unichar_t *pt;
738 
739     if ( cret==NULL )
740 return;
741     file = fopen(cret,"w");
742     if ( file==NULL ) {
743 	ff_post_error(_("Could not open"),_("Could not open %.100s"),cret);
744 	free(cret);
745 return;
746     }
747     free(cret);
748 
749 	putc(0xef,file);		/* Zero width something or other. Marks this as unicode, utf8 */
750 	putc(0xbb,file);
751 	putc(0xbf,file);
752 	for ( pt = st->li.text ; *pt; ++pt ) {
753 	    if ( *pt<0x80 )
754 		putc(*pt,file);
755 	    else if ( *pt<0x800 ) {
756 		putc(0xc0 | (*pt>>6), file);
757 		putc(0x80 | (*pt&0x3f), file);
758 	    } else if ( *pt>=0xd800 && *pt<0xdc00 && pt[1]>=0xdc00 && pt[1]<0xe000 ) {
759 		int u = ((*pt>>6)&0xf)+1, y = ((*pt&3)<<4) | ((pt[1]>>6)&0xf);
760 		putc( 0xf0 | (u>>2),file );
761 		putc( 0x80 | ((u&3)<<4) | ((*pt>>2)&0xf),file );
762 		putc( 0x80 | y,file );
763 		putc( 0x80 | (pt[1]&0x3f),file );
764 	    } else {
765 		putc( 0xe0 | (*pt>>12),file );
766 		putc( 0x80 | ((*pt>>6)&0x3f),file );
767 		putc( 0x80 | (*pt&0x3f),file );
768 	    }
769 	}
770     fclose(file);
771 }
772 
SFTextAreaSaveImage(SFTextArea * st)773 static void SFTextAreaSaveImage(SFTextArea *st) {
774     char *cret;
775     GImage *image;
776     struct _GImage *base;
777     char *basename;
778     int i,ret, p, x, j;
779     struct opentype_str **line;
780     int as;
781 
782     if ( st->li.lcnt==0 )
783 return;
784 
785     basename = NULL;
786     if ( st->li.fontlist!=NULL ) {
787 	basename = malloc(strlen(st->li.fontlist->fd->sf->fontname)+8);
788 	strcpy(basename, st->li.fontlist->fd->sf->fontname);
789 #ifdef _NO_LIBPNG
790 	strcat(basename,".bmp");
791 #else
792 	strcat(basename,".png");
793 #endif
794     }
795 #ifdef _NO_LIBPNG
796     cret = gwwv_save_filename(_("Save Image"),basename, "*.bmp");
797 #else
798     cret = gwwv_save_filename(_("Save Image"),basename, "*.{bmp,png}");
799 #endif
800     free(basename);
801     if ( cret==NULL )
802 return;
803 
804     image = GImageCreate(it_index,st->g.inner.width+2,
805 	    st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh+2);
806     base = image->u.image;
807     memset(base->data,0,base->bytes_per_line*base->height);
808     for ( i=0; i<256; ++i )
809 	base->clut->clut[i] = (255-i)*0x010101;
810     base->clut->is_grey = true;
811     base->clut->clut_len = 256;
812 
813     as = 0;
814     if ( st->li.lcnt!=0 )
815 	as = st->li.lineheights[0].as;
816 
817     for ( i=0; i<st->li.lcnt; ++i ) {
818 	/* Does this para start out r2l or l2r? */
819 	p = st->li.lineheights[i].p;
820 	if ( st->li.paras[p].para[0]!=NULL &&
821 		ScriptIsRightToLeft( ((struct fontlist *) (st->li.paras[p].para[0]->fl))->script ))
822 	    x = st->li.xmax - st->li.lineheights[i].linelen;
823 	else
824 	    x = 0;
825 	line = st->li.lines[i];
826 	for ( j=0; line[j]!=NULL; ++j ) {
827 	    LI_FDDrawChar(image,
828 		    (void (*)(void *,GImage *,GRect *,int, int)) GImageDrawImage,
829 		    (void (*)(void *,GRect *,Color)) GImageDrawRect,
830 		    line[j],x,st->li.lineheights[i].y+as,0x000000);
831 	    x += line[j]->advance_width + line[j]->vr.h_adv_off;
832 	}
833     }
834 #ifndef _NO_LIBPNG
835     if ( strstrmatch(cret,".png")!=NULL )
836 	ret = GImageWritePng(image,cret,false);
837     else
838 #endif
839     if ( strstrmatch(cret,".bmp")!=NULL )
840 	ret = GImageWriteBmp(image,cret);
841     else
842 	ff_post_error(_("Unsupported image format"),
843 #ifndef _NO_LIBPNG
844 		_("Unsupported image format must be bmp or png")
845 #else
846 		_("Unsupported image format must be bmp")
847 #endif
848 	    );
849     if ( !ret )
850 	ff_post_error(_("Could not write"),_("Could not write %.100s"),cret);
851     free( cret );
852     GImageDestroy(image);
853 }
854 
855 #define MID_Cut		1
856 #define MID_Copy	2
857 #define MID_Paste	3
858 
859 #define MID_SelectAll	4
860 
861 #define MID_Save	5
862 #define MID_Import	6
863 #define MID_Insert	7
864 
865 #define MID_Undo	8
866 
867 #define MID_SaveImage	9
868 
869 static SFTextArea *popup_kludge;
870 
SFTFPopupInvoked(GWindow v,GMenuItem * mi,GEvent * e)871 static void SFTFPopupInvoked(GWindow v, GMenuItem *mi,GEvent *e) {
872     SFTextArea *st;
873     if ( popup_kludge==NULL )
874 return;
875     st = popup_kludge;
876     popup_kludge = NULL;
877     switch ( mi->mid ) {
878       case MID_Undo:
879 	sftextarea_editcmd(&st->g,ec_undo);
880       break;
881       case MID_Cut:
882 	sftextarea_editcmd(&st->g,ec_cut);
883       break;
884       case MID_Copy:
885 	sftextarea_editcmd(&st->g,ec_copy);
886       break;
887       case MID_Paste:
888 	sftextarea_editcmd(&st->g,ec_paste);
889       break;
890       case MID_SelectAll:
891 	sftextarea_editcmd(&st->g,ec_selectall);
892       break;
893       case MID_Save:
894 	SFTextAreaSave(st);
895       break;
896       case MID_Import:
897 	SFTextAreaImport(st);
898       break;
899       case MID_Insert:
900 	SFTextAreaInsertRandom(st);
901       break;
902       case MID_SaveImage:
903 	SFTextAreaSaveImage(st);
904       break;
905     }
906 }
907 
908 static GMenuItem sftf_popuplist[] = {
909     { { (unichar_t *) N_("_Undo"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'U' }, 'Z', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Undo },
910     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
911     { { (unichar_t *) N_("Cu_t"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 't' }, 'X', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Cut },
912     { { (unichar_t *) N_("_Copy"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'C' }, 'C', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Copy },
913     { { (unichar_t *) N_("_Paste"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'P' }, 'V', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Paste },
914     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
915     { { (unichar_t *) N_("_Save As..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'S' }, 'S', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Save },
916     { { (unichar_t *) N_("_Import..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'I' }, 'I', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Import },
917     { { (unichar_t *) N_("_Insert Random Text..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'I' }, 'T', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Insert },
918     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
919     { { (unichar_t *) N_("Save As _Image..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'S' }, 'S', ksm_control|ksm_shift, NULL, NULL, SFTFPopupInvoked, MID_SaveImage },
920     GMENUITEM_EMPTY
921 };
922 
SFTFPopupMenu(SFTextArea * st,GEvent * event)923 void SFTFPopupMenu(SFTextArea *st, GEvent *event) {
924     int no_sel = st->sel_start==st->sel_end;
925     static int done = false;
926 
927     if ( !done ) {
928 	int i;
929 	for ( i=0; sftf_popuplist[i].ti.text!=NULL || sftf_popuplist[i].ti.line; ++i )
930 	    if ( sftf_popuplist[i].ti.text!=NULL )
931 		sftf_popuplist[i].ti.text = (unichar_t *) _( (char *) sftf_popuplist[i].ti.text);
932 	done = true;
933     }
934 
935     sftf_popuplist[0].ti.disabled = st->li.oldtext==NULL;	/* Undo */
936     sftf_popuplist[2].ti.disabled = no_sel;		/* Cut */
937     sftf_popuplist[3].ti.disabled = no_sel;		/* Copy */
938     sftf_popuplist[4].ti.disabled = !GDrawSelectionHasType(st->g.base,sn_clipboard,"text/plain;charset=ISO-10646-UCS-2") &&
939 	    !GDrawSelectionHasType(st->g.base,sn_clipboard,"UTF8_STRING") &&
940 	    !GDrawSelectionHasType(st->g.base,sn_clipboard,"STRING");
941     sftf_popuplist[9].ti.disabled = (st->li.lcnt<=0);
942     popup_kludge = st;
943     GMenuCreatePopupMenu(st->g.base,event, sftf_popuplist);
944 }
945 
SFTextAreaDoChange(SFTextArea * st,GEvent * event)946 static int SFTextAreaDoChange(SFTextArea *st, GEvent *event) {
947     int ss = st->sel_start, se = st->sel_end;
948     int pos, l, xpos;
949     unichar_t *upt;
950 
951     if ( ( event->u.chr.state&(ksm_control|ksm_meta)) ||
952 	    event->u.chr.chars[0]<' ' || event->u.chr.chars[0]==0x7f ) {
953 	switch ( event->u.chr.keysym ) {
954 	  case GK_BackSpace:
955 	    if ( st->sel_start==st->sel_end ) {
956 		if ( st->sel_start==0 )
957 return( 2 );
958 		--st->sel_start;
959 	    }
960 	    SFTextArea_Replace(st,nullstr);
961 return( true );
962 	  break;
963 	  case GK_Delete:
964 	    if ( st->sel_start==st->sel_end ) {
965 		if ( st->li.text[st->sel_start]==0 )
966 return( 2 );
967 		++st->sel_end;
968 	    }
969 	    SFTextArea_Replace(st,nullstr);
970 return( true );
971 	  break;
972 	  case GK_Left: case GK_KP_Left:
973 	    if ( st->sel_start==st->sel_end ) {
974 		st->sel_start = GTBackPos(st,st->sel_start,event->u.chr.state&ksm_meta);
975 		if ( !(event->u.chr.state&ksm_shift ))
976 		    st->sel_end = st->sel_start;
977 	    } else if ( event->u.chr.state&ksm_shift ) {
978 		if ( st->sel_end==st->sel_base ) {
979 		    st->sel_start = GTBackPos(st,st->sel_start,event->u.chr.state&ksm_meta);
980 		} else {
981 		    st->sel_end = GTBackPos(st,st->sel_end,event->u.chr.state&ksm_meta);
982 		}
983 	    } else {
984 		st->sel_end = st->sel_base = st->sel_start;
985 	    }
986 	    SFTextArea_Show(st,st->sel_start);
987 return( 2 );
988 	  break;
989 	  case GK_Right: case GK_KP_Right:
990 	    if ( st->sel_start==st->sel_end ) {
991 		st->sel_end = GTForePos(st,st->sel_start,event->u.chr.state&ksm_meta);
992 		if ( !(event->u.chr.state&ksm_shift ))
993 		    st->sel_start = st->sel_end;
994 	    } else if ( event->u.chr.state&ksm_shift ) {
995 		if ( st->sel_end==st->sel_base ) {
996 		    st->sel_start = GTForePos(st,st->sel_start,event->u.chr.state&ksm_meta);
997 		} else {
998 		    st->sel_end = GTForePos(st,st->sel_end,event->u.chr.state&ksm_meta);
999 		}
1000 	    } else {
1001 		st->sel_start = st->sel_base = st->sel_end;
1002 	    }
1003 	    SFTextArea_Show(st,st->sel_start);
1004 return( 2 );
1005 	  break;
1006 	  case GK_Up: case GK_KP_Up:
1007 	    if ( !st->multi_line )
1008 	  break;
1009 	    if ( !( event->u.chr.state&ksm_shift ) && st->sel_start!=st->sel_end )
1010 		st->sel_end = st->sel_base = st->sel_start;
1011 	    else {
1012 		pos = st->sel_start;
1013 		if ( ( event->u.chr.state&ksm_shift ) && st->sel_start==st->sel_base )
1014 		    pos = st->sel_end;
1015 		l = SFTextAreaFindLine(st,st->sel_start);
1016 		xpos = SFTextAreaGetXPosFromOffset(st,l,st->sel_start);
1017 		if ( l!=0 )
1018 		    pos = SFTextAreaGetOffsetFromXPos(st,l-1,xpos);
1019 		if ( event->u.chr.state&ksm_shift ) {
1020 		    if ( pos<st->sel_base ) {
1021 			st->sel_start = pos;
1022 			st->sel_end = st->sel_base;
1023 		    } else {
1024 			st->sel_start = st->sel_base;
1025 			st->sel_end = pos;
1026 		    }
1027 		} else {
1028 		    st->sel_start = st->sel_end = st->sel_base = pos;
1029 		}
1030 	    }
1031 	    SFTextArea_Show(st,st->sel_start);
1032 return( 2 );
1033 	  break;
1034 	  case GK_Down: case GK_KP_Down:
1035 	    if ( !st->multi_line )
1036 	  break;
1037 	    if ( !( event->u.chr.state&ksm_shift ) && st->sel_start!=st->sel_end )
1038 		st->sel_end = st->sel_base = st->sel_end;
1039 	    else {
1040 		pos = st->sel_start;
1041 		if ( ( event->u.chr.state&ksm_shift ) && st->sel_start==st->sel_base )
1042 		    pos = st->sel_end;
1043 		l = SFTextAreaFindLine(st,st->sel_start);
1044 		xpos = SFTextAreaGetXPosFromOffset(st,l,st->sel_start);
1045 		if ( l<st->li.lcnt-1 )
1046 		    pos = SFTextAreaGetOffsetFromXPos(st,l+1,xpos);
1047 		if ( event->u.chr.state&ksm_shift ) {
1048 		    if ( pos<st->sel_base ) {
1049 			st->sel_start = pos;
1050 			st->sel_end = st->sel_base;
1051 		    } else {
1052 			st->sel_start = st->sel_base;
1053 			st->sel_end = pos;
1054 		    }
1055 		} else {
1056 		    st->sel_start = st->sel_end = st->sel_base = pos;
1057 		}
1058 	    }
1059 	    SFTextArea_Show(st,st->sel_start);
1060 return( 2 );
1061 	  break;
1062 	  case GK_Home: case GK_Begin: case GK_KP_Home: case GK_KP_Begin:
1063 	    if ( !(event->u.chr.state&ksm_shift) ) {
1064 		st->sel_start = st->sel_base = st->sel_end = 0;
1065 	    } else {
1066 		st->sel_start = 0; st->sel_end = st->sel_base;
1067 	    }
1068 	    SFTextArea_Show(st,st->sel_start);
1069 return( 2 );
1070 	  break;
1071 	  /* Move to eol. (if already at eol, move to next eol) */
1072 	  case 'E': case 'e':
1073 	    if ( !( event->u.chr.state&ksm_control ) )
1074 return( false );
1075 	    upt = st->li.text+st->sel_base;
1076 	    if ( *upt=='\n' )
1077 		++upt;
1078 	    upt = u_strchr(upt,'\n');
1079 	    if ( upt==NULL ) upt=st->li.text+u_strlen(st->li.text);
1080 	    if ( !(event->u.chr.state&ksm_shift) ) {
1081 		st->sel_start = st->sel_base = st->sel_end =upt-st->li.text;
1082 	    } else {
1083 		st->sel_start = st->sel_base; st->sel_end = upt-st->li.text;
1084 	    }
1085 	    SFTextArea_Show(st,st->sel_start);
1086 return( 2 );
1087 	  break;
1088 	  case GK_End: case GK_KP_End:
1089 	    if ( !(event->u.chr.state&ksm_shift) ) {
1090 		st->sel_start = st->sel_base = st->sel_end = u_strlen(st->li.text);
1091 	    } else {
1092 		st->sel_start = st->sel_base; st->sel_end = u_strlen(st->li.text);
1093 	    }
1094 	    SFTextArea_Show(st,st->sel_start);
1095 return( 2 );
1096 	  break;
1097 	  case 'A': case 'a':
1098 	    if ( event->u.chr.state&ksm_control ) {	/* Select All */
1099 		sftextarea_editcmd(&st->g,ec_selectall);
1100 return( 2 );
1101 	    }
1102 	  break;
1103 	  case 'C': case 'c':
1104 	    if ( event->u.chr.state&ksm_control ) {	/* Copy */
1105 		sftextarea_editcmd(&st->g,ec_copy);
1106 	    }
1107 	  break;
1108 	  case 'V': case 'v':
1109 	    if ( event->u.chr.state&ksm_control ) {	/* Paste */
1110 		sftextarea_editcmd(&st->g,ec_paste);
1111 		SFTextArea_Show(st,st->sel_start);
1112 return( true );
1113 	    }
1114 	  break;
1115 	  case 'X': case 'x':
1116 	    if ( event->u.chr.state&ksm_control ) {	/* Cut */
1117 		sftextarea_editcmd(&st->g,ec_cut);
1118 		SFTextArea_Show(st,st->sel_start);
1119 return( true );
1120 	    }
1121 	  break;
1122 	  case 'Z': case 'z':				/* Undo */
1123 	    if ( event->u.chr.state&ksm_control ) {
1124 		sftextarea_editcmd(&st->g,ec_undo);
1125 		SFTextArea_Show(st,st->sel_start);
1126 return( true );
1127 	    }
1128 	  break;
1129 	  case 'D': case 'd':
1130 	    if ( event->u.chr.state&ksm_control ) {	/* delete word */
1131 		sftextarea_editcmd(&st->g,ec_deleteword);
1132 		SFTextArea_Show(st,st->sel_start);
1133 return( true );
1134 	    }
1135 	  break;
1136 	  case 'W': case 'w':
1137 	    if ( event->u.chr.state&ksm_control ) {	/* backword */
1138 		sftextarea_editcmd(&st->g,ec_backword);
1139 		SFTextArea_Show(st,st->sel_start);
1140 return( true );
1141 	    }
1142 	  break;
1143 	  case 'M': case 'm': case 'J': case 'j':
1144 	    if ( !( event->u.chr.state&ksm_control ) )
1145 return( false );
1146 	    /* fall through into return case */
1147 	  case GK_Return: case GK_Linefeed:
1148 	    if ( st->accepts_returns ) {
1149 		SFTextArea_Replace(st,newlinestr);
1150 return( true );
1151 	    }
1152 	  break;
1153 	  case GK_Tab:
1154 	    if ( st->accepts_tabs ) {
1155 		SFTextArea_Replace(st,tabstr);
1156 return( true );
1157 	    }
1158 	  break;
1159 	  case 's': case 'S':
1160 	    if ( !( event->u.chr.state&ksm_control ) )
1161 return( false );
1162 	    SFTextAreaSave(st);
1163 return( 2 );
1164 	  break;
1165 	  case 'I': case 'i':
1166 	    if ( !( event->u.chr.state&ksm_control ) )
1167 return( false );
1168 	    SFTextAreaImport(st);
1169 return( true );
1170 	}
1171     } else {
1172 	SFTextArea_Replace(st,event->u.chr.chars);
1173 return( true );
1174     }
1175 
1176     if ( st->sel_start == st->sel_end )
1177 	st->sel_base = st->sel_start;
1178     if ( ss!=st->sel_start || se!=st->sel_end )
1179 	SFTextAreaGrabPrimarySelection(st);
1180 return( false );
1181 }
1182 
gt_cursor_pos(SFTextArea * st,int * x,int * y,int * fh)1183 static void gt_cursor_pos(SFTextArea *st, int *x, int *y, int *fh) {
1184     int l, ty;
1185 
1186     *x = 0; *y= 0; *fh = 20;
1187     if ( st->li.fontlist!=NULL )
1188 	*fh = st->li.fontlist->fd->pointsize*st->li.dpi/72;
1189     l = SFTextAreaFindLine(st,st->sel_start);
1190     if ( l<0 || l>=st->li.lcnt )
1191 return;
1192     ty = st->li.lineheights[l].y - st->li.lineheights[st->loff_top].y;
1193     if ( ty<0 || ty>st->g.inner.height ) {
1194 	*x = *y = -1;
1195 return;
1196     }
1197     *y = ty;
1198     *fh = st->li.lineheights[l].fh;
1199     *x = SFTextAreaGetXPosFromOffset(st,l,st->sel_start);
1200 }
1201 
GTPositionGIC(SFTextArea * st)1202 static void GTPositionGIC(SFTextArea *st) {
1203     int x,y,fh;
1204 
1205     if ( !st->g.has_focus || st->gic==NULL )
1206 return;
1207     gt_cursor_pos(st,&x,&y,&fh);
1208     if ( x<0 )
1209 return;
1210     GDrawSetGIC(st->g.base,st->gic,st->g.inner.x+x,st->g.inner.y+y+st->as);
1211 }
1212 
gt_draw_cursor(GWindow pixmap,SFTextArea * st)1213 static void gt_draw_cursor(GWindow pixmap, SFTextArea *st) {
1214     GRect old;
1215     int x, y, fh;
1216 
1217     if ( !st->cursor_on || st->sel_start != st->sel_end )
1218 return;
1219     gt_cursor_pos(st,&x,&y,&fh);
1220 
1221     if ( x<0 || x>=st->g.inner.width )
1222 return;
1223     GDrawPushClip(pixmap,&st->g.inner,&old);
1224     GDrawSetDifferenceMode(pixmap);
1225     GDrawDrawLine(pixmap, st->g.inner.x+x,st->g.inner.y+y,
1226 	    st->g.inner.x+x,st->g.inner.y+y+fh, COLOR_WHITE);
1227     GDrawPopClip(pixmap,&old);
1228 }
1229 
SFTextAreaDrawDDCursor(SFTextArea * st,int pos)1230 static void SFTextAreaDrawDDCursor(SFTextArea *st, int pos) {
1231     GRect old;
1232     int x, y, l;
1233 
1234     l = SFTextAreaFindLine(st,pos);
1235     y = st->li.lineheights[l].y - st->li.lineheights[st->loff_top].y;
1236     if ( y<0 || y>st->g.inner.height )
1237 return;
1238     x = SFTextAreaGetXPosFromOffset(st,l,pos);
1239     if ( x<0 || x>=st->g.inner.width )
1240 return;
1241 
1242     GDrawPushClip(st->g.base,&st->g.inner,&old);
1243     GDrawSetDifferenceMode(st->g.base);
1244     GDrawDrawLine(st->g.base,st->g.inner.x+x,st->g.inner.y+y,
1245 	    st->g.inner.x+x,st->g.inner.y+y+st->li.lineheights[l].fh,
1246         COLOR_WHITE);
1247     GDrawPopClip(st->g.base,&old);
1248     GDrawSetDashedLine(st->g.base,0,0,0);
1249     st->has_dd_cursor = !st->has_dd_cursor;
1250     st->dd_cursor_pos = pos;
1251 }
1252 
sftextarea_expose(GWindow pixmap,GGadget * g,GEvent * event)1253 static int sftextarea_expose(GWindow pixmap, GGadget *g, GEvent *event) {
1254     SFTextArea *st = (SFTextArea *) g;
1255     GRect old1, old2, *r = &g->r, selr;
1256     Color fg,sel;
1257     int y,x,p,i,dotext,j,xend;
1258     struct opentype_str **line;
1259 
1260     if ( g->state == gs_invisible || st->dontdraw )
1261 return( false );
1262 
1263     GDrawPushClip(pixmap,r,&old1);
1264 
1265     GBoxDrawBackground(pixmap,r,g->box,
1266 	    g->state==gs_enabled? gs_pressedactive: g->state,false);
1267     GBoxDrawBorder(pixmap,r,g->box,g->state,false);
1268 
1269     GDrawPushClip(pixmap,&g->inner,&old2);
1270     GDrawSetFont(pixmap,st->font);
1271     GDrawSetDither(NULL, false);	/* on 8 bit displays we don't want any dithering */
1272     GDrawSetLineWidth(pixmap,0);
1273 
1274     fg = g->state==gs_disabled?g->box->disabled_foreground:
1275 		    g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
1276 		    g->box->main_foreground;
1277     sel = g->box->active_border;
1278     for ( i=st->loff_top; i<st->li.lcnt; ++i ) {
1279 	int selstartx, selendx;
1280 	/* First draw the selection, then draw the text */
1281 	y = g->inner.y+ st->li.lineheights[i].y-st->li.lineheights[st->loff_top].y+
1282 		st->li.lineheights[i].as;
1283 	if ( y>g->inner.y+g->inner.height || y>event->u.expose.rect.y+event->u.expose.rect.height )
1284     break;
1285 	if ( y+st->li.lineheights[i].fh<=event->u.expose.rect.y )
1286     continue;
1287 	selstartx = selendx = -1;
1288 	for ( dotext=0; dotext<2; ++dotext ) {
1289 	    /* Does this para start out r2l or l2r? */
1290 	    p = st->li.lineheights[i].p;
1291 	    if ( st->li.paras[p].para[0]!=NULL &&
1292 		    st->li.paras[p].para[0]->fl!=NULL &&
1293 		    ScriptIsRightToLeft( ((struct fontlist *) (st->li.paras[p].para[0]->fl))->script ))
1294 		x = st->li.xmax - st->li.lineheights[i].linelen;
1295 	    else
1296 		x = 0;
1297 	    line = st->li.lines[i];
1298 	    for ( j=0; line[j]!=NULL; ++j ) {
1299 		xend = x + line[j]->advance_width + line[j]->vr.h_adv_off;
1300 		if ( dotext ) {
1301 		    LI_FDDrawChar(pixmap,
1302 			    (void (*)(void *,GImage *,GRect *,int, int)) GDrawDrawGlyph,
1303 			    (void (*)(void *,GRect *,Color)) GDrawDrawRect,
1304 			    line[j],g->inner.x+x-st->xoff_left,y,fg);
1305 		} else {
1306 		    int pos = line[j]->orig_index +
1307 			    ((struct fontlist *) (line[j]->fl))->start;
1308 		    if ( pos>=st->sel_start && pos<st->sel_end ) {
1309 			if ( selstartx==-1 )
1310 			    selstartx = x;
1311 			selendx = xend;
1312 		    }
1313 		    if ( !(pos>=st->sel_start && pos<st->sel_end) || line[j+1]==NULL ) {
1314 			if ( selstartx!=-1 ) {
1315 			    selr.x = selstartx+g->inner.x-st->xoff_left;
1316 			    selr.width = selendx-selstartx;
1317 			    selr.y = y-st->li.lineheights[i].as;
1318 			    selr.height = st->li.lineheights[i].fh;
1319 			    GDrawFillRect(pixmap,&selr,sel);
1320 			    selstartx = selendx = -1;
1321 			}
1322 		    }
1323 		}
1324 		x = xend;
1325 	    }
1326 	}
1327     }
1328 
1329     GDrawSetDither(NULL, true);
1330     GDrawPopClip(pixmap,&old2);
1331     GDrawPopClip(pixmap,&old1);
1332     gt_draw_cursor(pixmap, st);
1333 return( true );
1334 }
1335 
SFTextAreaDoDrop(SFTextArea * st,GEvent * event,int endpos)1336 static int SFTextAreaDoDrop(SFTextArea *st,GEvent *event,int endpos) {
1337 
1338     if ( st->has_dd_cursor )
1339 	SFTextAreaDrawDDCursor(st,st->dd_cursor_pos);
1340 
1341     if ( event->type == et_mousemove ) {
1342 	if ( GGadgetInnerWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
1343 	    if ( endpos<st->sel_start || endpos>=st->sel_end )
1344 		SFTextAreaDrawDDCursor(st,endpos);
1345 	} else if ( !GGadgetWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
1346 	    GDrawPostDragEvent(st->g.base,event,et_drag);
1347 	}
1348     } else {
1349 	if ( GGadgetInnerWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
1350 	    if ( endpos>=st->sel_start && endpos<st->sel_end ) {
1351 		st->sel_start = st->sel_end = endpos;
1352 	    } else {
1353 		unichar_t *old=st->li.oldtext, *temp;
1354 		int pos=0;
1355 		if ( event->u.mouse.state&ksm_control ) {
1356 		    temp = malloc((u_strlen(st->li.text)+st->sel_end-st->sel_start+1)*sizeof(unichar_t));
1357 		    memcpy(temp,st->li.text,endpos*sizeof(unichar_t));
1358 		    memcpy(temp+endpos,st->li.text+st->sel_start,
1359 			    (st->sel_end-st->sel_start)*sizeof(unichar_t));
1360 		    u_strcpy(temp+endpos+st->sel_end-st->sel_start,st->li.text+endpos);
1361 		} else if ( endpos>=st->sel_end ) {
1362 		    temp = u_copy(st->li.text);
1363 		    memcpy(temp+st->sel_start,temp+st->sel_end,
1364 			    (endpos-st->sel_end)*sizeof(unichar_t));
1365 		    memcpy(temp+endpos-(st->sel_end-st->sel_start),
1366 			    st->li.text+st->sel_start,(st->sel_end-st->sel_start)*sizeof(unichar_t));
1367 		    pos = endpos;
1368 		} else /*if ( endpos<st->sel_start )*/ {
1369 		    temp = u_copy(st->li.text);
1370 		    memcpy(temp+endpos,st->li.text+st->sel_start,
1371 			    (st->sel_end-st->sel_start)*sizeof(unichar_t));
1372 		    memcpy(temp+endpos+st->sel_end-st->sel_start,st->li.text+endpos,
1373 			    (st->sel_start-endpos)*sizeof(unichar_t));
1374 		    pos = endpos+st->sel_end-st->sel_start;
1375 		}
1376 		st->li.oldtext = st->li.text;
1377 		st->sel_oldstart = st->sel_start;
1378 		st->sel_oldend = st->sel_end;
1379 		st->sel_oldbase = st->sel_base;
1380 		st->sel_start = st->sel_end = pos;
1381 		st->li.text = temp;
1382 		free(old);
1383 		SFTextAreaRefigureLines(st, endpos<st->sel_oldstart?endpos:st->sel_oldstart,-1);
1384 	    }
1385 	} else if ( !GGadgetWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
1386 	    /* Don't delete the selection until someone actually accepts the drop */
1387 	    /* Don't delete at all (copy not move) if control key is down */
1388 	    if ( ( event->u.mouse.state&ksm_control ) )
1389 		SFTextAreaGrabSelection(st,sn_drag_and_drop);
1390 	    else
1391 		SFTextAreaGrabDDSelection(st);
1392 	    GDrawPostDragEvent(st->g.base,event,et_drop);
1393 	}
1394 	st->drag_and_drop = false;
1395 	GDrawSetCursor(st->g.base,st->old_cursor);
1396 	_ggadget_redraw(&st->g);
1397     }
1398 return( false );
1399 }
1400 
STChangeCheck(SFTextArea * st)1401 static void STChangeCheck(SFTextArea *st) {
1402     struct fontlist *fl;
1403 
1404     if ( st->changefontcallback==NULL )
1405 return;
1406     for ( fl=st->li.fontlist; fl!=NULL && fl->end<st->sel_end; fl=fl->next );
1407     if ( fl!=NULL && fl->next!=NULL && fl->next->end==st->sel_end )
1408 	fl = fl->next;
1409     if ( fl==NULL /* || fl->fd==st->last_fd ||*/ )
1410 return;
1411     (st->changefontcallback)(st->cbcontext,fl->fd->sf,fl->fd->fonttype,
1412 	    fl->fd->pointsize,fl->fd->antialias,fl->script,fl->lang,fl->feats);
1413 }
1414 
sftextarea_mouse(GGadget * g,GEvent * event)1415 static int sftextarea_mouse(GGadget *g, GEvent *event) {
1416     SFTextArea *st = (SFTextArea *) g;
1417     int end=-1;
1418     int i=0;
1419 
1420     if ( st->hidden_cursor ) {
1421 	GDrawSetCursor(st->g.base,st->old_cursor);
1422 	st->hidden_cursor = false;
1423 	_GWidget_ClearGrabGadget(g);
1424     }
1425     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
1426 return( false );
1427     if ( event->type == et_crossing )
1428 return( false );
1429     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
1430 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7)) {
1431 	int isv = event->u.mouse.button<=5;
1432 	if ( event->u.mouse.state&ksm_shift ) isv = !isv;
1433 	if ( isv && st->vsb!=NULL )
1434 return( GGadgetDispatchEvent(&st->vsb->g,event));
1435 	else if ( !isv && st->hsb!=NULL )
1436 return( GGadgetDispatchEvent(&st->hsb->g,event));
1437 	else
1438 return( true );
1439     }
1440 
1441     if ( st->pressed==NULL && event->type == et_mousemove && g->popup_msg!=NULL &&
1442 	    GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y))
1443 	GGadgetPreparePopup(g->base,g->popup_msg);
1444 
1445     if ( event->type == et_mousedown && event->u.mouse.button==3 &&
1446 	    GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
1447 	SFTFPopupMenu(st,event);
1448 return( true );
1449     }
1450 
1451     if ( event->type == et_mousedown || st->pressed ) {
1452 	for ( i=st->loff_top; i<st->li.lcnt-1 &&
1453 		event->u.mouse.y-g->inner.y>=st->li.lineheights[i+1].y-st->li.lineheights[st->loff_top].y;
1454 		++i );
1455 	if ( i<0 ) i = 0;
1456 	if ( !st->multi_line ) i = 0;
1457 	end = SFTextAreaGetOffsetFromXPos(st,i,event->u.mouse.x - st->g.inner.x - st->xoff_left);
1458     }
1459 
1460     if ( event->type == et_mousedown ) {
1461 	st->wordsel = st->linesel = false;
1462 	if ( event->u.mouse.button==1 && event->u.mouse.clicks>=3 ) {
1463 	    if ( i<st->li.lcnt )
1464 		st->sel_start = st->li.lineheights[i].start_pos;
1465 	    else
1466 		st->sel_start = end;
1467 	    if ( i+1<st->li.lcnt )
1468 		st->sel_end = st->li.lineheights[i+1].start_pos;
1469 	    else
1470 		st->sel_end = u_strlen(st->li.text);
1471 	    st->wordsel = false; st->linesel = true;
1472 	} else if ( event->u.mouse.button==1 && event->u.mouse.clicks==2 ) {
1473 	    st->sel_start = st->sel_end = st->sel_base = end;
1474 	    st->wordsel = true;
1475 	    SFTextAreaSelectWords(st,st->sel_base);
1476 	} else if ( end>=st->sel_start && end<st->sel_end &&
1477 		st->sel_start!=st->sel_end &&
1478 		event->u.mouse.button==1 ) {
1479 	    st->drag_and_drop = true;
1480 	    if ( !st->hidden_cursor )
1481 		st->old_cursor = GDrawGetCursor(st->g.base);
1482 	    GDrawSetCursor(st->g.base,ct_draganddrop);
1483 	} else if ( event->u.mouse.button!=3 && !(event->u.mouse.state&ksm_shift) ) {
1484 	    if ( event->u.mouse.button==1 )
1485 		SFTextAreaGrabPrimarySelection(st);
1486 	    st->sel_start = st->sel_end = st->sel_base = end;
1487 	} else if ( end>st->sel_base ) {
1488 	    st->sel_start = st->sel_base;
1489 	    st->sel_end = end;
1490 	} else {
1491 	    st->sel_start = end;
1492 	    st->sel_end = st->sel_base;
1493 	}
1494 	if ( st->pressed==NULL )
1495 	    st->pressed = GDrawRequestTimer(st->g.base,200,100,NULL);
1496 	if ( st->sel_start > u_strlen( st->li.text ))	/* Ok to have selection at end, but beyond is an error */
1497 	    fprintf( stderr, "About to crash\n" );
1498 	_ggadget_redraw(g);
1499 	if ( st->changefontcallback )
1500 	    STChangeCheck(st);
1501 return( true );
1502     } else if ( st->pressed && (event->type == et_mousemove || event->type == et_mouseup )) {
1503 	int refresh = true;
1504 
1505 	if ( st->drag_and_drop ) {
1506 	    refresh = SFTextAreaDoDrop(st,event,end);
1507 	} else if ( st->linesel ) {
1508 	    int basel, l, spos;
1509 	    basel = SFTextAreaFindLine(st,st->sel_base);
1510 	    l = basel<i ? basel : i;
1511 	    if ( l<st->li.lcnt )
1512 		spos = st->li.lineheights[l].start_pos;
1513 	    else
1514 		spos = basel<i ? st->sel_base : end;
1515 	    st->sel_start = spos;
1516 	    l = basel>i ? basel : i;
1517 	    if ( l+1<st->li.lcnt )
1518 		spos = st->li.lineheights[l+1].start_pos;
1519 	    else
1520 		spos = u_strlen(st->li.text);
1521 	    st->sel_end = spos;
1522 	} else if ( st->wordsel )
1523 	    SFTextAreaSelectWords(st,end);
1524 	else if ( event->u.mouse.button!=2 ) {
1525 	    int e = end;
1526 	    if ( e>st->sel_base ) {
1527 		st->sel_start = st->sel_base; st->sel_end = e;
1528 	    } else {
1529 		st->sel_start = e; st->sel_end = st->sel_base;
1530 	    }
1531 	}
1532 	if ( event->type==et_mouseup ) {
1533 	    GDrawCancelTimer(st->pressed); st->pressed = NULL;
1534 	    if ( event->u.mouse.button==2 )
1535 		SFTextAreaPaste(st,sn_primary);
1536 	    if ( st->sel_start==st->sel_end )
1537 		SFTextArea_Show(st,st->sel_start);
1538 	}
1539 	if ( st->sel_end > u_strlen( st->li.text ))
1540 	    fprintf( stderr, "About to crash\n" );
1541 	if ( refresh )
1542 	    _ggadget_redraw(g);
1543 	if ( event->type==et_mouseup && st->changefontcallback )
1544 	    STChangeCheck(st);
1545 return( true );
1546     }
1547 return( false );
1548 }
1549 
sftextarea_key(GGadget * g,GEvent * event)1550 static int sftextarea_key(GGadget *g, GEvent *event) {
1551     SFTextArea *st = (SFTextArea *) g;
1552     int ret;
1553 
1554     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
1555 return( false );
1556 
1557     if ( event->type == et_charup )
1558 return( false );
1559     if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ||
1560 	    (event->u.chr.keysym == GK_Return && !st->accepts_returns ) ||
1561 	    ( event->u.chr.keysym == GK_Tab && !st->accepts_tabs ) ||
1562 	    event->u.chr.keysym == GK_BackTab || event->u.chr.keysym == GK_Escape )
1563 return( false );
1564 
1565     if ( !st->hidden_cursor ) {	/* hide the mouse pointer */
1566 	if ( !st->drag_and_drop )
1567 	    st->old_cursor = GDrawGetCursor(st->g.base);
1568 	GDrawSetCursor(g->base,ct_invisible);
1569 	st->hidden_cursor = true;
1570 	_GWidget_SetGrabGadget(g);	/* so that we get the next mouse movement to turn the cursor on */
1571     }
1572     if( st->cursor_on ) {	/* undraw the blinky text cursor if it is drawn */
1573 	gt_draw_cursor(g->base, st);
1574 	st->cursor_on = false;
1575     }
1576 
1577     ret = SFTextAreaDoChange(st,event);
1578     if ( st->changefontcallback )
1579 	STChangeCheck(st);
1580     switch ( ret ) {
1581       case 2:
1582       break;
1583       case true:
1584 	SFTextAreaChanged(st,-1);
1585       break;
1586       case false:
1587 return( false );
1588     }
1589     _ggadget_redraw(g);
1590 return( true );
1591 }
1592 
sftextarea_focus(GGadget * g,GEvent * event)1593 static int sftextarea_focus(GGadget *g, GEvent *event) {
1594     SFTextArea *st = (SFTextArea *) g;
1595     if ( st->cursor!=NULL ) {
1596 	GDrawCancelTimer(st->cursor);
1597 	st->cursor = NULL;
1598 	st->cursor_on = false;
1599     }
1600     if ( st->hidden_cursor && !event->u.focus.gained_focus ) {
1601 	GDrawSetCursor(st->g.base,st->old_cursor);
1602 	st->hidden_cursor = false;
1603     }
1604     st->g.has_focus = event->u.focus.gained_focus;
1605     if ( event->u.focus.gained_focus ) {
1606 	st->cursor = GDrawRequestTimer(st->g.base,400,400,NULL);
1607 	st->cursor_on = true;
1608 	if ( event->u.focus.mnemonic_focus != mf_normal )
1609 	    SFTextAreaSelect(&st->g,0,-1);
1610 	if ( st->gic!=NULL )
1611 	    GTPositionGIC(st);
1612     }
1613     _ggadget_redraw(g);
1614     SFTextAreaFocusChanged(st,event->u.focus.gained_focus);
1615 return( true );
1616 }
1617 
sftextarea_timer(GGadget * g,GEvent * event)1618 static int sftextarea_timer(GGadget *g, GEvent *event) {
1619     SFTextArea *st = (SFTextArea *) g;
1620 
1621     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
1622 return(false);
1623     if ( st->cursor == event->u.timer.timer ) {
1624 	if ( st->cursor_on ) {
1625 	    gt_draw_cursor(g->base, st);
1626 	    st->cursor_on = false;
1627 	} else {
1628 	    st->cursor_on = true;
1629 	    gt_draw_cursor(g->base, st);
1630 	}
1631 return( true );
1632     }
1633     if ( st->pressed == event->u.timer.timer ) {
1634 	GEvent e;
1635 	GDrawSetFont(g->base,st->font);
1636 	GDrawGetPointerPosition(g->base,&e);
1637 	if ( (e.u.mouse.x<g->r.x && st->xoff_left>0 ) ||
1638 		(st->multi_line && e.u.mouse.y<g->r.y && st->loff_top>0 ) ||
1639 		( e.u.mouse.x >= g->r.x + g->r.width &&
1640 			st->li.xmax-st->xoff_left>g->inner.width ) ||
1641 		( e.u.mouse.y >= g->r.y + g->r.height &&
1642 			st->li.lineheights[st->li.lcnt-1].y-st->li.lineheights[st->loff_top].y >= g->inner.height )) {
1643 	    int l;
1644 	    int xpos, end;
1645 
1646 	    for ( l=st->loff_top; l<st->li.lcnt-1 && e.u.mouse.y-g->inner.y>st->li.lineheights[l+1].y-st->li.lineheights[st->loff_top].y;
1647 		    ++l );
1648 	    if ( e.u.mouse.y<g->r.y && st->loff_top>0 )
1649 		l = --st->loff_top;
1650 	    else if ( e.u.mouse.y >= g->r.y + g->r.height &&
1651 			    st->li.lineheights[st->li.lcnt-1].y-st->li.lineheights[st->loff_top].y > g->inner.height ) {
1652 		++st->loff_top;
1653 		++l;
1654 	    } else if ( l<st->loff_top )
1655 		l = st->loff_top;
1656 	    else if ( st->li.lineheights[l].y>=st->li.lineheights[st->loff_top].y + g->inner.height ) {
1657 		for ( l = st->loff_top+1; st->li.lineheights[l].y<st->li.lineheights[st->loff_top].y+g->inner.height; ++l );
1658 		--l;
1659 		if ( l==st->loff_top ) ++l;
1660 	    }
1661 	    if ( l>=st->li.lcnt ) l = st->li.lcnt-1;
1662 
1663 	    xpos = e.u.mouse.x+st->xoff_left;
1664 	    if ( e.u.mouse.x<g->r.x && st->xoff_left>0 ) {
1665 		st->xoff_left -= st->nw;
1666 		xpos = g->inner.x + st->xoff_left;
1667 	    } else if ( e.u.mouse.x >= g->r.x + g->r.width &&
1668 			    st->li.xmax-st->xoff_left>g->inner.width ) {
1669 		st->xoff_left += st->nw;
1670 		xpos = g->inner.x + st->xoff_left + g->inner.width;
1671 	    }
1672 
1673 	    end = SFTextAreaGetOffsetFromXPos(st,l,xpos - st->g.inner.x - st->xoff_left);
1674 	    if ( end > st->sel_base ) {
1675 		st->sel_start = st->sel_base;
1676 		st->sel_end = end;
1677 	    } else {
1678 		st->sel_start = end;
1679 		st->sel_end = st->sel_base;
1680 	    }
1681 	    _ggadget_redraw(g);
1682 	    if ( st->vsb!=NULL )
1683 		GScrollBarSetPos(&st->vsb->g,st->li.lineheights[st->loff_top].y);
1684 	    if ( st->hsb!=NULL )
1685 		GScrollBarSetPos(&st->hsb->g,st->xoff_left);
1686 	}
1687 return( true );
1688     }
1689 return( false );
1690 }
1691 
sftextarea_sel(GGadget * g,GEvent * event)1692 static int sftextarea_sel(GGadget *g, GEvent *event) {
1693     SFTextArea *st = (SFTextArea *) g;
1694     int end;
1695     int i;
1696 
1697     if ( event->type == et_selclear ) {
1698 	if ( event->u.selclear.sel==sn_primary && st->sel_start!=st->sel_end ) {
1699 return( true );
1700 	}
1701 return( false );
1702     }
1703 
1704     if ( st->has_dd_cursor )
1705 	SFTextAreaDrawDDCursor(st,st->dd_cursor_pos);
1706     GDrawSetFont(g->base,st->font);
1707     for ( i=st->loff_top ; i<st->li.lcnt-1 && st->li.lineheights[i+1].y-st->li.lineheights[st->loff_top].y<
1708 	    event->u.drag_drop.y-g->inner.y; ++i );
1709     if ( !st->multi_line ) i = 0;
1710     if ( i>=st->li.lcnt )
1711 	end = u_strlen(st->li.text);
1712     else
1713 	end = SFTextAreaGetOffsetFromXPos(st,i,event->u.drag_drop.x - st->g.inner.x - st->xoff_left);
1714     if ( event->type == et_drag ) {
1715 	SFTextAreaDrawDDCursor(st,end);
1716     } else if ( event->type == et_dragout ) {
1717 	/* this event exists simply to clear the dd cursor line. We've done */
1718 	/*  that already */
1719     } else if ( event->type == et_drop ) {
1720 	st->sel_start = st->sel_end = st->sel_base = end;
1721 	SFTextAreaPaste(st,sn_drag_and_drop);
1722 	SFTextArea_Show(st,st->sel_start);
1723 	_ggadget_redraw(&st->g);
1724     } else
1725 return( false );
1726 
1727 return( true );
1728 }
1729 
sftextarea_destroy(GGadget * g)1730 static void sftextarea_destroy(GGadget *g) {
1731     SFTextArea *st = (SFTextArea *) g;
1732 
1733     if ( st==NULL )
1734 return;
1735 
1736     if ( st->vsb!=NULL )
1737 	(st->vsb->g.funcs->destroy)(&st->vsb->g);
1738     if ( st->hsb!=NULL )
1739 	(st->hsb->g.funcs->destroy)(&st->hsb->g);
1740     GDrawCancelTimer(st->pressed);
1741     GDrawCancelTimer(st->cursor);
1742     LayoutInfo_Destroy(&st->li);
1743     _ggadget_destroy(g);
1744 }
1745 
SFTextAreaSetTitle(GGadget * g,const unichar_t * tit)1746 static void SFTextAreaSetTitle(GGadget *g,const unichar_t *tit) {
1747     SFTextArea *st = (SFTextArea *) g;
1748     unichar_t *old = st->li.oldtext;
1749     if ( u_strcmp(tit,st->li.text)==0 )	/* If it doesn't change anything, then don't trash undoes or selection */
1750 return;
1751     st->li.oldtext = st->li.text;
1752     st->sel_oldstart = st->sel_start; st->sel_oldend = st->sel_end; st->sel_oldbase = st->sel_base;
1753     st->li.text = u_copy(tit);		/* tit might be oldtext, so must copy before freeing */
1754     free(old);
1755     st->sel_start = st->sel_end = st->sel_base = u_strlen(tit);
1756     LI_fontlistmergecheck(&st->li);
1757     LayoutInfoRefigureLines(&st->li,0,-1,st->g.inner.width);
1758     SFTextArea_Show(st,st->sel_start);
1759     _ggadget_redraw(g);
1760 }
1761 
_SFTextAreaGetTitle(GGadget * g)1762 static const unichar_t *_SFTextAreaGetTitle(GGadget *g) {
1763     SFTextArea *st = (SFTextArea *) g;
1764 return( st->li.text );
1765 }
1766 
SFTextAreaSetFont(GGadget * g,FontInstance * new)1767 static void SFTextAreaSetFont(GGadget *g,FontInstance *new) {
1768     SFTextArea *st = (SFTextArea *) g;
1769     st->font = new;
1770     /* Irrelevant */;
1771 }
1772 
SFTextAreaGetFont(GGadget * g)1773 static FontInstance *SFTextAreaGetFont(GGadget *g) {
1774     SFTextArea *st = (SFTextArea *) g;
1775 return( st->font );
1776 }
1777 
SFTextAreaShow(GGadget * g,int pos)1778 void SFTextAreaShow(GGadget *g,int pos) {
1779     SFTextArea *st = (SFTextArea *) g;
1780 
1781     SFTextArea_Show(st,pos);
1782     _ggadget_redraw(g);
1783 }
1784 
SFTextAreaSelect(GGadget * g,int start,int end)1785 void SFTextAreaSelect(GGadget *g,int start, int end) {
1786     SFTextArea *st = (SFTextArea *) g;
1787 
1788     SFTextAreaGrabPrimarySelection(st);
1789     if ( end<0 ) {
1790 	end = u_strlen(st->li.text);
1791 	if ( start<0 ) start = end;
1792     }
1793     if ( start>end ) { int temp = start; start = end; end = temp; }
1794     if ( end>u_strlen(st->li.text)) end = u_strlen(st->li.text);
1795     if ( start>u_strlen(st->li.text)) start = end;
1796     else if ( start<0 ) start=0;
1797     st->sel_start = st->sel_base = start;
1798     st->sel_end = end;
1799     _ggadget_redraw(g);			/* Should be safe just to draw the textfield gadget, sbs won't have changed */
1800 }
1801 
SFTextAreaReplace(GGadget * g,const unichar_t * txt)1802 void SFTextAreaReplace(GGadget *g,const unichar_t *txt) {
1803     SFTextArea *st = (SFTextArea *) g;
1804 
1805     SFTextArea_Replace(st,txt);
1806     _ggadget_redraw(g);
1807 }
1808 
sftextarea_redraw(GGadget * g)1809 static void sftextarea_redraw(GGadget *g) {
1810     SFTextArea *st = (SFTextArea *) g;
1811     if ( st->vsb!=NULL )
1812 	_ggadget_redraw((GGadget *) (st->vsb));
1813     if ( st->hsb!=NULL )
1814 	_ggadget_redraw((GGadget *) (st->hsb));
1815     _ggadget_redraw(g);
1816 }
1817 
sftextarea_move(GGadget * g,int32 x,int32 y)1818 static void sftextarea_move(GGadget *g, int32 x, int32 y ) {
1819     SFTextArea *st = (SFTextArea *) g;
1820     if ( st->vsb!=NULL )
1821 	_ggadget_move((GGadget *) (st->vsb),x+(st->vsb->g.r.x-g->r.x),y);
1822     if ( st->hsb!=NULL )
1823 	_ggadget_move((GGadget *) (st->hsb),x,y+(st->hsb->g.r.y-g->r.y));
1824     _ggadget_move(g,x,y);
1825 }
1826 
sftextarea_resize(GGadget * g,int32 width,int32 height)1827 static void sftextarea_resize(GGadget *g, int32 width, int32 height ) {
1828     SFTextArea *st = (SFTextArea *) g;
1829     int gtwidth=width, gtheight=height, oldheight=0;
1830     int l;
1831 
1832     if ( st->hsb!=NULL ) {
1833 	oldheight = st->hsb->g.r.y+st->hsb->g.r.height-g->r.y;
1834 	gtheight = height - (oldheight-g->r.height);
1835     }
1836     if ( st->vsb!=NULL ) {
1837 	int oldwidth = st->vsb->g.r.x+st->vsb->g.r.width-g->r.x;
1838 	gtwidth = width - (oldwidth-g->r.width);
1839 	_ggadget_move((GGadget *) (st->vsb),st->vsb->g.r.x+width-oldwidth,st->vsb->g.r.y);
1840 	_ggadget_resize((GGadget *) (st->vsb),st->vsb->g.r.width,gtheight);
1841     }
1842     if ( st->hsb!=NULL ) {
1843 	_ggadget_move((GGadget *) (st->hsb),st->hsb->g.r.y,st->hsb->g.r.y+height-oldheight);
1844 	_ggadget_resize((GGadget *) (st->hsb),gtwidth,st->hsb->g.r.height);
1845     }
1846     _ggadget_resize(g,gtwidth, gtheight);
1847     SFTextAreaRefigureLines(st,0,-1);
1848     if ( st->vsb!=NULL ) {
1849 	GScrollBarSetBounds(&st->vsb->g,0,st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh,st->g.inner.height);
1850 	if ( st->loff_top>=st->li.lcnt )
1851 	    st->loff_top = st->li.lcnt-1;
1852 	l = st->li.lcnt - SFTextArea_EndPage(st);
1853 	if ( l<0 ) l = 0;
1854 	if ( l!=st->loff_top ) {
1855 	    st->loff_top = l;
1856 	    GScrollBarSetPos(&st->vsb->g,st->li.lineheights[l].y);
1857 	    _ggadget_redraw(&st->g);
1858 	}
1859     }
1860     SFTextAreaShow(&st->g,st->sel_start);
1861 }
1862 
sftextarea_getsize(GGadget * g,GRect * r)1863 static GRect *sftextarea_getsize(GGadget *g, GRect *r ) {
1864     SFTextArea *st = (SFTextArea *) g;
1865     _ggadget_getsize(g,r);
1866     if ( st->vsb!=NULL )
1867 	r->width =  st->vsb->g.r.x+st->vsb->g.r.width-g->r.x;
1868     if ( st->hsb!=NULL )
1869 	r->height =  st->hsb->g.r.y+st->hsb->g.r.height-g->r.y;
1870 return( r );
1871 }
1872 
sftextarea_setvisible(GGadget * g,int visible)1873 static void sftextarea_setvisible(GGadget *g, int visible ) {
1874     SFTextArea *st = (SFTextArea *) g;
1875     if ( st->vsb!=NULL ) _ggadget_setvisible(&st->vsb->g,visible);
1876     if ( st->hsb!=NULL ) _ggadget_setvisible(&st->hsb->g,visible);
1877     _ggadget_setvisible(g,visible);
1878 }
1879 
sftextarea_setenabled(GGadget * g,int enabled)1880 static void sftextarea_setenabled(GGadget *g, int enabled ) {
1881     SFTextArea *st = (SFTextArea *) g;
1882     if ( st->vsb!=NULL ) _ggadget_setenabled(&st->vsb->g,enabled);
1883     if ( st->hsb!=NULL ) _ggadget_setenabled(&st->hsb->g,enabled);
1884     _ggadget_setenabled(g,enabled);
1885 }
1886 
sftextarea_vscroll(GGadget * g,GEvent * event)1887 static int sftextarea_vscroll(GGadget *g, GEvent *event) {
1888     enum sb sbt = event->u.control.u.sb.type;
1889     SFTextArea *st = (SFTextArea *) (g->data);
1890     int loff = st->loff_top;
1891     int page;
1892 
1893     g = (GGadget *) st;
1894 
1895     if ( sbt==et_sb_top )
1896 	loff = 0;
1897     else if ( sbt==et_sb_bottom ) {
1898 	loff = st->li.lcnt;
1899     } else if ( sbt==et_sb_up ) {
1900 	if ( st->loff_top!=0 ) loff = st->loff_top-1; else loff = 0;
1901     } else if ( sbt==et_sb_down ) {
1902 	++loff;
1903     } else if ( sbt==et_sb_uppage ) {
1904 	for ( page=0; st->loff_top-page>=0 && st->li.lineheights[st->loff_top].y-st->li.lineheights[st->loff_top-page].y<=g->inner.height;
1905 		++page );
1906 	if ( --page < 1 ) page = 1;
1907 	else if ( page>2 ) page-=1;
1908 	loff = st->loff_top - page;
1909     } else if ( sbt==et_sb_downpage ) {
1910 	for ( page=0; st->loff_top+page<st->li.lcnt && st->li.lineheights[st->loff_top+page].y-st->li.lineheights[st->loff_top].y<=g->inner.height;
1911 		++page );
1912 	if ( --page < 1 ) page = 1;
1913 	else if ( page>2 ) page-=1;
1914 	loff = st->loff_top + page;
1915     } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
1916 	for ( loff = 0; loff<st->li.lcnt && st->li.lineheights[loff].y<event->u.control.u.sb.pos; ++loff );
1917     }
1918     for ( page=1; st->li.lcnt-page>=0 && st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh-st->li.lineheights[st->li.lcnt-page].y<=g->inner.height;
1919 	    ++page );
1920     --page;
1921     if ( loff > st->li.lcnt-page )
1922 	loff = st->li.lcnt - page;
1923     if ( loff<0 ) loff = 0;
1924     if ( loff!=st->loff_top ) {
1925 	st->loff_top = loff;
1926 	GScrollBarSetPos(&st->vsb->g,st->li.lineheights[loff].y);
1927 	_ggadget_redraw(&st->g);
1928     }
1929 return( true );
1930 }
1931 
sftextarea_hscroll(GGadget * g,GEvent * event)1932 static int sftextarea_hscroll(GGadget *g, GEvent *event) {
1933     enum sb sbt = event->u.control.u.sb.type;
1934     SFTextArea *st = (SFTextArea *) (g->data);
1935     int xoff = st->xoff_left;
1936 
1937     g = (GGadget *) st;
1938 
1939     if ( sbt==et_sb_top )
1940 	xoff = 0;
1941     else if ( sbt==et_sb_bottom ) {
1942 	xoff = st->li.xmax - st->g.inner.width;
1943 	if ( xoff<0 ) xoff = 0;
1944     } else if ( sbt==et_sb_up ) {
1945 	if ( st->xoff_left>st->nw ) xoff = st->xoff_left-st->nw; else xoff = 0;
1946     } else if ( sbt==et_sb_down ) {
1947 	if ( st->xoff_left + st->nw + st->g.inner.width >= st->li.xmax )
1948 	    xoff = st->li.xmax - st->g.inner.width;
1949 	else
1950 	    xoff += st->nw;
1951     } else if ( sbt==et_sb_uppage ) {
1952 	int page = (3*g->inner.width)/4;
1953 	xoff = st->xoff_left - page;
1954 	if ( xoff<0 ) xoff=0;
1955     } else if ( sbt==et_sb_downpage ) {
1956 	int page = (3*g->inner.width)/4;
1957 	xoff = st->xoff_left + page;
1958 	if ( xoff + st->g.inner.width >= st->li.xmax )
1959 	    xoff = st->li.xmax - st->g.inner.width;
1960     } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
1961 	xoff = event->u.control.u.sb.pos;
1962     }
1963     if ( xoff + st->g.inner.width >= st->li.xmax )
1964 	xoff = st->li.xmax - st->g.inner.width;
1965     if ( xoff<0 ) xoff = 0;
1966     if ( st->xoff_left!=xoff ) {
1967 	st->xoff_left = xoff;
1968 	GScrollBarSetPos(&st->hsb->g,xoff);
1969 	_ggadget_redraw(&st->g);
1970     }
1971 return( true );
1972 }
1973 
SFTextFieldSetDesiredSize(GGadget * g,GRect * outer,GRect * inner)1974 static void SFTextFieldSetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
1975     SFTextArea *gt = (SFTextArea *) g;
1976 
1977     if ( outer!=NULL ) {
1978 	g->desired_width = outer->width;
1979 	g->desired_height = outer->height;
1980     } else if ( inner!=NULL ) {
1981 	int bp = GBoxBorderWidth(g->base,g->box);
1982 	int extra=0;
1983 	g->desired_width = inner->width + 2*bp + extra;
1984 	g->desired_height = inner->height + 2*bp;
1985 	if ( gt->multi_line ) {
1986 	    int sbadd = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width) +
1987 		    GDrawPointsToPixels(gt->g.base,1);
1988 	    g->desired_width += sbadd;
1989 	    if ( !gt->li.wrap )
1990 		g->desired_height += sbadd;
1991 	}
1992     }
1993 }
1994 
SFTextFieldGetDesiredSize(GGadget * g,GRect * outer,GRect * inner)1995 static void SFTextFieldGetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
1996     SFTextArea *gt = (SFTextArea *) g;
1997     int width=0, height;
1998     int extra=0;
1999     int bp = GBoxBorderWidth(g->base,g->box);
2000 
2001     width = GGadgetScale(GDrawPointsToPixels(gt->g.base,80));
2002     height = gt->multi_line? 4*gt->fh:gt->fh;
2003 
2004     if ( g->desired_width>extra+2*bp ) width = g->desired_width - extra - 2*bp;
2005     if ( g->desired_height>2*bp ) height = g->desired_height - 2*bp;
2006 
2007     if ( gt->multi_line ) {
2008 	int sbadd = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width) +
2009 		GDrawPointsToPixels(gt->g.base,1);
2010 	width += sbadd;
2011 	if ( !gt->li.wrap )
2012 	    height += sbadd;
2013     }
2014 
2015     if ( inner!=NULL ) {
2016 	inner->x = inner->y = 0;
2017 	inner->width = width;
2018 	inner->height = height;
2019     }
2020     if ( outer!=NULL ) {
2021 	outer->x = outer->y = 0;
2022 	outer->width = width + extra + 2*bp;
2023 	outer->height = height + 2*bp;
2024     }
2025 }
2026 
SFtextfield_FillsWindow(GGadget * g)2027 static int SFtextfield_FillsWindow(GGadget *g) {
2028 return( ((SFTextArea *) g)->multi_line && g->prev==NULL &&
2029 	(_GWidgetGetGadgets(g->base)==g ||
2030 	 _GWidgetGetGadgets(g->base)==(GGadget *) ((SFTextArea *) g)->vsb ||
2031 	 _GWidgetGetGadgets(g->base)==(GGadget *) ((SFTextArea *) g)->hsb ));
2032 }
2033 
2034 struct gfuncs sftextarea_funcs = {
2035     0,
2036     sizeof(struct gfuncs),
2037 
2038     sftextarea_expose,
2039     sftextarea_mouse,
2040     sftextarea_key,
2041     _sftextarea_editcmd,
2042     sftextarea_focus,
2043     sftextarea_timer,
2044     sftextarea_sel,
2045 
2046     sftextarea_redraw,
2047     sftextarea_move,
2048     sftextarea_resize,
2049     sftextarea_setvisible,
2050     sftextarea_setenabled,
2051     sftextarea_getsize,
2052     _ggadget_getinnersize,
2053 
2054     sftextarea_destroy,
2055 
2056     SFTextAreaSetTitle,
2057     _SFTextAreaGetTitle,
2058     NULL,
2059     NULL,
2060     NULL,
2061     SFTextAreaSetFont,
2062     SFTextAreaGetFont,
2063 
2064     NULL,
2065     NULL,
2066     NULL,
2067     NULL,
2068     NULL,
2069     NULL,
2070     NULL,
2071     NULL,
2072     NULL,
2073     NULL,
2074     NULL,
2075 
2076     SFTextFieldGetDesiredSize,
2077     SFTextFieldSetDesiredSize,
2078     SFtextfield_FillsWindow,
2079     NULL
2080 };
2081 
SFTextAreaInit()2082 static void SFTextAreaInit() {
2083     FontRequest rq;
2084 
2085     GGadgetInit();
2086     GDrawDecomposeFont(_ggadget_default_font,&rq);
2087     rq.utf8_family_name = MONO_UI_FAMILIES;
2088     sftextarea_font = GDrawInstanciateFont(NULL,&rq);
2089     sftextarea_font = GResourceFindFont("SFTextArea.Font",sftextarea_font);
2090     _GGadgetCopyDefaultBox(&sftextarea_box);
2091     sftextarea_box.padding = 3;
2092     sftextarea_box.flags = box_active_border_inner;
2093     sftextarea_font = _GGadgetInitDefaultBox("SFTextArea.",&sftextarea_box,sftextarea_font);
2094     sftextarea_inited = true;
2095 }
2096 
SFTextAreaAddVSb(SFTextArea * st)2097 static void SFTextAreaAddVSb(SFTextArea *st) {
2098     GGadgetData gd;
2099 
2100     memset(&gd,'\0',sizeof(gd));
2101     gd.pos.y = st->g.r.y; gd.pos.height = st->g.r.height;
2102     gd.pos.width = GDrawPointsToPixels(st->g.base,_GScrollBar_Width);
2103     gd.pos.x = st->g.r.x+st->g.r.width - gd.pos.width;
2104     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
2105     gd.handle_controlevent = sftextarea_vscroll;
2106     st->vsb = (GScrollBar *) GScrollBarCreate(st->g.base,&gd,st);
2107     st->vsb->g.contained = true;
2108 
2109     gd.pos.width += GDrawPointsToPixels(st->g.base,1);
2110     st->g.r.width -= gd.pos.width;
2111     st->g.inner.width -= gd.pos.width;
2112 }
2113 
SFTextAreaAddHSb(SFTextArea * st)2114 static void SFTextAreaAddHSb(SFTextArea *st) {
2115     GGadgetData gd;
2116 
2117     memset(&gd,'\0',sizeof(gd));
2118     gd.pos.x = st->g.r.x; gd.pos.width = st->g.r.width;
2119     gd.pos.height = GDrawPointsToPixels(st->g.base,_GScrollBar_Width);
2120     gd.pos.y = st->g.r.y+st->g.r.height - gd.pos.height;
2121     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
2122     gd.handle_controlevent = sftextarea_hscroll;
2123     st->hsb = (GScrollBar *) GScrollBarCreate(st->g.base,&gd,st);
2124     st->hsb->g.contained = true;
2125 
2126     gd.pos.height += GDrawPointsToPixels(st->g.base,1);
2127     st->g.r.height -= gd.pos.height;
2128     st->g.inner.height -= gd.pos.height;
2129     if ( st->vsb!=NULL ) {
2130 	st->vsb->g.r.height -= gd.pos.height;
2131 	st->vsb->g.inner.height -= gd.pos.height;
2132     }
2133 }
2134 
SFTextAreaFit(SFTextArea * st)2135 static void SFTextAreaFit(SFTextArea *st) {
2136     GTextBounds bounds;
2137     int as=0, ds, ld, fh=0, temp;
2138     GRect needed;
2139     int extra=0;
2140 
2141     needed.x = needed.y = 0;
2142     needed.width = needed.height = 1;
2143 
2144     { /* This doesn't mean much of anything */
2145 	FontInstance *old = GDrawSetFont(st->g.base,st->font);
2146 	(void) GDrawGetTextBounds(st->g.base,st->li.text, -1, &bounds);
2147 	GDrawWindowFontMetrics(st->g.base,st->font,&as, &ds, &ld);
2148 	if ( as<bounds.as ) as = bounds.as;
2149 	if ( ds<bounds.ds ) ds = bounds.ds;
2150 	st->fh = fh = as+ds;
2151 	st->as = as;
2152 	st->nw = 6;
2153 	GDrawSetFont(st->g.base,old);
2154     }
2155 
2156     temp = GGadgetScale(GDrawPointsToPixels(st->g.base,80))+extra;
2157 
2158     if ( st->g.r.width==0 || st->g.r.height==0 ) {
2159 	int bp = GBoxBorderWidth(st->g.base,st->g.box);
2160 	needed.x = needed.y = 0;
2161 	needed.width = temp;
2162 	needed.height = st->multi_line? 4*fh:fh;
2163 	_ggadgetFigureSize(st->g.base,st->g.box,&needed,false);
2164 	if ( st->g.r.width==0 ) {
2165 	    st->g.r.width = needed.width;
2166 	    st->g.inner.width = temp-extra;
2167 	    st->g.inner.x = st->g.r.x + (needed.width-temp)/2;
2168 	} else {
2169 	    st->g.inner.x = st->g.r.x + bp;
2170 	    st->g.inner.width = st->g.r.width - 2*bp;
2171 	}
2172 	if ( st->g.r.height==0 ) {
2173 	    st->g.r.height = needed.height;
2174 	    st->g.inner.height = st->multi_line? 4*fh:fh;
2175 	    st->g.inner.y = st->g.r.y + (needed.height-st->g.inner.height)/2;
2176 	} else {
2177 	    st->g.inner.y = st->g.r.y + bp;
2178 	    st->g.inner.height = st->g.r.height - 2*bp;
2179 	}
2180 	if ( st->multi_line ) {
2181 	    int sbadd = GDrawPointsToPixels(st->g.base,_GScrollBar_Width) +
2182 		    GDrawPointsToPixels(st->g.base,1);
2183 	    {
2184 		st->g.r.width += sbadd;
2185 		st->g.inner.width += sbadd;
2186 	    }
2187 	    if ( !st->li.wrap ) {
2188 		st->g.r.height += sbadd;
2189 		st->g.inner.height += sbadd;
2190 	    }
2191 	}
2192     } else {
2193 	int bp = GBoxBorderWidth(st->g.base,st->g.box);
2194 	st->g.inner = st->g.r;
2195 	st->g.inner.x += bp; st->g.inner.y += bp;
2196 	st->g.inner.width -= 2*bp-extra; st->g.inner.height -= 2*bp;
2197     }
2198     if ( st->multi_line ) {
2199 	SFTextAreaAddVSb(st);
2200 	if ( !st->li.wrap )
2201 	    SFTextAreaAddHSb(st);
2202     }
2203 }
2204 
_SFTextAreaCreate(SFTextArea * st,struct gwindow * base,GGadgetData * gd,void * data,GBox * def)2205 static SFTextArea *_SFTextAreaCreate(SFTextArea *st, struct gwindow *base, GGadgetData *gd,void *data, GBox *def) {
2206 
2207     if ( !sftextarea_inited )
2208 	SFTextAreaInit();
2209     st->g.funcs = &sftextarea_funcs;
2210     _GGadget_Create(&st->g,base,gd,data,def);
2211 
2212     st->g.takes_input = true; st->g.takes_keyboard = true; st->g.focusable = true;
2213     if ( gd->label!=NULL ) {
2214 	if ( gd->label->text_in_resource )	/* This one use of GStringGetResource is ligit */
2215 	    st->li.text = u_copy((unichar_t *) GStringGetResource((intpt) gd->label->text,&st->g.mnemonic));
2216 	else if ( gd->label->text_is_1byte )
2217 	    st->li.text = utf82u_copy((char *) gd->label->text);
2218 	else
2219 	    st->li.text = u_copy(gd->label->text);
2220 	st->sel_start = st->sel_end = st->sel_base = u_strlen(st->li.text);
2221     }
2222     if ( st->li.text==NULL )
2223 	st->li.text = calloc(1,sizeof(unichar_t));
2224     st->font = sftextarea_font;
2225     if ( gd->label!=NULL && gd->label->font!=NULL )
2226 	st->font = gd->label->font;
2227     SFTextAreaFit(st);
2228     _GGadget_FinalPosition(&st->g,base,gd);
2229     SFTextAreaRefigureLines(st,0,-1);
2230 
2231     if ( gd->flags & gg_group_end )
2232 	_GGadgetCloseGroup(&st->g);
2233     GWidgetIndicateFocusGadget(&st->g);
2234     if ( gd->flags & gg_text_xim )
2235 	st->gic = GDrawCreateInputContext(base,gic_overspot|gic_orlesser);
2236 return( st );
2237 }
2238 
SFTextAreaCreate(struct gwindow * base,GGadgetData * gd,void * data)2239 GGadget *SFTextAreaCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2240     SFTextArea *st = calloc(1,sizeof(SFTextArea));
2241     st->multi_line = true;
2242     st->accepts_returns = true;
2243     st->li.wrap = true;
2244     _SFTextAreaCreate(st,base,gd,data,&sftextarea_box);
2245     st->li.dpi = 100;
2246 
2247 return( &st->g );
2248 }
2249 
SFTF_NormalizeStartEnd(SFTextArea * st,int start,int * _end)2250 static int SFTF_NormalizeStartEnd(SFTextArea *st, int start, int *_end) {
2251     int end = *_end;
2252     int len = u_strlen(st->li.text);
2253 
2254     if ( st->li.generated==NULL ) {
2255 	start = 0;
2256 	end = len;
2257     } else if ( start==-1 ) {
2258 	start = st->sel_start;
2259 	end = st->sel_end;
2260     } else if ( end==-1 )
2261 	end = len;
2262     if ( end>len ) end = len;
2263     if ( start<0 ) start = 0;
2264     if ( start>end ) start = end;
2265     *_end = end;
2266 return( start );
2267 }
2268 
SFTFMetaChangeCleanup(SFTextArea * st,int start,int end)2269 static void SFTFMetaChangeCleanup(SFTextArea *st,int start, int end) {
2270     LI_fontlistmergecheck(&st->li);
2271     SFTextAreaRefigureLines(st, start,end);
2272     GDrawRequestExpose(st->g.base,&st->g.inner,false);
2273     if ( st->changefontcallback != NULL )
2274 	STChangeCheck(st);
2275 }
2276 
SFTFSetFont(GGadget * g,int start,int end,SplineFont * sf)2277 int SFTFSetFont(GGadget *g, int start, int end, SplineFont *sf) {
2278     SFTextArea *st = (SFTextArea *) g;
2279     FontData *cur;
2280     struct fontlist *fl;
2281 
2282     start = SFTF_NormalizeStartEnd(st, start, &end);
2283     fl = LI_BreakFontList(&st->li,start,end);
2284     while ( fl!=NULL && fl->end<=end ) {
2285 	if ( fl->fd->sf!=sf ) {
2286 	    cur = LI_FindFontData(&st->li, sf, fl->fd->layer, fl->fd->fonttype, fl->fd->pointsize, fl->fd->antialias);
2287 	    if ( cur!=NULL )
2288 		fl->fd = cur;
2289 	}
2290 	fl = fl->next;
2291     }
2292 
2293     SFTFMetaChangeCleanup(st,start,end);
2294 return( true );
2295 }
2296 
SFTFSetFontType(GGadget * g,int start,int end,enum sftf_fonttype fonttype)2297 int SFTFSetFontType(GGadget *g, int start, int end, enum sftf_fonttype fonttype) {
2298     SFTextArea *st = (SFTextArea *) g;
2299     FontData *cur;
2300     struct fontlist *fl;
2301 
2302     start = SFTF_NormalizeStartEnd(st, start, &end);
2303     fl = LI_BreakFontList(&st->li,start,end);
2304     while ( fl!=NULL && fl->end<=end ) {
2305 	if ( fl->fd->fonttype!=fonttype ) {
2306 	    cur = LI_FindFontData(&st->li, fl->fd->sf, fl->fd->layer, fonttype, fl->fd->pointsize, fl->fd->antialias);
2307 	    if ( cur!=NULL )
2308 		fl->fd = cur;
2309 	}
2310 	fl = fl->next;
2311     }
2312 
2313     SFTFMetaChangeCleanup(st,start,end);
2314 return( true );
2315 }
2316 
SFTFSetSize(GGadget * g,int start,int end,int pointsize)2317 int SFTFSetSize(GGadget *g, int start, int end, int pointsize) {
2318     SFTextArea *st = (SFTextArea *) g;
2319     FontData *cur;
2320     struct fontlist *fl;
2321 
2322     if ( st->li.generated==NULL )
2323 return( false );
2324     start = SFTF_NormalizeStartEnd(st, start, &end);
2325     fl = LI_BreakFontList(&st->li,start,end);
2326     while ( fl!=NULL && fl->end<=end ) {
2327 	if ( fl->fd->pointsize!=pointsize ) {
2328 	    cur = LI_FindFontData(&st->li, fl->fd->sf, fl->fd->layer, fl->fd->fonttype, pointsize, fl->fd->antialias);
2329 	    if ( cur!=NULL )
2330 		fl->fd = cur;
2331 	}
2332 	fl = fl->next;
2333     }
2334 
2335     SFTFMetaChangeCleanup(st,start,end);
2336 return( true );
2337 }
2338 
SFTFSetAntiAlias(GGadget * g,int start,int end,int antialias)2339 int SFTFSetAntiAlias(GGadget *g, int start, int end, int antialias) {
2340     SFTextArea *st = (SFTextArea *) g;
2341     FontData *cur;
2342     struct fontlist *fl;
2343 
2344     start = SFTF_NormalizeStartEnd(st, start, &end);
2345     fl = LI_BreakFontList(&st->li,start,end);
2346     while ( fl!=NULL && fl->end<=end ) {
2347 	if ( fl->fd->antialias!=antialias ) {
2348 	    cur = LI_FindFontData(&st->li, fl->fd->sf, fl->fd->layer, fl->fd->fonttype, fl->fd->pointsize, antialias);
2349 	    if ( cur!=NULL )
2350 		fl->fd = cur;
2351 	}
2352 	fl = fl->next;
2353     }
2354 
2355     SFTFMetaChangeCleanup(st,start,end);
2356 return( true );
2357 }
2358 
SFTFSetScriptLang(GGadget * g,int start,int end,uint32 script,uint32 lang)2359 int SFTFSetScriptLang(GGadget *g, int start, int end, uint32 script, uint32 lang) {
2360     SFTextArea *st = (SFTextArea *) g;
2361     struct fontlist *fl;
2362 
2363     start = SFTF_NormalizeStartEnd(st, start, &end);
2364     fl = LI_BreakFontList(&st->li,start,end);
2365     while ( fl!=NULL && fl->end<=end ) {
2366 	if ( fl->script != script ) {
2367 	    free(fl->feats);
2368 	    fl->feats = LI_TagsCopy(StdFeaturesOfScript(script));
2369 	}
2370 	fl->script = script;
2371 	fl->lang = lang;
2372 	fl = fl->next;
2373     }
2374 
2375     SFTFMetaChangeCleanup(st,start,end);
2376 return( true );
2377 }
2378 
SFTFSetFeatures(GGadget * g,int start,int end,uint32 * features)2379 int SFTFSetFeatures(GGadget *g, int start, int end, uint32 *features) {
2380     SFTextArea *st = (SFTextArea *) g;
2381     struct fontlist *fl;
2382 
2383     start = SFTF_NormalizeStartEnd(st, start, &end);
2384     fl = LI_BreakFontList(&st->li,start,end);
2385     while ( fl!=NULL && fl->end<=end ) {
2386 	free(fl->feats);
2387 	fl->feats = LI_TagsCopy(features);
2388 	fl = fl->next;
2389     }
2390 
2391     SFTFMetaChangeCleanup(st,start,end);
2392 return( true );
2393 }
2394 
SFTFRegisterCallback(GGadget * g,void * cbcontext,void (* changefontcallback)(void *,SplineFont *,enum sftf_fonttype,int size,int aa,uint32 script,uint32 lang,uint32 * feats))2395 void SFTFRegisterCallback(GGadget *g, void *cbcontext,
2396 	void (*changefontcallback)(void *,SplineFont *,enum sftf_fonttype,int size,int aa, uint32 script, uint32 lang, uint32 *feats)) {
2397     SFTextArea *st = (SFTextArea *) g;
2398 
2399     st->cbcontext = cbcontext;
2400     st->changefontcallback = changefontcallback;
2401 }
2402 
SFTFProvokeCallback(GGadget * g)2403 void SFTFProvokeCallback(GGadget *g) {
2404     SFTextArea *st = (SFTextArea *) g;
2405     STChangeCheck(st);
2406 }
2407 
SFTFSetDPI(GGadget * g,float dpi)2408 void SFTFSetDPI(GGadget *g, float dpi) {
2409     SFTextArea *st = (SFTextArea *) g;
2410     FontData *fd;
2411 
2412     if ( st->li.dpi == dpi )
2413 return;
2414     st->li.dpi = dpi;
2415     for ( fd = st->li.generated; fd!=NULL; fd=fd->next ) {
2416 	LI_RegenFontData(&st->li,fd);
2417     }
2418     SFTextAreaRefigureLines(st,0,-1);
2419     SFTextAreaShow(&st->g,st->sel_start);	/* Refigure scrollbars for new size */
2420 	    /* And force an expose event */
2421 }
2422 
SFTFGetDPI(GGadget * g)2423 float SFTFGetDPI(GGadget *g) {
2424     SFTextArea *st = (SFTextArea *) g;
2425 
2426 return( st->li.dpi );
2427 }
2428 
SFTFRefreshFonts(GGadget * g)2429 void SFTFRefreshFonts(GGadget *g) {
2430     SFTextArea *st = (SFTextArea *) g;
2431     FontData *fd;
2432     struct sfmaps *sfmaps;
2433 
2434     /* First regenerate the EncMaps. Glyphs might have been added or removed */
2435     for ( sfmaps = st->li.sfmaps; sfmaps!=NULL; sfmaps = sfmaps->next ) {
2436 	EncMapFree(sfmaps->map);
2437 	SplineCharFree(sfmaps->fake_notdef);
2438 	sfmaps->fake_notdef = NULL;
2439 	SFMapFill(sfmaps,sfmaps->sf);
2440     }
2441 
2442     /* Then free all old generated bitmaps */
2443     /* need to do this first because otherwise we might reuse a freetype context */
2444     for ( fd = st->li.generated; fd!=NULL; fd=fd->next ) {
2445 	if ( fd->depends_on )
2446 	    fd->bdf->freetype_context = NULL;
2447 	if ( fd->fonttype!=sftf_bitmap ) {
2448 	    BDFFontFree(fd->bdf);
2449 	    fd->bdf = NULL;
2450 	}
2451     }
2452     for ( fd = st->li.generated; fd!=NULL; fd=fd->next ) {
2453 	LI_RegenFontData(&st->li,fd);
2454     }
2455     LayoutInfoRefigureLines(&st->li,0,-1,st->g.inner.width);
2456     SFTextAreaShow(&st->g,st->sel_start);	/* Refigure scrollbars for new size */
2457 	    /* And force an expose event */
2458 }
2459