Line data Source code
1 : /* Copyright (C) 2000-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 "gdraw.h"
28 : #include "gkeysym.h"
29 : #include "gresource.h"
30 : #include "gwidget.h"
31 : #include "ggadgetP.h"
32 : #include "ustring.h"
33 : #include "utype.h"
34 : #include <math.h>
35 :
36 : extern void (*_GDraw_InsCharHook)(GDisplay *,unichar_t);
37 :
38 : GBox _GGadget_gtextfield_box = GBOX_EMPTY; /* Don't initialize here */
39 : static GBox glistfield_box = GBOX_EMPTY; /* Don't initialize here */
40 : static GBox glistfieldmenu_box = GBOX_EMPTY; /* Don't initialize here */
41 : static GBox gnumericfield_box = GBOX_EMPTY; /* Don't initialize here */
42 : static GBox gnumericfieldspinner_box = GBOX_EMPTY; /* Don't initialize here */
43 : FontInstance *_gtextfield_font = NULL;
44 : static int gtextfield_inited = false;
45 :
46 : static GResInfo listfield_ri, listfieldmenu_ri, numericfield_ri, numericfieldspinner_ri;
47 : static GTextInfo text_lab[] = {
48 : { (unichar_t *) "Disabled", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
49 : { (unichar_t *) "Enabled" , NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' }
50 : };
51 : static GTextInfo list_choices[] = {
52 : { (unichar_t *) "1", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
53 : { (unichar_t *) "2", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
54 : { (unichar_t *) "3", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
55 : GTEXTINFO_EMPTY
56 : };
57 : static GGadgetCreateData text_gcd[] = {
58 : { GTextFieldCreate, { { 0, 0, 70, 0 }, NULL, 0, 0, 0, 0, 0, &text_lab[0], { NULL }, gg_visible, NULL, NULL }, NULL, NULL },
59 : { GTextFieldCreate, { { 0, 0, 70, 0 }, NULL, 0, 0, 0, 0, 0, &text_lab[1], { NULL }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL }
60 : };
61 : static GGadgetCreateData *tarray[] = { GCD_Glue, &text_gcd[0], GCD_Glue, &text_gcd[1], GCD_Glue, NULL, NULL };
62 : static GGadgetCreateData textbox =
63 : { GHVGroupCreate, { { 2, 2, 0, 0 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) tarray }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL };
64 : static GResInfo gtextfield_ri = {
65 : &listfield_ri, &ggadget_ri,NULL, NULL,
66 : &_GGadget_gtextfield_box,
67 : &_gtextfield_font,
68 : &textbox,
69 : NULL,
70 : N_("Text Field"),
71 : N_("Text Field"),
72 : "GTextField",
73 : "Gdraw",
74 : false,
75 : omf_font|omf_padding,
76 : NULL,
77 : GBOX_EMPTY,
78 : NULL,
79 : NULL,
80 : NULL
81 : };
82 : static GGadgetCreateData textlist_gcd[] = {
83 : { GListFieldCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &text_lab[0], { list_choices }, gg_visible, NULL, NULL }, NULL, NULL },
84 : { GListFieldCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &text_lab[1], { list_choices }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL }
85 : };
86 : static GGadgetCreateData *tlarray[] = { GCD_Glue, &textlist_gcd[0], GCD_Glue, &textlist_gcd[1], GCD_Glue, NULL, NULL };
87 : static GGadgetCreateData textlistbox =
88 : { GHVGroupCreate, { { 2, 2, 0, 0 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) tlarray }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL };
89 : static GResInfo listfield_ri = {
90 : &listfieldmenu_ri, >extfield_ri,&listfieldmenu_ri, &listmark_ri,
91 : &glistfield_box,
92 : NULL,
93 : &textlistbox,
94 : NULL,
95 : N_("List Field"),
96 : N_("List Field (Combo Box)"),
97 : "GComboBox",
98 : "Gdraw",
99 : false,
100 : 0,
101 : NULL,
102 : GBOX_EMPTY,
103 : NULL,
104 : NULL,
105 : NULL
106 : };
107 : static GResInfo listfieldmenu_ri = {
108 : &numericfield_ri, &listfield_ri, &listmark_ri,NULL,
109 : &glistfieldmenu_box,
110 : NULL,
111 : &textlistbox,
112 : NULL,
113 : N_("List Field Menu"),
114 : N_("Box surrounding the ListMark in a list field (combobox)"),
115 : "GComboBoxMenu",
116 : "Gdraw",
117 : false,
118 : omf_padding,
119 : NULL,
120 : GBOX_EMPTY,
121 : NULL,
122 : NULL,
123 : NULL
124 : };
125 : static GGadgetCreateData num_gcd[] = {
126 : { GNumericFieldCreate, { { 0, 0, 50, 0 }, NULL, 0, 0, 0, 0, 0, &list_choices[0], { NULL }, gg_visible, NULL, NULL }, NULL, NULL },
127 : { GNumericFieldCreate, { { 0, 0, 50, 0 }, NULL, 0, 0, 0, 0, 0, &list_choices[0], { NULL }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL }
128 : };
129 : static GGadgetCreateData *narray[] = { GCD_Glue, &num_gcd[0], GCD_Glue, &num_gcd[1], GCD_Glue, NULL, NULL };
130 : static GGadgetCreateData numbox =
131 : { GHVGroupCreate, { { 2, 2, 0, 0 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) narray }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL };
132 : static GResInfo numericfield_ri = {
133 : &numericfieldspinner_ri, >extfield_ri,&numericfieldspinner_ri, NULL,
134 : &gnumericfield_box,
135 : NULL,
136 : &numbox,
137 : NULL,
138 : N_("Numeric Field"),
139 : N_("Numeric Field (Spinner)"),
140 : "GNumericField",
141 : "Gdraw",
142 : false,
143 : 0,
144 : NULL,
145 : GBOX_EMPTY,
146 : NULL,
147 : NULL,
148 : NULL
149 : };
150 : static GResInfo numericfieldspinner_ri = {
151 : NULL, &numericfield_ri,NULL, NULL,
152 : &gnumericfieldspinner_box,
153 : NULL,
154 : &numbox,
155 : NULL,
156 : N_("Numeric Field Sign"),
157 : N_("The box around the up/down arrows of a numeric field (spinner)"),
158 : "GNumericFieldSpinner",
159 : "Gdraw",
160 : false,
161 : omf_border_type|omf_border_width|omf_padding,
162 : NULL,
163 : GBOX_EMPTY,
164 : NULL,
165 : NULL,
166 : NULL
167 : };
168 :
169 : static unichar_t nullstr[] = { 0 }, nstr[] = { 'n', 0 },
170 : newlinestr[] = { '\n', 0 }, tabstr[] = { '\t', 0 };
171 :
172 : static void GListFieldSelected(GGadget *g, int i);
173 : static int GTextField_Show(GTextField *gt, int pos);
174 : static void GTPositionGIC(GTextField *gt);
175 :
176 : static void GCompletionDestroy(GCompletionField *gc);
177 : static void GTextFieldComplete(GTextField *gt,int from_tab);
178 : static int GCompletionHandleKey(GTextField *gt,GEvent *event);
179 :
180 :
181 0 : static int u2utf8_index(int pos,const char *start) {
182 0 : const char *pt = start;
183 :
184 0 : while ( --pos>=0 )
185 0 : utf8_ildb(&pt);
186 0 : return( pt-start );
187 : }
188 :
189 0 : static int utf82u_index(int pos, const char *start) {
190 0 : int uc = 0;
191 0 : const char *end = start+pos;
192 :
193 0 : while ( start<end ) {
194 0 : utf8_ildb(&start);
195 0 : ++uc;
196 : }
197 0 : return( uc );
198 : }
199 :
200 0 : static void GTextFieldChanged(GTextField *gt,int src) {
201 : GEvent e;
202 :
203 0 : e.type = et_controlevent;
204 0 : e.w = gt->g.base;
205 0 : e.u.control.subtype = et_textchanged;
206 0 : e.u.control.g = >->g;
207 0 : e.u.control.u.tf_changed.from_pulldown = src;
208 0 : if ( gt->g.handle_controlevent != NULL )
209 0 : (gt->g.handle_controlevent)(>->g,&e);
210 : else
211 0 : GDrawPostEvent(&e);
212 0 : }
213 :
214 0 : static void GTextFieldFocusChanged(GTextField *gt,int gained) {
215 : GEvent e;
216 :
217 0 : if ( (gt->g.box->flags & box_active_border_inner) &&
218 0 : ( gt->g.state==gs_enabled || gt->g.state==gs_active )) {
219 0 : int state = gained?gs_active:gs_enabled;
220 0 : if ( state!=gt->g.state ) {
221 0 : gt->g.state = state;
222 0 : GGadgetRedraw((GGadget *) gt);
223 : }
224 : }
225 0 : e.type = et_controlevent;
226 0 : e.w = gt->g.base;
227 0 : e.u.control.subtype = et_textfocuschanged;
228 0 : e.u.control.g = >->g;
229 0 : e.u.control.u.tf_focus.gained_focus = gained;
230 0 : if ( gt->g.handle_controlevent != NULL )
231 0 : (gt->g.handle_controlevent)(>->g,&e);
232 : else
233 0 : GDrawPostEvent(&e);
234 0 : }
235 :
236 0 : static void GTextFieldPangoRefigureLines(GTextField *gt, int start_of_change) {
237 : char *utf8_text, *pt, *ept;
238 : unichar_t *upt, *uept;
239 : int i, uc;
240 : GRect size;
241 :
242 0 : free(gt->utf8_text);
243 0 : if ( gt->lines8==NULL ) {
244 0 : gt->lines8 = malloc(gt->lmax*sizeof(int32));
245 0 : gt->lines8[0] = 0;
246 0 : gt->lines8[1] = -1;
247 : }
248 :
249 0 : if ( gt->password ) {
250 0 : int cnt = u_strlen(gt->text);
251 0 : utf8_text = malloc(cnt+1);
252 0 : memset(utf8_text,'*',cnt);
253 0 : utf8_text[cnt] = '\0';
254 : } else
255 0 : utf8_text = u2utf8_copy(gt->text);
256 0 : gt->utf8_text = utf8_text;
257 0 : GDrawLayoutInit(gt->g.base,utf8_text,-1,NULL);
258 :
259 0 : if ( !gt->multi_line ) {
260 0 : GDrawLayoutExtents(gt->g.base,&size);
261 0 : gt->xmax = size.width;
262 0 : return;
263 : }
264 :
265 0 : if ( !gt->wrap ) {
266 0 : pt = utf8_text;
267 0 : i=0;
268 0 : while ( ( ept = strchr(pt,'\n'))!=NULL ) {
269 0 : if ( i>=gt->lmax ) {
270 0 : gt->lines8 = realloc(gt->lines8,(gt->lmax+=10)*sizeof(int32));
271 0 : gt->lines = realloc(gt->lines,gt->lmax*sizeof(int32));
272 : }
273 0 : gt->lines8[i++] = pt-utf8_text;
274 0 : pt = ept+1;
275 : }
276 0 : if ( i>=gt->lmax ) {
277 0 : gt->lines8 = realloc(gt->lines8,(gt->lmax+=10)*sizeof(int32));
278 0 : gt->lines = realloc(gt->lines,gt->lmax*sizeof(int32));
279 : }
280 0 : gt->lines8[i++] = pt-utf8_text;
281 :
282 0 : upt = gt->text;
283 0 : i = 0;
284 0 : while ( ( uept = u_strchr(upt,'\n'))!=NULL ) {
285 0 : gt->lines[i++] = upt-gt->text;
286 0 : upt = uept+1;
287 : }
288 0 : gt->lines[i++] = upt-gt->text;
289 : } else {
290 : int lcnt;
291 0 : GDrawLayoutSetWidth(gt->g.base,gt->g.inner.width);
292 0 : lcnt = GDrawLayoutLineCount(gt->g.base);
293 0 : if ( lcnt+2>=gt->lmax ) {
294 0 : gt->lines8 = realloc(gt->lines8,(gt->lmax=lcnt+10)*sizeof(int32));
295 0 : gt->lines = realloc(gt->lines,gt->lmax*sizeof(int32));
296 : }
297 0 : pt = utf8_text; uc=0;
298 0 : for ( i=0; i<lcnt; ++i ) {
299 0 : gt->lines8[i] = GDrawLayoutLineStart(gt->g.base,i);
300 0 : ept = utf8_text + gt->lines8[i];
301 0 : while ( pt<ept ) {
302 0 : ++uc;
303 0 : utf8_ildb((const char **) &pt);
304 : }
305 0 : gt->lines[i] = uc;
306 : }
307 0 : if ( i==0 ) {
308 0 : gt->lines8[i] = strlen(utf8_text);
309 0 : gt->lines[i] = u_strlen(gt->text);
310 : } else {
311 0 : gt->lines8[i] = gt->lines8[i-1] + strlen( utf8_text + gt->lines8[i-1]);
312 0 : gt->lines [i] = gt->lines [i-1] + u_strlen( gt->text + gt->lines [i-1]);
313 : }
314 : }
315 0 : if ( gt->lcnt!=i ) {
316 0 : gt->lcnt = i;
317 0 : if ( gt->vsb!=NULL )
318 0 : GScrollBarSetBounds(>->vsb->g,0,gt->lcnt,
319 0 : gt->g.inner.height<gt->fh? 1 : gt->g.inner.height/gt->fh);
320 0 : if ( gt->loff_top+gt->g.inner.height/gt->fh>gt->lcnt ) {
321 0 : gt->loff_top = gt->lcnt-gt->g.inner.height/gt->fh;
322 0 : if ( gt->loff_top<0 ) gt->loff_top = 0;
323 0 : if ( gt->vsb!=NULL )
324 0 : GScrollBarSetPos(>->vsb->g,gt->loff_top);
325 : }
326 : }
327 0 : if ( i>=gt->lmax )
328 0 : gt->lines = realloc(gt->lines,(gt->lmax+=10)*sizeof(int32));
329 0 : gt->lines8[i] = -1;
330 0 : gt->lines[i++] = -1;
331 :
332 0 : GDrawLayoutExtents(gt->g.base,&size);
333 0 : gt->xmax = size.width;
334 :
335 0 : if ( gt->hsb!=NULL ) {
336 0 : GScrollBarSetBounds(>->hsb->g,0,gt->xmax,gt->g.inner.width);
337 : }
338 0 : GDrawLayoutSetWidth(gt->g.base,-1);
339 : }
340 :
341 0 : static void GTextFieldRefigureLines(GTextField *gt, int start_of_change) {
342 0 : GDrawSetFont(gt->g.base,gt->font);
343 0 : if ( gt->lines==NULL ) {
344 0 : gt->lines = malloc(10*sizeof(int32));
345 0 : gt->lines[0] = 0;
346 0 : gt->lines[1] = -1;
347 0 : gt->lmax = 10;
348 0 : gt->lcnt = 1;
349 0 : if ( gt->vsb!=NULL )
350 0 : GScrollBarSetBounds(>->vsb->g,0,gt->lcnt,
351 0 : gt->g.inner.height<gt->fh ? 1 : gt->g.inner.height/gt->fh);
352 : }
353 :
354 0 : GTextFieldPangoRefigureLines(gt,start_of_change);
355 0 : return;
356 : }
357 :
358 0 : static void _GTextFieldReplace(GTextField *gt, const unichar_t *str) {
359 0 : unichar_t *old = gt->oldtext;
360 0 : unichar_t *new = malloc((u_strlen(gt->text)-(gt->sel_end-gt->sel_start) + u_strlen(str)+1)*sizeof(unichar_t));
361 :
362 0 : gt->oldtext = gt->text;
363 0 : gt->sel_oldstart = gt->sel_start;
364 0 : gt->sel_oldend = gt->sel_end;
365 0 : gt->sel_oldbase = gt->sel_base;
366 :
367 0 : u_strncpy(new,gt->text,gt->sel_start);
368 0 : u_strcpy(new+gt->sel_start,str);
369 0 : gt->sel_start = u_strlen(new);
370 0 : u_strcpy(new+gt->sel_start,gt->text+gt->sel_end);
371 0 : gt->text = new;
372 0 : gt->sel_end = gt->sel_base = gt->sel_start;
373 0 : free(old);
374 :
375 0 : GTextFieldRefigureLines(gt,gt->sel_oldstart);
376 0 : }
377 :
378 0 : static void GTextField_Replace(GTextField *gt, const unichar_t *str) {
379 0 : _GTextFieldReplace(gt,str);
380 0 : GTextField_Show(gt,gt->sel_start);
381 0 : }
382 :
383 0 : static int GTextFieldFindLine(GTextField *gt, int pos) {
384 : int i;
385 0 : for ( i=0; gt->lines[i+1]!=-1; ++i )
386 0 : if ( pos<gt->lines[i+1])
387 0 : break;
388 0 : return( i );
389 : }
390 :
391 0 : static unichar_t *GTextFieldGetPtFromPos(GTextField *gt,int i,int xpos) {
392 : int ll;
393 : unichar_t *end;
394 :
395 0 : ll = gt->lines[i+1]==-1?-1:gt->lines[i+1]-gt->lines[i]-1;
396 : int index8, uc;
397 0 : if ( gt->lines8[i+1]==-1 )
398 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text + gt->lines8[i],-1,NULL);
399 : else {
400 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text + gt->lines8[i], gt->lines8[i+1]-gt->lines8[i], NULL);
401 : }
402 0 : index8 = GDrawLayoutXYToIndex(gt->g.base,xpos-gt->g.inner.x+gt->xoff_left,0);
403 0 : uc = utf82u_index(index8,gt->utf8_text + gt->lines8[i]);
404 0 : end = gt->text + gt->lines[i] + uc;
405 0 : return( end );
406 : }
407 :
408 0 : static int GTextField_Show(GTextField *gt, int pos) {
409 : int i, ll, xoff, loff;
410 0 : int refresh=false;
411 0 : GListField *ge = (GListField *) gt;
412 0 : int width = gt->g.inner.width;
413 :
414 0 : if ( gt->listfield || gt->numericfield )
415 0 : width = ge->fieldrect.width - 2*(gt->g.inner.x - gt->g.r.x);
416 :
417 0 : if ( pos < 0 ) pos = 0;
418 0 : if ( pos > u_strlen(gt->text)) pos = u_strlen(gt->text);
419 0 : i = GTextFieldFindLine(gt,pos);
420 :
421 0 : loff = gt->loff_top;
422 0 : if ( gt->lcnt<gt->g.inner.height/gt->fh || loff==0 )
423 0 : loff = 0;
424 0 : if ( i<loff )
425 0 : loff = i;
426 0 : if ( i>=loff+gt->g.inner.height/gt->fh ) {
427 0 : loff = i-(gt->g.inner.height/gt->fh);
428 0 : if ( gt->g.inner.height/gt->fh>2 )
429 0 : ++loff;
430 : }
431 :
432 0 : xoff = gt->xoff_left;
433 0 : if ( gt->lines[i+1]==-1 ) ll = -1; else ll = gt->lines[i+1]-gt->lines[i]-1;
434 : GRect size;
435 0 : if ( gt->lines8[i+1]==-1 ) ll = strlen(gt->utf8_text+gt->lines8[i]); else ll = gt->lines8[i+1]-gt->lines8[i]-1;
436 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[i],ll,NULL);
437 0 : GDrawLayoutExtents(gt->g.base,&size);
438 0 : if ( size.width < width )
439 0 : xoff = 0;
440 : else {
441 0 : int index8 = u2utf8_index(pos- gt->lines8[i],gt->utf8_text + gt->lines8[i]);
442 0 : GDrawLayoutIndexToPos(gt->g.base,index8,&size);
443 0 : if ( size.x + 2*size.width < width )
444 0 : xoff = 0;
445 : else
446 0 : xoff = size.x - (width - size.width)/2;
447 0 : if ( xoff<0 )
448 0 : xoff = 0;
449 : }
450 :
451 0 : if ( xoff!=gt->xoff_left ) {
452 0 : gt->xoff_left = xoff;
453 0 : if ( gt->hsb!=NULL )
454 0 : GScrollBarSetPos(>->hsb->g,xoff);
455 0 : refresh = true;
456 : }
457 0 : if ( loff!=gt->loff_top ) {
458 0 : gt->loff_top = loff;
459 0 : if ( gt->vsb!=NULL )
460 0 : GScrollBarSetPos(>->vsb->g,loff);
461 0 : refresh = true;
462 : }
463 0 : GTPositionGIC(gt);
464 0 : return( refresh );
465 : }
466 :
467 0 : static void *genunicodedata(void *_gt,int32 *len) {
468 0 : GTextField *gt = _gt;
469 : unichar_t *temp;
470 0 : *len = gt->sel_end-gt->sel_start + 1;
471 0 : temp = malloc((*len+2)*sizeof(unichar_t));
472 0 : temp[0] = 0xfeff; /* KDE expects a byte order flag */
473 0 : u_strncpy(temp+1,gt->text+gt->sel_start,gt->sel_end-gt->sel_start);
474 0 : temp[*len+1] = 0;
475 0 : return( temp );
476 : }
477 :
478 0 : static void *genutf8data(void *_gt,int32 *len) {
479 0 : GTextField *gt = _gt;
480 0 : unichar_t *temp =u_copyn(gt->text+gt->sel_start,gt->sel_end-gt->sel_start);
481 0 : char *ret = u2utf8_copy(temp);
482 0 : free(temp);
483 0 : *len = strlen(ret);
484 0 : return( ret );
485 : }
486 :
487 0 : static void *ddgenunicodedata(void *_gt,int32 *len) {
488 0 : void *temp = genunicodedata(_gt,len);
489 0 : GTextField *gt = _gt;
490 0 : _GTextFieldReplace(gt,nullstr);
491 0 : _ggadget_redraw(>->g);
492 0 : return( temp );
493 : }
494 :
495 0 : static void *genlocaldata(void *_gt,int32 *len) {
496 0 : GTextField *gt = _gt;
497 0 : unichar_t *temp =u_copyn(gt->text+gt->sel_start,gt->sel_end-gt->sel_start);
498 0 : char *ret = u2def_copy(temp);
499 0 : free(temp);
500 0 : *len = strlen(ret);
501 0 : return( ret );
502 : }
503 :
504 0 : static void *ddgenlocaldata(void *_gt,int32 *len) {
505 0 : void *temp = genlocaldata(_gt,len);
506 0 : GTextField *gt = _gt;
507 0 : _GTextFieldReplace(gt,nullstr);
508 0 : _ggadget_redraw(>->g);
509 0 : return( temp );
510 : }
511 :
512 0 : static void noop(void *_gt) {
513 0 : }
514 :
515 0 : static void GTextFieldGrabPrimarySelection(GTextField *gt) {
516 0 : int ss = gt->sel_start, se = gt->sel_end;
517 :
518 0 : GDrawGrabSelection(gt->g.base,sn_primary);
519 0 : gt->sel_start = ss; gt->sel_end = se;
520 0 : GDrawAddSelectionType(gt->g.base,sn_primary,"text/plain;charset=ISO-10646-UCS-4",gt,gt->sel_end-gt->sel_start,
521 : sizeof(unichar_t),
522 : genunicodedata,noop);
523 0 : GDrawAddSelectionType(gt->g.base,sn_primary,"UTF8_STRING",gt,gt->sel_end-gt->sel_start,
524 : sizeof(char),
525 : genutf8data,noop);
526 0 : GDrawAddSelectionType(gt->g.base,sn_primary,"text/plain;charset=UTF-8",gt,gt->sel_end-gt->sel_start,
527 : sizeof(char),
528 : genutf8data,noop);
529 0 : GDrawAddSelectionType(gt->g.base,sn_primary,"STRING",gt,gt->sel_end-gt->sel_start,
530 : sizeof(char),
531 : genlocaldata,noop);
532 0 : }
533 :
534 0 : static void GTextFieldGrabDDSelection(GTextField *gt) {
535 :
536 0 : GDrawGrabSelection(gt->g.base,sn_drag_and_drop);
537 0 : GDrawAddSelectionType(gt->g.base,sn_drag_and_drop,"text/plain;charset=ISO-10646-UCS-4",gt,gt->sel_end-gt->sel_start,
538 : sizeof(unichar_t),
539 : ddgenunicodedata,noop);
540 0 : GDrawAddSelectionType(gt->g.base,sn_drag_and_drop,"STRING",gt,gt->sel_end-gt->sel_start,sizeof(char),
541 : ddgenlocaldata,noop);
542 0 : }
543 :
544 0 : static void GTextFieldGrabSelection(GTextField *gt, enum selnames sel ) {
545 :
546 0 : if ( gt->sel_start!=gt->sel_end ) {
547 : unichar_t *temp;
548 : char *ctemp, *ctemp2;
549 : int i;
550 : uint16 *u2temp;
551 :
552 0 : GDrawGrabSelection(gt->g.base,sel);
553 0 : temp = malloc((gt->sel_end-gt->sel_start + 2)*sizeof(unichar_t));
554 0 : temp[0] = 0xfeff; /* KDE expects a byte order flag */
555 0 : u_strncpy(temp+1,gt->text+gt->sel_start,gt->sel_end-gt->sel_start);
556 0 : ctemp = u2utf8_copy(temp+1);
557 0 : ctemp2 = u2def_copy(temp+1);
558 0 : GDrawAddSelectionType(gt->g.base,sel,"text/plain;charset=ISO-10646-UCS-4",temp,u_strlen(temp),
559 : sizeof(unichar_t),
560 : NULL,NULL);
561 0 : u2temp = malloc((gt->sel_end-gt->sel_start + 2)*sizeof(uint16));
562 0 : for ( i=0; temp[i]!=0; ++i )
563 0 : u2temp[i] = temp[i];
564 0 : u2temp[i] = 0;
565 0 : GDrawAddSelectionType(gt->g.base,sel,"text/plain;charset=ISO-10646-UCS-2",u2temp,u_strlen(temp),
566 : 2,
567 : NULL,NULL);
568 0 : GDrawAddSelectionType(gt->g.base,sel,"UTF8_STRING",copy(ctemp),strlen(ctemp),
569 : sizeof(char),
570 : NULL,NULL);
571 0 : GDrawAddSelectionType(gt->g.base,sel,"text/plain;charset=UTF-8",ctemp,strlen(ctemp),
572 : sizeof(char),
573 : NULL,NULL);
574 :
575 0 : if ( ctemp2!=NULL && *ctemp2!='\0' /*strlen(ctemp2)==gt->sel_end-gt->sel_start*/ )
576 0 : GDrawAddSelectionType(gt->g.base,sel,"STRING",ctemp2,strlen(ctemp2),
577 : sizeof(char),
578 : NULL,NULL);
579 : else
580 0 : free(ctemp2);
581 : }
582 0 : }
583 :
584 0 : static int GTextFieldSelBackword(unichar_t *text,int start) {
585 0 : unichar_t ch = text[start-1];
586 :
587 0 : if ( start==0 )
588 : /* Can't go back */;
589 0 : else if ( isalnum(ch) || ch=='_' ) {
590 : int i;
591 0 : for ( i=start-1; i>=0 && ((text[i]<0x10000 && isalnum(text[i])) || text[i]=='_') ; --i );
592 0 : start = i+1;
593 : } else {
594 : int i;
595 0 : for ( i=start-1; i>=0 && !(text[i]<0x10000 && isalnum(text[i])) && text[i]!='_' ; --i );
596 0 : start = i+1;
597 : }
598 0 : return( start );
599 : }
600 :
601 0 : static int GTextFieldSelForeword(unichar_t *text,int end) {
602 0 : unichar_t ch = text[end];
603 :
604 0 : if ( ch=='\0' )
605 : /* Nothing */;
606 0 : else if ( isalnum(ch) || ch=='_' ) {
607 : int i;
608 0 : for ( i=end; (text[i]<0x10000 && isalnum(text[i])) || text[i]=='_' ; ++i );
609 0 : end = i;
610 : } else {
611 : int i;
612 0 : for ( i=end; !(text[i]<0x10000 && isalnum(text[i])) && text[i]!='_' && text[i]!='\0' ; ++i );
613 0 : end = i;
614 : }
615 0 : return( end );
616 : }
617 :
618 0 : static void GTextFieldSelectWord(GTextField *gt,int mid, int16 *start, int16 *end) {
619 : unichar_t *text;
620 0 : unichar_t ch = gt->text[mid];
621 :
622 0 : text = gt->text;
623 0 : ch = text[mid];
624 :
625 0 : if ( ch=='\0' )
626 0 : *start = *end = mid;
627 0 : else if ( (ch<0x10000 && isspace(ch)) ) {
628 : int i;
629 0 : for ( i=mid; text[i]<0x10000 && isspace(text[i]); ++i );
630 0 : *end = i;
631 0 : for ( i=mid-1; i>=0 && text[i]<0x10000 && isspace(text[i]) ; --i );
632 0 : *start = i+1;
633 0 : } else if ( (ch<0x10000 && isalnum(ch)) || ch=='_' ) {
634 : int i;
635 0 : for ( i=mid; (text[i]<0x10000 && isalnum(text[i])) || text[i]=='_' ; ++i );
636 0 : *end = i;
637 0 : for ( i=mid-1; i>=0 && ((text[i]<0x10000 && isalnum(text[i])) || text[i]=='_') ; --i );
638 0 : *start = i+1;
639 : } else {
640 : int i;
641 0 : for ( i=mid; !(text[i]<0x10000 && isalnum(text[i])) && text[i]!='_' && text[i]!='\0' ; ++i );
642 0 : *end = i;
643 0 : for ( i=mid-1; i>=0 && !(text[i]<0x10000 && isalnum(text[i])) && text[i]!='_' ; --i );
644 0 : *start = i+1;
645 : }
646 0 : }
647 :
648 0 : static void GTextFieldSelectWords(GTextField *gt,int last) {
649 : int16 ss, se;
650 0 : GTextFieldSelectWord(gt,gt->sel_base,>->sel_start,>->sel_end);
651 0 : if ( last!=gt->sel_base ) {
652 0 : GTextFieldSelectWord(gt,last,&ss,&se);
653 0 : if ( ss<gt->sel_start ) gt->sel_start = ss;
654 0 : if ( se>gt->sel_end ) gt->sel_end = se;
655 : }
656 0 : }
657 :
658 0 : static void GTextFieldPaste(GTextField *gt,enum selnames sel) {
659 0 : if ( GDrawSelectionHasType(gt->g.base,sel,"UTF8_STRING") ||
660 0 : GDrawSelectionHasType(gt->g.base,sel,"text/plain;charset=UTF-8")) {
661 : unichar_t *temp; char *ctemp;
662 : int32 len;
663 0 : ctemp = GDrawRequestSelection(gt->g.base,sel,"UTF8_STRING",&len);
664 0 : if ( ctemp==NULL || len==0 )
665 0 : ctemp = GDrawRequestSelection(gt->g.base,sel,"text/plain;charset=UTF-8",&len);
666 0 : if ( ctemp!=NULL ) {
667 0 : temp = utf82u_copyn(ctemp,strlen(ctemp));
668 0 : GTextField_Replace(gt,temp);
669 0 : free(ctemp); free(temp);
670 : }
671 : /* Bug in the xorg library on 64 bit machines and 32 bit transfers don't work */
672 : /* so avoid them, by looking for utf8 first */
673 0 : } else if ( GDrawSelectionHasType(gt->g.base,sel,"text/plain;charset=ISO-10646-UCS-4")) {
674 : unichar_t *temp;
675 : int32 len;
676 0 : temp = GDrawRequestSelection(gt->g.base,sel,"text/plain;charset=ISO-10646-UCS-4",&len);
677 : /* Bug! I don't handle byte reversed selections. But I don't think there should be any anyway... */
678 0 : if ( temp!=NULL )
679 0 : GTextField_Replace(gt,temp[0]==0xfeff?temp+1:temp);
680 0 : free(temp);
681 0 : } else if ( GDrawSelectionHasType(gt->g.base,sel,"Unicode") ||
682 0 : GDrawSelectionHasType(gt->g.base,sel,"text/plain;charset=ISO-10646-UCS-2")) {
683 : unichar_t *temp;
684 : uint16 *temp2;
685 : int32 len;
686 0 : temp2 = GDrawRequestSelection(gt->g.base,sel,"text/plain;charset=ISO-10646-UCS-2",&len);
687 0 : if ( temp2==NULL || len==0 )
688 0 : temp2 = GDrawRequestSelection(gt->g.base,sel,"Unicode",&len);
689 0 : if ( temp2!=NULL ) {
690 : int i;
691 0 : temp = malloc((len/2+1)*sizeof(unichar_t));
692 0 : for ( i=0; temp2[i]!=0; ++i )
693 0 : temp[i] = temp2[i];
694 0 : temp[i] = 0;
695 0 : GTextField_Replace(gt,temp[0]==0xfeff?temp+1:temp);
696 0 : free(temp);
697 : }
698 0 : free(temp2);
699 0 : } else if ( GDrawSelectionHasType(gt->g.base,sel,"STRING")) {
700 : unichar_t *temp; char *ctemp;
701 : int32 len;
702 0 : ctemp = GDrawRequestSelection(gt->g.base,sel,"STRING",&len);
703 0 : if ( ctemp==NULL || len==0 )
704 0 : ctemp = GDrawRequestSelection(gt->g.base,sel,"text/plain;charset=UTF-8",&len);
705 0 : if ( ctemp!=NULL ) {
706 0 : temp = def2u_copy(ctemp);
707 0 : GTextField_Replace(gt,temp);
708 0 : free(ctemp); free(temp);
709 : }
710 : }
711 0 : }
712 :
713 0 : static int gtextfield_editcmd(GGadget *g,enum editor_commands cmd) {
714 0 : GTextField *gt = (GTextField *) g;
715 :
716 0 : switch ( cmd ) {
717 : case ec_selectall:
718 0 : gt->sel_start = 0;
719 0 : gt->sel_end = u_strlen(gt->text);
720 0 : return( true );
721 : case ec_clear:
722 0 : GTextField_Replace(gt,nullstr);
723 0 : _ggadget_redraw(g);
724 0 : return( true );
725 : case ec_cut:
726 0 : GTextFieldGrabSelection(gt,sn_clipboard);
727 0 : GTextField_Replace(gt,nullstr);
728 0 : _ggadget_redraw(g);
729 0 : break;
730 : case ec_copy:
731 0 : GTextFieldGrabSelection(gt,sn_clipboard);
732 0 : return( true );
733 : case ec_paste:
734 0 : GTextFieldPaste(gt,sn_clipboard);
735 0 : GTextField_Show(gt,gt->sel_start);
736 0 : _ggadget_redraw(g);
737 0 : break;
738 : case ec_undo:
739 0 : if ( gt->oldtext!=NULL ) {
740 0 : unichar_t *temp = gt->text;
741 : int16 s;
742 0 : gt->text = gt->oldtext; gt->oldtext = temp;
743 0 : s = gt->sel_start; gt->sel_start = gt->sel_oldstart; gt->sel_oldstart = s;
744 0 : s = gt->sel_end; gt->sel_end = gt->sel_oldend; gt->sel_oldend = s;
745 0 : s = gt->sel_base; gt->sel_base = gt->sel_oldbase; gt->sel_oldbase = s;
746 0 : GTextFieldRefigureLines(gt, 0);
747 0 : GTextField_Show(gt,gt->sel_end);
748 0 : _ggadget_redraw(g);
749 : }
750 0 : break;
751 : case ec_redo: /* Hmm. not sure */ /* we don't do anything */
752 0 : return( true ); /* but probably best to return success */
753 : case ec_backword:
754 0 : if ( gt->sel_start==gt->sel_end && gt->sel_start!=0 ) {
755 0 : gt->sel_start = GTextFieldSelBackword(gt->text,gt->sel_start);
756 : }
757 0 : GTextField_Replace(gt,nullstr);
758 0 : _ggadget_redraw(g);
759 0 : break;
760 : case ec_deleteword:
761 0 : if ( gt->sel_start==gt->sel_end && gt->sel_start!=0 )
762 0 : GTextFieldSelectWord(gt,gt->sel_start,>->sel_start,>->sel_end);
763 0 : GTextField_Replace(gt,nullstr);
764 0 : _ggadget_redraw(g);
765 0 : break;
766 : default:
767 0 : return( false );
768 : }
769 0 : GTextFieldChanged(gt,-1);
770 0 : return( true );
771 : }
772 :
773 0 : static int _gtextfield_editcmd(GGadget *g,enum editor_commands cmd) {
774 0 : if ( gtextfield_editcmd(g,cmd)) {
775 0 : _ggadget_redraw(g);
776 0 : GTPositionGIC((GTextField *) g);
777 0 : return( true );
778 : }
779 0 : return( false );
780 : }
781 :
782 0 : static int GTBackPos(GTextField *gt,int pos, int ismeta) {
783 : int newpos;
784 :
785 0 : if ( ismeta )
786 0 : newpos = GTextFieldSelBackword(gt->text,pos);
787 : else
788 0 : newpos = pos-1;
789 0 : if ( newpos==-1 ) newpos = pos;
790 0 : return( newpos );
791 : }
792 :
793 0 : static int GTForePos(GTextField *gt,int pos, int ismeta) {
794 0 : int newpos=pos;
795 :
796 0 : if ( ismeta )
797 0 : newpos = GTextFieldSelForeword(gt->text,pos);
798 : else {
799 0 : if ( gt->text[pos]!=0 )
800 0 : newpos = pos+1;
801 : }
802 0 : return( newpos );
803 : }
804 :
805 0 : unichar_t *_GGadgetFileToUString(char *filename,int max) {
806 : FILE *file;
807 : int ch, ch2, ch3;
808 0 : int format=0;
809 : unichar_t *space, *upt, *end;
810 :
811 0 : file = fopen( filename,"r" );
812 0 : if ( file==NULL )
813 0 : return( NULL );
814 0 : ch = getc(file); ch2 = getc(file); ch3 = getc(file);
815 0 : ungetc(ch3,file);
816 0 : if ( ch==0xfe && ch2==0xff )
817 0 : format = 1; /* normal ucs2 */
818 0 : else if ( ch==0xff && ch2==0xfe )
819 0 : format = 2; /* byte-swapped ucs2 */
820 0 : else if ( ch==0xef && ch2==0xbb && ch3==0xbf ) {
821 0 : format = 3; /* utf8 */
822 0 : getc(file);
823 : } else {
824 0 : getc(file); /* rewind probably undoes the ungetc, but let's not depend on it */
825 0 : rewind(file);
826 : }
827 0 : space = upt = malloc((max+1)*sizeof(unichar_t));
828 0 : end = space+max;
829 0 : if ( format==3 ) { /* utf8 */
830 0 : while ( upt<end ) {
831 0 : ch=getc(file);
832 0 : if ( ch==EOF )
833 0 : break;
834 0 : if ( ch<0x80 )
835 0 : *upt++ = ch;
836 0 : else if ( ch<0xe0 ) {
837 0 : ch2 = getc(file);
838 0 : *upt++ = ((ch&0x1f)<<6)|(ch2&0x3f);
839 0 : } else if ( ch<0xf0 ) {
840 0 : ch2 = getc(file); ch3 = getc(file);
841 0 : *upt++ = ((ch&0xf)<<12)|((ch2&0x3f)<<6)|(ch3&0x3f);
842 : } else {
843 : int ch4, w;
844 0 : ch2 = getc(file); ch3 = getc(file); ch4=getc(file);
845 0 : w = ( ((ch&7)<<2) | ((ch2&0x30)>>4) ) -1;
846 0 : *upt++ = 0xd800 | (w<<6) | ((ch2&0xf)<<2) | ((ch3&0x30)>>4);
847 0 : if ( upt<end )
848 0 : *upt++ = 0xdc00 | ((ch3&0xf)<<6) | (ch4&0x3f);
849 : }
850 : }
851 0 : } else if ( format!=0 ) {
852 0 : while ( upt<end ) {
853 0 : ch = getc(file); ch2 = getc(file);
854 0 : if ( ch2==EOF )
855 0 : break;
856 0 : if ( format==1 )
857 0 : *upt ++ = (ch<<8)|ch2;
858 : else
859 0 : *upt ++ = (ch2<<8)|ch;
860 : }
861 : } else {
862 : char buffer[400];
863 0 : while ( fgets(buffer,sizeof(buffer),file)!=NULL ) {
864 0 : def2u_strncpy(upt,buffer,end-upt);
865 0 : upt += u_strlen(upt);
866 : }
867 : }
868 0 : *upt = '\0';
869 0 : fclose(file);
870 0 : return( space );
871 : }
872 :
873 : static unichar_t txt[] = { '*','.','t','x','t', '\0' };
874 : static unichar_t errort[] = { 'C','o','u','l','d',' ','n','o','t',' ','o','p','e','n', '\0' };
875 : static unichar_t error[] = { 'C','o','u','l','d',' ','n','o','t',' ','o','p','e','n',' ','%','.','1','0','0','h','s', '\0' };
876 :
877 0 : static void GTextFieldImport(GTextField *gt) {
878 : unichar_t *ret;
879 : char *cret;
880 : unichar_t *str;
881 :
882 0 : if ( _ggadget_use_gettext ) {
883 0 : char *temp = GWidgetOpenFile8(_("Open"),NULL,"*.txt",NULL,NULL);
884 0 : ret = utf82u_copy(temp);
885 0 : free(temp);
886 : } else {
887 0 : ret = GWidgetOpenFile(GStringGetResource(_STR_Open,NULL),NULL,
888 : txt,NULL,NULL);
889 : }
890 :
891 0 : if ( ret==NULL )
892 0 : return;
893 0 : cret = u2def_copy(ret);
894 0 : free(ret);
895 0 : str = _GGadgetFileToUString(cret,65536);
896 0 : if ( str==NULL ) {
897 0 : if ( _ggadget_use_gettext )
898 0 : GWidgetError8(_("Could not open file"), _("Could not open %.100s"),cret);
899 : else
900 0 : GWidgetError(errort,error,cret);
901 0 : free(cret);
902 0 : return;
903 : }
904 0 : free(cret);
905 0 : GTextField_Replace(gt,str);
906 0 : free(str);
907 : }
908 :
909 0 : static void GTextFieldSave(GTextField *gt,int utf8) {
910 : unichar_t *ret;
911 : char *cret;
912 : FILE *file;
913 : unichar_t *pt;
914 :
915 0 : if ( _ggadget_use_gettext ) {
916 0 : char *temp = GWidgetOpenFile8(_("Save"),NULL,"*.txt",NULL,NULL);
917 0 : ret = utf82u_copy(temp);
918 0 : free(temp);
919 : } else
920 0 : ret = GWidgetSaveAsFile(GStringGetResource(_STR_Save,NULL),NULL,
921 : txt,NULL,NULL);
922 :
923 0 : if ( ret==NULL )
924 0 : return;
925 0 : cret = u2def_copy(ret);
926 0 : free(ret);
927 0 : file = fopen(cret,"w");
928 0 : if ( file==NULL ) {
929 0 : if ( _ggadget_use_gettext )
930 0 : GWidgetError8(_("Could not open file"), _("Could not open %.100s"),cret);
931 : else
932 0 : GWidgetError(errort,error,cret);
933 0 : free(cret);
934 0 : return;
935 : }
936 0 : free(cret);
937 :
938 0 : if ( utf8 ) {
939 0 : putc(0xef,file); /* Zero width something or other. Marks this as unicode, utf8 */
940 0 : putc(0xbb,file);
941 0 : putc(0xbf,file);
942 0 : for ( pt = gt->text ; *pt; ++pt ) {
943 0 : if ( *pt<0x80 )
944 0 : putc(*pt,file);
945 0 : else if ( *pt<0x800 ) {
946 0 : putc(0xc0 | (*pt>>6), file);
947 0 : putc(0x80 | (*pt&0x3f), file);
948 0 : } else if ( *pt>=0xd800 && *pt<0xdc00 && pt[1]>=0xdc00 && pt[1]<0xe000 ) {
949 0 : int u = ((*pt>>6)&0xf)+1, y = ((*pt&3)<<4) | ((pt[1]>>6)&0xf);
950 0 : putc( 0xf0 | (u>>2),file );
951 0 : putc( 0x80 | ((u&3)<<4) | ((*pt>>2)&0xf),file );
952 0 : putc( 0x80 | y,file );
953 0 : putc( 0x80 | (pt[1]&0x3f),file );
954 : } else {
955 0 : putc( 0xe0 | (*pt>>12),file );
956 0 : putc( 0x80 | ((*pt>>6)&0x3f),file );
957 0 : putc( 0x80 | (*pt&0x3f),file );
958 : }
959 : }
960 : } else {
961 0 : putc(0xfeff>>8,file); /* Zero width something or other. Marks this as unicode */
962 0 : putc(0xfeff&0xff,file);
963 0 : for ( pt = gt->text ; *pt; ++pt ) {
964 0 : putc(*pt>>8,file);
965 0 : putc(*pt&0xff,file);
966 : }
967 : }
968 0 : fclose(file);
969 : }
970 :
971 : #define MID_Cut 1
972 : #define MID_Copy 2
973 : #define MID_Paste 3
974 :
975 : #define MID_SelectAll 4
976 :
977 : #define MID_Save 5
978 : #define MID_SaveUCS2 6
979 : #define MID_Import 7
980 :
981 : #define MID_Undo 8
982 :
983 : static GTextField *popup_kludge;
984 :
985 0 : static void GTFPopupInvoked(GWindow v, GMenuItem *mi,GEvent *e) {
986 : GTextField *gt;
987 0 : if ( popup_kludge==NULL )
988 0 : return;
989 0 : gt = popup_kludge;
990 0 : popup_kludge = NULL;
991 0 : switch ( mi->mid ) {
992 : case MID_Undo:
993 0 : gtextfield_editcmd(>->g,ec_undo);
994 0 : break;
995 : case MID_Cut:
996 0 : gtextfield_editcmd(>->g,ec_cut);
997 0 : break;
998 : case MID_Copy:
999 0 : gtextfield_editcmd(>->g,ec_copy);
1000 0 : break;
1001 : case MID_Paste:
1002 0 : gtextfield_editcmd(>->g,ec_paste);
1003 0 : break;
1004 : case MID_SelectAll:
1005 0 : gtextfield_editcmd(>->g,ec_selectall);
1006 0 : break;
1007 : case MID_Save:
1008 0 : GTextFieldSave(gt,true);
1009 0 : break;
1010 : case MID_SaveUCS2:
1011 0 : GTextFieldSave(gt,false);
1012 0 : break;
1013 : case MID_Import:
1014 0 : GTextFieldImport(gt);
1015 0 : break;
1016 : }
1017 0 : _ggadget_redraw(>->g);
1018 : }
1019 :
1020 : static GMenuItem gtf_popuplist[] = {
1021 : { { (unichar_t *) "_Undo", NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, '\0' }, 'Z', ksm_control, NULL, NULL, GTFPopupInvoked, MID_Undo },
1022 : { { 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 */
1023 : { { (unichar_t *) "Cu_t", NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, '\0' }, 'X', ksm_control, NULL, NULL, GTFPopupInvoked, MID_Cut },
1024 : { { (unichar_t *) "_Copy", NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, '\0' }, 'C', ksm_control, NULL, NULL, GTFPopupInvoked, MID_Copy },
1025 : { { (unichar_t *) "_Paste", NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, '\0' }, 'V', ksm_control, NULL, NULL, GTFPopupInvoked, MID_Paste },
1026 : { { 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 */
1027 : { { (unichar_t *) "_Save in UTF8", NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, '\0' }, 'S', ksm_control, NULL, NULL, GTFPopupInvoked, MID_Save },
1028 : { { (unichar_t *) "Save in _UCS2", NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, '\0' }, '\0', ksm_control, NULL, NULL, GTFPopupInvoked, MID_SaveUCS2 },
1029 : { { (unichar_t *) "_Import", NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, '\0' }, 'I', ksm_control, NULL, NULL, GTFPopupInvoked, MID_Import },
1030 : GMENUITEM_EMPTY
1031 : };
1032 : static int first = true;
1033 :
1034 0 : static void GTFPopupMenu(GTextField *gt, GEvent *event) {
1035 0 : int no_sel = gt->sel_start==gt->sel_end;
1036 :
1037 0 : if ( first ) {
1038 0 : gtf_popuplist[0].ti.text = (unichar_t *) _("_Undo");
1039 0 : gtf_popuplist[2].ti.text = (unichar_t *) _("Cu_t");
1040 0 : gtf_popuplist[3].ti.text = (unichar_t *) _("_Copy");
1041 0 : gtf_popuplist[4].ti.text = (unichar_t *) _("_Paste");
1042 0 : gtf_popuplist[6].ti.text = (unichar_t *) _("_Save in UTF8");
1043 0 : gtf_popuplist[7].ti.text = (unichar_t *) _("Save in _UCS2");
1044 0 : gtf_popuplist[8].ti.text = (unichar_t *) _("_Import");
1045 0 : first = false;
1046 : }
1047 :
1048 0 : gtf_popuplist[0].ti.disabled = gt->oldtext==NULL; /* Undo */
1049 0 : gtf_popuplist[2].ti.disabled = no_sel; /* Cut */
1050 0 : gtf_popuplist[3].ti.disabled = no_sel; /* Copy */
1051 0 : gtf_popuplist[4].ti.disabled = !GDrawSelectionHasType(gt->g.base,sn_clipboard,"text/plain;charset=ISO-10646-UCS-2") &&
1052 0 : !GDrawSelectionHasType(gt->g.base,sn_clipboard,"UTF8_STRING") &&
1053 0 : !GDrawSelectionHasType(gt->g.base,sn_clipboard,"STRING");
1054 0 : popup_kludge = gt;
1055 0 : GMenuCreatePopupMenu(gt->g.base,event, gtf_popuplist);
1056 0 : }
1057 :
1058 0 : static void GTextFieldIncrement(GTextField *gt,int amount) {
1059 : unichar_t *end;
1060 0 : double d = u_strtod(gt->text,&end);
1061 : char buf[40];
1062 :
1063 0 : while ( *end==' ' ) ++end;
1064 0 : if ( *end!='\0' ) {
1065 0 : GDrawBeep(NULL);
1066 0 : return;
1067 : }
1068 0 : d = floor(d)+amount;
1069 0 : sprintf(buf,"%g", d);
1070 0 : free(gt->oldtext);
1071 0 : gt->oldtext = gt->text;
1072 0 : gt->text = uc_copy(buf);
1073 0 : free(gt->utf8_text);
1074 0 : gt->utf8_text = copy(buf);
1075 0 : _ggadget_redraw(>->g);
1076 0 : GTextFieldChanged(gt,-1);
1077 : }
1078 :
1079 0 : static int GTextFieldDoChange(GTextField *gt, GEvent *event) {
1080 0 : int ss = gt->sel_start, se = gt->sel_end;
1081 : int pos, l, xpos, sel;
1082 : unichar_t *upt;
1083 :
1084 0 : if ( ( event->u.chr.state&(GMenuMask()&~ksm_shift)) ||
1085 0 : event->u.chr.chars[0]<' ' || event->u.chr.chars[0]==0x7f ) {
1086 0 : switch ( event->u.chr.keysym ) {
1087 : case GK_BackSpace:
1088 0 : if ( gt->sel_start==gt->sel_end ) {
1089 0 : if ( gt->sel_start==0 )
1090 0 : return( 2 );
1091 0 : --gt->sel_start;
1092 : }
1093 0 : GTextField_Replace(gt,nullstr);
1094 0 : return( true );
1095 : break;
1096 : case GK_Delete:
1097 0 : if ( gt->sel_start==gt->sel_end ) {
1098 0 : if ( gt->text[gt->sel_start]==0 )
1099 0 : return( 2 );
1100 0 : ++gt->sel_end;
1101 : }
1102 0 : GTextField_Replace(gt,nullstr);
1103 0 : return( true );
1104 : break;
1105 : case GK_Left: case GK_KP_Left:
1106 0 : if ( gt->sel_start==gt->sel_end ) {
1107 0 : gt->sel_start = GTBackPos(gt,gt->sel_start,event->u.chr.state&ksm_meta);
1108 0 : if ( !(event->u.chr.state&ksm_shift ))
1109 0 : gt->sel_end = gt->sel_start;
1110 0 : } else if ( event->u.chr.state&ksm_shift ) {
1111 0 : if ( gt->sel_end==gt->sel_base ) {
1112 0 : gt->sel_start = GTBackPos(gt,gt->sel_start,event->u.chr.state&ksm_meta);
1113 : } else {
1114 0 : gt->sel_end = GTBackPos(gt,gt->sel_end,event->u.chr.state&ksm_meta);
1115 : }
1116 : } else {
1117 0 : gt->sel_end = gt->sel_base = gt->sel_start;
1118 : }
1119 0 : GTextField_Show(gt,gt->sel_start);
1120 0 : return( 2 );
1121 : break;
1122 : case GK_Right: case GK_KP_Right:
1123 0 : if ( gt->sel_start==gt->sel_end ) {
1124 0 : gt->sel_end = GTForePos(gt,gt->sel_start,event->u.chr.state&ksm_meta);
1125 0 : if ( !(event->u.chr.state&ksm_shift ))
1126 0 : gt->sel_start = gt->sel_end;
1127 0 : } else if ( event->u.chr.state&ksm_shift ) {
1128 0 : if ( gt->sel_end==gt->sel_base ) {
1129 0 : gt->sel_start = GTForePos(gt,gt->sel_start,event->u.chr.state&ksm_meta);
1130 : } else {
1131 0 : gt->sel_end = GTForePos(gt,gt->sel_end,event->u.chr.state&ksm_meta);
1132 : }
1133 : } else {
1134 0 : gt->sel_start = gt->sel_base = gt->sel_end;
1135 : }
1136 0 : GTextField_Show(gt,gt->sel_start);
1137 0 : return( 2 );
1138 : break;
1139 : case GK_Up: case GK_KP_Up:
1140 0 : if ( gt->numericfield ) {
1141 0 : GTextFieldIncrement(gt,(event->u.chr.state&(ksm_shift|ksm_control))?10:1);
1142 0 : return( 2 );
1143 : }
1144 0 : if ( !gt->multi_line )
1145 0 : break;
1146 0 : if ( !( event->u.chr.state&ksm_shift ) && gt->sel_start!=gt->sel_end )
1147 0 : gt->sel_end = gt->sel_base = gt->sel_start;
1148 : else {
1149 0 : pos = gt->sel_start;
1150 0 : if ( ( event->u.chr.state&ksm_shift ) && gt->sel_start==gt->sel_base )
1151 0 : pos = gt->sel_end;
1152 0 : l = GTextFieldFindLine(gt,gt->sel_start);
1153 : GRect pos_rect;
1154 0 : int ll = gt->lines8[l+1]==-1 ? -1 : gt->lines8[l+1]-gt->lines8[l];
1155 0 : sel = u2utf8_index(gt->sel_start-gt->lines[l],gt->utf8_text+gt->lines8[l]);
1156 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[l],ll,NULL);
1157 0 : GDrawLayoutIndexToPos(gt->g.base,sel,&pos_rect);
1158 0 : xpos = pos_rect.x;
1159 0 : if ( l!=0 ) {
1160 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[l-1],gt->lines8[l]-gt->lines8[l-1],NULL);
1161 0 : pos = GDrawLayoutXYToIndex(gt->g.base,xpos,0);
1162 0 : pos = utf82u_index(pos,gt->utf8_text+gt->lines8[l-1]);
1163 : }
1164 0 : if ( event->u.chr.state&ksm_shift ) {
1165 0 : if ( pos<gt->sel_base ) {
1166 0 : gt->sel_start = pos;
1167 0 : gt->sel_end = gt->sel_base;
1168 : } else {
1169 0 : gt->sel_start = gt->sel_base;
1170 0 : gt->sel_end = pos;
1171 : }
1172 : } else {
1173 0 : gt->sel_start = gt->sel_end = gt->sel_base = pos;
1174 : }
1175 : }
1176 0 : GTextField_Show(gt,gt->sel_start);
1177 0 : return( 2 );
1178 : break;
1179 : case GK_Down: case GK_KP_Down:
1180 0 : if ( gt->numericfield ) {
1181 0 : GTextFieldIncrement(gt,(event->u.chr.state&(ksm_shift|ksm_control))?-10:-1);
1182 0 : return( 2 );
1183 : }
1184 0 : if ( !gt->multi_line )
1185 0 : break;
1186 0 : if ( !( event->u.chr.state&ksm_shift ) && gt->sel_start!=gt->sel_end )
1187 0 : gt->sel_end = gt->sel_base = gt->sel_end;
1188 : else {
1189 0 : pos = gt->sel_start;
1190 0 : if ( ( event->u.chr.state&ksm_shift ) && gt->sel_start==gt->sel_base )
1191 0 : pos = gt->sel_end;
1192 0 : l = GTextFieldFindLine(gt,gt->sel_start);
1193 : GRect pos_rect;
1194 0 : int ll = gt->lines8[l+1]==-1 ? -1 : gt->lines8[l+1]-gt->lines8[l];
1195 0 : sel = u2utf8_index(gt->sel_start-gt->lines[l],gt->utf8_text+gt->lines8[l]);
1196 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[l],ll,NULL);
1197 0 : GDrawLayoutIndexToPos(gt->g.base,sel,&pos_rect);
1198 0 : xpos = pos_rect.x;
1199 0 : if ( l<gt->lcnt-1 ) {
1200 0 : ll = gt->lines8[l+2]==-1 ? -1 : gt->lines8[l+2]-gt->lines8[l+1];
1201 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[l+1],ll,NULL);
1202 0 : pos = GDrawLayoutXYToIndex(gt->g.base,xpos,0);
1203 0 : pos = utf82u_index(pos,gt->utf8_text+gt->lines8[l+1]);
1204 : }
1205 0 : if ( event->u.chr.state&ksm_shift ) {
1206 0 : if ( pos<gt->sel_base ) {
1207 0 : gt->sel_start = pos;
1208 0 : gt->sel_end = gt->sel_base;
1209 : } else {
1210 0 : gt->sel_start = gt->sel_base;
1211 0 : gt->sel_end = pos;
1212 : }
1213 : } else {
1214 0 : gt->sel_start = gt->sel_end = gt->sel_base = pos;
1215 : }
1216 : }
1217 0 : GTextField_Show(gt,gt->sel_start);
1218 0 : return( 2 );
1219 : break;
1220 : case GK_Home: case GK_Begin: case GK_KP_Home: case GK_KP_Begin:
1221 0 : if ( !(event->u.chr.state&ksm_shift) ) {
1222 0 : gt->sel_start = gt->sel_base = gt->sel_end = 0;
1223 : } else {
1224 0 : gt->sel_start = 0; gt->sel_end = gt->sel_base;
1225 : }
1226 0 : GTextField_Show(gt,gt->sel_start);
1227 0 : return( 2 );
1228 : break;
1229 : /* Move to eol. (if already at eol, move to next eol) */
1230 : case 'E': case 'e':
1231 0 : if ( !( event->u.chr.state&ksm_control ) )
1232 0 : return( false );
1233 0 : upt = gt->text+gt->sel_base;
1234 0 : if ( *upt=='\n' )
1235 0 : ++upt;
1236 0 : upt = u_strchr(upt,'\n');
1237 0 : if ( upt==NULL ) upt=gt->text+u_strlen(gt->text);
1238 0 : if ( !(event->u.chr.state&ksm_shift) ) {
1239 0 : gt->sel_start = gt->sel_base = gt->sel_end =upt-gt->text;
1240 : } else {
1241 0 : gt->sel_start = gt->sel_base; gt->sel_end = upt-gt->text;
1242 : }
1243 0 : GTextField_Show(gt,gt->sel_start);
1244 0 : return( 2 );
1245 : break;
1246 : case GK_End: case GK_KP_End:
1247 0 : if ( !(event->u.chr.state&ksm_shift) ) {
1248 0 : gt->sel_start = gt->sel_base = gt->sel_end = u_strlen(gt->text);
1249 : } else {
1250 0 : gt->sel_start = gt->sel_base; gt->sel_end = u_strlen(gt->text);
1251 : }
1252 0 : GTextField_Show(gt,gt->sel_start);
1253 0 : return( 2 );
1254 : break;
1255 : case 'D': case 'd':
1256 0 : if ( event->u.chr.state&ksm_control ) { /* delete word */
1257 0 : gtextfield_editcmd(>->g,ec_deleteword);
1258 0 : GTextField_Show(gt,gt->sel_start);
1259 0 : return( true );
1260 : }
1261 0 : break;
1262 : case 'W': case 'w':
1263 0 : if ( event->u.chr.state&ksm_control ) { /* backword */
1264 0 : gtextfield_editcmd(>->g,ec_backword);
1265 0 : GTextField_Show(gt,gt->sel_start);
1266 0 : return( true );
1267 : }
1268 0 : break;
1269 : case 'M': case 'm': case 'J': case 'j':
1270 0 : if ( !( event->u.chr.state&ksm_control ) )
1271 0 : return( false );
1272 : /* fall through into return case */
1273 : case GK_Return: case GK_Linefeed:
1274 0 : if ( gt->accepts_returns ) {
1275 0 : GTextField_Replace(gt,newlinestr);
1276 0 : return( true );
1277 : }
1278 0 : break;
1279 : case GK_Tab:
1280 0 : if ( gt->completionfield && ((GCompletionField *) gt)->completion!=NULL ) {
1281 0 : GTextFieldComplete(gt,true);
1282 0 : gt->was_completing = true;
1283 0 : return( 3 );
1284 : }
1285 0 : if ( gt->accepts_tabs ) {
1286 0 : GTextField_Replace(gt,tabstr);
1287 0 : return( true );
1288 : }
1289 0 : break;
1290 : default:
1291 0 : if ( GMenuIsCommand(event,H_("Select All|Ctl+A")) ) {
1292 0 : gtextfield_editcmd(>->g,ec_selectall);
1293 0 : return( 2 );
1294 0 : } else if ( GMenuIsCommand(event,H_("Copy|Ctl+C")) ) {
1295 0 : gtextfield_editcmd(>->g,ec_copy);
1296 0 : } else if ( GMenuIsCommand(event,H_("Paste|Ctl+V")) ) {
1297 0 : gtextfield_editcmd(>->g,ec_paste);
1298 0 : GTextField_Show(gt,gt->sel_start);
1299 0 : return( true );
1300 0 : } else if ( GMenuIsCommand(event,H_("Cut|Ctl+X")) ) {
1301 0 : gtextfield_editcmd(>->g,ec_cut);
1302 0 : GTextField_Show(gt,gt->sel_start);
1303 0 : return( true );
1304 0 : } else if ( GMenuIsCommand(event,H_("Undo|Ctl+Z")) ) {
1305 0 : gtextfield_editcmd(>->g,ec_undo);
1306 0 : GTextField_Show(gt,gt->sel_start);
1307 0 : return( true );
1308 0 : } else if ( GMenuIsCommand(event,H_("Save|Ctl+S")) ) {
1309 0 : GTextFieldSave(gt,true);
1310 0 : return( 2 );
1311 0 : } else if ( GMenuIsCommand(event,H_("Import...|Ctl+Shft+I")) ) {
1312 0 : GTextFieldImport(gt);
1313 0 : return( true );
1314 : } else
1315 0 : return( false );
1316 0 : break;
1317 : }
1318 0 : } else {
1319 0 : GTextField_Replace(gt,event->u.chr.chars);
1320 0 : return( 4 /* Do name completion */ );
1321 : }
1322 :
1323 0 : if ( gt->sel_start == gt->sel_end )
1324 0 : gt->sel_base = gt->sel_start;
1325 0 : if ( ss!=gt->sel_start || se!=gt->sel_end )
1326 0 : GTextFieldGrabPrimarySelection(gt);
1327 0 : return( false );
1328 : }
1329 :
1330 0 : static void _gt_cursor_pos(GTextField *gt, int sel_start, int *x, int *y) {
1331 : int l, sel;
1332 :
1333 0 : *x = -1; *y= -1;
1334 0 : GDrawSetFont(gt->g.base,gt->font);
1335 0 : l = GTextFieldFindLine(gt,sel_start);
1336 0 : if ( l<gt->loff_top || l>=gt->loff_top + ((gt->g.inner.height+gt->fh/2)/gt->fh))
1337 0 : return;
1338 0 : *y = (l-gt->loff_top)*gt->fh;
1339 : GRect pos_rect;
1340 0 : int ll = gt->lines8[l+1]==-1 ? -1 : gt->lines8[l+1]-gt->lines8[l];
1341 0 : sel = u2utf8_index(sel_start-gt->lines[l],gt->utf8_text+gt->lines8[l]);
1342 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[l],ll,NULL);
1343 0 : GDrawLayoutIndexToPos(gt->g.base,sel,&pos_rect);
1344 0 : *x = pos_rect.x - gt->xoff_left;
1345 : }
1346 :
1347 0 : static void gt_cursor_pos(GTextField *gt, int *x, int *y) {
1348 0 : _gt_cursor_pos(gt,gt->sel_start,x,y);
1349 0 : }
1350 :
1351 0 : static void GTPositionGIC(GTextField *gt) {
1352 : int x,y;
1353 :
1354 0 : if ( !gt->g.has_focus || gt->gic==NULL )
1355 0 : return;
1356 0 : gt_cursor_pos(gt,&x,&y);
1357 0 : if ( x<0 )
1358 0 : return;
1359 0 : GDrawSetGIC(gt->g.base,gt->gic,gt->g.inner.x+x,gt->g.inner.y+y+gt->as);
1360 : }
1361 :
1362 0 : static void gt_draw_cursor(GWindow pixmap, GTextField *gt) {
1363 : GRect old;
1364 : int x, y;
1365 :
1366 0 : if ( !gt->cursor_on || gt->sel_start != gt->sel_end )
1367 0 : return;
1368 0 : gt_cursor_pos(gt,&x,&y);
1369 :
1370 0 : if ( x<0 || x>=gt->g.inner.width )
1371 0 : return;
1372 0 : GDrawPushClip(pixmap,>->g.inner,&old);
1373 0 : GDrawSetXORMode(pixmap);
1374 0 : GDrawSetXORBase(pixmap,gt->g.box->main_background!=COLOR_DEFAULT?gt->g.box->main_background:
1375 0 : GDrawGetDefaultBackground(GDrawGetDisplayOfWindow(pixmap)) );
1376 0 : GDrawSetFont(pixmap,gt->font);
1377 0 : GDrawSetLineWidth(pixmap,0);
1378 0 : GDrawDrawLine(pixmap,gt->g.inner.x+x,gt->g.inner.y+y,
1379 0 : gt->g.inner.x+x,gt->g.inner.y+y+gt->fh,
1380 0 : gt->g.box->main_foreground!=COLOR_DEFAULT?gt->g.box->main_foreground:
1381 0 : GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)) );
1382 0 : GDrawSetCopyMode(pixmap);
1383 0 : GDrawPopClip(pixmap,&old);
1384 : }
1385 :
1386 0 : static void GTextFieldDrawDDCursor(GTextField *gt, int pos) {
1387 : GRect old;
1388 : int x, y, l;
1389 :
1390 0 : l = GTextFieldFindLine(gt,pos);
1391 0 : if ( l<gt->loff_top || l>=gt->loff_top + (gt->g.inner.height/gt->fh))
1392 0 : return;
1393 0 : _gt_cursor_pos(gt,pos,&x,&y);
1394 0 : if ( x<0 || x>=gt->g.inner.width )
1395 0 : return;
1396 :
1397 0 : GDrawPushClip(gt->g.base,>->g.inner,&old);
1398 0 : GDrawSetXORMode(gt->g.base);
1399 0 : GDrawSetXORBase(gt->g.base,gt->g.box->main_background!=COLOR_DEFAULT?gt->g.box->main_background:
1400 0 : GDrawGetDefaultBackground(GDrawGetDisplayOfWindow(gt->g.base)) );
1401 0 : GDrawSetFont(gt->g.base,gt->font);
1402 0 : GDrawSetLineWidth(gt->g.base,0);
1403 0 : GDrawSetDashedLine(gt->g.base,2,2,0);
1404 0 : GDrawDrawLine(gt->g.base,gt->g.inner.x+x,gt->g.inner.y+y,
1405 0 : gt->g.inner.x+x,gt->g.inner.y+y+gt->fh,
1406 0 : gt->g.box->main_foreground!=COLOR_DEFAULT?gt->g.box->main_foreground:
1407 0 : GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(gt->g.base)) );
1408 0 : GDrawSetCopyMode(gt->g.base);
1409 0 : GDrawPopClip(gt->g.base,&old);
1410 0 : GDrawSetDashedLine(gt->g.base,0,0,0);
1411 0 : gt->has_dd_cursor = !gt->has_dd_cursor;
1412 0 : gt->dd_cursor_pos = pos;
1413 : }
1414 :
1415 0 : static void GTextFieldDrawLineSel(GWindow pixmap, GTextField *gt, int line ) {
1416 : GRect selr, sofar, nextch;
1417 : int s,e, y,llen,i,j;
1418 :
1419 : /* Caller has checked to make sure selection applies to this line */
1420 :
1421 0 : y = gt->g.inner.y+(line-gt->loff_top)*gt->fh;
1422 0 : selr = gt->g.inner; selr.y = y; selr.height = gt->fh;
1423 0 : if ( !gt->g.has_focus ) --selr.height;
1424 0 : llen = gt->lines[line+1]==-1?
1425 0 : u_strlen(gt->text+gt->lines[line])+gt->lines[line]:
1426 0 : gt->lines[line+1];
1427 0 : s = gt->sel_start<gt->lines[line]?gt->lines[line]:gt->sel_start;
1428 0 : e = gt->sel_end>gt->lines[line+1] && gt->lines[line+1]!=-1?gt->lines[line+1]-1:
1429 0 : gt->sel_end;
1430 :
1431 0 : s = u2utf8_index(s-gt->lines[line],gt->utf8_text+gt->lines8[line]);
1432 0 : e = u2utf8_index(e-gt->lines[line],gt->utf8_text+gt->lines8[line]);
1433 0 : llen = gt->lines8[line+1]==-1? -1 : gt->lines8[line+1]-gt->lines8[line];
1434 0 : GDrawLayoutInit(pixmap,gt->utf8_text+gt->lines8[line],llen,NULL);
1435 0 : for ( i=s; i<e; ) {
1436 0 : GDrawLayoutIndexToPos(pixmap,i,&sofar);
1437 0 : for ( j=i+1; j<e; ++j ) {
1438 0 : GDrawLayoutIndexToPos(pixmap,j,&nextch);
1439 0 : if ( nextch.x != sofar.x+sofar.width )
1440 0 : break;
1441 0 : sofar.width += nextch.width;
1442 : }
1443 0 : if ( sofar.width<0 ) {
1444 0 : selr.x = sofar.x+sofar.width + gt->g.inner.x - gt->xoff_left;
1445 0 : selr.width = -sofar.width;
1446 : } else {
1447 0 : selr.x = sofar.x + gt->g.inner.x - gt->xoff_left;
1448 0 : selr.width = sofar.width;
1449 : }
1450 0 : GDrawFillRect(pixmap,&selr,gt->g.box->active_border);
1451 0 : i = j;
1452 : }
1453 0 : }
1454 :
1455 0 : static void GTextFieldDrawLine(GWindow pixmap, GTextField *gt, int line, Color fg ) {
1456 0 : int y = gt->g.inner.y+(line-gt->loff_top)*gt->fh;
1457 0 : int ll = gt->lines[line+1]==-1 ? -1 : gt->lines[line+1]-gt->lines[line];
1458 :
1459 0 : ll = gt->lines8[line+1]==-1? -1 : gt->lines8[line+1]-gt->lines8[line];
1460 0 : GDrawLayoutInit(pixmap,gt->utf8_text+gt->lines8[line],ll,NULL);
1461 0 : GDrawLayoutDraw(pixmap,gt->g.inner.x-gt->xoff_left,y+gt->as,fg);
1462 0 : }
1463 :
1464 0 : static int gtextfield_expose(GWindow pixmap, GGadget *g, GEvent *event) {
1465 0 : GTextField *gt = (GTextField *) g;
1466 0 : GListField *ge = (GListField *) g;
1467 0 : GRect old1, old2, *r = &g->r;
1468 : Color fg;
1469 : int ll,i, last;
1470 : GRect unpadded_inner;
1471 : int pad;
1472 :
1473 0 : if ( g->state == gs_invisible || gt->dontdraw )
1474 0 : return( false );
1475 :
1476 0 : if ( gt->listfield || gt->numericfield ) r = &ge->fieldrect;
1477 :
1478 0 : GDrawPushClip(pixmap,r,&old1);
1479 :
1480 0 : GBoxDrawBackground(pixmap,r,g->box,
1481 0 : g->state==gs_enabled? gs_pressedactive: g->state,false);
1482 0 : GBoxDrawBorder(pixmap,r,g->box,g->state,false);
1483 :
1484 0 : unpadded_inner = g->inner;
1485 0 : pad = GDrawPointsToPixels(g->base,g->box->padding);
1486 0 : unpadded_inner.x -= pad; unpadded_inner.y -= pad;
1487 0 : unpadded_inner.width += 2*pad; unpadded_inner.height += 2*pad;
1488 0 : GDrawPushClip(pixmap,&unpadded_inner,&old2);
1489 0 : GDrawSetFont(pixmap,gt->font);
1490 :
1491 0 : fg = g->state==gs_disabled?g->box->disabled_foreground:
1492 0 : g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
1493 0 : g->box->main_foreground;
1494 0 : ll = 0;
1495 0 : if ( (last = gt->g.inner.height/gt->fh)==0 ) last = 1;
1496 0 : if ( gt->sel_start != gt->sel_end ) {
1497 : /* I used to have support for drawing on a bw display where the */
1498 : /* selection and the foreground color were the same (black) and */
1499 : /* selected text was white. No longer. */
1500 : /* Draw the entire selection first, then the text itself */
1501 0 : for ( i=gt->loff_top; i<gt->loff_top+last && gt->lines[i]!=-1; ++i ) {
1502 0 : if ( gt->sel_end>gt->lines[i] &&
1503 0 : (gt->lines[i+1]==-1 || gt->sel_start<gt->lines[i+1]))
1504 0 : GTextFieldDrawLineSel(pixmap,gt,i);
1505 : }
1506 : }
1507 0 : for ( i=gt->loff_top; i<gt->loff_top+last && gt->lines[i]!=-1; ++i )
1508 0 : GTextFieldDrawLine(pixmap,gt,i,fg);
1509 :
1510 0 : GDrawPopClip(pixmap,&old2);
1511 0 : GDrawPopClip(pixmap,&old1);
1512 0 : gt_draw_cursor(pixmap, gt);
1513 :
1514 0 : if ( gt->listfield ) {
1515 0 : int marklen = GDrawPointsToPixels(pixmap,_GListMarkSize);
1516 :
1517 0 : GDrawPushClip(pixmap,&ge->buttonrect,&old1);
1518 :
1519 0 : GBoxDrawBackground(pixmap,&ge->buttonrect,&glistfieldmenu_box,
1520 0 : g->state==gs_enabled? gs_pressedactive: g->state,false);
1521 0 : GBoxDrawBorder(pixmap,&ge->buttonrect,&glistfieldmenu_box,g->state,false);
1522 :
1523 0 : GListMarkDraw(pixmap,
1524 0 : ge->buttonrect.x + (ge->buttonrect.width - marklen)/2,
1525 : g->inner.y,
1526 : g->inner.height,
1527 : g->state);
1528 0 : GDrawPopClip(pixmap,&old1);
1529 0 : } else if ( gt->numericfield ) {
1530 : int y, w;
1531 : int half;
1532 : GPoint pts[5];
1533 0 : int bp = GBoxBorderWidth(gt->g.base,&gnumericfieldspinner_box);
1534 0 : Color fg = g->state==gs_disabled?gnumericfieldspinner_box.disabled_foreground:
1535 0 : gnumericfieldspinner_box.main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
1536 : gnumericfieldspinner_box.main_foreground;
1537 :
1538 0 : GBoxDrawBackground(pixmap,&ge->buttonrect,&gnumericfieldspinner_box,
1539 0 : g->state==gs_enabled? gs_pressedactive: g->state,false);
1540 0 : GBoxDrawBorder(pixmap,&ge->buttonrect,&gnumericfieldspinner_box,g->state,false);
1541 : /* GDrawDrawRect(pixmap,&ge->buttonrect,fg); */
1542 :
1543 0 : y = ge->buttonrect.y + ge->buttonrect.height/2;
1544 0 : w = ge->buttonrect.width;
1545 0 : w &= ~1;
1546 0 : pts[0].x = ge->buttonrect.x+3+bp;
1547 0 : pts[1].x = ge->buttonrect.x+w-3-bp;
1548 0 : pts[2].x = ge->buttonrect.x + w/2;
1549 0 : half = pts[2].x-pts[0].x;
1550 0 : GDrawDrawLine(pixmap, pts[0].x-3,y, pts[1].x+3,y, fg );
1551 0 : pts[0].y = pts[1].y = y-2;
1552 0 : pts[2].y = pts[1].y-half;
1553 0 : pts[3] = pts[0];
1554 0 : GDrawFillPoly(pixmap,pts,3,fg);
1555 0 : pts[0].y = pts[1].y = y+2;
1556 0 : pts[2].y = pts[1].y+half;
1557 0 : pts[3] = pts[0];
1558 0 : GDrawFillPoly(pixmap,pts,3,fg);
1559 : }
1560 0 : return( true );
1561 : }
1562 :
1563 0 : static int glistfield_mouse(GListField *ge, GEvent *event) {
1564 0 : if ( event->type!=et_mousedown )
1565 0 : return( true );
1566 0 : if ( ge->popup != NULL ) {
1567 0 : GDrawDestroyWindow(ge->popup);
1568 0 : ge->popup = NULL;
1569 0 : return( true );
1570 : }
1571 0 : ge->popup = GListPopupCreate(&ge->gt.g,GListFieldSelected,ge->ti);
1572 0 : return( true );
1573 : }
1574 :
1575 0 : static int gnumericfield_mouse(GTextField *gt, GEvent *event) {
1576 0 : GListField *ge = (GListField *) gt;
1577 0 : if ( event->type==et_mousedown ) {
1578 0 : gt->incr_down = event->u.mouse.y > (ge->buttonrect.y + ge->buttonrect.height/2);
1579 0 : GTextFieldIncrement(gt,gt->incr_down?-1:1);
1580 0 : if ( gt->numeric_scroll==NULL )
1581 0 : gt->numeric_scroll = GDrawRequestTimer(gt->g.base,200,100,NULL);
1582 0 : } else if ( gt->numeric_scroll!=NULL ) {
1583 0 : GDrawCancelTimer(gt->numeric_scroll);
1584 0 : gt->numeric_scroll = NULL;
1585 : }
1586 0 : return( true );
1587 : }
1588 :
1589 0 : static int GTextFieldDoDrop(GTextField *gt,GEvent *event,int endpos) {
1590 :
1591 0 : if ( gt->has_dd_cursor )
1592 0 : GTextFieldDrawDDCursor(gt,gt->dd_cursor_pos);
1593 :
1594 0 : if ( event->type == et_mousemove ) {
1595 0 : if ( GGadgetInnerWithin(>->g,event->u.mouse.x,event->u.mouse.y) ) {
1596 0 : if ( endpos<gt->sel_start || endpos>=gt->sel_end )
1597 0 : GTextFieldDrawDDCursor(gt,endpos);
1598 0 : } else if ( !GGadgetWithin(>->g,event->u.mouse.x,event->u.mouse.y) ) {
1599 0 : GDrawPostDragEvent(gt->g.base,event,et_drag);
1600 : }
1601 : } else {
1602 0 : if ( GGadgetInnerWithin(>->g,event->u.mouse.x,event->u.mouse.y) ) {
1603 0 : if ( endpos>=gt->sel_start && endpos<gt->sel_end ) {
1604 0 : gt->sel_start = gt->sel_end = endpos;
1605 : } else {
1606 0 : unichar_t *old=gt->oldtext, *temp;
1607 0 : int pos=0;
1608 0 : if ( event->u.mouse.state&ksm_control ) {
1609 0 : temp = malloc((u_strlen(gt->text)+gt->sel_end-gt->sel_start+1)*sizeof(unichar_t));
1610 0 : memcpy(temp,gt->text,endpos*sizeof(unichar_t));
1611 0 : memcpy(temp+endpos,gt->text+gt->sel_start,
1612 0 : (gt->sel_end-gt->sel_start)*sizeof(unichar_t));
1613 0 : u_strcpy(temp+endpos+gt->sel_end-gt->sel_start,gt->text+endpos);
1614 0 : } else if ( endpos>=gt->sel_end ) {
1615 0 : temp = u_copy(gt->text);
1616 0 : memcpy(temp+gt->sel_start,temp+gt->sel_end,
1617 0 : (endpos-gt->sel_end)*sizeof(unichar_t));
1618 0 : memcpy(temp+endpos-(gt->sel_end-gt->sel_start),
1619 0 : gt->text+gt->sel_start,(gt->sel_end-gt->sel_start)*sizeof(unichar_t));
1620 0 : pos = endpos;
1621 : } else /*if ( endpos<gt->sel_start )*/ {
1622 0 : temp = u_copy(gt->text);
1623 0 : memcpy(temp+endpos,gt->text+gt->sel_start,
1624 0 : (gt->sel_end-gt->sel_start)*sizeof(unichar_t));
1625 0 : memcpy(temp+endpos+gt->sel_end-gt->sel_start,gt->text+endpos,
1626 0 : (gt->sel_start-endpos)*sizeof(unichar_t));
1627 0 : pos = endpos+gt->sel_end-gt->sel_start;
1628 : }
1629 0 : gt->oldtext = gt->text;
1630 0 : gt->sel_oldstart = gt->sel_start;
1631 0 : gt->sel_oldend = gt->sel_end;
1632 0 : gt->sel_oldbase = gt->sel_base;
1633 0 : gt->sel_start = gt->sel_end = pos;
1634 0 : gt->text = temp;
1635 0 : free(old);
1636 0 : GTextFieldRefigureLines(gt, endpos<gt->sel_oldstart?endpos:gt->sel_oldstart);
1637 : }
1638 0 : } else if ( !GGadgetWithin(>->g,event->u.mouse.x,event->u.mouse.y) ) {
1639 : /* Don't delete the selection until someone actually accepts the drop */
1640 : /* Don't delete at all (copy not move) if control key is down */
1641 0 : if ( ( event->u.mouse.state&ksm_control ) )
1642 0 : GTextFieldGrabSelection(gt,sn_drag_and_drop);
1643 : else
1644 0 : GTextFieldGrabDDSelection(gt);
1645 0 : GDrawPostDragEvent(gt->g.base,event,et_drop);
1646 : }
1647 0 : gt->drag_and_drop = false;
1648 0 : GDrawSetCursor(gt->g.base,gt->old_cursor);
1649 0 : _ggadget_redraw(>->g);
1650 : }
1651 0 : return( false );
1652 : }
1653 :
1654 0 : static int gtextfield_mouse(GGadget *g, GEvent *event) {
1655 0 : GTextField *gt = (GTextField *) g;
1656 0 : GListField *ge = (GListField *) g;
1657 0 : unichar_t *end=NULL, *end1, *end2;
1658 0 : int i=0,ll;
1659 :
1660 0 : if ( gt->hidden_cursor ) {
1661 0 : GDrawSetCursor(gt->g.base,gt->old_cursor);
1662 0 : gt->hidden_cursor = false;
1663 0 : _GWidget_ClearGrabGadget(g);
1664 : }
1665 0 : if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
1666 0 : return( false );
1667 0 : if ( event->type == et_crossing )
1668 0 : return( false );
1669 0 : if ( gt->completionfield && ((GCompletionField *) gt)->choice_popup!=NULL &&
1670 0 : event->type==et_mousedown )
1671 0 : GCompletionDestroy((GCompletionField *) gt);
1672 0 : if (( gt->listfield && event->u.mouse.x>=ge->buttonrect.x &&
1673 0 : event->u.mouse.x<ge->buttonrect.x+ge->buttonrect.width &&
1674 0 : event->u.mouse.y>=ge->buttonrect.y &&
1675 0 : event->u.mouse.y<ge->buttonrect.y+ge->buttonrect.height ) ||
1676 0 : ( gt->listfield && ge->popup!=NULL ))
1677 0 : return( glistfield_mouse(ge,event));
1678 0 : if ( gt->numericfield && event->u.mouse.x>=ge->buttonrect.x &&
1679 0 : event->u.mouse.x<ge->buttonrect.x+ge->buttonrect.width &&
1680 0 : event->u.mouse.y>=ge->buttonrect.y &&
1681 0 : event->u.mouse.y<ge->buttonrect.y+ge->buttonrect.height )
1682 0 : return( gnumericfield_mouse(gt,event));
1683 0 : if (( event->type==et_mouseup || event->type==et_mousedown ) &&
1684 0 : (event->u.mouse.button>=4 && event->u.mouse.button<=7)) {
1685 0 : int isv = event->u.mouse.button<=5;
1686 0 : if ( event->u.mouse.state&ksm_shift ) isv = !isv;
1687 0 : if ( isv && gt->vsb!=NULL )
1688 0 : return( GGadgetDispatchEvent(>->vsb->g,event));
1689 0 : else if ( !isv && gt->hsb!=NULL )
1690 0 : return( GGadgetDispatchEvent(>->hsb->g,event));
1691 : else
1692 0 : return( true );
1693 : }
1694 :
1695 0 : if ( gt->pressed==NULL && event->type == et_mousemove && g->popup_msg!=NULL &&
1696 0 : GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y))
1697 0 : GGadgetPreparePopup(g->base,g->popup_msg);
1698 :
1699 0 : if ( event->type == et_mousedown || gt->pressed ) {
1700 0 : i = (event->u.mouse.y-g->inner.y)/gt->fh + gt->loff_top;
1701 0 : if ( i<0 ) i = 0;
1702 0 : if ( !gt->multi_line ) i = 0;
1703 0 : if ( i>=gt->lcnt )
1704 0 : end = gt->text+u_strlen(gt->text);
1705 : else
1706 0 : end = GTextFieldGetPtFromPos(gt,i,event->u.mouse.x);
1707 : }
1708 :
1709 0 : if ( event->type == et_mousedown ) {
1710 0 : if ( event->u.mouse.button==3 &&
1711 0 : GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
1712 0 : GTFPopupMenu(gt,event);
1713 0 : return( true );
1714 : }
1715 :
1716 0 : if ( i>=gt->lcnt )
1717 0 : end1 = end2 = end;
1718 : else {
1719 : int end8;
1720 0 : ll = gt->lines8[i+1]==-1?-1:gt->lines8[i+1]-gt->lines8[i]-1;
1721 0 : GDrawLayoutInit(gt->g.base,gt->utf8_text+gt->lines8[i],ll,NULL);
1722 0 : end8 = GDrawLayoutXYToIndex(gt->g.base,event->u.mouse.x-g->inner.x+gt->xoff_left,0);
1723 0 : end1 = end2 = gt->text + gt->lines[i] + utf82u_index(end8,gt->utf8_text+gt->lines8[i]);
1724 : }
1725 :
1726 0 : gt->wordsel = gt->linesel = false;
1727 0 : if ( event->u.mouse.button==1 && event->u.mouse.clicks>=3 ) {
1728 0 : gt->sel_start = gt->lines[i]; gt->sel_end = gt->lines[i+1];
1729 0 : if ( gt->sel_end==-1 ) gt->sel_end = u_strlen(gt->text);
1730 0 : gt->wordsel = false; gt->linesel = true;
1731 0 : } else if ( event->u.mouse.button==1 && event->u.mouse.clicks==2 ) {
1732 0 : gt->sel_start = gt->sel_end = gt->sel_base = end-gt->text;
1733 0 : gt->wordsel = true;
1734 0 : GTextFieldSelectWords(gt,gt->sel_base);
1735 0 : } else if ( end1-gt->text>=gt->sel_start && end2-gt->text<gt->sel_end &&
1736 0 : gt->sel_start!=gt->sel_end &&
1737 0 : event->u.mouse.button==1 ) {
1738 0 : gt->drag_and_drop = true;
1739 0 : if ( !gt->hidden_cursor )
1740 0 : gt->old_cursor = GDrawGetCursor(gt->g.base);
1741 0 : GDrawSetCursor(gt->g.base,ct_draganddrop);
1742 0 : } else if ( /*event->u.mouse.button!=3 &&*/ !(event->u.mouse.state&ksm_shift) ) {
1743 0 : if ( event->u.mouse.button==1 )
1744 0 : GTextFieldGrabPrimarySelection(gt);
1745 0 : gt->sel_start = gt->sel_end = gt->sel_base = end-gt->text;
1746 0 : } else if ( end-gt->text>gt->sel_base ) {
1747 0 : gt->sel_start = gt->sel_base;
1748 0 : gt->sel_end = end-gt->text;
1749 : } else {
1750 0 : gt->sel_start = end-gt->text;
1751 0 : gt->sel_end = gt->sel_base;
1752 : }
1753 :
1754 0 : if ( gt->pressed==NULL )
1755 0 : gt->pressed = GDrawRequestTimer(gt->g.base,200,100,NULL);
1756 0 : if ( gt->sel_start > u_strlen( gt->text )) /* Ok to have selection at end, but beyond is an error */
1757 0 : fprintf( stderr, "About to crash\n" );
1758 0 : _ggadget_redraw(g);
1759 0 : return( true );
1760 0 : } else if ( gt->pressed && (event->type == et_mousemove || event->type == et_mouseup )) {
1761 0 : int refresh = true;
1762 :
1763 0 : if ( gt->drag_and_drop ) {
1764 0 : refresh = GTextFieldDoDrop(gt,event,end-gt->text);
1765 0 : } else if ( gt->linesel ) {
1766 : int j, e;
1767 0 : gt->sel_start = gt->lines[i]; gt->sel_end = gt->lines[i+1];
1768 0 : if ( gt->sel_end==-1 ) gt->sel_end = u_strlen(gt->text);
1769 0 : for ( j=0; gt->lines[i+1]!=-1 && gt->sel_base>=gt->lines[i+1]; ++j );
1770 0 : if ( gt->sel_start<gt->lines[i] ) gt->sel_start = gt->lines[i];
1771 0 : e = gt->lines[j+1]==-1 ? u_strlen(gt->text): gt->lines[j+1];
1772 0 : if ( e>gt->sel_end ) gt->sel_end = e;
1773 0 : } else if ( gt->wordsel )
1774 0 : GTextFieldSelectWords(gt,end-gt->text);
1775 0 : else if ( event->u.mouse.button!=2 ) {
1776 0 : int e = end-gt->text;
1777 0 : if ( e>gt->sel_base ) {
1778 0 : gt->sel_start = gt->sel_base; gt->sel_end = e;
1779 : } else {
1780 0 : gt->sel_start = e; gt->sel_end = gt->sel_base;
1781 : }
1782 : }
1783 0 : if ( event->type==et_mouseup ) {
1784 0 : GDrawCancelTimer(gt->pressed); gt->pressed = NULL;
1785 0 : if ( event->u.mouse.button==2 )
1786 0 : GTextFieldPaste(gt,sn_primary);
1787 0 : if ( gt->sel_start==gt->sel_end )
1788 0 : GTextField_Show(gt,gt->sel_start);
1789 0 : GTextFieldChanged(gt,-1);
1790 0 : if ( gt->sel_start<gt->sel_end && _GDraw_InsCharHook!=NULL && !gt->donthook )
1791 0 : (_GDraw_InsCharHook)(GDrawGetDisplayOfWindow(gt->g.base),
1792 0 : gt->text[gt->sel_start]);
1793 : }
1794 0 : if ( gt->sel_end > u_strlen( gt->text ))
1795 0 : fprintf( stderr, "About to crash\n" );
1796 0 : if ( refresh )
1797 0 : _ggadget_redraw(g);
1798 0 : return( true );
1799 : }
1800 0 : return( false );
1801 : }
1802 :
1803 0 : static int gtextfield_key(GGadget *g, GEvent *event) {
1804 0 : GTextField *gt = (GTextField *) g;
1805 :
1806 0 : if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
1807 0 : return( false );
1808 0 : if ( gt->listfield && ((GListField *) gt)->popup!=NULL ) {
1809 0 : GWindow popup = ((GListField *) gt)->popup;
1810 0 : (GDrawGetEH(popup))(popup,event);
1811 0 : return( true );
1812 : }
1813 :
1814 0 : if ( gt->completionfield && ((GCompletionField *) gt)->choice_popup!=NULL &&
1815 0 : GCompletionHandleKey(gt,event))
1816 0 : return( true );
1817 :
1818 0 : if ( event->type == et_charup )
1819 0 : return( false );
1820 0 : if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ||
1821 0 : (event->u.chr.keysym == GK_Return && !gt->accepts_returns ) ||
1822 0 : ( event->u.chr.keysym == GK_Tab && !gt->accepts_tabs ) ||
1823 0 : event->u.chr.keysym == GK_BackTab || event->u.chr.keysym == GK_Escape )
1824 0 : return( false );
1825 :
1826 0 : if ( !gt->hidden_cursor ) { /* hide the mouse pointer */
1827 0 : if ( !gt->drag_and_drop )
1828 0 : gt->old_cursor = GDrawGetCursor(gt->g.base);
1829 0 : GDrawSetCursor(g->base,ct_invisible);
1830 0 : gt->hidden_cursor = true;
1831 0 : _GWidget_SetGrabGadget(g); /* so that we get the next mouse movement to turn the cursor on */
1832 : }
1833 0 : if( gt->cursor_on ) { /* undraw the blinky text cursor if it is drawn */
1834 0 : gt_draw_cursor(g->base, gt);
1835 0 : gt->cursor_on = false;
1836 : }
1837 :
1838 0 : switch ( GTextFieldDoChange(gt,event)) {
1839 : case 4:
1840 : /* We should try name completion */
1841 0 : if ( gt->completionfield && ((GCompletionField *) gt)->completion!=NULL &&
1842 0 : gt->was_completing && gt->sel_start == u_strlen(gt->text))
1843 0 : GTextFieldComplete(gt,false);
1844 : else
1845 0 : GTextFieldChanged(gt,-1);
1846 0 : break;
1847 : case 3:
1848 : /* They typed a Tab */
1849 0 : break;
1850 : case 2:
1851 0 : break;
1852 : case true:
1853 0 : GTextFieldChanged(gt,-1);
1854 0 : break;
1855 : case false:
1856 0 : return( false );
1857 : }
1858 0 : _ggadget_redraw(g);
1859 0 : return( true );
1860 : }
1861 :
1862 0 : static int gtextfield_focus(GGadget *g, GEvent *event) {
1863 0 : GTextField *gt = (GTextField *) g;
1864 :
1865 0 : if ( g->state == gs_invisible || g->state == gs_disabled )
1866 0 : return( false );
1867 :
1868 0 : if ( gt->cursor!=NULL ) {
1869 0 : GDrawCancelTimer(gt->cursor);
1870 0 : gt->cursor = NULL;
1871 0 : gt->cursor_on = false;
1872 : }
1873 0 : if ( gt->hidden_cursor && !event->u.focus.gained_focus ) {
1874 0 : GDrawSetCursor(gt->g.base,gt->old_cursor);
1875 0 : gt->hidden_cursor = false;
1876 : }
1877 0 : gt->g.has_focus = event->u.focus.gained_focus;
1878 0 : if ( event->u.focus.gained_focus ) {
1879 0 : gt->cursor = GDrawRequestTimer(gt->g.base,400,400,NULL);
1880 0 : gt->cursor_on = true;
1881 0 : if ( event->u.focus.mnemonic_focus != mf_normal )
1882 0 : GTextFieldSelect(>->g,0,-1);
1883 0 : if ( gt->gic!=NULL )
1884 0 : GTPositionGIC(gt);
1885 0 : else if ( GWidgetGetInputContext(gt->g.base)!=NULL )
1886 0 : GDrawSetGIC(gt->g.base,GWidgetGetInputContext(gt->g.base),10000,10000);
1887 : }
1888 0 : _ggadget_redraw(g);
1889 0 : GTextFieldFocusChanged(gt,event->u.focus.gained_focus);
1890 0 : return( true );
1891 : }
1892 :
1893 0 : static int gtextfield_timer(GGadget *g, GEvent *event) {
1894 0 : GTextField *gt = (GTextField *) g;
1895 :
1896 0 : if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
1897 0 : return(false);
1898 0 : if ( gt->cursor == event->u.timer.timer ) {
1899 0 : if ( gt->cursor_on ) {
1900 0 : gt_draw_cursor(g->base, gt);
1901 0 : gt->cursor_on = false;
1902 : } else {
1903 0 : gt->cursor_on = true;
1904 0 : gt_draw_cursor(g->base, gt);
1905 : }
1906 0 : return( true );
1907 : }
1908 0 : if ( gt->numeric_scroll == event->u.timer.timer ) {
1909 0 : GTextFieldIncrement(gt,gt->incr_down?-1:1);
1910 0 : return( true );
1911 : }
1912 0 : if ( gt->pressed == event->u.timer.timer ) {
1913 : GEvent e;
1914 0 : GDrawSetFont(g->base,gt->font);
1915 0 : GDrawGetPointerPosition(g->base,&e);
1916 0 : if ( (e.u.mouse.x<g->r.x && gt->xoff_left>0 ) ||
1917 0 : (gt->multi_line && e.u.mouse.y<g->r.y && gt->loff_top>0 ) ||
1918 0 : ( e.u.mouse.x >= g->r.x + g->r.width &&
1919 0 : gt->xmax-gt->xoff_left>g->inner.width ) ||
1920 0 : ( e.u.mouse.y >= g->r.y + g->r.height &&
1921 0 : gt->lcnt-gt->loff_top > g->inner.height/gt->fh )) {
1922 0 : int l = gt->loff_top + (e.u.mouse.y-g->inner.y)/gt->fh;
1923 : int xpos; unichar_t *end;
1924 :
1925 0 : if ( e.u.mouse.y<g->r.y && gt->loff_top>0 )
1926 0 : l = --gt->loff_top;
1927 0 : else if ( e.u.mouse.y >= g->r.y + g->r.height &&
1928 0 : gt->lcnt-gt->loff_top > g->inner.height/gt->fh ) {
1929 0 : ++gt->loff_top;
1930 0 : l = gt->loff_top + g->inner.width/gt->fh;
1931 0 : } else if ( l<gt->loff_top )
1932 0 : l = gt->loff_top;
1933 0 : else if ( l>=gt->loff_top + g->inner.height/gt->fh )
1934 0 : l = gt->loff_top + g->inner.height/gt->fh-1;
1935 0 : if ( l>=gt->lcnt ) l = gt->lcnt-1;
1936 :
1937 0 : xpos = e.u.mouse.x+gt->xoff_left;
1938 0 : if ( e.u.mouse.x<g->r.x && gt->xoff_left>0 ) {
1939 0 : gt->xoff_left -= gt->nw;
1940 0 : xpos = g->inner.x + gt->xoff_left;
1941 0 : } else if ( e.u.mouse.x >= g->r.x + g->r.width &&
1942 0 : gt->xmax-gt->xoff_left>g->inner.width ) {
1943 0 : gt->xoff_left += gt->nw;
1944 0 : xpos = g->inner.x + gt->xoff_left + g->inner.width;
1945 : }
1946 :
1947 0 : end = GTextFieldGetPtFromPos(gt,l,xpos);
1948 0 : if ( end-gt->text > gt->sel_base ) {
1949 0 : gt->sel_start = gt->sel_base;
1950 0 : gt->sel_end = end-gt->text;
1951 : } else {
1952 0 : gt->sel_start = end-gt->text;
1953 0 : gt->sel_end = gt->sel_base;
1954 : }
1955 0 : _ggadget_redraw(g);
1956 0 : if ( gt->vsb!=NULL )
1957 0 : GScrollBarSetPos(>->vsb->g,gt->loff_top);
1958 0 : if ( gt->hsb!=NULL )
1959 0 : GScrollBarSetPos(>->hsb->g,gt->xoff_left);
1960 : }
1961 0 : return( true );
1962 : }
1963 0 : return( false );
1964 : }
1965 :
1966 0 : static int gtextfield_sel(GGadget *g, GEvent *event) {
1967 0 : GTextField *gt = (GTextField *) g;
1968 : unichar_t *end;
1969 : int i;
1970 :
1971 0 : if ( event->type == et_selclear ) {
1972 0 : if ( event->u.selclear.sel==sn_primary && gt->sel_start!=gt->sel_end ) {
1973 0 : gt->sel_start = gt->sel_end = gt->sel_base;
1974 0 : _ggadget_redraw(g);
1975 0 : return( true );
1976 : }
1977 0 : return( false );
1978 : }
1979 :
1980 0 : if ( gt->has_dd_cursor )
1981 0 : GTextFieldDrawDDCursor(gt,gt->dd_cursor_pos);
1982 0 : GDrawSetFont(g->base,gt->font);
1983 0 : i = (event->u.drag_drop.y-g->inner.y)/gt->fh + gt->loff_top;
1984 0 : if ( !gt->multi_line ) i = 0;
1985 0 : if ( i>=gt->lcnt )
1986 0 : end = gt->text+u_strlen(gt->text);
1987 : else
1988 0 : end = GTextFieldGetPtFromPos(gt,i,event->u.drag_drop.x);
1989 0 : if ( event->type == et_drag ) {
1990 0 : GTextFieldDrawDDCursor(gt,end-gt->text);
1991 0 : } else if ( event->type == et_dragout ) {
1992 : /* this event exists simply to clear the dd cursor line. We've done */
1993 : /* that already */
1994 0 : } else if ( event->type == et_drop ) {
1995 0 : gt->sel_start = gt->sel_end = gt->sel_base = end-gt->text;
1996 0 : GTextFieldPaste(gt,sn_drag_and_drop);
1997 0 : GTextField_Show(gt,gt->sel_start);
1998 0 : GTextFieldChanged(gt,-1);
1999 0 : _ggadget_redraw(>->g);
2000 : } else
2001 0 : return( false );
2002 :
2003 0 : return( true );
2004 : }
2005 :
2006 0 : static void gtextfield_destroy(GGadget *g) {
2007 0 : GTextField *gt = (GTextField *) g;
2008 :
2009 0 : if ( gt==NULL )
2010 0 : return;
2011 0 : if ( gt->listfield ) {
2012 0 : GListField *glf = (GListField *) g;
2013 0 : if ( glf->popup ) {
2014 0 : GDrawDestroyWindow(glf->popup);
2015 0 : GDrawSync(NULL);
2016 0 : GDrawProcessWindowEvents(glf->popup); /* popup's destroy routine must execute before we die */
2017 : }
2018 0 : GTextInfoArrayFree(glf->ti);
2019 : }
2020 0 : if ( gt->completionfield )
2021 0 : GCompletionDestroy((GCompletionField *) g);
2022 :
2023 0 : if ( gt->vsb!=NULL )
2024 0 : (gt->vsb->g.funcs->destroy)(>->vsb->g);
2025 0 : if ( gt->hsb!=NULL )
2026 0 : (gt->hsb->g.funcs->destroy)(>->hsb->g);
2027 0 : GDrawCancelTimer(gt->numeric_scroll);
2028 0 : GDrawCancelTimer(gt->pressed);
2029 0 : GDrawCancelTimer(gt->cursor);
2030 0 : free(gt->lines);
2031 0 : free(gt->oldtext);
2032 0 : free(gt->text);
2033 0 : free(gt->utf8_text);
2034 0 : free(gt->lines8);
2035 0 : _ggadget_destroy(g);
2036 : }
2037 :
2038 0 : static void GTextFieldSetTitle(GGadget *g,const unichar_t *tit) {
2039 0 : GTextField *gt = (GTextField *) g;
2040 0 : unichar_t *old = gt->oldtext;
2041 0 : if ( tit==NULL || u_strcmp(tit,gt->text)==0 ) /* If it doesn't change anything, then don't trash undoes or selection */
2042 0 : return;
2043 0 : gt->oldtext = gt->text;
2044 0 : gt->sel_oldstart = gt->sel_start; gt->sel_oldend = gt->sel_end; gt->sel_oldbase = gt->sel_base;
2045 0 : gt->text = u_copy(tit); /* tit might be oldtext, so must copy before freeing */
2046 0 : free(old);
2047 0 : free(gt->utf8_text);
2048 0 : gt->utf8_text = u2utf8_copy(gt->text);
2049 0 : gt->sel_start = gt->sel_end = gt->sel_base = u_strlen(tit);
2050 0 : GTextFieldRefigureLines(gt,0);
2051 0 : GTextField_Show(gt,gt->sel_start);
2052 0 : _ggadget_redraw(g);
2053 : }
2054 :
2055 0 : static const unichar_t *_GTextFieldGetTitle(GGadget *g) {
2056 0 : GTextField *gt = (GTextField *) g;
2057 0 : return( gt->text );
2058 : }
2059 :
2060 0 : static void GTextFieldSetFont(GGadget *g,FontInstance *new) {
2061 0 : GTextField *gt = (GTextField *) g;
2062 0 : gt->font = new;
2063 0 : GTextFieldRefigureLines(gt,0);
2064 0 : }
2065 :
2066 0 : static FontInstance *GTextFieldGetFont(GGadget *g) {
2067 0 : GTextField *gt = (GTextField *) g;
2068 0 : return( gt->font );
2069 : }
2070 :
2071 0 : void GTextFieldShow(GGadget *g,int pos) {
2072 0 : GTextField *gt = (GTextField *) g;
2073 :
2074 0 : GTextField_Show(gt,pos);
2075 0 : _ggadget_redraw(g);
2076 0 : }
2077 :
2078 0 : void GTextFieldSelect(GGadget *g,int start, int end) {
2079 0 : GTextField *gt = (GTextField *) g;
2080 :
2081 0 : GTextFieldGrabPrimarySelection(gt);
2082 0 : if ( end<0 ) {
2083 0 : end = u_strlen(gt->text);
2084 0 : if ( start<0 ) start = end;
2085 : }
2086 0 : if ( start>end ) { int temp = start; start = end; end = temp; }
2087 0 : if ( end>u_strlen(gt->text)) end = u_strlen(gt->text);
2088 0 : if ( start>u_strlen(gt->text)) start = end;
2089 0 : else if ( start<0 ) start=0;
2090 0 : gt->sel_start = gt->sel_base = start;
2091 0 : gt->sel_end = end;
2092 0 : _ggadget_redraw(g); /* Should be safe just to draw the textfield gadget, sbs won't have changed */
2093 0 : }
2094 :
2095 0 : void GTextFieldReplace(GGadget *g,const unichar_t *txt) {
2096 0 : GTextField *gt = (GTextField *) g;
2097 :
2098 0 : GTextField_Replace(gt,txt);
2099 0 : _ggadget_redraw(g);
2100 0 : }
2101 :
2102 0 : static void GListFSelectOne(GGadget *g, int32 pos) {
2103 0 : GListField *gl = (GListField *) g;
2104 : int i;
2105 :
2106 0 : for ( i=0; i<gl->ltot; ++i )
2107 0 : gl->ti[i]->selected = false;
2108 0 : if ( pos>=gl->ltot ) pos = gl->ltot-1;
2109 0 : if ( pos<0 ) pos = 0;
2110 0 : if ( gl->ltot>0 ) {
2111 0 : gl->ti[pos]->selected = true;
2112 0 : GTextFieldSetTitle(g,gl->ti[pos]->text);
2113 : }
2114 0 : }
2115 :
2116 0 : static int32 GListFIsSelected(GGadget *g, int32 pos) {
2117 0 : GListField *gl = (GListField *) g;
2118 :
2119 0 : if ( pos>=gl->ltot )
2120 0 : return( false );
2121 0 : if ( pos<0 )
2122 0 : return( false );
2123 0 : if ( gl->ltot>0 )
2124 0 : return( gl->ti[pos]->selected );
2125 :
2126 0 : return( false );
2127 : }
2128 :
2129 0 : static int32 GListFGetFirst(GGadget *g) {
2130 : int i;
2131 0 : GListField *gl = (GListField *) g;
2132 :
2133 0 : for ( i=0; i<gl->ltot; ++i )
2134 0 : if ( gl->ti[i]->selected )
2135 0 : return( i );
2136 :
2137 0 : return( -1 );
2138 : }
2139 :
2140 0 : static GTextInfo **GListFGet(GGadget *g,int32 *len) {
2141 0 : GListField *gl = (GListField *) g;
2142 0 : if ( len!=NULL ) *len = gl->ltot;
2143 0 : return( gl->ti );
2144 : }
2145 :
2146 0 : static GTextInfo *GListFGetItem(GGadget *g,int32 pos) {
2147 0 : GListField *gl = (GListField *) g;
2148 0 : if ( pos<0 || pos>=gl->ltot )
2149 0 : return( NULL );
2150 :
2151 0 : return(gl->ti[pos]);
2152 : }
2153 :
2154 0 : static void GListFSet(GGadget *g,GTextInfo **ti,int32 docopy) {
2155 0 : GListField *gl = (GListField *) g;
2156 :
2157 0 : GTextInfoArrayFree(gl->ti);
2158 0 : if ( docopy || ti==NULL )
2159 0 : ti = GTextInfoArrayCopy(ti);
2160 0 : gl->ti = ti;
2161 0 : gl->ltot = GTextInfoArrayCount(ti);
2162 0 : }
2163 :
2164 0 : static void GListFClear(GGadget *g) {
2165 0 : GListFSet(g,NULL,true);
2166 0 : }
2167 :
2168 0 : static void gtextfield_redraw(GGadget *g) {
2169 0 : GTextField *gt = (GTextField *) g;
2170 0 : if ( gt->vsb!=NULL )
2171 0 : _ggadget_redraw((GGadget *) (gt->vsb));
2172 0 : if ( gt->hsb!=NULL )
2173 0 : _ggadget_redraw((GGadget *) (gt->hsb));
2174 0 : _ggadget_redraw(g);
2175 0 : }
2176 :
2177 0 : static void gtextfield_move(GGadget *g, int32 x, int32 y ) {
2178 0 : GTextField *gt = (GTextField *) g;
2179 0 : int fxo=0, fyo=0, bxo, byo;
2180 :
2181 0 : if ( gt->listfield || gt->numericfield ) {
2182 0 : fxo = ((GListField *) gt)->fieldrect.x - g->r.x;
2183 0 : fyo = ((GListField *) gt)->fieldrect.y - g->r.y;
2184 0 : bxo = ((GListField *) gt)->buttonrect.x - g->r.x;
2185 0 : byo = ((GListField *) gt)->buttonrect.y - g->r.y;
2186 : }
2187 0 : if ( gt->vsb!=NULL )
2188 0 : _ggadget_move((GGadget *) (gt->vsb),x+(gt->vsb->g.r.x-g->r.x),y);
2189 0 : if ( gt->hsb!=NULL )
2190 0 : _ggadget_move((GGadget *) (gt->hsb),x,y+(gt->hsb->g.r.y-g->r.y));
2191 0 : _ggadget_move(g,x,y);
2192 0 : if ( gt->listfield || gt->numericfield ) {
2193 0 : ((GListField *) gt)->fieldrect.x = g->r.x + fxo;
2194 0 : ((GListField *) gt)->fieldrect.y = g->r.y + fyo;
2195 0 : ((GListField *) gt)->buttonrect.x = g->r.x + bxo;
2196 0 : ((GListField *) gt)->buttonrect.y = g->r.y + byo;
2197 : }
2198 0 : }
2199 :
2200 0 : static void gtextfield_resize(GGadget *g, int32 width, int32 height ) {
2201 0 : GTextField *gt = (GTextField *) g;
2202 0 : int gtwidth=width, gtheight=height, oldheight=0;
2203 0 : int fxo=0, fwo=0, fyo=0, bxo, byo;
2204 : int l;
2205 :
2206 0 : if ( gt->listfield || gt->numericfield ) {
2207 0 : fxo = ((GListField *) gt)->fieldrect.x - g->r.x;
2208 0 : fwo = g->r.width - ((GListField *) gt)->fieldrect.width;
2209 0 : fyo = ((GListField *) gt)->fieldrect.y - g->r.y;
2210 0 : bxo = g->r.x+g->r.width - ((GListField *) gt)->buttonrect.x;
2211 0 : byo = ((GListField *) gt)->buttonrect.y - g->r.y;
2212 : }
2213 0 : if ( gt->hsb!=NULL ) {
2214 0 : oldheight = gt->hsb->g.r.y+gt->hsb->g.r.height-g->r.y;
2215 0 : gtheight = height - (oldheight-g->r.height);
2216 : }
2217 0 : if ( gt->vsb!=NULL ) {
2218 0 : int oldwidth = gt->vsb->g.r.x+gt->vsb->g.r.width-g->r.x;
2219 0 : gtwidth = width - (oldwidth-g->r.width);
2220 0 : _ggadget_move((GGadget *) (gt->vsb),gt->vsb->g.r.x+width-oldwidth,gt->vsb->g.r.y);
2221 0 : _ggadget_resize((GGadget *) (gt->vsb),gt->vsb->g.r.width,gtheight);
2222 : }
2223 0 : if ( gt->hsb!=NULL ) {
2224 0 : _ggadget_move((GGadget *) (gt->hsb),gt->hsb->g.r.x,gt->hsb->g.r.y+height-oldheight);
2225 0 : _ggadget_resize((GGadget *) (gt->hsb),gtwidth,gt->hsb->g.r.height);
2226 : }
2227 0 : _ggadget_resize(g,gtwidth, gtheight);
2228 :
2229 0 : if ( gt->hsb==NULL && gt->xoff_left!=0 && !gt->multi_line &&
2230 0 : GDrawGetTextWidth(gt->g.base,gt->text,-1)<gt->g.inner.width )
2231 0 : gt->xoff_left = 0;
2232 :
2233 0 : GTextFieldRefigureLines(gt,0);
2234 0 : if ( gt->vsb!=NULL ) {
2235 0 : GScrollBarSetBounds(>->vsb->g,0,gt->lcnt,
2236 0 : gt->g.inner.height<gt->fh ? 1 : gt->g.inner.height/gt->fh);
2237 0 : l = gt->loff_top;
2238 0 : if ( gt->loff_top>gt->lcnt-gt->g.inner.height/gt->fh )
2239 0 : l = gt->lcnt-gt->g.inner.height/gt->fh;
2240 0 : if ( l<0 ) l = 0;
2241 0 : if ( l!=gt->loff_top ) {
2242 0 : gt->loff_top = l;
2243 0 : GScrollBarSetPos(>->vsb->g,l);
2244 0 : _ggadget_redraw(>->g);
2245 : }
2246 : }
2247 0 : if ( gt->listfield || gt->numericfield) {
2248 0 : ((GListField *) gt)->fieldrect.x = g->r.x + fxo;
2249 0 : ((GListField *) gt)->fieldrect.width = g->r.width -fwo;
2250 0 : ((GListField *) gt)->fieldrect.y = g->r.y + fyo;
2251 0 : ((GListField *) gt)->buttonrect.x = g->r.x+g->r.width - bxo;
2252 0 : ((GListField *) gt)->buttonrect.y = g->r.y + byo;
2253 : }
2254 0 : }
2255 :
2256 0 : static GRect *gtextfield_getsize(GGadget *g, GRect *r ) {
2257 0 : GTextField *gt = (GTextField *) g;
2258 0 : _ggadget_getsize(g,r);
2259 0 : if ( gt->vsb!=NULL )
2260 0 : r->width = gt->vsb->g.r.x+gt->vsb->g.r.width-g->r.x;
2261 0 : if ( gt->hsb!=NULL )
2262 0 : r->height = gt->hsb->g.r.y+gt->hsb->g.r.height-g->r.y;
2263 0 : return( r );
2264 : }
2265 :
2266 0 : static void gtextfield_setvisible(GGadget *g, int visible ) {
2267 0 : GTextField *gt = (GTextField *) g;
2268 0 : if ( gt->vsb!=NULL ) _ggadget_setvisible(>->vsb->g,visible);
2269 0 : if ( gt->hsb!=NULL ) _ggadget_setvisible(>->hsb->g,visible);
2270 0 : _ggadget_setvisible(g,visible);
2271 0 : }
2272 :
2273 0 : static void gtextfield_setenabled(GGadget *g, int enabled ) {
2274 0 : GTextField *gt = (GTextField *) g;
2275 0 : if ( gt->vsb!=NULL ) _ggadget_setenabled(>->vsb->g,enabled);
2276 0 : if ( gt->hsb!=NULL ) _ggadget_setenabled(>->hsb->g,enabled);
2277 0 : _ggadget_setenabled(g,enabled);
2278 0 : }
2279 :
2280 0 : static int gtextfield_vscroll(GGadget *g, GEvent *event) {
2281 0 : enum sb sbt = event->u.control.u.sb.type;
2282 0 : GTextField *gt = (GTextField *) (g->data);
2283 0 : int loff = gt->loff_top;
2284 :
2285 0 : g = (GGadget *) gt;
2286 :
2287 0 : if ( sbt==et_sb_top )
2288 0 : loff = 0;
2289 0 : else if ( sbt==et_sb_bottom ) {
2290 0 : loff = gt->lcnt - gt->g.inner.height/gt->fh;
2291 0 : } else if ( sbt==et_sb_up ) {
2292 0 : if ( gt->loff_top!=0 ) loff = gt->loff_top-1; else loff = 0;
2293 0 : } else if ( sbt==et_sb_down ) {
2294 0 : if ( gt->loff_top + gt->g.inner.height/gt->fh >= gt->lcnt )
2295 0 : loff = gt->lcnt - gt->g.inner.height/gt->fh;
2296 : else
2297 0 : ++loff;
2298 0 : } else if ( sbt==et_sb_uppage ) {
2299 0 : int page = g->inner.height/gt->fh- (g->inner.height/gt->fh>2?1:0);
2300 0 : loff = gt->loff_top - page;
2301 0 : if ( loff<0 ) loff=0;
2302 0 : } else if ( sbt==et_sb_downpage ) {
2303 0 : int page = g->inner.height/gt->fh- (g->inner.height/gt->fh>2?1:0);
2304 0 : loff = gt->loff_top + page;
2305 0 : if ( loff + gt->g.inner.height/gt->fh >= gt->lcnt )
2306 0 : loff = gt->lcnt - gt->g.inner.height/gt->fh;
2307 : } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
2308 0 : loff = event->u.control.u.sb.pos;
2309 : }
2310 0 : if ( loff + gt->g.inner.height/gt->fh >= gt->lcnt )
2311 0 : loff = gt->lcnt - gt->g.inner.height/gt->fh;
2312 0 : if ( loff<0 ) loff = 0;
2313 0 : if ( loff!=gt->loff_top ) {
2314 0 : gt->loff_top = loff;
2315 0 : GScrollBarSetPos(>->vsb->g,loff);
2316 0 : _ggadget_redraw(>->g);
2317 : }
2318 0 : return( true );
2319 : }
2320 :
2321 0 : static int gtextfield_hscroll(GGadget *g, GEvent *event) {
2322 0 : enum sb sbt = event->u.control.u.sb.type;
2323 0 : GTextField *gt = (GTextField *) (g->data);
2324 0 : int xoff = gt->xoff_left;
2325 :
2326 0 : g = (GGadget *) gt;
2327 :
2328 0 : if ( sbt==et_sb_top )
2329 0 : xoff = 0;
2330 0 : else if ( sbt==et_sb_bottom ) {
2331 0 : xoff = gt->xmax - gt->g.inner.width;
2332 0 : if ( xoff<0 ) xoff = 0;
2333 0 : } else if ( sbt==et_sb_up ) {
2334 0 : if ( gt->xoff_left>gt->nw ) xoff = gt->xoff_left-gt->nw; else xoff = 0;
2335 0 : } else if ( sbt==et_sb_down ) {
2336 0 : if ( gt->xoff_left + gt->nw + gt->g.inner.width >= gt->xmax )
2337 0 : xoff = gt->xmax - gt->g.inner.width;
2338 : else
2339 0 : xoff += gt->nw;
2340 0 : } else if ( sbt==et_sb_uppage ) {
2341 0 : int page = (3*g->inner.width)/4;
2342 0 : xoff = gt->xoff_left - page;
2343 0 : if ( xoff<0 ) xoff=0;
2344 0 : } else if ( sbt==et_sb_downpage ) {
2345 0 : int page = (3*g->inner.width)/4;
2346 0 : xoff = gt->xoff_left + page;
2347 0 : if ( xoff + gt->g.inner.width >= gt->xmax )
2348 0 : xoff = gt->xmax - gt->g.inner.width;
2349 : } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
2350 0 : xoff = event->u.control.u.sb.pos;
2351 : }
2352 0 : if ( xoff + gt->g.inner.width >= gt->xmax )
2353 0 : xoff = gt->xmax - gt->g.inner.width;
2354 0 : if ( xoff<0 ) xoff = 0;
2355 0 : if ( gt->xoff_left!=xoff ) {
2356 0 : gt->xoff_left = xoff;
2357 0 : GScrollBarSetPos(>->hsb->g,xoff);
2358 0 : _ggadget_redraw(>->g);
2359 : }
2360 0 : return( true );
2361 : }
2362 :
2363 0 : static void GTextFieldSetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
2364 0 : GTextField *gt = (GTextField *) g;
2365 :
2366 0 : if ( outer!=NULL ) {
2367 0 : g->desired_width = outer->width;
2368 0 : g->desired_height = outer->height;
2369 0 : } else if ( inner!=NULL ) {
2370 0 : int bp = GBoxBorderWidth(g->base,g->box);
2371 0 : int extra=0;
2372 :
2373 0 : if ( gt->listfield ) {
2374 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize) +
2375 0 : GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2376 0 : 2*GBoxBorderWidth(gt->g.base,&_GListMark_Box) +
2377 0 : GBoxBorderWidth(gt->g.base,&glistfieldmenu_box);
2378 0 : } else if ( gt->numericfield ) {
2379 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize)/2 +
2380 0 : GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2381 0 : 2*GBoxBorderWidth(gt->g.base,&gnumericfieldspinner_box);
2382 : }
2383 0 : g->desired_width = inner->width + 2*bp + extra;
2384 0 : g->desired_height = inner->height + 2*bp;
2385 0 : if ( gt->multi_line ) {
2386 0 : int sbadd = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width) +
2387 0 : GDrawPointsToPixels(gt->g.base,1);
2388 0 : g->desired_width += sbadd;
2389 0 : if ( !gt->wrap )
2390 0 : g->desired_height += sbadd;
2391 : }
2392 : }
2393 0 : }
2394 :
2395 0 : static void GTextFieldGetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
2396 0 : GTextField *gt = (GTextField *) g;
2397 0 : int width=0, height;
2398 0 : int extra=0;
2399 0 : int bp = GBoxBorderWidth(g->base,g->box);
2400 :
2401 0 : if ( gt->listfield ) {
2402 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize) +
2403 0 : GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2404 0 : 2*GBoxBorderWidth(gt->g.base,&_GListMark_Box) +
2405 0 : GBoxBorderWidth(gt->g.base,&glistfieldmenu_box);
2406 0 : } else if ( gt->numericfield ) {
2407 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize)/2 +
2408 0 : GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2409 0 : 2*GBoxBorderWidth(gt->g.base,&gnumericfieldspinner_box);
2410 : }
2411 :
2412 0 : width = GGadgetScale(GDrawPointsToPixels(gt->g.base,80));
2413 0 : height = gt->multi_line? 4*gt->fh:gt->fh;
2414 :
2415 0 : if ( g->desired_width>extra+2*bp ) width = g->desired_width - extra - 2*bp;
2416 0 : if ( g->desired_height>2*bp ) height = g->desired_height - 2*bp;
2417 :
2418 0 : if ( gt->multi_line ) {
2419 0 : int sbadd = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width) +
2420 0 : GDrawPointsToPixels(gt->g.base,1);
2421 0 : width += sbadd;
2422 0 : if ( !gt->wrap )
2423 0 : height += sbadd;
2424 : }
2425 :
2426 0 : if ( inner!=NULL ) {
2427 0 : inner->x = inner->y = 0;
2428 0 : inner->width = width;
2429 0 : inner->height = height;
2430 : }
2431 0 : if ( outer!=NULL ) {
2432 0 : outer->x = outer->y = 0;
2433 0 : outer->width = width + extra + 2*bp;
2434 0 : outer->height = height + 2*bp;
2435 : }
2436 0 : }
2437 :
2438 0 : static int gtextfield_FillsWindow(GGadget *g) {
2439 0 : return( ((GTextField *) g)->multi_line && g->prev==NULL &&
2440 0 : (_GWidgetGetGadgets(g->base)==g ||
2441 0 : _GWidgetGetGadgets(g->base)==(GGadget *) ((GTextField *) g)->vsb ||
2442 0 : _GWidgetGetGadgets(g->base)==(GGadget *) ((GTextField *) g)->hsb ));
2443 : }
2444 :
2445 : struct gfuncs gtextfield_funcs = {
2446 : 0,
2447 : sizeof(struct gfuncs),
2448 :
2449 : gtextfield_expose,
2450 : gtextfield_mouse,
2451 : gtextfield_key,
2452 : _gtextfield_editcmd,
2453 : gtextfield_focus,
2454 : gtextfield_timer,
2455 : gtextfield_sel,
2456 :
2457 : gtextfield_redraw,
2458 : gtextfield_move,
2459 : gtextfield_resize,
2460 : gtextfield_setvisible,
2461 : gtextfield_setenabled,
2462 : gtextfield_getsize,
2463 : _ggadget_getinnersize,
2464 :
2465 : gtextfield_destroy,
2466 :
2467 : GTextFieldSetTitle,
2468 : _GTextFieldGetTitle,
2469 : NULL,
2470 : NULL,
2471 : NULL,
2472 : GTextFieldSetFont,
2473 : GTextFieldGetFont,
2474 :
2475 : NULL,
2476 : NULL,
2477 : NULL,
2478 : NULL,
2479 : NULL,
2480 : NULL,
2481 : NULL,
2482 : NULL,
2483 : NULL,
2484 : NULL,
2485 : NULL,
2486 :
2487 : GTextFieldGetDesiredSize,
2488 : GTextFieldSetDesiredSize,
2489 : gtextfield_FillsWindow,
2490 : NULL
2491 : };
2492 :
2493 : struct gfuncs glistfield_funcs = {
2494 : 0,
2495 : sizeof(struct gfuncs),
2496 :
2497 : gtextfield_expose,
2498 : gtextfield_mouse,
2499 : gtextfield_key,
2500 : gtextfield_editcmd,
2501 : gtextfield_focus,
2502 : gtextfield_timer,
2503 : gtextfield_sel,
2504 :
2505 : gtextfield_redraw,
2506 : gtextfield_move,
2507 : gtextfield_resize,
2508 : gtextfield_setvisible,
2509 : gtextfield_setenabled,
2510 : gtextfield_getsize,
2511 : _ggadget_getinnersize,
2512 :
2513 : gtextfield_destroy,
2514 :
2515 : GTextFieldSetTitle,
2516 : _GTextFieldGetTitle,
2517 : NULL,
2518 : NULL,
2519 : NULL,
2520 : GTextFieldSetFont,
2521 : GTextFieldGetFont,
2522 :
2523 : GListFClear,
2524 : GListFSet,
2525 : GListFGet,
2526 : GListFGetItem,
2527 : NULL,
2528 : GListFSelectOne,
2529 : GListFIsSelected,
2530 : GListFGetFirst,
2531 : NULL,
2532 : NULL,
2533 : NULL,
2534 :
2535 : GTextFieldGetDesiredSize,
2536 : GTextFieldSetDesiredSize,
2537 : NULL,
2538 : NULL
2539 : };
2540 :
2541 0 : static void GTextFieldInit() {
2542 : FontRequest rq;
2543 :
2544 0 : memset(&rq,0,sizeof(rq));
2545 0 : GGadgetInit();
2546 0 : GDrawDecomposeFont(_ggadget_default_font,&rq);
2547 0 : rq.family_name = NULL;
2548 0 : rq.utf8_family_name = MONO_UI_FAMILIES;
2549 0 : _gtextfield_font = GDrawInstanciateFont(NULL,&rq);
2550 0 : _GGadgetCopyDefaultBox(&_GGadget_gtextfield_box);
2551 0 : _GGadget_gtextfield_box.padding = 3;
2552 : /*_GGadget_gtextfield_box.flags = box_active_border_inner;*/
2553 0 : _gtextfield_font = _GGadgetInitDefaultBox("GTextField.",&_GGadget_gtextfield_box,_gtextfield_font);
2554 0 : glistfield_box = _GGadget_gtextfield_box;
2555 0 : _GGadgetInitDefaultBox("GComboBox.",&glistfield_box,_gtextfield_font);
2556 0 : glistfieldmenu_box = glistfield_box;
2557 0 : glistfieldmenu_box.padding = 1;
2558 0 : _GGadgetInitDefaultBox("GComboBoxMenu.",&glistfieldmenu_box,_gtextfield_font);
2559 0 : gnumericfield_box = _GGadget_gtextfield_box;
2560 0 : _GGadgetInitDefaultBox("GNumericField.",&gnumericfield_box,_gtextfield_font);
2561 0 : gnumericfieldspinner_box = gnumericfield_box;
2562 0 : gnumericfieldspinner_box.border_type = bt_none;
2563 0 : gnumericfieldspinner_box.border_width = 0;
2564 0 : gnumericfieldspinner_box.padding = 0;
2565 0 : _GGadgetInitDefaultBox("GNumericFieldSpinner.",&gnumericfieldspinner_box,_gtextfield_font);
2566 0 : gtextfield_inited = true;
2567 0 : }
2568 :
2569 0 : static void GTextFieldAddVSb(GTextField *gt) {
2570 : GGadgetData gd;
2571 :
2572 0 : memset(&gd,'\0',sizeof(gd));
2573 0 : gd.pos.y = gt->g.r.y; gd.pos.height = gt->g.r.height;
2574 0 : gd.pos.width = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width);
2575 0 : gd.pos.x = gt->g.r.x+gt->g.r.width - gd.pos.width;
2576 0 : gd.flags = (gt->g.state==gs_invisible?0:gg_visible)|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
2577 0 : gd.handle_controlevent = gtextfield_vscroll;
2578 0 : gt->vsb = (GScrollBar *) GScrollBarCreate(gt->g.base,&gd,gt);
2579 0 : gt->vsb->g.contained = true;
2580 :
2581 0 : gd.pos.width += GDrawPointsToPixels(gt->g.base,1);
2582 0 : gt->g.r.width -= gd.pos.width;
2583 0 : gt->g.inner.width -= gd.pos.width;
2584 0 : }
2585 :
2586 0 : static void GTextFieldAddHSb(GTextField *gt) {
2587 : GGadgetData gd;
2588 :
2589 0 : memset(&gd,'\0',sizeof(gd));
2590 0 : gd.pos.x = gt->g.r.x; gd.pos.width = gt->g.r.width;
2591 0 : gd.pos.height = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width);
2592 0 : gd.pos.y = gt->g.r.y+gt->g.r.height - gd.pos.height;
2593 0 : gd.flags = (gt->g.state==gs_invisible?0:gg_visible)|gg_enabled|gg_pos_in_pixels;
2594 0 : gd.handle_controlevent = gtextfield_hscroll;
2595 0 : gt->hsb = (GScrollBar *) GScrollBarCreate(gt->g.base,&gd,gt);
2596 0 : gt->hsb->g.contained = true;
2597 :
2598 0 : gd.pos.height += GDrawPointsToPixels(gt->g.base,1);
2599 0 : gt->g.r.height -= gd.pos.height;
2600 0 : gt->g.inner.height -= gd.pos.height;
2601 0 : if ( gt->vsb!=NULL ) {
2602 0 : gt->vsb->g.r.height -= gd.pos.height;
2603 0 : gt->vsb->g.inner.height -= gd.pos.height;
2604 : }
2605 0 : }
2606 :
2607 0 : static void GTextFieldFit(GTextField *gt) {
2608 : GTextBounds bounds;
2609 0 : int as=0, ds, ld, width=0;
2610 : GRect inner, outer;
2611 0 : int bp = GBoxBorderWidth(gt->g.base,gt->g.box);
2612 :
2613 : {
2614 0 : FontInstance *old = GDrawSetFont(gt->g.base,gt->font);
2615 : FontRequest rq;
2616 : int tries;
2617 0 : for ( tries = 0; tries<2; ++tries ) {
2618 0 : width = GDrawGetTextBounds(gt->g.base,gt->text, -1, &bounds);
2619 0 : GDrawWindowFontMetrics(gt->g.base,gt->font,&as, &ds, &ld);
2620 0 : if ( gt->g.r.height==0 || as+ds-3+2*bp<=gt->g.r.height || tries==1 )
2621 : break;
2622 : /* Doesn't fit. Try a smaller size */
2623 0 : GDrawDecomposeFont(gt->font,&rq);
2624 0 : --rq.point_size;
2625 0 : gt->font = GDrawInstanciateFont(gt->g.base,&rq);
2626 : }
2627 0 : gt->fh = as+ds;
2628 0 : gt->as = as;
2629 0 : gt->nw = GDrawGetTextWidth(gt->g.base,nstr, 1);
2630 0 : GDrawSetFont(gt->g.base,old);
2631 : }
2632 :
2633 0 : GTextFieldGetDesiredSize(>->g,&outer,&inner);
2634 0 : if ( gt->g.r.width==0 ) {
2635 0 : int extra=0;
2636 0 : if ( gt->listfield ) {
2637 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize) +
2638 0 : 2*GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2639 0 : GBoxBorderWidth(gt->g.base,&_GListMark_Box);
2640 0 : } else if ( gt->numericfield ) {
2641 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize)/2 +
2642 0 : GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2643 0 : 2*GBoxBorderWidth(gt->g.base,&gnumericfieldspinner_box);
2644 : }
2645 0 : gt->g.r.width = outer.width;
2646 0 : gt->g.inner.width = inner.width;
2647 0 : gt->g.inner.x = gt->g.r.x + (outer.width-inner.width-extra)/2;
2648 : } else {
2649 0 : gt->g.inner.x = gt->g.r.x + bp;
2650 0 : gt->g.inner.width = gt->g.r.width - 2*bp;
2651 : }
2652 0 : if ( gt->g.r.height==0 ) {
2653 0 : gt->g.r.height = outer.height;
2654 0 : gt->g.inner.height = inner.height;
2655 0 : gt->g.inner.y = gt->g.r.y + (outer.height-gt->g.inner.height)/2;
2656 : } else {
2657 0 : gt->g.inner.y = gt->g.r.y + bp;
2658 0 : gt->g.inner.height = gt->g.r.height - 2*bp;
2659 : }
2660 :
2661 0 : if ( gt->multi_line ) {
2662 0 : GTextFieldAddVSb(gt);
2663 0 : if ( !gt->wrap )
2664 0 : GTextFieldAddHSb(gt);
2665 : }
2666 0 : if ( gt->listfield || gt->numericfield ) {
2667 0 : GListField *ge = (GListField *) gt;
2668 : int extra;
2669 0 : if ( gt->listfield )
2670 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize) +
2671 0 : GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2672 0 : 2*GBoxBorderWidth(gt->g.base,&_GListMark_Box)+
2673 0 : GBoxBorderWidth(gt->g.base,&glistfieldmenu_box);
2674 : else {
2675 0 : extra = GDrawPointsToPixels(gt->g.base,_GListMarkSize)/2 +
2676 0 : GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip) +
2677 0 : 2*GBoxBorderWidth(gt->g.base,&gnumericfieldspinner_box);
2678 : }
2679 0 : ge->fieldrect = ge->buttonrect = gt->g.r;
2680 0 : ge->fieldrect.width -= extra;
2681 0 : extra -= GDrawPointsToPixels(gt->g.base,_GGadget_TextImageSkip)/2;
2682 0 : ge->buttonrect.x = ge->buttonrect.x+ge->buttonrect.width-extra;
2683 0 : ge->buttonrect.width = extra;
2684 0 : if ( gt->numericfield )
2685 0 : ++ge->fieldrect.width;
2686 : }
2687 0 : }
2688 :
2689 0 : static GTextField *_GTextFieldCreate(GTextField *gt, struct gwindow *base, GGadgetData *gd,void *data, GBox *def) {
2690 :
2691 0 : if ( !gtextfield_inited )
2692 0 : GTextFieldInit();
2693 0 : gt->g.funcs = >extfield_funcs;
2694 0 : _GGadget_Create(>->g,base,gd,data,def);
2695 :
2696 0 : gt->g.takes_input = true; gt->g.takes_keyboard = true; gt->g.focusable = true;
2697 0 : if ( gd->label!=NULL ) {
2698 0 : if ( gd->label->text_is_1byte )
2699 0 : gt->text = /* def2u_*/ utf82u_copy((char *) gd->label->text);
2700 0 : else if ( gd->label->text_in_resource )
2701 0 : gt->text = u_copy((unichar_t *) GStringGetResource((intpt) gd->label->text,>->g.mnemonic));
2702 : else
2703 0 : gt->text = u_copy(gd->label->text);
2704 0 : gt->sel_start = gt->sel_end = gt->sel_base = u_strlen(gt->text);
2705 : }
2706 0 : if ( gt->text==NULL )
2707 0 : gt->text = calloc(1,sizeof(unichar_t));
2708 0 : gt->font = _gtextfield_font;
2709 0 : if ( gd->label!=NULL && gd->label->font!=NULL )
2710 0 : gt->font = gd->label->font;
2711 0 : if ( (gd->flags & gg_textarea_wrap) && gt->multi_line )
2712 0 : gt->wrap = true;
2713 0 : else if ( (gd->flags & gg_textarea_wrap) ) /* only used by gchardlg.c no need to make it look nice */
2714 0 : gt->donthook = true;
2715 0 : GTextFieldFit(gt);
2716 0 : _GGadget_FinalPosition(>->g,base,gd);
2717 0 : GTextFieldRefigureLines(gt,0);
2718 :
2719 0 : if ( gd->flags & gg_group_end )
2720 0 : _GGadgetCloseGroup(>->g);
2721 0 : GWidgetIndicateFocusGadget(>->g);
2722 0 : if ( gd->flags & gg_text_xim )
2723 0 : gt->gic = GWidgetCreateInputContext(base,gic_overspot|gic_orlesser);
2724 0 : return( gt );
2725 : }
2726 :
2727 0 : GGadget *GTextFieldCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2728 0 : GTextField *gt = _GTextFieldCreate(calloc(1,sizeof(GTextField)),base,gd,data,&_GGadget_gtextfield_box);
2729 :
2730 0 : return( >->g );
2731 : }
2732 :
2733 0 : GGadget *GPasswordCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2734 0 : GTextField *gt = _GTextFieldCreate(calloc(1,sizeof(GTextField)),base,gd,data,&_GGadget_gtextfield_box);
2735 0 : gt->password = true;
2736 0 : GTextFieldRefigureLines(gt, 0);
2737 :
2738 0 : return( >->g );
2739 : }
2740 :
2741 0 : GGadget *GNumericFieldCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2742 0 : GTextField *gt = calloc(1,sizeof(GNumericField));
2743 0 : gt->numericfield = true;
2744 0 : _GTextFieldCreate(gt,base,gd,data,&gnumericfield_box);
2745 :
2746 0 : return( >->g );
2747 : }
2748 :
2749 0 : GGadget *GTextCompletionCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2750 0 : GTextField *gt = calloc(1,sizeof(GCompletionField));
2751 0 : gt->accepts_tabs = true;
2752 0 : gt->completionfield = true;
2753 0 : gt->was_completing = true;
2754 0 : ((GCompletionField *) gt)->completion = gd->u.completion;
2755 0 : _GTextFieldCreate(gt,base,gd,data,&_GGadget_gtextfield_box);
2756 0 : gt->accepts_tabs = ((GCompletionField *) gt)->completion != NULL;
2757 :
2758 0 : return( >->g );
2759 : }
2760 :
2761 0 : GGadget *GTextAreaCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2762 0 : GTextField *gt = calloc(1,sizeof(GTextField));
2763 0 : gt->multi_line = true;
2764 0 : gt->accepts_returns = true;
2765 0 : _GTextFieldCreate(gt,base,gd,data,&_GGadget_gtextfield_box);
2766 :
2767 0 : return( >->g );
2768 : }
2769 :
2770 0 : static void GListFieldSelected(GGadget *g, int i) {
2771 0 : GListField *ge = (GListField *) g;
2772 :
2773 0 : ge->popup = NULL;
2774 0 : _GWidget_ClearGrabGadget(&ge->gt.g);
2775 0 : if ( i<0 || i>=ge->ltot || ge->ti[i]->text==NULL )
2776 0 : return;
2777 0 : GTextFieldSetTitle(g,ge->ti[i]->text);
2778 0 : _ggadget_redraw(g);
2779 :
2780 0 : GTextFieldChanged(&ge->gt,i);
2781 : }
2782 :
2783 0 : GGadget *GSimpleListFieldCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2784 0 : GListField *ge = calloc(1,sizeof(GListField));
2785 :
2786 0 : ge->gt.listfield = true;
2787 0 : if ( gd->u.list!=NULL )
2788 0 : ge->ti = GTextInfoArrayFromList(gd->u.list,&ge->ltot);
2789 0 : _GTextFieldCreate(&ge->gt,base,gd,data,&glistfield_box);
2790 0 : ge->gt.g.funcs = &glistfield_funcs;
2791 0 : return( &ge->gt.g );
2792 : }
2793 :
2794 0 : static unichar_t **GListField_NameCompletion(GGadget *t,int from_tab) {
2795 : const unichar_t *spt; unichar_t **ret;
2796 : GTextInfo **ti;
2797 : int32 len;
2798 : int i, cnt, doit, match_len;
2799 :
2800 0 : spt = _GGadgetGetTitle(t);
2801 0 : if ( spt==NULL )
2802 0 : return( NULL );
2803 :
2804 0 : match_len = u_strlen(spt);
2805 0 : ti = GGadgetGetList(t,&len);
2806 0 : ret = NULL;
2807 0 : for ( doit=0; doit<2; ++doit ) {
2808 0 : cnt=0;
2809 0 : for ( i=0; i<len; ++i ) {
2810 0 : if ( ti[i]->text && u_strncmp(ti[i]->text,spt,match_len)==0 ) {
2811 0 : if ( doit )
2812 0 : ret[cnt] = u_copy(ti[i]->text);
2813 0 : ++cnt;
2814 : }
2815 : }
2816 0 : if ( doit )
2817 0 : ret[cnt] = NULL;
2818 0 : else if ( cnt==0 )
2819 0 : return( NULL );
2820 : else
2821 0 : ret = malloc((cnt+1)*sizeof(unichar_t *));
2822 : }
2823 0 : return( ret );
2824 : }
2825 :
2826 0 : GGadget *GListFieldCreate(struct gwindow *base, GGadgetData *gd,void *data) {
2827 0 : GListField *ge = calloc(1,sizeof(GCompletionField));
2828 :
2829 0 : ge->gt.listfield = true;
2830 0 : if ( gd->u.list!=NULL )
2831 0 : ge->ti = GTextInfoArrayFromList(gd->u.list,&ge->ltot);
2832 0 : ge->gt.accepts_tabs = true;
2833 0 : ge->gt.completionfield = true;
2834 : /* ge->gt.was_completing = true; */
2835 0 : ((GCompletionField *) ge)->completion = GListField_NameCompletion;
2836 0 : _GTextFieldCreate(&ge->gt,base,gd,data,&_GGadget_gtextfield_box);
2837 0 : ge->gt.g.funcs = &glistfield_funcs;
2838 0 : return( &ge->gt.g );
2839 : }
2840 :
2841 : /* ************************************************************************** */
2842 : /* ***************************** text completion **************************** */
2843 : /* ************************************************************************** */
2844 :
2845 0 : static void GCompletionDestroy(GCompletionField *gc) {
2846 : int i;
2847 :
2848 0 : if ( gc->choice_popup!=NULL ) {
2849 0 : GWindow cp = gc->choice_popup;
2850 0 : gc->choice_popup = NULL;
2851 0 : GDrawSetUserData(cp,NULL);
2852 0 : GDrawDestroyWindow(cp);
2853 : }
2854 0 : if ( gc->choices!=NULL ) {
2855 0 : for ( i=0; gc->choices[i]!=NULL; ++i )
2856 0 : free(gc->choices[i]);
2857 0 : free(gc->choices);
2858 0 : gc->choices = NULL;
2859 : }
2860 0 : }
2861 :
2862 0 : static int GTextFieldSetTitleRmDotDotDot(GGadget *g,unichar_t *tit) {
2863 0 : unichar_t *pt = uc_strstr(tit," ...");
2864 0 : if ( pt!=NULL )
2865 0 : *pt = '\0';
2866 0 : GTextFieldSetTitle(g,tit);
2867 0 : if ( pt!=NULL )
2868 0 : *pt = ' ';
2869 0 : return( pt!=NULL );
2870 : }
2871 :
2872 0 : static int popup_eh(GWindow popup,GEvent *event) {
2873 0 : GGadget *owner = GDrawGetUserData(popup);
2874 0 : GTextField *gt = (GTextField *) owner;
2875 0 : GCompletionField *gc = (GCompletionField *) owner;
2876 : GRect old1, r;
2877 : Color fg;
2878 : int i, bp;
2879 :
2880 0 : if ( owner==NULL ) /* dying */
2881 0 : return( true );
2882 :
2883 0 : bp = GBoxBorderWidth(owner->base,owner->box);
2884 0 : if ( event->type == et_expose ) {
2885 0 : GDrawPushClip(popup,&event->u.expose.rect,&old1);
2886 0 : GDrawSetFont(popup,gt->font);
2887 0 : GBoxDrawBackground(popup,&event->u.expose.rect,owner->box,
2888 : owner->state,false);
2889 0 : GDrawGetSize(popup,&r);
2890 0 : r.x = r.y = 0;
2891 0 : GBoxDrawBorder(popup,&r,owner->box,owner->state,false);
2892 0 : r.x += bp; r.width -= 2*bp;
2893 0 : fg = owner->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(popup)):
2894 0 : owner->box->main_foreground;
2895 0 : for ( i=0; gc->choices[i]!=NULL; ++i ) {
2896 0 : if ( i==gc->selected ) {
2897 0 : r.y = i*gt->fh+bp;
2898 0 : r.height = gt->fh;
2899 0 : GDrawFillRect(popup,&r,owner->box->active_border);
2900 : }
2901 0 : GDrawDrawText(popup,bp,i*gt->fh+gt->as+bp,gc->choices[i],-1,fg);
2902 : }
2903 0 : GDrawPopClip(popup,&old1);
2904 0 : } else if ( event->type == et_mouseup ) {
2905 0 : gc->selected = (event->u.mouse.y-bp)/gt->fh;
2906 0 : if ( gc->selected>=0 && gc->selected<gc->ctot ) {
2907 0 : int tryagain = GTextFieldSetTitleRmDotDotDot(owner,gc->choices[gc->selected]);
2908 0 : GTextFieldChanged(gt,-1);
2909 0 : GCompletionDestroy(gc);
2910 0 : if ( tryagain )
2911 0 : GTextFieldComplete(gt,false);
2912 : } else {
2913 0 : gc->selected = -1;
2914 0 : GDrawRequestExpose(popup,NULL,false);
2915 : }
2916 0 : } else if ( event->type == et_char ) {
2917 0 : return( gtextfield_key(owner,event));
2918 : }
2919 0 : return( true );
2920 : }
2921 :
2922 0 : static void GCompletionCreatePopup(GCompletionField *gc) {
2923 : int width, maxw, i;
2924 : GWindowAttrs pattrs;
2925 0 : GWindow base = gc->gl.gt.g.base;
2926 0 : GDisplay *disp = GDrawGetDisplayOfWindow(base);
2927 0 : GWindow root = GDrawGetRoot(disp);
2928 0 : int bp = GBoxBorderWidth(base,gc->gl.gt.g.box);
2929 : GRect pos, screen;
2930 : GPoint pt;
2931 :
2932 0 : GDrawSetFont(base,gc->gl.gt.font);
2933 :
2934 0 : maxw = 0;
2935 0 : for ( i=0; i<gc->ctot; ++i ) {
2936 0 : width = GDrawGetTextWidth(base,gc->choices[i],-1);
2937 0 : if ( width > maxw ) maxw = width;
2938 : }
2939 0 : maxw += 2*bp;
2940 0 : pos.width = maxw; pos.height = gc->gl.gt.fh*gc->ctot+2*bp;
2941 0 : if ( pos.width < gc->gl.gt.g.r.width )
2942 0 : pos.width = gc->gl.gt.g.r.width;
2943 :
2944 0 : pattrs.mask = wam_events|wam_nodecor|wam_positioned|wam_cursor|
2945 : wam_transient|wam_verytransient/*|wam_bordwidth|wam_bordcol*/;
2946 0 : pattrs.event_masks = -1;
2947 0 : pattrs.nodecoration = true;
2948 0 : pattrs.positioned = true;
2949 0 : pattrs.cursor = ct_pointer;
2950 0 : pattrs.transient = GWidgetGetTopWidget(base);
2951 0 : pattrs.border_width = 1;
2952 0 : pattrs.border_color = gc->gl.gt.g.box->main_foreground;
2953 :
2954 0 : GDrawGetSize(root,&screen);
2955 0 : pt.x = gc->gl.gt.g.r.x;
2956 0 : pt.y = gc->gl.gt.g.r.y + gc->gl.gt.g.r.height;
2957 0 : GDrawTranslateCoordinates(base,root,&pt);
2958 0 : if ( pt.y+pos.height > screen.height ) {
2959 0 : if ( pt.y-gc->gl.gt.g.r.height-pos.height>=0 ) {
2960 : /* Is there more room above the widget ?? */
2961 0 : pt.y -= gc->gl.gt.g.r.height;
2962 0 : pt.y -= pos.height;
2963 0 : } else if ( pt.x + gc->gl.gt.g.r.width + maxw <= screen.width ) {
2964 0 : pt.x += gc->gl.gt.g.r.width;
2965 0 : pt.y = 0;
2966 : } else
2967 0 : pt.x = pt.y = 0;
2968 : }
2969 0 : pos.x = pt.x; pos.y = pt.y;
2970 :
2971 0 : gc->choice_popup = GWidgetCreateTopWindow(disp,&pos,popup_eh,gc,&pattrs);
2972 0 : GDrawSetGIC(gc->choice_popup,GWidgetCreateInputContext(gc->choice_popup,gic_overspot|gic_orlesser),
2973 0 : gc->gl.gt.g.inner.x,gc->gl.gt.g.inner.y+gc->gl.gt.as);
2974 0 : GDrawSetVisible(gc->choice_popup,true);
2975 : /* Don't grab this one. User should be free to ignore it */
2976 0 : }
2977 :
2978 0 : static int ucmp(const void *_s1, const void *_s2) {
2979 0 : return( u_strcmp(*(const unichar_t **)_s1,*(const unichar_t **)_s2));
2980 : }
2981 :
2982 : #define MAXLINES 30 /* Maximum # entries allowed in popup window */
2983 : #define MAXBRACKETS 30 /* Maximum # chars allowed in [] pairs */
2984 :
2985 0 : static void GTextFieldComplete(GTextField *gt,int from_tab) {
2986 0 : GCompletionField *gc = (GCompletionField *) gt;
2987 : unichar_t **ret;
2988 : int i, len, orig_len;
2989 : unichar_t *pt1, *pt2, ch;
2990 : /* If not from_tab, then the textfield has already been changed and we */
2991 : /* must mark it as such (but don't mark twice) */
2992 :
2993 0 : ret = (gc->completion)(>->g,from_tab);
2994 0 : if ( ret==NULL || ret[0]==NULL ) {
2995 0 : if ( from_tab )
2996 0 : GDrawBeep(NULL);
2997 : else
2998 0 : GTextFieldChanged(gt,-1);
2999 0 : free(ret);
3000 : } else {
3001 0 : orig_len = u_strlen(gt->text);
3002 0 : len = u_strlen(ret[0]);
3003 0 : for ( i=1; ret[i]!=NULL; ++i ) {
3004 0 : for ( pt1=ret[0], pt2=ret[i]; *pt1==*pt2 && pt1-ret[0]<len ; ++pt1, ++pt2 );
3005 0 : len = pt1-ret[0];
3006 : }
3007 0 : if ( orig_len!=len ) {
3008 0 : ch = ret[0][len]; ret[0][len] = '\0';
3009 0 : GTextFieldSetTitle(>->g,ret[0]);
3010 0 : ret[0][len] = ch;
3011 0 : if ( !from_tab )
3012 0 : GTextFieldSelect(>->g,orig_len,len);
3013 0 : GTextFieldChanged(gt,-1);
3014 0 : } else if ( !from_tab )
3015 0 : GTextFieldChanged(gt,-1);
3016 0 : if ( ret[1]!=NULL ) {
3017 0 : gc->choices = ret;
3018 0 : gc->selected = -1;
3019 0 : if ( from_tab ) GDrawBeep(NULL);
3020 0 : qsort(ret,i,sizeof(unichar_t *),ucmp);
3021 0 : gc->ctot = i;
3022 0 : if ( i>=MAXLINES ) {
3023 : /* Try to shrink the list by just showing initial stubs of the */
3024 : /* names with multiple entries with a common next character */
3025 : /* So if we have matched against "a" and we have "abc", "abd" "acc" */
3026 : /* the show "ab..." and "acc" */
3027 0 : unichar_t **ret2=NULL, last_ch = -1;
3028 0 : int cnt, doit, type2=false;
3029 0 : for ( doit=0; doit<2; ++doit ) {
3030 0 : for ( i=cnt=0; ret[i]!=NULL; ++i ) {
3031 0 : if ( last_ch!=ret[i][len] ) {
3032 0 : if ( doit && type2 ) {
3033 0 : int c2 = cnt/MAXBRACKETS, c3 = cnt%MAXBRACKETS;
3034 0 : if ( ret[i][len]=='\0' )
3035 0 : continue;
3036 0 : if ( c3==0 ) {
3037 0 : ret2[c2] = calloc((len+MAXBRACKETS+2+4+1),sizeof(unichar_t));
3038 0 : memcpy(ret2[c2],ret[i],len*sizeof(unichar_t));
3039 0 : ret2[c2][len] = '[';
3040 : }
3041 0 : ret2[c2][len+1+c3] = ret[i][len];
3042 0 : uc_strcpy(ret2[c2]+len+2+c3,"] ...");
3043 0 : } else if ( doit ) {
3044 0 : ret2[cnt] = malloc((u_strlen(ret[i])+5)*sizeof(unichar_t));
3045 0 : u_strcpy(ret2[cnt],ret[i]);
3046 : }
3047 0 : ++cnt;
3048 0 : last_ch = ret[i][len];
3049 0 : } else if ( doit && !type2 ) {
3050 : int j;
3051 0 : for ( j=len+1; ret[i][j]!='\0' && ret[i][j] == ret2[cnt-1][j]; ++j );
3052 0 : uc_strcpy(ret2[cnt-1]+j," ...");
3053 : }
3054 : }
3055 0 : if ( cnt>=MAXLINES*MAXBRACKETS )
3056 0 : break;
3057 0 : if ( cnt>=MAXLINES && !doit ) {
3058 0 : type2 = (cnt+MAXBRACKETS-1)/MAXBRACKETS;
3059 0 : ret2 = malloc((type2+1)*sizeof(unichar_t *));
3060 0 : } else if ( !doit )
3061 0 : ret2 = malloc((cnt+1)*sizeof(unichar_t *));
3062 : else {
3063 0 : if ( type2 )
3064 0 : cnt = type2;
3065 0 : ret2[cnt] = NULL;
3066 : }
3067 : }
3068 0 : if ( ret2!=NULL ) {
3069 0 : for ( i=0; ret[i]!=NULL; ++i )
3070 0 : free(ret[i]);
3071 0 : free(ret);
3072 0 : ret = gc->choices = ret2;
3073 0 : i = gc->ctot = cnt;
3074 : }
3075 : }
3076 0 : if ( gc->ctot>=MAXLINES ) {
3077 : /* Too many choices. Don't popup a list of them */
3078 0 : gc->choices = NULL;
3079 0 : for ( i=0; ret[i]!=NULL; ++i )
3080 0 : free(ret[i]);
3081 0 : free(ret);
3082 : } else {
3083 0 : gc->ctot = i;
3084 0 : GCompletionCreatePopup(gc);
3085 : }
3086 : } else {
3087 0 : free(ret[1]);
3088 0 : free(ret);
3089 : }
3090 : }
3091 0 : }
3092 :
3093 0 : static int GCompletionHandleKey(GTextField *gt,GEvent *event) {
3094 0 : GCompletionField *gc = (GCompletionField *) gt;
3095 0 : int dir = 0;
3096 :
3097 0 : if ( gc->choice_popup==NULL || event->type == et_charup )
3098 0 : return( false );
3099 :
3100 0 : if ( event->u.chr.keysym == GK_Up || event->u.chr.keysym == GK_KP_Up )
3101 0 : dir = -1;
3102 0 : else if ( event->u.chr.keysym == GK_Down || event->u.chr.keysym == GK_KP_Down )
3103 0 : dir = 1;
3104 :
3105 0 : if ( dir==0 || event->u.chr.chars[0]!='\0' ) {
3106 : /* For normal characters we destroy the popup window and pretend it */
3107 : /* wasn't there */
3108 0 : GCompletionDestroy(gc);
3109 0 : if ( event->u.chr.keysym == GK_Escape )
3110 0 : gt->was_completing = false;
3111 0 : return( event->u.chr.keysym == GK_Escape || /* Eat an escape, other chars will be processed further */
3112 0 : event->u.chr.keysym == GK_Return );
3113 : }
3114 :
3115 0 : if (( gc->selected==-1 && dir==-1 ) || ( gc->selected==gc->ctot-1 && dir==1 ))
3116 0 : return( true );
3117 0 : gc->selected += dir;
3118 0 : if ( gc->selected!=-1 )
3119 0 : GTextFieldSetTitleRmDotDotDot(>->g,gc->choices[gc->selected]);
3120 0 : GTextFieldChanged(gt,-1);
3121 0 : GDrawRequestExpose(gc->choice_popup,NULL,false);
3122 0 : return( true );
3123 : }
3124 :
3125 0 : void GCompletionFieldSetCompletion(GGadget *g,GTextCompletionHandler completion) {
3126 0 : ((GCompletionField *) g)->completion = completion;
3127 0 : ((GTextField *) g)->accepts_tabs = ((GCompletionField *) g)->completion != NULL;
3128 0 : }
3129 :
3130 0 : void GCompletionFieldSetCompletionMode(GGadget *g,int enabled) {
3131 0 : ((GTextField *) g)->was_completing = enabled;
3132 0 : }
3133 :
3134 0 : GResInfo *_GTextFieldRIHead(void) {
3135 :
3136 0 : if ( !gtextfield_inited )
3137 0 : GTextFieldInit();
3138 0 : return( >extfield_ri );
3139 : }
|