914e7f9b09
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9816 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
1326 lines
38 KiB
C++
1326 lines
38 KiB
C++
//
|
|
// "$Id$"
|
|
//
|
|
// Common input widget routines for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 1998-2011 by Bill Spitzak and others.
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// http://www.fltk.org/COPYING.php
|
|
//
|
|
// Please report all bugs and problems on the following page:
|
|
//
|
|
// http://www.fltk.org/str.php
|
|
//
|
|
|
|
#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 <FL/fl_utf8.h>
|
|
#include "flstring.h"
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#define MAXBUF 1024
|
|
#if defined(USE_X11) && !USE_XFT
|
|
const int secret_char = '*'; // asterisk to hide secret input
|
|
#else
|
|
const int secret_char = 0x2022; // bullet to hide secret input
|
|
#endif
|
|
static int l_secret;
|
|
|
|
extern void fl_draw(const char*, int, float, float);
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
/** \internal
|
|
Converts a given text segment into the text that will be rendered on screen.
|
|
|
|
This copies the text from \p p to \p buf, replacing characters with <tt>^X</tt>
|
|
and <tt>\\nnn</tt> as necessary.
|
|
|
|
The destination buffer is limited to \c MAXBUF (currently at 1024). All
|
|
following text is truncated.
|
|
|
|
\param [in] p pointer to source buffer
|
|
\param [in] buf pointer to destination buffer
|
|
\return pointer to the end of the destination buffer
|
|
*/
|
|
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;
|
|
// const char *pe = p + strlen(p);
|
|
|
|
if (input_type()==FL_SECRET_INPUT) {
|
|
while (o<e && p < value_+size_) {
|
|
if (fl_utf8len((char)p[0]) >= 1) {
|
|
l_secret = fl_utf8encode(secret_char, o);
|
|
o += l_secret;
|
|
}
|
|
p++;
|
|
}
|
|
|
|
} else while (o<e) {
|
|
if (wrap() && (p >= value_+size_ || isspace(*p & 255))) {
|
|
word_wrap = w() - Fl::box_dw(box()) - 2;
|
|
width_to_lastspace += (int)fl_width(lastspace_out, (int) (o-lastspace_out));
|
|
if (p > lastspace+1) {
|
|
if (word_count && width_to_lastspace > word_wrap) {
|
|
p = lastspace; o = lastspace_out; break;
|
|
}
|
|
word_count++;
|
|
}
|
|
lastspace = p;
|
|
lastspace_out = o;
|
|
}
|
|
|
|
if (p >= value_+size_) break;
|
|
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 = fl_utf_nb_char((uchar*)buf, (int) (o-buf))%8; c<8 && o<e; c++) {
|
|
*o++ = ' ';
|
|
}
|
|
} else {
|
|
*o++ = '^';
|
|
*o++ = c ^ 0x40;
|
|
}
|
|
} else {
|
|
*o++ = c;
|
|
}
|
|
}
|
|
*o = 0;
|
|
return p;
|
|
}
|
|
|
|
/** \internal
|
|
Calculates the width in pixels of part of a text buffer.
|
|
|
|
This call takes a string, usually created by expand, and calculates
|
|
the width of the string when rendered with the given font.
|
|
|
|
\param [in] p pointer to the start of the original string
|
|
\param [in] e pointer to the end of the original string
|
|
\param [in] buf pointer to the buffer as returned by expand()
|
|
\return width of string in pixels
|
|
*/
|
|
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;
|
|
int chr = 0;
|
|
int l;
|
|
if (input_type()==FL_SECRET_INPUT) {
|
|
while (p<e) {
|
|
l = fl_utf8len((char)p[0]);
|
|
if (l >= 1) n += l_secret;
|
|
p += l;
|
|
}
|
|
} else while (p<e) {
|
|
int c = *p & 255;
|
|
if (c < ' ' || c == 127) {
|
|
if (c == '\t' && input_type()==FL_MULTILINE_INPUT) {
|
|
n += 8-(chr%8);
|
|
chr += 7-(chr%8);
|
|
} else n += 2;
|
|
} else {
|
|
n++;
|
|
}
|
|
chr += fl_utf8len((char)p[0]) >= 1;
|
|
p++;
|
|
}
|
|
if (returnn) *returnn = n;
|
|
return fl_width(buf, n);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
/** \internal
|
|
Marks a range of characters for update.
|
|
|
|
This call marks all characters from \p to the end of the
|
|
text buffer for update. At least these characters
|
|
will be redrawn in the next update cycle.
|
|
|
|
Characters from \p mu_p to end of widget are redrawn.
|
|
If \p erase_cursor_only, small part at \p mu_p is redrawn.
|
|
Right now minimal update just keeps unchanged characters from
|
|
being erased, so they don't blink.
|
|
|
|
\param [in] p start of update range
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** \internal
|
|
Marks a range of characters for update.
|
|
|
|
This call marks a text range for update. At least all characters
|
|
from \p p to \p q will be redrawn in the next update cycle.
|
|
|
|
\param [in] p start of update range
|
|
\param [in] q end of update range
|
|
*/
|
|
void Fl_Input_::minimal_update(int p, int q) {
|
|
if (q < p) p = q;
|
|
minimal_update(p);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
/* Horizontal cursor position in pixels while moving up or down. */
|
|
double Fl_Input_::up_down_pos = 0;
|
|
|
|
/* Flag to remember last cursor move. */
|
|
int Fl_Input_::was_up_down = 0;
|
|
|
|
/**
|
|
Sets the current font and font size.
|
|
*/
|
|
void Fl_Input_::setfont() const {
|
|
fl_font(textfont(), textsize());
|
|
}
|
|
|
|
/**
|
|
Draws the text in the passed bounding box.
|
|
|
|
If <tt>damage() & FL_DAMAGE_ALL</tt> is true, this assumes the
|
|
area has already been erased to color(). Otherwise it does
|
|
minimal update and erases the area itself.
|
|
|
|
\param X, Y, W, H area that must be redrawn
|
|
*/
|
|
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 threshold = height/2;
|
|
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-threshold) {
|
|
// figure out scrolling so there is space after the cursor:
|
|
newscroll = curx+threshold-W;
|
|
// figure out the furthest left we ever want to scroll:
|
|
int ex = int(expandpos(p, e, buf, 0))+4-W;
|
|
// use minimum of both amounts:
|
|
if (ex < newscroll) newscroll = ex;
|
|
} else if (curx < newscroll+threshold) {
|
|
newscroll = curx-threshold;
|
|
}
|
|
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_push_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 = (int) strlen(buf);
|
|
#ifdef __APPLE__ // Mac OS: underline marked ( = selected + Fl::compose_state != 0) text
|
|
if (Fl::compose_state) {
|
|
fl_color(textcolor());
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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));
|
|
#ifdef __APPLE__ // Mac OS: underline marked ( = selected + Fl::compose_state != 0) text
|
|
if (Fl::compose_state) {
|
|
fl_color( fl_color_average(textcolor(), color(), 0.6) );
|
|
float width = fl_width(buf+offset1, offset2-offset1);
|
|
fl_line(x1, Y+ypos+height-1, x1+width, Y+ypos+height-1);
|
|
}
|
|
#endif
|
|
if (pp < e) {
|
|
fl_color(tc);
|
|
fl_draw(buf+offset2, (int) strlen(buf+offset2), x2, (float)(Y+ypos+desc));
|
|
}
|
|
} else {
|
|
// draw unselected text
|
|
fl_color(tc);
|
|
fl_draw(buf, (int) strlen(buf), xpos, (float)(Y+ypos+desc));
|
|
}
|
|
|
|
if (do_mu) fl_pop_clip();
|
|
|
|
CONTINUE2:
|
|
// draw the cursor:
|
|
if (Fl::focus() == this && (
|
|
#ifdef __APPLE__
|
|
Fl::compose_state ||
|
|
#endif
|
|
selstart == selend) &&
|
|
position() >= p-value() && position() <= e-value()) {
|
|
fl_color(cursor_color());
|
|
// cursor position may need to be recomputed (see STR #2486)
|
|
curx = int(expandpos(p, value()+position(), buf, 0)+.5);
|
|
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);
|
|
}
|
|
#ifdef __APPLE__
|
|
Fl::insertion_point_location(xpos+curx, Y+ypos+height, height);
|
|
#endif
|
|
}
|
|
|
|
CONTINUE:
|
|
ypos += height;
|
|
if (e >= value_+size_) break;
|
|
if (*e == '\n' || *e == ' ') e++;
|
|
p = e;
|
|
}
|
|
|
|
// 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();
|
|
if (Fl::focus() == this) {
|
|
fl_set_spot(textfont(), textsize(),
|
|
(int)xpos+curx, Y+ypos-fl_descent(), W, H, window());
|
|
}
|
|
}
|
|
|
|
/** \internal
|
|
Simple function that determines if a character could be part of a word.
|
|
\todo This function is not ucs4-aware.
|
|
*/
|
|
static int isword(char c) {
|
|
return (c&128 || isalnum(c) || strchr("#%&-/@\\_~", c));
|
|
}
|
|
|
|
/**
|
|
Finds the end of a word.
|
|
|
|
This call calculates the end of a word based on the given
|
|
index \p i. Calling this function repeatedly will move
|
|
forwards to the end of the text.
|
|
|
|
\param [in] i starting index for the search
|
|
\return end of the word
|
|
*/
|
|
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++;
|
|
while (i < size() && isword(index(i))) i++;
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
Finds the start of a word.
|
|
|
|
This call calculates the start of a word based on the given
|
|
index \p i. Calling this function repeatedly will move
|
|
backwards to the beginning of the text.
|
|
|
|
\param [in] i starting index for the search
|
|
\return start of the word
|
|
*/
|
|
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--;
|
|
while (i > 0 && isword(index(i-1))) i--;
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
Finds the end of a line.
|
|
|
|
This call calculates the end of a line based on the given
|
|
index \p i.
|
|
|
|
\param [in] i starting index for the search
|
|
\return end of the line
|
|
*/
|
|
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);
|
|
int k = (int) (p-value());
|
|
if (k >= i) return k;
|
|
p++;
|
|
}
|
|
} else {
|
|
while (i < size() && index(i) != '\n') i++;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Finds the start of a line.
|
|
|
|
This call calculates the start of a line based on the given
|
|
index \p i.
|
|
|
|
\param [in] i starting index for the search
|
|
\return start of the line
|
|
*/
|
|
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 ((int) (e-value()) >= i) return (int) (p-value());
|
|
p = e+1;
|
|
}
|
|
} else return j;
|
|
}
|
|
|
|
/**
|
|
Handles mouse clicks and mouse moves.
|
|
\todo Add comment and parameters
|
|
*/
|
|
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;
|
|
int cw = fl_utf8len((char)l[0]);
|
|
if (cw < 1) cw = 1;
|
|
t = l+cw;
|
|
f = X-xscroll_+expandpos(p, t, buf, 0);
|
|
if (f <= Fl::event_x()) {l = t; f0 = Fl::event_x()-f;}
|
|
else r = t-cw;
|
|
}
|
|
if (l < e) { // see if closer to character on right:
|
|
double f1;
|
|
int cw = fl_utf8len((char)l[0]);
|
|
if (cw > 0) {
|
|
f1 = X-xscroll_+expandpos(p, l + cw, buf, 0) - Fl::event_x();
|
|
if (f1 < f0) l = l+cw;
|
|
}
|
|
}
|
|
newpos = (int) (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 = (int) (l-value());
|
|
}
|
|
}
|
|
position(newpos, newmark);
|
|
}
|
|
|
|
/**
|
|
Sets the index for the cursor and mark.
|
|
|
|
The input widget maintains two pointers into the string. The
|
|
\e position (\c p) is where the cursor is. The
|
|
\e mark (\c m) is the other end of the selected text. If they
|
|
are equal then there is no selection. Changing this does not
|
|
affect the clipboard (use copy() to do that).
|
|
|
|
Changing these values causes a redraw(). The new
|
|
values are bounds checked.
|
|
|
|
\param p index for the cursor position
|
|
\param m index for the mark
|
|
\return 0 if no positions changed
|
|
\see position(int), position(), mark(int)
|
|
*/
|
|
int Fl_Input_::position(int p, int m) {
|
|
int is_same = 0;
|
|
was_up_down = 0;
|
|
if (p<0) p = 0;
|
|
if (p>size()) p = size();
|
|
if (m<0) m = 0;
|
|
if (m>size()) m = size();
|
|
if (p == m) is_same = 1;
|
|
|
|
while (p < position_ && p > 0 && (size() - p) > 0 &&
|
|
(fl_utf8len((char)(value() + p)[0]) < 1)) { p--; }
|
|
int ul = fl_utf8len((char)(value() + p)[0]);
|
|
while (p < size() && p > position_ && ul < 0) {
|
|
p++;
|
|
ul = fl_utf8len((char)(value() + p)[0]);
|
|
}
|
|
|
|
while (m < mark_ && m > 0 && (size() - m) > 0 &&
|
|
(fl_utf8len((char)(value() + m)[0]) < 1)) { m--; }
|
|
ul = fl_utf8len((char)(value() + m)[0]);
|
|
while (m < size() && m > mark_ && ul < 0) {
|
|
m++;
|
|
ul = fl_utf8len((char)(value() + m)[0]);
|
|
}
|
|
if (is_same) m = p;
|
|
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;
|
|
}
|
|
|
|
/**
|
|
Moves the cursor to the column given by \p up_down_pos.
|
|
|
|
This function is helpful when implementing up and down
|
|
cursor movement. It moves the cursor from the beginning
|
|
of a line to the column indicated by the global variable
|
|
\p up_down_pos in pixel units.
|
|
|
|
\param [in] i index into the beginning of a line of text
|
|
\param [in] keepmark if set, move only the cursor, but not the mark
|
|
\return index to new cursor position
|
|
*/
|
|
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 = (int) (l-value());
|
|
j = position(j, keepmark ? mark_ : j);
|
|
was_up_down = 1;
|
|
return j;
|
|
}
|
|
|
|
/**
|
|
Put the current selection into the clipboard.
|
|
|
|
This function copies the current selection between mark() and
|
|
position() into the specified \c clipboard. This does not
|
|
replace the old clipboard contents if position() and
|
|
mark() are equal. Clipboard 0 maps to the current text
|
|
selection and clipboard 1 maps to the cut/paste clipboard.
|
|
|
|
\param clipboard the clipboard destination 0 or 1
|
|
\return 0 if no text is selected, 1 if the selection was copied
|
|
\see Fl::copy(const char *, int, int)
|
|
*/
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Deletes text from \p b to \p e and inserts the new string \p text.
|
|
|
|
All changes to the text buffer go through this function.
|
|
It deletes the region between \p b and \p e (either one may be less or
|
|
equal to the other), and then inserts the string \p text
|
|
at that point and moves the mark() and
|
|
position() to the end of the insertion. Does the callback if
|
|
<tt>when() & FL_WHEN_CHANGED</tt> and there is a change.
|
|
|
|
Set \p b and \p e equal to not delete anything.
|
|
Set \p text to \c NULL to not insert anything.
|
|
|
|
\p ilen can be zero or <tt>strlen(text)</tt>, which
|
|
saves a tiny bit of time if you happen to already know the
|
|
length of the insertion, or can be used to insert a portion of a
|
|
string. If \p ilen is zero, <tt>strlen(text)</tt> is used instead.
|
|
|
|
\p b and \p e are clamped to the <tt>0..size()</tt> range, so it is
|
|
safe to pass any values. \p b, \p e, and \p ilen are used as numbers
|
|
of bytes (not characters), where \p b and \p e count from 0 to
|
|
size() (end of buffer).
|
|
|
|
If \p b and/or \p e don't point to a valid UTF-8 character boundary,
|
|
they are adjusted to the previous (\p b) or the next (\p e) valid
|
|
UTF-8 character boundary, resp..
|
|
|
|
If the current number of characters in the buffer minus deleted
|
|
characters plus inserted characters in \p text would overflow the
|
|
number of allowed characters (maximum_size()), then only the first
|
|
characters of the string are inserted, so that maximum_size()
|
|
is not exceeded.
|
|
|
|
cut() and insert() are just inline functions that call replace().
|
|
|
|
\param [in] b beginning index of text to be deleted
|
|
\param [in] e ending index of text to be deleted and insertion position
|
|
\param [in] text string that will be inserted
|
|
\param [in] ilen length of \p text or 0 for \c nul terminated strings
|
|
\return 0 if nothing changed
|
|
|
|
\note If \p text does not point to a valid UTF-8 character or includes
|
|
invalid UTF-8 sequences, the text is inserted nevertheless (counting
|
|
invalid UTF-8 bytes as one character each).
|
|
*/
|
|
int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
|
|
int ul, om, op;
|
|
was_up_down = 0;
|
|
|
|
if (b<0) b = 0;
|
|
if (e<0) e = 0;
|
|
if (b>size_) b = size_;
|
|
if (e>size_) e = size_;
|
|
if (e<b) {int t=b; b=e; e=t;}
|
|
while (b != e && b > 0 && (size_ - b) > 0 &&
|
|
(fl_utf8len((value_ + b)[0]) < 1)) { b--; }
|
|
ul = fl_utf8len((char)(value_ + e)[0]);
|
|
while (e < size_ && e > 0 && ul < 0) {
|
|
e++;
|
|
ul = fl_utf8len((char)(value_ + e)[0]);
|
|
}
|
|
if (text && !ilen) ilen = (int) strlen(text);
|
|
if (e<=b && !ilen) return 0; // don't clobber undo for a null operation
|
|
|
|
// we must count UTF-8 *characters* to determine whether we can insert
|
|
// the full text or only a part of it (and how much this would be)
|
|
|
|
int nchars = 0; // characters in value() - deleted + inserted
|
|
const char *p = value_;
|
|
while (p < (char *)(value_+size_)) {
|
|
if (p == (char *)(value_+b)) { // skip removed part
|
|
p = (char *)(value_+e);
|
|
if (p >= (char *)(value_+size_)) break;
|
|
}
|
|
int ulen = fl_utf8len(*p);
|
|
if (ulen < 1) ulen = 1; // invalid UTF-8 character: count as 1
|
|
nchars++;
|
|
p += ulen;
|
|
}
|
|
int nlen = 0; // length (in bytes) to be inserted
|
|
p = text;
|
|
while (p < (char *)(text+ilen) && nchars < maximum_size()) {
|
|
int ulen = fl_utf8len(*p);
|
|
if (ulen < 1) ulen = 1; // invalid UTF-8 character: count as 1
|
|
nchars++;
|
|
p += ulen;
|
|
nlen += ulen;
|
|
}
|
|
ilen = nlen;
|
|
|
|
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;
|
|
om = mark_;
|
|
op = position_;
|
|
mark_ = position_ = 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 (om < b) b = om;
|
|
if (op < b) b = op;
|
|
|
|
minimal_update(b);
|
|
|
|
mark_ = position_ = undoat;
|
|
|
|
set_changed();
|
|
if (when()&FL_WHEN_CHANGED) do_callback();
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
Undoes previous changes to the text buffer.
|
|
|
|
This call undoes a number of previous calls to replace().
|
|
|
|
\return non-zero if any change was made.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
Copies the \e yank buffer to the clipboard.
|
|
|
|
This method copies all the previous contiguous cuts from the undo
|
|
information to the clipboard. This function implements
|
|
the \c ^K shortcut key.
|
|
|
|
\return 0 if the operation did not change the clipboard
|
|
\see copy(int), cut()
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** \internal
|
|
Checks the when() field and does a callback if indicated.
|
|
*/
|
|
void Fl_Input_::maybe_do_callback() {
|
|
if (changed() || (when()&FL_WHEN_NOT_CHANGED)) {
|
|
do_callback();
|
|
}
|
|
}
|
|
|
|
/**
|
|
Handles all kinds of text field related events.
|
|
|
|
This is called by derived classes.
|
|
\todo Add comment and parameters
|
|
*/
|
|
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:
|
|
fl_set_spot(textfont(), textsize(), x(), y(), w(), h(), window());
|
|
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:
|
|
fl_reset_spot();
|
|
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, (int) (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, (int) (e-t));
|
|
}
|
|
return replace(position(), mark(), t, (int) (e-t));}
|
|
|
|
case FL_SHORTCUT:
|
|
if (!(shortcut() ? Fl::test_shortcut(shortcut()) : test_shortcut()))
|
|
return 0;
|
|
if (Fl::visible_focus() && handle(FL_FOCUS)) {
|
|
Fl::focus(this);
|
|
return 1;
|
|
} // else fall through
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*------------------------------*/
|
|
|
|
/**
|
|
Creates a new Fl_Input_ widget.
|
|
|
|
This function creates a new Fl_Input_ widget and adds it to the current
|
|
Fl_Group. The value() is set to \c NULL.
|
|
The default boxtype is \c FL_DOWN_BOX.
|
|
|
|
\param X, Y, W, H the dimensions of the new widget
|
|
\param l an optional label text
|
|
*/
|
|
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_ = 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;
|
|
shortcut_ = 0;
|
|
set_flag(SHORTCUT_LABEL);
|
|
set_flag(MAC_USE_ACCENTS_MENU);
|
|
tab_nav(1);
|
|
}
|
|
|
|
/**
|
|
Copies the value from a possibly static entry into the internal buffer.
|
|
|
|
\param [in] len size of the current text
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
Changes the widget text.
|
|
|
|
This function changes the text and sets the mark and the point to
|
|
the end of it. The string is \e not copied. If the user edits the
|
|
string it is copied to the internal buffer then. This can save a
|
|
great deal of time and memory if your program is rapidly
|
|
changing the values of text fields, but this will only work if
|
|
the passed string remains unchanged until either the
|
|
Fl_Input is destroyed or value() is called again.
|
|
|
|
You can use the \p len parameter to directly set the length
|
|
if you know it already or want to put \c nul characters in the text.
|
|
|
|
\param [in] str the new text
|
|
\param [in] len the length of the new text
|
|
\return non-zero if the new value is different than the current one
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
Changes the widget text.
|
|
|
|
This function changes the text and sets the mark and the point to
|
|
the end of it. The string is \e not copied. If the user edits the
|
|
string it is copied to the internal buffer then. This can save a
|
|
great deal of time and memory if your program is rapidly
|
|
changing the values of text fields, but this will only work if
|
|
the passed string remains unchanged until either the
|
|
Fl_Input is destroyed or value() is called again.
|
|
|
|
\param [in] str the new text
|
|
\return non-zero if the new value is different than the current one
|
|
*/
|
|
int Fl_Input_::static_value(const char* str) {
|
|
return static_value(str, str ? (int) strlen(str) : 0);
|
|
}
|
|
|
|
/**
|
|
Changes the widget text.
|
|
|
|
This function changes the text and sets the mark and the
|
|
point to the end of it. The string is copied to the internal
|
|
buffer. Passing \c NULL is the same as "".
|
|
|
|
You can use the \p length parameter to directly set the length
|
|
if you know it already or want to put \c nul characters in the text.
|
|
|
|
\param [in] str the new text
|
|
\param [in] len the length of the new text
|
|
\return non-zero if the new value is different than the current one
|
|
\see Fl_Input_::value(const char* str), Fl_Input_::value()
|
|
*/
|
|
int Fl_Input_::value(const char* str, int len) {
|
|
int r = static_value(str, len);
|
|
if (len) put_in_buffer(len);
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
Changes the widget text.
|
|
|
|
This function changes the text and sets the mark and the
|
|
point to the end of it. The string is copied to the internal
|
|
buffer. Passing \c NULL is the same as \c "".
|
|
|
|
\param [in] str the new text
|
|
\return non-zero if the new value is different than the current one
|
|
\see Fl_Input_::value(const char* str, int len), Fl_Input_::value()
|
|
*/
|
|
int Fl_Input_::value(const char* str) {
|
|
return value(str, str ? (int) strlen(str) : 0);
|
|
}
|
|
|
|
/**
|
|
Changes the size of the widget.
|
|
This call updates the text layout so that the cursor is visible.
|
|
\param [in] X, Y, W, H new size of the widget
|
|
\see Fl_Widget::resize(int, int, int, int)
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
Destroys the widget.
|
|
|
|
The destructor clears all allocated buffers and removes the widget
|
|
from the parent Fl_Group.
|
|
*/
|
|
Fl_Input_::~Fl_Input_() {
|
|
if (undowidget == this) undowidget = 0;
|
|
if (bufsize) free((void*)buffer);
|
|
}
|
|
|
|
/** \internal
|
|
Returns the number of lines displayed on a single page.
|
|
\return widget height divided by the font height
|
|
*/
|
|
int Fl_Input_::linesPerPage() {
|
|
int n = 1;
|
|
if (input_type() == FL_MULTILINE_INPUT) {
|
|
fl_font(textfont(),textsize()); //ensure current font is set to ours
|
|
n = h()/fl_height(); // number of lines to scroll
|
|
if (n<=0) n = 1;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
Returns the character at index \p i.
|
|
|
|
This function returns the UTF-8 character at \p i
|
|
as a ucs4 character code.
|
|
|
|
\param [in] i index into the value field
|
|
\return the character at index \p i
|
|
*/
|
|
unsigned int Fl_Input_::index(int i) const
|
|
{
|
|
int len = 0;
|
|
return fl_utf8decode(value_+i, value_+size_, &len);
|
|
}
|
|
|
|
//
|
|
// End of "$Id$".
|
|
//
|