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