1 /* stringgadget.cc
2  * This file belongs to Worker, a file manager for UN*X/X11.
3  * Copyright (C) 2001-2019 Ralf Hoffmann.
4  * You can contact me at: ralf@boomerangsworld.de
5  *   or http://www.boomerangsworld.de/worker
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "stringgadget.h"
23 #include "awindow.h"
24 #include "guielement.h"
25 #include "utf8.hh"
26 #include "drawablecont.hh"
27 #include "util.h"
28 
29 const char *StringGadget::type="StringGadget";
30 
~StringGadget()31 StringGadget::~StringGadget()
32 {
33   _aguix->cancelCutPaste( this );
34   destroy();
35 }
36 
StringGadget(AGUIX * taguix,int tx,int ty,int width,int height,const char * ttext,int tdata)37 StringGadget::StringGadget(AGUIX *taguix,int tx,int ty,int width,int height,const char *ttext,int tdata):GUIElement(taguix)
38 {
39   this->data=tdata;
40   _x=tx;
41   _y=ty;
42   if ( width > 8 )
43     _w = width;
44   else
45     _w = 8;
46   if ( height > 8 )
47     _h = height;
48   else
49     _h = 8;
50   active=false;
51   content.setText( ttext );
52 
53   font=NULL;
54   forbidPosChange=false;
55   strongkeycapture=true;
56   wantpaste=false;
57   pasterequest=0;
58   setCanHandleFocus();
59   setAcceptFocus( true );
60   passwordMode = false;
61   m_read_only = false;
62 }
63 
StringGadget(AGUIX * taguix,int tx,int ty,int width,const char * ttext,int tdata)64 StringGadget::StringGadget(AGUIX *taguix,int tx,int ty,int width,const char *ttext,int tdata):GUIElement(taguix)
65 {
66   int bw;
67 
68   bw = getBorderWidth();
69   this->data=tdata;
70   _x=tx;
71   _y=ty;
72   if ( width > 8 )
73     _w = width;
74   else
75     _w = 8;
76 
77   _h = taguix->getCharHeight() + 2 * bw;
78   active=false;
79   content.setText( ttext );
80 
81   font=NULL;
82   forbidPosChange=false;
83   strongkeycapture=true;
84   wantpaste=false;
85   pasterequest=0;
86   setCanHandleFocus();
87   setAcceptFocus( true );
88   passwordMode = false;
89   m_read_only = false;
90 }
91 
resize(int tw,int th)92 void StringGadget::resize(int tw,int th)
93 {
94   if ( tw < 8 ) tw = 8;
95   if ( th < 8 ) th = 8;
96   _w=tw;
97   _h=th;
98   if ( isCreated() == true ) {
99     _parent->resizeSubWin(win,tw,th);
100   }
101   updateWin();
102   redraw();
103 }
104 
getData() const105 int StringGadget::getData() const
106 {
107   return data;
108 }
109 
setData(int tdata)110 void StringGadget::setData(int tdata)
111 {
112   this->data=tdata;
113 }
114 
redraw()115 void StringGadget::redraw()
116 {
117   if ( isCreated() == true ) {
118     prepareBG();
119 
120     _aguix->ClearWin(win);
121 
122     _aguix->setFG( _aguix->getFaceCol_3d_bright() );
123     _aguix->DrawLine(win,0,_h-1,0,0);
124     _aguix->DrawLine(win,0,0,_w-1,0);
125     _aguix->DrawLine(win,2,_h-1-2,_w-1-2,_h-1-2);
126     _aguix->DrawLine(win,_w-1-2,_h-1-2,_w-1-2,2);
127     _aguix->setFG( _aguix->getFaceCol_3d_dark() );
128     _aguix->DrawLine(win,0,_h-1,_w-1,_h-1);
129     _aguix->DrawLine(win,_w-1,_h-1,_w-1,0);
130     _aguix->DrawLine(win,2,_h-1-2,2,2);
131     _aguix->DrawLine(win,2,2,_w-1-2,2);
132     textRedraw();
133   }
134 }
135 
textRedraw()136 void StringGadget::textRedraw()
137 {
138   int minx, maxx;
139   int bw = getBorderWidth();
140   int inner_x, inner_y;
141 
142   if ( isCreated() == false ) return;
143 
144   clearTextBG();
145 
146   inner_x = inner_y = getBorderWidth();
147 
148   int ch;
149   if(font==NULL) {
150     ch=_aguix->getCharHeight();
151   } else {
152     ch=font->getCharHeight();
153   }
154 
155   char *use_text = NULL;
156   int use_xoffset = 0, use_selstart = 0, use_selend = 0;
157 
158   use_xoffset = content.getXOffset();
159   use_selstart = content.getSelStart();
160   use_selend = content.getSelEnd();
161   use_text = dupstring( content.getText() );
162 
163   int use_text_len = strlen( use_text );
164 
165   int dx;
166   dx=1;
167   double f1 = ( ( _h - 2 * bw ) / 2 ) - ( ch / 2 );
168   int dy=(int)f1;
169   int x1,x2;
170   int temp_ch_pos;
171   char temp_ch;
172   int text_color;
173 
174   x1 = ( use_selstart <= use_selend ? use_selstart : use_selend );
175   x2 = ( use_selstart > use_selend ? use_selstart : use_selend );
176   if ( x2 == use_text_len && use_selstart != use_selend ) {
177     UTF8::movePosToPrevChar( use_text, x2 );
178   }
179 
180   if ( x1 < 0 || x1 > use_text_len ) x1 = 0;
181   if ( x2 < 0 || x2 > use_text_len ) x2 = 0;
182   if ( x2 < x1 ) x2 = x1;
183 
184   int restwidth = getInnerWidth() - 2;
185   int strwidth, vislen;
186   bool cont = true;
187 
188   DrawableCont dc( _aguix, win );
189 
190   if( use_xoffset < x1 ) {
191     // links vom x1 ist noch Text
192     temp_ch = use_text[x1];
193     temp_ch_pos = x1;
194     use_text[x1] = '\0';
195     vislen = _aguix->getStrlen4Width( use_text + use_xoffset, restwidth, &strwidth, font );
196 
197     // use_xoffset + vislen is equal or less x1 in any case
198     if ( use_text[use_xoffset + vislen] != '\0' ) {
199       use_text[temp_ch_pos] = temp_ch;
200       temp_ch = use_text[use_xoffset + vislen];
201       temp_ch_pos = use_xoffset + vislen;
202       use_text[temp_ch_pos] = '\0';
203 
204       // doesn't fit completely so don't draw anything else
205       cont = false;
206     }
207 
208     if ( vislen > 0 ) {
209       if ( active ) {
210           text_color = _aguix->getFaces().getColor( "stringgadget-active-fg" );
211       } else {
212           text_color = _aguix->getFaces().getColor( "stringgadget-normal-fg" );
213       }
214 
215       _aguix->DrawText( dc, font, use_text + use_xoffset, inner_x + dx, inner_y + dy, text_color );
216     }
217 
218     use_text[temp_ch_pos] = temp_ch;
219 
220     dx += strwidth;
221     restwidth -= strwidth;
222   }
223 
224   if ( ( cont == true ) && ( x2 >= use_xoffset ) ) {
225     // some part of the selection is visible
226 
227     minx = ( x1 < use_xoffset ) ? use_xoffset : x1;
228     maxx = x2;
229     if ( use_text[maxx] != '\0' ) {
230       UTF8::movePosToNextChar( use_text, maxx );
231     }
232 
233     // draw selection from minx to maxx which can be outside screen
234 
235     temp_ch = use_text[maxx];
236     temp_ch_pos = maxx;
237     use_text[maxx] = '\0';
238 
239     vislen = _aguix->getStrlen4Width( use_text + minx, restwidth, &strwidth, font );
240 
241     if ( use_text[minx + vislen] != '\0' ) {
242       use_text[temp_ch_pos] = temp_ch;
243       temp_ch = use_text[minx + vislen];
244       temp_ch_pos = minx + vislen;
245       use_text[temp_ch_pos] = '\0';
246 
247       // doesn't fit completely so don't draw anything else
248       cont = false;
249     }
250 
251     if ( ( vislen > 0 ) || ( ( minx + vislen ) == use_text_len ) ) {
252         if(active==true) {
253             // draw selection area
254             _aguix->setFG( _aguix->getFaces().getColor( "stringgadget-selection-bg" ) );
255             _aguix->FillRectangle( win, inner_x + dx, inner_y + dy, strwidth, ch );
256         }
257         if(active==true) {
258             text_color = _aguix->getFaces().getColor( "stringgadget-selection-fg" );
259         } else {
260             text_color = _aguix->getFaces().getColor( "stringgadget-normal-fg" );
261         }
262         _aguix->DrawText( dc, font, use_text + minx, inner_x + dx, inner_y + dy, text_color );
263     }
264 
265     use_text[temp_ch_pos] = temp_ch;
266 
267     dx += strwidth;
268     restwidth -= strwidth;
269   }
270 
271   if ( ( cont == true ) && ( use_text[x2] != '\0' ) ) {
272     // the rest after the selection is possibly visible
273     int first_vis_char;
274 
275     // end of selection can be less than use_xoffset
276     first_vis_char = x2;
277     UTF8::movePosToNextChar( use_text, first_vis_char );
278     first_vis_char = a_max( first_vis_char, use_xoffset );
279 
280     vislen = _aguix->getStrlen4Width( use_text + first_vis_char, restwidth, &strwidth, font );
281 
282     if ( vislen > 0 ) {
283       use_text[first_vis_char + vislen] = '\0';
284 
285       if ( active ) {
286           text_color = _aguix->getFaces().getColor( "stringgadget-active-fg" );
287       } else {
288           text_color = _aguix->getFaces().getColor( "stringgadget-normal-fg" );
289       }
290 
291       _aguix->DrawText( dc, font, use_text + first_vis_char, inner_x + dx, inner_y + dy, text_color );
292     }
293   }
294   _freesafe(use_text);
295   _aguix->Flush();
296 }
297 
flush()298 void StringGadget::flush()
299 {
300 }
301 
isInside(int px,int py) const302 bool StringGadget::isInside(int px,int py) const
303 {
304 /*  if((px>x)&&(px<=(x+width))) {
305     if((py>y)&&(py<=(y+height))) return true;
306   }*/
307   return false;
308 }
309 
handleMessage(XEvent * E,Message * msg)310 bool StringGadget::handleMessage(XEvent *E,Message *msg)
311 {
312     bool returnvalue;
313     returnvalue=false;
314     int tx,ty;
315     AGMessage *agmsg;
316     int oldselstart;
317 
318     if ( isCreated() == false ) return false;
319 
320     returnvalue = GUIElement::handleMessage( E, msg );
321 
322     if(active==true) {
323         if(msg->type==ButtonPress) {
324             if ( msg->window != win ) {
325                 active=false;
326                 redraw();
327                 //        returnvalue=true;
328                 agmsg = AGUIX_allocAGMessage();
329                 agmsg->type=AG_STRINGGADGET_DEACTIVATE;
330                 agmsg->stringgadget.sg=this;
331                 agmsg->stringgadget.ok = false;
332                 msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
333             } else {
334                 if(forbidPosChange==false) {
335                     _aguix->queryPointer(msg->window,&tx,&ty);
336 
337                     int bw = getBorderWidth();
338                     tx -= bw;
339                     ty -= bw;
340 
341                     int l, strwidth;
342                     l = _aguix->getStrlen4Width( content.getText() + content.getXOffset(), a_max( tx - 2, 0 ), &strwidth, font );
343 
344                     int new_cursorpos = l + content.getXOffset();
345                     if ( new_cursorpos >= (int)strlen( content.getText() ) ) new_cursorpos = (int)strlen( content.getText() );
346                     if ( new_cursorpos < 0 ) new_cursorpos = 0;
347                     content.setCursorPos( new_cursorpos );
348                     content.setSelStart( new_cursorpos );
349                     content.setSelEnd( new_cursorpos );
350                     if ( ! m_read_only && msg->button == Button2 ) {
351                         insertSelection();
352                     }
353                     redraw();
354                 }
355             }
356         } else {
357             if(msg->type==KeyPress) {
358                 if ( isVisible() == true ) {
359                     if ( _parent->isTopParent( msg->window ) == true ) {
360                         ignoreRelease=false;
361                         int keystate=KEYSTATEMASK(msg->keystate);
362                         if((msg->key==XK_Right)||
363                            (msg->key==XK_End)||
364                            ((msg->key==XK_e)&&(keystate==ControlMask))||
365                            ((msg->key==XK_f)&&(keystate==ControlMask))) {
366                             if(strongkeycapture==true) returnvalue=true;
367                             if(forbidPosChange==false) {
368                                 if((((keystate==0)||(keystate==ShiftMask))&&
369                                     (msg->key==XK_Right))||
370                                    ((msg->key==XK_f)&&(keystate==ControlMask))) {
371                                     if(content.getCursorPos()<(int)strlen( content.getText() )) {
372                                         oldselstart = content.getSelStart();
373                                         int new_cur = content.getCursorPos();
374                                         UTF8::movePosToNextChar( content.getText(), new_cur );
375 
376                                         //TODO if next character is invalid setCursor will go back to prev
377                                         //  so effectively don't change the cursor at all
378                                         setCursor( new_cur );
379 
380                                         content.setSelStart( oldselstart );
381                                         if(keystate!=ShiftMask) {
382                                             content.setSelStart( content.getCursorPos() );
383                                             content.setSelEnd( content.getCursorPos() );
384                                         } else {
385                                             content.setSelEnd( content.getCursorPos() );
386                                             applySelection();
387                                         }
388                                         textRedraw();
389                                         returnvalue=true;
390                                     } else if ( ( content.getSelStart() != content.getSelEnd() ) && ( keystate != ShiftMask ) ) {
391                                         // selection active and cursor is right
392                                         // => deactivate selection
393                                         content.setSelStart( content.getCursorPos() );
394                                         content.setSelEnd( content.getCursorPos() );
395                                         textRedraw();
396                                         returnvalue = true;
397                                     }
398                                 } else if(((keystate&Mod1Mask)==Mod1Mask)||
399                                           (msg->key==XK_End)||
400                                           ((msg->key==XK_e)&&(keystate==ControlMask))) {
401                                     oldselstart = content.getSelStart();
402                                     setCursor( (int)strlen( content.getText() ) );
403                                     content.setSelStart( oldselstart );
404                                     if((keystate&ShiftMask)==0) {
405                                         content.setSelStart( content.getCursorPos() );
406                                         content.setSelEnd( content.getCursorPos() );
407                                     } else {
408                                         content.setSelEnd( content.getCursorPos() );
409                                         applySelection();
410                                     }
411                                     textRedraw();
412                                     returnvalue=true;
413                                 }
414                                 agmsg = AGUIX_allocAGMessage();
415                                 agmsg->type=AG_STRINGGADGET_CURSORCHANGE;
416                                 agmsg->stringgadget.sg=this;
417                                 msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
418                             }
419                         } else if((msg->key==XK_Left)||
420                                   (msg->key==XK_Home)||
421                                   ((msg->key==XK_a)&&(keystate==ControlMask))||
422                                   ((msg->key==XK_b)&&(keystate==ControlMask))) {
423                             if(strongkeycapture==true) returnvalue=true;
424                             if(forbidPosChange==false) {
425                                 if((((keystate==0)||(keystate==ShiftMask))&&
426                                     (msg->key==XK_Left))||
427                                    ((msg->key==XK_b)&&(keystate==ControlMask))) {
428                                     if(content.getCursorPos()>0) {
429                                         oldselstart = content.getSelStart();
430                                         int new_cur = content.getCursorPos();
431 
432                                         UTF8::movePosToPrevChar( content.getText(), new_cur );
433                                         setCursor( new_cur );
434 
435                                         content.setSelStart( oldselstart );
436                                         if(keystate!=ShiftMask) {
437                                             content.setSelStart( content.getCursorPos() );
438                                             content.setSelEnd( content.getCursorPos() );
439                                         } else {
440                                             content.setSelEnd( content.getCursorPos() );
441                                             applySelection();
442                                         }
443                                         textRedraw();
444                                         returnvalue=true;
445                                     } else if ( ( content.getSelStart() != content.getSelEnd() ) && ( keystate != ShiftMask ) ) {
446                                         // selection active and normal cursor left
447                                         // => deactivate selection
448                                         content.setSelStart( content.getCursorPos() );
449                                         content.setSelEnd( content.getCursorPos() );
450                                         textRedraw();
451                                         returnvalue = true;
452                                     }
453                                 } else if(((keystate&Mod1Mask)==Mod1Mask)||
454                                           (msg->key==XK_Home)||
455                                           ((msg->key==XK_a)&&(keystate==ControlMask))) {
456                                     content.setXOffset( 0 );
457                                     content.setCursorPos( 0 );
458                                     if((keystate&ShiftMask)==0) {
459                                         content.setSelStart( content.getCursorPos() );
460                                         content.setSelEnd( content.getCursorPos() );
461                                     } else {
462                                         content.setSelEnd( content.getCursorPos() );
463                                         applySelection();
464                                     }
465                                     textRedraw();
466                                     returnvalue=true;
467                                 }
468                                 agmsg = AGUIX_allocAGMessage();
469                                 agmsg->type=AG_STRINGGADGET_CURSORCHANGE;
470                                 agmsg->stringgadget.sg=this;
471                                 msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
472                             }
473                         } else if ( msg->key == XK_Delete ||
474                                     ( msg->key == XK_d && keystate == ControlMask ) ) {
475                             int len=strlen( content.getText() );
476                             if(strongkeycapture==true) returnvalue=true;
477 
478                             if ( ! m_read_only ) {
479                                 if(content.getSelStart()!=content.getSelEnd()) {
480                                     removeSelection();
481 
482                                     returnvalue = true;
483                                     agmsg = AGUIX_allocAGMessage();
484                                     agmsg->type = AG_STRINGGADGET_CONTENTCHANGE;
485                                     agmsg->stringgadget.sg = this;
486                                     msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
487                                 } else if((content.getCursorPos()<len)&&(len>0)) {
488                                     content.removeRange( content.getCursorPos(), content.getCursorPos() );
489                                     returnvalue=true;
490                                     agmsg = AGUIX_allocAGMessage();
491                                     agmsg->type=AG_STRINGGADGET_CONTENTCHANGE;
492                                     agmsg->stringgadget.sg=this;
493                                     msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
494                                 }
495                             }
496                             textRedraw();
497                         } else if ( msg->key == XK_BackSpace ||
498                                     ( msg->key == XK_h && keystate == ControlMask ) ) {
499                             if(strongkeycapture==true) returnvalue=true;
500 
501                             if ( ! m_read_only ) {
502                                 if(content.getSelEnd()!=content.getSelStart()) {
503                                     removeSelection();
504 
505                                     returnvalue = true;
506                                     agmsg = AGUIX_allocAGMessage();
507                                     agmsg->type = AG_STRINGGADGET_CONTENTCHANGE;
508                                     agmsg->stringgadget.sg = this;
509                                     msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
510                                 } else if(content.getCursorPos()>0) {
511                                     int prev_char = content.getCursorPos();
512                                     UTF8::movePosToPrevChar( content.getText(), prev_char );
513 
514                                     content.removeRange( prev_char, prev_char );
515 
516                                     setCursor( content.getCursorPos() );
517 
518                                     content.setSelStart( content.getCursorPos() );
519                                     content.setSelEnd( content.getCursorPos() );
520                                     returnvalue=true;
521                                     agmsg = AGUIX_allocAGMessage();
522                                     agmsg->type=AG_STRINGGADGET_CONTENTCHANGE;
523                                     agmsg->stringgadget.sg=this;
524                                     msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
525                                 }
526                             }
527                             textRedraw();
528                         } else if ( msg->key == XK_Return ) {
529                             if ( strongkeycapture == true ) returnvalue = true;
530                         } else if ( msg->key == XK_x && keystate == ControlMask ) {
531                             if ( strongkeycapture == true ) returnvalue = true;
532 
533                             if ( ! m_read_only ) {
534                                 applySelection();
535                                 removeSelection();
536                             }
537 
538                             textRedraw();
539                         } else if ( msg->key == XK_c && keystate == ControlMask ) {
540                             if ( strongkeycapture == true ) returnvalue = true;
541 
542                             applySelection();
543                         } else if ( msg->key == XK_v && keystate == ControlMask ) {
544                             if ( strongkeycapture == true ) returnvalue = true;
545 
546                             if ( ! m_read_only ) {
547                                 insertSelection();
548                             }
549 
550                             textRedraw();
551                         } else if ( ( msg->key == XK_f ||
552                                       msg->key == XK_F ) && ( keystate & Mod1Mask ) == Mod1Mask ) {
553                             if ( strongkeycapture == true ) returnvalue = true;
554 
555                             if ( ! forbidPosChange ) {
556                                 int oldselstart = content.getSelStart();
557 
558                                 jumpToNextWord();
559 
560                                 content.setSelStart( oldselstart );
561                                 if ( ! ( keystate & ShiftMask ) ) {
562                                     content.setSelStart( content.getCursorPos() );
563                                     content.setSelEnd( content.getCursorPos() );
564                                 } else {
565                                     content.setSelEnd( content.getCursorPos() );
566                                     applySelection();
567                                 }
568                                 textRedraw();
569                             }
570                         } else if ( ( msg->key == XK_b ||
571                                       msg->key == XK_B ) && ( keystate & Mod1Mask ) == Mod1Mask ) {
572                             if ( strongkeycapture == true ) returnvalue = true;
573 
574                             if ( ! forbidPosChange ) {
575                                 int oldselstart = content.getSelStart();
576 
577                                 jumpToPrevWord();
578 
579                                 content.setSelStart( oldselstart );
580                                 if ( ! ( keystate & ShiftMask ) ) {
581                                     content.setSelStart( content.getCursorPos() );
582                                     content.setSelEnd( content.getCursorPos() );
583                                 } else {
584                                     content.setSelEnd( content.getCursorPos() );
585                                     applySelection();
586                                 }
587                                 textRedraw();
588                             }
589                         } else if ( IsModifierKey( msg->key ) ||
590                                     IsCursorKey( msg->key ) ||
591                                     IsPFKey( msg->key ) ||
592                                     IsFunctionKey( msg->key ) ||
593                                     IsMiscFunctionKey( msg->key ) ||
594                                     ( ( msg->key >= XK_BackSpace ) && ( msg->key <= XK_Escape ) ) ) {
595                             // catch this special keys even they are not handled
596                             // so they will not be added to the text
597                         } else if ( strlen( msg->keybuf ) > 0 && ( keystate & ControlMask ) == 0 ) {
598                             if ( ! m_read_only ) {
599                                 if ( content.getSelStart() != content.getSelEnd() ) {
600                                     removeSelection();
601                                 }
602                                 if ( UTF8::isValidCharacterString( msg->keybuf ) == true ) {
603                                     content.insertAtCursor( msg->keybuf );
604                                     int nr_of_char = UTF8::getNumberOfCharacters( msg->keybuf );
605                                     int new_cur = content.getCursorPos();
606                                     while ( nr_of_char > 0 ) {
607                                         UTF8::movePosToNextChar( content.getText(), new_cur );
608                                         nr_of_char--;
609                                     }
610                                     setCursor( new_cur );
611                                     content.setSelStart( content.getCursorPos() );
612                                     content.setSelEnd( content.getCursorPos() );
613                                 }
614                                 textRedraw();
615                                 agmsg = AGUIX_allocAGMessage();
616                                 agmsg->type = AG_STRINGGADGET_CONTENTCHANGE;
617                                 agmsg->stringgadget.sg = this;
618                                 msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
619                             }
620                             returnvalue = true;
621                         }
622                     }
623                 }
624             } else if((msg->type==KeyRelease)&&(ignoreRelease==false)) {
625                 if ( isVisible() == true ) {
626                     if ( _parent->isTopParent( msg->window ) == true ) {
627                         int keystate=KEYSTATEMASK(msg->keystate);
628                         if((msg->key==XK_Return)||(msg->key==XK_Escape)) {
629                             active=false;
630                             content.setSelStart( content.getCursorPos() );
631                             content.setSelEnd( content.getCursorPos() );
632                             redraw();
633                             returnvalue=true;
634                             agmsg = AGUIX_allocAGMessage();
635                             agmsg->type=AG_STRINGGADGET_DEACTIVATE;
636                             agmsg->stringgadget.sg=this;
637                             agmsg->stringgadget.ok = ( msg->key == XK_Return ) ? true : false;
638                             msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
639                             if(msg->key==XK_Return) {
640                                 agmsg = AGUIX_allocAGMessage();
641                                 agmsg->type=AG_STRINGGADGET_OK;
642                                 agmsg->stringgadget.sg=this;
643                                 agmsg->stringgadget.keystate = keystate;
644                                 msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
645                             } else {
646                                 agmsg = AGUIX_allocAGMessage();
647                                 agmsg->type=AG_STRINGGADGET_CANCEL;
648                                 agmsg->stringgadget.sg=this;
649                                 msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
650                             }
651                         } else if((msg->key==XK_Right)||
652                                   (msg->key==XK_End)||
653                                   ((msg->key==XK_e)&&(keystate==ControlMask))||
654                                   ((msg->key==XK_f)&&(keystate==ControlMask))) {
655                             if(strongkeycapture==true) returnvalue=true;
656                         } else if((msg->key==XK_Left)||
657                                   (msg->key==XK_Home)||
658                                   ((msg->key==XK_a)&&(keystate==ControlMask))||
659                                   ((msg->key==XK_b)&&(keystate==ControlMask))) {
660                             if(strongkeycapture==true) returnvalue=true;
661                         } else if((msg->key==XK_Delete)||((msg->key==XK_d)&&(keystate==ControlMask))) {
662                             if(strongkeycapture==true) returnvalue=true;
663                         } else if((msg->key==XK_BackSpace)||((msg->key==XK_h)&&(keystate==ControlMask))) {
664                             if(strongkeycapture==true) returnvalue=true;
665                         } else if ( IsModifierKey( msg->key ) ||
666                                     IsCursorKey( msg->key ) ||
667                                     IsPFKey( msg->key ) ||
668                                     IsFunctionKey( msg->key ) ||
669                                     IsMiscFunctionKey( msg->key ) ||
670                                     ( ( msg->key >= XK_BackSpace ) && ( msg->key <= XK_Escape ) ) ) {
671                             // Some of these keys could create a char in keybuf but because we don't
672                             // handle them here do nothing
673                         } else if ( ( strlen( msg->keybuf ) > 0 ) && ( keystate != ControlMask ) ) {
674                             if(strongkeycapture==true) returnvalue=true;
675                         }
676                     }
677                 }
678             } else if(msg->type==ButtonRelease) {
679                 applySelection();
680             } else if ( msg->type == MotionNotify &&
681                         msg->window == win &&
682                         ( msg->keystate & Button1Mask ) == Button1Mask ) {
683                 _aguix->queryPointer(msg->window,&tx,&ty);
684 
685                 int bw = getBorderWidth();
686                 tx -= bw;
687                 ty -= bw;
688 
689                 if ( ( tx < 0 ) && ( content.getXOffset() > 0 ) ){
690                     int val = content.getXOffset();
691                     UTF8::movePosToPrevChar( content.getText(), val );
692                     content.setXOffset( val );
693                     content.setCursorPos( val );
694                 } else if ( tx >= getInnerWidth() ) {
695                     int l, strwidth;
696                     l = _aguix->getStrlen4Width( content.getText() + content.getXOffset(), a_min( tx , getInnerWidth() ) - 2, &strwidth, font );
697 
698                     content.setCursorPos( l + content.getXOffset() );
699                     if ( content.getCursorPos() < (int)strlen( content.getText() ) ) {
700                         int val = content.getXOffset();
701                         UTF8::movePosToNextChar( content.getText(), val );
702                         content.setXOffset( val );
703                     }
704                 } else {
705                     int l, strwidth;
706                     l = _aguix->getStrlen4Width( content.getText() + content.getXOffset(), tx - 2, &strwidth, font );
707 
708                     int ncp = l + content.getXOffset();
709                     if(ncp>=(int)strlen(content.getText())) ncp=strlen(content.getText());
710                     if(ncp<0) ncp=0;
711                     content.setCursorPos( ncp );
712                 }
713 
714                 content.setSelEnd( content.getCursorPos() );
715                 redraw();
716             }
717         }
718     } else if(msg->type==ButtonPress) {
719         if ( msg->window == win ) {
720             takeFocus();
721             active=true;
722             //      returnvalue=true;
723             agmsg = AGUIX_allocAGMessage();
724             agmsg->type=AG_STRINGGADGET_ACTIVATE;
725             agmsg->stringgadget.sg=this;
726             msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
727             if(forbidPosChange==false) {
728                 _aguix->queryPointer(msg->window,&tx,&ty);
729 
730                 int bw = getBorderWidth();
731                 tx -= bw;
732                 ty -= bw;
733 
734                 int l, strwidth;
735                 l = _aguix->getStrlen4Width( content.getText() + content.getXOffset(), a_max( tx - 2, 0 ), &strwidth, font );
736                 content.setCursorPos( l + content.getXOffset() );
737                 if(content.getCursorPos()>=(int)strlen(content.getText())) content.setCursorPos( (int)strlen(content.getText()) );
738                 if(content.getCursorPos()<0) content.setCursorPos( 0 );
739                 content.setSelStart( content.getCursorPos() );
740                 content.setSelEnd( content.getCursorPos() );
741                 if(msg->button==Button2) insertSelection();
742             }
743             redraw();
744         }
745     }
746     if(msg->type==Expose) {
747         if ( msg->window == win ) {
748             redraw();
749         }
750     }
751     return returnvalue;
752 }
753 
setText(const char * new_text)754 void StringGadget::setText(const char *new_text)
755 {
756   content.setText( new_text );
757 
758   content.setSelStart( content.getCursorPos() );
759   content.setSelEnd( content.getCursorPos() );
760   setXOffset( content.getXOffset() );
761 
762   textRedraw();
763 }
764 
getText() const765 const char *StringGadget::getText() const
766 {
767   return content.getRealText();
768 }
769 
isActive() const770 bool StringGadget::isActive() const
771 {
772   return active;
773 }
774 
updateWin()775 void StringGadget::updateWin()
776 {
777   int i1;
778 
779   if ( isCreated() == true ) {
780     i1 = a_min( content.getSelStart(), content.getSelEnd() );
781     setXOffset( i1 );
782     textRedraw();
783   }
784 }
785 
activate()786 void StringGadget::activate()
787 {
788   active=true;
789   ignoreRelease=true;
790   redraw();
791 }
792 
deactivate()793 void StringGadget::deactivate()
794 {
795   active=false;
796   redraw();
797 }
798 
applySelection()799 void StringGadget::applySelection()
800 {
801   int x1,x2;
802   char *buffer;
803 
804   if ( passwordMode == false ) {
805     x1=(content.getSelStart()<=content.getSelEnd()?content.getSelStart():content.getSelEnd());
806     x2=(content.getSelStart()>content.getSelEnd()?content.getSelStart():content.getSelEnd());
807 
808     if ( x1 < x2 ) {
809       // x2 is the end character inside the selection so
810       // move to next character and use this position as
811       // limit
812       if ( x2 < (int)strlen( content.getText() ) ) {
813         UTF8::movePosToNextChar( content.getText(), x2 );
814       }
815 
816       buffer = dupstring( content.getText() + x1 );
817       buffer[x2 - x1] = 0;
818       _aguix->startCut( this, buffer );
819       _freesafe( buffer );
820     }
821   }
822 }
823 
removeSelection()824 void StringGadget::removeSelection()
825 {
826   int x1,x2;
827   x1=(content.getSelStart()<=content.getSelEnd()?content.getSelStart():content.getSelEnd());
828   x2=(content.getSelStart()>content.getSelEnd()?content.getSelStart():content.getSelEnd());
829 
830   content.removeRange( x1, x2 );
831 
832   content.setCursorPos( x1 );
833   content.setSelStart( x1 );
834   content.setSelEnd( x1 );
835   if ( content.getCursorPos() < content.getXOffset() ) content.setXOffset( content.getCursorPos() );
836 }
837 
insertSelection()838 void StringGadget::insertSelection()
839 {
840   if ( isCreated() == false ) return;
841 
842   if ( _aguix->amiOwner() == true ) {
843     std::string s2 = AGUIXUtils::remove_char( _aguix->getCutBuffer(), '\n' );
844     content.insertAtCursor( s2.c_str() );
845   } else {
846     _aguix->requestCut(getWindow());
847     _aguix->startPaste(this);
848     wantpaste=true;
849     pasterequest=time(NULL);
850   }
851 }
852 
setFont(const char * fontname)853 int StringGadget::setFont( const char *fontname )
854 {
855   font=_aguix->getFont(fontname);
856   updateWin();
857   if(font==NULL) return -1;
858   return 0;
859 }
860 
getType() const861 const char *StringGadget::getType() const
862 {
863   return type;
864 }
865 
isType(const char * qtype) const866 bool StringGadget::isType(const char *qtype) const
867 {
868   if(strcmp(type,qtype)==0) return true;
869   return false;
870 }
871 
isParent(Window child) const872 bool StringGadget::isParent(Window child) const
873 {
874   if ( isCreated() == false ) return false;
875   if(child==win) return true;
876   return false;
877 }
878 
getXOffset() const879 int StringGadget::getXOffset() const
880 {
881   return content.getXOffset();
882 }
883 
setXOffset(int new_pos)884 void StringGadget::setXOffset( int new_pos )
885 {
886   int l, strwidth, newx;
887 
888   content.setXOffset( new_pos );
889 
890   newx = content.getXOffset();
891   while ( newx >= 0 ) {
892     l = _aguix->getStrlen4Width( content.getText() + newx, getInnerWidth() - 2, &strwidth, font );
893 
894     if ( ( l + newx ) == (int)strlen( content.getText() ) ) {
895       // everything visible reduce xoffset
896       // lower means not everything is visible, higher is not possible
897       content.setXOffset( newx );
898       UTF8::movePosToPrevChar( content.getText(), newx );
899       if ( content.getXOffset() == newx ) break; // no prev char
900     } else break;
901   }
902 
903   if ( content.getXOffset() < 0 ) content.setXOffset( 0 );
904   textRedraw();
905 }
906 
getCursor() const907 int StringGadget::getCursor() const
908 {
909   return content.getCursorPos();
910 }
911 
setCursor(int new_pos)912 void StringGadget::setCursor( int new_pos )
913 {
914   int newx, l, strwidth;
915 
916   content.setCursorPos( new_pos );
917 
918   content.setSelStart( content.getCursorPos() );
919   content.setSelEnd( content.getCursorPos() );
920 
921   if ( ( content.getCursorPos() - content.getXOffset() ) < 0 )
922     content.setXOffset( content.getCursorPos() );
923 
924   newx = content.getXOffset();
925   for (;;) {
926     l = _aguix->getStrlen4Width( content.getText() + newx, getInnerWidth() - 2, &strwidth, font );
927     if ( ( l + newx ) == (int)strlen( content.getText() ) ) break;
928     else if ( content.getCursorPos() >= ( l + newx ) ) {
929       UTF8::movePosToNextChar( content.getText(), newx );
930     } else break;
931   }
932 
933   setXOffset( newx );
934 }
935 
isPosChangeForbidden() const936 bool StringGadget::isPosChangeForbidden() const
937 {
938   return forbidPosChange;
939 }
940 
setForbidPosChange(bool nv)941 void StringGadget::setForbidPosChange(bool nv)
942 {
943   forbidPosChange=nv;
944 }
945 
setStrongKeyCapture(bool nv)946 void StringGadget::setStrongKeyCapture(bool nv)
947 {
948   strongkeycapture=nv;
949 }
950 
paste(unsigned char * buf)951 void StringGadget::paste(unsigned char*buf)
952 {
953   if((wantpaste==true)&&(difftime(time(NULL),pasterequest)<10.0)) {
954     if(forbidPosChange==false) {
955       content.setSelStart( content.getCursorPos() );
956       content.setSelEnd( content.getCursorPos() );
957 
958       std::string s2 = AGUIXUtils::remove_char( (const char *)buf, '\n' );
959       content.insertAtCursor( s2.c_str() );
960 
961       redraw();
962     }
963   }
964 }
965 
cancelpaste()966 void StringGadget::cancelpaste()
967 {
968   wantpaste=false;
969   pasterequest=0;
970 }
971 
cancelcut()972 void StringGadget::cancelcut()
973 {
974   content.setSelStart( content.getCursorPos() );
975   content.setSelEnd( content.getCursorPos() );
976   redraw();
977 }
978 
doCreateStuff()979 void StringGadget::doCreateStuff()
980 {
981   GUIElement::doCreateStuff();
982 }
983 
doDestroyStuff()984 void StringGadget::doDestroyStuff()
985 {
986   GUIElement::doDestroyStuff();
987 }
988 
insertAtCursor(const char * str)989 void StringGadget::insertAtCursor( const char *str )
990 {
991   if ( str == NULL ) return;
992 
993   content.insertAtCursor( str );
994 
995   textRedraw();
996 }
997 
selectAll(bool copy_selection)998 void StringGadget::selectAll( bool copy_selection )
999 {
1000   content.setCursorPos( (int)strlen(content.getText()) );
1001   setXOffset( content.getCursorPos() );
1002   content.setSelStart( 0 );
1003   content.setSelEnd( content.getCursorPos() );
1004 
1005   if ( copy_selection == true ) {
1006       applySelection();
1007   }
1008   redraw();
1009 }
1010 
setPasswordMode(bool nv)1011 void StringGadget::setPasswordMode( bool nv )
1012 {
1013   passwordMode = nv;
1014   content.setPasswordMode( nv );
1015   redraw();
1016 }
1017 
lostFocus()1018 void StringGadget::lostFocus()
1019 {
1020   if ( active == true ) {
1021     active = false;
1022     redraw();
1023     AGMessage *agmsg = AGUIX_allocAGMessage();
1024     agmsg->type = AG_STRINGGADGET_DEACTIVATE;
1025     agmsg->stringgadget.sg = this;
1026     agmsg->stringgadget.ok = false;
1027     msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
1028   }
1029 }
1030 
gotFocus()1031 void StringGadget::gotFocus()
1032 {
1033   if ( active == false ) {
1034     activate();
1035     AGMessage *agmsg = AGUIX_allocAGMessage();
1036     agmsg->type = AG_STRINGGADGET_ACTIVATE;
1037     agmsg->stringgadget.sg = this;
1038     msgAndCB( std::unique_ptr<AGMessage>( agmsg ) );
1039   }
1040 }
1041 
getInnerWidth() const1042 int StringGadget::getInnerWidth() const
1043 {
1044   int bw = getBorderWidth();
1045   return _w - 2 * bw;
1046 }
1047 
prepareBG(bool force)1048 void StringGadget::prepareBG( bool force )
1049 {
1050   if ( isCreated() == false ) return;
1051   if ( win == 0 ) return;
1052 
1053   _aguix->SetWindowBG( win, _parent->getBG() );
1054 }
1055 
getBorderWidth() const1056 int StringGadget::getBorderWidth() const
1057 {
1058   int bw;
1059 
1060   bw = 4;
1061 
1062   return bw;
1063 }
1064 
clearTextBG()1065 int StringGadget::clearTextBG()
1066 {
1067   if ( isCreated() == false ) return 1;
1068 
1069   int bw = getBorderWidth();
1070   int newbg;
1071 
1072   if ( active == true ) {
1073       newbg = _aguix->getFaces().getColor( "stringgadget-active-bg" );
1074   } else {
1075       newbg = _aguix->getFaces().getColor( "stringgadget-normal-bg" );
1076   }
1077 
1078   _aguix->setFG( newbg );
1079   _aguix->FillRectangle( win, bw, bw, getInnerWidth(), _h - 2 * bw );
1080   return 0;
1081 }
1082 
SGContent()1083 StringGadget::SGContent::SGContent()
1084 {
1085   real_text = "";
1086   UTF8::buildCharacterLookupList( real_text.c_str(), real_char2byte_lookup );
1087 
1088   pw_text = "";
1089   UTF8::buildCharacterLookupList( pw_text.c_str(), pw_char2byte_lookup );
1090 
1091   xoffset = selstart = selend = cursorpos = 0;
1092   password_mode = false;
1093 }
1094 
setXOffset(int np)1095 void StringGadget::SGContent::setXOffset( int np )
1096 {
1097   if ( np != xoffset && np >= 0 ) {
1098     fixPosition( np );
1099     xoffset = np;
1100   }
1101 }
1102 
setSelStart(int np)1103 void StringGadget::SGContent::setSelStart( int np )
1104 {
1105   if ( np != selstart && np >= 0 ) {
1106     fixPosition( np );
1107     selstart = np;
1108   }
1109 }
1110 
setSelEnd(int np)1111 void StringGadget::SGContent::setSelEnd( int np )
1112 {
1113   if ( np != selend && np >= 0 ) {
1114     fixPosition( np );
1115     selend = np;
1116   }
1117 }
1118 
setCursorPos(int np)1119 void StringGadget::SGContent::setCursorPos( int np )
1120 {
1121   if ( np != cursorpos && np >= 0 ) {
1122     fixPosition( np );
1123     cursorpos = np;
1124   }
1125 }
1126 
setPasswordMode(bool mode)1127 void StringGadget::SGContent::setPasswordMode( bool mode )
1128 {
1129   if ( mode == password_mode ) return;
1130 
1131   if ( mode == true ) {
1132     xoffset = real2pwPos( xoffset );
1133     cursorpos = real2pwPos( cursorpos );
1134     selstart = real2pwPos( selstart );
1135     selend = real2pwPos( selend );
1136   } else {
1137     xoffset = pw2realPos( xoffset );
1138     cursorpos = pw2realPos( cursorpos );
1139     selstart = pw2realPos( selstart );
1140     selend = pw2realPos( selend );
1141   }
1142   password_mode = mode;
1143 }
1144 
getText() const1145 const char *StringGadget::SGContent::getText() const
1146 {
1147   if ( password_mode == true ) return pw_text.c_str();
1148   return real_text.c_str();
1149 }
1150 
getXOffset() const1151 int StringGadget::SGContent::getXOffset() const
1152 {
1153   return xoffset;
1154 }
1155 
getSelStart() const1156 int StringGadget::SGContent::getSelStart() const
1157 {
1158   return selstart;
1159 }
1160 
getSelEnd() const1161 int StringGadget::SGContent::getSelEnd() const
1162 {
1163   return selend;
1164 }
1165 
getCursorPos() const1166 int StringGadget::SGContent::getCursorPos() const
1167 {
1168   return cursorpos;
1169 }
1170 
insertAtCursor(const char * text)1171 void StringGadget::SGContent::insertAtCursor( const char *text )
1172 {
1173   if ( text == NULL ) return;
1174 
1175   if ( UTF8::isValidCharacterString( text ) == false ) return;
1176 
1177   int pos = getCursorPos();
1178   std::string new_text = real_text;
1179 
1180   if ( password_mode == true ) {
1181     pos = pw2realPos( pos );
1182   }
1183 
1184   new_text.insert( pos, text );
1185   setText( new_text.c_str() );
1186 }
1187 
setText(const char * text)1188 void StringGadget::SGContent::setText( const char *text )
1189 {
1190   if ( text == NULL ) return;
1191 
1192   if ( UTF8::isValidCharacterString( text ) == false ) return;
1193 
1194   real_text = text;
1195 
1196   real_char2byte_lookup.clear();
1197   UTF8::buildCharacterLookupList( real_text.c_str(), real_char2byte_lookup );
1198 
1199   pw_text = "";
1200   pw_char2byte_lookup.clear();
1201 
1202   int l = real_char2byte_lookup.size() - 1;
1203   pw_text = std::string( l, '*' );
1204   UTF8::buildCharacterLookupList( pw_text.c_str(), pw_char2byte_lookup );
1205 
1206   fixPositions();
1207 }
1208 
getRealText() const1209 const char *StringGadget::SGContent::getRealText() const
1210 {
1211   return real_text.c_str();
1212 }
1213 
pw2realPos(int pos)1214 int StringGadget::SGContent::pw2realPos( int pos )
1215 {
1216   int p = UTF8::findCharacterPosition( pos, pw_char2byte_lookup );
1217   return real_char2byte_lookup[p];
1218 }
1219 
real2pwPos(int pos)1220 int StringGadget::SGContent::real2pwPos( int pos )
1221 {
1222   int p = UTF8::findCharacterPosition( pos, real_char2byte_lookup );
1223   return pw_char2byte_lookup[p];
1224 }
1225 
removeRange(int start,int end)1226 void StringGadget::SGContent::removeRange( int start, int end )
1227 {
1228   if ( start > end ) return;
1229   if ( start < 0 ) return;
1230 
1231   std::string s;
1232 
1233   if ( password_mode == true ) {
1234     s = pw_text;
1235   } else {
1236     s = real_text;
1237   }
1238 
1239   if ( end > (int)s.length() ) return;
1240 
1241   if ( end < (int)s.length() ) {
1242     UTF8::movePosToNextChar( s.c_str(), end );
1243   }
1244 
1245   // start and end are now valid position in either real or
1246   // password text depending on current mode
1247   // however, we want to remove the bytes from the real
1248   // text and update the password text accordingly so
1249   // convert the position to real_text positions
1250 
1251   int real_start = start;
1252   int real_end = end;
1253 
1254   if ( password_mode == true ) {
1255     real_start = pw2realPos( start );
1256     real_end = pw2realPos( end );
1257   }
1258 
1259   int real_bytes = real_end - real_start;
1260 
1261   s = real_text;
1262   s.erase( real_start, real_bytes );
1263 
1264   // okay, text is changed but before actually setting it
1265   // we will update the position so setText doesn't change them
1266 
1267   int bytes = end - start;
1268 
1269   // start, end and bytes are positions relative to current text (pw or real)
1270   // just as xoffset/cursorpos/selstart and selend are so use the former to
1271   // fix the latter
1272   if ( xoffset > start ) {
1273     if ( xoffset < end )
1274       xoffset = start;
1275     else
1276       xoffset -= bytes;
1277   }
1278   if ( cursorpos > start ) {
1279     if ( cursorpos < end )
1280       cursorpos = start;
1281     else
1282       cursorpos -= bytes;
1283   }
1284   if ( selstart > start ) {
1285     if ( selstart < end )
1286       selstart = start;
1287     else
1288       selstart -= bytes;
1289   }
1290   if ( selend > start ) {
1291     if ( selend < end )
1292       selend = start;
1293     else
1294       selend -= bytes;
1295   }
1296 
1297   setText( s.c_str() );
1298 }
1299 
fixPositions()1300 void StringGadget::SGContent::fixPositions()
1301 {
1302   fixPosition( cursorpos );
1303   fixPosition( selstart );
1304   fixPosition( selend );
1305   fixPosition( xoffset );
1306 }
1307 
fixPosition(int & pos)1308 void StringGadget::SGContent::fixPosition( int &pos )
1309 {
1310   std::string s;
1311 
1312   if ( password_mode == true ) {
1313     s = pw_text;
1314   } else {
1315     s = real_text;
1316   }
1317 
1318   if ( pos > (int)s.length() )
1319     pos = (int)s.length();
1320 
1321   if ( pos < 0 )
1322     pos = 0;
1323 
1324   if ( UTF8::isValidCharacter( s.c_str() + pos ) == false ) {
1325     UTF8::movePosToPrevChar( s.c_str(), pos );
1326   }
1327 }
1328 
changeHeightForCurFont()1329 void StringGadget::changeHeightForCurFont()
1330 {
1331   int ch;
1332   if ( font == NULL ) {
1333     ch = _aguix->getCharHeight();
1334   } else {
1335     ch = font->getCharHeight();
1336   }
1337   resize( getWidth(), ch + 2 * getBorderWidth() );
1338 }
1339 
setSelection(int start,int end,bool copy_selection)1340 void StringGadget::setSelection( int start, int end, bool copy_selection )
1341 {
1342     content.setSelStart( start );
1343     content.setSelEnd( end );
1344 
1345     if ( copy_selection == true ) {
1346         applySelection();
1347     }
1348     redraw();
1349 }
1350 
jumpToNextWord()1351 void StringGadget::jumpToNextWord()
1352 {
1353     int new_cur = content.getCursorPos();
1354 
1355     //TODO this is not utf8 safe
1356 
1357     while ( new_cur < (int)strlen( content.getText() ) &&
1358             ! std::isalnum( *(content.getText() + new_cur) ) ) {
1359         UTF8::movePosToNextChar( content.getText(), new_cur );
1360     }
1361 
1362     while ( new_cur < (int)strlen( content.getText() ) &&
1363             std::isalnum( *(content.getText() + new_cur) ) ) {
1364         UTF8::movePosToNextChar( content.getText(), new_cur );
1365     }
1366 
1367     setCursor( new_cur );
1368 }
1369 
jumpToPrevWord()1370 void StringGadget::jumpToPrevWord()
1371 {
1372     int new_cur = content.getCursorPos();
1373 
1374     if ( new_cur > 0 ) {
1375         UTF8::movePosToPrevChar( content.getText(), new_cur );
1376     }
1377 
1378     //TODO this is not utf8 safe
1379 
1380     while ( new_cur > 0 &&
1381             ! std::isalnum( *(content.getText() + new_cur) ) ) {
1382         UTF8::movePosToPrevChar( content.getText(), new_cur );
1383     }
1384 
1385     int last_valid_pos = new_cur;
1386 
1387     while ( new_cur > 0 &&
1388             std::isalnum( *(content.getText() + new_cur) ) ) {
1389 
1390         last_valid_pos = new_cur;
1391 
1392         UTF8::movePosToPrevChar( content.getText(), new_cur );
1393     }
1394 
1395     setCursor( last_valid_pos );
1396 }
1397 
setReadOnly(bool nv)1398 void StringGadget::setReadOnly( bool nv )
1399 {
1400     m_read_only = nv;
1401 }
1402