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

          Line data    Source code
       1             : /* Copyright (C) 2002-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 "fontforgeui.h"
      28             : #include "langfreq.h"
      29             : #include "splinefill.h"
      30             : #include "splineutil.h"
      31             : #include "tottfgpos.h"
      32             : #include <gkeysym.h>
      33             : #include <math.h>
      34             : 
      35             : 
      36             : #include "sftextfieldP.h"
      37             : #include <ustring.h>
      38             : #include <utype.h>
      39             : #include <chardata.h>
      40             : 
      41             : static GBox sftextarea_box = GBOX_EMPTY; /* Don't initialize here */
      42             : static int sftextarea_inited = false;
      43             : static FontInstance *sftextarea_font;
      44             : 
      45             : static unichar_t nullstr[] = { 0 }, 
      46             :         newlinestr[] = { '\n', 0 }, tabstr[] = { '\t', 0 };
      47             : 
      48             : static int SFTextArea_Show(SFTextArea *st, int pos);
      49             : static void GTPositionGIC(SFTextArea *st);
      50             : 
      51           0 : static void SFTextAreaRefigureLines(SFTextArea *st, int start_of_change,
      52             :         int end_of_change) {
      53             : 
      54           0 :     if ( st->vsb!=NULL && st->li.lines==NULL )
      55           0 :         GScrollBarSetBounds(&st->vsb->g,0,0,st->g.inner.height);
      56             : 
      57           0 :     LayoutInfoRefigureLines(&st->li,start_of_change,end_of_change,
      58             :             st->g.inner.width);
      59             : 
      60           0 :     if ( st->hsb!=NULL )
      61           0 :         GScrollBarSetBounds(&st->hsb->g,0,st->li.xmax,st->g.inner.width);
      62           0 :     if ( st->vsb!=NULL && st->li.lcnt>0 )
      63           0 :         GScrollBarSetBounds(&st->vsb->g,0,st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh,st->g.inner.height);
      64           0 : }
      65             : 
      66           0 : static void SFTextAreaChanged(SFTextArea *st,int src) {
      67             :     GEvent e;
      68             : 
      69           0 :     e.type = et_controlevent;
      70           0 :     e.w = st->g.base;
      71           0 :     e.u.control.subtype = et_textchanged;
      72           0 :     e.u.control.g = &st->g;
      73           0 :     e.u.control.u.tf_changed.from_pulldown = src;
      74           0 :     if ( st->g.handle_controlevent != NULL )
      75           0 :         (st->g.handle_controlevent)(&st->g,&e);
      76             :     else
      77           0 :         GDrawPostEvent(&e);
      78           0 : }
      79             : 
      80           0 : static void SFTextAreaFocusChanged(SFTextArea *st,int gained) {
      81             :     GEvent e;
      82             : 
      83           0 :     e.type = et_controlevent;
      84           0 :     e.w = st->g.base;
      85           0 :     e.u.control.subtype = et_textfocuschanged;
      86           0 :     e.u.control.g = &st->g;
      87           0 :     e.u.control.u.tf_focus.gained_focus = gained;
      88           0 :     if ( st->g.handle_controlevent != NULL )
      89           0 :         (st->g.handle_controlevent)(&st->g,&e);
      90             :     else
      91           0 :         GDrawPostEvent(&e);
      92           0 : }
      93             : 
      94           0 : static void _SFTextAreaReplace(SFTextArea *st, const unichar_t *str) {
      95             : 
      96           0 :     st->sel_oldstart = st->sel_start;
      97           0 :     st->sel_oldend = st->sel_end;
      98           0 :     st->sel_oldbase = st->sel_base;
      99           0 :     st->sel_start += LayoutInfoReplace(&st->li,str,st->sel_start,st->sel_end,
     100             :             st->g.inner.width);
     101           0 :     st->sel_end = st->sel_start;
     102             : 
     103           0 :     if ( st->hsb!=NULL )
     104           0 :         GScrollBarSetBounds(&st->hsb->g,0,st->li.xmax,st->g.inner.width);
     105           0 :     if ( st->vsb!=NULL && st->li.lcnt>0 )
     106           0 :         GScrollBarSetBounds(&st->vsb->g,0,st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh,st->g.inner.height);
     107           0 : }
     108             : 
     109           0 : static void SFTextArea_Replace(SFTextArea *st, const unichar_t *str) {
     110           0 :     _SFTextAreaReplace(st,str);
     111           0 :     SFTextArea_Show(st,st->sel_start);
     112           0 : }
     113             : 
     114           0 : static int SFTextAreaFindLine(SFTextArea *st, int pos) {
     115             :     int i;
     116             : 
     117           0 :     for ( i=0; i+1<st->li.lcnt; ++i )
     118           0 :         if ( pos<st->li.lineheights[i+1].start_pos )
     119           0 :     break;
     120             : 
     121           0 : return( i );
     122             : }
     123             : 
     124           0 : static int PSTComponentCount(PST *pst) {
     125           0 :     int cnt=0;
     126           0 :     char *pt = pst->u.lig.components;
     127             : 
     128             :     for (;;) {
     129           0 :         while ( *pt==' ' ) ++pt;
     130           0 :         if ( *pt=='\0' )
     131           0 : return( cnt );
     132           0 :         while ( *pt!=' ' && *pt!='\0' ) ++pt;
     133           0 :         ++cnt;
     134           0 :     }
     135             : }
     136             : 
     137           0 : static int PSTLigComponentCount(SplineChar *sc) {
     138             :     PST *pst;
     139             :     int lcnt, ltemp;
     140             : 
     141           0 :     lcnt = 0;
     142           0 :     for ( pst=sc->possub; pst!=NULL ; pst=pst->next ) {
     143             :         /* Find out the number of components. Note that ffi might be f+f+i or ff+i */
     144             :         /*  so find the max */
     145           0 :         if ( pst->type==pst_ligature ) {
     146           0 :             ltemp = PSTComponentCount(pst);
     147           0 :             if ( ltemp>lcnt )
     148           0 :                 lcnt = ltemp;
     149             :         }
     150             :     }
     151           0 : return( lcnt );
     152             : }
     153             : 
     154           0 : static int SFTextAreaGetOffsetFromXPos(SFTextArea *st,int i,int xpos) {
     155             :     int p, x, xend, pos, j, l, r2l, lcnt;
     156             :     struct opentype_str **line;
     157             :     PST *pst;
     158             : 
     159           0 :     if ( i<0 )
     160           0 : return( 0 );
     161           0 :     if ( i>=st->li.lcnt )
     162           0 : return( u_strlen(st->li.text));
     163             : 
     164           0 :     p = st->li.lineheights[i].p;
     165           0 :     if ( st->li.paras[p].para[0]!=NULL &&
     166           0 :             ScriptIsRightToLeft( ((struct fontlist *) (st->li.paras[p].para[0]->fl))->script )) {
     167           0 :         x = st->li.xmax - st->li.lineheights[i].linelen;
     168           0 :         r2l = true;
     169             :     } else {
     170           0 :         x = 0;
     171           0 :         r2l = false;
     172             :     }
     173             : 
     174           0 :     line = st->li.lines[i];
     175           0 :     if ( line[0]==NULL ) {
     176           0 :         pos = st->li.lineheights[i].start_pos;
     177             :     } else {
     178           0 :         for ( j=0; line[j]!=NULL; ++j ) {
     179           0 :             xend = x + line[j]->advance_width + line[j]->vr.h_adv_off;
     180           0 :             if ( xpos<xend ) {
     181             :                 double scale;
     182           0 :                 xpos -= x; xend -= x;
     183             :                 /* Check for ligature carets */
     184           0 :                 for ( pst=line[j]->sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
     185           0 :                 if ( pst!=NULL && pst->u.lcaret.cnt==0 )
     186           0 :                     pst = NULL;
     187           0 :                 lcnt = 0;
     188           0 :                 if ( pst==NULL ) {
     189           0 :                     lcnt = PSTLigComponentCount(line[j]->sc);
     190           0 :                     if ( lcnt<=1 ) {
     191           0 :                         if ( xpos>xend/2 )
     192           0 :                             ++j;
     193             :                     } else {
     194           0 :                         if ( line[j+1]!=NULL && xpos>(2*lcnt-1)*xend/lcnt ) {
     195           0 :                             ++j;
     196           0 :                             lcnt = 0;
     197             :                         }
     198             :                     }
     199             :                 } else {
     200           0 :                     FontData *fd = ((struct fontlist *) (line[j]->fl))->fd;
     201           0 :                     scale = fd->pointsize*st->li.dpi / (72.0*(fd->sf->ascent+fd->sf->descent));
     202           0 :                     if ( xpos-pst->u.lcaret.carets[ pst->u.lcaret.cnt-1 ]*scale > xend-xpos ) {
     203           0 :                         ++j;
     204           0 :                         pst = NULL;
     205             :                     }
     206             :                 }
     207           0 :                 if ( line[j]==NULL ) {
     208           0 :                     pos = line[j-1]->orig_index + ((struct fontlist *) (line[j-1]->fl))->start +1;
     209             :                 } else
     210           0 :                     pos = line[j]->orig_index + ((struct fontlist *) (line[j]->fl))->start;
     211           0 :                 if ( pst!=NULL ) {
     212           0 :                     for ( l=0; l<pst->u.lcaret.cnt; ++l )
     213           0 :                         if (( l==0 && xpos < scale*pst->u.lcaret.carets[0]-xpos ) ||
     214           0 :                                 (l!=0 && xpos-scale*pst->u.lcaret.carets[l-1] < scale*pst->u.lcaret.carets[0]-xpos ))
     215             :                     break;
     216           0 :                     if ( r2l )
     217           0 :                         l = pst->u.lcaret.cnt-l;
     218           0 :                     pos += l;
     219           0 :                 } else if ( lcnt>=2 ) {
     220             :                     /* Ok it's a ligature with lcnt components. Assume each has the */
     221             :                     /*  length */
     222           0 :                     l = (xpos+xend/(2*lcnt))/(xend/lcnt);
     223           0 :                     if ( r2l )
     224           0 :                         l = lcnt-1-l;
     225           0 :                     pos += l;
     226             :                 }
     227           0 :         break;
     228             :             }
     229           0 :             x = xend;
     230             :         }
     231           0 :         if ( line[j]==NULL )
     232           0 :             pos = line[j-1]->orig_index + ((struct fontlist *) (line[j-1]->fl))->start +1;
     233             :     }
     234           0 : return( pos );
     235             : }
     236             : 
     237           0 : static int SFTextAreaGetXPosFromOffset(SFTextArea *st,int l,int pos) {
     238             :     int j, scpos, lcnt, x;
     239             :     struct opentype_str **line;
     240             :     PST *pst;
     241             : 
     242           0 :     if ( l<0 || l>= st->li.lcnt )
     243           0 : return( 0 );
     244           0 :     if ( st->li.lines[0]==NULL || pos < st->li.lineheights[l].start_pos )
     245           0 : return( 0 );
     246             : 
     247           0 :     line = st->li.lines[l];
     248           0 :     x = 0;
     249           0 :     for ( j=0; line[j]!=NULL; ++j ) {
     250           0 :         scpos = line[j]->orig_index + ((struct fontlist *) (line[j]->fl))->start;
     251           0 :         if ( scpos==pos )
     252           0 : return( x );
     253           0 :         for ( pst=line[j]->sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
     254           0 :         if ( pst!=NULL && pst->u.lcaret.cnt==0 )
     255           0 :             pst = NULL;
     256           0 :         if ( pst!=NULL && pos>scpos && pos<=scpos+pst->u.lcaret.cnt ) {
     257           0 :             FontData *fd = ((struct fontlist *) (line[j]->fl))->fd;
     258           0 :             double scale = fd->pointsize*st->li.dpi / (72.0*(fd->sf->ascent+fd->sf->descent));
     259           0 : return( x + rint(scale*pst->u.lcaret.carets[pos-scpos-1]) );
     260             :         }
     261           0 :         x += line[j]->advance_width + line[j]->vr.h_adv_off;
     262             :     }
     263             :     /* Ok, maybe they didn't specify lig carets. Check if we are within a ligature */
     264           0 :     x=0;
     265           0 :     for ( j=0; line[j]!=NULL; ++j ) {
     266           0 :         scpos = line[j]->orig_index + ((struct fontlist *) (line[j]->fl))->start;
     267           0 :         if ( scpos==pos )
     268           0 : return( x );
     269           0 :         lcnt = PSTLigComponentCount(line[j]->sc);
     270           0 :         if ( pos>scpos && pos<scpos+lcnt ) {
     271           0 :             int wid = line[j]->advance_width + line[j]->vr.h_adv_off;
     272           0 : return( x + (pos-scpos)*wid/lcnt );
     273             :         }
     274           0 :         x += line[j]->advance_width + line[j]->vr.h_adv_off;
     275             :     }
     276           0 : return( x );
     277             : }
     278             : 
     279           0 : static int SFTextArea_EndPage(SFTextArea *st) {
     280             :     int endpage;
     281             : 
     282           0 :     for ( endpage=1; st->li.lcnt-endpage>=0 &&
     283           0 :             st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh
     284           0 :                 -st->li.lineheights[st->li.lcnt-endpage].y <= st->g.inner.height;
     285           0 :             ++endpage );
     286           0 :     if ( (endpage-=2) < 1 ) endpage = 1;
     287           0 : return( endpage );
     288             : }
     289             : 
     290           0 : static int SFTextArea_Show(SFTextArea *st, int pos) {
     291             :     int i, xoff, loff, x, xlen;
     292           0 :     int refresh=false, endpage, page;
     293             : 
     294           0 :     if ( pos < 0 ) pos = 0;
     295           0 :     if ( pos > u_strlen(st->li.text)) pos = u_strlen(st->li.text);
     296           0 :     i = SFTextAreaFindLine(st,pos);
     297             : 
     298           0 :     loff = st->loff_top;
     299           0 :     for ( page=1; st->loff_top+page<st->li.lcnt && st->li.lineheights[st->loff_top+page].y-st->li.lineheights[st->loff_top].y<=st->g.inner.height;
     300           0 :             ++page );
     301           0 :     if ( --page < 1 ) page = 1;
     302             :     /* a page starting at loff_top may have a different number of lines than */
     303             :     /*  a page ending at lcnt */
     304           0 :     endpage = SFTextArea_EndPage(st);
     305           0 :     if ( i<loff || i>=st->loff_top+page)
     306           0 :         loff = i-page/4;
     307           0 :     if ( loff > st->li.lcnt-endpage )
     308           0 :         loff = st->li.lcnt-endpage;
     309           0 :     if ( loff<0 ) loff = 0;
     310           0 :     if ( st->li.lcnt==0 || st->li.lineheights[st->li.lcnt-1].y<st->g.inner.height )
     311           0 :         loff = 0;
     312             : 
     313           0 :     xoff = st->xoff_left;
     314           0 :     x = 0;
     315           0 :     if ( i<st->li.lcnt ) {
     316           0 :         x = SFTextAreaGetXPosFromOffset(st,i,pos);
     317           0 :         xlen = st->li.lineheights[i].linelen;
     318           0 :         if ( xlen< st->g.inner.width )
     319           0 :             xoff = 0;
     320           0 :         else if ( x<xoff+4 || x>=xoff+st->g.inner.width-4 ) {
     321           0 :             xoff = x - xlen/4;
     322           0 :             if ( xoff<0 ) xoff = 0;
     323             :         }
     324             :     }
     325             : 
     326           0 :     if ( xoff!=st->xoff_left ) {
     327           0 :         st->xoff_left = xoff;
     328           0 :         if ( st->hsb!=NULL )
     329           0 :             GScrollBarSetPos(&st->hsb->g,xoff);
     330           0 :         refresh = true;
     331             :     }
     332           0 :     if ( loff!=st->loff_top ) {
     333           0 :         st->loff_top = loff;
     334           0 :         if ( st->vsb!=NULL )
     335           0 :             GScrollBarSetPos(&st->vsb->g,st->li.lineheights[loff].y);
     336           0 :         refresh = true;
     337             :     }
     338           0 :     GTPositionGIC(st);
     339           0 : return( refresh );
     340             : }
     341             : 
     342           0 : static void *genunicodedata(void *_gt,int32 *len) {
     343           0 :     SFTextArea *st = _gt;
     344             :     unichar_t *temp;
     345           0 :     *len = st->sel_end-st->sel_start + 1;
     346           0 :     temp = malloc((*len+2)*sizeof(unichar_t));
     347           0 :     temp[0] = 0xfeff;           /* KDE expects a byte order flag */
     348           0 :     u_strncpy(temp+1,st->li.text+st->sel_start,st->sel_end-st->sel_start);
     349           0 :     temp[*len+1] = 0;
     350           0 : return( temp );
     351             : }
     352             : 
     353           0 : static void *genutf8data(void *_gt,int32 *len) {
     354           0 :     SFTextArea *st = _gt;
     355           0 :     unichar_t *temp =u_copyn(st->li.text+st->sel_start,st->sel_end-st->sel_start);
     356           0 :     char *ret = u2utf8_copy(temp);
     357           0 :     free(temp);
     358           0 :     *len = strlen(ret);
     359           0 : return( ret );
     360             : }
     361             : 
     362           0 : static void *ddgenunicodedata(void *_gt,int32 *len) {
     363           0 :     void *temp = genunicodedata(_gt,len);
     364           0 :     SFTextArea *st = _gt;
     365           0 :     _SFTextAreaReplace(st,nullstr);
     366           0 :     _ggadget_redraw(&st->g);
     367           0 : return( temp );
     368             : }
     369             : 
     370           0 : static void *genlocaldata(void *_gt,int32 *len) {
     371           0 :     SFTextArea *st = _gt;
     372           0 :     unichar_t *temp =u_copyn(st->li.text+st->sel_start,st->sel_end-st->sel_start);
     373           0 :     char *ret = u2def_copy(temp);
     374           0 :     free(temp);
     375           0 :     *len = strlen(ret);
     376           0 : return( ret );
     377             : }
     378             : 
     379           0 : static void *ddgenlocaldata(void *_gt,int32 *len) {
     380           0 :     void *temp = genlocaldata(_gt,len);
     381           0 :     SFTextArea *st = _gt;
     382           0 :     _SFTextAreaReplace(st,nullstr);
     383           0 :     _ggadget_redraw(&st->g);
     384           0 : return( temp );
     385             : }
     386             : 
     387           0 : static void noop(void *_st) {
     388           0 : }
     389             : 
     390           0 : static void SFTextAreaGrabPrimarySelection(SFTextArea *st) {
     391           0 :     int ss = st->sel_start, se = st->sel_end;
     392             : 
     393           0 :     GDrawGrabSelection(st->g.base,sn_primary);
     394           0 :     st->sel_start = ss; st->sel_end = se;
     395           0 :     GDrawAddSelectionType(st->g.base,sn_primary,"text/plain;charset=ISO-10646-UCS-4",st,st->sel_end-st->sel_start,
     396             :             sizeof(unichar_t),
     397             :             genunicodedata,noop);
     398           0 :     GDrawAddSelectionType(st->g.base,sn_primary,"UTF8_STRING",st,3*(st->sel_end-st->sel_start),
     399             :             sizeof(unichar_t),
     400             :             genutf8data,noop);
     401           0 :     GDrawAddSelectionType(st->g.base,sn_primary,"STRING",st,st->sel_end-st->sel_start,sizeof(char),
     402             :             genlocaldata,noop);
     403           0 : }
     404             : 
     405           0 : static void SFTextAreaGrabDDSelection(SFTextArea *st) {
     406             : 
     407           0 :     GDrawGrabSelection(st->g.base,sn_drag_and_drop);
     408           0 :     GDrawAddSelectionType(st->g.base,sn_drag_and_drop,"text/plain;charset=ISO-10646-UCS-4",st,st->sel_end-st->sel_start,
     409             :             sizeof(unichar_t),
     410             :             ddgenunicodedata,noop);
     411           0 :     GDrawAddSelectionType(st->g.base,sn_drag_and_drop,"STRING",st,st->sel_end-st->sel_start,sizeof(char),
     412             :             ddgenlocaldata,noop);
     413           0 : }
     414             : 
     415           0 : static void SFTextAreaGrabSelection(SFTextArea *st, enum selnames sel ) {
     416             : 
     417           0 :     if ( st->sel_start!=st->sel_end ) {
     418             :         unichar_t *temp;
     419             :         char *ctemp;
     420             :         int i;
     421             :         uint16 *u2temp;
     422             : 
     423           0 :         GDrawGrabSelection(st->g.base,sel);
     424           0 :         temp = malloc((st->sel_end-st->sel_start + 2)*sizeof(unichar_t));
     425           0 :         temp[0] = 0xfeff;               /* KDE expects a byte order flag */
     426           0 :         u_strncpy(temp+1,st->li.text+st->sel_start,st->sel_end-st->sel_start);
     427           0 :         ctemp = u2utf8_copy(temp);
     428           0 :         GDrawAddSelectionType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-4",temp,u_strlen(temp),
     429             :                 sizeof(unichar_t),
     430             :                 NULL,NULL);
     431           0 :         u2temp = malloc((st->sel_end-st->sel_start + 2)*sizeof(uint16));
     432           0 :         for ( i=0; temp[i]!=0; ++i )
     433           0 :             u2temp[i] = temp[i];
     434           0 :         u2temp[i] = 0;
     435           0 :         GDrawAddSelectionType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-2",u2temp,u_strlen(temp),
     436             :                 2,
     437             :                 NULL,NULL);
     438           0 :         GDrawAddSelectionType(st->g.base,sel,"UTF8_STRING",ctemp,strlen(ctemp),sizeof(char),
     439             :                 NULL,NULL);
     440           0 :         GDrawAddSelectionType(st->g.base,sel,"STRING",u2def_copy(temp),u_strlen(temp),sizeof(char),
     441             :                 NULL,NULL);
     442             :     }
     443           0 : }
     444             : 
     445           0 : static int SFTextAreaSelBackword(unichar_t *text,int start) {
     446             :     unichar_t ch;
     447             : 
     448           0 :     if ( start==0 )
     449           0 : return( 0 ); /* Can't go back */;
     450             : 
     451           0 :     ch = text[start-1];
     452           0 :     if ( isalnum(ch) || ch=='_' ) {
     453             :         int i;
     454           0 :         for ( i=start-1; i>=0 && (isalnum(text[i]) || text[i]=='_') ; --i );
     455           0 :         start = i+1;
     456             :     } else {
     457             :         int i;
     458           0 :         for ( i=start-1; i>=0 && !isalnum(text[i]) && text[i]!='_' ; --i );
     459           0 :         start = i+1;
     460             :     }
     461           0 : return( start );
     462             : }
     463             : 
     464           0 : static int SFTextAreaSelForeword(unichar_t *text,int end) {
     465           0 :     unichar_t ch = text[end];
     466             : 
     467           0 :     if ( ch=='\0' )
     468             :         /* Nothing */;
     469           0 :     else if ( isalnum(ch) || ch=='_' ) {
     470             :         int i;
     471           0 :         for ( i=end; isalnum(text[i]) || text[i]=='_' ; ++i );
     472           0 :         end = i;
     473             :     } else {
     474             :         int i;
     475           0 :         for ( i=end; !isalnum(text[i]) && text[i]!='_' && text[i]!='\0' ; ++i );
     476           0 :         end = i;
     477             :     }
     478           0 : return( end );
     479             : }
     480             : 
     481           0 : static void SFTextAreaSelectWord(SFTextArea *st,int mid, int16 *start, int16 *end) {
     482           0 :     unichar_t *text = st->li.text;
     483           0 :     unichar_t ch = text[mid];
     484             : 
     485           0 :     if ( ch=='\0' )
     486           0 :         *start = *end = mid;
     487           0 :     else if ( isspace(ch) ) {
     488             :         int i;
     489           0 :         for ( i=mid; isspace(text[i]); ++i );
     490           0 :         *end = i;
     491           0 :         for ( i=mid-1; i>=0 && isspace(text[i]) ; --i );
     492           0 :         *start = i+1;
     493           0 :     } else if ( isalnum(ch) || ch=='_' ) {
     494             :         int i;
     495           0 :         for ( i=mid; isalnum(text[i]) || text[i]=='_' ; ++i );
     496           0 :         *end = i;
     497           0 :         for ( i=mid-1; i>=0 && (isalnum(text[i]) || text[i]=='_') ; --i );
     498           0 :         *start = i+1;
     499             :     } else {
     500             :         int i;
     501           0 :         for ( i=mid; !isalnum(text[i]) && text[i]!='_' && text[i]!='\0' ; ++i );
     502           0 :         *end = i;
     503           0 :         for ( i=mid-1; i>=0 && !isalnum(text[i]) && text[i]!='_' ; --i );
     504           0 :         *start = i+1;
     505             :     }
     506           0 : }
     507             : 
     508           0 : static void SFTextAreaSelectWords(SFTextArea *st,int last) {
     509             :     int16 ss, se;
     510           0 :     SFTextAreaSelectWord(st,st->sel_base,&st->sel_start,&st->sel_end);
     511           0 :     if ( last!=st->sel_base ) {
     512           0 :         SFTextAreaSelectWord(st,last,&ss,&se);
     513           0 :         if ( ss<st->sel_start ) st->sel_start = ss;
     514           0 :         if ( se>st->sel_end ) st->sel_end = se;
     515             :     }
     516           0 : }
     517             : 
     518           0 : static void SFTextAreaPaste(SFTextArea *st,enum selnames sel) {
     519           0 :     if ( GDrawSelectionHasType(st->g.base,sel,"UTF8_STRING") ||
     520           0 :             GDrawSelectionHasType(st->g.base,sel,"text/plain;charset=UTF-8")) {
     521             :         unichar_t *temp; char *ctemp;
     522             :         int32 len;
     523           0 :         if ( GDrawSelectionHasType(st->g.base,sel,"UTF8_STRING") )
     524           0 :             ctemp = GDrawRequestSelection(st->g.base,sel,"UTF8_STRING",&len);
     525             :         else
     526           0 :             ctemp = GDrawRequestSelection(st->g.base,sel,"text/plain;charset=UTF-8",&len);
     527           0 :         if ( ctemp!=NULL ) {
     528           0 :             temp = utf82u_copyn(ctemp,strlen(ctemp));
     529           0 :             SFTextArea_Replace(st,temp);
     530           0 :             free(ctemp); free(temp);
     531             :         }
     532           0 :     } else if ( GDrawSelectionHasType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-4")) {
     533             :         unichar_t *temp;
     534             :         int32 len;
     535           0 :         temp = GDrawRequestSelection(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-4",&len);
     536             :         /* Bug! I don't handle byte reversed selections. But I don't think there should be any anyway... */
     537           0 :         if ( temp!=NULL )
     538           0 :             SFTextArea_Replace(st,temp[0]==0xfeff?temp+1:temp);
     539           0 :         free(temp);
     540           0 :     } else if ( GDrawSelectionHasType(st->g.base,sel,"Unicode") ||
     541           0 :             GDrawSelectionHasType(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-2")) {
     542             :         unichar_t *temp;
     543             :         uint16 *temp2;
     544             :         int32 len;
     545           0 :         temp2 = GDrawRequestSelection(st->g.base,sel,"text/plain;charset=ISO-10646-UCS-2",&len);
     546           0 :         if ( temp2==NULL || len==0 )
     547           0 :             temp2 = GDrawRequestSelection(st->g.base,sel,"Unicode",&len);
     548           0 :         if ( temp2!=NULL ) {
     549             :             int i;
     550           0 :             temp = malloc((len/2+1)*sizeof(unichar_t));
     551           0 :             for ( i=0; temp2[i]!=0; ++i )
     552           0 :                 temp[i] = temp2[i];
     553           0 :             temp[i] = 0;
     554           0 :             SFTextArea_Replace(st,temp[0]==0xfeff?temp+1:temp);
     555           0 :             free(temp);
     556             :         }
     557           0 :         free(temp2);
     558           0 :     } else if ( GDrawSelectionHasType(st->g.base,sel,"STRING")) {
     559             :         unichar_t *temp; char *ctemp;
     560             :         int32 len;
     561           0 :         ctemp = GDrawRequestSelection(st->g.base,sel,"STRING",&len);
     562           0 :         if ( ctemp!=NULL ) {
     563           0 :             temp = def2u_copy(ctemp);
     564           0 :             SFTextArea_Replace(st,temp);
     565           0 :             free(ctemp); free(temp);
     566             :         }
     567             :     }
     568           0 : }
     569             : 
     570           0 : static int sftextarea_editcmd(GGadget *g,enum editor_commands cmd) {
     571           0 :     SFTextArea *st = (SFTextArea *) g;
     572             :     int i;
     573             : 
     574           0 :     switch ( cmd ) {
     575             :       case ec_selectall:
     576           0 :         st->sel_start = 0;
     577           0 :         st->sel_end = u_strlen(st->li.text);
     578           0 : return( true );
     579             :       case ec_clear:
     580           0 :         SFTextArea_Replace(st,nullstr);
     581           0 : return( true );
     582             :       case ec_cut:
     583           0 :         SFTextAreaGrabSelection(st,sn_clipboard);
     584           0 :         SFTextArea_Replace(st,nullstr);
     585           0 : return( true );
     586             :       case ec_copy:
     587           0 :         SFTextAreaGrabSelection(st,sn_clipboard);
     588           0 : return( true );
     589             :       case ec_paste:
     590           0 :         SFTextAreaPaste(st,sn_clipboard);
     591           0 :         SFTextArea_Show(st,st->sel_start);
     592           0 : return( true );
     593             :       case ec_undo:
     594           0 :         if ( st->li.oldtext!=NULL ) {
     595           0 :             unichar_t *temp = st->li.text;
     596           0 :             struct fontlist *ofl = st->li.fontlist;
     597             :             int16 s;
     598           0 :             st->li.text = st->li.oldtext; st->li.oldtext = temp;
     599           0 :             st->li.fontlist = st->li.oldfontlist; st->li.oldfontlist = ofl;
     600           0 :             s = st->sel_start; st->sel_start = st->sel_oldstart; st->sel_oldstart = s;
     601           0 :             s = st->sel_end; st->sel_end = st->sel_oldend; st->sel_oldend = s;
     602           0 :             s = st->sel_base; st->sel_base = st->sel_oldbase; st->sel_oldbase = s;
     603           0 :             for ( i=0; i<st->li.pcnt; ++i )
     604           0 :                 free( st->li.paras[i].para);
     605           0 :             free(st->li.paras); st->li.paras = NULL; st->li.pcnt = 0; st->li.pmax = 0;
     606           0 :             for ( i=0; i<st->li.lcnt; ++i )
     607           0 :                 free( st->li.lines[i]);
     608           0 :             free( st->li.lines );
     609           0 :             free( st->li.lineheights );
     610           0 :             st->li.lines = NULL; st->li.lineheights = NULL; st->li.lcnt = 0;
     611           0 :             SFTextAreaRefigureLines(st, 0, -1);
     612           0 :             SFTextArea_Show(st,st->sel_end);
     613             :         }
     614           0 : return( true );
     615             :       case ec_redo:             /* Hmm. not sure */ /* we don't do anything */
     616           0 : return( true );                 /* but probably best to return success */
     617             :       case ec_backword:
     618           0 :         if ( st->sel_start==st->sel_end && st->sel_start!=0 ) {
     619           0 :             st->sel_start = SFTextAreaSelBackword(st->li.text,st->sel_start);
     620             :         }
     621           0 :         SFTextArea_Replace(st,nullstr);
     622           0 : return( true );
     623             :       case ec_deleteword:
     624           0 :         if ( st->sel_start==st->sel_end && st->sel_start!=0 )
     625           0 :             SFTextAreaSelectWord(st,st->sel_start,&st->sel_start,&st->sel_end);
     626           0 :         SFTextArea_Replace(st,nullstr);
     627           0 : return( true );
     628             :     }
     629           0 : return( false );
     630             : }
     631             : 
     632           0 : static int _sftextarea_editcmd(GGadget *g,enum editor_commands cmd) {
     633           0 :     if ( sftextarea_editcmd(g,cmd)) {
     634           0 :         _ggadget_redraw(g);
     635           0 :         GTPositionGIC((SFTextArea *) g);
     636           0 : return( true );
     637             :     }
     638           0 : return( false );
     639             : }
     640             : 
     641           0 : static int GTBackPos(SFTextArea *st,int pos, int ismeta) {
     642             :     int newpos/*, xloc, l*/;
     643             : 
     644           0 :     if ( ismeta )
     645           0 :         newpos = SFTextAreaSelBackword(st->li.text,pos);
     646             :     else
     647           0 :         newpos = pos-1;
     648           0 :     if ( newpos==-1 ) newpos = 0;
     649           0 : return( newpos );
     650             : }
     651             : 
     652           0 : static int GTForePos(SFTextArea *st,int pos, int ismeta) {
     653           0 :     int newpos=pos/*, xloc, l*/;
     654             : 
     655           0 :     if ( ismeta )
     656           0 :         newpos = SFTextAreaSelForeword(st->li.text,pos);
     657             :     else {
     658           0 :         if ( st->li.text[pos]!=0 )
     659           0 :             newpos = pos+1;
     660             :     }
     661           0 : return( newpos );
     662             : }
     663             : 
     664           0 : static void SFTextAreaImport(SFTextArea *st) {
     665           0 :     char *cret = gwwv_open_filename(_("Open"),NULL,
     666             :             "*.txt",NULL);
     667             :     unichar_t *str;
     668             : 
     669           0 :     if ( cret==NULL )
     670           0 : return;
     671           0 :     str = _GGadgetFileToUString(cret,65536);
     672           0 :     if ( str==NULL ) {
     673           0 :         ff_post_error(_("Could not open"),_("Could not open %.100s"),cret);
     674           0 :         free(cret);
     675           0 : return;
     676             :     }
     677           0 :     free(cret);
     678           0 :     SFTextArea_Replace(st,str);
     679           0 :     free(str);
     680             : }
     681             : 
     682           0 : static void SFTextAreaInsertRandom(SFTextArea *st) {
     683           0 :     LayoutInfo *li = &st->li;
     684             :     struct fontlist *fl, *prev;
     685             :     char **scriptlangs;
     686             :     int i,cnt;
     687             :     uint32 script, lang;
     688             :     char *utf8_str;
     689             :     unichar_t *str;
     690             :     int start, pos;
     691             :     struct lang_frequencies **freq;
     692             : 
     693           0 :     for ( fl=li->fontlist, prev = NULL; fl!=NULL && fl->start<=st->sel_start ; prev=fl, fl=fl->next );
     694           0 :     if ( prev==NULL )
     695           0 : return;
     696           0 :     scriptlangs = SFScriptLangs(prev->fd->sf,&freq);
     697           0 :     if ( scriptlangs==NULL || scriptlangs[0]==NULL ) {
     698           0 :         ff_post_error(_("No letters in font"), _("No letters in font"));
     699           0 :         free(scriptlangs);
     700           0 :         free(freq);
     701           0 : return;
     702             :     }
     703           0 :     for ( cnt=0; scriptlangs[cnt]!=NULL; ++cnt );
     704           0 :     i = ff_choose(_("Text from script"),(const char **) scriptlangs,cnt,0,_("Insert random text in the specified script"));
     705           0 :     if ( i==-1 )
     706           0 : return;
     707           0 :     pos = strlen(scriptlangs[i])-10;
     708           0 :     script = (scriptlangs[i][pos+0]<<24) |
     709           0 :              (scriptlangs[i][pos+1]<<16) |
     710           0 :              (scriptlangs[i][pos+2]<<8 ) |
     711           0 :              (scriptlangs[i][pos+3]    );
     712           0 :     lang = (scriptlangs[i][pos+5]<<24) |
     713           0 :            (scriptlangs[i][pos+6]<<16) |
     714           0 :            (scriptlangs[i][pos+7]<<8 ) |
     715           0 :            (scriptlangs[i][pos+8]    );
     716             : 
     717           0 :     utf8_str = RandomParaFromScriptLang(script,lang,prev->fd->sf,freq[i]);
     718           0 :     str = utf82u_copy(utf8_str);
     719             : 
     720           0 :     start = st->sel_start;
     721           0 :     SFTextArea_Replace(st,str);
     722           0 :     SFTFSetScriptLang(&st->g,start,start+u_strlen(str),script,lang);
     723             : 
     724           0 :     free(str);
     725           0 :     free(utf8_str);
     726           0 :     for ( i=0; scriptlangs[i]!=NULL; ++i )
     727           0 :         free(scriptlangs[i]);
     728           0 :     free(scriptlangs);
     729           0 :     free(freq);
     730             : }
     731             : 
     732           0 : static void SFTextAreaSave(SFTextArea *st) {
     733           0 :     char *cret = gwwv_save_filename(_("Save"),NULL, "*.txt");
     734             :     FILE *file;
     735             :     unichar_t *pt;
     736             : 
     737           0 :     if ( cret==NULL )
     738           0 : return;
     739           0 :     file = fopen(cret,"w");
     740           0 :     if ( file==NULL ) {
     741           0 :         ff_post_error(_("Could not open"),_("Could not open %.100s"),cret);
     742           0 :         free(cret);
     743           0 : return;
     744             :     }
     745           0 :     free(cret);
     746             : 
     747           0 :         putc(0xef,file);                /* Zero width something or other. Marks this as unicode, utf8 */
     748           0 :         putc(0xbb,file);
     749           0 :         putc(0xbf,file);
     750           0 :         for ( pt = st->li.text ; *pt; ++pt ) {
     751           0 :             if ( *pt<0x80 )
     752           0 :                 putc(*pt,file);
     753           0 :             else if ( *pt<0x800 ) {
     754           0 :                 putc(0xc0 | (*pt>>6), file);
     755           0 :                 putc(0x80 | (*pt&0x3f), file);
     756           0 :             } else if ( *pt>=0xd800 && *pt<0xdc00 && pt[1]>=0xdc00 && pt[1]<0xe000 ) {
     757           0 :                 int u = ((*pt>>6)&0xf)+1, y = ((*pt&3)<<4) | ((pt[1]>>6)&0xf);
     758           0 :                 putc( 0xf0 | (u>>2),file );
     759           0 :                 putc( 0x80 | ((u&3)<<4) | ((*pt>>2)&0xf),file );
     760           0 :                 putc( 0x80 | y,file );
     761           0 :                 putc( 0x80 | (pt[1]&0x3f),file );
     762             :             } else {
     763           0 :                 putc( 0xe0 | (*pt>>12),file );
     764           0 :                 putc( 0x80 | ((*pt>>6)&0x3f),file );
     765           0 :                 putc( 0x80 | (*pt&0x3f),file );
     766             :             }
     767             :         }
     768           0 :     fclose(file);
     769             : }
     770             : 
     771           0 : static void SFTextAreaSaveImage(SFTextArea *st) {
     772             :     char *cret;
     773             :     GImage *image;
     774             :     struct _GImage *base;
     775             :     char *basename;
     776             :     int i,ret, p, x, j;
     777             :     struct opentype_str **line;
     778             :     int as;
     779             : 
     780           0 :     if ( st->li.lcnt==0 )
     781           0 : return;
     782             : 
     783           0 :     basename = NULL;
     784           0 :     if ( st->li.fontlist!=NULL ) {
     785           0 :         basename = malloc(strlen(st->li.fontlist->fd->sf->fontname)+8);
     786           0 :         strcpy(basename, st->li.fontlist->fd->sf->fontname);
     787             : #ifdef _NO_LIBPNG
     788             :         strcat(basename,".bmp");
     789             : #else
     790           0 :         strcat(basename,".png");
     791             : #endif
     792             :     }
     793             : #ifdef _NO_LIBPNG
     794             :     cret = gwwv_save_filename(_("Save Image"),basename, "*.bmp");
     795             : #else
     796           0 :     cret = gwwv_save_filename(_("Save Image"),basename, "*.{bmp,png}");
     797             : #endif
     798           0 :     free(basename);
     799           0 :     if ( cret==NULL )
     800           0 : return;
     801             : 
     802           0 :     image = GImageCreate(it_index,st->g.inner.width+2,
     803           0 :             st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh+2);
     804           0 :     base = image->u.image;
     805           0 :     memset(base->data,0,base->bytes_per_line*base->height);
     806           0 :     for ( i=0; i<256; ++i )
     807           0 :         base->clut->clut[i] = (255-i)*0x010101;
     808           0 :     base->clut->is_grey = true;
     809           0 :     base->clut->clut_len = 256;
     810             : 
     811           0 :     as = 0;
     812           0 :     if ( st->li.lcnt!=0 )
     813           0 :         as = st->li.lineheights[0].as;
     814             : 
     815           0 :     for ( i=0; i<st->li.lcnt; ++i ) {
     816             :         /* Does this para start out r2l or l2r? */
     817           0 :         p = st->li.lineheights[i].p;
     818           0 :         if ( st->li.paras[p].para[0]!=NULL &&
     819           0 :                 ScriptIsRightToLeft( ((struct fontlist *) (st->li.paras[p].para[0]->fl))->script ))
     820           0 :             x = st->li.xmax - st->li.lineheights[i].linelen;
     821             :         else
     822           0 :             x = 0;
     823           0 :         line = st->li.lines[i];
     824           0 :         for ( j=0; line[j]!=NULL; ++j ) {
     825           0 :             LI_FDDrawChar(image,
     826             :                     (void (*)(void *,GImage *,GRect *,int, int)) GImageDrawImage,
     827             :                     (void (*)(void *,GRect *,Color)) GImageDrawRect,
     828           0 :                     line[j],x,st->li.lineheights[i].y+as,0x000000);
     829           0 :             x += line[j]->advance_width + line[j]->vr.h_adv_off;
     830             :         }
     831             :     }
     832             : #ifndef _NO_LIBPNG
     833           0 :     if ( strstrmatch(cret,".png")!=NULL )
     834           0 :         ret = GImageWritePng(image,cret,false);
     835             :     else
     836             : #endif
     837           0 :     if ( strstrmatch(cret,".bmp")!=NULL )
     838           0 :         ret = GImageWriteBmp(image,cret);
     839             :     else
     840           0 :         ff_post_error(_("Unsupported image format"),
     841             : #ifndef _NO_LIBPNG
     842           0 :                 _("Unsupported image format must be bmp or png")
     843             : #else
     844             :                 _("Unsupported image format must be bmp")
     845             : #endif
     846             :             );
     847           0 :     if ( !ret )
     848           0 :         ff_post_error(_("Could not write"),_("Could not write %.100s"),cret);
     849           0 :     free( cret );
     850           0 :     GImageDestroy(image);
     851             : }
     852             : 
     853             : #define MID_Cut         1
     854             : #define MID_Copy        2
     855             : #define MID_Paste       3
     856             : 
     857             : #define MID_SelectAll   4
     858             : 
     859             : #define MID_Save        5
     860             : #define MID_Import      6
     861             : #define MID_Insert      7
     862             : 
     863             : #define MID_Undo        8
     864             : 
     865             : #define MID_SaveImage   9
     866             : 
     867             : static SFTextArea *popup_kludge;
     868             : 
     869           0 : static void SFTFPopupInvoked(GWindow v, GMenuItem *mi,GEvent *e) {
     870             :     SFTextArea *st;
     871           0 :     if ( popup_kludge==NULL )
     872           0 : return;
     873           0 :     st = popup_kludge;
     874           0 :     popup_kludge = NULL;
     875           0 :     switch ( mi->mid ) {
     876             :       case MID_Undo:
     877           0 :         sftextarea_editcmd(&st->g,ec_undo);
     878           0 :       break;
     879             :       case MID_Cut:
     880           0 :         sftextarea_editcmd(&st->g,ec_cut);
     881           0 :       break;
     882             :       case MID_Copy:
     883           0 :         sftextarea_editcmd(&st->g,ec_copy);
     884           0 :       break;
     885             :       case MID_Paste:
     886           0 :         sftextarea_editcmd(&st->g,ec_paste);
     887           0 :       break;
     888             :       case MID_SelectAll:
     889           0 :         sftextarea_editcmd(&st->g,ec_selectall);
     890           0 :       break;
     891             :       case MID_Save:
     892           0 :         SFTextAreaSave(st);
     893           0 :       break;
     894             :       case MID_Import:
     895           0 :         SFTextAreaImport(st);
     896           0 :       break;
     897             :       case MID_Insert:
     898           0 :         SFTextAreaInsertRandom(st);
     899           0 :       break;
     900             :       case MID_SaveImage:
     901           0 :         SFTextAreaSaveImage(st);
     902           0 :       break;
     903             :     }
     904             : }
     905             : 
     906             : static GMenuItem sftf_popuplist[] = {
     907             :     { { (unichar_t *) N_("_Undo"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'U' }, 'Z', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Undo },
     908             :     { { 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 */
     909             :     { { (unichar_t *) N_("Cu_t"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 't' }, 'X', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Cut },
     910             :     { { (unichar_t *) N_("_Copy"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'C' }, 'C', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Copy },
     911             :     { { (unichar_t *) N_("_Paste"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'P' }, 'V', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Paste },
     912             :     { { 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 */
     913             :     { { (unichar_t *) N_("_Save As..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'S' }, 'S', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Save },
     914             :     { { (unichar_t *) N_("_Import..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'I' }, 'I', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Import },
     915             :     { { (unichar_t *) N_("_Insert Random Text..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'I' }, 'T', ksm_control, NULL, NULL, SFTFPopupInvoked, MID_Insert },
     916             :     { { 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 */
     917             :     { { (unichar_t *) N_("Save As _Image..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'S' }, 'S', ksm_control|ksm_shift, NULL, NULL, SFTFPopupInvoked, MID_SaveImage },
     918             :     GMENUITEM_EMPTY
     919             : };
     920             : 
     921           0 : void SFTFPopupMenu(SFTextArea *st, GEvent *event) {
     922           0 :     int no_sel = st->sel_start==st->sel_end;
     923             :     static int done = false;
     924             : 
     925           0 :     if ( !done ) {
     926             :         int i;
     927           0 :         for ( i=0; sftf_popuplist[i].ti.text!=NULL || sftf_popuplist[i].ti.line; ++i )
     928           0 :             if ( sftf_popuplist[i].ti.text!=NULL )
     929           0 :                 sftf_popuplist[i].ti.text = (unichar_t *) _( (char *) sftf_popuplist[i].ti.text);
     930           0 :         done = true;
     931             :     }
     932             : 
     933           0 :     sftf_popuplist[0].ti.disabled = st->li.oldtext==NULL;    /* Undo */
     934           0 :     sftf_popuplist[2].ti.disabled = no_sel;             /* Cut */
     935           0 :     sftf_popuplist[3].ti.disabled = no_sel;             /* Copy */
     936           0 :     sftf_popuplist[4].ti.disabled = !GDrawSelectionHasType(st->g.base,sn_clipboard,"text/plain;charset=ISO-10646-UCS-2") &&
     937           0 :             !GDrawSelectionHasType(st->g.base,sn_clipboard,"UTF8_STRING") &&
     938           0 :             !GDrawSelectionHasType(st->g.base,sn_clipboard,"STRING");
     939           0 :     sftf_popuplist[9].ti.disabled = (st->li.lcnt<=0);
     940           0 :     popup_kludge = st;
     941           0 :     GMenuCreatePopupMenu(st->g.base,event, sftf_popuplist);
     942           0 : }
     943             : 
     944           0 : static int SFTextAreaDoChange(SFTextArea *st, GEvent *event) {
     945           0 :     int ss = st->sel_start, se = st->sel_end;
     946             :     int pos, l, xpos;
     947             :     unichar_t *upt;
     948             : 
     949           0 :     if ( ( event->u.chr.state&(ksm_control|ksm_meta)) ||
     950           0 :             event->u.chr.chars[0]<' ' || event->u.chr.chars[0]==0x7f ) {
     951           0 :         switch ( event->u.chr.keysym ) {
     952             :           case GK_BackSpace:
     953           0 :             if ( st->sel_start==st->sel_end ) {
     954           0 :                 if ( st->sel_start==0 )
     955           0 : return( 2 );
     956           0 :                 --st->sel_start;
     957             :             }
     958           0 :             SFTextArea_Replace(st,nullstr);
     959           0 : return( true );
     960             :           break;
     961             :           case GK_Delete:
     962           0 :             if ( st->sel_start==st->sel_end ) {
     963           0 :                 if ( st->li.text[st->sel_start]==0 )
     964           0 : return( 2 );
     965           0 :                 ++st->sel_end;
     966             :             }
     967           0 :             SFTextArea_Replace(st,nullstr);
     968           0 : return( true );
     969             :           break;
     970             :           case GK_Left: case GK_KP_Left:
     971           0 :             if ( st->sel_start==st->sel_end ) {
     972           0 :                 st->sel_start = GTBackPos(st,st->sel_start,event->u.chr.state&ksm_meta);
     973           0 :                 if ( !(event->u.chr.state&ksm_shift ))
     974           0 :                     st->sel_end = st->sel_start;
     975           0 :             } else if ( event->u.chr.state&ksm_shift ) {
     976           0 :                 if ( st->sel_end==st->sel_base ) {
     977           0 :                     st->sel_start = GTBackPos(st,st->sel_start,event->u.chr.state&ksm_meta);
     978             :                 } else {
     979           0 :                     st->sel_end = GTBackPos(st,st->sel_end,event->u.chr.state&ksm_meta);
     980             :                 }
     981             :             } else {
     982           0 :                 st->sel_end = st->sel_base = st->sel_start;
     983             :             }
     984           0 :             SFTextArea_Show(st,st->sel_start);
     985           0 : return( 2 );
     986             :           break;
     987             :           case GK_Right: case GK_KP_Right:
     988           0 :             if ( st->sel_start==st->sel_end ) {
     989           0 :                 st->sel_end = GTForePos(st,st->sel_start,event->u.chr.state&ksm_meta);
     990           0 :                 if ( !(event->u.chr.state&ksm_shift ))
     991           0 :                     st->sel_start = st->sel_end;
     992           0 :             } else if ( event->u.chr.state&ksm_shift ) {
     993           0 :                 if ( st->sel_end==st->sel_base ) {
     994           0 :                     st->sel_start = GTForePos(st,st->sel_start,event->u.chr.state&ksm_meta);
     995             :                 } else {
     996           0 :                     st->sel_end = GTForePos(st,st->sel_end,event->u.chr.state&ksm_meta);
     997             :                 }
     998             :             } else {
     999           0 :                 st->sel_start = st->sel_base = st->sel_end;
    1000             :             }
    1001           0 :             SFTextArea_Show(st,st->sel_start);
    1002           0 : return( 2 );
    1003             :           break;
    1004             :           case GK_Up: case GK_KP_Up:
    1005           0 :             if ( !st->multi_line )
    1006           0 :           break;
    1007           0 :             if ( !( event->u.chr.state&ksm_shift ) && st->sel_start!=st->sel_end )
    1008           0 :                 st->sel_end = st->sel_base = st->sel_start;
    1009             :             else {
    1010           0 :                 pos = st->sel_start;
    1011           0 :                 if ( ( event->u.chr.state&ksm_shift ) && st->sel_start==st->sel_base )
    1012           0 :                     pos = st->sel_end;
    1013           0 :                 l = SFTextAreaFindLine(st,st->sel_start);
    1014           0 :                 xpos = SFTextAreaGetXPosFromOffset(st,l,st->sel_start);
    1015           0 :                 if ( l!=0 )
    1016           0 :                     pos = SFTextAreaGetOffsetFromXPos(st,l-1,xpos);
    1017           0 :                 if ( event->u.chr.state&ksm_shift ) {
    1018           0 :                     if ( pos<st->sel_base ) {
    1019           0 :                         st->sel_start = pos;
    1020           0 :                         st->sel_end = st->sel_base;
    1021             :                     } else {
    1022           0 :                         st->sel_start = st->sel_base;
    1023           0 :                         st->sel_end = pos;
    1024             :                     }
    1025             :                 } else {
    1026           0 :                     st->sel_start = st->sel_end = st->sel_base = pos;
    1027             :                 }
    1028             :             }
    1029           0 :             SFTextArea_Show(st,st->sel_start);
    1030           0 : return( 2 );
    1031             :           break;
    1032             :           case GK_Down: case GK_KP_Down:
    1033           0 :             if ( !st->multi_line )
    1034           0 :           break;
    1035           0 :             if ( !( event->u.chr.state&ksm_shift ) && st->sel_start!=st->sel_end )
    1036           0 :                 st->sel_end = st->sel_base = st->sel_end;
    1037             :             else {
    1038           0 :                 pos = st->sel_start;
    1039           0 :                 if ( ( event->u.chr.state&ksm_shift ) && st->sel_start==st->sel_base )
    1040           0 :                     pos = st->sel_end;
    1041           0 :                 l = SFTextAreaFindLine(st,st->sel_start);
    1042           0 :                 xpos = SFTextAreaGetXPosFromOffset(st,l,st->sel_start);
    1043           0 :                 if ( l<st->li.lcnt-1 )
    1044           0 :                     pos = SFTextAreaGetOffsetFromXPos(st,l+1,xpos);
    1045           0 :                 if ( event->u.chr.state&ksm_shift ) {
    1046           0 :                     if ( pos<st->sel_base ) {
    1047           0 :                         st->sel_start = pos;
    1048           0 :                         st->sel_end = st->sel_base;
    1049             :                     } else {
    1050           0 :                         st->sel_start = st->sel_base;
    1051           0 :                         st->sel_end = pos;
    1052             :                     }
    1053             :                 } else {
    1054           0 :                     st->sel_start = st->sel_end = st->sel_base = pos;
    1055             :                 }
    1056             :             }
    1057           0 :             SFTextArea_Show(st,st->sel_start);
    1058           0 : return( 2 );
    1059             :           break;
    1060             :           case GK_Home: case GK_Begin: case GK_KP_Home: case GK_KP_Begin:
    1061           0 :             if ( !(event->u.chr.state&ksm_shift) ) {
    1062           0 :                 st->sel_start = st->sel_base = st->sel_end = 0;
    1063             :             } else {
    1064           0 :                 st->sel_start = 0; st->sel_end = st->sel_base;
    1065             :             }
    1066           0 :             SFTextArea_Show(st,st->sel_start);
    1067           0 : return( 2 );
    1068             :           break;
    1069             :           /* Move to eol. (if already at eol, move to next eol) */
    1070             :           case 'E': case 'e':
    1071           0 :             if ( !( event->u.chr.state&ksm_control ) )
    1072           0 : return( false );
    1073           0 :             upt = st->li.text+st->sel_base;
    1074           0 :             if ( *upt=='\n' )
    1075           0 :                 ++upt;
    1076           0 :             upt = u_strchr(upt,'\n');
    1077           0 :             if ( upt==NULL ) upt=st->li.text+u_strlen(st->li.text);
    1078           0 :             if ( !(event->u.chr.state&ksm_shift) ) {
    1079           0 :                 st->sel_start = st->sel_base = st->sel_end =upt-st->li.text;
    1080             :             } else {
    1081           0 :                 st->sel_start = st->sel_base; st->sel_end = upt-st->li.text;
    1082             :             }
    1083           0 :             SFTextArea_Show(st,st->sel_start);
    1084           0 : return( 2 );
    1085             :           break;
    1086             :           case GK_End: case GK_KP_End:
    1087           0 :             if ( !(event->u.chr.state&ksm_shift) ) {
    1088           0 :                 st->sel_start = st->sel_base = st->sel_end = u_strlen(st->li.text);
    1089             :             } else {
    1090           0 :                 st->sel_start = st->sel_base; st->sel_end = u_strlen(st->li.text);
    1091             :             }
    1092           0 :             SFTextArea_Show(st,st->sel_start);
    1093           0 : return( 2 );
    1094             :           break;
    1095             :           case 'A': case 'a':
    1096           0 :             if ( event->u.chr.state&ksm_control ) {      /* Select All */
    1097           0 :                 sftextarea_editcmd(&st->g,ec_selectall);
    1098           0 : return( 2 );
    1099             :             }
    1100           0 :           break;
    1101             :           case 'C': case 'c':
    1102           0 :             if ( event->u.chr.state&ksm_control ) {      /* Copy */
    1103           0 :                 sftextarea_editcmd(&st->g,ec_copy);
    1104             :             }
    1105           0 :           break;
    1106             :           case 'V': case 'v':
    1107           0 :             if ( event->u.chr.state&ksm_control ) {      /* Paste */
    1108           0 :                 sftextarea_editcmd(&st->g,ec_paste);
    1109           0 :                 SFTextArea_Show(st,st->sel_start);
    1110           0 : return( true );
    1111             :             }
    1112           0 :           break;
    1113             :           case 'X': case 'x':
    1114           0 :             if ( event->u.chr.state&ksm_control ) {      /* Cut */
    1115           0 :                 sftextarea_editcmd(&st->g,ec_cut);
    1116           0 :                 SFTextArea_Show(st,st->sel_start);
    1117           0 : return( true );
    1118             :             }
    1119           0 :           break;
    1120             :           case 'Z': case 'z':                           /* Undo */
    1121           0 :             if ( event->u.chr.state&ksm_control ) {
    1122           0 :                 sftextarea_editcmd(&st->g,ec_undo);
    1123           0 :                 SFTextArea_Show(st,st->sel_start);
    1124           0 : return( true );
    1125             :             }
    1126           0 :           break;
    1127             :           case 'D': case 'd':
    1128           0 :             if ( event->u.chr.state&ksm_control ) {      /* delete word */
    1129           0 :                 sftextarea_editcmd(&st->g,ec_deleteword);
    1130           0 :                 SFTextArea_Show(st,st->sel_start);
    1131           0 : return( true );
    1132             :             }
    1133           0 :           break;
    1134             :           case 'W': case 'w':
    1135           0 :             if ( event->u.chr.state&ksm_control ) {      /* backword */
    1136           0 :                 sftextarea_editcmd(&st->g,ec_backword);
    1137           0 :                 SFTextArea_Show(st,st->sel_start);
    1138           0 : return( true );
    1139             :             }
    1140           0 :           break;
    1141             :           case 'M': case 'm': case 'J': case 'j':
    1142           0 :             if ( !( event->u.chr.state&ksm_control ) )
    1143           0 : return( false );
    1144             :             /* fall through into return case */
    1145             :           case GK_Return: case GK_Linefeed:
    1146           0 :             if ( st->accepts_returns ) {
    1147           0 :                 SFTextArea_Replace(st,newlinestr);
    1148           0 : return( true );
    1149             :             }
    1150           0 :           break;
    1151             :           case GK_Tab:
    1152           0 :             if ( st->accepts_tabs ) {
    1153           0 :                 SFTextArea_Replace(st,tabstr);
    1154           0 : return( true );
    1155             :             }
    1156           0 :           break;
    1157             :           case 's': case 'S':
    1158           0 :             if ( !( event->u.chr.state&ksm_control ) )
    1159           0 : return( false );
    1160           0 :             SFTextAreaSave(st);
    1161           0 : return( 2 );
    1162             :           break;
    1163             :           case 'I': case 'i':
    1164           0 :             if ( !( event->u.chr.state&ksm_control ) )
    1165           0 : return( false );
    1166           0 :             SFTextAreaImport(st);
    1167           0 : return( true );
    1168             :         }
    1169           0 :     } else {
    1170           0 :         SFTextArea_Replace(st,event->u.chr.chars);
    1171           0 : return( true );
    1172             :     }
    1173             : 
    1174           0 :     if ( st->sel_start == st->sel_end )
    1175           0 :         st->sel_base = st->sel_start;
    1176           0 :     if ( ss!=st->sel_start || se!=st->sel_end )
    1177           0 :         SFTextAreaGrabPrimarySelection(st);
    1178           0 : return( false );
    1179             : }
    1180             : 
    1181           0 : static void gt_cursor_pos(SFTextArea *st, int *x, int *y, int *fh) {
    1182             :     int l, ty;
    1183             : 
    1184           0 :     *x = 0; *y= 0; *fh = 20;
    1185           0 :     if ( st->li.fontlist!=NULL )
    1186           0 :         *fh = st->li.fontlist->fd->pointsize*st->li.dpi/72;
    1187           0 :     l = SFTextAreaFindLine(st,st->sel_start);
    1188           0 :     if ( l<0 || l>=st->li.lcnt )
    1189           0 : return;
    1190           0 :     ty = st->li.lineheights[l].y - st->li.lineheights[st->loff_top].y;
    1191           0 :     if ( ty<0 || ty>st->g.inner.height ) {
    1192           0 :         *x = *y = -1;
    1193           0 : return;
    1194             :     }
    1195           0 :     *y = ty;
    1196           0 :     *fh = st->li.lineheights[l].fh;
    1197           0 :     *x = SFTextAreaGetXPosFromOffset(st,l,st->sel_start);
    1198             : }
    1199             : 
    1200           0 : static void GTPositionGIC(SFTextArea *st) {
    1201             :     int x,y,fh;
    1202             : 
    1203           0 :     if ( !st->g.has_focus || st->gic==NULL )
    1204           0 : return;
    1205           0 :     gt_cursor_pos(st,&x,&y,&fh);
    1206           0 :     if ( x<0 )
    1207           0 : return;
    1208           0 :     GDrawSetGIC(st->g.base,st->gic,st->g.inner.x+x,st->g.inner.y+y+st->as);
    1209             : }
    1210             : 
    1211           0 : static void gt_draw_cursor(GWindow pixmap, SFTextArea *st) {
    1212             :     GRect old;
    1213             :     int x, y, fh;
    1214             : 
    1215           0 :     if ( !st->cursor_on || st->sel_start != st->sel_end )
    1216           0 : return;
    1217           0 :     gt_cursor_pos(st,&x,&y,&fh);
    1218             : 
    1219           0 :     if ( x<0 || x>=st->g.inner.width )
    1220           0 : return;
    1221           0 :     GDrawPushClip(pixmap,&st->g.inner,&old);
    1222           0 :     GDrawSetXORMode(pixmap);
    1223           0 :     GDrawSetXORBase(pixmap,st->g.box->main_background!=COLOR_DEFAULT?st->g.box->main_background:
    1224           0 :             GDrawGetDefaultBackground(GDrawGetDisplayOfWindow(pixmap)) );
    1225           0 :     GDrawSetFont(pixmap,st->font);
    1226           0 :     GDrawSetLineWidth(pixmap,0);
    1227           0 :     GDrawDrawLine(pixmap,st->g.inner.x+x,st->g.inner.y+y,
    1228           0 :             st->g.inner.x+x,st->g.inner.y+y+fh,
    1229           0 :             st->g.box->main_foreground!=COLOR_DEFAULT?st->g.box->main_foreground:
    1230           0 :             GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)) );
    1231           0 :     GDrawSetCopyMode(pixmap);
    1232           0 :     GDrawPopClip(pixmap,&old);
    1233             : }
    1234             : 
    1235           0 : static void SFTextAreaDrawDDCursor(SFTextArea *st, int pos) {
    1236             :     GRect old;
    1237             :     int x, y, l;
    1238             : 
    1239           0 :     l = SFTextAreaFindLine(st,pos);
    1240           0 :     y = st->li.lineheights[l].y - st->li.lineheights[st->loff_top].y;
    1241           0 :     if ( y<0 || y>st->g.inner.height )
    1242           0 : return;
    1243           0 :     x = SFTextAreaGetXPosFromOffset(st,l,pos);
    1244           0 :     if ( x<0 || x>=st->g.inner.width )
    1245           0 : return;
    1246             : 
    1247           0 :     GDrawPushClip(st->g.base,&st->g.inner,&old);
    1248           0 :     GDrawSetXORMode(st->g.base);
    1249           0 :     GDrawSetXORBase(st->g.base,st->g.box->main_background!=COLOR_DEFAULT?st->g.box->main_background:
    1250           0 :             GDrawGetDefaultBackground(GDrawGetDisplayOfWindow(st->g.base)) );
    1251           0 :     GDrawSetFont(st->g.base,st->font);
    1252           0 :     GDrawSetLineWidth(st->g.base,0);
    1253           0 :     GDrawSetDashedLine(st->g.base,2,2,0);
    1254           0 :     GDrawDrawLine(st->g.base,st->g.inner.x+x,st->g.inner.y+y,
    1255           0 :             st->g.inner.x+x,st->g.inner.y+y+st->li.lineheights[l].fh,
    1256           0 :             st->g.box->main_foreground!=COLOR_DEFAULT?st->g.box->main_foreground:
    1257           0 :             GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(st->g.base)) );
    1258           0 :     GDrawSetCopyMode(st->g.base);
    1259           0 :     GDrawPopClip(st->g.base,&old);
    1260           0 :     GDrawSetDashedLine(st->g.base,0,0,0);
    1261           0 :     st->has_dd_cursor = !st->has_dd_cursor;
    1262           0 :     st->dd_cursor_pos = pos;
    1263             : }
    1264             : 
    1265           0 : static int sftextarea_expose(GWindow pixmap, GGadget *g, GEvent *event) {
    1266           0 :     SFTextArea *st = (SFTextArea *) g;
    1267           0 :     GRect old1, old2, *r = &g->r, selr;
    1268             :     Color fg,sel;
    1269             :     int y,x,p,i,dotext,j,xend;
    1270             :     struct opentype_str **line;
    1271             : 
    1272           0 :     if ( g->state == gs_invisible || st->dontdraw )
    1273           0 : return( false );
    1274             : 
    1275           0 :     GDrawPushClip(pixmap,r,&old1);
    1276             : 
    1277           0 :     GBoxDrawBackground(pixmap,r,g->box,
    1278           0 :             g->state==gs_enabled? gs_pressedactive: g->state,false);
    1279           0 :     GBoxDrawBorder(pixmap,r,g->box,g->state,false);
    1280             : 
    1281           0 :     GDrawPushClip(pixmap,&g->inner,&old2);
    1282           0 :     GDrawSetFont(pixmap,st->font);
    1283           0 :     GDrawSetDither(NULL, false);        /* on 8 bit displays we don't want any dithering */
    1284           0 :     GDrawSetLineWidth(pixmap,0);
    1285             : 
    1286           0 :     fg = g->state==gs_disabled?g->box->disabled_foreground:
    1287           0 :                     g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
    1288           0 :                     g->box->main_foreground;
    1289           0 :     sel = g->box->active_border;
    1290           0 :     for ( i=st->loff_top; i<st->li.lcnt; ++i ) {
    1291             :         int selstartx, selendx;
    1292             :         /* First draw the selection, then draw the text */
    1293           0 :         y = g->inner.y+ st->li.lineheights[i].y-st->li.lineheights[st->loff_top].y+
    1294           0 :                 st->li.lineheights[i].as;
    1295           0 :         if ( y>g->inner.y+g->inner.height || y>event->u.expose.rect.y+event->u.expose.rect.height )
    1296             :     break;
    1297           0 :         if ( y+st->li.lineheights[i].fh<=event->u.expose.rect.y )
    1298           0 :     continue;
    1299           0 :         selstartx = selendx = -1;
    1300           0 :         for ( dotext=0; dotext<2; ++dotext ) {
    1301             :             /* Does this para start out r2l or l2r? */
    1302           0 :             p = st->li.lineheights[i].p;
    1303           0 :             if ( st->li.paras[p].para[0]!=NULL &&
    1304           0 :                     st->li.paras[p].para[0]->fl!=NULL &&
    1305           0 :                     ScriptIsRightToLeft( ((struct fontlist *) (st->li.paras[p].para[0]->fl))->script ))
    1306           0 :                 x = st->li.xmax - st->li.lineheights[i].linelen;
    1307             :             else
    1308           0 :                 x = 0;
    1309           0 :             line = st->li.lines[i];
    1310           0 :             for ( j=0; line[j]!=NULL; ++j ) {
    1311           0 :                 xend = x + line[j]->advance_width + line[j]->vr.h_adv_off;
    1312           0 :                 if ( dotext ) {
    1313           0 :                     LI_FDDrawChar(pixmap,
    1314             :                             (void (*)(void *,GImage *,GRect *,int, int)) GDrawDrawGlyph,
    1315             :                             (void (*)(void *,GRect *,Color)) GDrawDrawRect,
    1316           0 :                             line[j],g->inner.x+x-st->xoff_left,y,fg);
    1317             :                 } else {
    1318           0 :                     int pos = line[j]->orig_index +
    1319           0 :                             ((struct fontlist *) (line[j]->fl))->start;
    1320           0 :                     if ( pos>=st->sel_start && pos<st->sel_end ) {
    1321           0 :                         if ( selstartx==-1 )
    1322           0 :                             selstartx = x;
    1323           0 :                         selendx = xend;
    1324             :                     }
    1325           0 :                     if ( !(pos>=st->sel_start && pos<st->sel_end) || line[j+1]==NULL ) {
    1326           0 :                         if ( selstartx!=-1 ) {
    1327           0 :                             selr.x = selstartx+g->inner.x-st->xoff_left;
    1328           0 :                             selr.width = selendx-selstartx;
    1329           0 :                             selr.y = y-st->li.lineheights[i].as;
    1330           0 :                             selr.height = st->li.lineheights[i].fh;
    1331           0 :                             GDrawFillRect(pixmap,&selr,sel);
    1332           0 :                             selstartx = selendx = -1;
    1333             :                         }
    1334             :                     }
    1335             :                 }
    1336           0 :                 x = xend;
    1337             :             }
    1338             :         }
    1339             :     }
    1340             : 
    1341           0 :     GDrawSetDither(NULL, true);
    1342           0 :     GDrawPopClip(pixmap,&old2);
    1343           0 :     GDrawPopClip(pixmap,&old1);
    1344           0 :     gt_draw_cursor(pixmap, st);
    1345           0 : return( true );
    1346             : }
    1347             : 
    1348           0 : static int SFTextAreaDoDrop(SFTextArea *st,GEvent *event,int endpos) {
    1349             : 
    1350           0 :     if ( st->has_dd_cursor )
    1351           0 :         SFTextAreaDrawDDCursor(st,st->dd_cursor_pos);
    1352             : 
    1353           0 :     if ( event->type == et_mousemove ) {
    1354           0 :         if ( GGadgetInnerWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
    1355           0 :             if ( endpos<st->sel_start || endpos>=st->sel_end )
    1356           0 :                 SFTextAreaDrawDDCursor(st,endpos);
    1357           0 :         } else if ( !GGadgetWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
    1358           0 :             GDrawPostDragEvent(st->g.base,event,et_drag);
    1359             :         }
    1360             :     } else {
    1361           0 :         if ( GGadgetInnerWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
    1362           0 :             if ( endpos>=st->sel_start && endpos<st->sel_end ) {
    1363           0 :                 st->sel_start = st->sel_end = endpos;
    1364             :             } else {
    1365           0 :                 unichar_t *old=st->li.oldtext, *temp;
    1366           0 :                 int pos=0;
    1367           0 :                 if ( event->u.mouse.state&ksm_control ) {
    1368           0 :                     temp = malloc((u_strlen(st->li.text)+st->sel_end-st->sel_start+1)*sizeof(unichar_t));
    1369           0 :                     memcpy(temp,st->li.text,endpos*sizeof(unichar_t));
    1370           0 :                     memcpy(temp+endpos,st->li.text+st->sel_start,
    1371           0 :                             (st->sel_end-st->sel_start)*sizeof(unichar_t));
    1372           0 :                     u_strcpy(temp+endpos+st->sel_end-st->sel_start,st->li.text+endpos);
    1373           0 :                 } else if ( endpos>=st->sel_end ) {
    1374           0 :                     temp = u_copy(st->li.text);
    1375           0 :                     memcpy(temp+st->sel_start,temp+st->sel_end,
    1376           0 :                             (endpos-st->sel_end)*sizeof(unichar_t));
    1377           0 :                     memcpy(temp+endpos-(st->sel_end-st->sel_start),
    1378           0 :                             st->li.text+st->sel_start,(st->sel_end-st->sel_start)*sizeof(unichar_t));
    1379           0 :                     pos = endpos;
    1380             :                 } else /*if ( endpos<st->sel_start )*/ {
    1381           0 :                     temp = u_copy(st->li.text);
    1382           0 :                     memcpy(temp+endpos,st->li.text+st->sel_start,
    1383           0 :                             (st->sel_end-st->sel_start)*sizeof(unichar_t));
    1384           0 :                     memcpy(temp+endpos+st->sel_end-st->sel_start,st->li.text+endpos,
    1385           0 :                             (st->sel_start-endpos)*sizeof(unichar_t));
    1386           0 :                     pos = endpos+st->sel_end-st->sel_start;
    1387             :                 }
    1388           0 :                 st->li.oldtext = st->li.text;
    1389           0 :                 st->sel_oldstart = st->sel_start;
    1390           0 :                 st->sel_oldend = st->sel_end;
    1391           0 :                 st->sel_oldbase = st->sel_base;
    1392           0 :                 st->sel_start = st->sel_end = pos;
    1393           0 :                 st->li.text = temp;
    1394           0 :                 free(old);
    1395           0 :                 SFTextAreaRefigureLines(st, endpos<st->sel_oldstart?endpos:st->sel_oldstart,-1);
    1396             :             }
    1397           0 :         } else if ( !GGadgetWithin(&st->g,event->u.mouse.x,event->u.mouse.y) ) {
    1398             :             /* Don't delete the selection until someone actually accepts the drop */
    1399             :             /* Don't delete at all (copy not move) if control key is down */
    1400           0 :             if ( ( event->u.mouse.state&ksm_control ) )
    1401           0 :                 SFTextAreaGrabSelection(st,sn_drag_and_drop);
    1402             :             else
    1403           0 :                 SFTextAreaGrabDDSelection(st);
    1404           0 :             GDrawPostDragEvent(st->g.base,event,et_drop);
    1405             :         }
    1406           0 :         st->drag_and_drop = false;
    1407           0 :         GDrawSetCursor(st->g.base,st->old_cursor);
    1408           0 :         _ggadget_redraw(&st->g);
    1409             :     }
    1410           0 : return( false );
    1411             : }
    1412             : 
    1413           0 : static void STChangeCheck(SFTextArea *st) {
    1414             :     struct fontlist *fl;
    1415             : 
    1416           0 :     if ( st->changefontcallback==NULL )
    1417           0 : return;
    1418           0 :     for ( fl=st->li.fontlist; fl!=NULL && fl->end<st->sel_end; fl=fl->next );
    1419           0 :     if ( fl!=NULL && fl->next!=NULL && fl->next->end==st->sel_end )
    1420           0 :         fl = fl->next;
    1421           0 :     if ( fl==NULL /* || fl->fd==st->last_fd ||*/ )
    1422           0 : return;
    1423           0 :     (st->changefontcallback)(st->cbcontext,fl->fd->sf,fl->fd->fonttype,
    1424           0 :             fl->fd->pointsize,fl->fd->antialias,fl->script,fl->lang,fl->feats);
    1425             : }
    1426             : 
    1427           0 : static int sftextarea_mouse(GGadget *g, GEvent *event) {
    1428           0 :     SFTextArea *st = (SFTextArea *) g;
    1429           0 :     int end=-1;
    1430           0 :     int i=0;
    1431             : 
    1432           0 :     if ( st->hidden_cursor ) {
    1433           0 :         GDrawSetCursor(st->g.base,st->old_cursor);
    1434           0 :         st->hidden_cursor = false;
    1435           0 :         _GWidget_ClearGrabGadget(g);
    1436             :     }
    1437           0 :     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
    1438           0 : return( false );
    1439           0 :     if ( event->type == et_crossing )
    1440           0 : return( false );
    1441           0 :     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
    1442           0 :             (event->u.mouse.button>=4 && event->u.mouse.button<=7)) {
    1443           0 :         int isv = event->u.mouse.button<=5;
    1444           0 :         if ( event->u.mouse.state&ksm_shift ) isv = !isv;
    1445           0 :         if ( isv && st->vsb!=NULL )
    1446           0 : return( GGadgetDispatchEvent(&st->vsb->g,event));
    1447           0 :         else if ( !isv && st->hsb!=NULL )
    1448           0 : return( GGadgetDispatchEvent(&st->hsb->g,event));
    1449             :         else
    1450           0 : return( true );
    1451             :     }
    1452             : 
    1453           0 :     if ( st->pressed==NULL && event->type == et_mousemove && g->popup_msg!=NULL &&
    1454           0 :             GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y))
    1455           0 :         GGadgetPreparePopup(g->base,g->popup_msg);
    1456             : 
    1457           0 :     if ( event->type == et_mousedown && event->u.mouse.button==3 &&
    1458           0 :             GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
    1459           0 :         SFTFPopupMenu(st,event);
    1460           0 : return( true );
    1461             :     }
    1462             : 
    1463           0 :     if ( event->type == et_mousedown || st->pressed ) {
    1464           0 :         for ( i=st->loff_top; i<st->li.lcnt-1 &&
    1465           0 :                 event->u.mouse.y-g->inner.y>=st->li.lineheights[i+1].y-st->li.lineheights[st->loff_top].y;
    1466           0 :                 ++i );
    1467           0 :         if ( i<0 ) i = 0;
    1468           0 :         if ( !st->multi_line ) i = 0;
    1469           0 :         end = SFTextAreaGetOffsetFromXPos(st,i,event->u.mouse.x - st->g.inner.x - st->xoff_left);
    1470             :     }
    1471             : 
    1472           0 :     if ( event->type == et_mousedown ) {
    1473           0 :         st->wordsel = st->linesel = false;
    1474           0 :         if ( event->u.mouse.button==1 && event->u.mouse.clicks>=3 ) {
    1475           0 :             if ( i<st->li.lcnt )
    1476           0 :                 st->sel_start = st->li.lineheights[i].start_pos;
    1477             :             else
    1478           0 :                 st->sel_start = end;
    1479           0 :             if ( i+1<st->li.lcnt )
    1480           0 :                 st->sel_end = st->li.lineheights[i+1].start_pos;
    1481             :             else
    1482           0 :                 st->sel_end = u_strlen(st->li.text);
    1483           0 :             st->wordsel = false; st->linesel = true;
    1484           0 :         } else if ( event->u.mouse.button==1 && event->u.mouse.clicks==2 ) {
    1485           0 :             st->sel_start = st->sel_end = st->sel_base = end;
    1486           0 :             st->wordsel = true;
    1487           0 :             SFTextAreaSelectWords(st,st->sel_base);
    1488           0 :         } else if ( end>=st->sel_start && end<st->sel_end &&
    1489           0 :                 st->sel_start!=st->sel_end &&
    1490           0 :                 event->u.mouse.button==1 ) {
    1491           0 :             st->drag_and_drop = true;
    1492           0 :             if ( !st->hidden_cursor )
    1493           0 :                 st->old_cursor = GDrawGetCursor(st->g.base);
    1494           0 :             GDrawSetCursor(st->g.base,ct_draganddrop);
    1495           0 :         } else if ( event->u.mouse.button!=3 && !(event->u.mouse.state&ksm_shift) ) {
    1496           0 :             if ( event->u.mouse.button==1 )
    1497           0 :                 SFTextAreaGrabPrimarySelection(st);
    1498           0 :             st->sel_start = st->sel_end = st->sel_base = end;
    1499           0 :         } else if ( end>st->sel_base ) {
    1500           0 :             st->sel_start = st->sel_base;
    1501           0 :             st->sel_end = end;
    1502             :         } else {
    1503           0 :             st->sel_start = end;
    1504           0 :             st->sel_end = st->sel_base;
    1505             :         }
    1506           0 :         if ( st->pressed==NULL )
    1507           0 :             st->pressed = GDrawRequestTimer(st->g.base,200,100,NULL);
    1508           0 :         if ( st->sel_start > u_strlen( st->li.text ))  /* Ok to have selection at end, but beyond is an error */
    1509           0 :             fprintf( stderr, "About to crash\n" );
    1510           0 :         _ggadget_redraw(g);
    1511           0 :         if ( st->changefontcallback )
    1512           0 :             STChangeCheck(st);
    1513           0 : return( true );
    1514           0 :     } else if ( st->pressed && (event->type == et_mousemove || event->type == et_mouseup )) {
    1515           0 :         int refresh = true;
    1516             : 
    1517           0 :         if ( st->drag_and_drop ) {
    1518           0 :             refresh = SFTextAreaDoDrop(st,event,end);
    1519           0 :         } else if ( st->linesel ) {
    1520             :             int basel, l, spos;
    1521           0 :             basel = SFTextAreaFindLine(st,st->sel_base);
    1522           0 :             l = basel<i ? basel : i;
    1523           0 :             if ( l<st->li.lcnt )
    1524           0 :                 spos = st->li.lineheights[l].start_pos;
    1525             :             else
    1526           0 :                 spos = basel<i ? st->sel_base : end;
    1527           0 :             st->sel_start = spos;
    1528           0 :             l = basel>i ? basel : i;
    1529           0 :             if ( l+1<st->li.lcnt )
    1530           0 :                 spos = st->li.lineheights[l+1].start_pos;
    1531             :             else
    1532           0 :                 spos = u_strlen(st->li.text);
    1533           0 :             st->sel_end = spos;
    1534           0 :         } else if ( st->wordsel )
    1535           0 :             SFTextAreaSelectWords(st,end);
    1536           0 :         else if ( event->u.mouse.button!=2 ) {
    1537           0 :             int e = end;
    1538           0 :             if ( e>st->sel_base ) {
    1539           0 :                 st->sel_start = st->sel_base; st->sel_end = e;
    1540             :             } else {
    1541           0 :                 st->sel_start = e; st->sel_end = st->sel_base;
    1542             :             }
    1543             :         }
    1544           0 :         if ( event->type==et_mouseup ) {
    1545           0 :             GDrawCancelTimer(st->pressed); st->pressed = NULL;
    1546           0 :             if ( event->u.mouse.button==2 )
    1547           0 :                 SFTextAreaPaste(st,sn_primary);
    1548           0 :             if ( st->sel_start==st->sel_end )
    1549           0 :                 SFTextArea_Show(st,st->sel_start);
    1550             :         }
    1551           0 :         if ( st->sel_end > u_strlen( st->li.text ))
    1552           0 :             fprintf( stderr, "About to crash\n" );
    1553           0 :         if ( refresh )
    1554           0 :             _ggadget_redraw(g);
    1555           0 :         if ( event->type==et_mouseup && st->changefontcallback )
    1556           0 :             STChangeCheck(st);
    1557           0 : return( true );
    1558             :     }
    1559           0 : return( false );
    1560             : }
    1561             : 
    1562           0 : static int sftextarea_key(GGadget *g, GEvent *event) {
    1563           0 :     SFTextArea *st = (SFTextArea *) g;
    1564             :     int ret;
    1565             : 
    1566           0 :     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
    1567           0 : return( false );
    1568             : 
    1569           0 :     if ( event->type == et_charup )
    1570           0 : return( false );
    1571           0 :     if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ||
    1572           0 :             (event->u.chr.keysym == GK_Return && !st->accepts_returns ) ||
    1573           0 :             ( event->u.chr.keysym == GK_Tab && !st->accepts_tabs ) ||
    1574           0 :             event->u.chr.keysym == GK_BackTab || event->u.chr.keysym == GK_Escape )
    1575           0 : return( false );
    1576             : 
    1577           0 :     if ( !st->hidden_cursor ) {      /* hide the mouse pointer */
    1578           0 :         if ( !st->drag_and_drop )
    1579           0 :             st->old_cursor = GDrawGetCursor(st->g.base);
    1580           0 :         GDrawSetCursor(g->base,ct_invisible);
    1581           0 :         st->hidden_cursor = true;
    1582           0 :         _GWidget_SetGrabGadget(g);      /* so that we get the next mouse movement to turn the cursor on */
    1583             :     }
    1584           0 :     if( st->cursor_on ) {    /* undraw the blinky text cursor if it is drawn */
    1585           0 :         gt_draw_cursor(g->base, st);
    1586           0 :         st->cursor_on = false;
    1587             :     }
    1588             : 
    1589           0 :     ret = SFTextAreaDoChange(st,event);
    1590           0 :     if ( st->changefontcallback )
    1591           0 :         STChangeCheck(st);
    1592           0 :     switch ( ret ) {
    1593             :       case 2:
    1594           0 :       break;
    1595             :       case true:
    1596           0 :         SFTextAreaChanged(st,-1);
    1597           0 :       break;
    1598             :       case false:
    1599           0 : return( false );
    1600             :     }
    1601           0 :     _ggadget_redraw(g);
    1602           0 : return( true );
    1603             : }
    1604             : 
    1605           0 : static int sftextarea_focus(GGadget *g, GEvent *event) {
    1606           0 :     SFTextArea *st = (SFTextArea *) g;
    1607           0 :     if ( st->cursor!=NULL ) {
    1608           0 :         GDrawCancelTimer(st->cursor);
    1609           0 :         st->cursor = NULL;
    1610           0 :         st->cursor_on = false;
    1611             :     }
    1612           0 :     if ( st->hidden_cursor && !event->u.focus.gained_focus ) {
    1613           0 :         GDrawSetCursor(st->g.base,st->old_cursor);
    1614           0 :         st->hidden_cursor = false;
    1615             :     }
    1616           0 :     st->g.has_focus = event->u.focus.gained_focus;
    1617           0 :     if ( event->u.focus.gained_focus ) {
    1618           0 :         st->cursor = GDrawRequestTimer(st->g.base,400,400,NULL);
    1619           0 :         st->cursor_on = true;
    1620           0 :         if ( event->u.focus.mnemonic_focus != mf_normal )
    1621           0 :             SFTextAreaSelect(&st->g,0,-1);
    1622           0 :         if ( st->gic!=NULL )
    1623           0 :             GTPositionGIC(st);
    1624             :     }
    1625           0 :     _ggadget_redraw(g);
    1626           0 :     SFTextAreaFocusChanged(st,event->u.focus.gained_focus);
    1627           0 : return( true );
    1628             : }
    1629             : 
    1630           0 : static int sftextarea_timer(GGadget *g, GEvent *event) {
    1631           0 :     SFTextArea *st = (SFTextArea *) g;
    1632             : 
    1633           0 :     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
    1634           0 : return(false);
    1635           0 :     if ( st->cursor == event->u.timer.timer ) {
    1636           0 :         if ( st->cursor_on ) {
    1637           0 :             gt_draw_cursor(g->base, st);
    1638           0 :             st->cursor_on = false;
    1639             :         } else {
    1640           0 :             st->cursor_on = true;
    1641           0 :             gt_draw_cursor(g->base, st);
    1642             :         }
    1643           0 : return( true );
    1644             :     }
    1645           0 :     if ( st->pressed == event->u.timer.timer ) {
    1646             :         GEvent e;
    1647           0 :         GDrawSetFont(g->base,st->font);
    1648           0 :         GDrawGetPointerPosition(g->base,&e);
    1649           0 :         if ( (e.u.mouse.x<g->r.x && st->xoff_left>0 ) ||
    1650           0 :                 (st->multi_line && e.u.mouse.y<g->r.y && st->loff_top>0 ) ||
    1651           0 :                 ( e.u.mouse.x >= g->r.x + g->r.width &&
    1652           0 :                         st->li.xmax-st->xoff_left>g->inner.width ) ||
    1653           0 :                 ( e.u.mouse.y >= g->r.y + g->r.height &&
    1654           0 :                         st->li.lineheights[st->li.lcnt-1].y-st->li.lineheights[st->loff_top].y >= g->inner.height )) {
    1655             :             int l;
    1656             :             int xpos, end;
    1657             : 
    1658           0 :             for ( l=st->loff_top; l<st->li.lcnt-1 && e.u.mouse.y-g->inner.y>st->li.lineheights[l+1].y-st->li.lineheights[st->loff_top].y;
    1659           0 :                     ++l );
    1660           0 :             if ( e.u.mouse.y<g->r.y && st->loff_top>0 )
    1661           0 :                 l = --st->loff_top;
    1662           0 :             else if ( e.u.mouse.y >= g->r.y + g->r.height &&
    1663           0 :                             st->li.lineheights[st->li.lcnt-1].y-st->li.lineheights[st->loff_top].y > g->inner.height ) {
    1664           0 :                 ++st->loff_top;
    1665           0 :                 ++l;
    1666           0 :             } else if ( l<st->loff_top )
    1667           0 :                 l = st->loff_top; 
    1668           0 :             else if ( st->li.lineheights[l].y>=st->li.lineheights[st->loff_top].y + g->inner.height ) {
    1669           0 :                 for ( l = st->loff_top+1; st->li.lineheights[l].y<st->li.lineheights[st->loff_top].y+g->inner.height; ++l );
    1670           0 :                 --l;
    1671           0 :                 if ( l==st->loff_top ) ++l;
    1672             :             }
    1673           0 :             if ( l>=st->li.lcnt ) l = st->li.lcnt-1;
    1674             : 
    1675           0 :             xpos = e.u.mouse.x+st->xoff_left;
    1676           0 :             if ( e.u.mouse.x<g->r.x && st->xoff_left>0 ) {
    1677           0 :                 st->xoff_left -= st->nw;
    1678           0 :                 xpos = g->inner.x + st->xoff_left;
    1679           0 :             } else if ( e.u.mouse.x >= g->r.x + g->r.width &&
    1680           0 :                             st->li.xmax-st->xoff_left>g->inner.width ) {
    1681           0 :                 st->xoff_left += st->nw;
    1682           0 :                 xpos = g->inner.x + st->xoff_left + g->inner.width;
    1683             :             }
    1684             : 
    1685           0 :             end = SFTextAreaGetOffsetFromXPos(st,l,xpos - st->g.inner.x - st->xoff_left);
    1686           0 :             if ( end > st->sel_base ) {
    1687           0 :                 st->sel_start = st->sel_base;
    1688           0 :                 st->sel_end = end;
    1689             :             } else {
    1690           0 :                 st->sel_start = end;
    1691           0 :                 st->sel_end = st->sel_base;
    1692             :             }
    1693           0 :             _ggadget_redraw(g);
    1694           0 :             if ( st->vsb!=NULL )
    1695           0 :                 GScrollBarSetPos(&st->vsb->g,st->li.lineheights[st->loff_top].y);
    1696           0 :             if ( st->hsb!=NULL )
    1697           0 :                 GScrollBarSetPos(&st->hsb->g,st->xoff_left);
    1698             :         }
    1699           0 : return( true );
    1700             :     }
    1701           0 : return( false );
    1702             : }
    1703             : 
    1704           0 : static int sftextarea_sel(GGadget *g, GEvent *event) {
    1705           0 :     SFTextArea *st = (SFTextArea *) g;
    1706             :     int end;
    1707             :     int i;
    1708             : 
    1709           0 :     if ( event->type == et_selclear ) {
    1710           0 :         if ( event->u.selclear.sel==sn_primary && st->sel_start!=st->sel_end ) {
    1711           0 : return( true );
    1712             :         }
    1713           0 : return( false );
    1714             :     }
    1715             : 
    1716           0 :     if ( st->has_dd_cursor )
    1717           0 :         SFTextAreaDrawDDCursor(st,st->dd_cursor_pos);
    1718           0 :     GDrawSetFont(g->base,st->font);
    1719           0 :     for ( i=st->loff_top ; i<st->li.lcnt-1 && st->li.lineheights[i+1].y-st->li.lineheights[st->loff_top].y<
    1720           0 :             event->u.drag_drop.y-g->inner.y; ++i );
    1721           0 :     if ( !st->multi_line ) i = 0;
    1722           0 :     if ( i>=st->li.lcnt )
    1723           0 :         end = u_strlen(st->li.text);
    1724             :     else
    1725           0 :         end = SFTextAreaGetOffsetFromXPos(st,i,event->u.drag_drop.x - st->g.inner.x - st->xoff_left);
    1726           0 :     if ( event->type == et_drag ) {
    1727           0 :         SFTextAreaDrawDDCursor(st,end);
    1728           0 :     } else if ( event->type == et_dragout ) {
    1729             :         /* this event exists simply to clear the dd cursor line. We've done */
    1730             :         /*  that already */ 
    1731           0 :     } else if ( event->type == et_drop ) {
    1732           0 :         st->sel_start = st->sel_end = st->sel_base = end;
    1733           0 :         SFTextAreaPaste(st,sn_drag_and_drop);
    1734           0 :         SFTextArea_Show(st,st->sel_start);
    1735           0 :         _ggadget_redraw(&st->g);
    1736             :     } else
    1737           0 : return( false );
    1738             : 
    1739           0 : return( true );
    1740             : }
    1741             : 
    1742           0 : static void sftextarea_destroy(GGadget *g) {
    1743           0 :     SFTextArea *st = (SFTextArea *) g;
    1744             : 
    1745           0 :     if ( st==NULL )
    1746           0 : return;
    1747             : 
    1748           0 :     if ( st->vsb!=NULL )
    1749           0 :         (st->vsb->g.funcs->destroy)(&st->vsb->g);
    1750           0 :     if ( st->hsb!=NULL )
    1751           0 :         (st->hsb->g.funcs->destroy)(&st->hsb->g);
    1752           0 :     GDrawCancelTimer(st->pressed);
    1753           0 :     GDrawCancelTimer(st->cursor);
    1754           0 :     LayoutInfo_Destroy(&st->li);
    1755           0 :     _ggadget_destroy(g);
    1756             : }
    1757             : 
    1758           0 : static void SFTextAreaSetTitle(GGadget *g,const unichar_t *tit) {
    1759           0 :     SFTextArea *st = (SFTextArea *) g;
    1760           0 :     unichar_t *old = st->li.oldtext;
    1761           0 :     if ( u_strcmp(tit,st->li.text)==0 )      /* If it doesn't change anything, then don't trash undoes or selection */
    1762           0 : return;
    1763           0 :     st->li.oldtext = st->li.text;
    1764           0 :     st->sel_oldstart = st->sel_start; st->sel_oldend = st->sel_end; st->sel_oldbase = st->sel_base;
    1765           0 :     st->li.text = u_copy(tit);               /* tit might be oldtext, so must copy before freeing */
    1766           0 :     free(old);
    1767           0 :     st->sel_start = st->sel_end = st->sel_base = u_strlen(tit);
    1768           0 :     LI_fontlistmergecheck(&st->li);
    1769           0 :     LayoutInfoRefigureLines(&st->li,0,-1,st->g.inner.width);
    1770           0 :     SFTextArea_Show(st,st->sel_start);
    1771           0 :     _ggadget_redraw(g);
    1772             : }
    1773             : 
    1774           0 : static const unichar_t *_SFTextAreaGetTitle(GGadget *g) {
    1775           0 :     SFTextArea *st = (SFTextArea *) g;
    1776           0 : return( st->li.text );
    1777             : }
    1778             : 
    1779           0 : static void SFTextAreaSetFont(GGadget *g,FontInstance *new) {
    1780           0 :     SFTextArea *st = (SFTextArea *) g;
    1781           0 :     st->font = new;
    1782             :     /* Irrelevant */;
    1783           0 : }
    1784             : 
    1785           0 : static FontInstance *SFTextAreaGetFont(GGadget *g) {
    1786           0 :     SFTextArea *st = (SFTextArea *) g;
    1787           0 : return( st->font );
    1788             : }
    1789             : 
    1790           0 : void SFTextAreaShow(GGadget *g,int pos) {
    1791           0 :     SFTextArea *st = (SFTextArea *) g;
    1792             : 
    1793           0 :     SFTextArea_Show(st,pos);
    1794           0 :     _ggadget_redraw(g);
    1795           0 : }
    1796             : 
    1797           0 : void SFTextAreaSelect(GGadget *g,int start, int end) {
    1798           0 :     SFTextArea *st = (SFTextArea *) g;
    1799             : 
    1800           0 :     SFTextAreaGrabPrimarySelection(st);
    1801           0 :     if ( end<0 ) {
    1802           0 :         end = u_strlen(st->li.text);
    1803           0 :         if ( start<0 ) start = end;
    1804             :     }
    1805           0 :     if ( start>end ) { int temp = start; start = end; end = temp; }
    1806           0 :     if ( end>u_strlen(st->li.text)) end = u_strlen(st->li.text);
    1807           0 :     if ( start>u_strlen(st->li.text)) start = end;
    1808           0 :     else if ( start<0 ) start=0;
    1809           0 :     st->sel_start = st->sel_base = start;
    1810           0 :     st->sel_end = end;
    1811           0 :     _ggadget_redraw(g);                 /* Should be safe just to draw the textfield gadget, sbs won't have changed */
    1812           0 : }
    1813             : 
    1814           0 : void SFTextAreaReplace(GGadget *g,const unichar_t *txt) {
    1815           0 :     SFTextArea *st = (SFTextArea *) g;
    1816             : 
    1817           0 :     SFTextArea_Replace(st,txt);
    1818           0 :     _ggadget_redraw(g);
    1819           0 : }
    1820             : 
    1821           0 : static void sftextarea_redraw(GGadget *g) {
    1822           0 :     SFTextArea *st = (SFTextArea *) g;
    1823           0 :     if ( st->vsb!=NULL )
    1824           0 :         _ggadget_redraw((GGadget *) (st->vsb));
    1825           0 :     if ( st->hsb!=NULL )
    1826           0 :         _ggadget_redraw((GGadget *) (st->hsb));
    1827           0 :     _ggadget_redraw(g);
    1828           0 : }
    1829             : 
    1830           0 : static void sftextarea_move(GGadget *g, int32 x, int32 y ) {
    1831           0 :     SFTextArea *st = (SFTextArea *) g;
    1832           0 :     if ( st->vsb!=NULL )
    1833           0 :         _ggadget_move((GGadget *) (st->vsb),x+(st->vsb->g.r.x-g->r.x),y);
    1834           0 :     if ( st->hsb!=NULL )
    1835           0 :         _ggadget_move((GGadget *) (st->hsb),x,y+(st->hsb->g.r.y-g->r.y));
    1836           0 :     _ggadget_move(g,x,y);
    1837           0 : }
    1838             : 
    1839           0 : static void sftextarea_resize(GGadget *g, int32 width, int32 height ) {
    1840           0 :     SFTextArea *st = (SFTextArea *) g;
    1841           0 :     int gtwidth=width, gtheight=height, oldheight=0;
    1842             :     int l;
    1843             : 
    1844           0 :     if ( st->hsb!=NULL ) {
    1845           0 :         oldheight = st->hsb->g.r.y+st->hsb->g.r.height-g->r.y;
    1846           0 :         gtheight = height - (oldheight-g->r.height);
    1847             :     }
    1848           0 :     if ( st->vsb!=NULL ) {
    1849           0 :         int oldwidth = st->vsb->g.r.x+st->vsb->g.r.width-g->r.x;
    1850           0 :         gtwidth = width - (oldwidth-g->r.width);
    1851           0 :         _ggadget_move((GGadget *) (st->vsb),st->vsb->g.r.x+width-oldwidth,st->vsb->g.r.y);
    1852           0 :         _ggadget_resize((GGadget *) (st->vsb),st->vsb->g.r.width,gtheight);
    1853             :     }
    1854           0 :     if ( st->hsb!=NULL ) {
    1855           0 :         _ggadget_move((GGadget *) (st->hsb),st->hsb->g.r.y,st->hsb->g.r.y+height-oldheight);
    1856           0 :         _ggadget_resize((GGadget *) (st->hsb),gtwidth,st->hsb->g.r.height);
    1857             :     }
    1858           0 :     _ggadget_resize(g,gtwidth, gtheight);
    1859           0 :     SFTextAreaRefigureLines(st,0,-1);
    1860           0 :     if ( st->vsb!=NULL ) {
    1861           0 :         GScrollBarSetBounds(&st->vsb->g,0,st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh,st->g.inner.height);
    1862           0 :         if ( st->loff_top>=st->li.lcnt )
    1863           0 :             st->loff_top = st->li.lcnt-1;
    1864           0 :         l = st->li.lcnt - SFTextArea_EndPage(st);
    1865           0 :         if ( l<0 ) l = 0;
    1866           0 :         if ( l!=st->loff_top ) {
    1867           0 :             st->loff_top = l;
    1868           0 :             GScrollBarSetPos(&st->vsb->g,st->li.lineheights[l].y);
    1869           0 :             _ggadget_redraw(&st->g);
    1870             :         }
    1871             :     }
    1872           0 :     SFTextAreaShow(&st->g,st->sel_start);
    1873           0 : }
    1874             : 
    1875           0 : static GRect *sftextarea_getsize(GGadget *g, GRect *r ) {
    1876           0 :     SFTextArea *st = (SFTextArea *) g;
    1877           0 :     _ggadget_getsize(g,r);
    1878           0 :     if ( st->vsb!=NULL )
    1879           0 :         r->width =  st->vsb->g.r.x+st->vsb->g.r.width-g->r.x;
    1880           0 :     if ( st->hsb!=NULL )
    1881           0 :         r->height =  st->hsb->g.r.y+st->hsb->g.r.height-g->r.y;
    1882           0 : return( r );
    1883             : }
    1884             : 
    1885           0 : static void sftextarea_setvisible(GGadget *g, int visible ) {
    1886           0 :     SFTextArea *st = (SFTextArea *) g;
    1887           0 :     if ( st->vsb!=NULL ) _ggadget_setvisible(&st->vsb->g,visible);
    1888           0 :     if ( st->hsb!=NULL ) _ggadget_setvisible(&st->hsb->g,visible);
    1889           0 :     _ggadget_setvisible(g,visible);
    1890           0 : }
    1891             : 
    1892           0 : static void sftextarea_setenabled(GGadget *g, int enabled ) {
    1893           0 :     SFTextArea *st = (SFTextArea *) g;
    1894           0 :     if ( st->vsb!=NULL ) _ggadget_setenabled(&st->vsb->g,enabled);
    1895           0 :     if ( st->hsb!=NULL ) _ggadget_setenabled(&st->hsb->g,enabled);
    1896           0 :     _ggadget_setenabled(g,enabled);
    1897           0 : }
    1898             : 
    1899           0 : static int sftextarea_vscroll(GGadget *g, GEvent *event) {
    1900           0 :     enum sb sbt = event->u.control.u.sb.type;
    1901           0 :     SFTextArea *st = (SFTextArea *) (g->data);
    1902           0 :     int loff = st->loff_top;
    1903             :     int page;
    1904             : 
    1905           0 :     g = (GGadget *) st;
    1906             : 
    1907           0 :     if ( sbt==et_sb_top )
    1908           0 :         loff = 0;
    1909           0 :     else if ( sbt==et_sb_bottom ) {
    1910           0 :         loff = st->li.lcnt;
    1911           0 :     } else if ( sbt==et_sb_up ) {
    1912           0 :         if ( st->loff_top!=0 ) loff = st->loff_top-1; else loff = 0;
    1913           0 :     } else if ( sbt==et_sb_down ) {
    1914           0 :         ++loff;
    1915           0 :     } else if ( sbt==et_sb_uppage ) {
    1916           0 :         for ( page=0; st->loff_top-page>=0 && st->li.lineheights[st->loff_top].y-st->li.lineheights[st->loff_top-page].y<=g->inner.height;
    1917           0 :                 ++page );
    1918           0 :         if ( --page < 1 ) page = 1;
    1919           0 :         else if ( page>2 ) page-=1;
    1920           0 :         loff = st->loff_top - page;
    1921           0 :     } else if ( sbt==et_sb_downpage ) {
    1922           0 :         for ( page=0; st->loff_top+page<st->li.lcnt && st->li.lineheights[st->loff_top+page].y-st->li.lineheights[st->loff_top].y<=g->inner.height;
    1923           0 :                 ++page );
    1924           0 :         if ( --page < 1 ) page = 1;
    1925           0 :         else if ( page>2 ) page-=1;
    1926           0 :         loff = st->loff_top + page;
    1927             :     } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
    1928           0 :         for ( loff = 0; loff<st->li.lcnt && st->li.lineheights[loff].y<event->u.control.u.sb.pos; ++loff );
    1929             :     }
    1930           0 :     for ( page=1; st->li.lcnt-page>=0 && st->li.lineheights[st->li.lcnt-1].y+st->li.lineheights[st->li.lcnt-1].fh-st->li.lineheights[st->li.lcnt-page].y<=g->inner.height;
    1931           0 :             ++page );
    1932           0 :     --page;
    1933           0 :     if ( loff > st->li.lcnt-page )
    1934           0 :         loff = st->li.lcnt - page;
    1935           0 :     if ( loff<0 ) loff = 0;
    1936           0 :     if ( loff!=st->loff_top ) {
    1937           0 :         st->loff_top = loff;
    1938           0 :         GScrollBarSetPos(&st->vsb->g,st->li.lineheights[loff].y);
    1939           0 :         _ggadget_redraw(&st->g);
    1940             :     }
    1941           0 : return( true );
    1942             : }
    1943             : 
    1944           0 : static int sftextarea_hscroll(GGadget *g, GEvent *event) {
    1945           0 :     enum sb sbt = event->u.control.u.sb.type;
    1946           0 :     SFTextArea *st = (SFTextArea *) (g->data);
    1947           0 :     int xoff = st->xoff_left;
    1948             : 
    1949           0 :     g = (GGadget *) st;
    1950             : 
    1951           0 :     if ( sbt==et_sb_top )
    1952           0 :         xoff = 0;
    1953           0 :     else if ( sbt==et_sb_bottom ) {
    1954           0 :         xoff = st->li.xmax - st->g.inner.width;
    1955           0 :         if ( xoff<0 ) xoff = 0;
    1956           0 :     } else if ( sbt==et_sb_up ) {
    1957           0 :         if ( st->xoff_left>st->nw ) xoff = st->xoff_left-st->nw; else xoff = 0;
    1958           0 :     } else if ( sbt==et_sb_down ) {
    1959           0 :         if ( st->xoff_left + st->nw + st->g.inner.width >= st->li.xmax )
    1960           0 :             xoff = st->li.xmax - st->g.inner.width;
    1961             :         else
    1962           0 :             xoff += st->nw;
    1963           0 :     } else if ( sbt==et_sb_uppage ) {
    1964           0 :         int page = (3*g->inner.width)/4;
    1965           0 :         xoff = st->xoff_left - page;
    1966           0 :         if ( xoff<0 ) xoff=0;
    1967           0 :     } else if ( sbt==et_sb_downpage ) {
    1968           0 :         int page = (3*g->inner.width)/4;
    1969           0 :         xoff = st->xoff_left + page;
    1970           0 :         if ( xoff + st->g.inner.width >= st->li.xmax )
    1971           0 :             xoff = st->li.xmax - st->g.inner.width;
    1972             :     } else /* if ( sbt==et_sb_thumb || sbt==et_sb_thumbrelease ) */ {
    1973           0 :         xoff = event->u.control.u.sb.pos;
    1974             :     }
    1975           0 :     if ( xoff + st->g.inner.width >= st->li.xmax )
    1976           0 :         xoff = st->li.xmax - st->g.inner.width;
    1977           0 :     if ( xoff<0 ) xoff = 0;
    1978           0 :     if ( st->xoff_left!=xoff ) {
    1979           0 :         st->xoff_left = xoff;
    1980           0 :         GScrollBarSetPos(&st->hsb->g,xoff);
    1981           0 :         _ggadget_redraw(&st->g);
    1982             :     }
    1983           0 : return( true );
    1984             : }
    1985             : 
    1986           0 : static void SFTextFieldSetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
    1987           0 :     SFTextArea *gt = (SFTextArea *) g;
    1988             : 
    1989           0 :     if ( outer!=NULL ) {
    1990           0 :         g->desired_width = outer->width;
    1991           0 :         g->desired_height = outer->height;
    1992           0 :     } else if ( inner!=NULL ) {
    1993           0 :         int bp = GBoxBorderWidth(g->base,g->box);
    1994           0 :         int extra=0;
    1995           0 :         g->desired_width = inner->width + 2*bp + extra;
    1996           0 :         g->desired_height = inner->height + 2*bp;
    1997           0 :         if ( gt->multi_line ) {
    1998           0 :             int sbadd = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width) +
    1999           0 :                     GDrawPointsToPixels(gt->g.base,1);
    2000           0 :             g->desired_width += sbadd;
    2001           0 :             if ( !gt->li.wrap )
    2002           0 :                 g->desired_height += sbadd;
    2003             :         }
    2004             :     }
    2005           0 : }
    2006             : 
    2007           0 : static void SFTextFieldGetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
    2008           0 :     SFTextArea *gt = (SFTextArea *) g;
    2009           0 :     int width=0, height;
    2010           0 :     int extra=0;
    2011           0 :     int bp = GBoxBorderWidth(g->base,g->box);
    2012             : 
    2013           0 :     width = GGadgetScale(GDrawPointsToPixels(gt->g.base,80));
    2014           0 :     height = gt->multi_line? 4*gt->fh:gt->fh;
    2015             : 
    2016           0 :     if ( g->desired_width>extra+2*bp ) width = g->desired_width - extra - 2*bp;
    2017           0 :     if ( g->desired_height>2*bp ) height = g->desired_height - 2*bp;
    2018             : 
    2019           0 :     if ( gt->multi_line ) {
    2020           0 :         int sbadd = GDrawPointsToPixels(gt->g.base,_GScrollBar_Width) +
    2021           0 :                 GDrawPointsToPixels(gt->g.base,1);
    2022           0 :         width += sbadd;
    2023           0 :         if ( !gt->li.wrap )
    2024           0 :             height += sbadd;
    2025             :     }
    2026             : 
    2027           0 :     if ( inner!=NULL ) {
    2028           0 :         inner->x = inner->y = 0;
    2029           0 :         inner->width = width;
    2030           0 :         inner->height = height;
    2031             :     }
    2032           0 :     if ( outer!=NULL ) {
    2033           0 :         outer->x = outer->y = 0;
    2034           0 :         outer->width = width + extra + 2*bp;
    2035           0 :         outer->height = height + 2*bp;
    2036             :     }
    2037           0 : }
    2038             : 
    2039           0 : static int SFtextfield_FillsWindow(GGadget *g) {
    2040           0 : return( ((SFTextArea *) g)->multi_line && g->prev==NULL &&
    2041           0 :         (_GWidgetGetGadgets(g->base)==g ||
    2042           0 :          _GWidgetGetGadgets(g->base)==(GGadget *) ((SFTextArea *) g)->vsb ||
    2043           0 :          _GWidgetGetGadgets(g->base)==(GGadget *) ((SFTextArea *) g)->hsb ));
    2044             : }
    2045             : 
    2046             : struct gfuncs sftextarea_funcs = {
    2047             :     0,
    2048             :     sizeof(struct gfuncs),
    2049             : 
    2050             :     sftextarea_expose,
    2051             :     sftextarea_mouse,
    2052             :     sftextarea_key,
    2053             :     _sftextarea_editcmd,
    2054             :     sftextarea_focus,
    2055             :     sftextarea_timer,
    2056             :     sftextarea_sel,
    2057             : 
    2058             :     sftextarea_redraw,
    2059             :     sftextarea_move,
    2060             :     sftextarea_resize,
    2061             :     sftextarea_setvisible,
    2062             :     sftextarea_setenabled,
    2063             :     sftextarea_getsize,
    2064             :     _ggadget_getinnersize,
    2065             : 
    2066             :     sftextarea_destroy,
    2067             : 
    2068             :     SFTextAreaSetTitle,
    2069             :     _SFTextAreaGetTitle,
    2070             :     NULL,
    2071             :     NULL,
    2072             :     NULL,
    2073             :     SFTextAreaSetFont,
    2074             :     SFTextAreaGetFont,
    2075             : 
    2076             :     NULL,
    2077             :     NULL,
    2078             :     NULL,
    2079             :     NULL,
    2080             :     NULL,
    2081             :     NULL,
    2082             :     NULL,
    2083             :     NULL,
    2084             :     NULL,
    2085             :     NULL,
    2086             :     NULL,
    2087             : 
    2088             :     SFTextFieldGetDesiredSize,
    2089             :     SFTextFieldSetDesiredSize,
    2090             :     SFtextfield_FillsWindow,
    2091             :     NULL
    2092             : };
    2093             : 
    2094           0 : static void SFTextAreaInit() {
    2095             :     FontRequest rq;
    2096             : 
    2097           0 :     GGadgetInit();
    2098           0 :     GDrawDecomposeFont(_ggadget_default_font,&rq);
    2099           0 :     rq.utf8_family_name = MONO_UI_FAMILIES;
    2100           0 :     sftextarea_font = GDrawInstanciateFont(NULL,&rq);
    2101           0 :     sftextarea_font = GResourceFindFont("SFTextArea.Font",sftextarea_font);
    2102           0 :     _GGadgetCopyDefaultBox(&sftextarea_box);
    2103           0 :     sftextarea_box.padding = 3;
    2104           0 :     sftextarea_box.flags = box_active_border_inner;
    2105           0 :     sftextarea_font = _GGadgetInitDefaultBox("SFTextArea.",&sftextarea_box,sftextarea_font);
    2106           0 :     sftextarea_inited = true;
    2107           0 : }
    2108             : 
    2109           0 : static void SFTextAreaAddVSb(SFTextArea *st) {
    2110             :     GGadgetData gd;
    2111             : 
    2112           0 :     memset(&gd,'\0',sizeof(gd));
    2113           0 :     gd.pos.y = st->g.r.y; gd.pos.height = st->g.r.height;
    2114           0 :     gd.pos.width = GDrawPointsToPixels(st->g.base,_GScrollBar_Width);
    2115           0 :     gd.pos.x = st->g.r.x+st->g.r.width - gd.pos.width;
    2116           0 :     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
    2117           0 :     gd.handle_controlevent = sftextarea_vscroll;
    2118           0 :     st->vsb = (GScrollBar *) GScrollBarCreate(st->g.base,&gd,st);
    2119           0 :     st->vsb->g.contained = true;
    2120             : 
    2121           0 :     gd.pos.width += GDrawPointsToPixels(st->g.base,1);
    2122           0 :     st->g.r.width -= gd.pos.width;
    2123           0 :     st->g.inner.width -= gd.pos.width;
    2124           0 : }
    2125             : 
    2126           0 : static void SFTextAreaAddHSb(SFTextArea *st) {
    2127             :     GGadgetData gd;
    2128             : 
    2129           0 :     memset(&gd,'\0',sizeof(gd));
    2130           0 :     gd.pos.x = st->g.r.x; gd.pos.width = st->g.r.width;
    2131           0 :     gd.pos.height = GDrawPointsToPixels(st->g.base,_GScrollBar_Width);
    2132           0 :     gd.pos.y = st->g.r.y+st->g.r.height - gd.pos.height;
    2133           0 :     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
    2134           0 :     gd.handle_controlevent = sftextarea_hscroll;
    2135           0 :     st->hsb = (GScrollBar *) GScrollBarCreate(st->g.base,&gd,st);
    2136           0 :     st->hsb->g.contained = true;
    2137             : 
    2138           0 :     gd.pos.height += GDrawPointsToPixels(st->g.base,1);
    2139           0 :     st->g.r.height -= gd.pos.height;
    2140           0 :     st->g.inner.height -= gd.pos.height;
    2141           0 :     if ( st->vsb!=NULL ) {
    2142           0 :         st->vsb->g.r.height -= gd.pos.height;
    2143           0 :         st->vsb->g.inner.height -= gd.pos.height;
    2144             :     }
    2145           0 : }
    2146             : 
    2147           0 : static void SFTextAreaFit(SFTextArea *st) {
    2148             :     GTextBounds bounds;
    2149           0 :     int as=0, ds, ld, fh=0, temp;
    2150             :     GRect needed;
    2151           0 :     int extra=0;
    2152             : 
    2153           0 :     needed.x = needed.y = 0;
    2154           0 :     needed.width = needed.height = 1;
    2155             : 
    2156             :     { /* This doesn't mean much of anything */
    2157           0 :         FontInstance *old = GDrawSetFont(st->g.base,st->font);
    2158           0 :         (void) GDrawGetTextBounds(st->g.base,st->li.text, -1, &bounds);
    2159           0 :         GDrawWindowFontMetrics(st->g.base,st->font,&as, &ds, &ld);
    2160           0 :         if ( as<bounds.as ) as = bounds.as;
    2161           0 :         if ( ds<bounds.ds ) ds = bounds.ds;
    2162           0 :         st->fh = fh = as+ds;
    2163           0 :         st->as = as;
    2164           0 :         st->nw = 6;
    2165           0 :         GDrawSetFont(st->g.base,old);
    2166             :     }
    2167             : 
    2168           0 :     temp = GGadgetScale(GDrawPointsToPixels(st->g.base,80))+extra;
    2169             : 
    2170           0 :     if ( st->g.r.width==0 || st->g.r.height==0 ) {
    2171           0 :         int bp = GBoxBorderWidth(st->g.base,st->g.box);
    2172           0 :         needed.x = needed.y = 0;
    2173           0 :         needed.width = temp;
    2174           0 :         needed.height = st->multi_line? 4*fh:fh;
    2175           0 :         _ggadgetFigureSize(st->g.base,st->g.box,&needed,false);
    2176           0 :         if ( st->g.r.width==0 ) {
    2177           0 :             st->g.r.width = needed.width;
    2178           0 :             st->g.inner.width = temp-extra;
    2179           0 :             st->g.inner.x = st->g.r.x + (needed.width-temp)/2;
    2180             :         } else {
    2181           0 :             st->g.inner.x = st->g.r.x + bp;
    2182           0 :             st->g.inner.width = st->g.r.width - 2*bp;
    2183             :         }
    2184           0 :         if ( st->g.r.height==0 ) {
    2185           0 :             st->g.r.height = needed.height;
    2186           0 :             st->g.inner.height = st->multi_line? 4*fh:fh;
    2187           0 :             st->g.inner.y = st->g.r.y + (needed.height-st->g.inner.height)/2;
    2188             :         } else {
    2189           0 :             st->g.inner.y = st->g.r.y + bp;
    2190           0 :             st->g.inner.height = st->g.r.height - 2*bp;
    2191             :         }
    2192           0 :         if ( st->multi_line ) {
    2193           0 :             int sbadd = GDrawPointsToPixels(st->g.base,_GScrollBar_Width) +
    2194           0 :                     GDrawPointsToPixels(st->g.base,1);
    2195             :             {
    2196           0 :                 st->g.r.width += sbadd;
    2197           0 :                 st->g.inner.width += sbadd;
    2198             :             }
    2199           0 :             if ( !st->li.wrap ) {
    2200           0 :                 st->g.r.height += sbadd;
    2201           0 :                 st->g.inner.height += sbadd;
    2202             :             }
    2203             :         }
    2204             :     } else {
    2205           0 :         int bp = GBoxBorderWidth(st->g.base,st->g.box);
    2206           0 :         st->g.inner = st->g.r;
    2207           0 :         st->g.inner.x += bp; st->g.inner.y += bp;
    2208           0 :         st->g.inner.width -= 2*bp-extra; st->g.inner.height -= 2*bp;
    2209             :     }
    2210           0 :     if ( st->multi_line ) {
    2211           0 :         SFTextAreaAddVSb(st);
    2212           0 :         if ( !st->li.wrap )
    2213           0 :             SFTextAreaAddHSb(st);
    2214             :     }
    2215           0 : }
    2216             : 
    2217           0 : static SFTextArea *_SFTextAreaCreate(SFTextArea *st, struct gwindow *base, GGadgetData *gd,void *data, GBox *def) {
    2218             : 
    2219           0 :     if ( !sftextarea_inited )
    2220           0 :         SFTextAreaInit();
    2221           0 :     st->g.funcs = &sftextarea_funcs;
    2222           0 :     _GGadget_Create(&st->g,base,gd,data,def);
    2223             : 
    2224           0 :     st->g.takes_input = true; st->g.takes_keyboard = true; st->g.focusable = true;
    2225           0 :     if ( gd->label!=NULL ) {
    2226           0 :         if ( gd->label->text_in_resource )        /* This one use of GStringGetResource is ligit */
    2227           0 :             st->li.text = u_copy((unichar_t *) GStringGetResource((intpt) gd->label->text,&st->g.mnemonic));
    2228           0 :         else if ( gd->label->text_is_1byte )
    2229           0 :             st->li.text = utf82u_copy((char *) gd->label->text);
    2230             :         else
    2231           0 :             st->li.text = u_copy(gd->label->text);
    2232           0 :         st->sel_start = st->sel_end = st->sel_base = u_strlen(st->li.text);
    2233             :     }
    2234           0 :     if ( st->li.text==NULL )
    2235           0 :         st->li.text = calloc(1,sizeof(unichar_t));
    2236           0 :     st->font = sftextarea_font;
    2237           0 :     if ( gd->label!=NULL && gd->label->font!=NULL )
    2238           0 :         st->font = gd->label->font;
    2239           0 :     SFTextAreaFit(st);
    2240           0 :     _GGadget_FinalPosition(&st->g,base,gd);
    2241           0 :     SFTextAreaRefigureLines(st,0,-1);
    2242             : 
    2243           0 :     if ( gd->flags & gg_group_end )
    2244           0 :         _GGadgetCloseGroup(&st->g);
    2245           0 :     GWidgetIndicateFocusGadget(&st->g);
    2246           0 :     if ( gd->flags & gg_text_xim )
    2247           0 :         st->gic = GDrawCreateInputContext(base,gic_overspot|gic_orlesser);
    2248           0 : return( st );
    2249             : }
    2250             : 
    2251           0 : GGadget *SFTextAreaCreate(struct gwindow *base, GGadgetData *gd,void *data) {
    2252           0 :     SFTextArea *st = calloc(1,sizeof(SFTextArea));
    2253           0 :     st->multi_line = true;
    2254           0 :     st->accepts_returns = true;
    2255           0 :     st->li.wrap = true;
    2256           0 :     _SFTextAreaCreate(st,base,gd,data,&sftextarea_box);
    2257           0 :     st->li.dpi = 100;
    2258             : 
    2259           0 : return( &st->g );
    2260             : }
    2261             : 
    2262           0 : static int SFTF_NormalizeStartEnd(SFTextArea *st, int start, int *_end) {
    2263           0 :     int end = *_end;
    2264           0 :     int len = u_strlen(st->li.text);
    2265             : 
    2266           0 :     if ( st->li.generated==NULL ) {
    2267           0 :         start = 0;
    2268           0 :         end = len;
    2269           0 :     } else if ( start==-1 ) {
    2270           0 :         start = st->sel_start;
    2271           0 :         end = st->sel_end;
    2272           0 :     } else if ( end==-1 )
    2273           0 :         end = len;
    2274           0 :     if ( end>len ) end = len;
    2275           0 :     if ( start<0 ) start = 0;
    2276           0 :     if ( start>end ) start = end;
    2277           0 :     *_end = end;
    2278           0 : return( start );
    2279             : }
    2280             : 
    2281           0 : static void SFTFMetaChangeCleanup(SFTextArea *st,int start, int end) {
    2282           0 :     LI_fontlistmergecheck(&st->li);
    2283           0 :     SFTextAreaRefigureLines(st, start,end);
    2284           0 :     GDrawRequestExpose(st->g.base,&st->g.inner,false);
    2285           0 :     if ( st->changefontcallback != NULL )
    2286           0 :         STChangeCheck(st);
    2287           0 : }
    2288             : 
    2289           0 : int SFTFSetFont(GGadget *g, int start, int end, SplineFont *sf) {
    2290           0 :     SFTextArea *st = (SFTextArea *) g;
    2291             :     FontData *cur;
    2292             :     struct fontlist *fl;
    2293             : 
    2294           0 :     start = SFTF_NormalizeStartEnd(st, start, &end);
    2295           0 :     fl = LI_BreakFontList(&st->li,start,end);
    2296           0 :     while ( fl!=NULL && fl->end<=end ) {
    2297           0 :         if ( fl->fd->sf!=sf ) {
    2298           0 :             cur = LI_FindFontData(&st->li, sf, fl->fd->layer, fl->fd->fonttype, fl->fd->pointsize, fl->fd->antialias);
    2299           0 :             if ( cur!=NULL )
    2300           0 :                 fl->fd = cur;
    2301             :         }
    2302           0 :         fl = fl->next;
    2303             :     }
    2304             : 
    2305           0 :     SFTFMetaChangeCleanup(st,start,end);
    2306           0 : return( true );
    2307             : }
    2308             : 
    2309           0 : int SFTFSetFontType(GGadget *g, int start, int end, enum sftf_fonttype fonttype) {
    2310           0 :     SFTextArea *st = (SFTextArea *) g;
    2311             :     FontData *cur;
    2312             :     struct fontlist *fl;
    2313             : 
    2314           0 :     start = SFTF_NormalizeStartEnd(st, start, &end);
    2315           0 :     fl = LI_BreakFontList(&st->li,start,end);
    2316           0 :     while ( fl!=NULL && fl->end<=end ) {
    2317           0 :         if ( fl->fd->fonttype!=fonttype ) {
    2318           0 :             cur = LI_FindFontData(&st->li, fl->fd->sf, fl->fd->layer, fonttype, fl->fd->pointsize, fl->fd->antialias);
    2319           0 :             if ( cur!=NULL )
    2320           0 :                 fl->fd = cur;
    2321             :         }
    2322           0 :         fl = fl->next;
    2323             :     }
    2324             : 
    2325           0 :     SFTFMetaChangeCleanup(st,start,end);
    2326           0 : return( true );
    2327             : }
    2328             : 
    2329           0 : int SFTFSetSize(GGadget *g, int start, int end, int pointsize) {
    2330           0 :     SFTextArea *st = (SFTextArea *) g;
    2331             :     FontData *cur;
    2332             :     struct fontlist *fl;
    2333             : 
    2334           0 :     if ( st->li.generated==NULL )
    2335           0 : return( false );
    2336           0 :     start = SFTF_NormalizeStartEnd(st, start, &end);
    2337           0 :     fl = LI_BreakFontList(&st->li,start,end);
    2338           0 :     while ( fl!=NULL && fl->end<=end ) {
    2339           0 :         if ( fl->fd->pointsize!=pointsize ) {
    2340           0 :             cur = LI_FindFontData(&st->li, fl->fd->sf, fl->fd->layer, fl->fd->fonttype, pointsize, fl->fd->antialias);
    2341           0 :             if ( cur!=NULL )
    2342           0 :                 fl->fd = cur;
    2343             :         }
    2344           0 :         fl = fl->next;
    2345             :     }
    2346             : 
    2347           0 :     SFTFMetaChangeCleanup(st,start,end);
    2348           0 : return( true );
    2349             : }
    2350             : 
    2351           0 : int SFTFSetAntiAlias(GGadget *g, int start, int end, int antialias) {
    2352           0 :     SFTextArea *st = (SFTextArea *) g;
    2353             :     FontData *cur;
    2354             :     struct fontlist *fl;
    2355             : 
    2356           0 :     start = SFTF_NormalizeStartEnd(st, start, &end);
    2357           0 :     fl = LI_BreakFontList(&st->li,start,end);
    2358           0 :     while ( fl!=NULL && fl->end<=end ) {
    2359           0 :         if ( fl->fd->antialias!=antialias ) {
    2360           0 :             cur = LI_FindFontData(&st->li, fl->fd->sf, fl->fd->layer, fl->fd->fonttype, fl->fd->pointsize, antialias);
    2361           0 :             if ( cur!=NULL )
    2362           0 :                 fl->fd = cur;
    2363             :         }
    2364           0 :         fl = fl->next;
    2365             :     }
    2366             : 
    2367           0 :     SFTFMetaChangeCleanup(st,start,end);
    2368           0 : return( true );
    2369             : }
    2370             : 
    2371           0 : int SFTFSetScriptLang(GGadget *g, int start, int end, uint32 script, uint32 lang) {
    2372           0 :     SFTextArea *st = (SFTextArea *) g;
    2373             :     struct fontlist *fl;
    2374             : 
    2375           0 :     start = SFTF_NormalizeStartEnd(st, start, &end);
    2376           0 :     fl = LI_BreakFontList(&st->li,start,end);
    2377           0 :     while ( fl!=NULL && fl->end<=end ) {
    2378           0 :         if ( fl->script != script ) {
    2379           0 :             free(fl->feats);
    2380           0 :             fl->feats = LI_TagsCopy(StdFeaturesOfScript(script));
    2381             :         }
    2382           0 :         fl->script = script;
    2383           0 :         fl->lang = lang;
    2384           0 :         fl = fl->next;
    2385             :     }
    2386             : 
    2387           0 :     SFTFMetaChangeCleanup(st,start,end);
    2388           0 : return( true );
    2389             : }
    2390             : 
    2391           0 : int SFTFSetFeatures(GGadget *g, int start, int end, uint32 *features) {
    2392           0 :     SFTextArea *st = (SFTextArea *) g;
    2393             :     struct fontlist *fl;
    2394             : 
    2395           0 :     start = SFTF_NormalizeStartEnd(st, start, &end);
    2396           0 :     fl = LI_BreakFontList(&st->li,start,end);
    2397           0 :     while ( fl!=NULL && fl->end<=end ) {
    2398           0 :         free(fl->feats);
    2399           0 :         fl->feats = LI_TagsCopy(features);
    2400           0 :         fl = fl->next;
    2401             :     }
    2402             : 
    2403           0 :     SFTFMetaChangeCleanup(st,start,end);
    2404           0 : return( true );
    2405             : }
    2406             : 
    2407           0 : void SFTFRegisterCallback(GGadget *g, void *cbcontext,
    2408             :         void (*changefontcallback)(void *,SplineFont *,enum sftf_fonttype,int size,int aa, uint32 script, uint32 lang, uint32 *feats)) {
    2409           0 :     SFTextArea *st = (SFTextArea *) g;
    2410             : 
    2411           0 :     st->cbcontext = cbcontext;
    2412           0 :     st->changefontcallback = changefontcallback;
    2413           0 : }
    2414             : 
    2415           0 : void SFTFProvokeCallback(GGadget *g) {
    2416           0 :     SFTextArea *st = (SFTextArea *) g;
    2417           0 :     STChangeCheck(st);
    2418           0 : }
    2419             : 
    2420           0 : void SFTFSetDPI(GGadget *g, float dpi) {
    2421           0 :     SFTextArea *st = (SFTextArea *) g;
    2422             :     FontData *fd;
    2423             : 
    2424           0 :     if ( st->li.dpi == dpi )
    2425           0 : return;
    2426           0 :     st->li.dpi = dpi;
    2427           0 :     for ( fd = st->li.generated; fd!=NULL; fd=fd->next ) {
    2428           0 :         LI_RegenFontData(&st->li,fd);
    2429             :     }
    2430           0 :     SFTextAreaRefigureLines(st,0,-1);
    2431           0 :     SFTextAreaShow(&st->g,st->sel_start);     /* Refigure scrollbars for new size */
    2432             :             /* And force an expose event */
    2433             : }
    2434             : 
    2435           0 : float SFTFGetDPI(GGadget *g) {
    2436           0 :     SFTextArea *st = (SFTextArea *) g;
    2437             : 
    2438           0 : return( st->li.dpi );
    2439             : }
    2440             : 
    2441           0 : void SFTFRefreshFonts(GGadget *g) {
    2442           0 :     SFTextArea *st = (SFTextArea *) g;
    2443             :     FontData *fd;
    2444             :     struct sfmaps *sfmaps;
    2445             : 
    2446             :     /* First regenerate the EncMaps. Glyphs might have been added or removed */
    2447           0 :     for ( sfmaps = st->li.sfmaps; sfmaps!=NULL; sfmaps = sfmaps->next ) {
    2448           0 :         EncMapFree(sfmaps->map);
    2449           0 :         SplineCharFree(sfmaps->fake_notdef);
    2450           0 :         sfmaps->fake_notdef = NULL;
    2451           0 :         SFMapFill(sfmaps,sfmaps->sf);
    2452             :     }
    2453             : 
    2454             :     /* Then free all old generated bitmaps */
    2455             :     /* need to do this first because otherwise we might reuse a freetype context */
    2456           0 :     for ( fd = st->li.generated; fd!=NULL; fd=fd->next ) {
    2457           0 :         if ( fd->depends_on )
    2458           0 :             fd->bdf->freetype_context = NULL;
    2459           0 :         if ( fd->fonttype!=sftf_bitmap ) {
    2460           0 :             BDFFontFree(fd->bdf);
    2461           0 :             fd->bdf = NULL;
    2462             :         }
    2463             :     }
    2464           0 :     for ( fd = st->li.generated; fd!=NULL; fd=fd->next ) {
    2465           0 :         LI_RegenFontData(&st->li,fd);
    2466             :     }
    2467           0 :     LayoutInfoRefigureLines(&st->li,0,-1,st->g.inner.width);
    2468           0 :     SFTextAreaShow(&st->g,st->sel_start);     /* Refigure scrollbars for new size */
    2469             :             /* And force an expose event */
    2470           0 : }

Generated by: LCOV version 1.10