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 "ui.hxx"
33 
34 #include "avtk.hxx"
35 #include "theme.hxx"
36 #include "themes.hxx"
37 
38 #ifdef AVTK_TESTER
39 #include "tester.hxx"
40 #endif
41 
42 using namespace Avtk;
43 
UI(int w__,int h__,PuglNativeWindow parent,const char * windowName)44 UI::UI( int w__, int h__, PuglNativeWindow parent, const char* windowName ) :
45 	Group( this, w__, h__ ),
46 	quit_( false ),
47 	w_( w__ ),
48 	h_( h__ )
49 #ifdef AVTK_TESTER
50 	, tester( new Tester( this ) )
51 #endif
52 {
53 	view = puglInit(NULL, NULL);
54 
55 	parentStack.push( this );
56 
57 	if( parent != 0 )
58 		puglInitWindowParent( view, parent );
59 
60 	puglInitWindowSize  (view, w_, h_ );
61 	puglInitResizable   (view, true );
62 	puglInitContextType (view, PUGL_CAIRO);
63 	puglIgnoreKeyRepeat (view, true );
64 
65 	puglSetEventFunc    (view, UI::onEvent  );
66 	puglSetDisplayFunc  (view, UI::onDisplay);
67 	puglSetCloseFunc    (view, UI::onClose  );
68 	puglSetMotionFunc   (view, UI::onMotion );
69 	puglSetReshapeFunc  (view, UI::onReshape);
70 
71 	puglCreateWindow    (view, windowName );
72 	puglShowWindow      (view);
73 
74 	puglSetHandle       (view, this);
75 
76 	motionUpdateWidget = 0;
77 	handleOnlyWidget = 0;
78 
79 	dragDropOrigin   = 0;
80 	dragDropDataSize = 0;
81 	dragDropDataPtr  = 0;
82 
83 	dragDropTargetVerified       = false;
84 	dragDropTargetVerifiedWidget = 0;
85 
86 	themes.push_back( new Theme( this, AVTK_BLUE ) );
87 	theme_ = themes.back();
88 }
89 
reshape(int x,int y)90 void UI::reshape(int x, int y)
91 {
92 #ifdef AVTK_DEBUG
93 	AVTK_DEV("reshaping UI: scale factor: %f \t%f\n", x/float(initW), y/float(initH) );
94 #endif
95 
96 	//Group::resize( );
97 
98 	//Group::w( x );
99 	//Group::h( y );
100 }
101 
~UI()102 UI::~UI()
103 {
104 #ifdef AVTK_DEBUG_DTOR
105 	AVTK_DEV("%s %s\n", __PRETTY_FUNCTION__, label() );
106 #endif
107 
108 	while( themes.size() > 0 ) {
109 		Theme* tmp = themes.at(0);
110 		themes.erase( themes.begin() );
111 		delete tmp;
112 	}
113 
114 	puglDestroy( view );
115 }
116 
display(cairo_t * cr)117 void UI::display( cairo_t* cr )
118 {
119 	/// clear the screen
120 	cairo_rectangle( cr, 0, 0, w_, h_ );
121 	themes[0]->color( cr, BG_DARK );
122 	cairo_fill( cr );
123 
124 	/// use the group abstraction to draw the widgets, as the UI class derives
125 	/// from a Group, this process is simple.
126 	Group::draw( cr );
127 }
128 
theme(int id)129 Theme* UI::theme( int id )
130 {
131 	if( id < themes.size() )
132 		return themes.at( id );
133 
134 	// default theme
135 	return themes.at( 0 );
136 }
137 
idle()138 int UI::idle()
139 {
140 	puglProcessEvents(view);
141 	return quit_;
142 }
143 
run()144 int UI::run()
145 {
146 	redraw();
147 
148 	while ( !quit_ ) {
149 		puglProcessEvents(view);
150 		usleep( 25000 );
151 
152 #ifdef AVTK_TESTER
153 		tester->process();
154 #endif
155 
156 	}
157 
158 	return 0;
159 }
160 
pushParent(Avtk::Group * g)161 void UI::pushParent( Avtk::Group* g )
162 {
163 	AVTK_DEV("%s - pushing %s to stack\n", __PRETTY_FUNCTION__, g->label() );
164 	parentStack.push( g );
165 }
166 
popParent()167 void UI::popParent()
168 {
169 	AVTK_DEV("%s - popping %s from stack\n", __PRETTY_FUNCTION__, parentStack.top()->label() );
170 	parentStack.pop();
171 }
172 
remove(Avtk::Widget * w)173 void UI::remove( Avtk::Widget* w )
174 {
175 	if( w == handleOnlyWidget ) {
176 		handleOnlyWidget = 0x0;
177 	} else if ( w == motionUpdateWidget ) {
178 		motionUpdateWidget = 0x0;
179 	} else if ( w == dragDropOrigin ) {
180 		dragDropOrigin = 0x0;
181 	} else if ( w == dragDropTargetVerifiedWidget ) {
182 		dragDropTargetVerifiedWidget = 0x0;
183 	}
184 
185 	Group::remove( w );
186 }
187 
handleOnly(Widget * wid)188 void UI::handleOnly( Widget* wid )
189 {
190 	handleOnlyWidget = wid;
191 }
192 
event(const PuglEvent * event)193 void UI::event( const PuglEvent* event )
194 {
195 	if( event->type != PUGL_EXPOSE ) {
196 		//AVTK_DEV("UI::handle() type = %i, sending to Tester\n", event->type );
197 #ifdef AVTK_TESTER
198 		// eat AVTK start record events shortcut:
199 		if( event->type == PUGL_KEY_PRESS ) {
200 			// ^1 pressed (Ctrl and number 1)
201 			if( event->key.character == 'a' )
202 				//&& (((PuglEventKey*)event)->state & PUGL_MOD_CTRL) )
203 			{
204 				if( !tester->recording() ) {
205 					AVTK_DEV("AVTK: Tester Record Starting!\n");
206 					tester->record( "test1" );
207 				} else {
208 					tester->recordStop();
209 				}
210 				return;
211 			}
212 
213 			// replay on Ctrl^2
214 			if( event->key.character == 's' )
215 				// && (((PuglEventKey*)event)->state & PUGL_MOD_CTRL) )
216 			{
217 				AVTK_DEV("run test!\n");
218 				tester->runTest( "test1" );
219 				return;
220 			}
221 		} else {
222 			tester->handle( event );
223 		}
224 #endif
225 
226 		if( handleOnlyWidget ) {
227 			handleOnlyWidget->handle( event );
228 			internalEvent( event );
229 			return;
230 		}
231 
232 
233 		// pass event to UI handle
234 		if( handle( event )  )
235 			return;
236 
237 		// if not handled, try all child-widgets
238 		int ret = Group::handle( event );
239 		if ( ret ) {
240 			redraw();
241 			return;
242 		}
243 
244 		// if no widget handles the event, then we test the main UI shortcuts
245 		internalEvent( event );
246 
247 	} else if( event->type == PUGL_CONFIGURE ) {
248 		AVTK_DEV("UI handleing PUGL_CONFIGURE\n");
249 	}
250 }
251 
internalEvent(const PuglEvent * event)252 void UI::internalEvent( const PuglEvent* event )
253 {
254 	// code is only reached if *none* of the widgets handled an event:
255 	// we can implement UI wide hotkeys here, handle unknown events
256 	switch (event->type) {
257 	case PUGL_BUTTON_PRESS:
258 		break;
259 
260 	case PUGL_KEY_PRESS:
261 		if (event->key.character == 'q' ||
262 		    event->key.character == 'Q' ||
263 		    event->key.character == PUGL_CHAR_ESCAPE) {
264 			if( handleOnlyWidget ) {
265 				handleOnlyWidget->visible( false );
266 				handleOnlyWidget = 0x0;
267 				redraw();
268 			} else {
269 				quit_ = 1;
270 			}
271 		}
272 		break;
273 
274 	default:
275 		break;
276 	}
277 
278 	return;
279 }
280 
redraw()281 void UI::redraw()
282 {
283 	puglPostRedisplay( view );
284 }
285 
redraw(Avtk::Widget * w)286 void UI::redraw( Avtk::Widget* w )
287 {
288 	puglPostRedisplay( view );
289 	//puglPostExpose( view, w->x, w->y, w->w, w->h );
290 }
291 
motion(int x,int y)292 void UI::motion(int x, int y)
293 {
294 	if( motionUpdateWidget ) {
295 		motionUpdateWidget->motion( x, y );
296 	} else if( dragDropOrigin ) {
297 		/*
298 		refactor to use GROUP!
299 		// scan trough widgets on mouse-move, as it *could* be a drag-drop action.
300 		for (std::list< Avtk::Widget* >::iterator it = widgets.begin(); it != widgets.end(); it++)
301 		{
302 		  if( (*it)->touches( x, y ) )
303 		  {
304 		    //AVTK_DEV("DragDropVerify: Origin %s, Target %s\n", dragDropOrigin->label(), (*it)->label() );
305 		    dragDropVerify( (*it) );
306 		  }
307 		}
308 		*/
309 	}
310 }
311 
dragDropInit(Avtk::Widget * origin,size_t size,void * data)312 void UI::dragDropInit( Avtk::Widget* origin, size_t size, void* data )
313 {
314 	// set the dragDropOrigin widget, and set the motionUpdateWidget to NULL.
315 	dragDropOrigin = origin;
316 
317 	motionUpdateWidget = 0;
318 
319 	if( dragDropDataPtr ) {
320 		AVTK_DEV("UI delete[] existing dragDropDataPtr\n");
321 		delete[] dragDropDataPtr;
322 	}
323 
324 	AVTK_DEV("UI new dragDropDataPtr, size %i\n", size);
325 	dragDropDataSize = size;
326 	dragDropDataPtr  = new char[size];
327 
328 	memcpy( dragDropDataPtr, data, size );
329 }
330 
dragDropVerify(Avtk::Widget * target)331 bool UI::dragDropVerify( Avtk::Widget* target )
332 {
333 	if ( dragDropTargetVerified && dragDropTargetVerifiedWidget == target ) {
334 		// we've already found a valid match for this widget, just return true
335 		return true;
336 	} else {
337 		// reset search for a match
338 		dragDropTargetVerifiedWidget = 0;
339 	}
340 
341 	// haven't tested this widget yet
342 	if( dragDropTargetVerifiedWidget == 0 ) {
343 		// TODO: match in the origin data-types and target data-types
344 		for( int i = 0; i < 1/*dragDropOrigin->dragDropDataTypes()*/; i++ ) {
345 			if( true /*target->dragDropDataTypeCheck( i )*/ ) {
346 				dragDropTargetVerified = true;
347 				dragDropTargetVerifiedWidget = target;
348 				AVTK_DEV("DragDropVerify to %s OK: data = %s\n", target->label(), dragDropDataPtr );
349 				return true;
350 			}
351 		}
352 
353 		dragDropTargetVerified = false;
354 		AVTK_DEV("DragDropVerify Failed no data-type matches\n" );
355 		return false;
356 	}
357 
358 	return false;
359 }
360 
dragDropComplete(Avtk::Widget * target)361 void UI::dragDropComplete( Avtk::Widget* target )
362 {
363 
364 }
365