#include <FL/Fl.H>
#include <FL/Fl_Browser.H>
#include <FL/fl_draw.H>
#include "flstring.h"
#include <stdlib.h>
#include <math.h>
#include <FL/Fl_Hold_Browser.H>
#include <FL/Fl_Multi_Browser.H>
#include <FL/Fl_Select_Browser.H>
#define SELECTED 1
#define NOTDISPLAYED 2
struct FL_BLINE { FL_BLINE* prev;
FL_BLINE* next;
void* data;
Fl_Image* icon;
short length; char flags; char txt[1]; };
void* Fl_Browser::item_first() const {return first;}
void* Fl_Browser::item_next(void* item) const {return ((FL_BLINE*)item)->next;}
void* Fl_Browser::item_prev(void* item) const {return ((FL_BLINE*)item)->prev;}
void* Fl_Browser::item_last() const {return last;}
int Fl_Browser::item_selected(void* item) const {
return ((FL_BLINE*)item)->flags&SELECTED;
}
void Fl_Browser::item_select(void *item, int val) {
if (val) ((FL_BLINE*)item)->flags |= SELECTED;
else ((FL_BLINE*)item)->flags &= ~SELECTED;
}
const char *Fl_Browser::item_text(void *item) const {
return ((FL_BLINE*)item)->txt;
}
FL_BLINE* Fl_Browser::find_line(int line) const {
int n; FL_BLINE* l;
if (line == cacheline) return cache;
if (cacheline && line > (cacheline/2) && line < ((cacheline+lines)/2)) {
n = cacheline; l = cache;
} else if (line <= (lines/2)) {
n = 1; l = first;
} else {
n = lines; l = last;
}
for (; n < line && l; n++) l = l->next;
for (; n > line && l; n--) l = l->prev;
((Fl_Browser*)this)->cacheline = line;
((Fl_Browser*)this)->cache = l;
return l;
}
int Fl_Browser::lineno(void *item) const {
FL_BLINE* l = (FL_BLINE*)item;
if (!l) return 0;
if (l == cache) return cacheline;
if (l == first) return 1;
if (l == last) return lines;
if (!cache) {
((Fl_Browser*)this)->cache = first;
((Fl_Browser*)this)->cacheline = 1;
}
FL_BLINE* b = cache->prev;
int bnum = cacheline-1;
FL_BLINE* f = cache->next;
int fnum = cacheline+1;
int n = 0;
for (;;) {
if (b == l) {n = bnum; break;}
if (f == l) {n = fnum; break;}
if (b) {b = b->prev; bnum--;}
if (f) {f = f->next; fnum++;}
}
((Fl_Browser*)this)->cache = l;
((Fl_Browser*)this)->cacheline = n;
return n;
}
FL_BLINE* Fl_Browser::_remove(int line) {
FL_BLINE* ttt = find_line(line);
deleting(ttt);
cacheline = line-1;
cache = ttt->prev;
lines--;
full_height_ -= item_height(ttt) + linespacing();
if (ttt->prev) ttt->prev->next = ttt->next;
else first = ttt->next;
if (ttt->next) ttt->next->prev = ttt->prev;
else last = ttt->prev;
return(ttt);
}
void Fl_Browser::remove(int line) {
if (line < 1 || line > lines) return;
free(_remove(line));
}
void Fl_Browser::insert(int line, FL_BLINE* item) {
if (!first) {
item->prev = item->next = 0;
first = last = item;
} else if (line <= 1) {
inserting(first, item);
item->prev = 0;
item->next = first;
item->next->prev = item;
first = item;
} else if (line > lines) {
item->prev = last;
item->prev->next = item;
item->next = 0;
last = item;
} else {
FL_BLINE* n = find_line(line);
inserting(n, item);
item->next = n;
item->prev = n->prev;
item->prev->next = item;
n->prev = item;
}
cacheline = line;
cache = item;
lines++;
full_height_ += item_height(item) + linespacing();
redraw_line(item);
}
void Fl_Browser::insert(int line, const char* newtext, void* d) {
if (!newtext) newtext = ""; int l = (int) strlen(newtext);
FL_BLINE* t = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l);
t->length = (short)l;
t->flags = 0;
strcpy(t->txt, newtext);
t->data = d;
t->icon = 0;
insert(line, t);
}
void Fl_Browser::move(int to, int from) {
if (from < 1 || from > lines) return;
insert(to, _remove(from));
}
void Fl_Browser::text(int line, const char* newtext) {
if (line < 1 || line > lines) return;
FL_BLINE* t = find_line(line);
if (!newtext) newtext = ""; int l = (int) strlen(newtext);
if (l > t->length) {
FL_BLINE* n = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l);
replacing(t, n);
cache = n;
n->data = t->data;
n->icon = t->icon;
n->length = (short)l;
n->flags = t->flags;
n->prev = t->prev;
if (n->prev) n->prev->next = n; else first = n;
n->next = t->next;
if (n->next) n->next->prev = n; else last = n;
free(t);
t = n;
}
strcpy(t->txt, newtext);
redraw_line(t);
}
void Fl_Browser::data(int line, void* d) {
if (line < 1 || line > lines) return;
find_line(line)->data = d;
}
int Fl_Browser::item_height(void *item) const {
FL_BLINE* l = (FL_BLINE*)item;
if (l->flags & NOTDISPLAYED) return 0;
int hmax = 2;
if (!l->txt[0]) {
fl_font(textfont(), textsize());
int hh = fl_height();
if (hh > hmax) hmax = hh;
} else {
const int* i = column_widths();
for (char* str = l->txt; str && *str; str++) {
Fl_Font font = textfont(); int tsize = textsize(); if ( format_char() ) { while (*str==format_char() && *str++ && *str!=format_char()) {
switch (*str++) {
case 'l': case 'L': tsize = 24; break;
case 'm': case 'M': tsize = 18; break;
case 's': tsize = 11; break;
case 'b': font = (Fl_Font)(font|FL_BOLD); break;
case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
case 'f': case 't': font = FL_COURIER; break;
case 'B':
case 'C': while (isdigit(*str & 255)) str++; break; case 'F': font = (Fl_Font)strtol(str,&str,10); break;
case 'S': tsize = (int)strtol(str,&str,10); break;
case '.': goto END_FORMAT;
}
}
}
END_FORMAT:
char* ptr = str;
if (ptr && *i++) str = strchr(str, column_char());
else str = NULL;
if((!str && *ptr) || (str && ptr < str) || hmax == 2) {
fl_font(font, tsize); int hh = fl_height();
if (hh > hmax) hmax = hh;
}
if (!str || !*str) break;
}
}
if (l->icon && (l->icon->h()+2)>hmax) {
hmax = l->icon->h() + 2; }
return hmax; }
int Fl_Browser::item_width(void *item) const {
FL_BLINE* l=(FL_BLINE*)item;
char* str = l->txt;
const int* i = column_widths();
int ww = 0;
while (*i) { char* e;
e = strchr(str, column_char());
if (!e) break; str = e+1;
ww += *i++;
}
int tsize = textsize();
Fl_Font font = textfont();
int done = 0;
if ( format_char() ) { while (*str == format_char_ && str[1] && str[1] != format_char_) {
str ++;
switch (*str++) {
case 'l': case 'L': tsize = 24; break;
case 'm': case 'M': tsize = 18; break;
case 's': tsize = 11; break;
case 'b': font = (Fl_Font)(font|FL_BOLD); break;
case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
case 'f': case 't': font = FL_COURIER; break;
case 'B':
case 'C': while (isdigit(*str & 255)) str++; break; case 'F': font = (Fl_Font)strtol(str, &str, 10); break;
case 'S': tsize = (int)strtol(str, &str, 10); break;
case '.':
done = 1;
break;
}
if (done)
break;
}
if (*str == format_char_ && str[1])
str ++;
}
if (ww==0 && l->icon) ww = l->icon->w();
fl_font(font, tsize);
return ww + int(fl_width(str)) + 6;
}
int Fl_Browser::full_height() const {
return full_height_;
}
int Fl_Browser::incr_height() const {
return textsize() + 2 + linespacing();
}
void Fl_Browser::item_draw(void* item, int X, int Y, int W, int H) const {
FL_BLINE* l = (FL_BLINE*)item;
char* str = l->txt;
const int* i = column_widths();
bool firstLoop = true; while (W > 6) { int w1 = W; char* e = 0; if (*i) { e = strchr(str, column_char());
if (e) {*e = 0; w1 = *i++;}
}
if (firstLoop) {
firstLoop = false;
if (l->icon) {
l->icon->draw(X+2,Y+1); int iconw = l->icon->w()+2;
X += iconw; W -= iconw; w1 -= iconw;
}
}
int tsize = textsize();
Fl_Font font = textfont();
Fl_Color lcol = textcolor();
Fl_Align talign = FL_ALIGN_LEFT;
if ( format_char() ) { while (*str == format_char() && *++str && *str != format_char()) {
switch (*str++) {
case 'l': case 'L': tsize = 24; break;
case 'm': case 'M': tsize = 18; break;
case 's': tsize = 11; break;
case 'b': font = (Fl_Font)(font|FL_BOLD); break;
case 'i': font = (Fl_Font)(font|FL_ITALIC); break;
case 'f': case 't': font = FL_COURIER; break;
case 'c': talign = FL_ALIGN_CENTER; break;
case 'r': talign = FL_ALIGN_RIGHT; break;
case 'B':
if (!(l->flags & SELECTED)) {
fl_color((Fl_Color)strtoul(str, &str, 10));
fl_rectf(X, Y, w1, H);
} else while (isdigit(*str & 255)) str++; break;
case 'C':
lcol = (Fl_Color)strtoul(str, &str, 10);
break;
case 'F':
font = (Fl_Font)strtol(str, &str, 10);
break;
case 'N':
lcol = FL_INACTIVE_COLOR;
break;
case 'S':
tsize = (int)strtol(str, &str, 10);
break;
case '-':
fl_color(FL_DARK3);
fl_line(X+3, Y+H/2, X+w1-3, Y+H/2);
fl_color(FL_LIGHT3);
fl_line(X+3, Y+H/2+1, X+w1-3, Y+H/2+1);
break;
case 'u':
case '_':
fl_color(lcol);
fl_line(X+3, Y+H-1, X+w1-3, Y+H-1);
break;
case '.':
goto BREAK;
}
}
}
BREAK:
fl_font(font, tsize);
if (l->flags & SELECTED)
lcol = fl_contrast(lcol, selection_color());
if (!active_r()) lcol = fl_inactive(lcol);
fl_color(lcol);
fl_draw(str, X+3, Y, w1-6, H, e ? Fl_Align(talign|FL_ALIGN_CLIP) : talign, 0, 0);
if (!e) break; *e = column_char(); X += w1;
W -= w1;
str = e+1;
}
}
static const int no_columns[1] = {0};
Fl_Browser::Fl_Browser(int X, int Y, int W, int H, const char *L)
: Fl_Browser_(X, Y, W, H, L) {
column_widths_ = no_columns;
lines = 0;
full_height_ = 0;
cacheline = 0;
format_char_ = '@';
column_char_ = '\t';
first = last = cache = 0;
}
void Fl_Browser::lineposition(int line, Fl_Line_Position pos) {
if (line<1) line = 1;
if (line>lines) line = lines;
int p = 0;
FL_BLINE* l;
for (l=first; l && line>1; l = l->next) {
line--; p += item_height(l) + linespacing();
}
if (l && (pos == BOTTOM)) p += item_height(l) + linespacing();
int final = p, X, Y, W, H;
bbox(X, Y, W, H);
switch(pos) {
case TOP: break;
case BOTTOM: final -= H; break;
case MIDDLE: final -= H/2; break;
}
if (final > (full_height() - H)) final = full_height() -H;
vposition(final);
}
int Fl_Browser::topline() const {
return lineno(top());
}
void Fl_Browser::textsize(Fl_Fontsize newSize) {
if (newSize == textsize())
return; Fl_Browser_::textsize(newSize);
new_list();
full_height_ = 0;
if (lines == 0) return;
for (FL_BLINE* itm=(FL_BLINE *)item_first(); itm; itm=(FL_BLINE *)item_next(itm)) {
full_height_ += item_height(itm) + linespacing();
}
}
void Fl_Browser::clear() {
for (FL_BLINE* l = first; l;) {
FL_BLINE* n = l->next;
free(l);
l = n;
}
full_height_ = 0;
first = 0;
last = 0;
lines = 0;
new_list();
}
void Fl_Browser::add(const char* newtext, void* d) {
insert(lines+1, newtext, d);
}
const char* Fl_Browser::text(int line) const {
if (line < 1 || line > lines) return 0;
return find_line(line)->txt;
}
void* Fl_Browser::data(int line) const {
if (line < 1 || line > lines) return 0;
return find_line(line)->data;
}
int Fl_Browser::select(int line, int val) {
if (line < 1 || line > lines) return 0;
return Fl_Browser_::select(find_line(line), val);
}
int Fl_Browser::selected(int line) const {
if (line < 1 || line > lines) return 0;
return find_line(line)->flags & SELECTED;
}
void Fl_Browser::show(int line) {
FL_BLINE* t = find_line(line);
if (t->flags & NOTDISPLAYED) {
t->flags &= ~NOTDISPLAYED;
full_height_ += item_height(t) + linespacing();
if (Fl_Browser_::displayed(t)) redraw();
}
}
void Fl_Browser::hide(int line) {
FL_BLINE* t = find_line(line);
if (!(t->flags & NOTDISPLAYED)) {
full_height_ -= item_height(t) + linespacing();
t->flags |= NOTDISPLAYED;
if (Fl_Browser_::displayed(t)) redraw();
}
}
void Fl_Browser::display(int line, int val) {
if (line < 1 || line > lines) return;
if (val) show(line); else hide(line);
}
int Fl_Browser::visible(int line) const {
if (line < 1 || line > lines) return 0;
return !(find_line(line)->flags&NOTDISPLAYED);
}
int Fl_Browser::value() const {
return lineno(selection());
}
void Fl_Browser::swap(FL_BLINE *a, FL_BLINE *b) {
if ( a == b || !a || !b) return; swapping(a, b);
FL_BLINE *aprev = a->prev;
FL_BLINE *anext = a->next;
FL_BLINE *bprev = b->prev;
FL_BLINE *bnext = b->next;
if ( b->prev == a ) { if ( aprev ) aprev->next = b; else first = b;
b->next = a;
a->next = bnext;
b->prev = aprev;
a->prev = b;
if ( bnext ) bnext->prev = a; else last = a;
} else if ( a->prev == b ) { if ( bprev ) bprev->next = a; else first = a;
a->next = b;
b->next = anext;
a->prev = bprev;
b->prev = a;
if ( anext ) anext->prev = b; else last = b;
} else { b->prev = aprev;
if ( anext ) anext->prev = b; else last = b;
a->prev = bprev;
if ( bnext ) bnext->prev = a; else last = a;
if ( aprev ) aprev->next = b; else first = b;
b->next = anext;
if ( bprev ) bprev->next = a; else first = a;
a->next = bnext;
}
cacheline = 0;
cache = 0;
}
void Fl_Browser::swap(int a, int b) {
if (a < 1 || a > lines || b < 1 || b > lines) return;
FL_BLINE* ai = find_line(a);
FL_BLINE* bi = find_line(b);
swap(ai,bi);
}
void Fl_Browser::icon(int line, Fl_Image* icon) {
if (line<1 || line > lines) return;
FL_BLINE* bl = find_line(line);
int old_h = bl->icon ? bl->icon->h()+2 : 0; bl->icon = 0; int th = item_height(bl); int new_h = icon ? icon->h()+2 : 0; if (th > old_h) old_h = th;
if (th > new_h) new_h = th;
int dh = new_h - old_h;
full_height_ += dh;
bl->icon = icon; if (dh>0) {
redraw(); } else {
redraw_line(bl); }
replacing(bl,bl); }
Fl_Image* Fl_Browser::icon(int line) const {
FL_BLINE* l = find_line(line);
return(l ? l->icon : NULL);
}
void Fl_Browser::remove_icon(int line) {
icon(line,0);
}
Fl_Hold_Browser::Fl_Hold_Browser(int X,int Y,int W,int H,const char *L)
: Fl_Browser(X,Y,W,H,L)
{
type(FL_HOLD_BROWSER);
}
Fl_Multi_Browser::Fl_Multi_Browser(int X,int Y,int W,int H,const char *L)
: Fl_Browser(X,Y,W,H,L)
{
type(FL_MULTI_BROWSER);
}
Fl_Select_Browser::Fl_Select_Browser(int X,int Y,int W,int H,const char *L)
: Fl_Browser(X,Y,W,H,L)
{
type(FL_SELECT_BROWSER);
}