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

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

Generated by: LCOV version 1.10