Thank you for your work in this string handling problem . But I am urgen with my application. So I did'nt want to wait the final version of UTF8. I had spend several days, modified the Fl_Input_.cxx and Fl_Input.cxx in 1.1.9 to adapt to chinese character process. What I do indeed is add a condition in existing code, when encounter a char greater than 0x80 try to interpret it as an 2 byte chinese character. It involve line sliting, inserting, deleting ,selecting, cursor moving etc. Now it seems good to me (I did'nt test exhausit).
the following is code, for your evaluation: ==============Fl_Input_.cxx================================== // // "$Id: Fl_Input_.cxx 6104 2008-04-21 20:54:37Z matt $" // // Common input widget routines for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2006 by Bill Spitzak and others. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please report all bugs and problems on the following page: // // http://www.fltk.org/str.php // // This is the base class for Fl_Input. You can use it directly // if you are one of those people who like to define their own // set of editing keys. It may also be useful for adding scrollbars // to the input field. #include <FL/Fl.H> #include <FL/Fl_Input_.H> #include <FL/Fl_Window.H> #include <FL/fl_draw.H> #include <FL/fl_ask.H> #include <math.h> #include "flstring.h" #include <stdlib.h> #include <ctype.h> #define MAXBUF 1024 extern void fl_draw(const char*, int, float, float); //////////////////////////////////////////////////////////////// // Copy string p..e to the buffer, replacing characters with ^X and \nnn // as necessary. Truncate if necessary so the resulting string and // null terminator fits in a buffer of size n. Return new end pointer. const char* Fl_Input_::expand(const char* p, char* buf) const { char* o = buf; char* e = buf+(MAXBUF-4); const char* lastspace = p; char* lastspace_out = o; int width_to_lastspace = 0; int word_count = 0; int word_wrap; //comment by chenqg: remember start of p; const char* start = p; for(char *wipe=o;wipe<e;wipe++) *wipe=0; /* comment by chenqg p for values string, buf for display string. if input type is secret ,i.e. password, display string will be mask by star */ if (input_type()==FL_SECRET_INPUT) { while (o<e && p < value_+size_) {*o++ = '*'; p++;} // comment by chenqg: mask buf by star } else while (o<e) { //expand string from p to buf if (wrap() && (p >= value_+size_ || isspace(*p & 255) || (*p & 128))) //comment by chenqg: if input set to wrap mode and now a word is exceed width ,need to be wrap , further more , if it is a chinese character ,it also need to be wrap treated like 2 bytes width word { word_wrap = w() - Fl::box_dw(box()) - 2; //comment by chenqg: input's width sub box frame width and 2 //width_to_lastspace += (int)fl_width(lastspace_out, o-lastspace_out); //comment by chenqg: calculate in display string, the word width in pixel width_to_lastspace = (int)fl_width(buf, o-buf); if (p > lastspace+1) { if (word_count && width_to_lastspace > word_wrap) { p = lastspace; o = lastspace_out; break; } word_count++; } if ((*p & 128) ){//comment by chenqg: remember the last space position in string p and buf int tmp; tmp = p-start; chineseCharRePositionLeast(start,tmp); if(start+tmp < p){//comment by chenqg: if current p is second byte of a chinese char lastspace = p-2; lastspace_out = o-2; } else{//comment by chenqg: if current p is fisrt byte of a chinese char lastspace = p-1; lastspace_out = o-1; } } else{ lastspace = p; lastspace_out = o; } } if (p >= value_+size_) break; // comment by chenqg: provent out of range int c = *p++ & 255; if (c < ' ' || c == 127) { if (c=='\n' && input_type()==FL_MULTILINE_INPUT) {p--; break;} if (c == '\t' && input_type()==FL_MULTILINE_INPUT) { for (c = (o-buf)%8; c<8 && o<e; c++) *o++ = ' '; // comment by chenqg: replace \t with space to tab column } else { // comment by chenqg: replace control charactor with ^ *o++ = '^'; *o++ = c ^ 0x40; } #ifdef __APPLE__ // In MacRoman, all characters are defined, and non-break-space is 0xca } else if (c == 0xCA) { // nbsp *o++ = ' '; #else // in ISO 8859-1, undefined characters are rendered as octal // this is commented out since most X11 seems to use MSWindows Latin-1 //} else if (c >= 128 && c < 0xA0) { // these codes are not defined in ISO code, so we output the octal code instead // *o++ = '\\'; // *o++ = ((c>>6)&0x03) + '0'; // *o++ = ((c>>3)&0x07) + '0'; // *o++ = (c&0x07) + '0'; } else if (c == 0xA0) { // nbsp *o++ = ' '; #endif } else { *o++ = c; } } //*o=0; comment by chenqg: the last o maybe a chinese char, cannot replace by 0; if (o[0] &128) o[1] = 0; else o[0] = 0; return p; } // After filling in such a buffer, find the width to e double Fl_Input_::expandpos( const char* p, // real string const char* e, // pointer into real string const char* buf, // conversion of real string by expand() int* returnn // return offset into buf here ) const { int n = 0; if (input_type()==FL_SECRET_INPUT) n = e-p; else while (p<e) { int c = *p++ & 255; if (c < ' ' || c == 127) { if (c == '\t' && input_type()==FL_MULTILINE_INPUT) n += 8-(n%8); else n += 2; #ifdef __APPLE__ // in MacRoman, all characters are defined #else // in Windows Latin-1 all characters are defined //} else if (c >= 128 && c < 0xA0) { // these codes are not defined in ISO code, so we output the octal code instead // n += 4; #endif } else { n++; } } if (returnn) *returnn = n; return fl_width(buf, n); } //////////////////////////////////////////////////////////////// // minimal update: // Characters from mu_p to end of widget are redrawn. // If erase_cursor_only, small part at mu_p is redrawn. // Right now minimal update just keeps unchanged characters from // being erased, so they don't blink. void Fl_Input_::minimal_update(int p) { if (damage() & FL_DAMAGE_ALL) return; // don't waste time if it won't be done if (damage() & FL_DAMAGE_EXPOSE) { if (p < mu_p) mu_p = p; } else { mu_p = p; } damage(FL_DAMAGE_EXPOSE); erase_cursor_only = 0; } void Fl_Input_::minimal_update(int p, int q) { if (q < p) p = q; minimal_update(p); } //////////////////////////////////////////////////////////////// static double up_down_pos; static int was_up_down; void Fl_Input_::setfont() const { fl_font(textfont(), textsize()); } void Fl_Input_::drawtext(int X, int Y, int W, int H) { int do_mu = !(damage()&FL_DAMAGE_ALL); if (Fl::focus()!=this && !size()) { if (do_mu) { // we have to erase it if cursor was there draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()), W+Fl::box_dw(box()), H+Fl::box_dh(box()), color()); } return; } int selstart, selend; if (Fl::focus()!=this && /*Fl::selection_owner()!=this &&*/ Fl::pushed()!=this) selstart = selend = 0; else if (position() <= mark()) { selstart = position(); selend = mark(); } else { selend = position(); selstart = mark(); } setfont(); const char *p, *e; char buf[MAXBUF]; // count how many lines and put the last one into the buffer: // And figure out where the cursor is: int height = fl_height(); int lines; int curx, cury; for (p=value(), curx=cury=lines=0; ;) { e = expand(p, buf); if (position() >= p-value() && position() <= e-value()) { curx = int(expandpos(p, value()+position(), buf, 0)+.5); if (Fl::focus()==this && !was_up_down) up_down_pos = curx; cury = lines*height; int newscroll = xscroll_; if (curx > newscroll+W-20) { // figure out scrolling so there is space after the cursor: newscroll = curx+20-W; // figure out the furthest left we ever want to scroll: int ex = int(expandpos(p, e, buf, 0))+2-W; // use minimum of both amounts: if (ex < newscroll) newscroll = ex; } else if (curx < newscroll+20) { newscroll = curx-20; } if (newscroll < 0) newscroll = 0; if (newscroll != xscroll_) { xscroll_ = newscroll; mu_p = 0; erase_cursor_only = 0; } } lines++; if (e >= value_+size_) break; p = e+1; } // adjust the scrolling: if (input_type()==FL_MULTILINE_INPUT) { int newy = yscroll_; if (cury < newy) newy = cury; if (cury > newy+H-height) newy = cury-H+height; if (newy < -1) newy = -1; if (newy != yscroll_) {yscroll_ = newy; mu_p = 0; erase_cursor_only = 0;} } else { yscroll_ = -(H-height)/2; } fl_clip(X, Y, W, H); Fl_Color tc = active_r() ? textcolor() : fl_inactive(textcolor()); p = value(); // visit each line and draw it: int desc = height-fl_descent(); float xpos = (float)(X - xscroll_ + 1); int ypos = -yscroll_; for (; ypos < H;) { // re-expand line unless it is the last one calculated above: if (lines>1) e = expand(p, buf); if (ypos <= -height) goto CONTINUE; // clipped off top if (do_mu) { // for minimal update: const char* pp = value()+mu_p; // pointer to where minimal update starts if (e < pp) goto CONTINUE2; // this line is before the changes if (readonly()) erase_cursor_only = 0; // this isn't the most efficient way if (erase_cursor_only && p > pp) goto CONTINUE2; // this line is after // calculate area to erase: float r = (float)(X+W); float xx; if (p >= pp) { xx = (float)X; if (erase_cursor_only) r = xpos+2; else if (readonly()) xx -= 3; } else { xx = xpos + (float)expandpos(p, pp, buf, 0); if (erase_cursor_only) r = xx+2; else if (readonly()) xx -= 3; } // clip to and erase it: fl_push_clip((int)xx-1-height/8, Y+ypos, (int)(r-xx+2+height/4), height); draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()), W+Fl::box_dw(box()), H+Fl::box_dh(box()), color()); // it now draws entire line over it // this should not draw letters to left of erased area, but // that is nyi. } // Draw selection area if required: if (selstart < selend && selstart <= e-value() && selend > p-value()) { const char* pp = value()+selstart; float x1 = xpos; int offset1 = 0; if (pp > p) { fl_color(tc); x1 += (float)expandpos(p, pp, buf, &offset1); fl_draw(buf, offset1, xpos, (float)(Y+ypos+desc)); } pp = value()+selend; float x2 = (float)(X+W); int offset2; if (pp <= e) x2 = xpos + (float)expandpos(p, pp, buf, &offset2); else offset2 = strlen(buf); fl_color(selection_color()); fl_rectf((int)(x1+0.5), Y+ypos, (int)(x2-x1+0.5), height); fl_color(fl_contrast(textcolor(), selection_color())); fl_draw(buf+offset1, offset2-offset1, x1, (float)(Y+ypos+desc)); if (pp < e) { fl_color(tc); fl_draw(buf+offset2, strlen(buf+offset2), x2, (float)(Y+ypos+desc)); } } else { // draw unselected text fl_color(tc); fl_draw(buf, strlen(buf), xpos, (float)(Y+ypos+desc)); } if (do_mu) fl_pop_clip(); CONTINUE2: // draw the cursor: if (Fl::focus() == this && selstart == selend && position() >= p-value() && position() <= e-value()) { fl_color(cursor_color()); if (readonly()) { fl_line((int)(xpos+curx-2.5f), Y+ypos+height-1, (int)(xpos+curx+0.5f), Y+ypos+height-4, (int)(xpos+curx+3.5f), Y+ypos+height-1); } else { fl_rectf((int)(xpos+curx+0.5), Y+ypos, 2, height); } } CONTINUE: ypos += height; if (e >= value_+size_) break; // if (*e == '\n' || *e == ' ') e++; // p = e; p =e+1; } // for minimal update, erase all lines below last one if necessary: if (input_type()==FL_MULTILINE_INPUT && do_mu && ypos<H && (!erase_cursor_only || p <= value()+mu_p)) { if (ypos < 0) ypos = 0; fl_push_clip(X, Y+ypos, W, H-ypos); draw_box(box(), X-Fl::box_dx(box()), Y-Fl::box_dy(box()), W+Fl::box_dw(box()), H+Fl::box_dh(box()), color()); fl_pop_clip(); } fl_pop_clip(); } static int isword(char c) { return (c&128 || isalnum(c) || strchr("#%&-/@\\_~", c)); } int Fl_Input_::word_end(int i) const { if (input_type() == FL_SECRET_INPUT) return size(); //while (i < size() && !isword(index(i))) i++; while (i < size() && isword(index(i))) i++; return i; } int Fl_Input_::word_start(int i) const { if (input_type() == FL_SECRET_INPUT) return 0; // if (i >= size() || !isword(index(i))) // while (i > 0 && !isword(index(i-1))) i--; while (i > 0 && isword(index(i-1))) i--; return i; } int Fl_Input_::line_end(int i) const { if (input_type() != FL_MULTILINE_INPUT) return size(); if (wrap()) { // go to the start of the paragraph: int j = i; while (j > 0 && index(j-1) != '\n') j--; // now measure lines until we get past i, end of that line is real eol: setfont(); for (const char* p=value()+j; ;) { char buf[MAXBUF]; p = expand(p, buf); if (p-value() >= i) return p-value(); p++; } } else { while (i < size() && index(i) != '\n') i++; return i; } } int Fl_Input_::line_start(int i) const { if (input_type() != FL_MULTILINE_INPUT) return 0; int j = i; while (j > 0 && index(j-1) != '\n') j--; if (wrap()) { // now measure lines until we get past i, start of that line is real eol: setfont(); for (const char* p=value()+j; ;) { char buf[MAXBUF]; const char* e = expand(p, buf); if (e-value() >= i) return p-value(); p = e+1; } } else return j; } void Fl_Input_::handle_mouse(int X, int Y, int /*W*/, int /*H*/, int drag) { was_up_down = 0; if (!size()) return; setfont(); const char *p, *e; char buf[MAXBUF]; int theline = (input_type()==FL_MULTILINE_INPUT) ? (Fl::event_y()-Y+yscroll_)/fl_height() : 0; int newpos = 0; for (p=value();; ) { e = expand(p, buf); theline--; if (theline < 0) break; if (e >= value_+size_) break; p = e+1; } const char *l, *r, *t; double f0 = Fl::event_x()-X+xscroll_; for (l = p, r = e; l<r; ) { double f; t = l+(r-l+1)/2; f = X-xscroll_+expandpos(p, t, buf, 0); if (f <= Fl::event_x()) {l = t; f0 = Fl::event_x()-f;} else r = t-1; } if (l < e) { // see if closer to character on right: double f1 = X-xscroll_+expandpos(p, l+1, buf, 0)-Fl::event_x(); if (f1 < f0) l = l+1; } newpos = l-value(); int newmark = drag ? mark() : newpos; if (Fl::event_clicks()) { if (newpos >= newmark) { if (newpos == newmark) { if (newpos < size()) newpos++; else newmark--; } if (Fl::event_clicks() > 1) { newpos = line_end(newpos); newmark = line_start(newmark); } else { newpos = word_end(newpos); newmark = word_start(newmark); } } else { if (Fl::event_clicks() > 1) { newpos = line_start(newpos); newmark = line_end(newmark); } else { newpos = word_start(newpos); newmark = word_end(newmark); } } // if the multiple click does not increase the selection, revert // to single-click behavior: if (!drag && (mark() > position() ? (newmark >= position() && newpos <= mark()) : (newmark >= mark() && newpos <= position()))) { Fl::event_clicks(0); newmark = newpos = l-value(); } } chineseCharRePositionLeast(p-1,newpos); position(newpos, newmark); } //comment by chenqg: if p position is a chinese character, need to reposistion to that start of end of the chinese character, but not in the middle // in reposition least , p will reposition to start of chinese character, while reposition great will reposition to the end. void chineseCharRePositionLeast(const char *start,int &p) { if (start[p] &128){ int i,curs; //comment by chenqg: search from back to front , to find the tart of chinese character for(i=p;i >= 0;i --){ if ( !(start[i] &128) ) break; } curs = p-i; if ((curs/2)*2==curs ) p--; //if odd(curs) , p must sub 1 } } void chineseCharRePositionGreat(const char *start,int &p) { if (start[p] &128){ int p1=p; chineseCharRePositionLeast(start,p1); if (p1 < p ) p++; } } int Fl_Input_::position(int p, int m) { was_up_down = 0; if (p<0) p = 0; if (p>size()) p = size(); if (m<0) m = 0; if (m>size()) m = size(); chineseCharRePositionLeast(value(),p); //comment by chenqg: to check if it is chinese character ,need to reposition chineseCharRePositionLeast(value(),m);//comment by chenqg: to check if it is chinese character ,need to reposition if (p == position_ && m == mark_) return 0; //if (Fl::selection_owner() == this) Fl::selection_owner(0); if (p != m) { if (p != position_) minimal_update(position_, p); if (m != mark_) minimal_update(mark_, m); } else { // new position is a cursor if (position_ == mark_) { // old position was just a cursor if (Fl::focus() == this && !(damage()&FL_DAMAGE_EXPOSE)) { minimal_update(position_); erase_cursor_only = 1; } } else { // old position was a selection minimal_update(position_, mark_); } } position_ = p; mark_ = m; return 1; } int Fl_Input_::up_down_position(int i, int keepmark) { // unlike before, i must be at the start of the line already! setfont(); char buf[MAXBUF]; const char* p = value()+i; const char* e = expand(p, buf); const char *l, *r, *t; for (l = p, r = e; l<r; ) { t = l+(r-l+1)/2; int f = (int)expandpos(p, t, buf, 0); if (f <= up_down_pos) l = t; else r = t-1; } int j = l-value(); j = position(j, keepmark ? mark_ : j); was_up_down = 1; return j; } int Fl_Input_::copy(int clipboard) { int b = position(); int e = mark(); if (b != e) { if (b > e) {b = mark(); e = position();} if (input_type() == FL_SECRET_INPUT) e = b; Fl::copy(value()+b, e-b, clipboard); return 1; } return 0; } #define MAXFLOATSIZE 40 static char* undobuffer; static int undobufferlength; static Fl_Input_* undowidget; static int undoat; // points after insertion static int undocut; // number of characters deleted there static int undoinsert; // number of characters inserted static int yankcut; // length of valid contents of buffer, even if undocut=0 static void undobuffersize(int n) { if (n > undobufferlength) { if (undobuffer) { do {undobufferlength *= 2;} while (undobufferlength < n); undobuffer = (char*)realloc(undobuffer, undobufferlength); } else { undobufferlength = n+9; undobuffer = (char*)malloc(undobufferlength); } } } // all changes go through here, delete characters b-e and insert text: int Fl_Input_::replace(int b, int e, const char* text, int ilen) { was_up_down = 0; if (b<0) b = 0; if (e<0) e = 0; if (b>size_) b = size_; if (e>size_) e = size_; chineseCharRePositionLeast(value(),b);//comment by chenqg: reposition for chinese characters chineseCharRePositionLeast(value(),e); if (e<b) {int t=b; b=e; e=t;} if (text && !ilen) ilen = strlen(text); if (e<=b && !ilen) return 0; // don't clobber undo for a null operation if (size_+ilen-(e-b) > maximum_size_) { ilen = maximum_size_-size_+(e-b); if (ilen < 0) ilen = 0; } put_in_buffer(size_+ilen); if (e>b) { if (undowidget == this && b == undoat) { undobuffersize(undocut+(e-b)); memcpy(undobuffer+undocut, value_+b, e-b); undocut += e-b; } else if (undowidget == this && e == undoat && !undoinsert) { undobuffersize(undocut+(e-b)); memmove(undobuffer+(e-b), undobuffer, undocut); memcpy(undobuffer, value_+b, e-b); undocut += e-b; } else if (undowidget == this && e == undoat && (e-b)<undoinsert) { undoinsert -= e-b; } else { undobuffersize(e-b); memcpy(undobuffer, value_+b, e-b); undocut = e-b; undoinsert = 0; } memmove(buffer+b, buffer+e, size_-e+1); size_ -= e-b; undowidget = this; undoat = b; if (input_type() == FL_SECRET_INPUT) yankcut = 0; else yankcut = undocut; } if (ilen) { if (undowidget == this && b == undoat) undoinsert += ilen; else { undocut = 0; undoinsert = ilen; } memmove(buffer+b+ilen, buffer+b, size_-b+1); memcpy(buffer+b, text, ilen); size_ += ilen; } undowidget = this; undoat = b+ilen; // Insertions into the word at the end of the line will cause it to // wrap to the next line, so we must indicate that the changes may start // right after the whitespace before the current word. This will // result in sub-optimal update when such wrapping does not happen // but it is too hard to figure out for now... if (wrap()) { // if there is a space in the pasted text, the whole line may have rewrapped int i; for (i=0; i<ilen; i++) if (text[i]==' ') break; if (i==ilen) while (b > 0 && !isspace(index(b) & 255) && index(b)!='\n') b--; else while (b > 0 && index(b)!='\n') b--; } // make sure we redraw the old selection or cursor: if (mark_ < b) b = mark_; if (position_ < b) b = position_; minimal_update(b); mark_ = position_ = undoat; set_changed(); if (when()&FL_WHEN_CHANGED) do_callback(); return 1; } int Fl_Input_::undo() { was_up_down = 0; if (undowidget != this || !undocut && !undoinsert) return 0; int ilen = undocut; int xlen = undoinsert; int b = undoat-xlen; int b1 = b; put_in_buffer(size_+ilen); if (ilen) { memmove(buffer+b+ilen, buffer+b, size_-b+1); memcpy(buffer+b, undobuffer, ilen); size_ += ilen; b += ilen; } if (xlen) { undobuffersize(xlen); memcpy(undobuffer, buffer+b, xlen); memmove(buffer+b, buffer+b+xlen, size_-xlen-b+1); size_ -= xlen; } undocut = xlen; if (xlen) yankcut = xlen; undoinsert = ilen; undoat = b; mark_ = b /* -ilen */; position_ = b; if (wrap()) while (b1 > 0 && index(b1)!='\n') b1--; minimal_update(b1); set_changed(); if (when()&FL_WHEN_CHANGED) do_callback(); return 1; } int Fl_Input_::copy_cuts() { // put the yank buffer into the X clipboard if (!yankcut || input_type()==FL_SECRET_INPUT) return 0; Fl::copy(undobuffer, yankcut, 1); return 1; } void Fl_Input_::maybe_do_callback() { if (changed() || (when()&FL_WHEN_NOT_CHANGED)) { do_callback(); } } int Fl_Input_::handletext(int event, int X, int Y, int W, int H) { switch (event) { case FL_ENTER: case FL_MOVE: if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT); return 1; case FL_LEAVE: if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT); return 1; case FL_FOCUS: if (mark_ == position_) { minimal_update(size()+1); } else //if (Fl::selection_owner() != this) minimal_update(mark_, position_); return 1; case FL_UNFOCUS: if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT); if (mark_ == position_) { if (!(damage()&FL_DAMAGE_EXPOSE)) {minimal_update(position_); erase_cursor_only = 1;} } else //if (Fl::selection_owner() != this) minimal_update(mark_, position_); case FL_HIDE: if (!readonly() && (when() & FL_WHEN_RELEASE)) maybe_do_callback(); return 1; case FL_PUSH: if (active_r() && window()) window()->cursor(FL_CURSOR_INSERT); handle_mouse(X, Y, W, H, Fl::event_state(FL_SHIFT)); if (Fl::focus() != this) { Fl::focus(this); handle(FL_FOCUS); } return 1; case FL_DRAG: handle_mouse(X, Y, W, H, 1); return 1; case FL_RELEASE: copy(0); return 1; case FL_PASTE: { // Don't allow pastes into readonly widgets... if (readonly()) { fl_beep(FL_BEEP_ERROR); return 1; } // See if we have anything to paste... if (!Fl::event_text() || !Fl::event_length()) return 1; // strip trailing control characters and spaces before pasting: const char* t = Fl::event_text(); const char* e = t+Fl::event_length(); if (input_type() != FL_MULTILINE_INPUT) while (e > t && isspace(*(e-1) & 255)) e--; if (!t || e <= t) return 1; // Int/float stuff will crash without this test if (input_type() == FL_INT_INPUT) { while (isspace(*t & 255) && t < e) t ++; const char *p = t; if (*p == '+' || *p == '-') p ++; if (strncmp(p, "0x", 2) == 0) { p += 2; while (isxdigit(*p & 255) && p < e) p ++; } else { while (isdigit(*p & 255) && p < e) p ++; } if (p < e) { fl_beep(FL_BEEP_ERROR); return 1; } else return replace(0, size(), t, e - t); } else if (input_type() == FL_FLOAT_INPUT) { while (isspace(*t & 255) && t < e) t ++; const char *p = t; if (*p == '+' || *p == '-') p ++; while (isdigit(*p & 255) && p < e) p ++; if (*p == '.') { p ++; while (isdigit(*p & 255) && p < e) p ++; if (*p == 'e' || *p == 'E') { p ++; if (*p == '+' || *p == '-') p ++; while (isdigit(*p & 255) && p < e) p ++; } } if (p < e) { fl_beep(FL_BEEP_ERROR); return 1; } else return replace(0, size(), t, e - t); } return replace(position(), mark(), t, e-t);} default: return 0; } } /*------------------------------*/ Fl_Input_::Fl_Input_(int X, int Y, int W, int H, const char* l) : Fl_Widget(X, Y, W, H, l) { box(FL_DOWN_BOX); color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR); align(FL_ALIGN_LEFT); textsize_ = (uchar)FL_NORMAL_SIZE; textfont_ = FL_HELVETICA; textcolor_ = FL_FOREGROUND_COLOR; cursor_color_ = FL_FOREGROUND_COLOR; // was FL_BLUE mark_ = position_ = size_ = 0; bufsize = 0; buffer = 0; value_ = ""; xscroll_ = yscroll_ = 0; maximum_size_ = 32767; } void Fl_Input_::put_in_buffer(int len) { if (value_ == buffer && bufsize > len) { buffer[size_] = 0; return; } if (!bufsize) { if (len > size_) len += 9; // let a few characters insert before realloc bufsize = len+1; buffer = (char*)malloc(bufsize); } else if (bufsize <= len) { // we may need to move old value in case it points into buffer: int moveit = (value_ >= buffer && value_ < buffer+bufsize); // enlarge current buffer if (len > size_) { do {bufsize *= 2;} while (bufsize <= len); } else { bufsize = len+1; } // Note: the following code is equivalent to: // // if (moveit) value_ = value_ - buffer; // char* nbuffer = (char*)realloc(buffer, bufsize); // if (moveit) value_ = value_ + nbuffer; // buffer = nbuffer; // // We just optimized the pointer arithmetic for value_... // char* nbuffer = (char*)realloc(buffer, bufsize); if (moveit) value_ += (nbuffer-buffer); buffer = nbuffer; } memmove(buffer, value_, size_); buffer[size_] = 0; value_ = buffer; } int Fl_Input_::static_value(const char* str, int len) { clear_changed(); if (undowidget == this) undowidget = 0; if (str == value_ && len == size_) return 0; if (len) { // non-empty new value: if (xscroll_ || yscroll_) { xscroll_ = yscroll_ = 0; minimal_update(0); } else { int i = 0; // find first different character: if (value_) { for (; i<size_ && i<len && str[i]==value_[i]; i++); if (i==size_ && i==len) return 0; } minimal_update(i); } value_ = str; size_ = len; } else { // empty new value: if (!size_) return 0; // both old and new are empty. size_ = 0; value_ = ""; xscroll_ = yscroll_ = 0; minimal_update(0); } position(readonly() ? 0 : size()); return 1; } int Fl_Input_::static_value(const char* str) { return static_value(str, str ? strlen(str) : 0); } int Fl_Input_::value(const char* str, int len) { int r = static_value(str, len); if (len) put_in_buffer(len); return r; } int Fl_Input_::value(const char* str) { return value(str, str ? strlen(str) : 0); } void Fl_Input_::resize(int X, int Y, int W, int H) { if (W != w()) xscroll_ = 0; if (H != h()) yscroll_ = 0; Fl_Widget::resize(X, Y, W, H); } Fl_Input_::~Fl_Input_() { if (undowidget == this) undowidget = 0; if (bufsize) free((void*)buffer); } // // End of "$Id: Fl_Input_.cxx 6104 2008-04-21 20:54:37Z matt $". // ==================Fl_Input.cxx=========================== // // "$Id: Fl_Input.cxx 6103 2008-04-21 20:42:51Z matt $" // // Input widget for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2006 by Bill Spitzak and others. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 // USA. // // Please report all bugs and problems on the following page: // // http://www.fltk.org/str.php // // This is the "user interface", it decodes user actions into what to // do to the text. See also Fl_Input_.cxx, where the text is actually // manipulated (and some ui, in particular the mouse, is done...). // In theory you can replace this code with another subclass to change // the keybindings. #include <stdio.h> #include <stdlib.h> #include <FL/Fl.H> #include <FL/Fl_Window.H> #include <FL/Fl_Input.H> #include <FL/fl_draw.H> #include <FL/fl_ask.H> #include "flstring.h" #ifdef HAVE_LOCALE_H # include <locale.h> #endif void Fl_Input::draw() { if (input_type() == FL_HIDDEN_INPUT) return; Fl_Boxtype b = box(); if (damage() & FL_DAMAGE_ALL) draw_box(b, color()); Fl_Input_::drawtext(x()+Fl::box_dx(b), y()+Fl::box_dy(b), w()-Fl::box_dw(b), h()-Fl::box_dh(b)); } // kludge so shift causes selection to extend: int Fl_Input::shift_position(int p) { return position(p, Fl::event_state(FL_SHIFT) ? mark() : p); } int Fl_Input::shift_up_down_position(int p) { return up_down_position(p, Fl::event_state(FL_SHIFT)); } // If you define this symbol as zero you will get the peculiar fltk // behavior where moving off the end of an input field will move the // cursor into the next field: // define it as 1 to prevent cursor movement from going to next field: #define NORMAL_INPUT_MOVE 0 #define ctrl(x) ((x)^0x40) // List of characters that are legal in a floating point input field. // This text string is created at run-time to take the current locale // into account (for example, continental Europe uses a comma instead // of a decimal point). For back compatibility reasons, we always // allow the decimal point. #ifdef HAVE_LOCALECONV static const char *standard_fp_chars = ".eE+-"; static const char *legal_fp_chars = 0L; #else static const char *legal_fp_chars = ".eE+-"; #endif int Fl_Input::handle_key() { char ascii = Fl::event_text()[0]; int repeat_num=1; int del; if (Fl::compose(del)) { // Insert characters into numeric fields after checking for legality: if (input_type() == FL_FLOAT_INPUT || input_type() == FL_INT_INPUT) { Fl::compose_reset(); // ignore any foreign letters... // initialize the list of legal characters inside a floating point number #ifdef HAVE_LOCALECONV if (!legal_fp_chars) { int len = strlen(standard_fp_chars); struct lconv *lc = localeconv(); if (lc) { if (lc->decimal_point) len += strlen(lc->decimal_point); if (lc->mon_decimal_point) len += strlen(lc->mon_decimal_point); if (lc->positive_sign) len += strlen(lc->positive_sign); if (lc->negative_sign) len += strlen(lc->negative_sign); } // the following line is not a true memory leak because the array is only // allocated once if required, and automatically freed when the program quits char *chars = (char*)malloc(len+1); legal_fp_chars = chars; strcpy(chars, standard_fp_chars); if (lc) { if (lc->decimal_point) strcat(chars, lc->decimal_point); if (lc->mon_decimal_point) strcat(chars, lc->mon_decimal_point); if (lc->positive_sign) strcat(chars, lc->positive_sign); if (lc->negative_sign) strcat(chars, lc->negative_sign); } } #endif // HAVE_LOCALECONV // This is complex to allow "0xff12" hex to be typed: if (!position() && (ascii == '+' || ascii == '-') || (ascii >= '0' && ascii <= '9') || (position()==1 && index(0)=='0' && (ascii=='x' || ascii == 'X')) || (position()>1 && index(0)=='0' && (index(1)=='x'||index(1)=='X') && (ascii>='A'&& ascii<='F' || ascii>='a'&& ascii<='f')) || input_type()==FL_FLOAT_INPUT && ascii && strchr(legal_fp_chars, ascii)) { if (readonly()) fl_beep(); else replace(position(), mark(), &ascii, 1); } return 1; } if (del || Fl::event_length()) { if (readonly()) fl_beep(); else replace(position(), del ? position()-del : mark(), Fl::event_text(), Fl::event_length()); } return 1; } switch (Fl::event_key()) { case FL_Insert: if (Fl::event_state() & FL_CTRL) ascii = ctrl('C'); else if (Fl::event_state() & FL_SHIFT) ascii = ctrl('V'); break; case FL_Delete: if (Fl::event_state() & FL_SHIFT) ascii = ctrl('X'); else ascii = ctrl('D'); break; case FL_Left: ascii = ctrl('B'); break; case FL_Right: ascii = ctrl('F'); break; case FL_Page_Up: fl_font(textfont(),textsize()); //ensure current font is set to ours repeat_num=h()/fl_height(); // number of lines to scroll if (!repeat_num) repeat_num=1; case FL_Up: ascii = ctrl('P'); break; case FL_Page_Down: fl_font(textfont(),textsize()); repeat_num=h()/fl_height(); if (!repeat_num) repeat_num=1; case FL_Down: ascii = ctrl('N'); break; case FL_Home: if (Fl::event_state() & FL_CTRL) { shift_position(0); return 1; } ascii = ctrl('A'); break; case FL_End: if (Fl::event_state() & FL_CTRL) { shift_position(size()); return 1; } ascii = ctrl('E'); break; case FL_BackSpace: ascii = ctrl('H'); break; case FL_Enter: case FL_KP_Enter: if (when() & FL_WHEN_ENTER_KEY) { position(size(), 0); maybe_do_callback(); return 1; } else if (input_type() == FL_MULTILINE_INPUT && !readonly()) return replace(position(), mark(), "\n", 1); else return 0; // reserved for shortcuts case FL_Tab: if (Fl::event_state(FL_CTRL|FL_SHIFT) || input_type()!=FL_MULTILINE_INPUT || readonly()) return 0; return replace(position(), mark(), &ascii, 1); #ifdef __APPLE__ case 'c' : case 'v' : case 'x' : case 'z' : // printf("'%c' (0x%02x) pressed with%s%s%s%s\n", ascii, ascii, // Fl::event_state(FL_SHIFT) ? " FL_SHIFT" : "", // Fl::event_state(FL_CTRL) ? " FL_CTRL" : "", // Fl::event_state(FL_ALT) ? " FL_ALT" : "", // Fl::event_state(FL_META) ? " FL_META" : ""); if (Fl::event_state(FL_META)) ascii -= 0x60; // printf("using '%c' (0x%02x)...\n", ascii, ascii); break; #endif // __APPLE__ } int i,pp; switch (ascii) { case ctrl('A'): return shift_position(line_start(position())) + NORMAL_INPUT_MOVE; case ctrl('B'): //commet by chenqg: if is chinese charactor , move in 2 byte step pp=position()-1; chineseCharRePositionLeast(value(),pp); return shift_position(pp) + NORMAL_INPUT_MOVE; case ctrl('C'): // copy return copy(1); case ctrl('D'): case ctrl('?'): if (readonly()) { fl_beep(); return 1; } //commet by chenqg: if is chinese charactor , delete in 2 byte step pp = position()+1; chineseCharRePositionGreat(value(),pp); if (pp == position()+2) pp=2; else pp=1; if (mark() != position()) return cut(); else return cut(pp); case ctrl('E'): return shift_position(line_end(position())) + NORMAL_INPUT_MOVE; case ctrl('F'): //commet by chenqg: if is chinese charactor , move in 2 byte step pp=position()+1; chineseCharRePositionGreat(value(),pp); return shift_position(pp) + NORMAL_INPUT_MOVE; case ctrl('H'): if (readonly()) { fl_beep(); return 1; } if (mark() != position()) cut(); else cut(-1); return 1; case ctrl('K'): if (readonly()) { fl_beep(); return 1; } if (position()>=size()) return 0; i = line_end(position()); if (i == position() && i < size()) i++; cut(position(), i); return copy_cuts(); case ctrl('N'): i = position(); if (line_end(i) >= size()) return NORMAL_INPUT_MOVE; while (repeat_num--) { i = line_end(i); if (i >= size()) break; i++; } shift_up_down_position(i); return 1; case ctrl('P'): i = position(); if (!line_start(i)) return NORMAL_INPUT_MOVE; while(repeat_num--) { i = line_start(i); if (!i) break; i--; } shift_up_down_position(line_start(i)); return 1; case ctrl('U'): if (readonly()) { fl_beep(); return 1; } return cut(0, size()); case ctrl('V'): case ctrl('Y'): if (readonly()) { fl_beep(); return 1; } Fl::paste(*this, 1); return 1; case ctrl('X'): case ctrl('W'): if (readonly()) { fl_beep(); return 1; } copy(1); return cut(); case ctrl('Z'): case ctrl('_'): if (readonly()) { fl_beep(); return 1; } return undo(); case ctrl('I'): case ctrl('J'): case ctrl('L'): case ctrl('M'): if (readonly()) { fl_beep(); return 1; } // insert a few selected control characters literally: if (input_type() != FL_FLOAT_INPUT && input_type() != FL_INT_INPUT) return replace(position(), mark(), &ascii, 1); } return 0; } int Fl_Input::handle(int event) { static int dnd_save_position, dnd_save_mark, drag_start = -1, newpos; static Fl_Widget *dnd_save_focus; switch (event) { case FL_FOCUS: switch (Fl::event_key()) { case FL_Right: position(0); break; case FL_Left: position(size()); break; case FL_Down: up_down_position(0); break; case FL_Up: up_down_position(line_start(size())); break; case FL_Tab: case 0xfe20: // XK_ISO_Left_Tab position(size(),0); break; default: position(position(),mark());// turns off the saved up/down arrow position break; } break; case FL_KEYBOARD: if (Fl::event_key() == FL_Tab && mark() != position()) { // Set the current cursor position to the end of the selection... if (mark() > position()) position(mark()); else position(position()); return (1); } else { if (active_r() && window() && this == Fl::belowmouse()) window()->cursor(FL_CURSOR_NONE); return handle_key(); } case FL_PUSH: if (Fl::dnd_text_ops()) { int oldpos = position(), oldmark = mark(); Fl_Boxtype b = box(); Fl_Input_::handle_mouse( x()+Fl::box_dx(b), y()+Fl::box_dy(b), w()-Fl::box_dw(b), h()-Fl::box_dh(b), 0); newpos = position(); position( oldpos, oldmark ); if (Fl::focus()==this && !Fl::event_state(FL_SHIFT) && input_type()!=FL_SECRET_INPUT && (newpos >= mark() && newpos < position() || newpos >= position() && newpos < mark())) { // user clicked in the selection, may be trying to drag drag_start = newpos; return 1; } drag_start = -1; } if (Fl::focus() != this) { Fl::focus(this); handle(FL_FOCUS); } break; case FL_DRAG: if (Fl::dnd_text_ops()) { if (drag_start >= 0) { if (Fl::event_is_click()) return 1; // debounce the mouse // save the position because sometimes we don't get DND_ENTER: dnd_save_position = position(); dnd_save_mark = mark(); // drag the data: copy(0); Fl::dnd(); return 1; } } break; case FL_RELEASE: if (Fl::event_button() == 2) { Fl::event_is_click(0); // stop double click from picking a word Fl::paste(*this, 0); } else if (!Fl::event_is_click()) { // copy drag-selected text to the clipboard. copy(0); } else if (Fl::event_is_click() && drag_start >= 0) { // user clicked in the field and wants to reset the cursor position... position(drag_start, drag_start); drag_start = -1; } else if (Fl::event_clicks()) { // user double or triple clicked to select word or whole text copy(0); } // For output widgets, do the callback so the app knows the user // did something with the mouse... if (readonly()) do_callback(); return 1; case FL_DND_ENTER: Fl::belowmouse(this); // send the leave events first dnd_save_position = position(); dnd_save_mark = mark(); dnd_save_focus = Fl::focus(); if (dnd_save_focus != this) { Fl::focus(this); handle(FL_FOCUS); } // fall through: case FL_DND_DRAG: //int p = mouse_position(X, Y, W, H); #if DND_OUT_XXXX if (Fl::focus()==this && (p>=dnd_save_position && p<=dnd_save_mark || p>=dnd_save_mark && p<=dnd_save_position)) { position(dnd_save_position, dnd_save_mark); return 0; } #endif { Fl_Boxtype b = box(); Fl_Input_::handle_mouse( x()+Fl::box_dx(b), y()+Fl::box_dy(b), w()-Fl::box_dw(b), h()-Fl::box_dh(b), 0); } return 1; case FL_DND_LEAVE: position(dnd_save_position, dnd_save_mark); #if DND_OUT_XXXX if (!focused()) #endif if (dnd_save_focus != this) { Fl::focus(dnd_save_focus); handle(FL_UNFOCUS); } return 1; case FL_DND_RELEASE: take_focus(); return 1; } Fl_Boxtype b = box(); return Fl_Input_::handletext(event, x()+Fl::box_dx(b), y()+Fl::box_dy(b), w()-Fl::box_dw(b), h()-Fl::box_dh(b)); } Fl_Input::Fl_Input(int X, int Y, int W, int H, const char *l) : Fl_Input_(X, Y, W, H, l) { } // // End of "$Id: Fl_Input.cxx 6103 2008-04-21 20:42:51Z matt $". // _______________________________________________ fltk mailing list [email protected] http://lists.easysw.com/mailman/listinfo/fltk

