#include <FL/Fl_Grid.H>
#include <FL/fl_draw.H>
class Fl_Grid::Col {
friend class Fl_Grid;
int minw_; int w_; short weight_; short gap_; Col() {
minw_ = 0;
w_ = 0;
weight_ = 50;
gap_ = -1;
}
~Col() {};
};
class Fl_Grid::Row {
friend class Fl_Grid;
Cell *cells_; int minh_; int h_; short weight_; short gap_;
Row() {
cells_ = NULL;
minh_ = 0;
h_ = 0;
weight_ = 50;
gap_ = -1;
}
~Row() {
free_cells();
};
void free_cells() {
Cell *cel = cells_;
while (cel) {
Cell *next = cel->next();
delete cel;
cel = next;
} cells_ = 0;
}
void remove_cell(int col) { Cell *cel = cells_;
Cell *prev = 0;
while (cel) {
Cell *next = cel->next();
if (cel->col() == col) {
if (prev) {
prev->next(next);
} else {
cells_ = next;
}
delete cel;
return;
} else {
prev = cel;
cel = next;
}
} }
};
Fl_Grid::Fl_Grid(int X, int Y, int W, int H, const char *L)
: Fl_Group(X, Y, W, H, L) {
init();
box(FL_FLAT_BOX);
}
void Fl_Grid::init() {
rows_ = 0;
cols_ = 0;
margin_left_ = 0;
margin_top_ = 0;
margin_right_ = 0;
margin_bottom_ = 0;
gap_row_ = 0;
gap_col_ = 0;
Cols_ = 0;
Rows_ = 0;
old_size = Fl_Rect(0, 0, 0, 0);
need_layout_ = false; grid_color = (Fl_Color)0xbbeebb00; draw_grid_ = false; if (fl_getenv("FLTK_GRID_DEBUG"))
draw_grid_ = true;
}
Fl_Grid::~Fl_Grid() {
delete[] Cols_;
delete[] Rows_;
}
void Fl_Grid::layout(int rows, int cols, int margin, int gap) {
if (margin >= 0)
margin_left_ = margin_top_ = margin_right_ = margin_bottom_ = margin;
if (gap >= 0)
gap_row_ = gap_col_ = gap;
if (cols == cols_ && rows == rows_) return;
if (rows <= 0 || cols <= 0) {
clear_layout();
return;
}
if (cols != cols_) {
Col *new_cols = new Col[cols];
for (int c = 0; c < cols; c++) {
if (c < cols_)
new_cols[c] = Cols_[c];
else
break;
}
delete[] Cols_;
Cols_ = new_cols;
}
if (rows != rows_) {
Row *new_rows = new Row[rows];
Row *row = Rows_;
for (int r = 0; r < rows; r++, row++) {
if (r < rows_) {
new_rows[r] = *row;
row->cells_ = 0;
} else {
break;
}
}
delete[] Rows_;
Rows_ = new_rows;
}
cols_ = cols;
rows_ = rows;
need_layout(1);
}
void Fl_Grid::draw_grid() {
int x0 = x() + Fl::box_dx(box()) + margin_left_;
int y0 = y() + Fl::box_dy(box()) + margin_top_;
int x1 = x() + w() - Fl::box_dx(box()) - margin_right_;
int y1 = y() + h() - Fl::box_dy(box()) - margin_bottom_;
fl_line_style(FL_SOLID, 1);
fl_color(grid_color);
fl_rect(x0, y0, x1 - x0, y1 - y0);
for (int r = 0; r < rows_ - 1; r++) {
int gap = Rows_[r].gap_ >= 0 ? Rows_[r].gap_ : gap_row_;
y0 += Rows_[r].h_;
if (gap == 0) {
fl_xyline(x0, y0, x1);
} else {
fl_rectf(x0, y0, x1 - x0, gap);
}
y0 += gap;
}
x0 = x() + Fl::box_dx(box()) + margin_left_;
y0 = y() + Fl::box_dy(box()) + margin_top_;
for (int c = 0; c < cols_ - 1; c++) {
int gap = Cols_[c].gap_ >= 0 ? Cols_[c].gap_ : gap_col_;
x0 += Cols_[c].w_;
if (gap == 0) {
fl_yxline(x0, y0, y1);
} else {
fl_rectf(x0, y0, gap, y1 - y0);
}
x0 += gap;
}
fl_line_style(FL_SOLID, 0);
fl_color(FL_BLACK);
}
void Fl_Grid::draw() {
if (need_layout())
layout();
if (damage() & ~FL_DAMAGE_CHILD) { draw_box();
if (draw_grid_)
draw_grid();
draw_label();
}
draw_children();
}
void Fl_Grid::layout() {
if (rows_ == 0 || cols_ == 0) return;
Row *row;
Col *col;
Cell *cel;
int tw = w() - Fl::box_dw(box()) - margin_left_ - margin_right_;
int th = h() - Fl::box_dh(box()) - margin_top_ - margin_bottom_;
col = Cols_;
for (int c = 0; c < cols_; c++, col++) {
col->w_ = col->minw_;
}
row = Rows_;
for (int r = 0; r < rows_; r++, row++) {
row->h_ = row->minh_;
}
row = Rows_;
for (int r = 0; r < rows_; r++, row++) {
col = Cols_;
for (int c = 0; c < cols_; c++, col++) {
cel = cell(r, c);
if (cel) {
Fl_Widget *wi = cel->widget_;
if (wi && wi->visible()) {
if (cel->colspan_ == 1 && cel->w_ > col->w_) col->w_ = cel->w_;
if (cel->rowspan_ == 1 && cel->h_ > row->h_) row->h_ = cel->h_;
} } } }
int tcwi = 0; int tcwe = 0; int hcwe = 0; int icwe = 0;
int trhe = 0; int trwe = 0; int hrwe = 0; int irwe = 0;
col = Cols_;
for (int c = 0; c < cols_; c++, col++) {
tcwi += col->w_;
tcwe += col->weight_;
if (c < cols_ - 1)
tcwi += ((col->gap_ >= 0) ? col->gap_ : gap_col_);
if (col->weight_ > hcwe) {
hcwe = col->weight_;
icwe = c;
}
}
row = Rows_;
for (int r = 0; r < rows_; r++, row++) {
trhe += row->h_;
trwe += row->weight_;
if (r < rows_ - 1)
trhe += ((row->gap_ >= 0) ? row->gap_ : gap_row_);
if (row->weight_ > hrwe) {
hrwe = row->weight_;
irwe = r;
}
}
int space = tw - tcwi; int add_space = 0; int remaining = 0;
if (space > 0 && tcwe > 0) {
remaining = space;
col = Cols_;
for (int c = 0; c < cols_; c++, col++) {
if (col->weight_ > 0) {
add_space = int(float(space * col->weight_) / tcwe + 0.5);
col->w_ += add_space;
remaining -= add_space;
}
}
if (remaining != 0)
Cols_[icwe].w_ += remaining;
}
space = th - trhe;
if (space > 0 && trwe > 0) {
remaining = space;
row = Rows_;
for (int r = 0; r < rows_; r++, row++) {
if (row->weight_ > 0) {
add_space = int(float(space * row->weight_) / trwe + 0.5);
row->h_ += add_space;
remaining -= add_space;
}
}
if (remaining != 0)
Rows_[irwe].h_ += remaining;
}
int x0, y0;
y0 = y() + Fl::box_dy(box()) + margin_top_;
row = Rows_;
for (int r = 0; r < rows_; r++, row++) {
x0 = x() + Fl::box_dx(box()) + margin_left_;
col = Cols_;
for (int c = 0; c < cols_; c++, col++) {
int wx = x0; int wy = y0; cel = cell(r, c);
if (cel) {
Fl_Widget *wi = cel->widget_;
if (wi && wi->visible()) {
int ww = col->w_;
int wh = row->h_;
for (int i = 0; i < cel->colspan_ - 1; i++) {
ww += (Cols_[c + i].gap_ >= 0) ? Cols_[c + i].gap_ : gap_col_;
ww += Cols_[c + i + 1].w_;
}
for (int i = 0; i < cel->rowspan_ - 1; i++) {
wh += (Rows_[r + i].gap_ >= 0) ? Rows_[r + i].gap_ : gap_row_;
wh += Rows_[r + i + 1].h_;
}
Fl_Grid_Align ali = cel->align_;
Fl_Grid_Align mask;
mask = FL_GRID_LEFT | FL_GRID_RIGHT | FL_GRID_HORIZONTAL;
if ((ali & mask) == 0) {
wx += (ww - cel->w_) / 2;
ww = cel->w_;
} else if ((ali & mask) == FL_GRID_LEFT) {
ww = cel->w_;
} else if ((ali & mask) == FL_GRID_RIGHT) {
wx += ww - cel->w_;
ww = cel->w_;
}
mask = FL_GRID_TOP | FL_GRID_BOTTOM | FL_GRID_VERTICAL;
if ((ali & mask) == 0) {
wy += (wh - cel->h_) / 2;
wh = cel->h_;
} else if ((ali & mask) == FL_GRID_TOP) {
wh = cel->h_;
} else if ((ali & mask) == FL_GRID_BOTTOM) {
wy += wh - cel->h_;
wh = cel->h_;
}
wi->resize(wx, wy, ww, wh);
}
}
x0 += (col->w_ + ((col->gap_ >= 0) ? col->gap_ : gap_col_));
}
y0 += ( row->h_ + ((row->gap_ >= 0) ? row->gap_ : gap_row_) );
}
need_layout(0);
redraw();
}
void Fl_Grid::on_remove(int index) {
Fl_Widget *wi = child(index);
Cell *c = cell(wi); if (c) {
remove_cell(c->row_, c->col_);
}
}
Fl_Grid::Cell *Fl_Grid::add_cell(int row, int col) {
Cell *c = new Cell(row, col);
Row *r = &Rows_[row];
Cell* cel = r->cells_; Cell* prev = 0; while (cel) { if (cel->col_ > col) { break;
}
prev = cel;
cel = cel->next_;
}
if (prev)
prev->next_ = c;
else
r->cells_ = c;
c->next_ = cel;
need_layout(1);
return c;
}
void Fl_Grid::remove_cell(int row, int col) {
Row *r = &Rows_[row];
r->remove_cell(col);
need_layout(1);
}
void Fl_Grid::resize(int X, int Y, int W, int H) {
old_size = Fl_Rect(x(), y(), w(), h());
Fl_Widget::resize(X, Y, W, H);
layout();
}
void Fl_Grid::clear_layout() {
delete[] Cols_;
delete[] Rows_;
init();
for (int i = 0; i < children(); i++) {
child(i)->hide();
}
need_layout(1);
return;
}
void Fl_Grid::margin(int left, int top, int right, int bottom) {
if (left >= 0)
margin_left_ = left;
if (top >= 0)
margin_top_ = top;
if (right >= 0)
margin_right_ = right;
if (bottom >= 0)
margin_bottom_ = bottom;
need_layout(1);
}
int Fl_Grid::margin(int *left, int *top, int *right, int *bottom) const {
if (left) *left = margin_left_;
if (top) *top = margin_top_;
if (right) *right = margin_right_;
if (bottom) *bottom = margin_bottom_;
if (margin_left_ == margin_top_ && margin_top_ == margin_right_ && margin_right_ == margin_bottom_)
return 1;
return 0;
}
void Fl_Grid::gap(int row_gap, int col_gap) {
if (row_gap >= 0)
gap_row_ = row_gap;
if (col_gap >= 0)
gap_col_ = col_gap;
need_layout(1);
}
void Fl_Grid::gap(int *row_gap, int *col_gap) const {
if (row_gap)
*row_gap = gap_row_;
if (col_gap)
*col_gap = gap_col_;
}
Fl_Grid::Cell* Fl_Grid::cell(int row, int col) const {
if (row < 0 || row >= rows_ || col < 0 || col >= cols_)
return 0;
Row *r = &Rows_[row];
Cell *cel = r->cells_;
while (cel) {
if (cel->col_ > col)
return 0;
if (cel->col_ == col)
return cel;
cel = cel->next_;
}
return 0;
}
Fl_Grid::Cell* Fl_Grid::cell(Fl_Widget *widget) const {
Row *row = Rows_;
for (int r = 0; r < rows_; r++, row++) {
Cell *cel = row->cells_;
while (cel) {
if (cel->widget_ == widget)
return cel;
cel = cel->next_;
}
}
return 0;
}
Fl_Grid::Cell *Fl_Grid::widget(Fl_Widget *wi, int row, int col, Fl_Grid_Align align) {
return widget(wi, row, col, 1, 1, align);
}
Fl_Grid::Cell *Fl_Grid::widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align) {
int child = Fl_Group::find(wi); if (child >= children()) {
return 0;
}
if (row < 0 || row > rows_)
return 0;
if (col < 0 || col > cols_)
return 0;
Cell *c = cell(row, col);
if (!c) {
c = add_cell(row, col);
}
if (c->widget_ != wi) {
Cell *oc = cell(wi); if (oc) { remove_cell(oc->row_, oc->col_);
}
}
c->widget_ = wi;
c->align_ = align;
c->w_ = wi->w();
c->h_ = wi->h();
if (rowspan > 0)
c->rowspan_ = rowspan;
if (colspan > 0)
c->colspan_ = colspan;
need_layout(1);
return c;
}
void Fl_Grid::col_width(int col, int value) {
if (col >= 0 && col < cols_) {
if (Cols_[col].minw_ != value) {
Cols_[col].minw_ = value;
need_layout(1);
}
}
}
void Fl_Grid::col_width(const int *value, size_t size) {
Col *c = Cols_;
for (int i = 0; i < cols_; i++, value++, c++) {
if (i >= (int)size) break;
if (*value >= 0)
c->minw_ = *value;
}
need_layout(1);
}
int Fl_Grid::col_width(int col) const {
if (col >= 0 && col < cols_) return Cols_[col].minw_;
return 0;
}
void Fl_Grid::col_weight(int col, int value) {
if (col >= 0 && col < cols_)
Cols_[col].weight_ = value;
need_layout(1);
}
void Fl_Grid::col_weight(const int *value, size_t size) {
Col *c = Cols_;
for (int i = 0; i < cols_; i++, value++, c++) {
if (i >= (int)size) break;
if (*value >= 0)
c->weight_ = *value;
}
need_layout(1);
}
int Fl_Grid::col_weight(int col) const {
if (col >= 0 && col < cols_) return Cols_[col].weight_;
return 0;
}
void Fl_Grid::col_gap(int col, int value) {
if (col >= 0 && col < cols_)
Cols_[col].gap_ = value;
need_layout(1);
}
void Fl_Grid::col_gap(const int *value, size_t size) {
Col *c = Cols_;
for (int i = 0; i < cols_; i++, value++, c++) {
if (i >= (int)size) break;
if (*value >= 0)
c->gap_ = *value;
}
need_layout(1);
}
int Fl_Grid::col_gap(int col) const {
if (col >= 0 && col < cols_) return Cols_[col].gap_;
return 0;
}
void Fl_Grid::row_height(int row, int value) {
if (row >= 0 && row < rows_)
Rows_[row].minh_ = value;
need_layout(1);
}
void Fl_Grid::row_height(const int *value, size_t size) {
Row *r = Rows_;
for (int i = 0; i < rows_; i++, value++, r++) {
if (i >= (int)size) break;
if (*value >= 0)
r->minh_ = *value;
}
need_layout(1);
}
int Fl_Grid::row_height(int row) const {
if (row >= 0 && row < rows_) return Rows_[row].minh_;
return 0;
}
void Fl_Grid::row_weight(int row, int value) {
if (row >= 0 && row < rows_)
Rows_[row].weight_ = value;
need_layout(1);
}
void Fl_Grid::row_weight(const int *value, size_t size) {
Row *r = Rows_;
for (int i = 0; i < rows_; i++, value++, r++) {
if (i >= (int)size) break;
if (*value >= 0)
r->weight_ = *value;
}
need_layout(1);
}
int Fl_Grid::row_weight(int row) const {
if (row >= 0 && row < rows_) return Rows_[row].weight_;
return 0;
}
void Fl_Grid::row_gap(int row, int value) {
if (row >= 0 && row < rows_)
Rows_[row].gap_ = value;
need_layout(1);
}
void Fl_Grid::row_gap(const int *value, size_t size) {
Row *r = Rows_;
for (int i = 0; i < rows_; i++, value++, r++) {
if (i >= (int)size) break;
if (*value >= 0)
r->gap_ = *value;
}
need_layout(1);
}
int Fl_Grid::row_gap(int row) const {
if (row >= 0 && row < rows_) return Rows_[row].gap_;
return 0;
}
int Fl_Grid::computed_col_width(int col) const {
return Cols_[col].w_;
}
int Fl_Grid::computed_row_height(int row) const {
return Rows_[row].h_;
}
void Fl_Grid::debug(int level) {
if (level <= 0)
return;
fprintf(stderr, "Fl_Grid::layout(%d, %d) at (%d, %d, %d, %d)\n",
rows_, cols_, x(), y(), w(), h());
fprintf(stderr, " margins: (%2d, %2d, %2d, %2d)\n",
margin_left_, margin_top_, margin_right_, margin_bottom_);
fprintf(stderr, " gaps: (%2d, %2d)\n",
gap_row_, gap_col_);
Row *row = Rows_;
for (int r = 0; r < rows_; r++, row++) {
fprintf(stderr, "Row %2d: minh = %d, weight = %d, gap = %d, h = %d\n",
r, row->minh_, row->weight_, row->gap_, row->h_);
Cell *cel = row->cells_;
while (cel) {
fprintf(stderr, " Cell(%2d, %2d)\n", cel->row_, cel->col_);
cel = cel->next_;
}
}
fflush(stderr); }