source: rtems-graphics-toolkit/fltk-1.1.10/src/Fl_Input_.cxx @ 513eea1

Last change on this file since 513eea1 was 513eea1, checked in by Joel Sherrill <joel.sherrill@…>, on 01/09/10 at 22:43:24

2010-01-08 Joel Sherrill <joel.sherrill@…>

fltk 1.1.10. imported

  • ORIGIN: Updated.
  • Property mode set to 100644
File size: 25.1 KB
Line 
1//
2// "$Id$"
3//
4// Common input widget routines for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2006 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// This is the base class for Fl_Input.  You can use it directly
29// if you are one of those people who like to define their own
30// set of editing keys.  It may also be useful for adding scrollbars
31// to the input field.
32
33#include <FL/Fl.H>
34#include <FL/Fl_Input_.H>
35#include <FL/Fl_Window.H>
36#include <FL/fl_draw.H>
37#include <FL/fl_ask.H>
38#include <math.h>
39#include "flstring.h"
40#include <stdlib.h>
41#include <ctype.h>
42
43#define MAXBUF 1024
44
45extern void fl_draw(const char*, int, float, float);
46
47////////////////////////////////////////////////////////////////
48
49// Copy string p..e to the buffer, replacing characters with ^X and \nnn
50// as necessary.  Truncate if necessary so the resulting string and
51// null terminator fits in a buffer of size n.  Return new end pointer.
52const char* Fl_Input_::expand(const char* p, char* buf) const {
53  char* o = buf;
54  char* e = buf+(MAXBUF-4);
55  const char* lastspace = p;
56  char* lastspace_out = o;
57  int width_to_lastspace = 0;
58  int word_count = 0;
59  int word_wrap;
60
61  if (input_type()==FL_SECRET_INPUT) {
62    while (o<e && p < value_+size_) {*o++ = '*'; p++;}
63  } else while (o<e) {
64    if (wrap() && (p >= value_+size_ || isspace(*p & 255))) {
65      word_wrap = w() - Fl::box_dw(box()) - 2;
66      width_to_lastspace += (int)fl_width(lastspace_out, o-lastspace_out);
67      if (p > lastspace+1) {
68        if (word_count && width_to_lastspace > word_wrap) {
69          p = lastspace; o = lastspace_out; break;
70        }
71        word_count++;
72      }
73      lastspace = p;
74      lastspace_out = o;
75    }
76
77    if (p >= value_+size_) break;
78    int c = *p++ & 255;
79    if (c < ' ' || c == 127) {
80      if (c=='\n' && input_type()==FL_MULTILINE_INPUT) {p--; break;}
81      if (c == '\t' && input_type()==FL_MULTILINE_INPUT) {
82        for (c = (o-buf)%8; c<8 && o<e; c++) *o++ = ' ';
83      } else {
84        *o++ = '^';
85        *o++ = c ^ 0x40;
86      }
87#ifdef __APPLE__
88    // In MacRoman, all characters are defined, and non-break-space is 0xca
89    } else if (c == 0xCA) { // nbsp
90      *o++ = ' ';
91#else
92    // in ISO 8859-1, undefined characters are rendered as octal
93    // this is commented out since most X11 seems to use MSWindows Latin-1
94    //} else if (c >= 128 && c < 0xA0) {
95      // these codes are not defined in ISO code, so we output the octal code instead
96    //  *o++ = '\\';
97    //  *o++ = ((c>>6)&0x03) + '0';
98    //  *o++ = ((c>>3)&0x07) + '0';
99    //  *o++ = (c&0x07) + '0';
100    } else if (c == 0xA0) { // nbsp
101      *o++ = ' ';
102#endif
103    } else {
104      *o++ = c;
105    }
106  }
107  *o = 0;
108  return p;
109}
110
111// After filling in such a buffer, find the width to e
112double Fl_Input_::expandpos(
113  const char* p,        // real string
114  const char* e,        // pointer into real string
115  const char* buf,      // conversion of real string by expand()
116  int* returnn          // return offset into buf here
117) const {
118  int n = 0;
119  if (input_type()==FL_SECRET_INPUT) n = e-p;
120  else while (p<e) {
121    int c = *p++ & 255;
122    if (c < ' ' || c == 127) {
123      if (c == '\t' && input_type()==FL_MULTILINE_INPUT) n += 8-(n%8);
124      else n += 2;
125#ifdef __APPLE__
126    // in MacRoman, all characters are defined
127#else
128    // in Windows Latin-1 all characters are defined
129    //} else if (c >= 128 && c < 0xA0) {
130      // these codes are not defined in ISO code, so we output the octal code instead
131    //  n += 4;
132#endif
133    } else {
134      n++;
135    }
136  }
137  if (returnn) *returnn = n;
138  return fl_width(buf, n);
139}
140
141////////////////////////////////////////////////////////////////
142
143// minimal update:
144// Characters from mu_p to end of widget are redrawn.
145// If erase_cursor_only, small part at mu_p is redrawn.
146// Right now minimal update just keeps unchanged characters from
147// being erased, so they don't blink.
148
149void Fl_Input_::minimal_update(int p) {
150  if (damage() & FL_DAMAGE_ALL) return; // don't waste time if it won't be done
151  if (damage() & FL_DAMAGE_EXPOSE) {
152    if (p < mu_p) mu_p = p;
153  } else {
154    mu_p = p;
155  }
156
157  damage(FL_DAMAGE_EXPOSE);
158  erase_cursor_only = 0;
159}
160
161void Fl_Input_::minimal_update(int p, int q) {
162  if (q < p) p = q;
163  minimal_update(p);
164}
165
166////////////////////////////////////////////////////////////////
167
168static double up_down_pos;
169static int was_up_down;
170
171void Fl_Input_::setfont() const {
172 fl_font(textfont(), textsize());
173}
174
175void Fl_Input_::drawtext(int X, int Y, int W, int H) {
176  int do_mu = !(damage()&FL_DAMAGE_ALL);
177
178  if (Fl::focus()!=this && !size()) {
179    if (do_mu) { // we have to erase it if cursor was there
180      draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()),
181               W+Fl::box_dw(box()), H+Fl::box_dh(box()), color());
182    }
183    return;
184  }
185
186  int selstart, selend;
187  if (Fl::focus()!=this && /*Fl::selection_owner()!=this &&*/ Fl::pushed()!=this)
188    selstart = selend = 0;
189  else if (position() <= mark()) {
190    selstart = position(); selend = mark();
191  } else {
192    selend = position(); selstart = mark();
193  }
194
195  setfont();
196  const char *p, *e;
197  char buf[MAXBUF];
198
199  // count how many lines and put the last one into the buffer:
200  // And figure out where the cursor is:
201  int height = fl_height();
202  int lines;
203  int curx, cury;
204  for (p=value(), curx=cury=lines=0; ;) {
205    e = expand(p, buf);
206    if (position() >= p-value() && position() <= e-value()) {
207      curx = int(expandpos(p, value()+position(), buf, 0)+.5);
208      if (Fl::focus()==this && !was_up_down) up_down_pos = curx;
209      cury = lines*height;
210      int newscroll = xscroll_;
211      if (curx > newscroll+W-20) {
212        // figure out scrolling so there is space after the cursor:
213        newscroll = curx+20-W;
214        // figure out the furthest left we ever want to scroll:
215        int ex = int(expandpos(p, e, buf, 0))+2-W;
216        // use minimum of both amounts:
217        if (ex < newscroll) newscroll = ex;
218      } else if (curx < newscroll+20) {
219        newscroll = curx-20;
220      }
221      if (newscroll < 0) newscroll = 0;
222      if (newscroll != xscroll_) {
223        xscroll_ = newscroll;
224        mu_p = 0; erase_cursor_only = 0;
225      }
226    }
227    lines++;
228    if (e >= value_+size_) break;
229    p = e+1;
230  }
231
232  // adjust the scrolling:
233  if (input_type()==FL_MULTILINE_INPUT) {
234    int newy = yscroll_;
235    if (cury < newy) newy = cury;
236    if (cury > newy+H-height) newy = cury-H+height;
237    if (newy < -1) newy = -1;
238    if (newy != yscroll_) {yscroll_ = newy; mu_p = 0; erase_cursor_only = 0;}
239  } else {
240    yscroll_ = -(H-height)/2;
241  }
242
243  fl_clip(X, Y, W, H);
244  Fl_Color tc = active_r() ? textcolor() : fl_inactive(textcolor());
245
246  p = value();
247  // visit each line and draw it:
248  int desc = height-fl_descent();
249  float xpos = (float)(X - xscroll_ + 1);
250  int ypos = -yscroll_;
251  for (; ypos < H;) {
252
253    // re-expand line unless it is the last one calculated above:
254    if (lines>1) e = expand(p, buf);
255
256    if (ypos <= -height) goto CONTINUE; // clipped off top
257
258    if (do_mu) {        // for minimal update:
259      const char* pp = value()+mu_p; // pointer to where minimal update starts
260      if (e < pp) goto CONTINUE2; // this line is before the changes
261      if (readonly()) erase_cursor_only = 0; // this isn't the most efficient way
262      if (erase_cursor_only && p > pp) goto CONTINUE2; // this line is after
263      // calculate area to erase:
264      float r = (float)(X+W);
265      float xx;
266      if (p >= pp) {
267        xx = (float)X;
268        if (erase_cursor_only) r = xpos+2;
269        else if (readonly()) xx -= 3;
270      } else {
271        xx = xpos + (float)expandpos(p, pp, buf, 0);
272        if (erase_cursor_only) r = xx+2;
273        else if (readonly()) xx -= 3;
274      }
275      // clip to and erase it:
276      fl_push_clip((int)xx-1-height/8, Y+ypos, (int)(r-xx+2+height/4), height);
277      draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()),
278               W+Fl::box_dw(box()), H+Fl::box_dh(box()), color());
279      // it now draws entire line over it
280      // this should not draw letters to left of erased area, but
281      // that is nyi.
282    }
283
284    // Draw selection area if required:
285    if (selstart < selend && selstart <= e-value() && selend > p-value()) {
286      const char* pp = value()+selstart;
287      float x1 = xpos;
288      int offset1 = 0;
289      if (pp > p) {
290        fl_color(tc);
291        x1 += (float)expandpos(p, pp, buf, &offset1);
292        fl_draw(buf, offset1, xpos, (float)(Y+ypos+desc));
293      }
294      pp = value()+selend;
295      float x2 = (float)(X+W);
296      int offset2;
297      if (pp <= e) x2 = xpos + (float)expandpos(p, pp, buf, &offset2);
298      else offset2 = strlen(buf);
299      fl_color(selection_color());
300      fl_rectf((int)(x1+0.5), Y+ypos, (int)(x2-x1+0.5), height);
301      fl_color(fl_contrast(textcolor(), selection_color()));
302      fl_draw(buf+offset1, offset2-offset1, x1, (float)(Y+ypos+desc));
303      if (pp < e) {
304        fl_color(tc);
305        fl_draw(buf+offset2, strlen(buf+offset2), x2, (float)(Y+ypos+desc));
306      }
307    } else {
308      // draw unselected text
309      fl_color(tc);
310      fl_draw(buf, strlen(buf), xpos, (float)(Y+ypos+desc));
311    }
312
313    if (do_mu) fl_pop_clip();
314
315  CONTINUE2:
316    // draw the cursor:
317    if (Fl::focus() == this && selstart == selend &&
318        position() >= p-value() && position() <= e-value()) {
319      fl_color(cursor_color());
320      if (readonly()) {
321        fl_line((int)(xpos+curx-2.5f), Y+ypos+height-1,
322                (int)(xpos+curx+0.5f), Y+ypos+height-4,
323                (int)(xpos+curx+3.5f), Y+ypos+height-1);
324      } else {
325        fl_rectf((int)(xpos+curx+0.5), Y+ypos, 2, height);
326      }
327    }
328
329  CONTINUE:
330    ypos += height;
331    if (e >= value_+size_) break;
332    if (*e == '\n' || *e == ' ') e++;
333    p = e;
334  }
335
336  // for minimal update, erase all lines below last one if necessary:
337  if (input_type()==FL_MULTILINE_INPUT && do_mu && ypos<H
338      && (!erase_cursor_only || p <= value()+mu_p)) {
339    if (ypos < 0) ypos = 0;
340    fl_push_clip(X, Y+ypos, W, H-ypos);
341    draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()),
342             W+Fl::box_dw(box()), H+Fl::box_dh(box()), color());
343    fl_pop_clip();
344  }
345
346  fl_pop_clip();
347}
348
349static int isword(char c) {
350  return (c&128 || isalnum(c) || strchr("#%&-/@\\_~", c));
351}
352
353int Fl_Input_::word_end(int i) const {
354  if (input_type() == FL_SECRET_INPUT) return size();
355  //while (i < size() && !isword(index(i))) i++;
356  while (i < size() && isword(index(i))) i++;
357  return i;
358}
359
360int Fl_Input_::word_start(int i) const {
361  if (input_type() == FL_SECRET_INPUT) return 0;
362//   if (i >= size() || !isword(index(i)))
363//     while (i > 0 && !isword(index(i-1))) i--;
364  while (i > 0 && isword(index(i-1))) i--;
365  return i;
366}
367
368int Fl_Input_::line_end(int i) const {
369  if (input_type() != FL_MULTILINE_INPUT) return size();
370
371  if (wrap()) {
372    // go to the start of the paragraph:
373    int j = i;
374    while (j > 0 && index(j-1) != '\n') j--;
375    // now measure lines until we get past i, end of that line is real eol:
376    setfont();
377    for (const char* p=value()+j; ;) {
378      char buf[MAXBUF];
379      p = expand(p, buf);
380      if (p-value() >= i) return p-value();
381      p++;
382    }
383  } else {
384    while (i < size() && index(i) != '\n') i++;
385    return i;
386  }
387}
388
389int Fl_Input_::line_start(int i) const {
390  if (input_type() != FL_MULTILINE_INPUT) return 0;
391  int j = i;
392  while (j > 0 && index(j-1) != '\n') j--;
393  if (wrap()) {
394    // now measure lines until we get past i, start of that line is real eol:
395    setfont();
396    for (const char* p=value()+j; ;) {
397      char buf[MAXBUF];
398      const char* e = expand(p, buf);
399      if (e-value() >= i) return p-value();
400      p = e+1;
401    }
402  } else return j;
403}
404
405void Fl_Input_::handle_mouse(int X, int Y, int /*W*/, int /*H*/, int drag) {
406  was_up_down = 0;
407  if (!size()) return;
408  setfont();
409
410  const char *p, *e;
411  char buf[MAXBUF];
412
413  int theline = (input_type()==FL_MULTILINE_INPUT) ?
414    (Fl::event_y()-Y+yscroll_)/fl_height() : 0;
415
416  int newpos = 0;
417  for (p=value();; ) {
418    e = expand(p, buf);
419    theline--; if (theline < 0) break;
420    if (e >= value_+size_) break;
421    p = e+1;
422  }
423  const char *l, *r, *t; double f0 = Fl::event_x()-X+xscroll_;
424  for (l = p, r = e; l<r; ) {
425    double f;
426    t = l+(r-l+1)/2;
427    f = X-xscroll_+expandpos(p, t, buf, 0);
428    if (f <= Fl::event_x()) {l = t; f0 = Fl::event_x()-f;}
429    else r = t-1;
430  }
431  if (l < e) { // see if closer to character on right:
432    double f1 = X-xscroll_+expandpos(p, l+1, buf, 0)-Fl::event_x();
433    if (f1 < f0) l = l+1;
434  }
435  newpos = l-value();
436
437  int newmark = drag ? mark() : newpos;
438  if (Fl::event_clicks()) {
439    if (newpos >= newmark) {
440      if (newpos == newmark) {
441        if (newpos < size()) newpos++;
442        else newmark--;
443      }
444      if (Fl::event_clicks() > 1) {
445        newpos = line_end(newpos);
446        newmark = line_start(newmark);
447      } else {
448        newpos = word_end(newpos);
449        newmark = word_start(newmark);
450      }
451    } else {
452      if (Fl::event_clicks() > 1) {
453        newpos = line_start(newpos);
454        newmark = line_end(newmark);
455      } else {
456        newpos = word_start(newpos);
457        newmark = word_end(newmark);
458      }
459    }
460    // if the multiple click does not increase the selection, revert
461    // to single-click behavior:
462    if (!drag && (mark() > position() ?
463                  (newmark >= position() && newpos <= mark()) :
464                  (newmark >= mark() && newpos <= position()))) {
465      Fl::event_clicks(0);
466      newmark = newpos = l-value();
467    }
468  }
469  position(newpos, newmark);
470}
471
472int Fl_Input_::position(int p, int m) {
473  was_up_down = 0;
474  if (p<0) p = 0;
475  if (p>size()) p = size();
476  if (m<0) m = 0;
477  if (m>size()) m = size();
478  if (p == position_ && m == mark_) return 0;
479  //if (Fl::selection_owner() == this) Fl::selection_owner(0);
480  if (p != m) {
481    if (p != position_) minimal_update(position_, p);
482    if (m != mark_) minimal_update(mark_, m);
483  } else {
484    // new position is a cursor
485    if (position_ == mark_) {
486      // old position was just a cursor
487      if (Fl::focus() == this && !(damage()&FL_DAMAGE_EXPOSE)) {
488        minimal_update(position_); erase_cursor_only = 1;
489      }
490    } else { // old position was a selection
491      minimal_update(position_, mark_);
492    }
493  }
494  position_ = p;
495  mark_ = m;
496  return 1;
497}
498
499int Fl_Input_::up_down_position(int i, int keepmark) {
500  // unlike before, i must be at the start of the line already!
501
502  setfont();
503  char buf[MAXBUF];
504  const char* p = value()+i;
505  const char* e = expand(p, buf);
506  const char *l, *r, *t;
507  for (l = p, r = e; l<r; ) {
508    t = l+(r-l+1)/2;
509    int f = (int)expandpos(p, t, buf, 0);
510    if (f <= up_down_pos) l = t; else r = t-1;
511  }
512  int j = l-value();
513  j = position(j, keepmark ? mark_ : j);
514  was_up_down = 1;
515  return j;
516}
517
518int Fl_Input_::copy(int clipboard) {
519  int b = position();
520  int e = mark();
521  if (b != e) {
522    if (b > e) {b = mark(); e = position();}
523    if (input_type() == FL_SECRET_INPUT) e = b;
524    Fl::copy(value()+b, e-b, clipboard);
525    return 1;
526  }
527  return 0;
528}
529
530#define MAXFLOATSIZE 40
531
532static char* undobuffer;
533static int undobufferlength;
534static Fl_Input_* undowidget;
535static int undoat;      // points after insertion
536static int undocut;     // number of characters deleted there
537static int undoinsert;  // number of characters inserted
538static int yankcut;     // length of valid contents of buffer, even if undocut=0
539
540static void undobuffersize(int n) {
541  if (n > undobufferlength) {
542    if (undobuffer) {
543      do {undobufferlength *= 2;} while (undobufferlength < n);
544      undobuffer = (char*)realloc(undobuffer, undobufferlength);
545    } else {
546      undobufferlength = n+9;
547      undobuffer = (char*)malloc(undobufferlength);
548    }
549  }
550}
551
552// all changes go through here, delete characters b-e and insert text:
553int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
554
555  was_up_down = 0;
556
557  if (b<0) b = 0;
558  if (e<0) e = 0;
559  if (b>size_) b = size_;
560  if (e>size_) e = size_;
561  if (e<b) {int t=b; b=e; e=t;}
562  if (text && !ilen) ilen = strlen(text);
563  if (e<=b && !ilen) return 0; // don't clobber undo for a null operation
564  if (size_+ilen-(e-b) > maximum_size_) {
565    ilen = maximum_size_-size_+(e-b);
566    if (ilen < 0) ilen = 0;
567  }
568
569  put_in_buffer(size_+ilen);
570
571  if (e>b) {
572    if (undowidget == this && b == undoat) {
573      undobuffersize(undocut+(e-b));
574      memcpy(undobuffer+undocut, value_+b, e-b);
575      undocut += e-b;
576    } else if (undowidget == this && e == undoat && !undoinsert) {
577      undobuffersize(undocut+(e-b));
578      memmove(undobuffer+(e-b), undobuffer, undocut);
579      memcpy(undobuffer, value_+b, e-b);
580      undocut += e-b;
581    } else if (undowidget == this && e == undoat && (e-b)<undoinsert) {
582      undoinsert -= e-b;
583    } else {
584      undobuffersize(e-b);
585      memcpy(undobuffer, value_+b, e-b);
586      undocut = e-b;
587      undoinsert = 0;
588    }
589    memmove(buffer+b, buffer+e, size_-e+1);
590    size_ -= e-b;
591    undowidget = this;
592    undoat = b;
593    if (input_type() == FL_SECRET_INPUT) yankcut = 0; else yankcut = undocut;
594  }
595
596  if (ilen) {
597    if (undowidget == this && b == undoat)
598      undoinsert += ilen;
599    else {
600      undocut = 0;
601      undoinsert = ilen;
602    }
603    memmove(buffer+b+ilen, buffer+b, size_-b+1);
604    memcpy(buffer+b, text, ilen);
605    size_ += ilen;
606  }
607  undowidget = this;
608  undoat = b+ilen;
609
610  // Insertions into the word at the end of the line will cause it to
611  // wrap to the next line, so we must indicate that the changes may start
612  // right after the whitespace before the current word.  This will
613  // result in sub-optimal update when such wrapping does not happen
614  // but it is too hard to figure out for now...
615  if (wrap()) {
616    // if there is a space in the pasted text, the whole line may have rewrapped
617    int i;
618    for (i=0; i<ilen; i++)
619      if (text[i]==' ') break;
620    if (i==ilen)
621      while (b > 0 && !isspace(index(b) & 255) && index(b)!='\n') b--;
622    else
623      while (b > 0 && index(b)!='\n') b--;
624  }
625
626  // make sure we redraw the old selection or cursor:
627  if (mark_ < b) b = mark_;
628  if (position_ < b) b = position_;
629
630  minimal_update(b);
631
632  mark_ = position_ = undoat;
633
634  set_changed();
635  if (when()&FL_WHEN_CHANGED) do_callback();
636  return 1;
637}
638
639int Fl_Input_::undo() {
640  was_up_down = 0;
641  if (undowidget != this || !undocut && !undoinsert) return 0;
642
643  int ilen = undocut;
644  int xlen = undoinsert;
645  int b = undoat-xlen;
646  int b1 = b;
647
648  put_in_buffer(size_+ilen);
649
650  if (ilen) {
651    memmove(buffer+b+ilen, buffer+b, size_-b+1);
652    memcpy(buffer+b, undobuffer, ilen);
653    size_ += ilen;
654    b += ilen;
655  }
656
657  if (xlen) {
658    undobuffersize(xlen);
659    memcpy(undobuffer, buffer+b, xlen);
660    memmove(buffer+b, buffer+b+xlen, size_-xlen-b+1);
661    size_ -= xlen;
662  }
663
664  undocut = xlen;
665  if (xlen) yankcut = xlen;
666  undoinsert = ilen;
667  undoat = b;
668  mark_ = b /* -ilen */;
669  position_ = b;
670
671  if (wrap())
672    while (b1 > 0 && index(b1)!='\n') b1--;
673  minimal_update(b1);
674  set_changed();
675  if (when()&FL_WHEN_CHANGED) do_callback();
676  return 1;
677}
678
679int Fl_Input_::copy_cuts() {
680  // put the yank buffer into the X clipboard
681  if (!yankcut || input_type()==FL_SECRET_INPUT) return 0;
682  Fl::copy(undobuffer, yankcut, 1);
683  return 1;
684}
685
686void Fl_Input_::maybe_do_callback() {
687  if (changed() || (when()&FL_WHEN_NOT_CHANGED)) {
688    do_callback();
689  }
690}
691
692int Fl_Input_::handletext(int event, int X, int Y, int W, int H) {
693  switch (event) {
694
695  case FL_ENTER:
696  case FL_MOVE:
697    if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT);
698    return 1;
699
700  case FL_LEAVE:
701    if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT);
702    return 1;
703
704  case FL_FOCUS:
705    if (mark_ == position_) {
706      minimal_update(size()+1);
707    } else //if (Fl::selection_owner() != this)
708      minimal_update(mark_, position_);
709    return 1;
710
711  case FL_UNFOCUS:
712    if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT);
713    if (mark_ == position_) {
714      if (!(damage()&FL_DAMAGE_EXPOSE)) {minimal_update(position_); erase_cursor_only = 1;}
715    } else //if (Fl::selection_owner() != this)
716      minimal_update(mark_, position_);
717  case FL_HIDE:
718    if (!readonly() && (when() & FL_WHEN_RELEASE))
719      maybe_do_callback();
720    return 1;
721
722  case FL_PUSH:
723    if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT);
724
725    handle_mouse(X, Y, W, H, Fl::event_state(FL_SHIFT));
726
727    if (Fl::focus() != this) {
728      Fl::focus(this);
729      handle(FL_FOCUS);
730    }
731    return 1;
732
733  case FL_DRAG:
734    handle_mouse(X, Y, W, H, 1);
735    return 1;
736
737  case FL_RELEASE:
738    copy(0);
739    return 1;
740
741  case FL_PASTE: {
742    // Don't allow pastes into readonly widgets...
743    if (readonly()) {
744      fl_beep(FL_BEEP_ERROR);
745      return 1;
746    }
747
748    // See if we have anything to paste...
749    if (!Fl::event_text() || !Fl::event_length()) return 1;
750
751    // strip trailing control characters and spaces before pasting:
752    const char* t = Fl::event_text();
753    const char* e = t+Fl::event_length();
754    if (input_type() != FL_MULTILINE_INPUT) while (e > t && isspace(*(e-1) & 255)) e--;
755    if (!t || e <= t) return 1; // Int/float stuff will crash without this test
756    if (input_type() == FL_INT_INPUT) {
757      while (isspace(*t & 255) && t < e) t ++;
758      const char *p = t;
759      if (*p == '+' || *p == '-') p ++;
760      if (strncmp(p, "0x", 2) == 0) {
761        p += 2;
762        while (isxdigit(*p & 255) && p < e) p ++;
763      } else {
764        while (isdigit(*p & 255) && p < e) p ++;
765      }
766      if (p < e) {
767        fl_beep(FL_BEEP_ERROR);
768        return 1;
769      } else return replace(0, size(), t, e - t);
770    } else if (input_type() == FL_FLOAT_INPUT) {
771      while (isspace(*t & 255) && t < e) t ++;
772      const char *p = t;
773      if (*p == '+' || *p == '-') p ++;
774      while (isdigit(*p & 255) && p < e) p ++;
775      if (*p == '.') {
776        p ++;
777        while (isdigit(*p & 255) && p < e) p ++;
778        if (*p == 'e' || *p == 'E') {
779          p ++;
780          if (*p == '+' || *p == '-') p ++;
781          while (isdigit(*p & 255) && p < e) p ++;
782        }
783      }
784      if (p < e) {
785        fl_beep(FL_BEEP_ERROR);
786        return 1;
787      } else return replace(0, size(), t, e - t);
788    }
789    return replace(position(), mark(), t, e-t);}
790
791  default:
792    return 0;
793  }
794}
795
796/*------------------------------*/
797
798Fl_Input_::Fl_Input_(int X, int Y, int W, int H, const char* l)
799: Fl_Widget(X, Y, W, H, l) {
800  box(FL_DOWN_BOX);
801  color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
802  align(FL_ALIGN_LEFT);
803  textsize_ = (uchar)FL_NORMAL_SIZE;
804  textfont_ = FL_HELVETICA;
805  textcolor_ = FL_FOREGROUND_COLOR;
806  cursor_color_ = FL_FOREGROUND_COLOR; // was FL_BLUE
807  mark_ = position_ = size_ = 0;
808  bufsize = 0;
809  buffer  = 0;
810  value_ = "";
811  xscroll_ = yscroll_ = 0;
812  maximum_size_ = 32767;
813}
814
815void Fl_Input_::put_in_buffer(int len) {
816  if (value_ == buffer && bufsize > len) {
817    buffer[size_] = 0;
818    return;
819  }
820  if (!bufsize) {
821    if (len > size_) len += 9; // let a few characters insert before realloc
822    bufsize = len+1;
823    buffer = (char*)malloc(bufsize);
824  } else if (bufsize <= len) {
825    // we may need to move old value in case it points into buffer:
826    int moveit = (value_ >= buffer && value_ < buffer+bufsize);
827    // enlarge current buffer
828    if (len > size_) {
829      do {bufsize *= 2;} while (bufsize <= len);
830    } else {
831      bufsize = len+1;
832    }
833    // Note: the following code is equivalent to:
834    //
835    //   if (moveit) value_ = value_ - buffer;
836    //   char* nbuffer = (char*)realloc(buffer, bufsize);
837    //   if (moveit) value_ = value_ + nbuffer;
838    //   buffer = nbuffer;
839    //
840    // We just optimized the pointer arithmetic for value_...
841    //
842    char* nbuffer = (char*)realloc(buffer, bufsize);
843    if (moveit) value_ += (nbuffer-buffer);
844    buffer = nbuffer;
845  }
846  memmove(buffer, value_, size_); buffer[size_] = 0;
847  value_ = buffer;
848}
849
850int Fl_Input_::static_value(const char* str, int len) {
851  clear_changed();
852  if (undowidget == this) undowidget = 0;
853  if (str == value_ && len == size_) return 0;
854  if (len) { // non-empty new value:
855    if (xscroll_ || yscroll_) {
856      xscroll_ = yscroll_ = 0;
857      minimal_update(0);
858    } else {
859      int i = 0;
860      // find first different character:
861      if (value_) {
862        for (; i<size_ && i<len && str[i]==value_[i]; i++);
863        if (i==size_ && i==len) return 0;
864      }
865      minimal_update(i);
866    }
867    value_ = str;
868    size_ = len;
869  } else { // empty new value:
870    if (!size_) return 0; // both old and new are empty.
871    size_ = 0;
872    value_ = "";
873    xscroll_ = yscroll_ = 0;
874    minimal_update(0);
875  }
876  position(readonly() ? 0 : size());
877  return 1;
878}
879
880int Fl_Input_::static_value(const char* str) {
881  return static_value(str, str ? strlen(str) : 0);
882}
883
884int Fl_Input_::value(const char* str, int len) {
885  int r = static_value(str, len);
886  if (len) put_in_buffer(len);
887  return r;
888}
889
890int Fl_Input_::value(const char* str) {
891  return value(str, str ? strlen(str) : 0);
892}
893
894void Fl_Input_::resize(int X, int Y, int W, int H) {
895  if (W != w()) xscroll_ = 0;
896  if (H != h()) yscroll_ = 0;
897  Fl_Widget::resize(X, Y, W, H);
898}
899
900Fl_Input_::~Fl_Input_() {
901  if (undowidget == this) undowidget = 0;
902  if (bufsize) free((void*)buffer);
903}
904
905//
906// End of "$Id$".
907//
Note: See TracBrowser for help on using the repository browser.