summaryrefslogtreecommitdiff
path: root/src/Fl_Browser.cxx
diff options
context:
space:
mode:
authorMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
committerMichael R Sweet <michael.r.sweet@gmail.com>1998-10-06 18:21:25 +0000
commitf9039b2ae21988783feae9b362818e7923e82d14 (patch)
tree6d6fe3679d73448758f9794e7d4d4f6b22a4adad /src/Fl_Browser.cxx
parent67e89232f9ba067825a158734a09e0fa21aacbe3 (diff)
Initial revision
git-svn-id: file:///fltk/svn/fltk/trunk@2 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/Fl_Browser.cxx')
-rw-r--r--src/Fl_Browser.cxx421
1 files changed, 421 insertions, 0 deletions
diff --git a/src/Fl_Browser.cxx b/src/Fl_Browser.cxx
new file mode 100644
index 000000000..f58447386
--- /dev/null
+++ b/src/Fl_Browser.cxx
@@ -0,0 +1,421 @@
+// Fl_Browser.C
+
+// Forms-compatable browser. Probably useful for other lists of
+// textual data.
+
+// I modified this from the original Forms data to use a linked list
+// so that the number of items in the browser and size of those items
+// is unlimited. The only problem is that the old browser used an
+// index number to identify a line, and it is slow to convert from/to
+// a pointer. I use a cache of the last match to try to speed this
+// up.
+
+// Also added the ability to "hide" a line. This set's it's height to
+// zero, so the Fl_Browser_ cannot pick it.
+
+#include <FL/Fl.H>
+#include <FL/Fl_Browser.H>
+#include <FL/fl_draw.H>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <FL/Fl_Input_.H> // for default_font
+
+#define SELECTED 1
+#define NOTDISPLAYED 2
+
+struct FL_BLINE { // data is in a linked list of these
+ FL_BLINE* prev;
+ FL_BLINE* next;
+ void* data;
+ short length; // sizeof(txt)-1, may be longer than string
+ char flags; // selected, displayed
+ char txt[1]; // start of allocated array
+};
+
+void* Fl_Browser::item_first() const {return first;}
+
+void* Fl_Browser::item_next(void* l) const {return ((FL_BLINE*)l)->next;}
+
+void* Fl_Browser::item_prev(void* l) const {return ((FL_BLINE*)l)->prev;}
+
+int Fl_Browser::item_selected(void* l) const {
+ return ((FL_BLINE*)l)->flags&SELECTED;}
+
+void Fl_Browser::item_select(void* l, int v) {
+ if (v) ((FL_BLINE*)l)->flags |= SELECTED;
+ else ((FL_BLINE*)l)->flags &= ~SELECTED;
+}
+
+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* v) const {
+ FL_BLINE* l = (FL_BLINE*)v;
+ 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;
+ }
+ // assumme it is near cache, search both directions:
+ 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;
+ if (ttt->prev) ttt->prev->next = ttt->next;
+ else first = ttt->next;
+ if (ttt->next) ttt->next->prev = ttt->prev;
+ else last = ttt->prev;
+
+ lines--;
+ full_height_ -= item_height(ttt);
+ 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* t) {
+ if (!first) {
+ t->prev = t->next = 0;
+ first = last = t;
+ } else if (line <= 1) {
+ inserting(first, t);
+ t->prev = 0;
+ t->next = first;
+ t->next->prev = t;
+ first = t;
+ } else if (line > lines) {
+ t->prev = last;
+ t->prev->next = t;
+ t->next = 0;
+ last = t;
+ } else {
+ FL_BLINE* n = find_line(line);
+ inserting(n, t);
+ t->next = n;
+ t->prev = n->prev;
+ t->prev->next = t;
+ n->prev = t;
+ }
+ cacheline = line;
+ cache = t;
+ lines++;
+ full_height_ += item_height(t);
+ redraw_line(t);
+}
+
+void Fl_Browser::insert(int line, const char* newtext, void* data) {
+ int l = strlen(newtext);
+ FL_BLINE* t = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l);
+ t->length = l;
+ t->flags = 0;
+ strcpy(t->txt, newtext);
+ t->data = data;
+ 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);
+ int l = strlen(newtext);
+ if (l > t->length) {
+ FL_BLINE* n = (FL_BLINE*)malloc(sizeof(FL_BLINE)+l);
+ replacing(t, n);
+ cache = n;
+ n->length = 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* data) {
+ if (line < 1 || line > lines) return;
+ find_line(line)->data = data;
+}
+
+int Fl_Browser::item_height(void* lv) const {
+ FL_BLINE* l = (FL_BLINE*)lv;
+ if (l->flags & NOTDISPLAYED) return 0;
+ char* str = l->txt;
+ int t = textsize()+2;
+ if (*str == format_char()) switch (*(str+1)) {
+ case 'l': case 'L': t = 26; break;
+ case 'm': case 'M': t = 20; break;
+ case 's': case 'S': t = 13; break;
+ }
+ return t + Fl_Input_::default_size();
+}
+
+int Fl_Browser::item_width(void* v) const {
+ char* str = ((FL_BLINE*)v)->txt;
+ const int* i = column_widths();
+ int w = 0;
+
+ while (*i) { // add up all tab-seperated fields
+ w += *i++;
+ char* e;
+ for (e = str; *e && *e != column_char(); e++);
+ if (!*e) return 0; // last one occupied by text
+ str = e+1;
+ }
+
+ // OK, we gotta parse the string and find the string width...
+ int size = textsize();
+ Fl_Font font = textfont();
+ int done = 0;
+
+ while (*str == format_char_ && *++str && *str != format_char_) {
+ switch (*str++) {
+ case 'l': case 'L': size = 24; break;
+ case 'm': case 'M': size = 18; break;
+ case 's': size = 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 'S':
+ size = strtol(str, &str, 10);
+ break;
+ case '.':
+ done = 1;
+ case '@':
+ str--;
+ done = 1;
+ }
+
+ if (done)
+ break;
+ }
+
+ fl_font(font, size, Fl_Input_::default_font(), Fl_Input_::default_size());
+ return w + int(fl_width(str)) + 6;
+}
+
+int Fl_Browser::full_height() const {
+ return full_height_;
+}
+
+int Fl_Browser::incr_height() const {
+ return textsize()+2;
+}
+
+void Fl_Browser::item_draw(void* v, int x, int y, int w, int h) const {
+ char* str = ((FL_BLINE*)v)->txt;
+ const int* i = column_widths();
+
+ while (w > 6) { // do each tab-seperated field
+ int w1 = w; // width for this field
+ char* e = 0; // pointer to end of field or null if none
+ if (*i) { // find end of field and temporarily replace with 0
+ for (e = str; *e && *e != column_char(); e++);
+ if (*e) {*e = 0; w1 = *i++;} else e = 0;
+ }
+ int size = textsize();
+ Fl_Font font = textfont();
+ Fl_Color lcol = textcolor();
+ Fl_Align align = FL_ALIGN_LEFT;
+ // check for all the @-lines recognized by XForms:
+ while (*str == format_char() && *++str && *str != format_char()) {
+ switch (*str++) {
+ case 'l': case 'L': size = 24; break;
+ case 'm': case 'M': size = 18; break;
+ case 's': size = 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': align = FL_ALIGN_CENTER; break;
+ case 'r': align = FL_ALIGN_RIGHT; break;
+ case 'B':
+ fl_color((Fl_Color)strtol(str, &str, 10));
+ fl_rectf(x, y, w1, h);
+ break;
+ case 'C':
+ lcol = (Fl_Color)strtol(str, &str, 10);
+ break;
+ case 'F':
+ font = (Fl_Font)strtol(str, &str, 10);
+ break;
+ case 'N':
+ lcol = FL_INACTIVE_COLOR;
+ break;
+ case 'S':
+ size = 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;
+ case '@':
+ str--; goto BREAK;
+ }
+ }
+ BREAK:
+ fl_font(font, size, Fl_Input_::default_font(), Fl_Input_::default_size());
+ if (!active_r()) lcol = inactive(lcol);
+ if (((FL_BLINE*)v)->flags & SELECTED)
+ lcol = contrast(lcol, selection_color());
+ fl_color(lcol);
+ fl_draw(str, x+3, y, w1-6, h, align);
+ if (!e) break; // no more fields...
+ *e = column_char(); // put the seperator back
+ 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::topline(int line) {
+ if (line<1) line = 1;
+ if (line>lines) line = lines;
+ int p = 0;
+ for (FL_BLINE* l=first; l&& line>1; l = l->next) {
+ line--; p += item_height(l);
+ }
+ position(p);
+}
+
+int Fl_Browser::topline() const {
+ return lineno(top());
+}
+
+void Fl_Browser::clear() {
+ for (FL_BLINE* l = first; l;) {
+ FL_BLINE* h = l->next;
+ free(l);
+ l = h;
+ }
+ full_height_ = 0;
+ first = 0;
+ lines = 0;
+ new_list();
+}
+
+void Fl_Browser::add(const char* newtext, void* data) {
+ insert(lines+1, newtext, data);
+ Fl_Browser_::display(last);
+}
+
+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 value) {
+ if (line < 1 || line > lines) return 0;
+ return Fl_Browser_::select(find_line(line), value);
+}
+
+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);
+ if (Fl_Browser_::displayed(t)) redraw_lines();
+ }
+}
+
+void Fl_Browser::hide(int line) {
+ FL_BLINE* t = find_line(line);
+ if (!(t->flags & NOTDISPLAYED)) {
+ full_height_ -= item_height(t);
+ t->flags |= NOTDISPLAYED;
+ if (Fl_Browser_::displayed(t)) redraw_lines();
+ }
+}
+
+void Fl_Browser::display(int line, int value) {
+ if (line < 1 || line > lines) return;
+ if (value) 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());
+}
+
+// end of Fl_Browser.C