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