1/*
2 * Copyright 2011, Haiku, Inc. All rights reserved.
3 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "RowColumnManager.h"
9
10
11#include <LayoutItem.h>
12
13
14using namespace LinearProgramming;
15
16
17namespace BALM {
18
19
20RowColumnManager::RowColumnManager(LinearSpec* spec)
21	:
22	fLinearSpec(spec)
23{
24
25}
26
27
28RowColumnManager::~RowColumnManager()
29{
30	for (int32 i = 0; i < fRows.CountItems(); i++)
31		delete fRows.ItemAt(i)->fPrefSizeConstraint;
32
33	for (int32 i = 0; i < fColumns.CountItems(); i++)
34		delete fColumns.ItemAt(i)->fPrefSizeConstraint;
35}
36
37
38void
39RowColumnManager::AddArea(Area* area)
40{
41	Row* row = _FindRowFor(area);
42	if (row == NULL) {
43		row = new Row(fLinearSpec, area->Top(), area->Bottom());
44		fRows.AddItem(row);
45	}
46	area->fRow = row;
47	row->fAreas.AddItem(area);
48
49	Column* column = _FindColumnFor(area);
50	if (column == NULL) {
51		column = new Column(fLinearSpec, area->Left(), area->Right());
52		fColumns.AddItem(column);
53	}
54	area->fColumn = column;
55	column->fAreas.AddItem(area);
56
57	_UpdateConstraints(row);
58	_UpdateConstraints(column);
59}
60
61
62void
63RowColumnManager::RemoveArea(Area* area)
64{
65	Row* row = area->fRow;
66	if (row) {
67		row->fAreas.RemoveItem(area);
68		area->fRow = NULL;
69		if (row->fAreas.CountItems() == 0) {
70			fRows.RemoveItem(row);
71			delete row;
72		} else
73			_UpdateConstraints(row);
74	}
75
76	Column* column = area->fColumn;
77	if (column) {
78		column->fAreas.RemoveItem(area);
79		area->fColumn = NULL;
80		if (column->fAreas.CountItems() == 0) {
81			fColumns.RemoveItem(column);
82			delete column;
83		} else
84			_UpdateConstraints(column);
85	}
86}
87
88
89void
90RowColumnManager::UpdateConstraints()
91{
92	for (int32 i = 0; i < fRows.CountItems(); i++)
93		_UpdateConstraints(fRows.ItemAt(i));
94	for (int32 i = 0; i < fColumns.CountItems(); i++)
95		_UpdateConstraints(fColumns.ItemAt(i));
96}
97
98
99void
100RowColumnManager::TabsChanged(Area* area)
101{
102	RemoveArea(area);
103	AddArea(area);
104}
105
106
107Row*
108RowColumnManager::_FindRowFor(Area* area)
109{
110	for (int32 i = 0; i < fRows.CountItems(); i++) {
111		Row* row = fRows.ItemAt(i);
112		if (row->fTop.Get() == area->Top()
113			&& row->fBottom.Get() == area->Bottom())
114			return row;
115	}
116	return NULL;
117}
118
119
120Column*
121RowColumnManager::_FindColumnFor(Area* area)
122{
123	for (int32 i = 0; i < fColumns.CountItems(); i++) {
124		Column* column = fColumns.ItemAt(i);
125		if (column->fLeft.Get() == area->Left()
126			&& column->fRight.Get() == area->Right())
127			return column;
128	}
129	return NULL;
130}
131
132
133double
134RowColumnManager::_PreferredHeight(Row* row, double& weight)
135{
136	weight = 0;
137	int nAreas = 0;
138	double pref = 0;
139	for (int32 i = 0; i < row->fAreas.CountItems(); i++) {
140		BSize prefSize = row->fAreas.ItemAt(i)->Item()->PreferredSize();
141		if (prefSize.height <= 0)
142			continue;
143		nAreas++;
144		pref += prefSize.height;
145		double negPen = row->fAreas.ItemAt(i)->ShrinkPenalties().height;
146		if (negPen > 0)
147			weight += negPen;
148	}
149	if (nAreas == 0) {
150		pref = -1;
151		weight = 1;
152	} else {
153		pref /= nAreas;
154		weight /= nAreas;
155	}
156	return pref;
157}
158
159
160double
161RowColumnManager::_PreferredWidth(Column* column, double& weight)
162{
163	weight = 0;
164	int nAreas = 0;
165	double pref = 0;
166	for (int32 i = 0; i < column->fAreas.CountItems(); i++) {
167		BSize prefSize = column->fAreas.ItemAt(i)->Item()->PreferredSize();
168		if (prefSize.width <= 0)
169			continue;
170		nAreas++;
171		pref += prefSize.width;
172
173		double negPen = column->fAreas.ItemAt(i)->ShrinkPenalties().height;
174		if (negPen > 0)
175			weight += negPen;
176	}
177	if (nAreas == 0) {
178		pref = -1;
179		weight = 1;
180	} else {
181		pref /= nAreas;
182		weight /= nAreas;
183	}
184	return pref;
185}
186
187
188void
189RowColumnManager::_UpdateConstraints(Row* row)
190{
191	double weight;
192	double prefSize = _PreferredHeight(row, weight);
193	if (prefSize >= 0) {
194		if (row->fPrefSizeConstraint == NULL) {
195			row->fPrefSizeConstraint = fLinearSpec->AddConstraint(1,
196				row->fBottom, -1, row->fTop, kEQ, prefSize, weight, weight);
197			row->fPrefSizeConstraint->SetLabel("Pref Height");
198		} else {
199			row->fPrefSizeConstraint->SetRightSide(prefSize);
200			row->fPrefSizeConstraint->SetPenaltyNeg(weight);
201			row->fPrefSizeConstraint->SetPenaltyPos(weight);
202		}
203	} else {
204		delete row->fPrefSizeConstraint;
205		row->fPrefSizeConstraint = NULL;
206	}
207}
208
209
210void
211RowColumnManager::_UpdateConstraints(Column* column)
212{
213	double weight;
214	double prefSize = _PreferredWidth(column, weight);
215	if (prefSize >= 0) {
216		if (column->fPrefSizeConstraint == NULL) {
217			column->fPrefSizeConstraint = fLinearSpec->AddConstraint(1,
218				column->fRight, -1, column->fLeft, kEQ, prefSize, weight,
219				weight);
220			column->fPrefSizeConstraint->SetLabel("Pref Width");
221		} else {
222			column->fPrefSizeConstraint->SetRightSide(prefSize);
223			column->fPrefSizeConstraint->SetPenaltyNeg(weight);
224			column->fPrefSizeConstraint->SetPenaltyPos(weight);
225		}
226	} else {
227		delete column->fPrefSizeConstraint;
228		column->fPrefSizeConstraint = NULL;
229	}
230}
231
232
233} // end BALM namespace
234
235