📄 fl_browser_.cxx
字号:
//
// "$Id: Fl_Browser_.cxx,v 1.1.1.1 2003/06/03 22:25:41 agno Exp $"
//
// Base Browser widget class for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2003 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@fltk.org".
//
#define DISPLAY_SEARCH_BOTH_WAYS_AT_ONCE
#include <stdio.h>
#include <FL/Fl.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Browser_.H>
#include <FL/fl_draw.H>
// This is the base class for browsers. To be useful it must be
// subclassed and several virtual functions defined. The
// Forms-compatable browser and the file chooser's browser are
// subclassed off of this.
// Yes, I know this should be a template...
// This has been designed so that the subclass has complete control
// over the storage of the data, although because next() and prev()
// functions are used to index, it works best as a linked list or as a
// large block of characters in which the line breaks must be searched
// for.
// A great deal of work has been done so that the "height" of a data
// object does not need to be determined until it is drawn. This was
// done for the file chooser, because the height requires doing stat()
// to see if the file is a directory, which can be annoyingly slow
// over the network.
/* redraw bits:
1 = redraw children (the scrollbar)
2 = redraw one or two items
4 = redraw all items
*/
static void scrollbar_callback(Fl_Widget* s, void*) {
((Fl_Browser_*)(s->parent()))->position(int(((Fl_Scrollbar*)s)->value()));
}
static void hscrollbar_callback(Fl_Widget* s, void*) {
((Fl_Browser_*)(s->parent()))->hposition(int(((Fl_Scrollbar*)s)->value()));
}
int Fl_Browser_::scrollbar_width_ = 16;
// return where to draw the actual box:
void Fl_Browser_::bbox(int& X, int& Y, int& W, int& H) const {
Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
X = x()+Fl::box_dx(b);
Y = y()+Fl::box_dy(b);
W = w()-Fl::box_dw(b);
H = h()-Fl::box_dh(b);
if (scrollbar.visible()) {
W -= scrollbar_width_;
if (scrollbar.align() & FL_ALIGN_LEFT) X += scrollbar_width_;
}
if (W < 0) W = 0;
if (hscrollbar.visible()) {
H -= scrollbar_width_;
if (scrollbar.align() & FL_ALIGN_TOP) Y += scrollbar_width_;
}
if (H < 0) H = 0;
}
int Fl_Browser_::leftedge() const {
int X, Y, W, H; bbox(X, Y, W, H);
return X;
}
// The scrollbars may be moved again by draw(), since each one's size
// depends on whether the other is visible or not. This skips over
// Fl_Group::resize since it moves the scrollbars uselessly.
void Fl_Browser_::resize(int X, int Y, int W, int H) {
Fl_Widget::resize(X, Y, W, H);
// move the scrollbars so they can respond to events:
bbox(X,Y,W,H);
scrollbar.resize(
scrollbar.align()&FL_ALIGN_LEFT ? X-scrollbar_width_ : X+W,
Y, scrollbar_width_, H);
hscrollbar.resize(
X, scrollbar.align()&FL_ALIGN_TOP ? Y-scrollbar_width_ : Y+H,
W, scrollbar_width_);
}
// Cause minimal update to redraw the given item:
void Fl_Browser_::redraw_line(void* l) {
if (!redraw1 || redraw1 == l) {redraw1 = l; damage(FL_DAMAGE_EXPOSE);}
else if (!redraw2 || redraw2 == l) {redraw2 = l; damage(FL_DAMAGE_EXPOSE);}
else damage(FL_DAMAGE_SCROLL);
}
// Figure out top() based on position():
void Fl_Browser_::update_top() {
if (!top_) top_ = item_first();
if (position_ != real_position_) {
void* l;
int ly;
int yy = position_;
// start from either head or current position, whichever is closer:
if (!top_ || yy <= (real_position_/2)) {
l = item_first();
ly = 0;
} else {
l = top_;
ly = real_position_-offset_;
}
if (!l) {
top_ = 0;
offset_ = 0;
real_position_ = 0;
} else {
int hh = item_quick_height(l);
// step through list until we find line containing this point:
while (ly > yy) {
void* l1 = item_prev(l);
if (!l1) {ly = 0; break;} // hit the top
l = l1;
hh = item_quick_height(l);
ly -= hh;
}
while ((ly+hh) <= yy) {
void* l1 = item_next(l);
if (!l1) {yy = ly+hh-1; break;}
l = l1;
ly += hh;
hh = item_quick_height(l);
}
// top item must *really* be visible, use slow height:
for (;;) {
hh = item_height(l);
if ((ly+hh) > yy) break; // it is big enough to see
// go up to top of previous item:
void* l1 = item_prev(l);
if (!l1) {ly = yy = 0; break;} // hit the top
l = l1; yy = position_ = ly = ly-item_quick_height(l);
}
// use it:
top_ = l;
offset_ = yy-ly;
real_position_ = yy;
}
damage(FL_DAMAGE_SCROLL);
}
}
// Change position(), top() will update when update_top() is called
// (probably by draw() or handle()):
void Fl_Browser_::position(int yy) {
if (yy < 0) yy = 0;
if (yy == position_) return;
position_ = yy;
if (yy != real_position_) redraw_lines();
}
void Fl_Browser_::hposition(int xx) {
if (xx < 0) xx = 0;
if (xx == hposition_) return;
hposition_ = xx;
if (xx != real_hposition_) redraw_lines();
}
// Tell whether item is currently displayed:
int Fl_Browser_::displayed(void* p) const {
int X, Y, W, H; bbox(X, Y, W, H);
int yy = H+offset_;
for (void* l = top_; l && yy > 0; l = item_next(l)) {
if (l == p) return 1;
yy -= item_height(l);
}
return 0;
}
// Ensure this item is displayed:
// Messy because we have no idea if it is before top or after bottom:
void Fl_Browser_::display(void* p) {
// First special case - want to display first item in the list?
update_top();
if (p == item_first()) {position(0); return;}
int X, Y, W, H, Yp; bbox(X, Y, W, H);
void* l = top_;
Y = Yp = -offset_;
int h1;
// 2nd special case - want to display item already displayed at top of browser?
if (l == p) {position(real_position_+Y); return;} // scroll up a bit
// 3rd special case - want to display item just above top of browser?
void* lp = item_prev(l);
if (lp == p) {position(real_position_+Y-item_quick_height(lp)); return;}
#ifdef DISPLAY_SEARCH_BOTH_WAYS_AT_ONCE
// search for item. We search both up and down the list at the same time,
// this evens up the execution time for the two cases - the old way was
// much slower for going up than for going down.
while (l || lp) {
if (l) {
h1 = item_quick_height(l);
if (l == p) {
if (Y <= H) { // it is visible or right at bottom
Y = Y+h1-H; // find where bottom edge is
if (Y > 0) position(real_position_+Y); // scroll down a bit
} else {
position(real_position_+Y-(H-h1)/2); // center it
}
return;
}
Y += h1;
l = item_next(l);
}
if (lp) {
h1 = item_quick_height(lp);
Yp -= h1;
if (lp == p) {
if ((Yp + h1) >= 0) position(real_position_+Yp);
else position(real_position_+Yp-(H-h1)/2);
return;
}
lp = item_prev(lp);
}
}
#else
// Old version went forwards and then backwards:
// search forward for it:
l = top_;
for (; l; l = item_next(l)) {
h1 = item_quick_height(l);
if (l == p) {
if (Y <= H) { // it is visible or right at bottom
Y = Y+h1-H; // find where bottom edge is
if (Y > 0) position(real_position_+Y); // scroll down a bit
} else {
position(real_position_+Y-(H-h1)/2); // center it
}
return;
}
Y += h1;
}
// search backward for it, if found center it:
l = lp;
Y = -offset_;
for (; l; l = item_prev(l)) {
h1 = item_quick_height(l);
Y -= h1;
if (l == p) {
if ((Y + h1) >= 0) position(real_position_+Y);
else position(real_position_+Y-(H-h1)/2);
return;
}
}
#endif
}
// redraw, has side effect of updating top and setting scrollbar:
void Fl_Browser_::draw() {
int drawsquare = 0;
if (damage() & FL_DAMAGE_ALL) { // redraw the box if full redraw
Fl_Boxtype b = box() ? box() : FL_DOWN_BOX;
draw_box(b, x(), y(), w(), h(), color());
drawsquare = 1;
}
update_top();
int full_width_ = full_width();
int full_height_ = full_height();
int X, Y, W, H; bbox(X, Y, W, H);
int dont_repeat = 0;
J1:
// see if scrollbar needs to be switched on/off:
if ((has_scrollbar_ & VERTICAL) && (
(has_scrollbar_ & ALWAYS_ON) || position_ || full_height_ > H)) {
if (!scrollbar.visible()) {
scrollbar.set_visible();
drawsquare = 1;
bbox(X, Y, W, H);
}
} else {
top_ = item_first(); real_position_ = offset_ = 0;
if (scrollbar.visible()) {
scrollbar.clear_visible();
clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
}
}
if ((has_scrollbar_ & HORIZONTAL) && (
(has_scrollbar_ & ALWAYS_ON) || hposition_ || full_width_ > W)) {
if (!hscrollbar.visible()) {
hscrollbar.set_visible();
drawsquare = 1;
bbox(X, Y, W, H);
}
} else {
real_hposition_ = 0;
if (hscrollbar.visible()) {
hscrollbar.clear_visible();
clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
}
}
// Check the vertical scrollbar again, just in case it needs to be drawn
// because the horizontal one is drawn. There should be a cleaner way
// to do this besides copying the same code...
if ((has_scrollbar_ & VERTICAL) && (
(has_scrollbar_ & ALWAYS_ON) || position_ || full_height_ > H)) {
if (!scrollbar.visible()) {
scrollbar.set_visible();
drawsquare = 1;
bbox(X, Y, W, H);
}
} else {
top_ = item_first(); real_position_ = offset_ = 0;
if (scrollbar.visible()) {
scrollbar.clear_visible();
clear_damage((uchar)(damage()|FL_DAMAGE_SCROLL));
}
}
bbox(X, Y, W, H);
fl_clip(X, Y, W, H);
// for each line, draw it if full redraw or scrolled. Erase background
// if not a full redraw or if it is selected:
void* l = top();
int yy = -offset_;
for (; l && yy < H; l = item_next(l)) {
int hh = item_height(l);
if (hh <= 0) continue;
if ((damage()&(FL_DAMAGE_SCROLL|FL_DAMAGE_ALL)) || l == redraw1 || l == redraw2) {
if (item_selected(l)) {
fl_color(active_r() ? selection_color() : fl_inactive(selection_color()));
fl_rectf(X, yy+Y, W, hh);
} else if (!(damage()&FL_DAMAGE_ALL)) {
fl_push_clip(X, yy+Y, W, hh);
draw_box(box() ? box() : FL_DOWN_BOX, x(), y(), w(), h(), color());
fl_pop_clip();
}
item_draw(l, X-hposition_, yy+Y, W+hposition_, hh);
if (l == selection_ && Fl::focus() == this) {
draw_focus(FL_NO_BOX, X, yy+Y+1, W, hh);
}
int ww = item_width(l);
if (ww > max_width) {max_width = ww; max_width_item = l;}
}
yy += hh;
}
// erase the area below last line:
if (!(damage()&FL_DAMAGE_ALL) && yy < H) {
fl_push_clip(X, yy+Y, W, H-yy);
draw_box(box() ? box() : FL_DOWN_BOX, x(), y(), w(), h(), color());
fl_pop_clip();
}
fl_pop_clip();
redraw1 = redraw2 = 0;
if (!dont_repeat) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -