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