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