1 /*
2 * Copyright(c) 2016, OpenAV
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of the <organization> nor the
15 * names of its contributors may be used to endorse or promote
16 * products derived from this software without specific prior
17 * written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OPENAV BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
26 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "group.hxx"
33
34 #include "ui.hxx"
35 #include "avtk.hxx"
36
37 namespace Avtk
38 {
39
40
Group(Avtk::UI * ui,int w,int h)41 Group::Group( Avtk::UI* ui, int w, int h ) :
42 Widget( ui, w, h ),
43 groupMode( NONE ),
44 valueMode_( VALUE_NORMAL ),
45 resizeMode_( RESIZE_NONE ),
46 spacing_( 0 )
47 {
48 noHandle_ = true;
49 }
50
Group(Avtk::UI * ui,int x,int y,int w,int h,std::string label)51 Group::Group( Avtk::UI* ui, int x, int y, int w, int h, std::string label ) :
52 Widget( ui, x, y, w, h, label ),
53 groupMode( NONE ),
54 valueMode_( VALUE_NORMAL ),
55 resizeMode_( RESIZE_NONE ),
56 spacing_( 1 )
57 {
58 noHandle_ = false;
59 ui->pushParent( this );
60 }
61
end()62 void Group::end()
63 {
64 ui->popParent();
65 //printf("Group::end(), num children = %i\n", children.size() );
66 }
67
add(Widget * child)68 void Group::add( Widget* child )
69 {
70 // if widget is currently already parented (not a new widget), remove it from
71 // its previous parent group.
72 if( child->parent() ) {
73 //printf("Group add: child parent was true, removing %s from %s\n", child->label(), child->parent()->label() );
74 child->parent()->remove( child );
75 }
76
77
78 // get the current size of the child widgets
79 int childY = y_;
80 for(int i = 0; i < children.size(); i++ )
81 childY += children.at(i)->h() + spacing_;
82
83 int childX = x_;
84 for(int i = 0; i < children.size(); i++ )
85 childX += children.at(i)->w() + spacing_;
86
87 #ifdef AVTK_DEBUG
88 printf("Group %s add: size %i\n", label(), children.size() );
89 #endif
90 child->addToGroup( this, children.size() );
91
92 // capture callback of the child widget
93 child->callback = staticGroupCB;
94 child->callbackUD = this;
95
96 // get the current state
97
98 // set the child's co-ords
99 if( groupMode == WIDTH_EQUAL ) {
100 child->x( x_ );
101 child->w( w_ );
102
103 child->y( childY );
104
105 if( resizeMode_ == RESIZE_FIT_TO_CHILDREN ) {
106 h( childY + child->h() - y_ );
107 //printf("group height %i : child y = %i\n", h_, child->y() );
108 }
109 } else if( groupMode == HEIGHT_EQUAL ) {
110 child->y( y_ );
111 child->h( h_ );
112
113 child->x( childX );
114
115 if( resizeMode_ == RESIZE_FIT_TO_CHILDREN ) {
116 w( childX + child->w() - x_ );
117 //printf("group width set to %i, childX %i\n", w_ );
118 }
119 }
120
121 children.push_back( child );
122
123 #ifdef AVTK_DEBUG
124 printf("Group after add: size %i\n", children.size() );
125 #endif
126
127 // notify parent that the size of this widget has changed
128 if( resizeMode_ == RESIZE_FIT_TO_CHILDREN && parent() )
129 parent()->childResize( this );
130 }
131
childResize(Widget * w)132 void Group::childResize( Widget* w )
133 {
134
135 }
136
remove(Avtk::Widget * wid)137 void Group::remove( Avtk::Widget* wid )
138 {
139 for(int i = 0; i < children.size(); i++ ) {
140 if( children.at(i) == wid ) {
141 #ifdef AVTK_DEBUG
142 //printf("Group::remove() %s, widget# %i\n", label(), i );
143 #endif
144 children.erase( children.begin() + i );
145 }
146 }
147 }
148
resizeNotify(Widget * w)149 void Group::resizeNotify( Widget* w )
150 {
151 #ifdef AVTK_DEBUG
152 printf("Group::resizeNotify() %s, widget %s\n", label(), w->label() );
153 #endif
154 }
155
visible(bool vis)156 void Group::visible( bool vis )
157 {
158 Widget::visible( vis );
159 for(int i = 0; i < children.size(); i++ ) {
160 children.at(i)->visible( vis );
161 }
162 }
163
visible()164 bool Group::visible()
165 {
166 return Widget::visible();
167 }
168
clear()169 void Group::clear()
170 {
171 //int i = 0;
172 //for(int i = 0; i < children.size(); ++i)
173
174 while( children.size() ) {
175 Avtk::Widget* tmp = children.at(0);
176 #ifdef AVTK_DEBUG
177 //printf("removing child %s from %s : size() %i\n", tmp->label(), label(), children.size() );
178 #endif
179 tmp->parent()->remove( tmp );
180 delete tmp;
181 }
182
183 //printf("done removing, %i\n", children.size() );
184
185 // resets size of vector to 0
186 children.clear();
187 }
188
mode(GROUP_MODE gm)189 void Group::mode( GROUP_MODE gm )
190 {
191 groupMode = gm;
192 }
193
valueCB(Widget * w)194 void Group::valueCB( Widget* w )
195 {
196 // only one widget is value( true ) in a group at a time
197 if( valueMode_ == VALUE_SINGLE_CHILD ) {
198 #ifdef AVTK_DEBUG
199 printf("Group child # %i : value : %f\tNow into Normal CB\n", w->groupItemNumber(), w->value() );
200 #endif
201 for(int i = 0; i < children.size(); i++ ) {
202 children.at(i)->value( false );
203 }
204
205 w->value( true );
206 }
207
208 // continue to widget's callback
209 if( true ) {
210 Avtk::UI::staticWidgetValueCB( w, ui );
211 }
212 }
213
x(int x__)214 void Group::x(int x__)
215 {
216 int d = x__ - x_;
217 x_ = x__;
218 for(int i = 0; i < children.size(); i++ ) {
219 Widget* c = children.at(i);
220 c->x( c->x() + d );
221 }
222 }
223
y(int y__)224 void Group::y(int y__)
225 {
226 int d = y__ - y_;
227 y_ = y__;
228 for(int i = 0; i < children.size(); i++ ) {
229 Widget* c = children.at(i);
230 c->y( c->y() + d );
231 }
232 }
233
w(int w__)234 void Group::w(int w__)
235 {
236 int d = w__ - w_;
237 w_ = w__;
238 if( groupMode == WIDTH_EQUAL ) {
239 for(int i = 0; i < children.size(); i++ ) {
240 Widget* c = children.at(i);
241 c->w( c->w() + d );
242 }
243 }
244 }
245
h(int h__)246 void Group::h(int h__)
247 {
248 int d = h__ - h_;
249 h_ = h__;
250 if( groupMode == HEIGHT_EQUAL ) {
251 for(int i = 0; i < children.size(); i++ ) {
252 Widget* c = children.at(i);
253 c->h( c->h() + d );
254 }
255 }
256 }
257
draw(cairo_t * cr)258 void Group::draw( cairo_t* cr )
259 {
260 if( visible() ) {
261 for(int i = 0; i < children.size(); i++ ) {
262 Widget* c = children.at( i );
263 if( c->visible() )
264 c->draw( cr );
265 }
266
267 if ( false ) { // draws group boundary
268 roundedBox(cr, x_, y_, w_, h_, theme_->cornerRadius_ );
269 theme_->color( cr, FG );
270 cairo_set_line_width(cr, 0.5);
271 cairo_stroke(cr);
272 }
273
274 //cairo_surface_write_to_png( cairo_get_target( cr ), "cr.png" );
275 }
276 }
277
handle(const PuglEvent * event)278 int Group::handle( const PuglEvent* event )
279 {
280 if( visible() ) {
281 // reverse iter over the children: top first
282 for(int i = children.size() - 1; i >= 0 ; i-- ) {
283 int ret = children.at( i )->handle( event );
284 if( ret ) {
285 //AVTK_DEV("widget %s : handles eventType %i ret = 1\n", children.at(i)->label(), event->type );
286 return ret; // child widget ate event: done :)
287 }
288 }
289
290 // if we haven't returned, the event was not consumed by the children, so we
291 // can check for a scroll event, and if yes, highlight the next item
292 if( event->type == PUGL_SCROLL &&
293 valueMode_ == VALUE_SINGLE_CHILD &&
294 touches( event->scroll.x, event->scroll.y ) &&
295 children.size() > 0 ) {
296 // find value() widget
297 int vw = -1;
298 for(int i = children.size() - 1; i >= 0 ; i-- ) {
299 if( children.at(i)->value() > 0.4999 )
300 vw = i;
301 }
302
303 int d = event->scroll.dy;
304 //printf("SCROLL: Value child %i, delta %i\n", vw, d );
305
306 // no widget selected
307 if( vw == -1 ) {
308 children.at(0)->value( true );
309 }
310 // scroll up
311 else if( vw > 0 && d > 0 ) {
312 children.at(vw-1)->value( true );
313 children.at(vw )->value( false );
314 }
315 // scroll down
316 else if( vw < children.size()-1 && d < 0 ) {
317 children.at(vw )->value( false );
318 children.at(vw+1)->value( true );
319 }
320
321 // handled scroll, so eat event
322 return 1;
323 }
324 }
325 return 0;
326 }
327
~Group()328 Group::~Group()
329 {
330 #ifdef AVTK_DEBUG_DTOR
331 printf("%s %s\n", __PRETTY_FUNCTION__, label() );
332 #endif
333 // on deletion, clean up all widgets left as children
334 clear();
335 }
336
337 };
338