1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
|
//
// Fl_Grid widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 2021-2022 by Albrecht Schlosser.
// Copyright 2022-2025 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:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
/** \file src/Fl_Grid.cxx
Implements the Fl_Grid container widget.
\since 1.4.0
*/
#include <FL/Fl_Grid.H>
#include <FL/fl_draw.H>
// private class Col for column management
class Fl_Grid::Col {
friend class Fl_Grid;
int minw_; // minimal size (width)
int w_; // calculated size (width)
short weight_; // weight used to allocate extra space
short gap_; // gap to the right of the column
Col() {
minw_ = 0;
w_ = 0;
weight_ = 50;
gap_ = -1;
}
~Col() {}
};
// private class Row for row management
class Fl_Grid::Row {
friend class Fl_Grid;
Cell *cells_; // cells of this row
int minh_; // minimal size (height)
int h_; // calculated size (height)
short weight_; // weight used to allocate extra space
short gap_; // gap below the row (-1 = use default)
Row() {
cells_ = NULL;
minh_ = 0;
h_ = 0;
weight_ = 50;
gap_ = -1;
}
~Row() {
free_cells();
}
// Fl_Grid::Row::free_cells() - free all cells of a row
void free_cells() {
Cell *cel = cells_;
while (cel) {
Cell *next = cel->next();
delete cel;
cel = next;
} // free_cells()
cells_ = 0;
}
// Fl_Grid::Row::remove_cell() - remove all cells of column col from the list of cells
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;
}
} // while (cel)
} // Row::remove_cell(col)
}; // class Row
/**
Create a new Fl_Grid widget.
\todo More documentation of Fl_Grid constructor?
*/
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);
}
// private: init vars
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; // no need to calculate layout
grid_color = (Fl_Color)0xbbeebb00; // light green
draw_grid_ = false; // don't draw grid helper lines
if (fl_getenv("FLTK_GRID_DEBUG"))
draw_grid_ = true;
}
Fl_Grid::~Fl_Grid() {
delete[] Cols_;
delete[] Rows_;
}
/**
Set the basic layout parameters of the Fl_Grid widget.
You need to specify at least \p rows and \p cols to define a layout
before you can add widgets to the grid.
Parameters \p margin and \p gap are optional.
You can call layout(int rows, int cols, int margin, int gap) again
to change the layout but this is inefficient since all cells are
reallocated if the layout changed.
Calling this with the same values of \p rows and \p cols is fast and
can be used to change \p margin and \p gap w/o reallocating the cells.
\p margin sets all margins (left, top, right, bottom) to the same value.
Negative values (e.g. -1) don't change the established margins.
The default value set by the constructor is 0.
\p gap sets row and column gaps to the same value. Negative values (e.g. -1)
do not affect the established gaps. The default value set by the constructor
is 0.
After you added all widgets you must call layout() once without arguments
to calculate the actual layout and to position and resize all widgets.
\todo Document when and why to call layout() w/o args. See Fl_Flex::layout()
\param[in] rows number of rows
\param[in] cols number of columns
\param[in] margin margin size inside the Fl_Grid's border
\param[in] gap gap size between cells
\see Fl_Grid::layout()
*/
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_) // same size, nothing to do
return;
// release allocated memory if either rows <= 0 or cols <= 0
if (rows <= 0 || cols <= 0) {
clear_layout();
return;
}
// allocate new cells, rows, and columns
// Cell *new_cells = new Cell[rows * cols];
// reallocate and copy old columns
if (cols != cols_) {
Col *new_cols = new Col[cols];
int c;
for (c = 0; c < cols; c++) {
if (c < cols_)
new_cols[c] = Cols_[c];
else
break;
}
delete[] Cols_;
Cols_ = new_cols;
}
// reallocate and copy old rows
if (rows != rows_) {
Row *new_rows = new Row[rows];
Row *row = Rows_;
int r;
for (r = 0; r < rows; r++, row++) {
if (r < rows_) {
new_rows[r] = *row;
row->cells_ = 0;
} else {
break;
}
}
delete[] Rows_;
Rows_ = new_rows;
}
// store new layout and cells
cols_ = cols;
rows_ = rows;
need_layout(1);
} // layout(int, int, int, int)
/**
Draws the grid helper lines for design and debugging purposes.
This method is protected so it can be modified in subclasses.
*/
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);
// draw total layout frame
fl_rect(x0, y0, x1 - x0, y1 - y0);
// draw horizontal lines (gap = 0) or rectangles (gap > 0)
int r;
for (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;
}
// draw vertical lines (gap = 0) or rectangles (gap > 0)
x0 = x() + Fl::box_dx(box()) + margin_left_;
y0 = y() + Fl::box_dy(box()) + margin_top_;
int c;
for (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);
} // Fl_Grid::draw_grid()
/**
Draws the Fl_Grid widget and all children.
If the layout has been changed layout() is called before the widget
is drawn so all children are arranged as designed.
\see layout()
\see need_layout()
*/
void Fl_Grid::draw() {
if (need_layout())
layout();
if (damage() & ~FL_DAMAGE_CHILD) { // draw the entire widget
draw_box();
if (draw_grid_)
draw_grid();
draw_label();
}
draw_children();
} // Fl_Grid::draw()
/**
Calculate the grid layout and resize and position all widgets.
This is called automatically when the Fl_Grid is resized. You need to
call it once after you added widgets or moved widgets between cells.
Calling it once after all modifications are completed is enough.
\todo Document when and why to call layout() w/o args. See Fl_Flex::layout()
\see Fl_Grid::layout(int rows, int cols, int margin, int gap)
*/
void Fl_Grid::layout() {
if (rows_ == 0 || cols_ == 0) // empty grid
return;
Row *row;
Col *col;
Cell *cel;
// calculate the total available space w/o borders and margins
int tw = w() - Fl::box_dw(box()) - margin_left_ - margin_right_;
int th = h() - Fl::box_dh(box()) - margin_top_ - margin_bottom_;
// initialize column widths and row heights
col = Cols_;
int c;
for (c = 0; c < cols_; c++, col++) {
col->w_ = col->minw_;
}
row = Rows_;
int r;
for (r = 0; r < rows_; r++, row++) {
row->h_ = row->minh_;
}
// calculate minimal column widths and row heights (in one loop)
row = Rows_;
for (r = 0; r < rows_; r++, row++) {
col = Cols_;
for (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_;
} // widget
} // colspan && rowspan
} // cols
} // rows
// calculate total space occupied by rows and columns including gaps
int tcwi = 0; // total column width incl. gaps
int tcwe = 0; // total column weight
int hcwe = 0; // highest column weight
int icwe = 0; // index of column with highest weight
int trhe = 0; // total row height incl. gaps
int trwe = 0; // total row weight
int hrwe = 0; // highest row weight
int irwe = 0; // index of row with highest weight
col = Cols_;
for (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 (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;
}
}
// Add extra space to columns and rows to fill the entire grid, using relative weights.
// Rounding differences are added to or subtracted from the col/row with the highest weight.
int space = tw - tcwi; // additional space for columns
int add_space = 0; // used for calculation
int remaining = 0; // remaining space
if (space > 0 && tcwe > 0) {
remaining = space;
col = Cols_;
for (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; // additional space for rows
if (space > 0 && trwe > 0) {
remaining = space;
row = Rows_;
for (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;
}
// calculate and assign widget positions and sizes
int x0, y0; // starting x/y positions
y0 = y() + Fl::box_dy(box()) + margin_top_;
row = Rows_;
for (r = 0; r < rows_; r++, row++) {
x0 = x() + Fl::box_dx(box()) + margin_left_;
col = Cols_;
for (c = 0; c < cols_; c++, col++) {
int wx = x0; // widget's x
int wy = y0; // widget's y
cel = cell(r, c);
if (cel) {
Fl_Widget *wi = cel->widget_;
if (wi && wi->visible()) {
// calculate the cell's position and size, take cell spanning into account
int ww = col->w_;
int wh = row->h_;
int i;
for (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 (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_;
}
// horizontal alignment: left + right => stretch
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_;
}
// vertical alignment: top + bottom => stretch
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);
} // widget is visible
} // cell
x0 += (col->w_ + ((col->gap_ >= 0) ? col->gap_ : gap_col_));
} // cols
y0 += ( row->h_ + ((row->gap_ >= 0) ? row->gap_ : gap_row_) );
} // rows
need_layout(0);
redraw();
} // layout()
/**
Fl_Group calls this method when a child widget is about to be removed.
Make sure that the widget is also removed from our internal list of children.
*/
void Fl_Grid::on_remove(int index) {
Fl_Widget *wi = child(index);
Cell *c = cell(wi); // find the cell of this child
if (c) {
remove_cell(c->row_, c->col_);
}
}
// private: add a new cell to the grid
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_; // "current" cell
Cell* prev = 0; // "previous" cell
while (cel) { // existing cells
if (cel->col_ > col) { // found spot ...
break;
}
prev = cel;
cel = cel->next_;
}
// insertion point: prev => last cell or NULL, cel == next cell or NULL
if (prev)
prev->next_ = c;
else
r->cells_ = c;
c->next_ = cel;
need_layout(1);
return c;
}
// private: remove a cell from the grid
void Fl_Grid::remove_cell(int row, int col) {
Row *r = &Rows_[row];
r->remove_cell(col);
need_layout(1);
}
/**
Recalculate the layout and position and resize all widgets.
This method s Fl_Group::resize() and calculates all positions and
sizes of its children according to its own rules.
\param[in] X,Y new widget position
\param[in] W,H new widget size
*/
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();
}
/**
Reset the layout w/o removing widgets.
Removes all cells and sets rows and cols to zero. Existing widgets are
kept as children of the Fl_Group (base class) but are hidden.
This method should be rarely used. You may want to call Fl_Grid::clear()
to remove all widgets and reset the layout to zero rows and columns.
You must call layout(int rows, int cols, ...) to set a new layout,
allocate new cells, and assign widgets to new cells.
\todo Fl_Grid::clear() needs to be implemented as documented above!
*/
void Fl_Grid::clear_layout() {
delete[] Cols_;
delete[] Rows_;
init();
int i;
for (i = 0; i < children(); i++) {
child(i)->hide();
}
need_layout(1);
return;
}
/**
Set all margins (left, top, right, bottom).
All margins are measured in pixels inside the box borders. You need
to specify at least one argument, all other arguments are optional.
If you don't specify an argument or use a negative value (e.g. -1)
then that particular margin is not affected.
\param[in] left left margin
\param[in] top top margin
\param[in] right right margin
\param[in] bottom bottom margin
*/
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);
}
/**
Returns all outside margin sizes of the grid.
All margin sizes are returned in the given arguments. If any argument
is \p NULL the respective value is not returned.
\param[out] left returns left margin if not \p NULL
\param[out] top returns top margin if not \p NULL
\param[out] right returns right margin if not \p NULL
\param[out] bottom returns bottom margin if not \p NULL
\return whether all margins are equal
\retval 1 all margins have the same size
\retval 0 at least one margin has a different size
*/
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;
}
/**
Set default gaps for rows and columns.
All gaps are positioned below the rows and right of their columns.
The bottom row and the right-most column don't have a gap, i.e. the gap
sizes of these columns and rows are ignored. You can use a right or
bottom margin instead.
You have to specify at least one argument, \p col_gap is optional.
If you don't specify an argument or use a negative value (e.g. -1)
then that margin is not affected.
You can also initialize the default gaps with layout(int, int, int, int).
\param[in] row_gap default gap for all rows
\param[in] col_gap default gap for all columns
\see Fl_Grid::layout(int rows, int cols, int margin, int gap)
*/
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);
}
/**
Get the default gaps for rows and columns.
\param[out] row_gap pointer to int to receive column gap, may be NULL
\param[out] col_gap pointer to int to receive column gap, may be NULL
*/
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_;
}
/**
Get the grid cell of row \p row and column \p col.
Widgets and other attributes are organized in cells (Fl_Grid::Cell).
This cell is an opaque structure (class) with some public methods.
\b Don't assume anything about grid cell sizes and ordering in memory.
These are implementation details that can be changed without notice.
The validity of an Fl_Grid::Cell pointer is limited. It will definitely be
invalidated when the overall grid layout is changed, for instance by calling
layout(int, int).
Adding new cells beyond the current layout limits will also invalidate
cell pointers but this is not (yet) implemented. Attempts to assign widgets
to out-of-bounds cells are currently ignored.
The only well-defined usage of cell pointers is to set one or more properties
like widget alignment of a cell after retrieving the cell pointer. Don't
store cell pointers in your program for later reference.
\param[in] row row index
\param[in] col column index
\returns pointer to cell
\retval NULL if \p row or \p col is out of bounds or no widget was assigned
*/
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;
}
/**
Get the grid cell of widget \p widget.
The pointer to the cell can be used for further assignment of properties
like alignment etc.
Hint: If you know the row and column index of the cell you should use
Fl_Grid::cell(int row, int col) instead because it is \b much faster.
Please see Fl_Grid::cell(int row, int col) for details and the
validity of cell pointers.
\param[in] widget widget whose cell is requested
\retval NULL if \p widget is not assigned to a cell
*/
Fl_Grid::Cell* Fl_Grid::cell(Fl_Widget *widget) const {
Row *row = Rows_;
int r;
for (r = 0; r < rows_; r++, row++) {
Cell *cel = row->cells_;
while (cel) {
if (cel->widget_ == widget)
return cel;
cel = cel->next_;
}
}
return 0;
}
/**
Assign a widget to a grid cell and set its alignment.
This short form sets row and column spanning attributes to (1, 1).
For more information see
Fl_Grid::widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align)
\param[in] wi widget to be assigned to the cell
\param[in] row row
\param[in] col column
\param[in] align widget alignment inside the cell
\return assigned cell
\retval NULL if \p row or \p col is out of bounds
\see Fl_Grid::widget(Fl_Widget *wi, int row, int col, int rowspan, int colspan, Fl_Grid_Align align)
*/
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);
}
/**
Assign a widget to a grid cell and set cell spanning and alignment.
Default alignment is \c FL_GRID_FILL which stretches the widget in
horizontal and vertical directions to fill the whole cell(s) given
by \p colspan and \p rowspan.
You can use this method to move a widget from one cell to another; it
is automatically removed from its old cell. If the new cell is already
assigned to another widget that widget is deassigned but kept as a
child of the group.
Before you can assign a widget to a cell it must have been created as
a child of the Fl_Grid widget (i.e. its Fl_Group).
\param[in] wi widget to be assigned to the cell
\param[in] row row
\param[in] col column
\param[in] rowspan vertical span in cells, default 1
\param[in] colspan horizontal span in cells, default 1
\param[in] align widget alignment inside the cell
\return assigned cell
\retval NULL if \p row or \p col is out of bounds or \p wi is not a child
*/
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); // is this widget one of our children?
if (child >= children()) {
// fprintf(stderr, "Fl_Grid::widget(): can't assign widget %p to cell (%d, %d): not a child!\n", wi, row, col);
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);
}
// check if the widget is already assigned to another cell and deassign it
if (c->widget_ != wi) {
Cell *oc = cell(wi); // search cells for the same widget
if (oc) { // if found: deassign and remove cell
remove_cell(oc->row_, oc->col_);
}
}
// assign the widget to this cell
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;
}
/**
Set the minimal width of a column.
Column widths are calculated by using the maximum of all widget widths
in that column and the given column width. After calculating the width
additional space is added when resizing according to the \c weight of
the column.
You can set one or more column widths in one call by using
Fl_Grid::col_width(const int *value, size_t size).
\param[in] col column number (counting from 0)
\param[in] value minimal column width, must be \>= 0
\see Fl_Grid::col_width(const int *value, size_t size)
*/
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);
}
}
}
/**
Set minimal widths of more than one column.
The values are taken from the array \p value and assigned sequentially
to columns, starting from column 0. If the array \p size is too large
extraneous values are ignored.
Negative values in the \p array are not assigned to their columns,
i.e. the existing value for the corresponding column is not changed.
Example:
\code
int widths[] = { 0, 0, 50, -1, -1, 50, 0 };
grid->col_width(widths, sizeof(width)/sizeof(width[0]));
\endcode
\param[in] value an array of column widths
\param[in] size the size of the array (number of values)
*/
void Fl_Grid::col_width(const int *value, size_t size) {
Col *c = Cols_;
int i;
for (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;
}
/**
Set the weight of a column.
Column and row weights are used to distribute additional space when the
grid is resized beyond its defined (minimal) size. All weight values are
relative and can be chosen freely. Suggested weights are in the range
{0 .. 100}, 0 (zero) disables resizing of the column.
How does it work?
Whenever additional space (say: \c SPACE in pixels) is to be distributed
to a set of columns the weights of all columns are added to a value \c SUM,
then every single column width is increased by the value (in pseudo code):
\code
col.width += SPACE * col.weight / SUM
\endcode
Resulting pixel values are rounded to the next integer and rounding
differences are added to or subtracted from the column with the highest
weight. If more columns have the same weight one of them is chosen.
\note If none of the columns considered for resizing have weights \> 0
then Fl_Grid assigns the remaining space to an arbitrary column or to
all considered columns evenly. This is implementation defined and can
be changed without notice. You can avoid this situation by designing
your grid with sensible sizes and weights.
\param[in] col column number (counting from 0)
\param[in] value weight, must be \>= 0
*/
void Fl_Grid::col_weight(int col, int value) {
if (col >= 0 && col < cols_)
Cols_[col].weight_ = value;
need_layout(1);
}
/**
Set the weight of more than one column.
The values are taken from the array \p value and assigned sequentially
to columns, starting from column 0. If the array \p size is too large
extraneous values are ignored.
Negative values in the \p array are not assigned to their columns,
i.e. the existing value for the corresponding column is not changed.
Example:
\code
int val[] = { 0, 0, 50, -1, -1, 50, 0 };
grid->col_weight(val, sizeof(val)/sizeof(val[0]));
\endcode
\param[in] value an array of column weights
\param[in] size the size of the array (number of values)
*/
void Fl_Grid::col_weight(const int *value, size_t size) {
Col *c = Cols_;
int i;
for (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;
}
/**
Set the gap of column \c col.
Note that the gap is right of each column except the last one
which is ignored. Use margin() for the right most column.
\param[in] col column
\param[in] value gap size after the column
*/
void Fl_Grid::col_gap(int col, int value) {
if (col >= 0 && col < cols_)
Cols_[col].gap_ = value;
need_layout(1);
}
/**
Set more than one column gaps at once.
\see Fl_Grid::col_weight(const int *value, size_t size) for
handling of the value array and \p size.
*/
void Fl_Grid::col_gap(const int *value, size_t size) {
Col *c = Cols_;
int i;
for (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;
}
/**
Set the minimal row height of row \c row.
\param[in] row row
\param[in] value minimal height of the row
*/
void Fl_Grid::row_height(int row, int value) {
if (row >= 0 && row < rows_)
Rows_[row].minh_ = value;
need_layout(1);
}
/**
Set the minimal row height of more than one row.
\param[in] value array of height values
\param[in] size size of array \p value
\see Fl_Grid::col_weight(const int *value, size_t size) for
handling of the value array and \p size.
*/
void Fl_Grid::row_height(const int *value, size_t size) {
Row *r = Rows_;
int i;
for (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;
}
/**
Set the row weight of row \c row.
\param[in] row row
\param[in] value weight of the row
*/
void Fl_Grid::row_weight(int row, int value) {
if (row >= 0 && row < rows_)
Rows_[row].weight_ = value;
need_layout(1);
}
/**
Set the weight of more than one row.
\param[in] value array of height values
\param[in] size size of array \p value
\see Fl_Grid::col_weight(const int *value, size_t size) for
handling of the \p value array and \p size.
*/
void Fl_Grid::row_weight(const int *value, size_t size) {
Row *r = Rows_;
int i;
for (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;
}
/**
Set the gap of row \c row.
Note that the gap is below each row except the last one
which is ignored. Use margin() for the bottom row.
\param[in] row row
\param[in] value gap size below the row
*/
void Fl_Grid::row_gap(int row, int value) {
if (row >= 0 && row < rows_)
Rows_[row].gap_ = value;
need_layout(1);
}
/**
Set more than one row gaps at once.
\see Fl_Grid::col_weight(const int *value, size_t size) for
handling of the value array and \p size.
*/
void Fl_Grid::row_gap(const int *value, size_t size) {
Row *r = Rows_;
int i;
for (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_;
}
/**
Output layout information of this Fl_Grid to stderr.
Parameter \p level will be used to define the amount of output.
- 0 = nothing
- 127 = everything
- other values not yet defined
\note It is not yet defined which kind of values \p level will have,
either a numerical value (127 = maximum, 0 = nothing) or a bit mask
that determines what to output.
\todo Add more information about cells and children.
\todo Control output by using \p level.
\param[in] level not yet used (0-127, default = 127)
*/
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_;
int r;
for (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); // necessary for Windows
}
|