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