LCOV - code coverage report
Current view: top level - gdraw - gmatrixedit.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 0 1654 0.0 %
Date: 2017-08-04 Functions: 0 80 0.0 %

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

Generated by: LCOV version 1.10