tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / interface / layouter / SimpleLayouter.cpp
blob70a9f21236c3155bee1ed718b17486776dd9b09e
1 /*
2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include "SimpleLayouter.h"
8 #include <math.h>
10 #include <LayoutUtils.h>
11 #include <List.h>
12 #include <Size.h>
15 // no lround() under BeOS R5 x86
16 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
17 # define lround(x) (long)floor((x) + 0.5)
18 #endif
21 // ElementLayoutInfo
22 class SimpleLayouter::ElementLayoutInfo {
23 public:
24 int32 size;
25 int32 location;
27 ElementLayoutInfo()
28 : size(0),
29 location(0)
34 // ElementInfo
35 class SimpleLayouter::ElementInfo {
36 public:
37 int32 index;
38 int32 min;
39 int32 max;
40 int32 preferred;
41 float weight;
42 int64 tempWeight;
44 ElementInfo()
45 : index(0),
46 min(0),
47 max(B_SIZE_UNLIMITED),
48 preferred(0),
49 weight(1),
50 tempWeight(0)
54 ElementInfo(int index)
55 : index(index),
56 min(0),
57 max(B_SIZE_UNLIMITED),
58 preferred(0),
59 weight(1),
60 tempWeight(0)
64 void Assign(const ElementInfo& info)
66 min = info.min;
67 max = info.max;
68 preferred = info.preferred;
69 weight = info.weight;
70 tempWeight = info.tempWeight;
74 // MyLayoutInfo
75 class SimpleLayouter::MyLayoutInfo : public LayoutInfo {
76 public:
77 int32 fSize;
78 ElementLayoutInfo* fElements;
79 int32 fElementCount;
81 MyLayoutInfo(int32 elementCount)
82 : fSize(0),
83 fElementCount(elementCount)
85 fElements = new ElementLayoutInfo[elementCount];
88 virtual ~MyLayoutInfo()
90 delete[] fElements;
93 virtual float ElementLocation(int32 element)
95 if (element < 0 || element >= fElementCount) {
96 // error
97 return 0;
100 return fElements[element].location;
103 virtual float ElementSize(int32 element)
105 if (element < 0 || element >= fElementCount) {
106 // error
107 return -1;
110 return fElements[element].size - 1;
115 // constructor
116 SimpleLayouter::SimpleLayouter(int32 elementCount, float spacing)
117 : fElementCount(elementCount),
118 fSpacing((int32)spacing),
119 fMin(0),
120 fMax(B_SIZE_UNLIMITED),
121 fPreferred(0),
122 fMinMaxValid(false),
123 fLayoutInfo(NULL)
125 fElements = new ElementInfo[elementCount];
126 for (int i = 0; i < elementCount; i++)
127 fElements[i].index = i;
130 // destructor
131 SimpleLayouter::~SimpleLayouter()
133 delete[] fElements;
136 // AddConstraints
137 void
138 SimpleLayouter::AddConstraints(int32 element, int32 length,
139 float _min, float _max, float _preferred)
141 if (element < 0 || element >= fElementCount) {
142 // error
143 return;
145 if (length != 1) {
146 // error
147 return;
150 int32 min = (int32)_min + 1;
151 int32 max = (int32)_max + 1;
152 // int32 preferred = (int32)_preferred + 1;
154 ElementInfo& info = fElements[element];
155 info.min = max_c(info.min, min);
156 info.max = min_c(info.max, max);
157 info.preferred = max_c(info.min, min);
159 fMinMaxValid = false;
162 // SetWeight
163 void
164 SimpleLayouter::SetWeight(int32 element, float weight)
166 if (element < 0 || element >= fElementCount) {
167 // error
168 return;
171 fElements[element].weight = weight;
174 // MinSize
175 float
176 SimpleLayouter::MinSize()
178 _ValidateMinMax();
179 return fMin - 1;
182 // MaxSize
183 float
184 SimpleLayouter::MaxSize()
186 _ValidateMinMax();
187 return fMax - 1;
190 // PreferredSize
191 float
192 SimpleLayouter::PreferredSize()
194 _ValidateMinMax();
195 return fPreferred - 1;
198 // CreateLayoutInfo
199 LayoutInfo*
200 SimpleLayouter::CreateLayoutInfo()
202 return new MyLayoutInfo(fElementCount);
205 // Layout
206 void
207 SimpleLayouter::Layout(LayoutInfo* layoutInfo, float _size)
209 int32 size = int32(_size + 1);
211 fLayoutInfo = (MyLayoutInfo*)layoutInfo;
213 _ValidateMinMax();
215 if (fElementCount == 0)
216 return;
218 fLayoutInfo->fSize = max_c(size, fMin);
220 // layout the elements
221 if (fLayoutInfo->fSize >= fMax)
222 _LayoutMax();
223 else
224 _LayoutStandard();
226 // set locations
227 int location = 0;
228 for (int i = 0; i < fElementCount; i++) {
229 fLayoutInfo->fElements[i].location = location;
230 location += fSpacing + fLayoutInfo->fElements[i].size;
234 // CloneLayouter
235 Layouter*
236 SimpleLayouter::CloneLayouter()
238 SimpleLayouter* layouter = new SimpleLayouter(fElementCount, fSpacing);
240 for (int i = 0; i < fElementCount; i++)
241 layouter->fElements[i].Assign(fElements[i]);
243 layouter->fMin = fMin;
244 layouter->fMax = fMax;
245 layouter->fPreferred = fPreferred;
247 return layouter;
250 // DistributeSize
251 void
252 SimpleLayouter::DistributeSize(int32 size, float weights[], int32 sizes[],
253 int32 count)
255 // create element infos
256 BList elementInfos(count);
257 for (int32 i = 0; i < count; i++) {
258 ElementInfo* info = new ElementInfo(i);
259 info->weight = weights[i];
260 elementInfos.AddItem(info);
263 // compute integer weights
264 int64 sumWeight = _CalculateSumWeight(elementInfos);
266 // distribute the size
267 int64 weight = 0;
268 int32 sumSize = 0;
269 for (int32 i = 0; i < count; i++) {
270 ElementInfo* info = (ElementInfo*)elementInfos.ItemAt(i);
271 weight += info->tempWeight;
272 int32 oldSumSize = sumSize;
273 sumSize = (int32)(size * weight / sumWeight);
274 sizes[i] = sumSize - oldSumSize;
276 delete info;
280 // _CalculateSumWeight
281 long
282 SimpleLayouter::_CalculateSumWeight(BList& elementInfos)
284 if (elementInfos.IsEmpty())
285 return 0;
286 int32 count = elementInfos.CountItems();
288 // sum up the floating point weight, so we get a scale
289 double scale = 0;
290 for (int32 i = 0; i < count; i++) {
291 ElementInfo* info = (ElementInfo*)elementInfos.ItemAt(i);
292 scale += info->weight;
295 int64 weight = 0;
297 if (scale == 0) {
298 // The weight sum is 0: We assign each info a temporary weight of 1.
299 for (int32 i = 0; i < count; i++) {
300 ElementInfo* info = (ElementInfo*)elementInfos.ItemAt(i);
301 info->tempWeight = 1;
302 weight += info->tempWeight;
304 } else {
305 // We scale the weights so that their sum is about 100000. This should
306 // give us ample resolution. If possible make the scale integer, so that
307 // integer weights will produce exact results.
308 if (scale >= 1 && scale <= 100000)
309 scale = lround(100000 / scale);
310 else
311 scale = 100000 / scale;
313 for (int32 i = 0; i < count; i++) {
314 ElementInfo* info = (ElementInfo*)elementInfos.ItemAt(i);
315 info->tempWeight = (int64)(info->weight * scale);
316 weight += info->tempWeight;
320 return weight;
323 // _ValidateMinMax
324 void
325 SimpleLayouter::_ValidateMinMax()
327 if (fMinMaxValid)
328 return;
330 fMinMaxValid = true;
332 if (fElementCount == 0) {
333 fMin = 0;
334 fMax = B_SIZE_UNLIMITED;
335 fPreferred = 0;
336 return;
339 int spacing = (fElementCount - 1) * fSpacing;
340 fMin = spacing;
341 fMax = spacing;
342 fPreferred = spacing;
344 for (int i = 0; i < fElementCount; i++) {
345 ElementInfo& info = fElements[i];
347 // correct the preferred and maximum sizes
348 if (info.max < info.min)
349 info.max = info.min;
350 if (info.preferred < info.min)
351 info.preferred = info.min;
352 else if (info.preferred > info.max)
353 info.preferred = info.max;
355 // sum up
356 fMin += info.min;
357 fMax = BLayoutUtils::AddSizesInt32(fMax, info.max);
358 fPreferred = BLayoutUtils::AddSizesInt32(fPreferred, info.preferred);
362 // _LayoutMax
363 void
364 SimpleLayouter::_LayoutMax()
366 ElementInfo* infos = fElements;
367 int32 count = fElementCount;
368 if (count == 0)
369 return;
371 int32 additionalSpace = fLayoutInfo->fSize - fMax;
373 // layout to the maximum first
374 for (int i = 0; i < count; i++)
375 fLayoutInfo->fElements[infos[i].index].size = infos[i].max;
377 // Mmh, distributing according to the weights doesn't look that good.
378 // // Now distribute the additional space according to the weights.
379 // int64 sumWeight = calculateSumWeight(Arrays.asList(infos));
380 // int64 weight = 0;
381 // int64 sumSize = 0;
382 // for (int i = 0; i < infos.length; i++) {
383 // weight += infos[i].tempWeight;
384 // int64 oldSumSize = sumSize;
385 // sumSize = (int)(additionalSpace * weight / sumWeight);
386 // fLayoutInfo.fElements[infos[i].index].size += sumSize - oldSumSize;
387 // }
389 // distribute the additional space equally
390 int64 sumSize = 0;
391 for (int i = 0; i < count; i++) {
392 int64 oldSumSize = sumSize;
393 sumSize = additionalSpace * (i + 1) / count;
394 fLayoutInfo->fElements[infos[i].index].size
395 += int32(sumSize - oldSumSize);
399 // _LayoutStandard
400 void
401 SimpleLayouter::_LayoutStandard()
403 int32 space = fLayoutInfo->fSize - (fElementCount - 1) * fSpacing;
405 BList infosToLayout(fElementCount);
406 for (int i = 0; i < fElementCount; i++) {
407 infosToLayout.AddItem(&fElements[i]);
408 fLayoutInfo->fElements[i].size = 0;
411 BList infosUnderMax(fElementCount);
412 BList infosOverMin(fElementCount);
413 while (infosToLayout.CountItems() > 0) {
414 int32 remainingSpace = 0;
415 int32 infoCount = infosToLayout.CountItems();
416 int64 sumWeight = _CalculateSumWeight(infosToLayout);
417 int64 assignedWeight = 0;
418 int32 assignedSize = 0;
420 for (int32 i = 0; i < infoCount; i++) {
421 ElementInfo* info = (ElementInfo*)infosToLayout.ItemAt(i);
422 ElementLayoutInfo& layoutInfo = fLayoutInfo->fElements[info->index];
423 // The simple algorithm is this:
424 // info.size += (int)(space * info.tempWeight / sumWeight);
425 // I.e. we simply assign space according to the weight. To avoid the
426 // rounding problematic, we make it a bit more complicated. We
427 // assign the difference of total assignment for all infos including
428 // the current one minus the total excluding the current one.
429 assignedWeight += info->tempWeight;
430 int32 oldAssignedSize = assignedSize;
431 assignedSize = (int32)(space * assignedWeight / sumWeight);
432 layoutInfo.size += assignedSize - oldAssignedSize;
434 if (layoutInfo.size < info->min) {
435 remainingSpace += layoutInfo.size - info->min;
436 layoutInfo.size = info->min;
437 } else if (layoutInfo.size > info->max) {
438 remainingSpace += layoutInfo.size - info->max;
439 layoutInfo.size = info->max;
442 if (layoutInfo.size > info->min)
443 infosOverMin.AddItem(info);
444 if (layoutInfo.size < info->max)
445 infosUnderMax.AddItem(info);
448 infosToLayout.MakeEmpty();
449 if (remainingSpace > 0)
450 infosToLayout.AddList(&infosUnderMax);
451 else if (remainingSpace < 0)
452 infosToLayout.AddList(&infosOverMin);
453 infosUnderMax.MakeEmpty();
454 infosOverMin.MakeEmpty();
455 space = remainingSpace;