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