Tweak themes for more color consistency.
[ntk.git] / src / Fl_Input_.cxx
blob276c38754aa7f90263266ccd5fc7008c8427711f
1 //
2 // "$Id: Fl_Input_.cxx 8413 2011-02-11 16:37:06Z manolo $"
3 //
4 // Common input widget routines for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2010 by Bill Spitzak and others.
7 //
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
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <FL/Fl.H>
29 #include <FL/Fl_Input_.H>
30 #include <FL/Fl_Window.H>
31 #include <FL/fl_draw.H>
32 #include <FL/fl_ask.H>
33 #include <math.h>
34 #include <FL/fl_utf8.h>
35 #include "flstring.h"
36 #include <stdlib.h>
37 #include <ctype.h>
39 #define MAXBUF 1024
40 #if defined(USE_X11) && !USE_XFT
41 const int secret_char = '*'; // asterisk to hide secret input
42 #else
43 const int secret_char = 0x2022; // bullet to hide secret input
44 #endif
45 static int l_secret;
47 extern void fl_draw(const char*, int, float, float);
49 ////////////////////////////////////////////////////////////////
51 /** \internal
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 {
65 char* o = buf;
66 char* e = buf+(MAXBUF-4);
67 const char* lastspace = p;
68 char* lastspace_out = o;
69 int width_to_lastspace = 0;
70 int word_count = 0;
71 int word_wrap;
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);
78 o += l_secret;
80 p++;
83 } else while (o<e) {
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;
91 word_count++;
93 lastspace = p;
94 lastspace_out = o;
97 if (p >= value_+size_) break;
98 int c = *p++ & 255;
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++) {
103 *o++ = ' ';
105 } else {
106 *o++ = '^';
107 *o++ = c ^ 0x40;
109 } else {
110 *o++ = c;
113 *o = 0;
114 return p;
117 /** \internal
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
133 ) const {
134 int n = 0;
135 int chr = 0;
136 int l;
137 if (input_type()==FL_SECRET_INPUT) {
138 while (p<e) {
139 l = fl_utf8len((char)p[0]);
140 if (l >= 1) n += l_secret;
141 p += l;
143 } else while (p<e) {
144 int c = *p & 255;
145 if (c < ' ' || c == 127) {
146 if (c == '\t' && input_type()==FL_MULTILINE_INPUT) {
147 n += 8-(chr%8);
148 chr += 7-(chr%8);
149 } else n += 2;
150 } else {
151 n++;
153 chr += fl_utf8len((char)p[0]) >= 1;
154 p++;
156 if (returnn) *returnn = n;
157 return fl_width(buf, n);
160 ////////////////////////////////////////////////////////////////
162 /** \internal
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;
180 } else {
181 mu_p = p;
184 damage(FL_DAMAGE_EXPOSE);
185 erase_cursor_only = 0;
188 /** \internal
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) {
198 if (q < p) p = q;
199 minimal_update(p);
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());
234 return;
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();
242 } else {
243 selend = position(); selstart = mark();
246 setfont();
247 const char *p, *e;
248 char buf[MAXBUF];
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;
254 int lines;
255 int curx, cury;
256 for (p=value(), curx=cury=lines=0; ;) {
257 e = expand(p, buf);
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;
261 cury = lines*height;
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;
279 lines++;
280 if (e >= value_+size_) break;
281 p = e+1;
284 // adjust the scrolling:
285 if (input_type()==FL_MULTILINE_INPUT) {
286 int newy = yscroll_;
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;}
291 } else {
292 yscroll_ = -(H-height)/2;
295 fl_push_clip(X, Y, W, H);
296 Fl_Color tc = active_r() ? textcolor() : fl_inactive(textcolor());
298 p = value();
299 // visit each line and draw it:
300 int desc = height-fl_descent();
301 float xpos = (float)(X - xscroll_ + 1);
302 int ypos = -yscroll_;
303 for (; ypos < H;) {
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);
317 float xx;
318 if (p >= pp) {
319 xx = (float)X;
320 if (erase_cursor_only) r = xpos+2;
321 else if (readonly()) xx -= 3;
322 } else {
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
333 // that is nyi.
336 // Draw selection area if required:
337 if (selstart < selend && selstart <= e-value() && selend > p-value()) {
338 const char* pp = value()+selstart;
339 float x1 = xpos;
340 int offset1 = 0;
341 if (pp > p) {
342 fl_color(tc);
343 x1 += (float)expandpos(p, pp, buf, &offset1);
344 fl_draw(buf, offset1, xpos, (float)(Y+ypos+desc));
346 pp = value()+selend;
347 float x2 = (float)(X+W);
348 int offset2;
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));
355 if (pp < e) {
356 fl_color(tc);
357 fl_draw(buf+offset2, strlen(buf+offset2), x2, (float)(Y+ypos+desc));
359 } else {
360 // draw unselected text
361 fl_color(tc);
362 fl_draw(buf, strlen(buf), xpos, (float)(Y+ypos+desc));
365 if (do_mu) fl_pop_clip();
367 CONTINUE2:
368 // draw the cursor:
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);
374 if (readonly()) {
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);
378 } else {
379 fl_rectf((int)(xpos+curx+0.5), Y+ypos, 2, height);
383 CONTINUE:
384 ypos += height;
385 if (e >= value_+size_) break;
386 if (*e == '\n' || *e == ' ') e++;
387 p = 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());
397 fl_pop_clip();
400 fl_pop_clip();
401 if (Fl::focus() == this) {
402 fl_set_spot(textfont(), textsize(),
403 (int)xpos+curx, Y+ypos-fl_descent(), W, H, window());
407 /** \internal
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++;
430 return 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--;
449 return i;
453 Finds the end of a line.
455 This call calculates the end of a line based on the given
456 index \p i.
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();
464 if (wrap()) {
465 // go to the start of the paragraph:
466 int j = i;
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:
469 setfont();
470 for (const char* p=value()+j; ;) {
471 char buf[MAXBUF];
472 p = expand(p, buf);
473 if (p-value() >= i) return p-value();
474 p++;
476 } else {
477 while (i < size() && index(i) != '\n') i++;
478 return i;
483 Finds the start of a line.
485 This call calculates the start of a line based on the given
486 index \p i.
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;
493 int j = i;
494 while (j > 0 && index(j-1) != '\n') j--;
495 if (wrap()) {
496 // now measure lines until we get past i, start of that line is real eol:
497 setfont();
498 for (const char* p=value()+j; ;) {
499 char buf[MAXBUF];
500 const char* e = expand(p, buf);
501 if (e-value() >= i) return p-value();
502 p = e+1;
504 } else return j;
507 /**
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) {
512 was_up_down = 0;
513 if (!size()) return;
514 setfont();
516 const char *p, *e;
517 char buf[MAXBUF];
519 int theline = (input_type()==FL_MULTILINE_INPUT) ?
520 (Fl::event_y()-Y+yscroll_)/fl_height() : 0;
522 int newpos = 0;
523 for (p=value();; ) {
524 e = expand(p, buf);
525 theline--; if (theline < 0) break;
526 if (e >= value_+size_) break;
527 p = e+1;
529 const char *l, *r, *t; double f0 = Fl::event_x()-X+xscroll_;
530 for (l = p, r = e; l<r; ) {
531 double f;
532 int cw = fl_utf8len((char)l[0]);
533 if (cw < 1) cw = 1;
534 t = l+cw;
535 f = X-xscroll_+expandpos(p, t, buf, 0);
536 if (f <= Fl::event_x()) {l = t; f0 = Fl::event_x()-f;}
537 else r = t-cw;
539 if (l < e) { // see if closer to character on right:
540 double f1;
541 int cw = fl_utf8len((char)l[0]);
542 if (cw > 0) {
543 f1 = X-xscroll_+expandpos(p, l + cw, buf, 0) - Fl::event_x();
544 if (f1 < f0) l = l+cw;
547 newpos = l-value();
549 int newmark = drag ? mark() : newpos;
550 if (Fl::event_clicks()) {
551 if (newpos >= newmark) {
552 if (newpos == newmark) {
553 if (newpos < size()) newpos++;
554 else newmark--;
556 if (Fl::event_clicks() > 1) {
557 newpos = line_end(newpos);
558 newmark = line_start(newmark);
559 } else {
560 newpos = word_end(newpos);
561 newmark = word_start(newmark);
563 } else {
564 if (Fl::event_clicks() > 1) {
565 newpos = line_start(newpos);
566 newmark = line_end(newmark);
567 } else {
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()))) {
577 Fl::event_clicks(0);
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) {
602 int is_same = 0;
603 was_up_down = 0;
604 if (p<0) p = 0;
605 if (p>size()) p = size();
606 if (m<0) m = 0;
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) {
614 p++;
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) {
622 m++;
623 ul = fl_utf8len((char)(value() + m)[0]);
625 if (is_same) m = p;
626 if (p == position_ && m == mark_) return 0;
629 //if (Fl::selection_owner() == this) Fl::selection_owner(0);
630 if (p != m) {
631 if (p != position_) minimal_update(position_, p);
632 if (m != mark_) minimal_update(mark_, m);
633 } else {
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_);
644 position_ = p;
645 mark_ = m;
646 return 1;
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!
664 setfont();
665 char buf[MAXBUF];
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; ) {
670 t = l+(r-l+1)/2;
671 int f = (int)expandpos(p, t, buf, 0);
672 if (f <= up_down_pos) l = t; else r = t-1;
674 int j = l-value();
675 j = position(j, keepmark ? mark_ : j);
676 was_up_down = 1;
677 return 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) {
694 int b = position();
695 int e = mark();
696 if (b != e) {
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);
700 return 1;
702 return 0;
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) {
717 if (undobuffer) {
718 do {undobufferlength *= 2;} while (undobufferlength < n);
719 undobuffer = (char*)realloc(undobuffer, undobufferlength);
720 } else {
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
743 string.
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) {
757 int ul, om, op;
758 was_up_down = 0;
760 if (b<0) b = 0;
761 if (e<0) e = 0;
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) {
769 e++;
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);
781 if (e>b) {
782 if (undowidget == this && b == undoat) {
783 undobuffersize(undocut+(e-b));
784 memcpy(undobuffer+undocut, value_+b, e-b);
785 undocut += 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);
790 undocut += e-b;
791 } else if (undowidget == this && e == undoat && (e-b)<undoinsert) {
792 undoinsert -= e-b;
793 } else {
794 undobuffersize(e-b);
795 memcpy(undobuffer, value_+b, e-b);
796 undocut = e-b;
797 undoinsert = 0;
799 memmove(buffer+b, buffer+e, size_-e+1);
800 size_ -= e-b;
801 undowidget = this;
802 undoat = b;
803 if (input_type() == FL_SECRET_INPUT) yankcut = 0; else yankcut = undocut;
806 if (ilen) {
807 if (undowidget == this && b == undoat)
808 undoinsert += ilen;
809 else {
810 undocut = 0;
811 undoinsert = ilen;
813 memmove(buffer+b+ilen, buffer+b, size_-b+1);
814 memcpy(buffer+b, text, ilen);
815 size_ += ilen;
817 undowidget = this;
818 om = mark_;
819 op = position_;
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...
827 if (wrap()) {
828 // if there is a space in the pasted text, the whole line may have rewrapped
829 int i;
830 for (i=0; i<ilen; i++)
831 if (text[i]==' ') break;
832 if (i==ilen)
833 while (b > 0 && !isspace(index(b) & 255) && index(b)!='\n') b--;
834 else
835 while (b > 0 && index(b)!='\n') b--;
838 // make sure we redraw the old selection or cursor:
839 if (om < b) b = om;
840 if (op < b) b = op;
842 minimal_update(b);
844 mark_ = position_ = undoat;
846 set_changed();
847 if (when()&FL_WHEN_CHANGED) do_callback();
848 return 1;
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() {
859 was_up_down = 0;
860 if ( undowidget != this || (!undocut && !undoinsert) ) return 0;
862 int ilen = undocut;
863 int xlen = undoinsert;
864 int b = undoat-xlen;
865 int b1 = b;
867 put_in_buffer(size_+ilen);
869 if (ilen) {
870 memmove(buffer+b+ilen, buffer+b, size_-b+1);
871 memcpy(buffer+b, undobuffer, ilen);
872 size_ += ilen;
873 b += ilen;
876 if (xlen) {
877 undobuffersize(xlen);
878 memcpy(undobuffer, buffer+b, xlen);
879 memmove(buffer+b, buffer+b+xlen, size_-xlen-b+1);
880 size_ -= xlen;
883 undocut = xlen;
884 if (xlen) yankcut = xlen;
885 undoinsert = ilen;
886 undoat = b;
887 mark_ = b /* -ilen */;
888 position_ = b;
890 if (wrap())
891 while (b1 > 0 && index(b1)!='\n') b1--;
892 minimal_update(b1);
893 set_changed();
894 if (when()&FL_WHEN_CHANGED) do_callback();
895 return 1;
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);
912 return 1;
915 /** \internal
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)) {
920 do_callback();
924 /**
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) {
931 switch (event) {
933 case FL_ENTER:
934 case FL_MOVE:
935 if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT);
936 return 1;
938 case FL_LEAVE:
939 if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT);
940 return 1;
942 case FL_FOCUS:
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_);
948 return 1;
950 case FL_UNFOCUS:
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_);
956 case FL_HIDE:
957 fl_reset_spot();
958 if (!readonly() && (when() & FL_WHEN_RELEASE))
959 maybe_do_callback();
960 return 1;
962 case FL_PUSH:
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) {
968 Fl::focus(this);
969 handle(FL_FOCUS);
971 return 1;
973 case FL_DRAG:
974 handle_mouse(X, Y, W, H, 1);
975 return 1;
977 case FL_RELEASE:
978 copy(0);
979 return 1;
981 case FL_PASTE: {
982 // Don't allow pastes into readonly widgets...
983 if (readonly()) {
984 fl_beep(FL_BEEP_ERROR);
985 return 1;
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 ++;
998 const char *p = t;
999 if (*p == '+' || *p == '-') p ++;
1000 if (strncmp(p, "0x", 2) == 0) {
1001 p += 2;
1002 while (isxdigit(*p & 255) && p < e) p ++;
1003 } else {
1004 while (isdigit(*p & 255) && p < e) p ++;
1006 if (p < e) {
1007 fl_beep(FL_BEEP_ERROR);
1008 return 1;
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 ++;
1012 const char *p = t;
1013 if (*p == '+' || *p == '-') p ++;
1014 while (isdigit(*p & 255) && p < e) p ++;
1015 if (*p == '.') {
1016 p ++;
1017 while (isdigit(*p & 255) && p < e) p ++;
1018 if (*p == 'e' || *p == 'E') {
1019 p ++;
1020 if (*p == '+' || *p == '-') p ++;
1021 while (isdigit(*p & 255) && p < e) p ++;
1024 if (p < e) {
1025 fl_beep(FL_BEEP_ERROR);
1026 return 1;
1027 } else return replace(0, size(), t, e - t);
1029 return replace(position(), mark(), t, e-t);}
1031 case FL_SHORTCUT:
1032 if (!(shortcut() ? Fl::test_shortcut(shortcut()) : test_shortcut()))
1033 return 0;
1034 if (Fl::visible_focus() && handle(FL_FOCUS)) {
1035 Fl::focus(this);
1036 return 1;
1037 } // else fall through
1039 default:
1040 return 0;
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) {
1058 box(FL_DOWN_BOX);
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;
1066 bufsize = 0;
1067 buffer = 0;
1068 value_ = "";
1069 xscroll_ = yscroll_ = 0;
1070 maximum_size_ = 32767;
1071 shortcut_ = 0;
1072 set_flag(SHORTCUT_LABEL);
1073 tab_nav(1);
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) {
1083 buffer[size_] = 0;
1084 return;
1086 if (!bufsize) {
1087 if (len > size_) len += 9; // let a few characters insert before realloc
1088 bufsize = len+1;
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
1094 if (len > size_) {
1095 do {bufsize *= 2;} while (bufsize <= len);
1096 } else {
1097 bufsize = len+1;
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);
1110 buffer = nbuffer;
1112 memmove(buffer, value_, size_); buffer[size_] = 0;
1113 value_ = buffer;
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) {
1135 clear_changed();
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;
1141 minimal_update(0);
1142 } else {
1143 int i = 0;
1144 // find first different character:
1145 if (value_) {
1146 for (; i<size_ && i<len && str[i]==value_[i]; i++);
1147 if (i==size_ && i==len) return 0;
1149 minimal_update(i);
1151 value_ = str;
1152 size_ = len;
1153 } else { // empty new value:
1154 if (!size_) return 0; // both old and new are empty.
1155 size_ = 0;
1156 value_ = "";
1157 xscroll_ = yscroll_ = 0;
1158 minimal_update(0);
1160 position(readonly() ? 0 : size());
1161 return 1;
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);
1200 return r;
1203 /**
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);
1241 /** \internal
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() {
1246 int n = 1;
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
1250 if (n<=0) n = 1;
1252 return n;
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
1266 int len = 0;
1267 return fl_utf8decode(value_+i, value_+size_, &len);
1271 // End of "$Id: Fl_Input_.cxx 8413 2011-02-11 16:37:06Z manolo $".