blob: 276c38754aa7f90263266ccd5fc7008c8427711f [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
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.
12//
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.
17//
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.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
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>
38
39#define MAXBUF 1024
40#if defined(USE_X11) && !USE_XFT
41const int secret_char = '*'; // asterisk to hide secret input
42#else
43const int secret_char = 0x2022; // bullet to hide secret input
44#endif
45static int l_secret;
46
47extern void fl_draw(const char*, int, float, float);
48
49////////////////////////////////////////////////////////////////
50
51/** \internal
52 Converts a given text segment into the text that will be rendered on screen.
53
54 This copies the text from \p p to \p buf, replacing characters with <tt>^X</tt>
55 and <tt>\\nnn</tt> as necessary.
56
57 The destination buffer is limited to \c MAXBUF (currently at 1024). All
58 following text is truncated.
59
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
63*/
64const 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);
73
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;
79 }
80 p++;
81 }
82
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;
90 }
91 word_count++;
92 }
93 lastspace = p;
94 lastspace_out = o;
95 }
96
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++ = ' ';
104 }
105 } else {
106 *o++ = '^';
107 *o++ = c ^ 0x40;
108 }
109 } else {
110 *o++ = c;
111 }
112 }
113 *o = 0;
114 return p;
115}
116
117/** \internal
118 Calculates the width in pixels of part of a text buffer.
119
120 This call takes a string, usually created by expand, and calculates
121 the width of the string when rendered with the given font.
122
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
127*/
128double 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;
142 }
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++;
152 }
153 chr += fl_utf8len((char)p[0]) >= 1;
154 p++;
155 }
156 if (returnn) *returnn = n;
157 return fl_width(buf, n);
158}
159
160////////////////////////////////////////////////////////////////
161
162/** \internal
163 Marks a range of characters for update.
164
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.
168
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.
173
174 \param [in] p start of update range
175*/
176void 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;
182 }
183
184 damage(FL_DAMAGE_EXPOSE);
185 erase_cursor_only = 0;
186}
187
188/** \internal
189 Marks a range of characters for update.
190
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.
193
194 \param [in] p start of update range
195 \param [in] q end of update range
196*/
197void Fl_Input_::minimal_update(int p, int q) {
198 if (q < p) p = q;
199 minimal_update(p);
200}
201
202////////////////////////////////////////////////////////////////
203
204/* Horizontal cursor position in pixels while moving up or down. */
205double Fl_Input_::up_down_pos = 0;
206
207/* Flag to remember last cursor move. */
208int Fl_Input_::was_up_down = 0;
209
210/**
211 Sets the current font and font size.
212*/
213void Fl_Input_::setfont() const {
214 fl_font(textfont(), textsize());
215}
216
217/**
218 Draws the text in the passed bounding box.
219
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.
223
224 \param X, Y, W, H area that must be redrawn
225*/
226void Fl_Input_::drawtext(int X, int Y, int W, int H) {
227 int do_mu = !(damage()&FL_DAMAGE_ALL);
228
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());
233 }
234 return;
235 }
236
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();
244 }
245
246 setfont();
247 const char *p, *e;
248 char buf[MAXBUF];
249
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;
272 }
273 if (newscroll < 0) newscroll = 0;
274 if (newscroll != xscroll_) {
275 xscroll_ = newscroll;
276 mu_p = 0; erase_cursor_only = 0;
277 }
278 }
279 lines++;
280 if (e >= value_+size_) break;
281 p = e+1;
282 }
283
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;
293 }
294
295 fl_push_clip(X, Y, W, H);
296 Fl_Color tc = active_r() ? textcolor() : fl_inactive(textcolor());
297
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;) {
304
305 // re-expand line unless it is the last one calculated above:
306 if (lines>1) e = expand(p, buf);
307
308 if (ypos <= -height) goto CONTINUE; // clipped off top
309
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;
326 }
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.
334 }
335
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));
345 }
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));
358 }
359 } else {
360 // draw unselected text
361 fl_color(tc);
362 fl_draw(buf, strlen(buf), xpos, (float)(Y+ypos+desc));
363 }
364
365 if (do_mu) fl_pop_clip();
366
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);
380 }
381 }
382
383 CONTINUE:
384 ypos += height;
385 if (e >= value_+size_) break;
386 if (*e == '\n' || *e == ' ') e++;
387 p = e;
388 }
389
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();
398 }
399
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());
404 }
405}
406
407/** \internal
408 Simple function that determines if a character could be part of a word.
409 \todo This function is not ucs4-aware.
410*/
411static int isword(char c) {
412 return (c&128 || isalnum(c) || strchr("#%&-/@\\_~", c));
413}
414
415/**
416 Finds the end of a word.
417
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.
421
422 \param [in] i starting index for the search
423 \return end of the word
424*/
425int 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;
431}
432
433/**
434 Finds the start of a word.
435
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.
439
440 \param [in] i starting index for the search
441 \return start of the word
442*/
443int 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;
450}
451
452/**
453 Finds the end of a line.
454
455 This call calculates the end of a line based on the given
456 index \p i.
457
458 \param [in] i starting index for the search
459 \return end of the line
460*/
461int Fl_Input_::line_end(int i) const {
462 if (input_type() != FL_MULTILINE_INPUT) return size();
463
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++;
475 }
476 } else {
477 while (i < size() && index(i) != '\n') i++;
478 return i;
479 }
480}
481
482/**
483 Finds the start of a line.
484
485 This call calculates the start of a line based on the given
486 index \p i.
487
488 \param [in] i starting index for the search
489 \return start of the line
490*/
491int 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;
503 }
504 } else return j;
505}
506
507/**
508 Handles mouse clicks and mouse moves.
509 \todo Add comment and parameters
510*/
511void 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();
515
516 const char *p, *e;
517 char buf[MAXBUF];
518
519 int theline = (input_type()==FL_MULTILINE_INPUT) ?
520 (Fl::event_y()-Y+yscroll_)/fl_height() : 0;
521
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;
528 }
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;
538 }
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;
545 }
546 }
547 newpos = l-value();
548
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--;
555 }
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);
562 }
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);
570 }
571 }
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();
579 }
580 }
581 position(newpos, newmark);
582}
583
584/**
585 Sets the index for the cursor and mark.
586
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).
592
593 Changing these values causes a redraw(). The new
594 values are bounds checked.
595
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)
600*/
601int 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;
609
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]);
616 }
617
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]);
624 }
625 if (is_same) m = p;
626 if (p == position_ && m == mark_) return 0;
627
628
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;
639 }
640 } else { // old position was a selection
641 minimal_update(position_, mark_);
642 }
643 }
644 position_ = p;
645 mark_ = m;
646 return 1;
647}
648
649/**
650 Moves the cursor to the column given by \p up_down_pos.
651
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.
656
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
660*/
661int Fl_Input_::up_down_position(int i, int keepmark) {
662 // unlike before, i must be at the start of the line already!
663
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;
673 }
674 int j = l-value();
675 j = position(j, keepmark ? mark_ : j);
676 was_up_down = 1;
677 return j;
678}
679
680/**
681 Put the current selection into the clipboard.
682
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.
688
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)
692*/
693int 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;
701 }
702 return 0;
703}
704
705#define MAXFLOATSIZE 40
706
707static char* undobuffer;
708static int undobufferlength;
709static Fl_Input_* undowidget;
710static int undoat; // points after insertion
711static int undocut; // number of characters deleted there
712static int undoinsert; // number of characters inserted
713static int yankcut; // length of valid contents of buffer, even if undocut=0
714
715static 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);
723 }
724 }
725}
726
727/**
728 Deletes text from \p b to \p e and inserts the new string \p text.
729
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.
736
737 Set \p b and \p e equal to not delete anything.
738 Set \p text to \c NULL to not insert anything.
739
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.
744
745 \p b and \p e are clamped to the
746 <tt>0..size()</tt> range, so it is safe to pass any values.
747
748 cut() and insert() are just inline functions that call replace().
749
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
755*/
756int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
757 int ul, om, op;
758 was_up_down = 0;
759
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]);
771 }
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;
777 }
778
779 put_in_buffer(size_+ilen);
780
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;
798 }
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;
804 }
805
806 if (ilen) {
807 if (undowidget == this && b == undoat)
808 undoinsert += ilen;
809 else {
810 undocut = 0;
811 undoinsert = ilen;
812 }
813 memmove(buffer+b+ilen, buffer+b, size_-b+1);
814 memcpy(buffer+b, text, ilen);
815 size_ += ilen;
816 }
817 undowidget = this;
818 om = mark_;
819 op = position_;
820 mark_ = position_ = undoat = b+ilen;
821
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--;
836 }
837
838 // make sure we redraw the old selection or cursor:
839 if (om < b) b = om;
840 if (op < b) b = op;
841
842 minimal_update(b);
843
844 mark_ = position_ = undoat;
845
846 set_changed();
847 if (when()&FL_WHEN_CHANGED) do_callback();
848 return 1;
849}
850
851/**
852 Undoes previous changes to the text buffer.
853
854 This call undoes a number of previous calls to replace().
855
856 \return non-zero if any change was made.
857*/
858int Fl_Input_::undo() {
859 was_up_down = 0;
860 if ( undowidget != this || (!undocut && !undoinsert) ) return 0;
861
862 int ilen = undocut;
863 int xlen = undoinsert;
864 int b = undoat-xlen;
865 int b1 = b;
866
867 put_in_buffer(size_+ilen);
868
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;
874 }
875
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;
881 }
882
883 undocut = xlen;
884 if (xlen) yankcut = xlen;
885 undoinsert = ilen;
886 undoat = b;
887 mark_ = b /* -ilen */;
888 position_ = b;
889
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;
896}
897
898/**
899 Copies the \e yank buffer to the clipboard.
900
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.
904
905 \return 0 if the operation did not change the clipboard
906 \see copy(int), cut()
907*/
908int 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;
913}
914
915/** \internal
916 Checks the when() field and does a callback if indicated.
917*/
918void Fl_Input_::maybe_do_callback() {
919 if (changed() || (when()&FL_WHEN_NOT_CHANGED)) {
920 do_callback();
921 }
922}
923
924/**
925 Handles all kinds of text field related events.
926
927 This is called by derived classes.
928 \todo Add comment and parameters
929*/
930int Fl_Input_::handletext(int event, int X, int Y, int W, int H) {
931 switch (event) {
932
933 case FL_ENTER:
934 case FL_MOVE:
935 if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT);
936 return 1;
937
938 case FL_LEAVE:
939 if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT);
940 return 1;
941
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;
949
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;
961
962 case FL_PUSH:
963 if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT);
964
965 handle_mouse(X, Y, W, H, Fl::event_state(FL_SHIFT));
966
967 if (Fl::focus() != this) {
968 Fl::focus(this);
969 handle(FL_FOCUS);
970 }
971 return 1;
972
973 case FL_DRAG:
974 handle_mouse(X, Y, W, H, 1);
975 return 1;
976
977 case FL_RELEASE:
978 copy(0);
979 return 1;
980
981 case FL_PASTE: {
982 // Don't allow pastes into readonly widgets...
983 if (readonly()) {
984 fl_beep(FL_BEEP_ERROR);
985 return 1;
986 }
987
988 // See if we have anything to paste...
989 if (!Fl::event_text() || !Fl::event_length()) return 1;
990
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 ++;
1005 }
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 ++;
1022 }
1023 }
1024 if (p < e) {
1025 fl_beep(FL_BEEP_ERROR);
1026 return 1;
1027 } else return replace(0, size(), t, e - t);
1028 }
1029 return replace(position(), mark(), t, e-t);}
1030
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
1038
1039 default:
1040 return 0;
1041 }
1042}
1043
1044/*------------------------------*/
1045
1046/**
1047 Creates a new Fl_Input_ widget.
1048
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.
1052
1053 \param X, Y, W, H the dimensions of the new widget
1054 \param l an optional label text
1055*/
1056Fl_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);
1074}
1075
1076/**
1077 Copies the value from a possibly static entry into the internal buffer.
1078
1079 \param [in] len size of the current text
1080*/
1081void Fl_Input_::put_in_buffer(int len) {
1082 if (value_ == buffer && bufsize > len) {
1083 buffer[size_] = 0;
1084 return;
1085 }
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;
1098 }
1099 // Note: the following code is equivalent to:
1100 //
1101 // if (moveit) value_ = value_ - buffer;
1102 // char* nbuffer = (char*)realloc(buffer, bufsize);
1103 // if (moveit) value_ = value_ + nbuffer;
1104 // buffer = nbuffer;
1105 //
1106 // We just optimized the pointer arithmetic for value_...
1107 //
1108 char* nbuffer = (char*)realloc(buffer, bufsize);
1109 if (moveit) value_ += (nbuffer-buffer);
1110 buffer = nbuffer;
1111 }
1112 memmove(buffer, value_, size_); buffer[size_] = 0;
1113 value_ = buffer;
1114}
1115
1116/**
1117 Changes the widget text.
1118
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.
1126
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.
1129
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
1133*/
1134int 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;
1148 }
1149 minimal_update(i);
1150 }
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);
1159 }
1160 position(readonly() ? 0 : size());
1161 return 1;
1162}
1163
1164/**
1165 Changes the widget text.
1166
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.
1174
1175 \param [in] str the new text
1176 \return non-zero if the new value is different than the current one
1177*/
1178int Fl_Input_::static_value(const char* str) {
1179 return static_value(str, str ? strlen(str) : 0);
1180}
1181
1182/**
1183 Changes the widget text.
1184
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 "".
1188
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.
1191
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()
1196*/
1197int 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;
1201}
1202
1203/**
1204 Changes the widget text.
1205
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 "".
1209
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()
1213*/
1214int Fl_Input_::value(const char* str) {
1215 return value(str, str ? strlen(str) : 0);
1216}
1217
1218/**
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)
1223*/
1224void 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);
1228}
1229
1230/**
1231 Destroys the widget.
1232
1233 The destructor clears all allocated buffers and removes the widget
1234 from the parent Fl_Group.
1235*/
1236Fl_Input_::~Fl_Input_() {
1237 if (undowidget == this) undowidget = 0;
1238 if (bufsize) free((void*)buffer);
1239}
1240
1241/** \internal
1242 Returns the number of lines displayed on a single page.
1243 \return widget height divided by the font height
1244*/
1245int 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;
1251 }
1252 return n;
1253}
1254
1255/**
1256 Returns the character at index \p i.
1257
1258 This function returns the UTF-8 character at \p i
1259 as a ucs4 character code.
1260
1261 \param [in] i index into the value field
1262 \return the character at index \p i
1263*/
1264unsigned int Fl_Input_::index(int i) const
1265{
1266 int len = 0;
1267 return fl_utf8decode(value_+i, value_+size_, &len);
1268}
1269
1270//
1271// End of "$Id: Fl_Input_.cxx 8413 2011-02-11 16:37:06Z manolo $".
1272//