2 // "$Id: Fl_Input_.cxx 8413 2011-02-11 16:37:06Z manolo $"
4 // Common input widget routines for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
29 #include <FL/Fl_Input_.H>
30 #include <FL/Fl_Window.H>
31 #include <FL/fl_draw.H>
32 #include <FL/fl_ask.H>
34 #include <FL/fl_utf8.h>
40 #if defined(USE_X11) && !USE_XFT
41 const int secret_char
= '*'; // asterisk to hide secret input
43 const int secret_char
= 0x2022; // bullet to hide secret input
47 extern void fl_draw(const char*, int, float, float);
49 ////////////////////////////////////////////////////////////////
52 Converts a given text segment into the text that will be rendered on screen.
54 This copies the text from \p p to \p buf, replacing characters with <tt>^X</tt>
55 and <tt>\\nnn</tt> as necessary.
57 The destination buffer is limited to \c MAXBUF (currently at 1024). All
58 following text is truncated.
60 \param [in] p pointer to source buffer
61 \param [in] buf pointer to destination buffer
62 \return pointer to the end of the destination buffer
64 const char* Fl_Input_::expand(const char* p
, char* buf
) const {
66 char* e
= buf
+(MAXBUF
-4);
67 const char* lastspace
= p
;
68 char* lastspace_out
= o
;
69 int width_to_lastspace
= 0;
72 // const char *pe = p + strlen(p);
74 if (input_type()==FL_SECRET_INPUT
) {
75 while (o
<e
&& p
< value_
+size_
) {
76 if (fl_utf8len((char)p
[0]) >= 1) {
77 l_secret
= fl_utf8encode(secret_char
, o
);
84 if (wrap() && (p
>= value_
+size_
|| isspace(*p
& 255))) {
85 word_wrap
= w() - Fl::box_dw(box()) - 2;
86 width_to_lastspace
+= (int)fl_width(lastspace_out
, o
-lastspace_out
);
87 if (p
> lastspace
+1) {
88 if (word_count
&& width_to_lastspace
> word_wrap
) {
89 p
= lastspace
; o
= lastspace_out
; break;
97 if (p
>= value_
+size_
) break;
99 if (c
< ' ' || c
== 127) {
100 if (c
=='\n' && input_type()==FL_MULTILINE_INPUT
) {p
--; break;}
101 if (c
== '\t' && input_type()==FL_MULTILINE_INPUT
) {
102 for (c
= fl_utf_nb_char((uchar
*)buf
, o
-buf
)%8; c
<8 && o
<e
; c
++) {
118 Calculates the width in pixels of part of a text buffer.
120 This call takes a string, usually created by expand, and calculates
121 the width of the string when rendered with the given font.
123 \param [in] p pointer to the start of the original string
124 \param [in] e pointer to the end of the original string
125 \param [in] buf pointer to the buffer as returned by expand()
126 \return width of string in pixels
128 double Fl_Input_::expandpos(
129 const char* p
, // real string
130 const char* e
, // pointer into real string
131 const char* buf
, // conversion of real string by expand()
132 int* returnn
// return offset into buf here
137 if (input_type()==FL_SECRET_INPUT
) {
139 l
= fl_utf8len((char)p
[0]);
140 if (l
>= 1) n
+= l_secret
;
145 if (c
< ' ' || c
== 127) {
146 if (c
== '\t' && input_type()==FL_MULTILINE_INPUT
) {
153 chr
+= fl_utf8len((char)p
[0]) >= 1;
156 if (returnn
) *returnn
= n
;
157 return fl_width(buf
, n
);
160 ////////////////////////////////////////////////////////////////
163 Marks a range of characters for update.
165 This call marks all characters from \p to the end of the
166 text buffer for update. At least these characters
167 will be redrawn in the next update cycle.
169 Characters from \p mu_p to end of widget are redrawn.
170 If \p erase_cursor_only, small part at \p mu_p is redrawn.
171 Right now minimal update just keeps unchanged characters from
172 being erased, so they don't blink.
174 \param [in] p start of update range
176 void Fl_Input_::minimal_update(int p
) {
177 if (damage() & FL_DAMAGE_ALL
) return; // don't waste time if it won't be done
178 if (damage() & FL_DAMAGE_EXPOSE
) {
179 if (p
< mu_p
) mu_p
= p
;
184 damage(FL_DAMAGE_EXPOSE
);
185 erase_cursor_only
= 0;
189 Marks a range of characters for update.
191 This call marks a text range for update. At least all characters
192 from \p p to \p q will be redrawn in the next update cycle.
194 \param [in] p start of update range
195 \param [in] q end of update range
197 void Fl_Input_::minimal_update(int p
, int q
) {
202 ////////////////////////////////////////////////////////////////
204 /* Horizontal cursor position in pixels while moving up or down. */
205 double Fl_Input_::up_down_pos
= 0;
207 /* Flag to remember last cursor move. */
208 int Fl_Input_::was_up_down
= 0;
211 Sets the current font and font size.
213 void Fl_Input_::setfont() const {
214 fl_font(textfont(), textsize());
218 Draws the text in the passed bounding box.
220 If <tt>damage() & FL_DAMAGE_ALL</tt> is true, this assumes the
221 area has already been erased to color(). Otherwise it does
222 minimal update and erases the area itself.
224 \param X, Y, W, H area that must be redrawn
226 void Fl_Input_::drawtext(int X
, int Y
, int W
, int H
) {
227 int do_mu
= !(damage()&FL_DAMAGE_ALL
);
229 if (Fl::focus()!=this && !size()) {
230 if (do_mu
) { // we have to erase it if cursor was there
231 draw_box(box(), X
-Fl::box_dx(box()), Y
-Fl::box_dy(box()),
232 W
+Fl::box_dw(box()), H
+Fl::box_dh(box()), color());
237 int selstart
, selend
;
238 if (Fl::focus()!=this && /*Fl::selection_owner()!=this &&*/ Fl::pushed()!=this)
239 selstart
= selend
= 0;
240 else if (position() <= mark()) {
241 selstart
= position(); selend
= mark();
243 selend
= position(); selstart
= mark();
250 // count how many lines and put the last one into the buffer:
251 // And figure out where the cursor is:
252 int height
= fl_height();
253 int threshold
= height
/2;
256 for (p
=value(), curx
=cury
=lines
=0; ;) {
258 if (position() >= p
-value() && position() <= e
-value()) {
259 curx
= int(expandpos(p
, value()+position(), buf
, 0)+.5);
260 if (Fl::focus()==this && !was_up_down
) up_down_pos
= curx
;
262 int newscroll
= xscroll_
;
263 if (curx
> newscroll
+W
-threshold
) {
264 // figure out scrolling so there is space after the cursor:
265 newscroll
= curx
+threshold
-W
;
266 // figure out the furthest left we ever want to scroll:
267 int ex
= int(expandpos(p
, e
, buf
, 0))+2-W
;
268 // use minimum of both amounts:
269 if (ex
< newscroll
) newscroll
= ex
;
270 } else if (curx
< newscroll
+threshold
) {
271 newscroll
= curx
-threshold
;
273 if (newscroll
< 0) newscroll
= 0;
274 if (newscroll
!= xscroll_
) {
275 xscroll_
= newscroll
;
276 mu_p
= 0; erase_cursor_only
= 0;
280 if (e
>= value_
+size_
) break;
284 // adjust the scrolling:
285 if (input_type()==FL_MULTILINE_INPUT
) {
287 if (cury
< newy
) newy
= cury
;
288 if (cury
> newy
+H
-height
) newy
= cury
-H
+height
;
289 if (newy
< -1) newy
= -1;
290 if (newy
!= yscroll_
) {yscroll_
= newy
; mu_p
= 0; erase_cursor_only
= 0;}
292 yscroll_
= -(H
-height
)/2;
295 fl_push_clip(X
, Y
, W
, H
);
296 Fl_Color tc
= active_r() ? textcolor() : fl_inactive(textcolor());
299 // visit each line and draw it:
300 int desc
= height
-fl_descent();
301 float xpos
= (float)(X
- xscroll_
+ 1);
302 int ypos
= -yscroll_
;
305 // re-expand line unless it is the last one calculated above:
306 if (lines
>1) e
= expand(p
, buf
);
308 if (ypos
<= -height
) goto CONTINUE
; // clipped off top
310 if (do_mu
) { // for minimal update:
311 const char* pp
= value()+mu_p
; // pointer to where minimal update starts
312 if (e
< pp
) goto CONTINUE2
; // this line is before the changes
313 if (readonly()) erase_cursor_only
= 0; // this isn't the most efficient way
314 if (erase_cursor_only
&& p
> pp
) goto CONTINUE2
; // this line is after
315 // calculate area to erase:
316 float r
= (float)(X
+W
);
320 if (erase_cursor_only
) r
= xpos
+2;
321 else if (readonly()) xx
-= 3;
323 xx
= xpos
+ (float)expandpos(p
, pp
, buf
, 0);
324 if (erase_cursor_only
) r
= xx
+2;
325 else if (readonly()) xx
-= 3;
327 // clip to and erase it:
328 fl_push_clip((int)xx
-1-height
/8, Y
+ypos
, (int)(r
-xx
+2+height
/4), height
);
329 draw_box(box(), X
-Fl::box_dx(box()), Y
-Fl::box_dy(box()),
330 W
+Fl::box_dw(box()), H
+Fl::box_dh(box()), color());
331 // it now draws entire line over it
332 // this should not draw letters to left of erased area, but
336 // Draw selection area if required:
337 if (selstart
< selend
&& selstart
<= e
-value() && selend
> p
-value()) {
338 const char* pp
= value()+selstart
;
343 x1
+= (float)expandpos(p
, pp
, buf
, &offset1
);
344 fl_draw(buf
, offset1
, xpos
, (float)(Y
+ypos
+desc
));
347 float x2
= (float)(X
+W
);
349 if (pp
<= e
) x2
= xpos
+ (float)expandpos(p
, pp
, buf
, &offset2
);
350 else offset2
= strlen(buf
);
351 fl_color(selection_color());
352 fl_rectf((int)(x1
+0.5), Y
+ypos
, (int)(x2
-x1
+0.5), height
);
353 fl_color(fl_contrast(textcolor(), selection_color()));
354 fl_draw(buf
+offset1
, offset2
-offset1
, x1
, (float)(Y
+ypos
+desc
));
357 fl_draw(buf
+offset2
, strlen(buf
+offset2
), x2
, (float)(Y
+ypos
+desc
));
360 // draw unselected text
362 fl_draw(buf
, strlen(buf
), xpos
, (float)(Y
+ypos
+desc
));
365 if (do_mu
) fl_pop_clip();
369 if (Fl::focus() == this && selstart
== selend
&&
370 position() >= p
-value() && position() <= e
-value()) {
371 fl_color(cursor_color());
372 // cursor position may need to be recomputed (see STR #2486)
373 curx
= int(expandpos(p
, value()+position(), buf
, 0)+.5);
375 fl_line((int)(xpos
+curx
-2.5f
), Y
+ypos
+height
-1,
376 (int)(xpos
+curx
+0.5f
), Y
+ypos
+height
-4,
377 (int)(xpos
+curx
+3.5f
), Y
+ypos
+height
-1);
379 fl_rectf((int)(xpos
+curx
+0.5), Y
+ypos
, 2, height
);
385 if (e
>= value_
+size_
) break;
386 if (*e
== '\n' || *e
== ' ') e
++;
390 // for minimal update, erase all lines below last one if necessary:
391 if (input_type()==FL_MULTILINE_INPUT
&& do_mu
&& ypos
<H
392 && (!erase_cursor_only
|| p
<= value()+mu_p
)) {
393 if (ypos
< 0) ypos
= 0;
394 fl_push_clip(X
, Y
+ypos
, W
, H
-ypos
);
395 draw_box(box(), X
-Fl::box_dx(box()), Y
-Fl::box_dy(box()),
396 W
+Fl::box_dw(box()), H
+Fl::box_dh(box()), color());
401 if (Fl::focus() == this) {
402 fl_set_spot(textfont(), textsize(),
403 (int)xpos
+curx
, Y
+ypos
-fl_descent(), W
, H
, window());
408 Simple function that determines if a character could be part of a word.
409 \todo This function is not ucs4-aware.
411 static int isword(char c
) {
412 return (c
&128 || isalnum(c
) || strchr("#%&-/@\\_~", c
));
416 Finds the end of a word.
418 This call calculates the end of a word based on the given
419 index \p i. Calling this function repeatedly will move
420 forwards to the end of the text.
422 \param [in] i starting index for the search
423 \return end of the word
425 int Fl_Input_::word_end(int i
) const {
426 if (input_type() == FL_SECRET_INPUT
) return size();
427 //while (i < size() && !isword(index(i))) i++;
428 while (i
< size() && !isword(index(i
))) i
++;
429 while (i
< size() && isword(index(i
))) i
++;
434 Finds the start of a word.
436 This call calculates the start of a word based on the given
437 index \p i. Calling this function repeatedly will move
438 backwards to the beginning of the text.
440 \param [in] i starting index for the search
441 \return start of the word
443 int Fl_Input_::word_start(int i
) const {
444 if (input_type() == FL_SECRET_INPUT
) return 0;
445 // if (i >= size() || !isword(index(i)))
446 // while (i > 0 && !isword(index(i-1))) i--;
447 while (i
> 0 && !isword(index(i
-1))) i
--;
448 while (i
> 0 && isword(index(i
-1))) i
--;
453 Finds the end of a line.
455 This call calculates the end of a line based on the given
458 \param [in] i starting index for the search
459 \return end of the line
461 int Fl_Input_::line_end(int i
) const {
462 if (input_type() != FL_MULTILINE_INPUT
) return size();
465 // go to the start of the paragraph:
467 while (j
> 0 && index(j
-1) != '\n') j
--;
468 // now measure lines until we get past i, end of that line is real eol:
470 for (const char* p
=value()+j
; ;) {
473 if (p
-value() >= i
) return p
-value();
477 while (i
< size() && index(i
) != '\n') i
++;
483 Finds the start of a line.
485 This call calculates the start of a line based on the given
488 \param [in] i starting index for the search
489 \return start of the line
491 int Fl_Input_::line_start(int i
) const {
492 if (input_type() != FL_MULTILINE_INPUT
) return 0;
494 while (j
> 0 && index(j
-1) != '\n') j
--;
496 // now measure lines until we get past i, start of that line is real eol:
498 for (const char* p
=value()+j
; ;) {
500 const char* e
= expand(p
, buf
);
501 if (e
-value() >= i
) return p
-value();
508 Handles mouse clicks and mouse moves.
509 \todo Add comment and parameters
511 void Fl_Input_::handle_mouse(int X
, int Y
, int /*W*/, int /*H*/, int drag
) {
519 int theline
= (input_type()==FL_MULTILINE_INPUT
) ?
520 (Fl::event_y()-Y
+yscroll_
)/fl_height() : 0;
525 theline
--; if (theline
< 0) break;
526 if (e
>= value_
+size_
) break;
529 const char *l
, *r
, *t
; double f0
= Fl::event_x()-X
+xscroll_
;
530 for (l
= p
, r
= e
; l
<r
; ) {
532 int cw
= fl_utf8len((char)l
[0]);
535 f
= X
-xscroll_
+expandpos(p
, t
, buf
, 0);
536 if (f
<= Fl::event_x()) {l
= t
; f0
= Fl::event_x()-f
;}
539 if (l
< e
) { // see if closer to character on right:
541 int cw
= fl_utf8len((char)l
[0]);
543 f1
= X
-xscroll_
+expandpos(p
, l
+ cw
, buf
, 0) - Fl::event_x();
544 if (f1
< f0
) l
= l
+cw
;
549 int newmark
= drag
? mark() : newpos
;
550 if (Fl::event_clicks()) {
551 if (newpos
>= newmark
) {
552 if (newpos
== newmark
) {
553 if (newpos
< size()) newpos
++;
556 if (Fl::event_clicks() > 1) {
557 newpos
= line_end(newpos
);
558 newmark
= line_start(newmark
);
560 newpos
= word_end(newpos
);
561 newmark
= word_start(newmark
);
564 if (Fl::event_clicks() > 1) {
565 newpos
= line_start(newpos
);
566 newmark
= line_end(newmark
);
568 newpos
= word_start(newpos
);
569 newmark
= word_end(newmark
);
572 // if the multiple click does not increase the selection, revert
573 // to single-click behavior:
574 if (!drag
&& (mark() > position() ?
575 (newmark
>= position() && newpos
<= mark()) :
576 (newmark
>= mark() && newpos
<= position()))) {
578 newmark
= newpos
= l
-value();
581 position(newpos
, newmark
);
585 Sets the index for the cursor and mark.
587 The input widget maintains two pointers into the string. The
588 \e position (\c p) is where the cursor is. The
589 \e mark (\c m) is the other end of the selected text. If they
590 are equal then there is no selection. Changing this does not
591 affect the clipboard (use copy() to do that).
593 Changing these values causes a redraw(). The new
594 values are bounds checked.
596 \param p index for the cursor position
597 \param m index for the mark
598 \return 0 if no positions changed
599 \see position(int), position(), mark(int)
601 int Fl_Input_::position(int p
, int m
) {
605 if (p
>size()) p
= size();
607 if (m
>size()) m
= size();
608 if (p
== m
) is_same
= 1;
610 while (p
< position_
&& p
> 0 && (size() - p
) > 0 &&
611 (fl_utf8len((char)(value() + p
)[0]) < 1)) { p
--; }
612 int ul
= fl_utf8len((char)(value() + p
)[0]);
613 while (p
< size() && p
> position_
&& ul
< 0) {
615 ul
= fl_utf8len((char)(value() + p
)[0]);
618 while (m
< mark_
&& m
> 0 && (size() - m
) > 0 &&
619 (fl_utf8len((char)(value() + m
)[0]) < 1)) { m
--; }
620 ul
= fl_utf8len((char)(value() + m
)[0]);
621 while (m
< size() && m
> mark_
&& ul
< 0) {
623 ul
= fl_utf8len((char)(value() + m
)[0]);
626 if (p
== position_
&& m
== mark_
) return 0;
629 //if (Fl::selection_owner() == this) Fl::selection_owner(0);
631 if (p
!= position_
) minimal_update(position_
, p
);
632 if (m
!= mark_
) minimal_update(mark_
, m
);
634 // new position is a cursor
635 if (position_
== mark_
) {
636 // old position was just a cursor
637 if (Fl::focus() == this && !(damage()&FL_DAMAGE_EXPOSE
)) {
638 minimal_update(position_
); erase_cursor_only
= 1;
640 } else { // old position was a selection
641 minimal_update(position_
, mark_
);
650 Moves the cursor to the column given by \p up_down_pos.
652 This function is helpful when implementing up and down
653 cursor movement. It moves the cursor from the beginning
654 of a line to the column indicated by the global variable
655 \p up_down_pos in pixel units.
657 \param [in] i index into the beginning of a line of text
658 \param [in] keepmark if set, move only the cursor, but not the mark
659 \return index to new cursor position
661 int Fl_Input_::up_down_position(int i
, int keepmark
) {
662 // unlike before, i must be at the start of the line already!
666 const char* p
= value()+i
;
667 const char* e
= expand(p
, buf
);
668 const char *l
, *r
, *t
;
669 for (l
= p
, r
= e
; l
<r
; ) {
671 int f
= (int)expandpos(p
, t
, buf
, 0);
672 if (f
<= up_down_pos
) l
= t
; else r
= t
-1;
675 j
= position(j
, keepmark
? mark_
: j
);
681 Put the current selection into the clipboard.
683 This function copies the current selection between mark() and
684 position() into the specified \c clipboard. This does not
685 replace the old clipboard contents if position() and
686 mark() are equal. Clipboard 0 maps to the current text
687 selection and clipboard 1 maps to the cut/paste clipboard.
689 \param clipboard the clipboard destination 0 or 1
690 \return 0 if no text is selected, 1 if the selection was copied
691 \see Fl::copy(const char *, int, int)
693 int Fl_Input_::copy(int clipboard
) {
697 if (b
> e
) {b
= mark(); e
= position();}
698 if (input_type() == FL_SECRET_INPUT
) e
= b
;
699 Fl::copy(value()+b
, e
-b
, clipboard
);
705 #define MAXFLOATSIZE 40
707 static char* undobuffer
;
708 static int undobufferlength
;
709 static Fl_Input_
* undowidget
;
710 static int undoat
; // points after insertion
711 static int undocut
; // number of characters deleted there
712 static int undoinsert
; // number of characters inserted
713 static int yankcut
; // length of valid contents of buffer, even if undocut=0
715 static void undobuffersize(int n
) {
716 if (n
> undobufferlength
) {
718 do {undobufferlength
*= 2;} while (undobufferlength
< n
);
719 undobuffer
= (char*)realloc(undobuffer
, undobufferlength
);
721 undobufferlength
= n
+9;
722 undobuffer
= (char*)malloc(undobufferlength
);
728 Deletes text from \p b to \p e and inserts the new string \p text.
730 All changes to the text buffer go through this function.
731 It deletes the region between \p a and \p b (either one may be less or
732 equal to the other), and then inserts the string \p text
733 at that point and moves the mark() and
734 position() to the end of the insertion. Does the callback if
735 <tt>when() & FL_WHEN_CHANGED</tt> and there is a change.
737 Set \p b and \p e equal to not delete anything.
738 Set \p text to \c NULL to not insert anything.
740 \p ilen can be zero or <tt>strlen(text)</tt>, which
741 saves a tiny bit of time if you happen to already know the
742 length of the insertion, or can be used to insert a portion of a
745 \p b and \p e are clamped to the
746 <tt>0..size()</tt> range, so it is safe to pass any values.
748 cut() and insert() are just inline functions that call replace().
750 \param [in] b beginning index of text to be deleted
751 \param [in] e ending index of text to be deleted and insertion position
752 \param [in] text string that will be inserted
753 \param [in] ilen length of \p text or 0 for \c nul terminated strings
754 \return 0 if nothing changed
756 int Fl_Input_::replace(int b
, int e
, const char* text
, int ilen
) {
762 if (b
>size_
) b
= size_
;
763 if (e
>size_
) e
= size_
;
764 if (e
<b
) {int t
=b
; b
=e
; e
=t
;}
765 while (b
!= e
&& b
> 0 && (size_
- b
) > 0 &&
766 (fl_utf8len((value_
+ b
)[0]) < 1)) { b
--; }
767 ul
= fl_utf8len((char)(value_
+ e
)[0]);
768 while (e
< size_
&& e
> 0 && ul
< 0) {
770 ul
= fl_utf8len((char)(value_
+ e
)[0]);
772 if (text
&& !ilen
) ilen
= strlen(text
);
773 if (e
<=b
&& !ilen
) return 0; // don't clobber undo for a null operation
774 if (size_
+ilen
-(e
-b
) > maximum_size_
) {
775 ilen
= maximum_size_
-size_
+(e
-b
);
776 if (ilen
< 0) ilen
= 0;
779 put_in_buffer(size_
+ilen
);
782 if (undowidget
== this && b
== undoat
) {
783 undobuffersize(undocut
+(e
-b
));
784 memcpy(undobuffer
+undocut
, value_
+b
, e
-b
);
786 } else if (undowidget
== this && e
== undoat
&& !undoinsert
) {
787 undobuffersize(undocut
+(e
-b
));
788 memmove(undobuffer
+(e
-b
), undobuffer
, undocut
);
789 memcpy(undobuffer
, value_
+b
, e
-b
);
791 } else if (undowidget
== this && e
== undoat
&& (e
-b
)<undoinsert
) {
795 memcpy(undobuffer
, value_
+b
, e
-b
);
799 memmove(buffer
+b
, buffer
+e
, size_
-e
+1);
803 if (input_type() == FL_SECRET_INPUT
) yankcut
= 0; else yankcut
= undocut
;
807 if (undowidget
== this && b
== undoat
)
813 memmove(buffer
+b
+ilen
, buffer
+b
, size_
-b
+1);
814 memcpy(buffer
+b
, text
, ilen
);
820 mark_
= position_
= undoat
= b
+ilen
;
822 // Insertions into the word at the end of the line will cause it to
823 // wrap to the next line, so we must indicate that the changes may start
824 // right after the whitespace before the current word. This will
825 // result in sub-optimal update when such wrapping does not happen
826 // but it is too hard to figure out for now...
828 // if there is a space in the pasted text, the whole line may have rewrapped
830 for (i
=0; i
<ilen
; i
++)
831 if (text
[i
]==' ') break;
833 while (b
> 0 && !isspace(index(b
) & 255) && index(b
)!='\n') b
--;
835 while (b
> 0 && index(b
)!='\n') b
--;
838 // make sure we redraw the old selection or cursor:
844 mark_
= position_
= undoat
;
847 if (when()&FL_WHEN_CHANGED
) do_callback();
852 Undoes previous changes to the text buffer.
854 This call undoes a number of previous calls to replace().
856 \return non-zero if any change was made.
858 int Fl_Input_::undo() {
860 if ( undowidget
!= this || (!undocut
&& !undoinsert
) ) return 0;
863 int xlen
= undoinsert
;
867 put_in_buffer(size_
+ilen
);
870 memmove(buffer
+b
+ilen
, buffer
+b
, size_
-b
+1);
871 memcpy(buffer
+b
, undobuffer
, ilen
);
877 undobuffersize(xlen
);
878 memcpy(undobuffer
, buffer
+b
, xlen
);
879 memmove(buffer
+b
, buffer
+b
+xlen
, size_
-xlen
-b
+1);
884 if (xlen
) yankcut
= xlen
;
887 mark_
= b
/* -ilen */;
891 while (b1
> 0 && index(b1
)!='\n') b1
--;
894 if (when()&FL_WHEN_CHANGED
) do_callback();
899 Copies the \e yank buffer to the clipboard.
901 This method copies all the previous contiguous cuts from the undo
902 information to the clipboard. This function implements
903 the \c ^K shortcut key.
905 \return 0 if the operation did not change the clipboard
906 \see copy(int), cut()
908 int Fl_Input_::copy_cuts() {
909 // put the yank buffer into the X clipboard
910 if (!yankcut
|| input_type()==FL_SECRET_INPUT
) return 0;
911 Fl::copy(undobuffer
, yankcut
, 1);
916 Checks the when() field and does a callback if indicated.
918 void Fl_Input_::maybe_do_callback() {
919 if (changed() || (when()&FL_WHEN_NOT_CHANGED
)) {
925 Handles all kinds of text field related events.
927 This is called by derived classes.
928 \todo Add comment and parameters
930 int Fl_Input_::handletext(int event
, int X
, int Y
, int W
, int H
) {
935 if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT
);
939 if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT
);
943 fl_set_spot(textfont(), textsize(), x(), y(), w(), h(), window());
944 if (mark_
== position_
) {
945 minimal_update(size()+1);
946 } else //if (Fl::selection_owner() != this)
947 minimal_update(mark_
, position_
);
951 if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT
);
952 if (mark_
== position_
) {
953 if (!(damage()&FL_DAMAGE_EXPOSE
)) {minimal_update(position_
); erase_cursor_only
= 1;}
954 } else //if (Fl::selection_owner() != this)
955 minimal_update(mark_
, position_
);
958 if (!readonly() && (when() & FL_WHEN_RELEASE
))
963 if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT
);
965 handle_mouse(X
, Y
, W
, H
, Fl::event_state(FL_SHIFT
));
967 if (Fl::focus() != this) {
974 handle_mouse(X
, Y
, W
, H
, 1);
982 // Don't allow pastes into readonly widgets...
984 fl_beep(FL_BEEP_ERROR
);
988 // See if we have anything to paste...
989 if (!Fl::event_text() || !Fl::event_length()) return 1;
991 // strip trailing control characters and spaces before pasting:
992 const char* t
= Fl::event_text();
993 const char* e
= t
+Fl::event_length();
994 if (input_type() != FL_MULTILINE_INPUT
) while (e
> t
&& isspace(*(e
-1) & 255)) e
--;
995 if (!t
|| e
<= t
) return 1; // Int/float stuff will crash without this test
996 if (input_type() == FL_INT_INPUT
) {
997 while (isspace(*t
& 255) && t
< e
) t
++;
999 if (*p
== '+' || *p
== '-') p
++;
1000 if (strncmp(p
, "0x", 2) == 0) {
1002 while (isxdigit(*p
& 255) && p
< e
) p
++;
1004 while (isdigit(*p
& 255) && p
< e
) p
++;
1007 fl_beep(FL_BEEP_ERROR
);
1009 } else return replace(0, size(), t
, e
- t
);
1010 } else if (input_type() == FL_FLOAT_INPUT
) {
1011 while (isspace(*t
& 255) && t
< e
) t
++;
1013 if (*p
== '+' || *p
== '-') p
++;
1014 while (isdigit(*p
& 255) && p
< e
) p
++;
1017 while (isdigit(*p
& 255) && p
< e
) p
++;
1018 if (*p
== 'e' || *p
== 'E') {
1020 if (*p
== '+' || *p
== '-') p
++;
1021 while (isdigit(*p
& 255) && p
< e
) p
++;
1025 fl_beep(FL_BEEP_ERROR
);
1027 } else return replace(0, size(), t
, e
- t
);
1029 return replace(position(), mark(), t
, e
-t
);}
1032 if (!(shortcut() ? Fl::test_shortcut(shortcut()) : test_shortcut()))
1034 if (Fl::visible_focus() && handle(FL_FOCUS
)) {
1037 } // else fall through
1044 /*------------------------------*/
1047 Creates a new Fl_Input_ widget.
1049 This function creates a new Fl_Input_ widget and adds it to the current
1050 Fl_Group. The value() is set to \c NULL.
1051 The default boxtype is \c FL_DOWN_BOX.
1053 \param X, Y, W, H the dimensions of the new widget
1054 \param l an optional label text
1056 Fl_Input_::Fl_Input_(int X
, int Y
, int W
, int H
, const char* l
)
1057 : Fl_Widget(X
, Y
, W
, H
, l
) {
1059 color(FL_BACKGROUND2_COLOR
, FL_SELECTION_COLOR
);
1060 align(FL_ALIGN_LEFT
);
1061 textsize_
= FL_NORMAL_SIZE
;
1062 textfont_
= FL_HELVETICA
;
1063 textcolor_
= FL_FOREGROUND_COLOR
;
1064 cursor_color_
= FL_FOREGROUND_COLOR
; // was FL_BLUE
1065 mark_
= position_
= size_
= 0;
1069 xscroll_
= yscroll_
= 0;
1070 maximum_size_
= 32767;
1072 set_flag(SHORTCUT_LABEL
);
1077 Copies the value from a possibly static entry into the internal buffer.
1079 \param [in] len size of the current text
1081 void Fl_Input_::put_in_buffer(int len
) {
1082 if (value_
== buffer
&& bufsize
> len
) {
1087 if (len
> size_
) len
+= 9; // let a few characters insert before realloc
1089 buffer
= (char*)malloc(bufsize
);
1090 } else if (bufsize
<= len
) {
1091 // we may need to move old value in case it points into buffer:
1092 int moveit
= (value_
>= buffer
&& value_
< buffer
+bufsize
);
1093 // enlarge current buffer
1095 do {bufsize
*= 2;} while (bufsize
<= len
);
1099 // Note: the following code is equivalent to:
1101 // if (moveit) value_ = value_ - buffer;
1102 // char* nbuffer = (char*)realloc(buffer, bufsize);
1103 // if (moveit) value_ = value_ + nbuffer;
1104 // buffer = nbuffer;
1106 // We just optimized the pointer arithmetic for value_...
1108 char* nbuffer
= (char*)realloc(buffer
, bufsize
);
1109 if (moveit
) value_
+= (nbuffer
-buffer
);
1112 memmove(buffer
, value_
, size_
); buffer
[size_
] = 0;
1117 Changes the widget text.
1119 This function changes the text and sets the mark and the point to
1120 the end of it. The string is \e not copied. If the user edits the
1121 string it is copied to the internal buffer then. This can save a
1122 great deal of time and memory if your program is rapidly
1123 changing the values of text fields, but this will only work if
1124 the passed string remains unchanged until either the
1125 Fl_Input is destroyed or value() is called again.
1127 You can use the \p len parameter to directly set the length
1128 if you know it already or want to put \c nul characters in the text.
1130 \param [in] str the new text
1131 \param [in] len the length of the new text
1132 \return non-zero if the new value is different than the current one
1134 int Fl_Input_::static_value(const char* str
, int len
) {
1136 if (undowidget
== this) undowidget
= 0;
1137 if (str
== value_
&& len
== size_
) return 0;
1138 if (len
) { // non-empty new value:
1139 if (xscroll_
|| yscroll_
) {
1140 xscroll_
= yscroll_
= 0;
1144 // find first different character:
1146 for (; i
<size_
&& i
<len
&& str
[i
]==value_
[i
]; i
++);
1147 if (i
==size_
&& i
==len
) return 0;
1153 } else { // empty new value:
1154 if (!size_
) return 0; // both old and new are empty.
1157 xscroll_
= yscroll_
= 0;
1160 position(readonly() ? 0 : size());
1165 Changes the widget text.
1167 This function changes the text and sets the mark and the point to
1168 the end of it. The string is \e not copied. If the user edits the
1169 string it is copied to the internal buffer then. This can save a
1170 great deal of time and memory if your program is rapidly
1171 changing the values of text fields, but this will only work if
1172 the passed string remains unchanged until either the
1173 Fl_Input is destroyed or value() is called again.
1175 \param [in] str the new text
1176 \return non-zero if the new value is different than the current one
1178 int Fl_Input_::static_value(const char* str
) {
1179 return static_value(str
, str
? strlen(str
) : 0);
1183 Changes the widget text.
1185 This function changes the text and sets the mark and the
1186 point to the end of it. The string is copied to the internal
1187 buffer. Passing \c NULL is the same as "".
1189 You can use the \p length parameter to directly set the length
1190 if you know it already or want to put \c nul characters in the text.
1192 \param [in] str the new text
1193 \param [in] len the length of the new text
1194 \return non-zero if the new value is different than the current one
1195 \see Fl_Input_::value(const char* str), Fl_Input_::value()
1197 int Fl_Input_::value(const char* str
, int len
) {
1198 int r
= static_value(str
, len
);
1199 if (len
) put_in_buffer(len
);
1204 Changes the widget text.
1206 This function changes the text and sets the mark and the
1207 point to the end of it. The string is copied to the internal
1208 buffer. Passing \c NULL is the same as \c "".
1210 \param [in] str the new text
1211 \return non-zero if the new value is different than the current one
1212 \see Fl_Input_::value(const char* str, int len), Fl_Input_::value()
1214 int Fl_Input_::value(const char* str
) {
1215 return value(str
, str
? strlen(str
) : 0);
1219 Changes the size of the widget.
1220 This call updates the text layout so that the cursor is visible.
1221 \param [in] X, Y, W, H new size of the widget
1222 \see Fl_Widget::resize(int, int, int, int)
1224 void Fl_Input_::resize(int X
, int Y
, int W
, int H
) {
1225 if (W
!= w()) xscroll_
= 0;
1226 if (H
!= h()) yscroll_
= 0;
1227 Fl_Widget::resize(X
, Y
, W
, H
);
1231 Destroys the widget.
1233 The destructor clears all allocated buffers and removes the widget
1234 from the parent Fl_Group.
1236 Fl_Input_::~Fl_Input_() {
1237 if (undowidget
== this) undowidget
= 0;
1238 if (bufsize
) free((void*)buffer
);
1242 Returns the number of lines displayed on a single page.
1243 \return widget height divided by the font height
1245 int Fl_Input_::linesPerPage() {
1247 if (input_type() == FL_MULTILINE_INPUT
) {
1248 fl_font(textfont(),textsize()); //ensure current font is set to ours
1249 n
= h()/fl_height(); // number of lines to scroll
1256 Returns the character at index \p i.
1258 This function returns the UTF-8 character at \p i
1259 as a ucs4 character code.
1261 \param [in] i index into the value field
1262 \return the character at index \p i
1264 unsigned int Fl_Input_::index(int i
) const
1267 return fl_utf8decode(value_
+i
, value_
+size_
, &len
);
1271 // End of "$Id: Fl_Input_.cxx 8413 2011-02-11 16:37:06Z manolo $".