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.
9 #include <GridLayout.h>
15 #include <ControlLook.h>
16 #include <LayoutItem.h>
20 #include "ViewLayoutItem.h"
28 MAX_COLUMN_ROW_COUNT
= 1024,
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
;
55 dimensions
.height
= 1;
60 class BGridLayout::RowInfoArray
{
68 for (int32 i
= 0; Info
* info
= (Info
*)fInfos
.ItemAt(i
); i
++)
74 return fInfos
.CountItems();
77 float Weight(int32 index
) const
79 if (Info
* info
= _InfoAt(index
))
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
))
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
;
110 void SetMaxSize(int32 index
, float size
)
112 if (Info
* info
= _InfoAt(index
, true))
113 info
->maxSize
= size
;
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
)
133 // resize, if necessary and desired
134 int32 count
= Count();
135 if (index
>= count
) {
139 for (int32 i
= count
; i
<= index
; i
++) {
140 Info
* info
= new Info
;
142 info
->minSize
= B_SIZE_UNSET
;
143 info
->maxSize
= B_SIZE_UNSET
;
144 fInfos
.AddItem(info
);
148 return _InfoAt(index
);
155 BGridLayout::BGridLayout(float horizontal
, float vertical
)
160 fRowInfos(new RowInfoArray
),
161 fColumnInfos(new RowInfoArray
),
162 fMultiColumnItems(0),
165 SetSpacing(horizontal
, vertical
);
169 BGridLayout::BGridLayout(BMessage
* from
)
171 BTwoDimensionalLayout(BUnarchiver::PrepareArchive(from
)),
175 fRowInfos(new RowInfoArray
),
176 fColumnInfos(new RowInfoArray
),
177 fMultiColumnItems(0),
180 BUnarchiver
unarchiver(from
);
182 from
->GetInfo(kColumnWeightField
, NULL
, &columns
);
185 from
->GetInfo(kRowWeightField
, NULL
, &rows
);
187 // sets fColumnCount && fRowCount on success
188 if (!_ResizeGrid(columns
, rows
)) {
189 unarchiver
.Finish(B_NO_MEMORY
);
193 for (int32 i
= 0; i
< fRowCount
; i
++) {
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
++) {
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()
224 for (int32 i
= 0; i
< fColumnCount
; i
++)
231 BGridLayout::CountColumns() const
238 BGridLayout::CountRows() const
245 BGridLayout::HorizontalSpacing() const
252 BGridLayout::VerticalSpacing() const
259 BGridLayout::SetHorizontalSpacing(float spacing
)
261 spacing
= BControlLook::ComposeSpacing(spacing
);
262 if (spacing
!= fHSpacing
) {
271 BGridLayout::SetVerticalSpacing(float spacing
)
273 spacing
= BControlLook::ComposeSpacing(spacing
);
274 if (spacing
!= fVSpacing
) {
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
;
297 BGridLayout::ColumnWeight(int32 column
) const
299 return fColumnInfos
->Weight(column
);
304 BGridLayout::SetColumnWeight(int32 column
, float weight
)
306 fColumnInfos
->SetWeight(column
, weight
);
311 BGridLayout::MinColumnWidth(int32 column
) const
313 return fColumnInfos
->MinSize(column
);
318 BGridLayout::SetMinColumnWidth(int32 column
, float width
)
320 fColumnInfos
->SetMinSize(column
, width
);
325 BGridLayout::MaxColumnWidth(int32 column
) const
327 return fColumnInfos
->MaxSize(column
);
332 BGridLayout::SetMaxColumnWidth(int32 column
, float width
)
334 fColumnInfos
->SetMaxSize(column
, width
);
339 BGridLayout::RowWeight(int32 row
) const
341 return fRowInfos
->Weight(row
);
346 BGridLayout::SetRowWeight(int32 row
, float weight
)
348 fRowInfos
->SetWeight(row
, weight
);
353 BGridLayout::MinRowHeight(int row
) const
355 return fRowInfos
->MinSize(row
);
360 BGridLayout::SetMinRowHeight(int32 row
, float height
)
362 fRowInfos
->SetMinSize(row
, height
);
367 BGridLayout::MaxRowHeight(int32 row
) const
369 return fRowInfos
->MaxSize(row
);
374 BGridLayout::SetMaxRowHeight(int32 row
, float height
)
376 fRowInfos
->SetMaxSize(row
, height
);
381 BGridLayout::ItemAt(int32 column
, int32 row
) const
383 if (column
< 0 || column
>= CountColumns()
384 || row
< 0 || row
>= CountRows())
387 return fGrid
[column
][row
];
392 BGridLayout::AddView(BView
* child
)
394 return BTwoDimensionalLayout::AddView(child
);
399 BGridLayout::AddView(int32 index
, BView
* child
)
401 return BTwoDimensionalLayout::AddView(index
, child
);
406 BGridLayout::AddView(BView
* child
, int32 column
, int32 row
, int32 columnCount
,
412 BLayoutItem
* item
= new BViewLayoutItem(child
);
413 if (!AddItem(item
, column
, row
, columnCount
, rowCount
)) {
423 BGridLayout::AddItem(BLayoutItem
* item
)
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);
439 BGridLayout::AddItem(int32 index
, BLayoutItem
* item
)
441 return AddItem(item
);
446 BGridLayout::AddItem(BLayoutItem
* item
, int32 column
, int32 row
,
447 int32 columnCount
, int32 rowCount
)
449 if (!_AreGridCellsEmpty(column
, row
, columnCount
, rowCount
))
452 bool success
= BTwoDimensionalLayout::AddItem(-1, item
);
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
)) {
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
));
487 result
= into
->AddFloat(kRowSizesField
, fRowInfos
->MinSize(i
));
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
));
495 result
= into
->AddFloat(kColumnSizesField
, fColumnInfos
->MinSize(i
));
497 result
= into
->AddFloat(kColumnSizesField
, fColumnInfos
->MaxSize(i
));
500 return archiver
.Finish(result
);
505 BGridLayout::AllArchived(BMessage
* into
) const
507 return BTwoDimensionalLayout::AllArchived(into
);
512 BGridLayout::AllUnarchived(const BMessage
* from
)
514 return BTwoDimensionalLayout::AllUnarchived(from
);
519 BGridLayout::Instantiate(BMessage
* from
)
521 if (validate_instantiation(from
, "BGridLayout"))
522 return new BGridLayout(from
);
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
);
534 result
= into
->AddInt32(kItemDimensionsField
, data
->dimensions
.y
);
537 result
= into
->AddInt32(kItemDimensionsField
, data
->dimensions
.width
);
540 result
= into
->AddInt32(kItemDimensionsField
, data
->dimensions
.height
);
547 BGridLayout::ItemUnarchived(const BMessage
* from
,
548 BLayoutItem
* item
, int32 index
)
550 ItemLayoutData
* data
= _LayoutDataForItem(item
);
551 Dimensions
& dimensions
= data
->dimensions
;
554 // each item stores 4 int32s into kItemDimensionsField
555 status_t result
= from
->FindInt32(kItemDimensionsField
, index
, &dimensions
.x
);
557 result
= from
->FindInt32(kItemDimensionsField
, ++index
, &dimensions
.y
);
560 result
= from
->FindInt32(kItemDimensionsField
, ++index
, &dimensions
.width
);
562 if (result
== B_OK
) {
563 result
= from
->FindInt32(kItemDimensionsField
,
564 ++index
, &dimensions
.height
);
570 if (!_AreGridCellsEmpty(dimensions
.x
, dimensions
.y
,
571 dimensions
.width
, dimensions
.height
))
574 if (!_InsertItemIntoGrid(item
))
577 if (dimensions
.width
> 1)
580 if (dimensions
.height
> 1)
588 BGridLayout::ItemAdded(BLayoutItem
* item
, int32 atIndex
)
590 item
->SetLayoutData(new(nothrow
) ItemLayoutData
);
591 return item
->LayoutData() != NULL
;
596 BGridLayout::ItemRemoved(BLayoutItem
* item
, int32 fromIndex
)
598 ItemLayoutData
* data
= _LayoutDataForItem(item
);
599 Dimensions itemDimensions
= data
->dimensions
;
600 item
->SetLayoutData(NULL
);
603 if (itemDimensions
.width
> 1)
606 if (itemDimensions
.height
> 1)
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
623 for (; columnCount
> 0; columnCount
--) {
624 for (int32 row
= 0; empty
&& row
< rowCount
; row
++)
625 empty
&= (fGrid
[columnCount
- 1][row
] == NULL
);
631 // check for empty rows
633 for (; rowCount
> 0; rowCount
--) {
634 for (int32 column
= 0; empty
&& column
< columnCount
; column
++)
635 empty
&= (fGrid
[column
][rowCount
- 1] == NULL
);
642 if (columnCount
!= fColumnCount
|| rowCount
!= fRowCount
)
643 _ResizeGrid(columnCount
, rowCount
);
649 BGridLayout::HasMultiColumnItems()
651 return fMultiColumnItems
> 0;
656 BGridLayout::HasMultiRowItems()
658 return fMultiRowItems
> 0;
663 BGridLayout::InternalCountColumns()
670 BGridLayout::InternalCountRows()
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
);
685 constraints
->min
= MinRowHeight(index
);
686 constraints
->max
= MaxRowHeight(index
);
687 constraints
->weight
= RowWeight(index
);
693 BGridLayout::GetItemDimensions(BLayoutItem
* item
, Dimensions
* dimensions
)
695 if (ItemLayoutData
* data
= _LayoutDataForItem(item
))
696 *dimensions
= data
->dimensions
;
701 BGridLayout::_IsGridCellEmpty(int32 column
, int32 row
)
703 if (column
< 0 || row
< 0)
706 if (column
>= fColumnCount
|| row
>= fRowCount
)
709 return (fGrid
[column
][row
] == NULL
);
714 BGridLayout::_AreGridCellsEmpty(int32 column
, int32 row
, int32 columnCount
,
717 if (column
< 0 || row
< 0)
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
)
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
))
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
;
756 fGrid
[column
+ x
][row
+ y
] = OCCUPIED_GRID_CELL
;
765 BGridLayout::_ResizeGrid(int32 columnCount
, int32 rowCount
)
767 if (columnCount
== fColumnCount
&& rowCount
== fRowCount
)
770 int32 rowsToKeep
= min_c(rowCount
, fRowCount
);
773 BLayoutItem
*** grid
= new(nothrow
) BLayoutItem
**[columnCount
];
777 memset(grid
, 0, sizeof(BLayoutItem
**) * columnCount
);
780 for (int32 i
= 0; i
< columnCount
; i
++) {
781 BLayoutItem
** column
= new(nothrow
) BLayoutItem
*[rowCount
];
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
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
++)
810 BGridLayout::ItemLayoutData
*
811 BGridLayout::_LayoutDataForItem(BLayoutItem
* item
) const
815 return (ItemLayoutData
*)item
->LayoutData();
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() {}