1 /* Copyright (C) 2006-2012 by George Williams */
2 /*
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <fontforge-config.h>
29
30 #include "ffglib.h"
31 #include "gdraw.h"
32 #include "gdrawP.h"
33 #include "ggadgetP.h"
34 #include "gkeysym.h"
35 #include "gresource.h"
36 #include "gwidget.h"
37 #include "ustring.h"
38
39 #define DEL_SPACE 6
40
41 static GBox gmatrixedit_box = GBOX_EMPTY; /* Don't initialize here */
42 static GBox gmatrixedit_button_box = GBOX_EMPTY; /* Don't initialize here */
43 static FontInstance *gmatrixedit_font = NULL, *gmatrixedit_titfont = NULL;
44 static Color gmatrixedit_title_bg = 0x808080, gmatrixedit_title_fg = 0x000000, gmatrixedit_title_divider = 0xffffff;
45 static Color gmatrixedit_rules = 0x000000;
46 static Color gmatrixedit_frozencol = 0xff0000,
47 gmatrixedit_activecol = 0x0000ff, gmatrixedit_activebg=0xffffc0;
48 static int gmatrixedit_inited = false;
49
50 static struct resed gmatrixedit_re[] = {
51 {N_("Title Background"), "TitleBG", rt_color, &gmatrixedit_title_bg, N_("Background color of column headers at the top of a matrix edit"), NULL, { 0 }, 0, 0 },
52 {N_("Title Text Color"), "TitleFG", rt_color, &gmatrixedit_title_fg, N_("Text color of column headers at the top of a matrix edit"), NULL, { 0 }, 0, 0 },
53 {N_("Title Divider Color"), "TitleDivider", rt_color, &gmatrixedit_title_divider, N_("Color of column dividers in the title section of a matrix edit"), NULL, { 0 }, 0, 0 },
54 {N_("Rule Color"), "RuleCol", rt_color, &gmatrixedit_rules, N_("Color of column dividers in the main section of a matrix edit"), NULL, { 0 }, 0, 0 },
55 {N_("Frozen Color"), "FrozenCol", rt_color, &gmatrixedit_frozencol, N_("Color of frozen (unchangeable) entries in the main section of a matrix edit"), NULL, { 0 }, 0, 0 },
56 {N_("Active Color"), "ActiveCol", rt_color, &gmatrixedit_activecol, N_("Color of the active entry in the main section of a matrix edit"), NULL, { 0 }, 0, 0 },
57 {N_("Active Background"), "ActiveBG", rt_color, &gmatrixedit_activebg, N_("Background color of the active entry in the main section of a matrix edit"), NULL, { 0 }, 0, 0 },
58 {N_("Title Font"), "TitleFont", rt_font, &gmatrixedit_titfont, N_("Font used to draw titles of a matrix edit"), NULL, { 0 }, 0, 0 },
59 RESED_EMPTY
60 };
61 static GResInfo gmatrixedit_ri = {
62 NULL, &ggadget_ri, NULL,NULL,
63 &gmatrixedit_box,
64 &gmatrixedit_font,
65 NULL,
66 gmatrixedit_re,
67 N_("Matrix Edit"),
68 N_("Matrix Edit (sort of like a spreadsheet)"),
69 "GMatrixEdit",
70 "Gdraw",
71 false,
72 omf_border_type|omf_border_width|omf_border_shape|omf_padding|
73 omf_main_background|omf_disabled_background,
74 NULL,
75 GBOX_EMPTY,
76 NULL,
77 NULL,
78 NULL
79 };
80 static GResInfo gmatrixedit2_ri = {
81 NULL, &ggadget_ri, &gmatrixedit_ri,NULL,
82 NULL,
83 NULL,
84 NULL,
85 gmatrixedit_re,
86 N_("Matrix Edit Continued"),
87 N_("Matrix Edit (sort of like a spreadsheet)"),
88 "GMatrixEdit",
89 "Gdraw",
90 false,
91 0,
92 NULL,
93 GBOX_EMPTY,
94 NULL,
95 NULL,
96 NULL
97 };
98
_GMatrixEdit_Init(void)99 static void _GMatrixEdit_Init(void) {
100 FontRequest rq;
101
102 if ( gmatrixedit_inited )
103 return;
104 _GGadgetCopyDefaultBox(&gmatrixedit_box);
105 gmatrixedit_box.border_type = bt_none;
106 gmatrixedit_box.border_width = 0;
107 gmatrixedit_box.border_shape = bs_rect;
108 gmatrixedit_box.padding = 0;
109 /*gmatrixedit_box.flags = 0;*/
110 gmatrixedit_box.main_background = COLOR_TRANSPARENT;
111 gmatrixedit_box.disabled_background = COLOR_TRANSPARENT;
112 GDrawDecomposeFont(_ggadget_default_font,&rq);
113 gmatrixedit_font = GDrawInstanciateFont(NULL,&rq);
114 gmatrixedit_font = _GGadgetInitDefaultBox("GMatrixEdit.",&gmatrixedit_box,gmatrixedit_font);
115 GDrawDecomposeFont(gmatrixedit_font,&rq);
116 rq.point_size = (rq.point_size>=12) ? rq.point_size-2 : rq.point_size>=10 ? rq.point_size-1 : rq.point_size;
117 rq.weight = 700;
118 gmatrixedit_titfont = GResourceFindFont("GMatrixEdit.TitleFont",GDrawInstanciateFont(NULL,&rq));
119 gmatrixedit_title_bg = GResourceFindColor("GMatrixEdit.TitleBG",gmatrixedit_title_bg);
120 gmatrixedit_title_fg = GResourceFindColor("GMatrixEdit.TitleFG",gmatrixedit_title_fg);
121 gmatrixedit_title_divider = GResourceFindColor("GMatrixEdit.TitleDivider",gmatrixedit_title_divider);
122 gmatrixedit_rules = GResourceFindColor("GMatrixEdit.RuleCol",gmatrixedit_rules);
123 gmatrixedit_frozencol = GResourceFindColor("GMatrixEdit.FrozenCol",gmatrixedit_frozencol);
124 gmatrixedit_activecol = GResourceFindColor("GMatrixEdit.ActiveCol",gmatrixedit_activecol);
125 gmatrixedit_activebg = GResourceFindColor("GMatrixEdit.ActiveBG",gmatrixedit_activebg);
126 gmatrixedit_inited = true;
127
128 _GGadgetCopyDefaultBox(&gmatrixedit_button_box);
129 gmatrixedit_button_box.border_width = 1;
130 gmatrixedit_button_box.flags |= box_foreground_border_inner;
131 gmatrixedit_button_box.main_background = gmatrixedit_box.main_background;
132 gmatrixedit_button_box.disabled_background = gmatrixedit_box.disabled_background;
133 _GGadgetInitDefaultBox("GMatrixEditButton.",&gmatrixedit_button_box,NULL);
134 }
135
MatrixDataFree(GMatrixEdit * gme)136 static void MatrixDataFree(GMatrixEdit *gme) {
137 int r,c;
138
139 for ( r=0; r<gme->rows; ++r ) for ( c=0; c<gme->cols; ++c ) {
140 if ( gme->col_data[c].me_type == me_string ||
141 gme->col_data[c].me_type == me_bigstr ||
142 gme->col_data[c].me_type == me_stringchoice ||
143 gme->col_data[c].me_type == me_stringchoicetrans ||
144 gme->col_data[c].me_type == me_stringchoicetag ||
145 gme->col_data[c].me_type == me_funcedit ||
146 gme->col_data[c].me_type == me_onlyfuncedit ||
147 gme->col_data[c].me_type == me_button ||
148 gme->col_data[c].me_type == me_func )
149 free( gme->data[r*gme->cols+c].u.md_str );
150 }
151 free( gme->data );
152 }
153
GMatrixEdit_destroy(GGadget * g)154 static void GMatrixEdit_destroy(GGadget *g) {
155 GMatrixEdit *gme = (GMatrixEdit *) g;
156 int c, i;
157
158 free(gme->newtext);
159 /* The textfield gme->tf lives in the nested window and doesn't need to be destroyed */
160 if ( gme->vsb!=NULL )
161 GGadgetDestroy(gme->vsb);
162 if ( gme->hsb!=NULL )
163 GGadgetDestroy(gme->hsb);
164 if ( gme->del!=NULL )
165 GGadgetDestroy(gme->del);
166 if ( gme->up!=NULL )
167 GGadgetDestroy(gme->up);
168 if ( gme->down!=NULL )
169 GGadgetDestroy(gme->down);
170 if ( gme->buttonlist!=NULL )
171 for ( i=0; gme->buttonlist[i]!=NULL; ++i )
172 GGadgetDestroy(gme->buttonlist[i]);
173 if ( gme->nested!=NULL ) {
174 GDrawSetUserData(gme->nested,NULL);
175 GDrawDestroyWindow(gme->nested);
176 }
177
178 MatrixDataFree(gme); /* Uses col data */
179
180 for ( c=0; c<gme->cols; ++c ) {
181 if ( gme->col_data[c].enum_vals!=NULL )
182 GMenuItemArrayFree(gme->col_data[c].enum_vals);
183 free( gme->col_data[c].title );
184 }
185 free( gme->col_data );
186
187 _ggadget_destroy(g);
188 }
189
GME_FixScrollBars(GMatrixEdit * gme)190 static void GME_FixScrollBars(GMatrixEdit *gme) {
191 int width;
192 int lastc;
193 int pagesize = gme->vsb->r.height/(gme->fh+gme->vpad);
194 if ( pagesize<=0 ) pagesize=1;
195
196 /* Editable matrixedits need one extra line, for the <New> button */
197 GScrollBarSetBounds(gme->vsb,0,gme->rows+!gme->no_edit,pagesize);
198 for (lastc=gme->cols-1; lastc>=0 && gme->col_data[lastc].hidden; lastc--);
199 width = gme->col_data[lastc].x + gme->col_data[lastc].width;
200 GScrollBarSetBounds(gme->hsb,0,width,gme->hsb->r.width);
201 }
202
GMatrixEdit_Move(GGadget * g,int32 x,int32 y)203 static void GMatrixEdit_Move(GGadget *g, int32 x, int32 y) {
204 GMatrixEdit *gme = (GMatrixEdit *) g;
205 int i;
206
207 /* I don't need to move the textfield because it is */
208 /* nested within the window, and I'm moving the window */
209 if ( gme->vsb!=NULL )
210 _ggadget_move((GGadget *) (gme->vsb),x+(gme->vsb->r.x-g->r.x),
211 y+(gme->vsb->r.y-g->r.y));
212 if ( gme->hsb!=NULL )
213 _ggadget_move((GGadget *) (gme->hsb),x+(gme->hsb->r.x-g->r.x),
214 y+(gme->hsb->r.y-g->r.y));
215 if ( gme->del!=NULL )
216 _ggadget_move((GGadget *) (gme->del),x+(gme->del->r.x-g->r.x),
217 y+(gme->del->r.y-g->r.y));
218 if ( gme->up!=NULL )
219 _ggadget_move((GGadget *) (gme->up),x+(gme->up->r.x-g->r.x),
220 y+(gme->up->r.y-g->r.y));
221 if ( gme->down!=NULL )
222 _ggadget_move((GGadget *) (gme->down),x+(gme->down->r.x-g->r.x),
223 y+(gme->down->r.y-g->r.y));
224 if ( gme->buttonlist!=NULL )
225 for ( i=0; gme->buttonlist[i]!=NULL; ++i )
226 if ( gme->buttonlist[i]!=NULL )
227 _ggadget_move((GGadget *) (gme->buttonlist[i]),x+(gme->buttonlist[i]->r.x-g->r.x),
228 y+(gme->buttonlist[i]->r.y-g->r.y));
229 GDrawMove(gme->nested,x+(g->inner.x-g->r.x),y+(g->inner.y-g->r.y+
230 (gme->has_titles?gme->fh:0)));
231 _ggadget_move(g,x,y);
232 }
233
FindMi(GMenuItem * mi,intpt val)234 static GMenuItem *FindMi(GMenuItem *mi, intpt val ) {
235 int i;
236
237 for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.line; ++i ) {
238 if ( mi[i].ti.userdata == (void *) (intpt) val && mi[i].ti.text!=NULL )
239 return( &mi[i] );
240 }
241 return( NULL );
242 }
243
GME_ColWidth(GMatrixEdit * gme,int c)244 static int GME_ColWidth(GMatrixEdit *gme, int c) {
245 int width=0, max, cur;
246 FontInstance *old = GDrawSetFont(gme->g.base,gme->font);
247 char *str, *pt;
248 int r;
249 GMenuItem *mi;
250
251 if ( gme->col_data[c].hidden )
252 return( 0 );
253 switch ( gme->col_data[c].me_type ) {
254 case me_int:
255 width = GDrawGetText8Width(gme->g.base,"1234", -1);
256 break;
257 case me_hex: case me_addr:
258 width = GDrawGetText8Width(gme->g.base,"0xFFFF", -1);
259 break;
260 case me_uhex:
261 width = GDrawGetText8Width(gme->g.base,"U+FFFF", -1);
262 break;
263 case me_real:
264 width = GDrawGetText8Width(gme->g.base,"1.234567", -1);
265 break;
266 case me_enum:
267 max = 0;
268 for ( r=0; r<gme->rows; ++r ) {
269 mi = FindMi(gme->col_data[c].enum_vals,gme->data[r*gme->cols+c].u.md_ival);
270 if ( mi!=NULL ) {
271 if ( mi->ti.text_is_1byte )
272 cur = GDrawGetText8Width(gme->g.base,(char *)mi->ti.text,-1);
273 else
274 cur = GDrawGetTextWidth(gme->g.base,mi->ti.text,-1);
275 if ( cur>max ) max = cur;
276 }
277 }
278 cur = 6 * GDrawGetText8Width(gme->g.base,"n", 1);
279 if ( max<cur )
280 max = cur;
281 width = max;
282 break;
283 case me_func:
284 case me_button:
285 case me_stringchoice: case me_stringchoicetrans: case me_stringchoicetag:
286 case me_funcedit:
287 case me_onlyfuncedit:
288 case me_string: case me_bigstr:
289 max = 0;
290 for ( r=0; r<gme->rows; ++r ) {
291 char *freeme = NULL;
292 str = gme->data[r*gme->cols+c].u.md_str;
293 if ( str==NULL && gme->col_data[c].me_type==me_func )
294 str = freeme = (gme->col_data[c].func)(&gme->g,r,c);
295 if ( str==NULL )
296 continue;
297 /* use the maximum width of 40 characters to avoid insanely wide
298 * cells and horizontal scrollbars, the magic number 40 is the max
299 * length of characters after which we use GME_StrBigEdit below */
300 char buf[1024];
301 utf8_strncpy(buf, str, 40);
302 pt = strchr(buf,'\n');
303 cur = GDrawGetText8Width(gme->g.base,buf, pt==NULL ? -1: pt-buf);
304 if ( cur>max ) max = cur;
305 free(freeme);
306 }
307 if ( max < 10*GDrawGetText8Width(gme->g.base,"n", 1) )
308 width = 10*GDrawGetText8Width(gme->g.base,"n", 1);
309 else
310 width = max;
311 if ( gme->col_data[c].me_type==me_stringchoice ||
312 gme->col_data[c].me_type==me_stringchoicetrans ||
313 gme->col_data[c].me_type==me_stringchoicetag ||
314 gme->col_data[c].me_type==me_onlyfuncedit ||
315 gme->col_data[c].me_type==me_funcedit )
316 width += gme->mark_size + gme->mark_skip;
317 break;
318 default:
319 width = 0;
320 break;
321 }
322 if ( gme->col_data[c].title!=NULL ) {
323 GDrawSetFont(gme->g.base,gme->titfont);
324 cur = GDrawGetText8Width(gme->g.base,gme->col_data[c].title, -1);
325 if ( cur>width ) width = cur;
326 }
327 GDrawSetFont(gme->g.base,old);
328 if ( width>0x7fff )
329 width = 0x7fff;
330 return( width );
331 }
332
333 static void GME_RedrawTitles(GMatrixEdit *gme);
GME_AdjustCol(GMatrixEdit * gme,int col)334 static int GME_AdjustCol(GMatrixEdit *gme,int col) {
335 int new_width, x,c, changed;
336 int orig_width, min_width;
337 int lastc;
338
339 changed = false;
340 if ( col==-1 ) {
341 for ( c=0; c<gme->cols; ++c ) if ( !gme->col_data[c].fixed ) {
342 new_width = GME_ColWidth(gme,c);
343 if ( new_width!=gme->col_data[c].width ) {
344 gme->col_data[c].width = new_width;
345 changed = true;
346 }
347 }
348 col = 0;
349 } else if ( !gme->col_data[col].fixed ) {
350 new_width = GME_ColWidth(gme,col);
351 if ( new_width!=gme->col_data[col].width ) {
352 gme->col_data[col].width = new_width;
353 changed = true;
354 }
355 }
356 if ( changed ) {
357 x = gme->col_data[col].x;
358 for ( c=col; c<gme->cols; ++c ) {
359 gme->col_data[c].x = x;
360 if ( !gme->col_data[c].hidden )
361 x += gme->col_data[c].width + gme->hpad;
362 }
363 }
364 for ( lastc=gme->cols-1; lastc>0 && gme->col_data[lastc].hidden; --lastc );
365 if ( !gme->col_data[lastc].fixed ) {
366 orig_width = gme->col_data[lastc].width;
367 min_width = GME_ColWidth(gme,lastc);
368 gme->col_data[lastc].width = (gme->g.inner.width-gme->vsb->r.width-gme->hpad-gme->col_data[lastc].x);
369 if ( gme->col_data[lastc].width<min_width )
370 gme->col_data[lastc].width = min_width;
371 if ( gme->col_data[lastc].width != orig_width )
372 changed = true;
373 }
374
375 if ( changed ) {
376 GME_FixScrollBars(gme);
377 GDrawRequestExpose(gme->nested,NULL,false);
378 GME_RedrawTitles(gme);
379 }
380 return( changed );
381 }
382
GMatrixEdit_SetDesiredSize(GGadget * g,GRect * outer,GRect * inner)383 static void GMatrixEdit_SetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
384 GMatrixEdit *gme = (GMatrixEdit *) g;
385 int bp = GBoxBorderWidth(g->base,g->box);
386
387 if ( outer!=NULL ) {
388 g->desired_width = outer->width;
389 g->desired_height = outer->height;
390 } else if ( inner!=NULL ) {
391 int extra = 2*bp+ gme->hsb->r.height+ (gme->has_titles?gme->fh:0);
392 if ( gme->del )
393 extra += gme->del->r.height+DEL_SPACE;
394 g->desired_width = inner->width<=0 ? -1 : inner->width+2*bp+gme->vsb->r.width;
395 g->desired_height = inner->height<=0 ? -1 :
396 inner->height<10 ? inner->height*(gme->fh + gme->vpad) + extra :
397 inner->height+extra;
398 }
399 }
400
GMatrixEdit_GetDesiredSize(GGadget * g,GRect * outer,GRect * inner)401 static void GMatrixEdit_GetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
402 GMatrixEdit *gme = (GMatrixEdit *) g;
403 int width, height;
404 int bp = GBoxBorderWidth(g->base,g->box);
405 int c, rows, i;
406 FontInstance *old = GDrawSetFont(gme->g.base,gme->font);
407 int sbwidth = GDrawPointsToPixels(g->base,_GScrollBar_Width);
408 int butwidth = 0;
409
410 width = 1;
411 for ( c=0; c<gme->cols; ++c ) {
412 width += GME_ColWidth(gme,c);
413 if ( c!=gme->cols-1 )
414 width += gme->hpad;
415 }
416 GDrawSetFont(gme->g.base,old);
417 width += sbwidth;
418
419 rows = (gme->rows<4) ? 4 : (gme->rows>20) ? 21 : gme->rows+1;
420 height = rows * (gme->fh + gme->vpad);
421
422 if ( gme->has_titles )
423 height += gme->fh;
424 height += sbwidth;
425 if ( gme->del ) {
426 height += gme->del->r.height+DEL_SPACE;
427 butwidth += gme->del->r.width + 10;
428 }
429 if ( gme->up && gme->up->state!=gs_invisible )
430 butwidth += gme->up->r.width+5;
431 if ( gme->down && gme->down->state!=gs_invisible )
432 butwidth += gme->down->r.width+5;
433 if ( gme->buttonlist!=NULL )
434 for ( i=0; gme->buttonlist[i]!=NULL; ++i )
435 if ( gme->buttonlist[i] && gme->buttonlist[i]->state!=gs_invisible )
436 butwidth += gme->buttonlist[i]->r.width+5;
437 if ( butwidth > width )
438 width = butwidth;
439
440 if ( g->desired_width>2*bp )
441 width = g->desired_width-2*bp;
442 if ( g->desired_height>2*bp )
443 height = g->desired_height-2*bp;
444 if ( inner!=NULL ) {
445 inner->x = inner->y = 0;
446 inner->width = width;
447 inner->height = height;
448 }
449 if ( outer!=NULL ) {
450 outer->x = outer->y = 0;
451 outer->width = width + 2*bp;
452 outer->height = height + 2*bp;
453 }
454 }
455
456 static void GME_PositionEdit(GMatrixEdit *gme);
GMatrixEdit_Resize(GGadget * g,int32 width,int32 height)457 static void GMatrixEdit_Resize(GGadget *g, int32 width, int32 height ) {
458 GMatrixEdit *gme = (GMatrixEdit *) g;
459 int bp = GBoxBorderWidth(g->base,g->box);
460 int subheight, subwidth;
461 /*int plus, extra,x,c;*/
462 int bcnt, i, min_width;
463 GRect wsize;
464
465 width -= 2*bp; height -= 2*bp;
466
467 subheight = height -
468 (gme->no_edit && gme->buttonlist==NULL?0:(gme->del->r.height+DEL_SPACE)) -
469 (gme->has_titles?gme->fh:0) -
470 gme->hsb->r.height;
471 subwidth = width - gme->vsb->r.width;
472 GDrawResize(gme->nested,subwidth, subheight);
473 /* Make sure the dimensions of the internal window are properly set. */
474 /* We need this because GDrawResize() just notifies WM about the size */
475 /* changes, but doesn't update the data structures used by FF internally. */
476 /* This may result into list marks/functional buttons being incorrectly */
477 /* positioned in their matrix cells when the dialog is displayed. */
478 GDrawGetSize(gme->nested, &wsize);
479 wsize.width = subwidth;
480 wsize.height = subheight;
481 gme->nested->pos = wsize;
482
483 GGadgetResize(gme->vsb,gme->vsb->r.width,subheight);
484 GGadgetMove(gme->vsb,gme->g.inner.x + width-2*bp-gme->vsb->r.width,
485 gme->vsb->r.y);
486 GGadgetResize(gme->hsb,subwidth,gme->hsb->r.height);
487 GGadgetMove(gme->hsb,gme->g.inner.x,
488 gme->g.inner.y + height - (gme->del->r.height+DEL_SPACE) - gme->hsb->r.height);
489 GME_FixScrollBars(gme);
490
491 bcnt = 1; /* delete */
492 if ( gme->up && gme->up->state!=gs_invisible )
493 bcnt += 2;
494 if ( gme->buttonlist!=NULL ) {
495 for ( i=0; gme->buttonlist[i]!=NULL; ++i )
496 if ( gme->buttonlist[i]->state!=gs_invisible )
497 ++bcnt;
498 }
499 if ( bcnt==1 && gme->no_edit ) {
500 /* No delete button to display */
501 } else if ( bcnt==1 ) {
502 GGadgetMove(gme->del,gme->g.inner.x + (width-gme->del->r.width)/2,
503 gme->g.inner.y + height - (gme->del->r.height+DEL_SPACE/2));
504 } else {
505 int y = gme->g.inner.y + height - (gme->del->r.height+DEL_SPACE/2);
506 int x = gme->g.inner.x + width-5;
507 GGadgetMove(gme->del,gme->g.inner.x + 5, y);
508 if ( gme->up && gme->up->state!=gs_invisible ) {
509 x -= gme->down->r.width;
510 GGadgetMove(gme->down, x, y);
511 x -= 5 + gme->up->r.width;
512 GGadgetMove(gme->up, x, y);
513 x -= 10;
514 }
515 if ( gme->buttonlist!=NULL ) {
516 for ( i=0; gme->buttonlist[i]!=NULL; ++i )
517 if ( gme->buttonlist[i]->state!=gs_invisible ) {
518 x -= gme->buttonlist[i]->r.width;
519 GGadgetMove(gme->buttonlist[i], x, y);
520 x -= 5;
521 }
522 }
523 }
524
525 /* I thought to leave the columns as they are, but that looks odd */
526 /* for the last column. Instead put all the extra space in the */
527 /* last column, but give it some minimal size */
528 min_width = GME_ColWidth(gme,gme->cols-1);
529 gme->col_data[gme->cols-1].width = (subwidth-gme->hpad-gme->col_data[gme->cols-1].x);
530 if ( gme->col_data[gme->cols-1].width<min_width )
531 gme->col_data[gme->cols-1].width = min_width;
532 GME_FixScrollBars(gme);
533 _ggadget_resize(g,width+2*bp, height+2*bp);
534 GME_PositionEdit(gme);
535 GDrawRequestExpose(gme->nested,NULL,false);
536 }
537
GMatrixEdit_Mouse(GGadget * g,GEvent * event)538 static int GMatrixEdit_Mouse(GGadget *g, GEvent *event) {
539 GMatrixEdit *gme = (GMatrixEdit *) g;
540 int c, nw, i, x, ex = event->u.mouse.x + gme->off_left;
541
542 if (( event->type==et_mouseup || event->type==et_mousedown ) &&
543 (event->u.mouse.button>=4 && event->u.mouse.button<=7)) {
544 int isv = event->u.mouse.button<=5;
545 if ( event->u.mouse.state&ksm_shift ) isv = !isv;
546 if ( isv && gme->vsb!=NULL )
547 return( GGadgetDispatchEvent(gme->vsb,event));
548 else if ( !isv && gme->hsb!=NULL )
549 return( GGadgetDispatchEvent(gme->hsb,event));
550 else
551 return( true );
552 }
553
554 if ( gme->pressed_col>=0 && (event->type==et_mouseup || event->type==et_mousemove)) {
555 c = gme->pressed_col;
556 nw = (ex-gme->g.inner.x-gme->col_data[c].x-gme->hpad/2);
557 if ( ex-gme->g.inner.x < gme->col_data[c].x ) {
558 if ( nw<=0 )
559 nw = 1;
560 }
561 nw = (ex-gme->g.inner.x-gme->col_data[c].x-gme->hpad/2);
562 x = gme->col_data[c].x;
563 for ( i=c; i<gme->cols; ++i ) {
564 gme->col_data[i].x = x;
565 x += gme->col_data[i].width + gme->hpad;
566 }
567 gme->col_data[c].width = nw;
568 if ( event->type==et_mouseup )
569 GME_FixScrollBars(gme);
570 GME_RedrawTitles(gme);
571 GME_PositionEdit(gme);
572 GDrawRequestExpose(gme->nested,NULL,false);
573 if ( event->type==et_mouseup ) {
574 GDrawSetCursor(g->base,ct_pointer);
575 gme->pressed_col = -1;
576 }
577 return( true );
578 }
579
580 if ( !gme->has_titles ||
581 event->u.mouse.x< gme->hsb->r.x || event->u.mouse.x >= gme->hsb->r.x+gme->hsb->r.width ||
582 event->u.mouse.y< gme->g.inner.y || event->u.mouse.y>=gme->g.inner.y+gme->fh ) {
583 if ( gme->lr_pointer ) {
584 gme->lr_pointer = false;
585 GDrawSetCursor(g->base,ct_pointer);
586 }
587 return( false );
588 }
589 for ( c=0; c<gme->cols; ++c ) {
590 if ( ex>= gme->g.inner.x + gme->col_data[c].x+gme->col_data[c].width+gme->hpad/2-4 &&
591 ex<= gme->g.inner.x + gme->col_data[c].x+gme->col_data[c].width+gme->hpad/2+4 )
592 break;
593 }
594 if ( c==gme->cols ) {
595 if ( gme->lr_pointer ) {
596 gme->lr_pointer = false;
597 GDrawSetCursor(g->base,ct_pointer);
598 }
599 } else {
600 if ( !gme->lr_pointer ) {
601 gme->lr_pointer = true;
602 GDrawSetCursor(g->base,ct_4way);
603 }
604 if ( event->type == et_mousedown )
605 gme->pressed_col = c;
606 }
607 return( true );
608 }
609
GMatrixEdit_Expose(GWindow pixmap,GGadget * g,GEvent * event)610 static int GMatrixEdit_Expose(GWindow pixmap, GGadget *g, GEvent *event) {
611 GMatrixEdit *gme = (GMatrixEdit *) g;
612 GRect r, old, older;
613 int c, y, lastc;
614 Color fg = gmatrixedit_title_fg;
615
616 if ( gme->g.state!=gs_enabled )
617 fg = gme->g.box->disabled_foreground;
618
619 GBoxDrawBorder(pixmap,&g->r,g->box,g->state,false);
620 if ( gme->has_titles ) {
621 r = gme->g.inner;
622 r.height = gme->font_fh;
623 r.width = gme->hsb->r.width;
624 GDrawPushClip(pixmap,&r,&older);
625 GDrawFillRect(pixmap,&r,gmatrixedit_title_bg);
626 y = r.y + gme->font_as;
627 GDrawSetFont(pixmap,gme->titfont);
628 for ( lastc = gme->cols-1; lastc>0 && gme->col_data[lastc].hidden; --lastc );
629 for ( c=0; c<gme->cols; ++c ) {
630 if ( gme->col_data[c].title!=NULL &&
631 !gme->col_data[c].hidden ) {
632 r.x = gme->col_data[c].x + gme->g.inner.x - gme->off_left;
633 r.width = gme->col_data[c].width;
634 GDrawPushClip(pixmap,&r,&old);
635 GDrawDrawText8(pixmap,r.x,y,gme->col_data[c].title,-1,fg);
636 GDrawPopClip(pixmap,&old);
637 }
638 if ( c!=lastc && !gme->col_data[c].hidden)
639 GDrawDrawLine(pixmap,r.x+gme->col_data[c].width+gme->hpad/2,r.y,
640 r.x+gme->col_data[c].width+gme->hpad/2,r.y+r.height,
641 gmatrixedit_title_divider);
642 }
643 GDrawPopClip(pixmap,&older);
644 }
645 return( true );
646 }
647
GME_RedrawTitles(GMatrixEdit * gme)648 static void GME_RedrawTitles(GMatrixEdit *gme) {
649 GMatrixEdit_Expose(gme->g.base,&gme->g,NULL);
650 }
651
GMatrixEdit_SetVisible(GGadget * g,int visible)652 static void GMatrixEdit_SetVisible(GGadget *g, int visible ) {
653 GMatrixEdit *gme = (GMatrixEdit *) g;
654 int i;
655
656 if ( gme->vsb!=NULL ) _ggadget_setvisible(gme->vsb,visible);
657 if ( gme->hsb!=NULL ) _ggadget_setvisible(gme->hsb,visible);
658 if ( gme->del!=NULL ) _ggadget_setvisible(gme->del,visible);
659 if ( gme->up!=NULL ) _ggadget_setvisible(gme->up,visible);
660 if ( gme->down!=NULL ) _ggadget_setvisible(gme->down,visible);
661 if ( gme->buttonlist!=NULL )
662 for ( i=0; gme->buttonlist[i]!=NULL; ++i )
663 _ggadget_setvisible(gme->buttonlist[i],visible);
664
665 GDrawSetVisible(gme->nested,visible);
666 _ggadget_setvisible(g,visible);
667 }
668
GMatrixEdit_Redraw(GGadget * g)669 static void GMatrixEdit_Redraw(GGadget *g ) {
670 GMatrixEdit *gme = (GMatrixEdit *) g;
671 GDrawRequestExpose(gme->nested,NULL,false);
672 _ggadget_redraw(g);
673 }
674
MD_Text(GMatrixEdit * gme,int r,int c)675 static char *MD_Text(GMatrixEdit *gme,int r, int c ) {
676 char buffer[20], *str= NULL;
677 struct matrix_data *d = &gme->data[r*gme->cols+c];
678
679 switch ( gme->col_data[c].me_type ) {
680 case me_enum:
681 /* Fall through into next case */
682 case me_int:
683 sprintf( buffer,"%d",(int) d->u.md_ival );
684 str = buffer;
685 break;
686 case me_hex:
687 sprintf( buffer,"0x%x",(int) d->u.md_ival );
688 str = buffer;
689 break;
690 case me_uhex:
691 sprintf( buffer,"U+%04X",(int) d->u.md_ival );
692 str = buffer;
693 break;
694 case me_addr:
695 sprintf( buffer,"%p", d->u.md_addr );
696 str = buffer;
697 break;
698 case me_real:
699 sprintf( buffer,"%g",d->u.md_real );
700 str = buffer;
701 break;
702 case me_string: case me_bigstr:
703 case me_funcedit:
704 case me_onlyfuncedit:
705 case me_button:
706 case me_stringchoice: case me_stringchoicetrans: case me_stringchoicetag:
707 str = d->u.md_str;
708 break;
709 case me_func:
710 str = d->u.md_str;
711 if ( str==NULL )
712 return( (gme->col_data[c].func)(&gme->g,r,c) );
713 break;
714 }
715 if (!str) str="";
716 return( copy(str));
717 }
718
GME_RecalcFH(GMatrixEdit * gme)719 static int GME_RecalcFH(GMatrixEdit *gme) {
720 int r,c, as, ds;
721 int32 end = -1;
722 char *str, *ept;
723 GTextBounds bounds;
724 GMenuItem *mi;
725
726 GDrawSetFont(gme->nested,gme->font);
727 as = gme->font_as; ds = gme->font_fh-as;
728 for ( r=0; r<gme->rows; ++r ) for ( c=0; c<gme->cols; ++c ) {
729 end = -1;
730 switch ( gme->col_data[c].me_type ) {
731 case me_enum:
732 mi = FindMi(gme->col_data[c].enum_vals,gme->data[r*gme->cols+c].u.md_ival);
733 if ( mi==NULL )
734 continue;
735 str = copy( (char *)mi->ti.text );
736 break;
737 default:
738 str = MD_Text(gme,r,c);
739 if ( str == NULL )
740 continue;
741 if ( ( ept = strchr(str,'\n') ) != NULL )
742 end = ept - str;
743 break;
744 }
745 GDrawGetText8Bounds(gme->nested, str, end, &bounds);
746 free(str);
747 if ( bounds.as>as )
748 as = bounds.as;
749 if ( bounds.ds>ds )
750 ds = bounds.ds;
751 }
752 if ( as!=gme->as || as+ds!=gme->fh ) {
753 gme->fh = as+ds;
754 gme->as = as;
755 return( true );
756 }
757 return( false );
758 }
759
GMatrixEdit_SetFont(GGadget * g,FontInstance * new)760 static void GMatrixEdit_SetFont(GGadget *g,FontInstance *new) {
761 GMatrixEdit *gme = (GMatrixEdit *) g;
762 int as, ds, ld;
763 gme->font = new;
764 GDrawWindowFontMetrics(g->base,gme->font,&as, &ds, &ld);
765 gme->font_as = gme->as = as;
766 gme->font_fh = gme->fh = as+ds;
767 GME_RecalcFH(gme);
768 GME_FixScrollBars(gme);
769 GDrawRequestExpose(gme->nested,NULL,false);
770 }
771
GMatrixEdit_GetFont(GGadget * g)772 static FontInstance *GMatrixEdit_GetFont(GGadget *g) {
773 GMatrixEdit *gme = (GMatrixEdit *) g;
774 return( gme->font );
775 }
776
GMatrixEdit_IsSelected(GGadget * g,int32 pos)777 static int32 GMatrixEdit_IsSelected(GGadget *g, int32 pos) {
778 GMatrixEdit *gme = (GMatrixEdit *) g;
779
780 return( pos==gme->active_row );
781 }
782
GMatrixEdit_GetFirst(GGadget * g)783 static int32 GMatrixEdit_GetFirst(GGadget *g) {
784 GMatrixEdit *gme = (GMatrixEdit *) g;
785
786 return( gme->active_row );
787 }
788
GMatrixEdit_FillsWindow(GGadget * g)789 static int GMatrixEdit_FillsWindow(GGadget *g) {
790 return( true );
791 }
792
793 struct gfuncs gmatrixedit_funcs = {
794 0,
795 sizeof(struct gfuncs),
796
797 GMatrixEdit_Expose,
798 GMatrixEdit_Mouse,
799 NULL,
800 NULL,
801 NULL,
802 NULL,
803 NULL,
804
805 GMatrixEdit_Redraw,
806 GMatrixEdit_Move,
807 GMatrixEdit_Resize,
808 GMatrixEdit_SetVisible,
809 _ggadget_setenabled,
810 _ggadget_getsize,
811 _ggadget_getinnersize,
812
813 GMatrixEdit_destroy,
814
815 NULL,
816 NULL,
817 NULL,
818 NULL,
819 NULL,
820 GMatrixEdit_SetFont,
821 GMatrixEdit_GetFont,
822
823 NULL,
824 NULL,
825 NULL,
826 NULL,
827 NULL,
828 NULL,
829 GMatrixEdit_IsSelected,
830 GMatrixEdit_GetFirst,
831 NULL,
832 NULL,
833 NULL,
834
835 GMatrixEdit_GetDesiredSize,
836 GMatrixEdit_SetDesiredSize,
837 GMatrixEdit_FillsWindow,
838 NULL
839 };
840
GME_PositionEdit(GMatrixEdit * gme)841 static void GME_PositionEdit(GMatrixEdit *gme) {
842 int x,y,end;
843 GRect wsize;
844 int c = gme->active_col, r = gme->active_row, lastc;
845
846 for ( lastc = gme->cols-1; lastc>0 && gme->col_data[lastc].hidden; --lastc );
847
848 if ( gme->edit_active ) {
849 x = gme->col_data[c].x - gme->off_left;
850 y = (r-gme->off_top)*(gme->fh+gme->vpad);
851 end = x + gme->col_data[c].width;
852
853 if ( c == lastc ) {
854 GDrawGetSize(gme->nested,&wsize);
855 if ( end>wsize.width )
856 end = wsize.width - x;
857 if ( gme->col_data[c].me_type==me_stringchoice ||
858 gme->col_data[c].me_type==me_stringchoicetrans ||
859 gme->col_data[c].me_type==me_stringchoicetag ||
860 gme->col_data[c].me_type==me_onlyfuncedit ||
861 gme->col_data[c].me_type==me_funcedit )
862 end -= gme->mark_size+gme->mark_skip;
863 }
864
865 GGadgetResize(gme->tf,end-x,gme->fh);
866 GGadgetMove(gme->tf,x,y);
867 }
868 }
869
GME_StrSmallEdit(GMatrixEdit * gme,char * str,GEvent * event)870 static void GME_StrSmallEdit(GMatrixEdit *gme,char *str, GEvent *event) {
871 gme->edit_active = true;
872 /* Shift so all of column is in window???? */
873 GME_PositionEdit(gme);
874 GGadgetSetTitle8(gme->tf,str);
875 GGadgetSetVisible(gme->tf,true);
876 GGadgetSetEnabled(gme->tf,true);
877 GCompletionFieldSetCompletion(gme->tf,gme->col_data[gme->active_col].completer);
878 ((GTextField *) (gme->tf))->accepts_tabs = false;
879 ((GTextField *) (gme->tf))->was_completing = gme->col_data[gme->active_col].completer!=NULL;
880 GWidgetIndicateFocusGadget(gme->tf);
881 if ( event->type == et_mousedown )
882 GGadgetDispatchEvent(gme->tf,event);
883 }
884
GME_SetValue(GMatrixEdit * gme,GGadget * g)885 static int GME_SetValue(GMatrixEdit *gme,GGadget *g ) {
886 int c = gme->active_col;
887 int r = gme->active_row;
888 intpt lval;
889 double dval;
890 char *end="";
891 char *str = GGadgetGetTitle8(g), *pt;
892 int kludge;
893
894 switch ( gme->col_data[c].me_type ) {
895 case me_enum:
896 {
897 const unichar_t *ustr = _GGadgetGetTitle(g), *test;
898 int i;
899 for ( i=0; (test=gme->col_data[c].enum_vals[i].ti.text)!=NULL || gme->col_data[c].enum_vals[i].ti.line ; ++i ) {
900 if ( u_strcmp(ustr,test)==0 ) {
901 if ( (intpt) gme->col_data[c].enum_vals[i].ti.userdata != GME_NoChange )
902 gme->data[r*gme->cols+c].u.md_ival =
903 (intpt) gme->col_data[c].enum_vals[i].ti.userdata;
904 free(str);
905 goto good;
906 }
907 }
908 }
909 /* Didn't match any of the enums try as a direct integer */
910 /* Fall through */
911 case me_int: case me_hex: case me_uhex: case me_addr:
912 if ( gme->validatestr!=NULL )
913 end = (gme->validatestr)(&gme->g,gme->active_row,gme->active_col,gme->wasnew,str);
914 if ( *end=='\0' ) {
915 if ( gme->col_data[c].me_type==me_hex || gme->col_data[c].me_type==me_uhex ) {
916 pt = str;
917 while ( *pt==' ' ) ++pt;
918 if ( (*pt=='u' || *pt=='U') && pt[1]=='+' )
919 pt += 2;
920 else if ( *pt=='0' && (pt[1]=='x' || pt[1]=='X'))
921 pt += 2;
922 lval = strtoul(pt,&end,16);
923 } else
924 lval = strtol(str,&end,10);
925 }
926 if ( *end!='\0' ) {
927 GTextFieldSelect(g,end-str,-1);
928 free(str);
929 GDrawBeep(NULL);
930 return( false );
931 }
932 if ( gme->col_data[c].me_type == me_addr )
933 gme->data[r*gme->cols+c].u.md_addr = (void *) lval;
934 else
935 gme->data[r*gme->cols+c].u.md_ival = lval;
936 free(str);
937 goto good;
938 case me_real:
939 if ( gme->validatestr!=NULL )
940 end = (gme->validatestr)(&gme->g,gme->active_row,gme->active_col,gme->wasnew,str);
941 if ( *end=='\0' )
942 dval = strtod(str,&end);
943 if ( *end!='\0' ) {
944 GTextFieldSelect(g,end-str,-1);
945 free(str);
946 GDrawBeep(NULL);
947 return( false );
948 }
949 gme->data[r*gme->cols+c].u.md_real = dval;
950 free(str);
951 goto good;
952 case me_stringchoice: case me_stringchoicetrans: case me_stringchoicetag:
953 case me_funcedit: case me_onlyfuncedit:
954 case me_string: case me_bigstr: case me_func: case me_button:
955 if ( gme->validatestr!=NULL )
956 end = (gme->validatestr)(&gme->g,gme->active_row,gme->active_col,gme->wasnew,str);
957 if ( *end!='\0' ) {
958 GTextFieldSelect(g,end-str,-1);
959 free(str);
960 GDrawBeep(NULL);
961 return( false );
962 }
963
964 free(gme->data[r*gme->cols+c].u.md_str);
965 gme->data[r*gme->cols+c].u.md_str = str;
966 /* Used to delete the row if this were a null string. seems extreme */
967 goto good;
968 default:
969 /* Eh? Can't happen */
970 GTextFieldSelect(g,0,-1);
971 GDrawBeep(NULL);
972 free(str);
973 return( false );
974 }
975 good:
976 kludge = gme->edit_active; gme->edit_active = false;
977 if ( gme->finishedit != NULL )
978 (gme->finishedit)(&gme->g,r,c,gme->wasnew);
979 gme->edit_active = kludge;
980 return( true );
981 }
982
GME_FinishEdit(GMatrixEdit * gme)983 static int GME_FinishEdit(GMatrixEdit *gme) {
984
985 if ( !gme->edit_active ) {
986 gme->wasnew = false;
987 return( true );
988 }
989 if ( !GME_SetValue(gme,gme->tf)) {
990 gme->wasnew = false;
991 return( false );
992 }
993 gme->edit_active = false;
994 GGadgetSetVisible(gme->tf,false);
995 GME_AdjustCol(gme,gme->active_col);
996 if ( GME_RecalcFH(gme) ) {
997 GME_FixScrollBars(gme);
998 GDrawRequestExpose(gme->nested,NULL,false);
999 }
1000
1001 gme->wasnew = false;
1002 return( true );
1003 }
1004
1005 /* Sometimes our data moves underneath us (if the validate function does */
1006 /* something weird). See if we can move the current row with the data */
GME_FinishEditPreserve(GMatrixEdit * gme,int r)1007 static int GME_FinishEditPreserve(GMatrixEdit *gme,int r) {
1008 int i;
1009
1010 if ( r<gme->rows ) {
1011 for ( i=0; i<gme->rows; ++i )
1012 gme->data[i*gme->cols].current = 0;
1013 gme->data[r*gme->cols].current = 1;
1014 }
1015 if ( !GME_FinishEdit(gme))
1016 return( -1 );
1017 if ( r==gme->rows )
1018 return( r );
1019 for ( i=0; i<gme->rows; ++i )
1020 if ( gme->data[i*gme->cols].current )
1021 return( i );
1022
1023 /* Quite lost */
1024 return( r );
1025 }
1026
GME_EnableDelete(GMatrixEdit * gme)1027 static void GME_EnableDelete(GMatrixEdit *gme) {
1028 int enabled = false;
1029
1030 if ( gme->setotherbuttons )
1031 (gme->setotherbuttons)(&gme->g,gme->active_row,gme->active_col);
1032 if ( gme->active_row>=0 && gme->active_row<gme->rows ) {
1033 enabled = true;
1034 if ( gme->candelete!=NULL && !(gme->candelete)(&gme->g,gme->active_row))
1035 enabled = false;
1036 }
1037 GGadgetSetEnabled(gme->del,enabled);
1038
1039 if ( gme->up!=NULL ) {
1040 enum gme_updown updown;
1041 if ( gme->canupdown != NULL )
1042 updown = (gme->canupdown)((GGadget *) gme,gme->active_row);
1043 else {
1044 updown = 0;
1045 if ( gme->active_row>=1 && gme->active_row<gme->rows )
1046 updown = ud_up_enabled;
1047 if ( gme->active_row>=0 && gme->active_row<gme->rows-1 )
1048 updown |= ud_down_enabled;
1049 }
1050 GGadgetSetEnabled(gme->up,updown & ud_up_enabled ? 1 : 0);
1051 GGadgetSetEnabled(gme->down,updown & ud_down_enabled ? 1 : 0);
1052 }
1053 }
1054
GME_DeleteActive(GMatrixEdit * gme)1055 static void GME_DeleteActive(GMatrixEdit *gme) {
1056 int r, c;
1057
1058 if ( gme->active_row==-1 || (gme->candelete && !(gme->candelete)(&gme->g,gme->active_row))) {
1059 GGadgetSetEnabled(gme->del,false);
1060 GDrawBeep(NULL);
1061 return;
1062 }
1063 if ( gme->predelete!=NULL )
1064 (gme->predelete)((GGadget *) gme, gme->active_row );
1065
1066 gme->edit_active = false;
1067 GGadgetSetVisible(gme->tf,false);
1068 for ( c=0; c<gme->cols; ++c ) {
1069 if ( gme->col_data[c].me_type == me_string || gme->col_data[c].me_type == me_bigstr ||
1070 gme->col_data[c].me_type == me_func || gme->col_data[c].me_type == me_funcedit ||
1071 gme->col_data[c].me_type == me_onlyfuncedit ||
1072 gme->col_data[c].me_type == me_button ||
1073 gme->col_data[c].me_type == me_stringchoice ||
1074 gme->col_data[c].me_type == me_stringchoicetag ||
1075 gme->col_data[c].me_type == me_stringchoicetrans ) {
1076 free(gme->data[gme->active_row*gme->cols+c].u.md_str);
1077 gme->data[gme->active_row*gme->cols+c].u.md_str = NULL;
1078 }
1079 }
1080 for ( r=gme->active_row+1; r<gme->rows; ++r )
1081 memcpy(gme->data+(r-1)*gme->cols,gme->data+r*gme->cols,
1082 gme->cols*sizeof(struct matrix_data));
1083 --gme->rows;
1084 gme->active_col = -1;
1085 if ( gme->active_row>=gme->rows ) gme->active_row = -1;
1086 GScrollBarSetBounds(gme->vsb,0,gme->rows,gme->vsb->inner.height/gme->fh);
1087 GDrawRequestExpose(gme->nested,NULL,false);
1088 GME_EnableDelete(gme);
1089 }
1090
GMatrixEditUp(GGadget * g)1091 void GMatrixEditUp(GGadget *g) {
1092 GMatrixEdit *gme = (GMatrixEdit *) g;
1093 GRect r;
1094 int i;
1095
1096 if ( gme->active_row<1 || gme->active_row>=gme->rows )
1097 return;
1098 for ( i=0; i<gme->cols; ++i ) {
1099 struct matrix_data md = gme->data[gme->active_row*gme->cols+i];
1100 gme->data[gme->active_row*gme->cols+i] = gme->data[(gme->active_row-1)*gme->cols+i];
1101 gme->data[(gme->active_row-1)*gme->cols+i] = md;
1102 }
1103 --gme->active_row;;
1104 GGadgetGetSize(gme->tf,&r);
1105 GGadgetMove(gme->tf,r.x,r.y-(gme->fh+1));
1106 GME_EnableDelete(gme);
1107 if ( gme->rowmotion!=NULL )
1108 (gme->rowmotion)((GGadget *) gme, gme->active_row+1,gme->active_row);
1109 GMatrixEditScrollToRowCol(&gme->g,gme->active_row,gme->active_col);
1110 }
1111
_GME_Up(GGadget * g,GEvent * e)1112 static int _GME_Up(GGadget *g, GEvent *e) {
1113 if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1114 GMatrixEditUp( g->data );
1115 }
1116 return( true );
1117 }
1118
GMatrixEditDown(GGadget * g)1119 void GMatrixEditDown(GGadget *g) {
1120 GMatrixEdit *gme = (GMatrixEdit *) g;
1121 GRect r;
1122 int i;
1123
1124 if ( gme->active_row<0 || gme->active_row>=gme->rows-1 )
1125 return;
1126 for ( i=0; i<gme->cols; ++i ) {
1127 struct matrix_data md = gme->data[gme->active_row*gme->cols+i];
1128 gme->data[gme->active_row*gme->cols+i] = gme->data[(gme->active_row+1)*gme->cols+i];
1129 gme->data[(gme->active_row+1)*gme->cols+i] = md;
1130 }
1131 ++gme->active_row;;
1132 GGadgetGetSize(gme->tf,&r);
1133 GGadgetMove(gme->tf,r.x,r.y-(gme->fh+1));
1134 GME_EnableDelete(gme);
1135 if ( gme->rowmotion!=NULL )
1136 (gme->rowmotion)((GGadget *) gme, gme->active_row-1,gme->active_row);
1137 GMatrixEditScrollToRowCol(&gme->g,gme->active_row,gme->active_col);
1138 return;
1139 }
1140
_GME_Down(GGadget * g,GEvent * e)1141 static int _GME_Down(GGadget *g, GEvent *e) {
1142 if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1143 GMatrixEditDown( g->data );
1144 }
1145 return( true );
1146 }
1147
1148 #define CID_OK 1001
1149 #define CID_Cancel 1002
1150 #define CID_EntryField 1011
1151
big_e_h(GWindow gw,GEvent * event)1152 static int big_e_h(GWindow gw, GEvent *event) {
1153 GMatrixEdit *gme = GDrawGetUserData(gw);
1154
1155 if ( event->type==et_close ) {
1156 gme->big_done = true;
1157 } else if ( event->type == et_char ) {
1158 return( false );
1159 } else if ( event->type == et_controlevent && event->u.control.subtype == et_buttonactivate ) {
1160 gme->big_done = true;
1161 if ( GGadgetGetCid(event->u.control.g)==CID_OK ) {
1162 gme->big_done = GME_SetValue(gme,GWidgetGetControl(gw,CID_EntryField) );
1163 if ( gme->big_done )
1164 GME_AdjustCol(gme,gme->active_col);
1165 } else if ( gme->wasnew ) {
1166 /* They canceled a click which produced a new row */
1167 GME_DeleteActive(gme);
1168 gme->wasnew = false;
1169 }
1170 }
1171 return( true );
1172 }
1173
GME_StrBigEdit(GMatrixEdit * gme,char * str)1174 static void GME_StrBigEdit(GMatrixEdit *gme,char *str) {
1175 GRect pos;
1176 GWindow gw;
1177 GWindowAttrs wattrs;
1178 GGadgetCreateData mgcd[6], boxes[3], *varray[5], *harray[6];
1179 GTextInfo mlabel[6];
1180 char *title_str = NULL;
1181
1182 if ( gme->bigedittitle!=NULL )
1183 title_str = (gme->bigedittitle)(&(gme->g),gme->active_row,gme->active_col);
1184
1185 memset(&wattrs,0,sizeof(wattrs));
1186 wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1187 wattrs.event_masks = ~(1<<et_charup);
1188 wattrs.is_dlg = true;
1189 wattrs.restrict_input_to_me = 1;
1190 wattrs.undercursor = 1;
1191 wattrs.cursor = ct_pointer;
1192 wattrs.utf8_window_title = title_str==NULL ? "Editing..." : title_str;
1193 pos.x = pos.y = 0;
1194 pos.width =GDrawPointsToPixels(NULL,GGadgetScale(500));
1195 pos.height = GDrawPointsToPixels(NULL,400);
1196 gme->big_done = 0;
1197 gw = GDrawCreateTopWindow(NULL,&pos,big_e_h,gme,&wattrs);
1198 free(title_str);
1199
1200 memset(&mgcd,0,sizeof(mgcd));
1201 memset(&boxes,0,sizeof(boxes));
1202 memset(&mlabel,0,sizeof(mlabel));
1203 mgcd[0].gd.pos.x = 4; mgcd[0].gd.pos.y = 6;
1204 mgcd[0].gd.pos.width = 492;
1205 mgcd[0].gd.pos.height = 260;
1206 mgcd[0].gd.flags = gg_visible | gg_enabled | gg_textarea_wrap | gg_text_xim;
1207 mgcd[0].gd.cid = CID_EntryField;
1208 mgcd[0].creator = GTextAreaCreate;
1209 varray[0] = &mgcd[0]; varray[1] = NULL;
1210 varray[2] = &boxes[2]; varray[3] = NULL;
1211 varray[4] = NULL;
1212
1213 mgcd[1].gd.pos.x = 30-3; mgcd[1].gd.pos.y = GDrawPixelsToPoints(NULL,pos.height)-35-3;
1214 mgcd[1].gd.pos.width = -1; mgcd[1].gd.pos.height = 0;
1215 mgcd[1].gd.flags = gg_visible | gg_enabled | gg_but_default;
1216 if ( _ggadget_use_gettext ) {
1217 mlabel[1].text = (unichar_t *) _("_OK");
1218 mlabel[1].text_is_1byte = true;
1219 } else
1220 mlabel[1].text = (unichar_t *) _STR_OK;
1221 mlabel[1].text_in_resource = true;
1222 mgcd[1].gd.label = &mlabel[1];
1223 mgcd[1].gd.cid = CID_OK;
1224 mgcd[1].creator = GButtonCreate;
1225
1226 mgcd[2].gd.pos.x = -30; mgcd[2].gd.pos.y = mgcd[1].gd.pos.y+3;
1227 mgcd[2].gd.pos.width = -1; mgcd[2].gd.pos.height = 0;
1228 mgcd[2].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
1229 if ( _ggadget_use_gettext ) {
1230 mlabel[2].text = (unichar_t *) _("_Cancel");
1231 mlabel[2].text_is_1byte = true;
1232 } else
1233 mlabel[2].text = (unichar_t *) _STR_Cancel;
1234 mlabel[2].text_in_resource = true;
1235 mgcd[2].gd.label = &mlabel[2];
1236 mgcd[2].gd.cid = CID_Cancel;
1237 mgcd[2].creator = GButtonCreate;
1238 harray[0] = GCD_Glue; harray[1] = &mgcd[1];
1239 harray[2] = GCD_Glue; harray[3] = &mgcd[2];
1240 harray[4] = GCD_Glue; harray[5] = NULL;
1241
1242 boxes[2].gd.flags = gg_visible | gg_enabled;
1243 boxes[2].gd.u.boxelements = harray;
1244 boxes[2].creator = GHBoxCreate;
1245
1246 boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
1247 boxes[0].gd.flags = gg_visible | gg_enabled;
1248 boxes[0].gd.u.boxelements = varray;
1249 boxes[0].creator = GHVGroupCreate;
1250
1251 GGadgetsCreate(gw,boxes);
1252 GHVBoxSetExpandableRow(boxes[0].ret,0);
1253 GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
1254 GHVBoxFitWindow(boxes[0].ret);
1255 GGadgetSetTitle8(mgcd[0].ret,str);
1256 GTextFieldSelect(mgcd[0].ret,0,0);
1257 GTextFieldShow(mgcd[0].ret,0);
1258
1259 GDrawSetVisible(gw,true);
1260 while ( !gme->big_done )
1261 GDrawProcessOneEvent(NULL);
1262 GDrawDestroyWindow(gw);
1263 GDrawRequestExpose(gme->nested,NULL,false);
1264
1265 gme->wasnew = false;
1266 }
1267
GME_EnumDispatch(GWindow gw,GMenuItem * mi,GEvent * e)1268 static void GME_EnumDispatch(GWindow gw, GMenuItem *mi, GEvent *e) {
1269 GMatrixEdit *gme = GDrawGetUserData(gw);
1270
1271 if ( (intpt) mi->ti.userdata == GME_NoChange )
1272 return;
1273
1274 gme->data[gme->active_row*gme->cols+gme->active_col].u.md_ival = (intpt) mi->ti.userdata;
1275
1276 if ( gme->finishedit != NULL )
1277 (gme->finishedit)(&gme->g,gme->active_row,gme->active_col,gme->wasnew);
1278 GME_AdjustCol(gme,gme->active_col);
1279 gme->wasnew = false;
1280 }
1281
GME_FinishChoice(GWindow gw)1282 static void GME_FinishChoice(GWindow gw) {
1283 GMatrixEdit *gme = GDrawGetUserData(gw);
1284
1285 /* If wasnew is still set then they didn't pick anything, so remove the row */
1286 if ( gme->wasnew && gme->active_col==0 )
1287 GME_DeleteActive(gme);
1288 gme->wasnew = false;
1289 GDrawRequestExpose(gme->nested,NULL,false);
1290 }
1291
GME_Choices(GMatrixEdit * gme,GEvent * event,int r,int c)1292 static void GME_Choices(GMatrixEdit *gme,GEvent *event,int r,int c) {
1293 GMenuItem *mi = gme->col_data[c].enum_vals;
1294 int val = gme->data[r*gme->cols+c].u.md_ival;
1295 int i;
1296
1297 for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.line || mi[i].ti.image!=NULL; ++i )
1298 mi[i].ti.selected = mi[i].ti.checked = !gme->wasnew && (mi[i].ti.userdata == (void *) (intpt) val);
1299 if ( gme->col_data[c].enable_enum!=NULL )
1300 (gme->col_data[c].enable_enum)(&gme->g,mi,r,c);
1301 _GMenuCreatePopupMenu(gme->nested,event, mi, GME_FinishChoice);
1302 }
1303
GME_EnumStringDispatch(GWindow gw,GMenuItem * mi,GEvent * e)1304 static void GME_EnumStringDispatch(GWindow gw, GMenuItem *mi, GEvent *e) {
1305 GMatrixEdit *gme = GDrawGetUserData(gw);
1306 int r = gme->active_row, c = gme->active_col;
1307
1308 if ( (intpt) mi->ti.userdata == GME_NoChange )
1309 return;
1310
1311 free(gme->data[r*gme->cols+c].u.md_str);
1312 if ( gme->col_data[c].me_type==me_stringchoicetrans )
1313 gme->data[r*gme->cols+c].u.md_str = copy( (char *) mi->ti.userdata );
1314 else if ( gme->col_data[c].me_type==me_stringchoicetag ) {
1315 char buf[8];
1316 buf[0] = ((intpt) mi->ti.userdata)>>24;
1317 buf[1] = ((intpt) mi->ti.userdata)>>16;
1318 buf[2] = ((intpt) mi->ti.userdata)>>8;
1319 buf[3] = ((intpt) mi->ti.userdata)&0xff;
1320 buf[4] = '\0';
1321 gme->data[r*gme->cols+c].u.md_str = copy( buf );
1322 } else
1323 gme->data[r*gme->cols+c].u.md_str = u2utf8_copy( mi->ti.text );
1324
1325 if ( gme->finishedit != NULL )
1326 (gme->finishedit)(&gme->g,r,c,gme->wasnew);
1327 GME_AdjustCol(gme,c);
1328 gme->wasnew = false;
1329 }
1330
GME_StringChoices(GMatrixEdit * gme,GEvent * event,int r,int c)1331 static void GME_StringChoices(GMatrixEdit *gme,GEvent *event,int r,int c) {
1332 GMenuItem *mi = gme->col_data[c].enum_vals;
1333 char *val = gme->data[r*gme->cols+c].u.md_str;
1334 int i;
1335
1336 if ( gme->col_data[c].me_type==me_stringchoicetag ) {
1337 char buf[8];
1338 for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.line || mi[i].ti.image!=NULL; ++i ) {
1339 buf[0] = ((intpt) mi[i].ti.userdata)>>24;
1340 buf[1] = ((intpt) mi[i].ti.userdata)>>16;
1341 buf[2] = ((intpt) mi[i].ti.userdata)>>8;
1342 buf[3] = ((intpt) mi[i].ti.userdata)&0xff;
1343 buf[4] = '\0';
1344 mi[i].ti.selected = mi[i].ti.checked = !gme->wasnew && val!=NULL &&
1345 strcmp(buf,val)==0;
1346 }
1347 } else if ( gme->col_data[c].me_type==me_stringchoicetrans ) {
1348 for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.line || mi[i].ti.image!=NULL; ++i )
1349 mi[i].ti.selected = mi[i].ti.checked = !gme->wasnew && val!=NULL &&
1350 strcmp((char *) mi[i].ti.userdata,val)==0;
1351 }
1352 if ( gme->col_data[c].enable_enum!=NULL )
1353 (gme->col_data[c].enable_enum)(&gme->g,mi,r,c);
1354 _GMenuCreatePopupMenu(gme->nested,event, mi, GME_FinishChoice);
1355 }
1356
GMatrixEdit_StartSubGadgets(GMatrixEdit * gme,int r,int c,GEvent * event)1357 static void GMatrixEdit_StartSubGadgets(GMatrixEdit *gme,int r, int c,GEvent *event) {
1358 int i, markpos, lastc;
1359 struct matrix_data *d;
1360 int old_off_left = gme->off_left; /* We sometimes scroll */
1361 int oldr;
1362 GRect size;
1363
1364 GDrawGetSize(gme->nested,&size);
1365
1366 for ( lastc = gme->cols-1; lastc>0 && gme->col_data[lastc].hidden; --lastc );
1367 /* new row */
1368 if ( c==0 && r==gme->rows && event->type == et_mousedown &&
1369 event->u.mouse.button==1 && !gme->no_edit ) {
1370 if ( gme->rows>=gme->row_max )
1371 gme->data = realloc(gme->data,(gme->row_max+=10)*gme->cols*sizeof(struct matrix_data));
1372 ++gme->rows;
1373 for ( i=0; i<gme->cols; ++i ) {
1374 d = &gme->data[r*gme->cols+i];
1375 memset(d,0,sizeof(*d));
1376 switch ( gme->col_data[i].me_type ) {
1377 case me_string: case me_bigstr:
1378 case me_stringchoice: case me_stringchoicetrans: case me_stringchoicetag:
1379 case me_funcedit:
1380 case me_onlyfuncedit:
1381 d->u.md_str = copy("");
1382 break;
1383 case me_enum:
1384 d->u.md_ival = (int) (intptr_t) gme->col_data[i].enum_vals[0].ti.userdata;
1385 break;
1386 }
1387 }
1388 if ( gme->initrow!=NULL )
1389 (gme->initrow)(&gme->g,r);
1390 GME_FixScrollBars(gme);
1391 GDrawRequestExpose(gme->nested,NULL,false);
1392 gme->wasnew = true;
1393 }
1394
1395 if ( c==gme->cols || r>=gme->rows || gme->col_data[c].disabled )
1396 return;
1397 oldr = gme->active_row;
1398 gme->active_col = c; gme->active_row = r;
1399 if ( r!=oldr && oldr!=-1 ) {
1400 GRect r;
1401 r.x = gme->col_data[c].x - gme->off_left; r.width = gme->col_data[c].width;
1402 r.y = (oldr-gme->off_top)*(gme->fh+gme->vpad); r.height = gme->fh+gme->vpad;
1403 if ( r.y+r.height>0 )
1404 GDrawRequestExpose(gme->nested,&r,false);
1405 }
1406 GME_EnableDelete(gme);
1407
1408 GMatrixEditScrollToRowCol(&gme->g,r,c);
1409 d = &gme->data[r*gme->cols+c];
1410
1411 markpos = ( c == lastc && gme->col_data[c].x + gme->col_data[c].width > size.width ) ?
1412 size.width - gme->col_data[c].x : gme->col_data[c].width;
1413 markpos -= (gme->mark_size+gme->mark_skip);
1414 if ( markpos < 0 ) markpos = 0;
1415 if ( event->type==et_mousedown && event->u.mouse.button==3 ) {
1416 if ( gme->popupmenu!=NULL )
1417 (gme->popupmenu)(&gme->g,event,r,c);
1418 } else if ( d->frozen ) {
1419 GDrawBeep(NULL);
1420 } else if ( gme->no_edit ) {
1421 /* Twiddle toes */;
1422 } else if ( gme->col_data[c].me_type==me_enum ) {
1423 GME_Choices(gme,event,r,c);
1424 } else if ( gme->col_data[c].me_type==me_button ) {
1425 char *ret = (gme->col_data[c].func)(&gme->g,r,c);
1426 if ( ret!=NULL ) {
1427 /* I don't bother validating it because I expect the function to */
1428 /* do that for me */
1429 free(gme->data[r*gme->cols+c].u.md_str);
1430 gme->data[r*gme->cols+c].u.md_str = ret;
1431 GDrawRequestExpose(gme->nested,NULL,false);
1432 }
1433 } else if ( ((gme->col_data[c].me_type==me_funcedit ||
1434 gme->col_data[c].me_type==me_onlyfuncedit ) &&
1435 event->type==et_mousedown &&
1436 event->u.mouse.x>gme->col_data[c].x + markpos - old_off_left ) ||
1437 (gme->col_data[c].me_type==me_onlyfuncedit &&
1438 event->type==et_mousedown &&
1439 (gme->wasnew || event->u.mouse.clicks==2)) ) {
1440 char *ret = (gme->col_data[c].func)(&gme->g,r,c);
1441 if ( ret!=NULL ) {
1442 /* I don't bother validating it because I expect the function to */
1443 /* do that for me */
1444 free(gme->data[r*gme->cols+c].u.md_str);
1445 gme->data[r*gme->cols+c].u.md_str = ret;
1446 if ( gme->finishedit != NULL )
1447 (gme->finishedit)(&gme->g,r,c,gme->wasnew);
1448 GDrawRequestExpose(gme->nested,NULL,false);
1449 gme->wasnew = false; // This is an attempted hack by somebody (Frank) who admittedly has no idea what is happening in all of this sparsely commented code.
1450 }
1451 } else if ( gme->col_data[c].me_type==me_onlyfuncedit ) {
1452 /* Don't allow other editing */
1453 } else if ( (gme->col_data[c].me_type==me_stringchoice ||
1454 gme->col_data[c].me_type==me_stringchoicetrans ||
1455 gme->col_data[c].me_type==me_stringchoicetag) &&
1456 event->type==et_mousedown &&
1457 event->u.mouse.x>gme->col_data[c].x + markpos - old_off_left ) {
1458 GME_StringChoices(gme,event,r,c);
1459 } else {
1460 char *str = MD_Text(gme,gme->active_row,gme->active_col);
1461 if ( str==NULL )
1462 str = copy("");
1463 if ( str!=NULL &&
1464 (g_utf8_strlen(str, -1)>40 || strchr(str,'\n')!=NULL || gme->col_data[c].me_type == me_bigstr))
1465 GME_StrBigEdit(gme,str);
1466 else
1467 GME_StrSmallEdit(gme,str,event);
1468 free(str);
1469 }
1470 }
1471
GMatrixEdit_MouseEvent(GMatrixEdit * gme,GEvent * event)1472 static void GMatrixEdit_MouseEvent(GMatrixEdit *gme,GEvent *event) {
1473 int r = event->u.mouse.y/(gme->fh+gme->vpad) + gme->off_top;
1474 int x = event->u.mouse.x + gme->off_left;
1475 int c, i;
1476
1477 if ( gme->edit_active && event->type==et_mousemove )
1478 return;
1479 for ( c=0; c<gme->cols; ++c ) {
1480 if ( gme->col_data[c].hidden )
1481 continue;
1482 if ( x>=gme->col_data[c].x && x<=gme->col_data[c].x+gme->col_data[c].width )
1483 break;
1484 }
1485 if ( event->type==et_mousemove && gme->reportmousemove!=NULL ) {
1486 (gme->reportmousemove)(&gme->g,r,c);
1487 return;
1488 }
1489 if ( gme->edit_active ) {
1490 if ( (r = GME_FinishEditPreserve(gme,r))== -1 )
1491 return;
1492 }
1493 if ( event->type==et_mousedown )
1494 GMatrixEdit_StartSubGadgets(gme,r,c,event);
1495 else if ( event->type==et_mousemove ) {
1496 if ( c<gme->cols && gme->col_data[c].me_type==me_stringchoicetrans &&
1497 gme->col_data[c].enum_vals!=NULL &&
1498 r<gme->rows ) {
1499 char *str = gme->data[r*gme->cols+c].u.md_str;
1500 GMenuItem *enums = gme->col_data[c].enum_vals;
1501 for ( i=0; enums[i].ti.text!=NULL || enums[i].ti.line ; ++i ) {
1502 if ( enums[i].ti.userdata!=NULL && strcmp(enums[i].ti.userdata,str)==0 ) {
1503 if ( enums[i].ti.text_is_1byte )
1504 GGadgetPreparePopup8(gme->nested,(char *) enums[i].ti.text);
1505 else
1506 GGadgetPreparePopup(gme->nested,enums[i].ti.text);
1507 break;
1508 }
1509 }
1510 } else if ( c<gme->cols && gme->col_data[c].me_type==me_stringchoicetag &&
1511 gme->col_data[c].enum_vals!=NULL &&
1512 r<gme->rows ) {
1513 char *str = gme->data[r*gme->cols+c].u.md_str, buf[8];
1514 GMenuItem *enums = gme->col_data[c].enum_vals;
1515 for ( i=0; enums[i].ti.text!=NULL || enums[i].ti.line ; ++i ) {
1516 buf[0] = ((intpt) enums[i].ti.userdata)>>24;
1517 buf[1] = ((intpt) enums[i].ti.userdata)>>16;
1518 buf[2] = ((intpt) enums[i].ti.userdata)>>8;
1519 buf[3] = ((intpt) enums[i].ti.userdata)&0xff;
1520 buf[4] = '\0';
1521 if ( enums[i].ti.userdata!=NULL && strcmp(buf,str)==0 ) {
1522 if ( enums[i].ti.text_is_1byte )
1523 GGadgetPreparePopup8(gme->nested,(char *) enums[i].ti.text);
1524 else
1525 GGadgetPreparePopup(gme->nested,enums[i].ti.text);
1526 break;
1527 }
1528 }
1529 } else if ( gme->g.popup_msg!=NULL )
1530 GGadgetPreparePopup(gme->nested,gme->g.popup_msg);
1531 }
1532 }
1533
GMatrixEdit_SubExpose(GMatrixEdit * gme,GWindow pixmap,GEvent * event)1534 static void GMatrixEdit_SubExpose(GMatrixEdit *gme,GWindow pixmap,GEvent *event) {
1535 int r,c, lastc, kludge;
1536 char *buf, *str, *pt;
1537 GRect size;
1538 GRect clip, old;
1539 Color fg, mkbg;
1540 struct matrix_data *data;
1541 GMenuItem *mi;
1542
1543 GDrawGetSize(gme->nested,&size);
1544 if ( gme->g.state!=gs_enabled )
1545 GDrawFillRect(pixmap,&size,gme->g.box->disabled_background);
1546
1547 GDrawDrawLine(pixmap,0,0,0,size.height,gmatrixedit_rules);
1548 /* Make sure the last visible column ends at (or after) the edge */
1549 for ( lastc = gme->cols-1; lastc>0 && gme->col_data[lastc].hidden; --lastc );
1550 if ( lastc>=0 && gme->col_data[lastc].x+gme->col_data[lastc].width < size.width ) {
1551 gme->col_data[lastc].width = size.width - gme->col_data[lastc].x;
1552 for ( c=lastc+1; c<gme->cols; ++c )
1553 gme->col_data[c].x = size.width;
1554 }
1555 for ( c=0; c<gme->cols-1; ++c ) if ( !gme->col_data[c].hidden )
1556 GDrawDrawLine(pixmap,
1557 gme->col_data[c].x+gme->col_data[c].width+gme->hpad/2-gme->off_left,0,
1558 gme->col_data[c].x+gme->col_data[c].width+gme->hpad/2-gme->off_left,size.height,
1559 gmatrixedit_rules);
1560 GDrawDrawLine(pixmap,0,0,size.width,0,gmatrixedit_rules);
1561 for ( r=gme->off_top; r<=gme->rows && (r-gme->off_top)*(gme->fh+gme->vpad)<=gme->g.inner.height; ++r )
1562 GDrawDrawLine(pixmap,
1563 0, (r-gme->off_top)*(gme->fh+gme->vpad)-1,
1564 size.width,(r-gme->off_top)*(gme->fh+gme->vpad)-1,
1565 gmatrixedit_rules);
1566
1567
1568 GDrawSetFont(pixmap,gme->font);
1569 for ( r=event->u.expose.rect.y/(gme->fh+gme->vpad);
1570 r<=(event->u.expose.rect.y+event->u.expose.rect.height+gme->fh+gme->vpad-1)/gme->fh &&
1571 r+gme->off_top<=gme->rows;
1572 ++r ) {
1573 int y, lastc;
1574 clip.y = r*(gme->fh+gme->vpad);
1575 y = clip.y + gme->font_as; /* I know this looks odd, but it seems to work when we grab a glyph from another font with cairo */
1576 clip.height = gme->fh;
1577 for ( lastc = gme->cols-1; lastc>0 && gme->col_data[lastc].hidden; --lastc );
1578 /* Compensate for the top border line */
1579 if ( clip.y <= 0 ) {
1580 clip.y = 1; clip.height += ( clip.y - 1 );
1581 }
1582
1583 for ( c=0; c<gme->cols; ++c ) {
1584 if ( gme->col_data[c].hidden )
1585 continue;
1586 if ( gme->col_data[c].x + gme->col_data[c].width < gme->off_left && gme->col_data[c].width > gme->hpad)
1587 continue;
1588 clip.x = gme->col_data[c].x - gme->off_left;
1589 clip.width = gme->col_data[c].width;
1590 if ( gme->col_data[c].me_type==me_button ) {
1591 int temp;
1592 clip.height += 2;
1593 GBoxDrawBackground(pixmap,&clip,&gmatrixedit_button_box,
1594 gme->col_data[c].disabled ? gs_disabled : gs_enabled, false);
1595 GBoxDrawBorder(pixmap,&clip,&gmatrixedit_button_box,
1596 gme->col_data[c].disabled ? gs_disabled : gs_enabled, false);
1597 clip.height -= 2;
1598 temp = GBoxBorderWidth(pixmap,&gmatrixedit_button_box)+2;
1599 clip.x += temp;
1600 clip.width -= temp;
1601 } else if ( gme->col_data[c].disabled && gme->g.box->disabled_background!=COLOR_TRANSPARENT )
1602 GDrawFillRect(pixmap,&clip,gme->g.box->disabled_background);
1603 else if ( gme->active_row==r+gme->off_top )
1604 GDrawFillRect(pixmap,&clip,gmatrixedit_activebg);
1605 if ( gme->col_data[c].me_type == me_stringchoice ||
1606 gme->col_data[c].me_type == me_stringchoicetrans ||
1607 gme->col_data[c].me_type == me_stringchoicetag ||
1608 gme->col_data[c].me_type == me_onlyfuncedit ||
1609 gme->col_data[c].me_type == me_funcedit ) {
1610
1611 if ( c == lastc ) {
1612 if ( clip.x < size.width && clip.x + clip.width > size.width )
1613 clip.width = size.width - clip.x;
1614 else if ( clip.x >= size.width )
1615 clip.width = 0;
1616 }
1617 if ( clip.width >= (gme->mark_size+gme->mark_skip) )
1618 clip.width -= (gme->mark_size+gme->mark_skip);
1619 else
1620 clip.width = 0;
1621 }
1622 if ( clip.width>0 ) {
1623 GDrawPushClip(pixmap,&clip,&old);
1624 str = NULL;
1625 if ( r+gme->off_top==gme->rows ) {
1626 if ( !gme->no_edit ) {
1627 if ( gme->newtext!=NULL )
1628 buf = smprintf( "<%s>", gme->newtext );
1629 else if ( _ggadget_use_gettext )
1630 buf = smprintf( "<%s>", S_("Row|New") );
1631 else {
1632 gchar *tmp = g_ucs4_to_utf8( (const gunichar *) GStringGetResource( _STR_New, NULL ),
1633 -1, NULL, NULL, NULL );
1634 buf = smprintf( "<%s>", tmp );
1635 g_free( tmp ); tmp = NULL;
1636 }
1637 GDrawDrawText8( pixmap, gme->col_data[0].x - gme->off_left,y,
1638 (char *) buf, -1, gmatrixedit_activecol );
1639 free( buf ) ; buf = NULL ;
1640 }
1641 } else {
1642 data = &gme->data[(r+gme->off_top)*gme->cols+c];
1643 fg = gme->g.state==gs_disabled?gme->g.box->disabled_foreground:
1644 data->frozen ? gmatrixedit_frozencol:
1645 gme->g.box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
1646 gme->g.box->main_foreground;
1647 switch ( gme->col_data[c].me_type ) {
1648 case me_enum:
1649 mi = FindMi(gme->col_data[c].enum_vals,data->u.md_ival);
1650 if ( mi!=NULL ) {
1651 if ( mi->ti.text_is_1byte )
1652 GDrawDrawText8(pixmap,clip.x,y,(char *)mi->ti.text,-1,fg);
1653 else
1654 GDrawDrawText(pixmap,clip.x,y,mi->ti.text,-1,fg);
1655 break;
1656 }
1657 /* Fall through into next case */
1658 default:
1659 kludge = gme->edit_active; gme->edit_active = false;
1660 str = MD_Text(gme,r+gme->off_top,c);
1661 if ( str==NULL && gme->col_data[c].me_type==me_button )
1662 str = copy("...");
1663 gme->edit_active = kludge;
1664 break;
1665 }
1666 if ( str!=NULL ) {
1667 pt = strchr(str,'\n');
1668 GDrawDrawText8(pixmap,clip.x,y,str,pt==NULL?-1:pt-str,fg);
1669 free(str);
1670 }
1671 }
1672 GDrawPopClip(pixmap,&old);
1673 }
1674 if ( gme->col_data[c].me_type == me_stringchoice ||
1675 gme->col_data[c].me_type == me_stringchoicetrans ||
1676 gme->col_data[c].me_type == me_stringchoicetag ||
1677 gme->col_data[c].me_type == me_onlyfuncedit ||
1678 gme->col_data[c].me_type == me_funcedit ) {
1679 GRect mr;
1680 mr.x = clip.x + clip.width; mr.width = gme->mark_size+gme->mark_skip;
1681 mr.y = clip.y; mr.height = clip.height;
1682
1683 mkbg = gme->active_row==r+gme->off_top ?
1684 gmatrixedit_activebg : GDrawGetDefaultBackground(GDrawGetDisplayOfWindow(pixmap));
1685 GDrawFillRect(pixmap,&mr,mkbg);
1686 GListMarkDraw(pixmap,
1687 mr.x + gme->mark_skip + (gme->mark_size - gme->mark_length)/2,
1688 clip.y,
1689 clip.height,
1690 gme->g.state);
1691 }
1692 if ( r+gme->off_top==gme->rows )
1693 return;
1694 }
1695 }
1696 }
1697
matrixeditsub_e_h(GWindow gw,GEvent * event)1698 static int matrixeditsub_e_h(GWindow gw, GEvent *event) {
1699 GMatrixEdit *gme = (GMatrixEdit *) GDrawGetUserData(gw);
1700 int r,c;
1701
1702 GGadgetPopupExternalEvent(event);
1703 if (( event->type==et_mouseup || event->type==et_mousedown ) &&
1704 (event->u.mouse.button>=4 && event->u.mouse.button<=7)) {
1705 int isv = event->u.mouse.button<=5;
1706 if ( event->u.mouse.state&ksm_shift ) isv = !isv;
1707 if ( isv && gme->vsb!=NULL )
1708 return( GGadgetDispatchEvent(gme->vsb,event));
1709 else if ( !isv && gme->hsb!=NULL )
1710 return( GGadgetDispatchEvent(gme->hsb,event));
1711 else
1712 return( true );
1713 }
1714
1715 switch ( event->type ) {
1716 case et_expose:
1717 GDrawSetLineWidth(gw,0);
1718 GMatrixEdit_SubExpose(gme,gw,event);
1719 break;
1720 case et_mousedown:
1721 case et_mouseup:
1722 if ( gme->g.state == gs_disabled )
1723 return( false );
1724 case et_mousemove:
1725 GMatrixEdit_MouseEvent(gme,event);
1726 break;
1727 case et_char:
1728 if ( gme->g.state == gs_disabled )
1729 return( false );
1730 r = gme->active_row;
1731 switch ( event->u.chr.keysym ) {
1732 case GK_Up: case GK_KP_Up:
1733 if ( (!gme->edit_active || GME_FinishEdit(gme)) &&
1734 gme->active_row>0 )
1735 GMatrixEdit_StartSubGadgets(gme,gme->active_row-1,gme->active_col,event);
1736 return( true );
1737 break;
1738 case GK_Down: case GK_KP_Down:
1739 if ( (!gme->edit_active || GME_FinishEdit(gme)) &&
1740 gme->active_row<gme->rows-(gme->active_col!=0) )
1741 GMatrixEdit_StartSubGadgets(gme,gme->active_row+1,gme->active_col,event);
1742 return( true );
1743 break;
1744 case GK_Left: case GK_KP_Left:
1745 case GK_BackTab:
1746 backtab:
1747 if ( (!gme->edit_active || (r=GME_FinishEditPreserve(gme,r))!=-1) &&
1748 gme->active_col>0 ) {
1749 for ( c = gme->active_col-1; c>=0 && gme->col_data[c].hidden; --c );
1750 if ( c>=0 )
1751 GMatrixEdit_StartSubGadgets(gme,r,c,event);
1752 }
1753 return( true );
1754 break;
1755 case GK_Tab:
1756 if ( event->u.chr.state&ksm_shift )
1757 goto backtab;
1758 /* Else fall through */
1759 case GK_Right: case GK_KP_Right:
1760 if ( (!gme->edit_active || (r=GME_FinishEditPreserve(gme,r))!=-1) &&
1761 gme->active_col<gme->cols-1 ) {
1762 for ( c = gme->active_col+1; c<gme->cols && gme->col_data[c].hidden; ++c );
1763 if ( c<gme->cols )
1764 GMatrixEdit_StartSubGadgets(gme,r,c,event);
1765 }
1766 return( true );
1767 break;
1768 case GK_Return: case GK_KP_Enter:
1769 if ( gme->edit_active && (r=GME_FinishEditPreserve(gme,r))!=-1 ) {
1770 GEvent dummy;
1771 memset(&dummy,0,sizeof(dummy));
1772 dummy.w = event->w;
1773 dummy.type = et_mousedown;
1774 dummy.u.mouse.state = event->u.chr.state;
1775 dummy.u.mouse.x = gme->off_left+gme->col_data[0].x+1;
1776 dummy.u.mouse.y = gme->off_top + (r+1)*(gme->fh+gme->vpad);
1777 dummy.u.mouse.button = 1;
1778 GMatrixEdit_StartSubGadgets(gme,r+1,0,&dummy);
1779 }
1780 return( true );
1781 }
1782 if ( gme->handle_key!=NULL )
1783 return( (gme->handle_key)(&gme->g,event) );
1784 return( false );
1785 break;
1786 case et_destroy:
1787 if ( gme!=NULL )
1788 gme->nested = NULL;
1789 break;
1790 case et_controlevent:
1791 if ( gme->reporttextchanged!=NULL )
1792 (gme->reporttextchanged)(&gme->g,gme->active_row,gme->active_col,gme->tf);
1793 break;
1794 }
1795 return( true );
1796 }
1797
GME_HScroll(GMatrixEdit * gme,struct sbevent * sb)1798 static void GME_HScroll(GMatrixEdit *gme,struct sbevent *sb) {
1799 int newpos = gme->off_left;
1800 GRect size;
1801 int hend = gme->col_data[gme->cols-1].x + gme->col_data[gme->cols-1].width;
1802
1803 GDrawGetSize(gme->nested,&size);
1804 switch( sb->type ) {
1805 case et_sb_top:
1806 newpos = 0;
1807 break;
1808 case et_sb_uppage:
1809 newpos -= 9*size.width/10;
1810 break;
1811 case et_sb_up:
1812 newpos -= size.width/15;
1813 break;
1814 case et_sb_down:
1815 newpos += size.width/15;
1816 break;
1817 case et_sb_downpage:
1818 newpos += 9*size.width/10;
1819 break;
1820 case et_sb_bottom:
1821 newpos = hend;
1822 break;
1823 case et_sb_thumb:
1824 case et_sb_thumbrelease:
1825 newpos = sb->pos;
1826 break;
1827 }
1828
1829 if ( newpos + size.width > hend )
1830 newpos = hend - size.width;
1831 if ( newpos<0 )
1832 newpos = 0;
1833 if ( newpos!=gme->off_left ) {
1834 int lastc, diff = gme->off_left-newpos;
1835 GRect clip;
1836 gme->off_left = newpos;
1837 GScrollBarSetPos(gme->hsb,newpos);
1838
1839 clip.y = 1;
1840 clip.height = size.height - 1;
1841 for ( lastc = gme->cols-1; lastc>0 && gme->col_data[lastc].hidden; --lastc );
1842
1843 gme->off_left = newpos;
1844 GScrollBarSetPos(gme->hsb,newpos);
1845 clip.x = 1; clip.y = 1; clip.width = size.width-1; clip.height = size.height-1;
1846
1847 if (( gme->col_data[lastc].me_type == me_stringchoice ||
1848 gme->col_data[lastc].me_type == me_stringchoicetrans ||
1849 gme->col_data[lastc].me_type == me_stringchoicetag ||
1850 gme->col_data[lastc].me_type == me_onlyfuncedit ||
1851 gme->col_data[lastc].me_type == me_funcedit ) &&
1852 gme->col_data[lastc].x <= gme->off_left + size.width - (gme->mark_size + gme->mark_skip) ) {
1853 int xdiff = gme->off_left + size.width - (gme->mark_size + gme->mark_skip) - gme->col_data[lastc].x;
1854 /* Catch the moment when we should stop scrolling the list mark area */
1855 if ( xdiff + diff < 0 ) {
1856 GDrawScroll( gme->nested,&clip,xdiff + diff,0 );
1857 diff = -xdiff;
1858 }
1859 clip.width -= (gme->mark_size + gme->mark_skip);
1860 }
1861 GDrawScroll( gme->nested,&clip,diff,0 );
1862 GME_PositionEdit(gme);
1863 GME_RedrawTitles(gme);
1864 }
1865 }
1866
GME_VScroll(GMatrixEdit * gme,struct sbevent * sb)1867 static void GME_VScroll(GMatrixEdit *gme,struct sbevent *sb) {
1868 int newpos = gme->off_top;
1869 int page;
1870 GRect size;
1871
1872 GDrawGetSize(gme->nested,&size);
1873 page = size.height/(gme->fh+gme->vpad);
1874
1875 switch( sb->type ) {
1876 case et_sb_top:
1877 newpos = 0;
1878 break;
1879 case et_sb_uppage:
1880 newpos -= 9*page/10;
1881 break;
1882 case et_sb_up:
1883 newpos--;
1884 break;
1885 case et_sb_down:
1886 newpos++;
1887 break;
1888 case et_sb_downpage:
1889 newpos += 9*page/10;
1890 break;
1891 case et_sb_bottom:
1892 newpos = gme->rows+1;
1893 break;
1894 case et_sb_thumb:
1895 case et_sb_thumbrelease:
1896 newpos = sb->pos;
1897 break;
1898 }
1899 if ( newpos + page > gme->rows+1 )
1900 newpos = gme->rows+1 - page;
1901 if ( newpos<0 )
1902 newpos = 0;
1903 if ( newpos!=gme->off_top ) {
1904 int diff = (newpos-gme->off_top)*(gme->fh+gme->vpad);
1905 GRect r;
1906 gme->off_top = newpos;
1907 GScrollBarSetPos(gme->vsb,newpos);
1908 r.x = 1; r.y = 1; r.width = size.width-1; r.height = size.height-1;
1909 GDrawScroll(gme->nested,&r,0,diff);
1910 GME_PositionEdit(gme);
1911 GDrawRequestExpose(gme->nested,&size,false);
1912 }
1913 }
1914
_GME_HScroll(GGadget * g,GEvent * e)1915 static int _GME_HScroll(GGadget *g, GEvent *e) {
1916 if ( e->type==et_controlevent && e->u.control.subtype == et_scrollbarchange ) {
1917 GMatrixEdit *gme = (GMatrixEdit *) g->data;
1918 GME_HScroll(gme,&e->u.control.u.sb);
1919 }
1920 return( true );
1921 }
1922
_GME_VScroll(GGadget * g,GEvent * e)1923 static int _GME_VScroll(GGadget *g, GEvent *e) {
1924 if ( e->type==et_controlevent && e->u.control.subtype == et_scrollbarchange ) {
1925 GMatrixEdit *gme = (GMatrixEdit *) g->data;
1926 GME_VScroll(gme,&e->u.control.u.sb);
1927 }
1928 return( true );
1929 }
1930
_GME_DeleteActive(GGadget * g,GEvent * e)1931 static int _GME_DeleteActive(GGadget *g, GEvent *e) {
1932 if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1933 GMatrixEdit *gme = (GMatrixEdit *) g->data;
1934 GME_DeleteActive(gme);
1935 }
1936 return( true );
1937 }
1938
GMenuItemFromTI(GTextInfo * ti,int is_enum)1939 static GMenuItem *GMenuItemFromTI(GTextInfo *ti,int is_enum) {
1940 int cnt;
1941 GMenuItem *mi;
1942
1943 for ( cnt=0; ti[cnt].text!=NULL || ti[cnt].line; ++cnt );
1944 mi = calloc((cnt+1),sizeof(GMenuItem));
1945 for ( cnt=0; ti[cnt].text!=NULL || ti[cnt].line; ++cnt ) {
1946 mi[cnt].ti = ti[cnt];
1947 if ( ti[cnt].bg == ti[cnt].fg )
1948 mi[cnt].ti.bg = mi[cnt].ti.fg = COLOR_DEFAULT;
1949 if ( mi[cnt].ti.text!=NULL ) {
1950 if ( ti[cnt].text_is_1byte )
1951 mi[cnt].ti.text = (unichar_t *) copy( (char *) mi[cnt].ti.text );
1952 else
1953 mi[cnt].ti.text = u_copy( mi[cnt].ti.text );
1954 mi[cnt].ti.checkable = true;
1955 mi[cnt].invoke = is_enum ? GME_EnumDispatch : GME_EnumStringDispatch;
1956 }
1957 }
1958 return( mi );
1959 }
1960
1961 /* GMatrixElement: External interface *************************************** */
GMatrixEditCreate(struct gwindow * base,GGadgetData * gd,void * data)1962 GGadget *GMatrixEditCreate(struct gwindow *base, GGadgetData *gd,void *data) {
1963 struct matrixinit *matrix = gd->u.matrix;
1964 GMatrixEdit *gme = calloc(1,sizeof(GMatrixEdit));
1965 int r, c, bp;
1966 int x;
1967 GRect outer;
1968 GRect pos;
1969 GWindowAttrs wattrs;
1970 int sbwidth = GDrawPointsToPixels(base,_GScrollBar_Width);
1971 GGadgetData sub_gd;
1972 GTextInfo label;
1973 int as, ds, ld;
1974
1975 if ( !gmatrixedit_inited )
1976 _GMatrixEdit_Init();
1977
1978 gme->g.funcs = &gmatrixedit_funcs;
1979 _GGadget_Create(&gme->g,base,gd,data,&gmatrixedit_box);
1980 gme->g.takes_input = true; gme->g.takes_keyboard = false; gme->g.focusable = false;
1981
1982 gme->font = gmatrixedit_font;
1983 gme->titfont = gmatrixedit_titfont;
1984 GDrawWindowFontMetrics(base,gme->font,&as, &ds, &ld);
1985 gme->font_as = gme->as = as;
1986 gme->font_fh = gme->fh = as+ds;
1987
1988 gme->rows = matrix->initial_row_cnt; gme->cols = matrix->col_cnt;
1989 gme->row_max = gme->rows;
1990 gme->hpad = gme->vpad = GDrawPointsToPixels(base,2);
1991
1992 gme->col_data = calloc(gme->cols,sizeof(struct col_data));
1993 for ( c=0; c<gme->cols; ++c ) {
1994 gme->col_data[c].me_type = matrix->col_init[c].me_type;
1995 gme->col_data[c].func = matrix->col_init[c].func;
1996 if ( matrix->col_init[c].enum_vals!=NULL )
1997 gme->col_data[c].enum_vals = GMenuItemFromTI(matrix->col_init[c].enum_vals,
1998 matrix->col_init[c].me_type==me_enum );
1999 else
2000 gme->col_data[c].enum_vals = NULL;
2001 gme->col_data[c].enable_enum = matrix->col_init[c].enable_enum;
2002 gme->col_data[c].title = copy( matrix->col_init[c].title );
2003 if ( gme->col_data[c].title!=NULL ) gme->has_titles = true;
2004 gme->col_data[c].fixed = false;
2005 }
2006
2007 gme->data = calloc(gme->rows*gme->cols,sizeof(struct matrix_data));
2008 memcpy(gme->data,matrix->matrix_data,gme->rows*gme->cols*sizeof(struct matrix_data));
2009 for ( c=0; c<gme->cols; ++c ) {
2010 enum me_type me_type = gme->col_data[c].me_type;
2011 if ( me_type==me_string || me_type==me_bigstr || me_type==me_func ||
2012 me_type==me_button || me_type==me_onlyfuncedit ||
2013 me_type==me_funcedit || me_type==me_stringchoice ||
2014 me_type==me_stringchoicetrans || me_type==me_stringchoicetag ) {
2015 for ( r=0; r<gme->rows; ++r )
2016 gme->data[r*gme->cols+c].u.md_str = copy(gme->data[r*gme->cols+c].u.md_str);
2017 }
2018 }
2019
2020 gme->mark_length = GDrawPointsToPixels(base,_GListMarkSize);
2021 gme->mark_size = gme->mark_length +
2022 2*GBoxBorderWidth(base,&_GListMark_Box);
2023 gme->mark_skip = GDrawPointsToPixels(base,_GGadget_TextImageSkip);
2024
2025 /* Can't do this earlier. It depends on matrix_data being set */
2026 x = 1;
2027 for ( c=0; c<gme->cols; ++c ) {
2028 gme->col_data[c].x = x;
2029 gme->col_data[c].width = GME_ColWidth(gme,c);
2030 x += gme->col_data[c].width + gme->hpad;
2031 }
2032
2033 gme->pressed_col = -1;
2034 gme->active_col = gme->active_row = -1;
2035 gme->initrow = matrix->initrow;
2036 gme->finishedit = matrix->finishedit;
2037 gme->candelete = matrix->candelete;
2038 gme->popupmenu = matrix->popupmenu;
2039 gme->handle_key = matrix->handle_key;
2040 gme->bigedittitle = matrix->bigedittitle;
2041
2042 GMatrixEdit_GetDesiredSize(&gme->g,&outer,NULL);
2043 if ( gme->g.r.width==0 )
2044 gme->g.r.width = outer.width;
2045 else
2046 gme->g.desired_width = gme->g.r.width;
2047 if ( gme->g.r.height==0 )
2048 gme->g.r.height = outer.height;
2049 else
2050 gme->g.desired_height = gme->g.r.height;
2051 bp = GBoxBorderWidth(gme->g.base,gme->g.box);
2052 gme->g.inner.x = gme->g.r.x + bp;
2053 gme->g.inner.y = gme->g.r.y + bp;
2054 gme->g.inner.width = gme->g.r.width -2*bp;
2055 gme->g.inner.height = gme->g.r.height -2*bp;
2056
2057 memset(&sub_gd,0,sizeof(sub_gd));
2058 memset(&label,0,sizeof(label));
2059 sub_gd.pos.x = sub_gd.pos.y = 1; sub_gd.pos.width = sub_gd.pos.height = 0;
2060 label.text = (unichar_t *) _("Delete");
2061 label.text_is_1byte = true;
2062 sub_gd.flags = gg_visible | gg_pos_in_pixels;
2063 sub_gd.label = &label;
2064 sub_gd.handle_controlevent = _GME_DeleteActive;
2065 gme->del = GButtonCreate(base,&sub_gd,gme);
2066 gme->del->contained = true;
2067
2068 if ( gme->g.r.height<10 ) {
2069 int extra = 2*bp+ sbwidth + (gme->has_titles?gme->fh:0) +
2070 gme->del->r.height+DEL_SPACE;
2071 gme->g.r.height = extra + gme->g.r.height*(gme->fh + gme->vpad);
2072 gme->g.inner.height = gme->g.r.height - 2*bp;
2073 }
2074
2075 memset(&wattrs,0,sizeof(wattrs));
2076 if ( gme->g.box->main_background!=COLOR_TRANSPARENT )
2077 wattrs.mask = wam_events|wam_cursor|wam_backcol;
2078 else
2079 wattrs.mask = wam_events|wam_cursor;
2080 wattrs.event_masks = ~(1<<et_charup);
2081 wattrs.cursor = ct_pointer;
2082 wattrs.background_color = gme->g.box->main_background;
2083 pos = gme->g.inner;
2084 pos.width -= sbwidth;
2085 pos.height -= sbwidth + gme->del->inner.height+DEL_SPACE;
2086 if ( gme->has_titles ) {
2087 pos.y += gme->fh;
2088 pos.height -= gme->fh;
2089 }
2090 gme->nested = GWidgetCreateSubWindow(base,&pos,matrixeditsub_e_h,gme,&wattrs);
2091
2092 GGadgetMove(gme->del,
2093 (gme->g.inner.width-gme->del->r.width)/2,
2094 gme->g.inner.height-gme->del->r.height-DEL_SPACE/2);
2095
2096 sub_gd.pos = pos;
2097 sub_gd.pos.x = pos.x+pos.width; sub_gd.pos.width = sbwidth;
2098 sub_gd.flags = (gd->flags & (gg_visible | gg_enabled)) | gg_sb_vert | gg_pos_in_pixels;
2099 sub_gd.handle_controlevent = _GME_VScroll;
2100 gme->vsb = GScrollBarCreate(base,&sub_gd,gme);
2101 gme->vsb->contained = true;
2102
2103 sub_gd.pos = pos;
2104 sub_gd.pos.y = pos.y+pos.height; sub_gd.pos.height = sbwidth;
2105 sub_gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
2106 sub_gd.handle_controlevent = _GME_HScroll;
2107 gme->hsb = GScrollBarCreate(base,&sub_gd,gme);
2108 gme->hsb->contained = true;
2109
2110 GME_RecalcFH(gme);
2111 {
2112 static GBox small = GBOX_EMPTY;
2113 static unichar_t nullstr[1] = { 0 };
2114
2115 small.main_background = gmatrixedit_activebg;
2116 small.main_foreground = gmatrixedit_activecol;
2117 memset(&sub_gd,'\0',sizeof(sub_gd));
2118 memset(&label,'\0',sizeof(label));
2119
2120 label.text = nullstr;
2121 label.font = gme->font;
2122 sub_gd.pos.height = gme->fh;
2123 sub_gd.pos.width = 40;
2124 sub_gd.label = &label;
2125 sub_gd.box = &small;
2126 sub_gd.flags = gg_enabled | gg_pos_in_pixels | gg_dontcopybox | gg_text_xim;
2127 gme->tf = GTextCompletionCreate(gme->nested,&sub_gd,gme);
2128 ((GTextField *) (gme->tf))->accepts_tabs = false;
2129 }
2130
2131 if ( gme->g.state!=gs_invisible )
2132 GDrawSetVisible(gme->nested,true);
2133 return( &gme->g );
2134 }
2135
GMatrixEditSet(GGadget * g,struct matrix_data * data,int rows,int copy_it)2136 void GMatrixEditSet(GGadget *g,struct matrix_data *data, int rows, int copy_it) {
2137 GMatrixEdit *gme = (GMatrixEdit *) g;
2138 int r,c;
2139
2140 if ( data==gme->data ) {
2141 if ( rows<gme->rows )
2142 gme->rows = rows;
2143 GME_RecalcFH(gme);
2144 } else {
2145 MatrixDataFree(gme);
2146
2147 gme->rows = gme->row_max = rows;
2148 if ( !copy_it ) {
2149 gme->data = data;
2150 } else {
2151 gme->data = calloc(rows*gme->cols,sizeof(struct matrix_data));
2152 memcpy(gme->data,data,rows*gme->cols*sizeof(struct matrix_data));
2153 for ( c=0; c<gme->cols; ++c ) {
2154 enum me_type me_type = gme->col_data[c].me_type;
2155 if ( me_type==me_string || me_type==me_bigstr || me_type==me_func ||
2156 me_type==me_button || me_type==me_onlyfuncedit ||
2157 me_type==me_funcedit || me_type==me_stringchoice ||
2158 me_type==me_stringchoicetrans || me_type==me_stringchoicetag ) {
2159 for ( r=0; r<rows; ++r )
2160 gme->data[r*gme->cols+c].u.md_str = copy(gme->data[r*gme->cols+c].u.md_str);
2161 }
2162 }
2163 }
2164 GME_RecalcFH(gme);
2165
2166 gme->active_row = gme->active_col = -1;
2167 GME_EnableDelete(gme);
2168 if ( !GME_AdjustCol(gme,-1)) {
2169 GME_FixScrollBars(gme);
2170 GDrawRequestExpose(gme->nested,NULL,false);
2171 }
2172 }
2173 }
2174
GMatrixEditDeleteRow(GGadget * g,int row)2175 void GMatrixEditDeleteRow(GGadget *g,int row) {
2176 GMatrixEdit *gme = (GMatrixEdit *) g;
2177
2178 if ( row!=-1 )
2179 gme->active_row = row;
2180 GME_DeleteActive(gme);
2181 }
2182
GMatrixEditStringDlg(GGadget * g,int row,int col)2183 int GMatrixEditStringDlg(GGadget *g,int row,int col) {
2184 GMatrixEdit *gme = (GMatrixEdit *) g;
2185 char *str;
2186
2187 if ( gme->edit_active ) {
2188 if ( !GME_FinishEdit(gme) )
2189 return(false);
2190 }
2191 if ( row!=-1 )
2192 gme->active_row = row;
2193 if ( col!=-1 )
2194 gme->active_col = col;
2195 str = MD_Text(gme,row,col);
2196 GME_StrBigEdit(gme,str);
2197 free(str);
2198 return( true );
2199 }
2200
GMatrixEditGet(GGadget * g,int * rows)2201 struct matrix_data *GMatrixEditGet(GGadget *g, int *rows) {
2202 GMatrixEdit *gme = (GMatrixEdit *) g;
2203
2204 if ( gme->edit_active && !GME_FinishEdit(gme) ) {
2205 *rows = 0;
2206 return( NULL );
2207 }
2208
2209 *rows = gme->rows;
2210 return( gme->data );
2211 }
2212
_GMatrixEditGet(GGadget * g,int * rows)2213 struct matrix_data *_GMatrixEditGet(GGadget *g, int *rows) {
2214 GMatrixEdit *gme = (GMatrixEdit *) g;
2215
2216 /* Does not try to parse the active textfield, if any */
2217 *rows = gme->rows;
2218 return( gme->data );
2219 }
2220
_GMatrixEditGetActiveTextField(GGadget * g)2221 GGadget *_GMatrixEditGetActiveTextField(GGadget *g) {
2222 GMatrixEdit *gme = (GMatrixEdit *) g;
2223 if ( gme->edit_active )
2224 return( gme->tf );
2225
2226 return( NULL );
2227 }
2228
GMatrixEditGetActiveRow(GGadget * g)2229 int GMatrixEditGetActiveRow(GGadget *g) {
2230 GMatrixEdit *gme = (GMatrixEdit *) g;
2231
2232 return( gme->active_row );
2233 }
2234
GMatrixEditGetActiveCol(GGadget * g)2235 int GMatrixEditGetActiveCol(GGadget *g) {
2236 GMatrixEdit *gme = (GMatrixEdit *) g;
2237
2238 return( gme->active_col );
2239 }
2240
GMatrixEditSetNewText(GGadget * g,char * text)2241 void GMatrixEditSetNewText(GGadget *g, char *text) {
2242 GMatrixEdit *gme = (GMatrixEdit *) g;
2243
2244 free(gme->newtext);
2245 gme->newtext = copy(text);
2246 }
2247
GMatrixEditSetOtherButtonEnable(GGadget * g,void (* sob)(GGadget * g,int r,int c))2248 void GMatrixEditSetOtherButtonEnable(GGadget *g, void (*sob)(GGadget *g, int r, int c)) {
2249 GMatrixEdit *gme = (GMatrixEdit *) g;
2250
2251 gme->setotherbuttons = sob;
2252 }
2253
GMatrixEditSetMouseMoveReporter(GGadget * g,void (* rmm)(GGadget * g,int r,int c))2254 void GMatrixEditSetMouseMoveReporter(GGadget *g, void (*rmm)(GGadget *g, int r, int c)) {
2255 GMatrixEdit *gme = (GMatrixEdit *) g;
2256
2257 gme->reportmousemove = rmm;
2258 }
2259
GMatrixEditSetTextChangeReporter(GGadget * g,void (* tcr)(GGadget * g,int r,int c,GGadget * text))2260 void GMatrixEditSetTextChangeReporter(GGadget *g, void (*tcr)(GGadget *g, int r, int c, GGadget *text)) {
2261 GMatrixEdit *gme = (GMatrixEdit *) g;
2262
2263 gme->reporttextchanged = tcr;
2264 }
2265
GMatrixEditSetValidateStr(GGadget * g,char * (* validate)(GGadget * g,int r,int c,int wasnew,char * str))2266 void GMatrixEditSetValidateStr(GGadget *g, char *(*validate)(GGadget *g, int r, int c, int wasnew, char *str)) {
2267 GMatrixEdit *gme = (GMatrixEdit *) g;
2268
2269 gme->validatestr = validate;
2270 }
2271
GMatrixEditSetUpDownVisible(GGadget * g,int visible)2272 void GMatrixEditSetUpDownVisible(GGadget *g, int visible) {
2273 GMatrixEdit *gme = (GMatrixEdit *) g;
2274 GGadgetCreateData gcd[3];
2275 GTextInfo label[2];
2276 int i;
2277
2278 if ( gme->up==NULL ) {
2279 if ( !visible )
2280 return;
2281
2282 memset(gcd,0,sizeof(gcd));
2283 memset(label,0,sizeof(label));
2284 i = 0;
2285
2286 /* I want the 2 pronged arrow, but gdraw can't find a nice one */
2287 /* label[i].text = (unichar_t *) "⇑"; *//* Up Arrow */
2288 label[i].text = (unichar_t *) "↑"; /* Up Arrow */
2289 label[i].text_is_1byte = true;
2290 gcd[i].gd.label = &label[i];
2291 gcd[i].gd.flags = gg_visible /*| gg_enabled*/ ;
2292 gcd[i].gd.handle_controlevent = _GME_Up;
2293 gcd[i].data = gme;
2294 gcd[i++].creator = GButtonCreate;
2295
2296 /* I want the 2 pronged arrow, but gdraw can't find a nice one */
2297 /* label[i].text = (unichar_t *) "⇓"; *//* Down Arrow */
2298 label[i].text = (unichar_t *) "↓"; /* Down Arrow */
2299 label[i].text_is_1byte = true;
2300 gcd[i].gd.label = &label[i];
2301 gcd[i].gd.flags = gg_visible /*| gg_enabled*/ ;
2302 gcd[i].gd.handle_controlevent = _GME_Down;
2303 gcd[i].data = gme;
2304 gcd[i++].creator = GButtonCreate;
2305 GGadgetsCreate(g->base,gcd);
2306
2307 gme->up = gcd[0].ret;
2308 gme->down = gcd[1].ret;
2309 gme->up->contained = gme->down->contained = true;
2310 } else {
2311 GGadgetSetVisible(gme->up,visible);
2312 GGadgetSetVisible(gme->down,visible);
2313 }
2314 }
2315
GMatrixEditAddButtons(GGadget * g,GGadgetCreateData * gcd)2316 void GMatrixEditAddButtons(GGadget *g, GGadgetCreateData *gcd) {
2317 GMatrixEdit *gme = (GMatrixEdit *) g;
2318 int i, base=0;
2319
2320 if ( gme->buttonlist!=NULL ) {
2321 for ( base=0; gme->buttonlist[base]!=NULL; ++base );
2322 }
2323 for ( i=0; gcd[i].creator!=NULL; ++i );
2324 gme->buttonlist = realloc(gme->buttonlist,(i+base+1)*sizeof(GGadget *));
2325 GGadgetsCreate(g->base,gcd);
2326 for ( i=0; gcd[i].creator!=NULL; ++i ) {
2327 gme->buttonlist[base+i] = gcd[i].ret;
2328 gcd[i].ret->contained = true;
2329 }
2330 gme->buttonlist[base+i] = NULL;
2331 }
2332
GMatrixEditEnableColumn(GGadget * g,int col,int enabled)2333 void GMatrixEditEnableColumn(GGadget *g, int col, int enabled) {
2334 GMatrixEdit *gme = (GMatrixEdit *) g;
2335 /* User must do a refresh of the gadget. Don't want to do it always */
2336 /* because multiple calls might cause a flicker */
2337
2338 if ( col<0 || col>=gme->cols )
2339 return;
2340 gme->col_data[col].disabled = !enabled;
2341 }
2342
GMatrixEditShowColumn(GGadget * g,int col,int visible)2343 void GMatrixEditShowColumn(GGadget *g, int col, int visible) {
2344 GMatrixEdit *gme = (GMatrixEdit *) g;
2345
2346 if ( col<0 || col>=gme->cols )
2347 return;
2348 gme->col_data[col].hidden = !visible;
2349 gme->col_data[col].fixed = false;
2350 GME_AdjustCol(gme,-1);
2351 }
2352
GMatrixEditSetColumnChoices(GGadget * g,int col,GTextInfo * ti)2353 void GMatrixEditSetColumnChoices(GGadget *g, int col, GTextInfo *ti) {
2354 GMatrixEdit *gme = (GMatrixEdit *) g;
2355
2356 if ( gme->col_data[col].enum_vals!=NULL )
2357 GMenuItemArrayFree(gme->col_data[col].enum_vals);
2358 if ( ti!=NULL )
2359 gme->col_data[col].enum_vals = GMenuItemFromTI(ti,
2360 gme->col_data[col].me_type==me_enum );
2361 else
2362 gme->col_data[col].enum_vals = NULL;
2363 }
2364
GMatrixEditGetColumnChoices(GGadget * g,int col)2365 GMenuItem *GMatrixEditGetColumnChoices(GGadget *g, int col) {
2366 GMatrixEdit *gme = (GMatrixEdit *) g;
2367
2368 return( gme->col_data[col].enum_vals );
2369 }
2370
GMatrixEditGetColCnt(GGadget * g)2371 int GMatrixEditGetColCnt(GGadget *g) {
2372 GMatrixEdit *gme = (GMatrixEdit *) g;
2373 return( gme->cols );
2374 }
2375
GMatrixEditScrollToRowCol(GGadget * g,int r,int c)2376 void GMatrixEditScrollToRowCol(GGadget *g,int r, int c) {
2377 GMatrixEdit *gme = (GMatrixEdit *) g;
2378 int rows_shown = gme->vsb->r.height/(gme->fh+gme->vpad);
2379 int context = rows_shown/3;
2380 int needs_expose = true;
2381 int width = gme->hsb->r.width;
2382 int i;
2383 GRect size;
2384
2385 if ( r<0 ) r = 0; else if ( r>=gme->rows ) r = gme->rows-1;
2386 if ( r<gme->off_top || r>=gme->off_top+rows_shown ) {
2387 gme->off_top = r-context;
2388 if ( gme->off_top<0 )
2389 gme->off_top = 0;
2390 needs_expose = true;
2391 }
2392 if ( c<0 ) c = 0; else if ( c>=gme->cols ) c = gme->cols-1;
2393 for ( i=0; i<gme->cols; ++i ) {
2394 if ( gme->col_data[i].x-gme->off_left>=0 )
2395 break;
2396 }
2397 if ( c<i ) {
2398 if ( c>0 && gme->col_data[c-1].width + gme->col_data[c].width<width )
2399 gme->off_left = gme->col_data[c-1].x;
2400 else
2401 gme->off_left = gme->col_data[c ].x;
2402 needs_expose = true;
2403 } else {
2404 for ( ; i<gme->cols; ++i ) {
2405 if ( gme->col_data[i].x+gme->col_data[i].width-gme->off_left>width )
2406 break;
2407 }
2408 if ( c>=i && gme->col_data[c].x!=gme->off_left ) {
2409 gme->off_left = gme->col_data[c].x;
2410 needs_expose = true;
2411 }
2412 }
2413 if ( needs_expose ) {
2414 int hend = gme->col_data[gme->cols-1].x + gme->col_data[gme->cols-1].width;
2415
2416 GDrawGetSize(gme->nested,&size);
2417 if ( gme->off_left>hend-size.width )
2418 gme->off_left = hend-size.width;
2419 if ( gme->off_left<0 )
2420 gme->off_left = 0;
2421 GScrollBarSetPos(gme->hsb,gme->off_left);
2422 GScrollBarSetPos(gme->vsb,gme->off_top);
2423 GGadgetRedraw(&gme->g);
2424 /* Used to request expose only if the row or column we are scrolling to */
2425 /* was outside of the visible area. However we need expose anyway, because */
2426 /* otherwise it is impossible to properly highlight fields in the active row. */
2427 /* So the rectangle associated with the expose event is now the only thing which */
2428 /* makes some difference. */
2429 } else {
2430 GGadgetGetSize(gme->tf,&size);
2431 GDrawRequestExpose(gme->nested,&size,false);
2432 }
2433 }
2434
GMatrixEditSetColumnCompletion(GGadget * g,int col,GTextCompletionHandler completion)2435 void GMatrixEditSetColumnCompletion(GGadget *g, int col,
2436 GTextCompletionHandler completion) {
2437 GMatrixEdit *gme = (GMatrixEdit *) g;
2438
2439 gme->col_data[col].completer = completion;
2440 }
2441
GMatrixEditSetBeforeDelete(GGadget * g,void (* predelete)(GGadget * g,int r))2442 void GMatrixEditSetBeforeDelete(GGadget *g, void (*predelete)(GGadget *g, int r)) {
2443 GMatrixEdit *gme = (GMatrixEdit *) g;
2444
2445 gme->predelete = predelete;
2446 }
2447
GMatrixEditSetRowMotionCallback(GGadget * g,void (* rowmotion)(GGadget * g,int oldr,int newr))2448 void GMatrixEditSetRowMotionCallback(GGadget *g, void (*rowmotion)(GGadget *g, int oldr, int newr)) {
2449 GMatrixEdit *gme = (GMatrixEdit *) g;
2450
2451 gme->rowmotion = rowmotion;
2452 }
2453
GMatrixEditSetCanUpDown(GGadget * g,enum gme_updown (* canupdown)(GGadget * g,int r))2454 void GMatrixEditSetCanUpDown(GGadget *g, enum gme_updown (*canupdown)(GGadget *g, int r)) {
2455 GMatrixEdit *gme = (GMatrixEdit *) g;
2456
2457 gme->canupdown = canupdown;
2458 }
2459
GMatrixEditActivateRowCol(GGadget * g,int r,int c)2460 void GMatrixEditActivateRowCol(GGadget *g, int r, int c) {
2461 GMatrixEdit *gme = (GMatrixEdit *) g;
2462
2463 gme->active_row = r;
2464 gme->active_col = c;
2465 GME_EnableDelete(gme);
2466 GDrawRequestExpose(gme->nested,NULL,false);
2467 }
2468
GMatrixEditSetEditable(GGadget * g,int editable)2469 void GMatrixEditSetEditable(GGadget *g, int editable ) {
2470 GMatrixEdit *gme = (GMatrixEdit *) g;
2471
2472 gme->no_edit = !editable;
2473 GGadgetSetVisible(gme->del,editable);
2474 GMatrixEdit_Resize(&gme->g,gme->g.r.width,gme->g.r.height);
2475 GDrawRequestExpose(gme->nested,NULL,false);
2476 }
2477
_GMatrixEditRIHead(void)2478 GResInfo *_GMatrixEditRIHead(void) {
2479 /* GRect size; */
2480
2481 _GMatrixEdit_Init();
2482 /* GDrawGetSize(GDrawGetRoot(NULL),&size);*/
2483 if ( true /* size.height<900*/ ) {
2484 gmatrixedit_ri.next = &gmatrixedit2_ri;
2485 gmatrixedit_ri.extras = NULL;
2486 gmatrixedit_ri.seealso1 = &gmatrixedit2_ri;
2487 }
2488
2489 return( &gmatrixedit_ri );
2490 }
2491