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