tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / GridLayout.cpp
blob135d7d86c2ab0d662ba6442907b8c968aec11535
1 /*
2 * Copyright 2010-2011 Haiku, Inc. All rights reserved.
3 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
5 * Distributed under the terms of the MIT License.
6 */
9 #include <GridLayout.h>
11 #include <algorithm>
12 #include <new>
13 #include <string.h>
15 #include <ControlLook.h>
16 #include <LayoutItem.h>
17 #include <List.h>
18 #include <Message.h>
20 #include "ViewLayoutItem.h"
23 using std::nothrow;
24 using std::swap;
27 enum {
28 MAX_COLUMN_ROW_COUNT = 1024,
32 namespace {
33 // a placeholder we put in our grid array to make a cell occupied
34 BLayoutItem* const OCCUPIED_GRID_CELL = (BLayoutItem*)0x1;
36 const char* const kRowSizesField = "BGridLayout:rowsizes";
37 // kRowSizesField = {min, max}
38 const char* const kRowWeightField = "BGridLayout:rowweight";
39 const char* const kColumnSizesField = "BGridLayout:columnsizes";
40 // kColumnSizesField = {min, max}
41 const char* const kColumnWeightField = "BGridLayout:columnweight";
42 const char* const kItemDimensionsField = "BGridLayout:item:dimensions";
43 // kItemDimensionsField = {x, y, width, height}
47 struct BGridLayout::ItemLayoutData {
48 Dimensions dimensions;
50 ItemLayoutData()
52 dimensions.x = 0;
53 dimensions.y = 0;
54 dimensions.width = 1;
55 dimensions.height = 1;
60 class BGridLayout::RowInfoArray {
61 public:
62 RowInfoArray()
66 ~RowInfoArray()
68 for (int32 i = 0; Info* info = (Info*)fInfos.ItemAt(i); i++)
69 delete info;
72 int32 Count() const
74 return fInfos.CountItems();
77 float Weight(int32 index) const
79 if (Info* info = _InfoAt(index))
80 return info->weight;
81 return 1;
84 void SetWeight(int32 index, float weight)
86 if (Info* info = _InfoAt(index, true))
87 info->weight = weight;
90 float MinSize(int32 index) const
92 if (Info* info = _InfoAt(index))
93 return info->minSize;
94 return B_SIZE_UNSET;
97 void SetMinSize(int32 index, float size)
99 if (Info* info = _InfoAt(index, true))
100 info->minSize = size;
103 float MaxSize(int32 index) const
105 if (Info* info = _InfoAt(index))
106 return info->maxSize;
107 return B_SIZE_UNSET;
110 void SetMaxSize(int32 index, float size)
112 if (Info* info = _InfoAt(index, true))
113 info->maxSize = size;
116 private:
117 struct Info {
118 float weight;
119 float minSize;
120 float maxSize;
123 Info* _InfoAt(int32 index) const
125 return (Info*)fInfos.ItemAt(index);
128 Info* _InfoAt(int32 index, bool resize)
130 if (index < 0 || index >= MAX_COLUMN_ROW_COUNT)
131 return NULL;
133 // resize, if necessary and desired
134 int32 count = Count();
135 if (index >= count) {
136 if (!resize)
137 return NULL;
139 for (int32 i = count; i <= index; i++) {
140 Info* info = new Info;
141 info->weight = 1;
142 info->minSize = B_SIZE_UNSET;
143 info->maxSize = B_SIZE_UNSET;
144 fInfos.AddItem(info);
148 return _InfoAt(index);
151 BList fInfos;
155 BGridLayout::BGridLayout(float horizontal, float vertical)
157 fGrid(NULL),
158 fColumnCount(0),
159 fRowCount(0),
160 fRowInfos(new RowInfoArray),
161 fColumnInfos(new RowInfoArray),
162 fMultiColumnItems(0),
163 fMultiRowItems(0)
165 SetSpacing(horizontal, vertical);
169 BGridLayout::BGridLayout(BMessage* from)
171 BTwoDimensionalLayout(BUnarchiver::PrepareArchive(from)),
172 fGrid(NULL),
173 fColumnCount(0),
174 fRowCount(0),
175 fRowInfos(new RowInfoArray),
176 fColumnInfos(new RowInfoArray),
177 fMultiColumnItems(0),
178 fMultiRowItems(0)
180 BUnarchiver unarchiver(from);
181 int32 columns;
182 from->GetInfo(kColumnWeightField, NULL, &columns);
184 int32 rows;
185 from->GetInfo(kRowWeightField, NULL, &rows);
187 // sets fColumnCount && fRowCount on success
188 if (!_ResizeGrid(columns, rows)) {
189 unarchiver.Finish(B_NO_MEMORY);
190 return;
193 for (int32 i = 0; i < fRowCount; i++) {
194 float getter;
195 if (from->FindFloat(kRowWeightField, i, &getter) == B_OK)
196 fRowInfos->SetWeight(i, getter);
198 if (from->FindFloat(kRowSizesField, i * 2, &getter) == B_OK)
199 fRowInfos->SetMinSize(i, getter);
201 if (from->FindFloat(kRowSizesField, i * 2 + 1, &getter) == B_OK)
202 fRowInfos->SetMaxSize(i, getter);
205 for (int32 i = 0; i < fColumnCount; i++) {
206 float getter;
207 if (from->FindFloat(kColumnWeightField, i, &getter) == B_OK)
208 fColumnInfos->SetWeight(i, getter);
210 if (from->FindFloat(kColumnSizesField, i * 2, &getter) == B_OK)
211 fColumnInfos->SetMinSize(i, getter);
213 if (from->FindFloat(kColumnSizesField, i * 2 + 1, &getter) == B_OK)
214 fColumnInfos->SetMaxSize(i, getter);
219 BGridLayout::~BGridLayout()
221 delete fRowInfos;
222 delete fColumnInfos;
224 for (int32 i = 0; i < fColumnCount; i++)
225 delete[] fGrid[i];
226 delete[] fGrid;
230 int32
231 BGridLayout::CountColumns() const
233 return fColumnCount;
237 int32
238 BGridLayout::CountRows() const
240 return fRowCount;
244 float
245 BGridLayout::HorizontalSpacing() const
247 return fHSpacing;
251 float
252 BGridLayout::VerticalSpacing() const
254 return fVSpacing;
258 void
259 BGridLayout::SetHorizontalSpacing(float spacing)
261 spacing = BControlLook::ComposeSpacing(spacing);
262 if (spacing != fHSpacing) {
263 fHSpacing = spacing;
265 InvalidateLayout();
270 void
271 BGridLayout::SetVerticalSpacing(float spacing)
273 spacing = BControlLook::ComposeSpacing(spacing);
274 if (spacing != fVSpacing) {
275 fVSpacing = spacing;
277 InvalidateLayout();
282 void
283 BGridLayout::SetSpacing(float horizontal, float vertical)
285 horizontal = BControlLook::ComposeSpacing(horizontal);
286 vertical = BControlLook::ComposeSpacing(vertical);
287 if (horizontal != fHSpacing || vertical != fVSpacing) {
288 fHSpacing = horizontal;
289 fVSpacing = vertical;
291 InvalidateLayout();
296 float
297 BGridLayout::ColumnWeight(int32 column) const
299 return fColumnInfos->Weight(column);
303 void
304 BGridLayout::SetColumnWeight(int32 column, float weight)
306 fColumnInfos->SetWeight(column, weight);
310 float
311 BGridLayout::MinColumnWidth(int32 column) const
313 return fColumnInfos->MinSize(column);
317 void
318 BGridLayout::SetMinColumnWidth(int32 column, float width)
320 fColumnInfos->SetMinSize(column, width);
324 float
325 BGridLayout::MaxColumnWidth(int32 column) const
327 return fColumnInfos->MaxSize(column);
331 void
332 BGridLayout::SetMaxColumnWidth(int32 column, float width)
334 fColumnInfos->SetMaxSize(column, width);
338 float
339 BGridLayout::RowWeight(int32 row) const
341 return fRowInfos->Weight(row);
345 void
346 BGridLayout::SetRowWeight(int32 row, float weight)
348 fRowInfos->SetWeight(row, weight);
352 float
353 BGridLayout::MinRowHeight(int row) const
355 return fRowInfos->MinSize(row);
359 void
360 BGridLayout::SetMinRowHeight(int32 row, float height)
362 fRowInfos->SetMinSize(row, height);
366 float
367 BGridLayout::MaxRowHeight(int32 row) const
369 return fRowInfos->MaxSize(row);
373 void
374 BGridLayout::SetMaxRowHeight(int32 row, float height)
376 fRowInfos->SetMaxSize(row, height);
380 BLayoutItem*
381 BGridLayout::ItemAt(int32 column, int32 row) const
383 if (column < 0 || column >= CountColumns()
384 || row < 0 || row >= CountRows())
385 return NULL;
387 return fGrid[column][row];
391 BLayoutItem*
392 BGridLayout::AddView(BView* child)
394 return BTwoDimensionalLayout::AddView(child);
398 BLayoutItem*
399 BGridLayout::AddView(int32 index, BView* child)
401 return BTwoDimensionalLayout::AddView(index, child);
405 BLayoutItem*
406 BGridLayout::AddView(BView* child, int32 column, int32 row, int32 columnCount,
407 int32 rowCount)
409 if (!child)
410 return NULL;
412 BLayoutItem* item = new BViewLayoutItem(child);
413 if (!AddItem(item, column, row, columnCount, rowCount)) {
414 delete item;
415 return NULL;
418 return item;
422 bool
423 BGridLayout::AddItem(BLayoutItem* item)
425 // find a free spot
426 for (int32 row = 0; row < fRowCount; row++) {
427 for (int32 column = 0; column < fColumnCount; column++) {
428 if (_IsGridCellEmpty(column, row))
429 return AddItem(item, column, row, 1, 1);
433 // no free spot, start a new column
434 return AddItem(item, fColumnCount, 0, 1, 1);
438 bool
439 BGridLayout::AddItem(int32 index, BLayoutItem* item)
441 return AddItem(item);
445 bool
446 BGridLayout::AddItem(BLayoutItem* item, int32 column, int32 row,
447 int32 columnCount, int32 rowCount)
449 if (!_AreGridCellsEmpty(column, row, columnCount, rowCount))
450 return false;
452 bool success = BTwoDimensionalLayout::AddItem(-1, item);
453 if (!success)
454 return false;
456 // set item dimensions
457 if (ItemLayoutData* data = _LayoutDataForItem(item)) {
458 data->dimensions.x = column;
459 data->dimensions.y = row;
460 data->dimensions.width = columnCount;
461 data->dimensions.height = rowCount;
464 if (!_InsertItemIntoGrid(item)) {
465 RemoveItem(item);
466 return false;
469 if (columnCount > 1)
470 fMultiColumnItems++;
471 if (rowCount > 1)
472 fMultiRowItems++;
474 return success;
478 status_t
479 BGridLayout::Archive(BMessage* into, bool deep) const
481 BArchiver archiver(into);
482 status_t result = BTwoDimensionalLayout::Archive(into, deep);
484 for (int32 i = 0; i < fRowCount && result == B_OK; i++) {
485 result = into->AddFloat(kRowWeightField, fRowInfos->Weight(i));
486 if (result == B_OK)
487 result = into->AddFloat(kRowSizesField, fRowInfos->MinSize(i));
488 if (result == B_OK)
489 result = into->AddFloat(kRowSizesField, fRowInfos->MaxSize(i));
492 for (int32 i = 0; i < fColumnCount && result == B_OK; i++) {
493 result = into->AddFloat(kColumnWeightField, fColumnInfos->Weight(i));
494 if (result == B_OK)
495 result = into->AddFloat(kColumnSizesField, fColumnInfos->MinSize(i));
496 if (result == B_OK)
497 result = into->AddFloat(kColumnSizesField, fColumnInfos->MaxSize(i));
500 return archiver.Finish(result);
504 status_t
505 BGridLayout::AllArchived(BMessage* into) const
507 return BTwoDimensionalLayout::AllArchived(into);
511 status_t
512 BGridLayout::AllUnarchived(const BMessage* from)
514 return BTwoDimensionalLayout::AllUnarchived(from);
518 BArchivable*
519 BGridLayout::Instantiate(BMessage* from)
521 if (validate_instantiation(from, "BGridLayout"))
522 return new BGridLayout(from);
523 return NULL;
527 status_t
528 BGridLayout::ItemArchived(BMessage* into, BLayoutItem* item, int32 index) const
530 ItemLayoutData* data = _LayoutDataForItem(item);
532 status_t result = into->AddInt32(kItemDimensionsField, data->dimensions.x);
533 if (result == B_OK)
534 result = into->AddInt32(kItemDimensionsField, data->dimensions.y);
536 if (result == B_OK)
537 result = into->AddInt32(kItemDimensionsField, data->dimensions.width);
539 if (result == B_OK)
540 result = into->AddInt32(kItemDimensionsField, data->dimensions.height);
542 return result;
546 status_t
547 BGridLayout::ItemUnarchived(const BMessage* from,
548 BLayoutItem* item, int32 index)
550 ItemLayoutData* data = _LayoutDataForItem(item);
551 Dimensions& dimensions = data->dimensions;
553 index *= 4;
554 // each item stores 4 int32s into kItemDimensionsField
555 status_t result = from->FindInt32(kItemDimensionsField, index, &dimensions.x);
556 if (result == B_OK)
557 result = from->FindInt32(kItemDimensionsField, ++index, &dimensions.y);
559 if (result == B_OK)
560 result = from->FindInt32(kItemDimensionsField, ++index, &dimensions.width);
562 if (result == B_OK) {
563 result = from->FindInt32(kItemDimensionsField,
564 ++index, &dimensions.height);
567 if (result != B_OK)
568 return result;
570 if (!_AreGridCellsEmpty(dimensions.x, dimensions.y,
571 dimensions.width, dimensions.height))
572 return B_BAD_DATA;
574 if (!_InsertItemIntoGrid(item))
575 return B_NO_MEMORY;
577 if (dimensions.width > 1)
578 fMultiColumnItems++;
580 if (dimensions.height > 1)
581 fMultiRowItems++;
583 return result;
587 bool
588 BGridLayout::ItemAdded(BLayoutItem* item, int32 atIndex)
590 item->SetLayoutData(new(nothrow) ItemLayoutData);
591 return item->LayoutData() != NULL;
595 void
596 BGridLayout::ItemRemoved(BLayoutItem* item, int32 fromIndex)
598 ItemLayoutData* data = _LayoutDataForItem(item);
599 Dimensions itemDimensions = data->dimensions;
600 item->SetLayoutData(NULL);
601 delete data;
603 if (itemDimensions.width > 1)
604 fMultiColumnItems--;
606 if (itemDimensions.height > 1)
607 fMultiRowItems--;
609 // remove the item from the grid
610 for (int x = 0; x < itemDimensions.width; x++) {
611 for (int y = 0; y < itemDimensions.height; y++)
612 fGrid[itemDimensions.x + x][itemDimensions.y + y] = NULL;
615 // check whether we can shrink the grid
616 if (itemDimensions.x + itemDimensions.width == fColumnCount
617 || itemDimensions.y + itemDimensions.height == fRowCount) {
618 int32 columnCount = fColumnCount;
619 int32 rowCount = fRowCount;
621 // check for empty columns
622 bool empty = true;
623 for (; columnCount > 0; columnCount--) {
624 for (int32 row = 0; empty && row < rowCount; row++)
625 empty &= (fGrid[columnCount - 1][row] == NULL);
627 if (!empty)
628 break;
631 // check for empty rows
632 empty = true;
633 for (; rowCount > 0; rowCount--) {
634 for (int32 column = 0; empty && column < columnCount; column++)
635 empty &= (fGrid[column][rowCount - 1] == NULL);
637 if (!empty)
638 break;
641 // resize the grid
642 if (columnCount != fColumnCount || rowCount != fRowCount)
643 _ResizeGrid(columnCount, rowCount);
648 bool
649 BGridLayout::HasMultiColumnItems()
651 return fMultiColumnItems > 0;
655 bool
656 BGridLayout::HasMultiRowItems()
658 return fMultiRowItems > 0;
662 int32
663 BGridLayout::InternalCountColumns()
665 return fColumnCount;
669 int32
670 BGridLayout::InternalCountRows()
672 return fRowCount;
676 void
677 BGridLayout::GetColumnRowConstraints(orientation orientation, int32 index,
678 ColumnRowConstraints* constraints)
680 if (orientation == B_HORIZONTAL) {
681 constraints->min = MinColumnWidth(index);
682 constraints->max = MaxColumnWidth(index);
683 constraints->weight = ColumnWeight(index);
684 } else {
685 constraints->min = MinRowHeight(index);
686 constraints->max = MaxRowHeight(index);
687 constraints->weight = RowWeight(index);
692 void
693 BGridLayout::GetItemDimensions(BLayoutItem* item, Dimensions* dimensions)
695 if (ItemLayoutData* data = _LayoutDataForItem(item))
696 *dimensions = data->dimensions;
700 bool
701 BGridLayout::_IsGridCellEmpty(int32 column, int32 row)
703 if (column < 0 || row < 0)
704 return false;
706 if (column >= fColumnCount || row >= fRowCount)
707 return true;
709 return (fGrid[column][row] == NULL);
713 bool
714 BGridLayout::_AreGridCellsEmpty(int32 column, int32 row, int32 columnCount,
715 int32 rowCount)
717 if (column < 0 || row < 0)
718 return false;
719 int32 toColumn = min_c(column + columnCount, fColumnCount);
720 int32 toRow = min_c(row + rowCount, fRowCount);
722 for (int32 x = column; x < toColumn; x++) {
723 for (int32 y = row; y < toRow; y++) {
724 if (fGrid[x][y] != NULL)
725 return false;
729 return true;
733 bool
734 BGridLayout::_InsertItemIntoGrid(BLayoutItem* item)
736 BGridLayout::ItemLayoutData* data = _LayoutDataForItem(item);
737 int32 column = data->dimensions.x;
738 int32 columnCount = data->dimensions.width;
739 int32 row = data->dimensions.y;
740 int32 rowCount = data->dimensions.height;
742 // resize the grid, if necessary
743 int32 newColumnCount = max_c(fColumnCount, column + columnCount);
744 int32 newRowCount = max_c(fRowCount, row + rowCount);
745 if (newColumnCount > fColumnCount || newRowCount > fRowCount) {
746 if (!_ResizeGrid(newColumnCount, newRowCount))
747 return false;
750 // enter the item in the grid
751 for (int32 x = 0; x < columnCount; x++) {
752 for (int32 y = 0; y < rowCount; y++) {
753 if (x == 0 && y == 0)
754 fGrid[column + x][row + y] = item;
755 else
756 fGrid[column + x][row + y] = OCCUPIED_GRID_CELL;
760 return true;
764 bool
765 BGridLayout::_ResizeGrid(int32 columnCount, int32 rowCount)
767 if (columnCount == fColumnCount && rowCount == fRowCount)
768 return true;
770 int32 rowsToKeep = min_c(rowCount, fRowCount);
772 // allocate new grid
773 BLayoutItem*** grid = new(nothrow) BLayoutItem**[columnCount];
774 if (grid == NULL)
775 return false;
777 memset(grid, 0, sizeof(BLayoutItem**) * columnCount);
779 bool success = true;
780 for (int32 i = 0; i < columnCount; i++) {
781 BLayoutItem** column = new(nothrow) BLayoutItem*[rowCount];
782 if (!column) {
783 success = false;
784 break;
786 grid[i] = column;
788 memset(column, 0, sizeof(BLayoutItem*) * rowCount);
789 if (i < fColumnCount && rowsToKeep > 0)
790 memcpy(column, fGrid[i], sizeof(BLayoutItem*) * rowsToKeep);
793 // if everything went fine, set the new grid
794 if (success) {
795 swap(grid, fGrid);
796 swap(columnCount, fColumnCount);
797 swap(rowCount, fRowCount);
800 // delete the old, respectively on error the partially created grid
801 for (int32 i = 0; i < columnCount; i++)
802 delete[] grid[i];
804 delete[] grid;
806 return success;
810 BGridLayout::ItemLayoutData*
811 BGridLayout::_LayoutDataForItem(BLayoutItem* item) const
813 if (!item)
814 return NULL;
815 return (ItemLayoutData*)item->LayoutData();
819 status_t
820 BGridLayout::Perform(perform_code d, void* arg)
822 return BTwoDimensionalLayout::Perform(d, arg);
826 void BGridLayout::_ReservedGridLayout1() {}
827 void BGridLayout::_ReservedGridLayout2() {}
828 void BGridLayout::_ReservedGridLayout3() {}
829 void BGridLayout::_ReservedGridLayout4() {}
830 void BGridLayout::_ReservedGridLayout5() {}
831 void BGridLayout::_ReservedGridLayout6() {}
832 void BGridLayout::_ReservedGridLayout7() {}
833 void BGridLayout::_ReservedGridLayout8() {}
834 void BGridLayout::_ReservedGridLayout9() {}
835 void BGridLayout::_ReservedGridLayout10() {}