1 //
2 // This file is part of libyacurs.
3 // Copyright (C) 2013 Rafael Ostertag
4 //
5 // This program is free software: you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see
17 // <http://www.gnu.org/licenses/>.
18 //
19 //
20 // $Id$
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <algorithm>
27 #include <cassert>
28 #include <cstdlib>
29 #include <functional>
30
31 #include "vpack.h"
32 #include "yacursex.h"
33
34 using namespace YACURS;
35
36 //
37 // Functors
38 //
39
40 namespace YACURS {
41 /**
42 * Name space reserved for functors.
43 */
44 namespace FUNCTORS {
45 namespace VPACK {
46 /**
47 * Realizes widgets.
48 *
49 * @internal
50 *
51 * It checks for exceptions when realizing.
52 */
53 class VRealizeWidgets {
54 public:
operator ()(WidgetBase * w)55 void operator()(WidgetBase* w) {
56 assert(w != 0);
57 // It is possible, that upon a resize the
58 // widget became to big for the screen, or
59 // overshoots it due to its position and
60 // size. Packs do not check whether or not
61 // statically sized become too big when
62 // stacked.
63 try {
64 w->realize();
65 } catch (EXCEPTIONS::BaseCurEx&) {
66 w->focusgroup_id(FocusManager::nfgid);
67 }
68 }
69 };
70
71 /**
72 * Calculate the size hint.
73 *
74 * @internal
75 *
76 * Functor for calculating the size hint by finding the
77 * max columns size and summing up row size.
78 */
79 class VGetMaxSizeHint {
80 private:
81 Size __size_max;
82
83 public:
VGetMaxSizeHint()84 VGetMaxSizeHint() : __size_max(Size::zero()) {}
85
VGetMaxSizeHint(const VGetMaxSizeHint & _v)86 VGetMaxSizeHint(const VGetMaxSizeHint& _v) : __size_max(_v.__size_max) {}
87
operator =(const VGetMaxSizeHint & _v)88 VGetMaxSizeHint& operator=(const VGetMaxSizeHint& _v) {
89 __size_max = _v.__size_max;
90 return *this;
91 }
92
operator ()(const WidgetBase * w)93 void operator()(const WidgetBase* w) {
94 assert(w != 0);
95 __size_max.cols(std::max(w->size_hint().cols(), __size_max.cols()));
96 __size_max.rows(w->size_hint().rows() + __size_max.rows());
97 }
98
hint() const99 const Size& hint() const { return __size_max; }
100 };
101
102 /**
103 * Set size_available() on widgets.
104 *
105 * Functor for setting available size on Widgets.
106 *
107 * @internal
108 *
109 * mainly used when compiling with Solaris Studio.
110 */
111 class VSetSizeAvail {
112 private:
113 const Size& __avail;
114
115 public:
VSetSizeAvail(const Size & _avail)116 VSetSizeAvail(const Size& _avail) : __avail(_avail) {}
117
VSetSizeAvail(const VSetSizeAvail & _s)118 VSetSizeAvail(const VSetSizeAvail& _s) : __avail(_s.__avail) {}
119
operator ()(WidgetBase * w)120 void operator()(WidgetBase* w) {
121 assert(w != 0);
122 assert(__avail.rows() > 0);
123 assert(__avail.cols() > 0);
124 w->size_available(__avail);
125 }
126 };
127
128 /**
129 * Set size_available on hinted Widgets.
130 *
131 * Functor for setting size_available on hinted Widgets.
132 */
133 class VSetSizeHinted {
134 private:
135 Size __const_cols;
136 Size __size_used;
137
138 public:
VSetSizeHinted(const Size & _cc)139 VSetSizeHinted(const Size& _cc) : __const_cols(_cc), __size_used() {
140 assert(__const_cols.cols() > 0);
141 }
142
VSetSizeHinted(const VSetSizeHinted & _h)143 VSetSizeHinted(const VSetSizeHinted& _h)
144 : __const_cols(_h.__const_cols), __size_used(_h.__size_used) {}
145
operator =(const VSetSizeHinted & _h)146 VSetSizeHinted& operator=(const VSetSizeHinted& _h) {
147 __const_cols = _h.__const_cols;
148 __size_used = _h.__size_used;
149 return *this;
150 }
151
operator ()(WidgetBase * w)152 void operator()(WidgetBase* w) {
153 assert(w != 0);
154 assert(w->size_hint().rows() > 0);
155
156 // _sa is supposed to hold the constant row
157 // value and the hinted cols value as
158 // retrieved from calling size_hint() on the
159 // Widget
160 Size _sa(__const_cols);
161 _sa.rows(w->size_hint().rows());
162
163 // Finally, set the available size on the
164 // Widget
165 w->size_available(_sa);
166
167 // Add the hinted cols to __size_used, so that
168 // they can be deducted from the total size
169 // available to the pack.
170 __size_used.rows(__size_used.rows() + w->size_hint().rows());
171 }
172
size_used() const173 const Size& size_used() const { return __size_used; }
174 };
175
176 /**
177 * Calculates and sets the available size for associated
178 * widgets.
179 *
180 * This class is passed in a std::for_each(). Once
181 * for_each() is done, finish() must be called, since that
182 * will set size on all dynamically sized Widgets.
183 */
184 class VCalcNSetSize {
185 private:
186 Size _size;
187 Size __size_available;
188 std::list<WidgetBase*> __dyn_widgets;
189 std::list<WidgetBase*> __hinted_widgets;
190
191 public:
VCalcNSetSize(const Size & _av)192 VCalcNSetSize(const Size& _av)
193 : _size(), __size_available(_av), __dyn_widgets(), __hinted_widgets() {
194 assert(__size_available.rows() > 0);
195 assert(__size_available.cols() > 0);
196 }
197
VCalcNSetSize(const VCalcNSetSize & _c)198 VCalcNSetSize(const VCalcNSetSize& _c)
199 : _size(_c._size),
200 __size_available(_c.__size_available),
201 __dyn_widgets(_c.__dyn_widgets),
202 __hinted_widgets(_c.__hinted_widgets) {}
203
operator =(const VCalcNSetSize & _c)204 VCalcNSetSize& operator=(const VCalcNSetSize& _c) {
205 _size = _c._size;
206 __size_available = _c.__size_available;
207 __dyn_widgets = _c.__dyn_widgets;
208 __hinted_widgets = _c.__hinted_widgets;
209 return *this;
210 }
211
operator ()(WidgetBase * w)212 void operator()(WidgetBase* w) {
213 assert(w != 0);
214
215 // First, reset the size, so that we can
216 // identify dynamically sized Widgets
217 w->reset_size();
218
219 if (w->size() == Size::zero()) {
220 // That's a dynamically sized widget, thus
221 // add it to one of the lists for later
222 // processing.
223 //
224 // if it is capable of giving a hint, add
225 // it to the __hinted_widgets list which
226 // is treated separately from
227 // __dyn_widgets. But only if the hint
228 // does not exceed the available size.
229 if (w->size_hint().rows() > 0 &&
230 w->size_hint().rows() <= __size_available.rows())
231 __hinted_widgets.push_back(w);
232 else
233 __dyn_widgets.push_back(w);
234
235 return;
236 }
237
238 _size.rows(_size.rows() + w->size().rows());
239 _size.cols(std::max(_size.cols(), w->size().cols()));
240 if (_size.rows() > __size_available.rows() ||
241 _size.cols() > __size_available.cols())
242 throw EXCEPTIONS::AreaExceeded();
243 // Also set the size availabe for the
244 // widget. Dynamically sized widgets are
245 // handled when CalcNSetSize::finish() is
246 // called.
247 w->size_available(w->size());
248 }
249
finish()250 void finish() {
251 if (__dyn_widgets.empty() && __hinted_widgets.empty()) {
252 // There are no dynamically sized widgets,
253 // there is nothing left to do
254 return;
255 }
256 // In order to process hinted widgets, get the
257 // cols. We only consider Widgets hinting on
258 // rows
259 Size cols_unhinted(0, __size_available.cols());
260
261 VSetSizeHinted hinted_size(
262 std::for_each(__hinted_widgets.begin(), __hinted_widgets.end(),
263 VSetSizeHinted(cols_unhinted)));
264
265 // Bail out if there are no unhinted dynamic
266 // widgets
267 if (__dyn_widgets.empty()) return;
268
269 // There are dynamically sized widgets. So
270 // let's find out the how much space is
271 // available for them
272 Size remaining_size(__size_available - _size);
273
274 remaining_size -= hinted_size.size_used();
275
276 // We ignore remaining_size.cols() because we
277 // vertically stack widgets and the
278 // dynamically sized widgets get the amount of
279 // _size.cols()
280 if (remaining_size.rows() < 1) throw EXCEPTIONS::InvalidSize();
281
282 // This gives the size for each dynamically
283 // sized Widget. The remaining size will be
284 // divided equally among all dynamically sized
285 // widgets.
286 Size per_dyn_widget(remaining_size.rows() / __dyn_widgets.size(),
287 __size_available.cols());
288
289 // Set the size for each widget.
290 //
291 // The following doesn't work when compiling
292 // with Solaris Studio on Solaris, so we use a
293 // Function Object:
294 //
295 // std::for_each(__dyn_widgets.begin(),
296 // __dyn_widgets.end(),
297 // std::bind2nd(std::mem_fun<void,WidgetBase,const
298 // Size&>(&WidgetBase::size_available),per_dyn_widget));
299 std::for_each(__dyn_widgets.begin(), __dyn_widgets.end(),
300 VSetSizeAvail(per_dyn_widget));
301 }
302 };
303
304 /**
305 * Functor calculating the size only if no dynamically
306 * sized Widgets are associated. Else it will return
307 * Size::zero().
308 *
309 * Used in calc_size_non_dynamic().
310 */
311 class VCalcSizeNonDynamic {
312 private:
313 Size _size;
314 bool _had_dynamic;
315
316 public:
VCalcSizeNonDynamic()317 VCalcSizeNonDynamic() : _size(), _had_dynamic(false) {}
318
VCalcSizeNonDynamic(const VCalcSizeNonDynamic & r)319 VCalcSizeNonDynamic(const VCalcSizeNonDynamic& r)
320 : _size(r._size), _had_dynamic(r._had_dynamic) {}
321
operator =(const VCalcSizeNonDynamic & r)322 VCalcSizeNonDynamic& operator=(const VCalcSizeNonDynamic& r) {
323 _size = r._size;
324 _had_dynamic = r._had_dynamic;
325 return *this;
326 }
327
operator ()(const WidgetBase * w)328 void operator()(const WidgetBase* w) {
329 assert(w != 0);
330
331 // Do nothing if we already found a dynamic
332 // widget
333 if (_had_dynamic) return;
334
335 if (w->size() == Size::zero()) {
336 // found a dynamic widget. Reset size and
337 // mark
338 _had_dynamic = true;
339 _size = Size::zero();
340 return;
341 }
342
343 _size.rows(_size.rows() + w->size().rows());
344 _size.cols(std::max(_size.cols(), w->size().cols()));
345 }
346
size() const347 const Size& size() const { return _size; }
348 };
349
350 /**
351 * Sets the position of a given Widget.
352 *
353 * Sets the position of a given Widget and updates an
354 * internal Coordinate object in manner, that subsequent
355 * positions will not overwrite previous Widgets on the
356 * screen.
357 *
358 * The position will be advanced from top to bottom
359 * (i.e. vertically).
360 */
361 class VSetPosWidget {
362 private:
363 /**
364 * Position of wich is used in the call to
365 * WidgetBase::position().
366 */
367 Coordinates _pos;
368
369 public:
370 /**
371 * @param _start the first call of the object
372 * will put the Widget at this position.
373 */
VSetPosWidget(const Coordinates & _start)374 VSetPosWidget(const Coordinates& _start) : _pos(_start) {}
375
VSetPosWidget(const VSetPosWidget & o)376 VSetPosWidget(const VSetPosWidget& o) : _pos(o._pos) {}
377
378 /**
379 * Call WidgetBase::postion().
380 *
381 * Call WidgetBase::position() and update _pos;
382 *
383 * @param w pointer to Widget.
384 */
operator ()(WidgetBase * w)385 void operator()(WidgetBase* w) {
386 w->position(_pos);
387 _pos.y(_pos.y() + w->size().rows());
388 }
389 };
390 } // namespace VPACK
391 } // namespace FUNCTORS
392 } // namespace YACURS
393
394 //
395 // Private
396 //
397
398 //
399 // Protected
400 //
401
recalc_size()402 void VPack::recalc_size() {
403 assert(WidgetBase::size_available() != Size::zero());
404
405 FUNCTORS::VPACK::VCalcNSetSize calc(WidgetBase::size_available());
406 calc = std::for_each(widget_list.begin(), widget_list.end(), calc);
407
408 // This is imperative, in order to set the size_available on any
409 // dynamically sized Widgets.
410 calc.finish();
411 }
412
calc_size_non_dynamic() const413 Size VPack::calc_size_non_dynamic() const {
414 FUNCTORS::VPACK::VCalcSizeNonDynamic _s;
415
416 _s = std::for_each(widget_list.begin(), widget_list.end(), _s);
417 return _s.size();
418 }
419
420 //
421 // Public
422 //
VPack()423 VPack::VPack() : Pack{} {}
424
~VPack()425 VPack::~VPack() {}
426
size_hint() const427 Size VPack::size_hint() const {
428 if (!hinting()) return Size::zero();
429
430 // remember that Packs may return either component >0 when
431 // hinting, see also comment on VGetMaxSizeHint
432 FUNCTORS::VPACK::VGetMaxSizeHint shint;
433
434 shint = std::for_each(widget_list.begin(), widget_list.end(), shint);
435 return shint.hint();
436 }
437
realize()438 void VPack::realize() {
439 REALIZE_ENTER;
440
441 assert(WidgetBase::size_available().rows() > 0);
442 assert(WidgetBase::size_available().cols() > 0);
443
444 try {
445 recalc_size();
446 } catch (EXCEPTIONS::AreaExceeded&) {
447 // Back off
448 std::for_each(widget_list.begin(), widget_list.end(),
449 std::mem_fun(&WidgetBase::unrealize));
450
451 realization(UNREALIZED);
452 return;
453 }
454
455 // Set position for each associated widget. That's the only reason
456 // we implement realize() in a derived class, i.e. here
457 std::for_each(widget_list.begin(), widget_list.end(),
458 FUNCTORS::VPACK::VSetPosWidget(position()));
459
460 // We have to catch exceptions, but continue. The
461 //
462 // std::for_each(widget_list.begin(),
463 // widget_list.end(),
464 // std::mem_fun(&WidgetBase::realize));
465 //
466 // can't be used for that.
467 std::for_each(widget_list.begin(), widget_list.end(),
468 FUNCTORS::VPACK::VRealizeWidgets());
469
470 REALIZE_LEAVE;
471 }
472