summaryrefslogtreecommitdiff
path: root/src/Fl_Input.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/Fl_Input.cxx')
-rw-r--r--src/Fl_Input.cxx299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/Fl_Input.cxx b/src/Fl_Input.cxx
new file mode 100644
index 000000000..34dfce1b9
--- /dev/null
+++ b/src/Fl_Input.cxx
@@ -0,0 +1,299 @@
+// Fl_Input.C
+
+// 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() ? box() : default_box();
+ if (damage() & 128) 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"
+// I tried to do compose characters "correctly" with much more user
+// feedback. They can see the character they will get, rather than
+// the "dead key" effect. Notice that I completely ignore that horrid
+// XIM extension!
+// Although the current scheme only works for Latin-NR1 character sets
+// the intention is to expand this to UTF-8 someday, to allow you to
+// compose all characters in all languages with no stupid "locale"
+// setting.
+// To use, you call "fl_compose()" for each keystroke. You pass it
+// the characters it displayed last time plus the new character. It
+// returns a new set of characters to replace the old one with. If
+// it returns zero length you should leave the old set unchanged and
+// treat the new key normally.
+// Pressing any function keys or moving the cursor should set the
+// compose state back to zero.
+
+// This string lists a pair for each possible foreign letter in Latin-NR1
+// 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'DDss"
+"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'ddy:";
+
+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 (!strchr("0123456789+-", key)) 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:
+ 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() ? box() : default_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) {}