comments in Fl_Input.cxx and fixed the documentation. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.0@552 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
342 lines
10 KiB
C++
342 lines
10 KiB
C++
//
|
|
// "$Id: Fl_Input.cxx,v 1.10.2.1 1999/04/20 04:43:24 bill Exp $"
|
|
//
|
|
// Input widget for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 1998-1999 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 to "fltk-bugs@easysw.com".
|
|
//
|
|
|
|
// This is the "user interface", it decodes user actions into what to
|
|
// do to the text. See also Fl_Input_.C, 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 <FL/Fl.H>
|
|
#include <FL/Fl_Input.H>
|
|
#include <FL/fl_draw.H>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
void Fl_Input::draw() {
|
|
if (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)+3, y()+Fl::box_dy(b),
|
|
w()-Fl::box_dw(b)-6, 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));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Fltk "compose"
|
|
//
|
|
// This is a demonstration of a IMHO "correct" interface to compose
|
|
// character sequences. It does not have a "dead key" effect: the
|
|
// user has feedback at all times, and sees exactly the symbol they
|
|
// will get if they stop typing at that point. Notice that I totally
|
|
// ignore the horrid XIM extension!
|
|
//
|
|
// You only need to keep track of your normal text buffer and a
|
|
// single integer "state". Call fl_compose() for each character
|
|
// keystroke. The return value is the new "state" that must be passed
|
|
// the next time you call fl_compose(). It also returns the number of
|
|
// characters to delete to the left, a buffer of new characters, and
|
|
// the number of characters in that buffer. Obey these editing
|
|
// instructions. Reset the state to zero if the user types any
|
|
// function keys or clicks the mouse.
|
|
//
|
|
// Fl_Input does not call fl_compose unless you hit the "compose" key
|
|
// first. It may be interesting and useful to always call it, though...
|
|
|
|
// Although this simple code is only for ISO-8859-1 character
|
|
// encodings, I think the interface can be expanded to UTF-8 (encoded
|
|
// Unicode) someday.
|
|
|
|
// This string lists a pair for each possible foreign letter in ISO-8859-1
|
|
// starting at code 0xa0 (nbsp). If the second character is a space then
|
|
// only the first character needs to by typed:
|
|
static const char* const compose_pairs =
|
|
" ! % # $ y=| & : c a <<~ - r _ * +-2 3 ' u p . , 1 o >>141234? "
|
|
"A`A'A^A~A:A*AEC,E`E'E^E:I`I'I^I:D-N~O`O'O^O~O:x O/U`U'U^U:Y'THss"
|
|
"a`a'a^a~a:a*aec,e`e'e^e:i`i'i^i:d-n~o`o'o^o~o:-:o/u`u'u^u:y'thy:";
|
|
|
|
int fl_compose(int state, char c, int& del, char* buffer, int& ins) {
|
|
del = 0; ins = 1; buffer[0] = c;
|
|
|
|
if (c == '"') c = ':';
|
|
|
|
if (!state) { // first character
|
|
if (c == ' ') {buffer[0]=char(0xA0);return 0x100;} // space turns into nbsp
|
|
// see if it is either character of any pair:
|
|
state = 0;
|
|
for (const char *p = compose_pairs; *p; p += 2)
|
|
if (p[0] == c || p[1] == c) {
|
|
if (p[1] == ' ') buffer[0] = (p-compose_pairs)/2+0xA0;
|
|
state = c;
|
|
}
|
|
return state;
|
|
|
|
} else if (state == 0x100) { // third character
|
|
return 0;
|
|
|
|
} else { // second character
|
|
char c1 = char(state); // first character
|
|
// now search for the pair in either order:
|
|
for (const char *p = compose_pairs; *p; p += 2) {
|
|
if (p[0] == c && p[1] == c1 || p[1] == c && p[0] == c1) {
|
|
buffer[0] = (p-compose_pairs)/2+0xA0;
|
|
ins = del = 1;
|
|
return 0x100;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
static int compose; // compose state (# of characters so far + 1)
|
|
|
|
// 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)
|
|
|
|
int Fl_Input::handle_key() {
|
|
int i;
|
|
|
|
int pcompose = compose; compose = 0;
|
|
char key = Fl::event_text()[0];
|
|
|
|
if (pcompose && Fl::event_length()) {
|
|
char buf[20]; int ins; int del;
|
|
compose = fl_compose(pcompose-1, key, del, buf, ins);
|
|
if (compose) {
|
|
replace(position(), del ? position()-del : mark(), buf, ins);
|
|
compose++; // store value+1 so 1 can initialize compose state
|
|
return 1;
|
|
} else {
|
|
if (pcompose==1) // compose also acts as quote-next:
|
|
return replace(position(),mark(),Fl::event_text(),Fl::event_length());
|
|
}
|
|
}
|
|
|
|
if (Fl::event_state(FL_ALT|FL_META)) { // reserved for shortcuts
|
|
compose = pcompose;
|
|
return 0;
|
|
}
|
|
|
|
switch (Fl::event_key()) {
|
|
case FL_Left:
|
|
key = ctrl('B'); break;
|
|
case FL_Right:
|
|
key = ctrl('F'); break;
|
|
case FL_Up:
|
|
key = ctrl('P'); break;
|
|
case FL_Down:
|
|
key = ctrl('N'); break;
|
|
case FL_Delete:
|
|
key = ctrl('D'); break;
|
|
case FL_Home:
|
|
key = ctrl('A'); break;
|
|
case FL_End:
|
|
key = ctrl('E'); break;
|
|
case FL_BackSpace:
|
|
if (mark() != position()) cut();
|
|
else cut(-1);
|
|
return 1;
|
|
case FL_Enter:
|
|
case FL_KP_Enter:
|
|
if (when() & FL_WHEN_ENTER_KEY) {
|
|
position(size(), 0);
|
|
maybe_do_callback();
|
|
return 1;
|
|
} else if (type() == FL_MULTILINE_INPUT)
|
|
return replace(position(), mark(), "\n", 1);
|
|
else
|
|
return 0; // reserved for shortcuts
|
|
case FL_Tab:
|
|
if (Fl::event_state(FL_CTRL) || type()!=FL_MULTILINE_INPUT) return 0;
|
|
break;
|
|
case FL_Escape:
|
|
return 0; // reserved for shortcuts (Forms cleared field)
|
|
case FL_Control_R:
|
|
case 0xff20: // Multi-Key
|
|
compose = 1;
|
|
return 1;
|
|
}
|
|
|
|
switch(key) {
|
|
case 0: // key did not translate to any text
|
|
compose = pcompose; // allow user to hit shift keys after ^Q
|
|
return 0;
|
|
case ctrl('A'):
|
|
if (type() == FL_MULTILINE_INPUT)
|
|
for (i=position(); i && index(i-1)!='\n'; i--) ;
|
|
else
|
|
i = 0;
|
|
return shift_position(i) + NORMAL_INPUT_MOVE;
|
|
case ctrl('B'):
|
|
return shift_position(position()-1) + NORMAL_INPUT_MOVE;
|
|
case ctrl('C'): // copy
|
|
return copy();
|
|
case ctrl('D'):
|
|
if (mark() != position()) return cut();
|
|
else return cut(1);
|
|
case ctrl('E'):
|
|
if (type() == FL_MULTILINE_INPUT)
|
|
for (i=position(); index(i) && index(i)!='\n'; i++) ;
|
|
else
|
|
i = size();
|
|
return shift_position(i) + NORMAL_INPUT_MOVE;
|
|
case ctrl('F'):
|
|
return shift_position(position()+1) + NORMAL_INPUT_MOVE;
|
|
case ctrl('K'):
|
|
if (position()>=size()) return 0;
|
|
if (type() == FL_MULTILINE_INPUT) {
|
|
if (index(position()) == '\n')
|
|
i = position() + 1;
|
|
else
|
|
for (i=position()+1; index(i) && index(i) != '\n'; i++);
|
|
} else
|
|
i = size();
|
|
cut(position(), i);
|
|
return copy_cuts();
|
|
case ctrl('N'):
|
|
if (type()!=FL_MULTILINE_INPUT) return 0;
|
|
for (i=position(); index(i)!='\n'; i++)
|
|
if (!index(i)) return NORMAL_INPUT_MOVE;
|
|
shift_up_down_position(i+1);
|
|
return 1;
|
|
case ctrl('P'):
|
|
if (type()!=FL_MULTILINE_INPUT) return 0;
|
|
for (i = position(); i > 0 && index(i-1) != '\n'; i--) ;
|
|
if (!i) return NORMAL_INPUT_MOVE;
|
|
shift_up_down_position(i-1);
|
|
return 1;
|
|
case ctrl('Q'):
|
|
compose = 1;
|
|
return 1;
|
|
case ctrl('U'):
|
|
return cut(0, size());
|
|
case ctrl('V'):
|
|
case ctrl('Y'):
|
|
Fl::paste(*this);
|
|
return 1;
|
|
case ctrl('X'):
|
|
case ctrl('W'):
|
|
copy();
|
|
return cut();
|
|
case ctrl('Z'):
|
|
case ctrl('_'):
|
|
return undo();
|
|
}
|
|
|
|
// skip all illegal characters
|
|
// this could be improved to make sure characters are inserted at
|
|
// legal positions...
|
|
if (type() == FL_FLOAT_INPUT) {
|
|
if (!strchr("0123456789.eE+-", key)) return 0;
|
|
} else if (type() == FL_INT_INPUT) {
|
|
if (!position() && (key == '+' || key == '-'));
|
|
else if (key >= '0' && key <= '9');
|
|
// we allow 0xabc style hex numbers to be typed:
|
|
else if (position()==1 && index(0)=='0' && (key == 'x' || key == 'X'));
|
|
else if (position()>1 && index(0)=='0' && (index(1)=='x'||index(1)=='X')
|
|
&& (key>='A'&& key<='F' || key>='a'&& key<='f'));
|
|
else return 0;
|
|
}
|
|
|
|
return replace(position(), mark(), Fl::event_text(), Fl::event_length());
|
|
}
|
|
|
|
int Fl_Input::handle(int event) {
|
|
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(size());
|
|
break;
|
|
case FL_Tab:
|
|
case 0xfe20: // XK_ISO_Left_Tab
|
|
position(size(),0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case FL_UNFOCUS:
|
|
compose = 0;
|
|
break;
|
|
|
|
case FL_KEYBOARD:
|
|
return handle_key();
|
|
|
|
case FL_PUSH:
|
|
compose = 0;
|
|
if (Fl::event_button() == 2) {
|
|
Fl::paste(*this);
|
|
if (Fl::focus()==this) return 1; // remove line for Motif behavior
|
|
}
|
|
if (Fl::focus() != this) {
|
|
Fl::focus(this);
|
|
handle(FL_FOCUS); // cause minimal update
|
|
}
|
|
break;
|
|
|
|
case FL_DRAG:
|
|
case FL_RELEASE:
|
|
if (Fl::event_button() == 2) return 0;
|
|
break;
|
|
}
|
|
Fl_Boxtype b = box();
|
|
return Fl_Input_::handletext(event,
|
|
x()+Fl::box_dx(b)+3, y()+Fl::box_dy(b),
|
|
w()-Fl::box_dw(b)-6, 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,v 1.10.2.1 1999/04/20 04:43:24 bill Exp $".
|
|
//
|