]> git.sesse.net Git - vlc/blob - modules/gui/skins2/src/generic_layout.cpp
97bb03ce4d269c01b16b90db7f144dd1d176c133
[vlc] / modules / gui / skins2 / src / generic_layout.cpp
1 /*****************************************************************************
2  * generic_layout.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include "generic_layout.hpp"
26 #include "top_window.hpp"
27 #include "os_factory.hpp"
28 #include "os_graphics.hpp"
29 #include "var_manager.hpp"
30 #include "anchor.hpp"
31 #include "../controls/ctrl_generic.hpp"
32 #include "../controls/ctrl_video.hpp"
33 #include "../utils/var_bool.hpp"
34 #include <set>
35
36
37 GenericLayout::GenericLayout( intf_thread_t *pIntf, int width, int height,
38                               int minWidth, int maxWidth, int minHeight,
39                               int maxHeight ):
40     SkinObject( pIntf ), m_pWindow( NULL ), m_rect( 0, 0, width, height ),
41     m_minWidth( minWidth ), m_maxWidth( maxWidth ),
42     m_minHeight( minHeight ), m_maxHeight( maxHeight ), m_pVideoCtrlSet(),
43     m_visible( false ), m_pVarActive( NULL )
44 {
45     // Get the OSFactory
46     OSFactory *pOsFactory = OSFactory::instance( getIntf() );
47     // Create the graphics buffer
48     m_pImage = pOsFactory->createOSGraphics( width, height );
49
50     // Create the "active layout" variable and register it in the manager
51     m_pVarActive = new VarBoolImpl( pIntf );
52     VarManager::instance( pIntf )->registerVar( VariablePtr( m_pVarActive ) );
53 }
54
55
56 GenericLayout::~GenericLayout()
57 {
58     delete m_pImage;
59     list<Anchor*>::const_iterator it;
60     for( it = m_anchorList.begin(); it != m_anchorList.end(); it++ )
61     {
62         delete *it;
63     }
64 }
65
66
67 void GenericLayout::setWindow( TopWindow *pWindow )
68 {
69     m_pWindow = pWindow;
70 }
71
72
73 void GenericLayout::onControlCapture( const CtrlGeneric &rCtrl )
74 {
75     // Just forward the request to the window
76     TopWindow *pWindow = getWindow();
77     if( pWindow )
78     {
79         pWindow->onControlCapture( rCtrl );
80     }
81 }
82
83
84 void GenericLayout::onControlRelease( const CtrlGeneric &rCtrl )
85 {
86     // Just forward the request to the window
87     TopWindow *pWindow = getWindow();
88     if( pWindow )
89     {
90         pWindow->onControlRelease( rCtrl );
91     }
92 }
93
94
95 void GenericLayout::addControl( CtrlGeneric *pControl,
96                                 const Position &rPosition, int layer )
97 {
98     if( pControl )
99     {
100         // Associate this layout to the control
101         pControl->setLayout( this, rPosition );
102
103         // Draw the control
104         pControl->draw( *m_pImage, rPosition.getLeft(), rPosition.getTop() );
105
106         // Add the control in the list.
107         // This list must remain sorted by layer order
108         list<LayeredControl>::iterator it;
109         for( it = m_controlList.begin(); it != m_controlList.end(); it++ )
110         {
111             if( layer < (*it).m_layer )
112             {
113                 m_controlList.insert( it, LayeredControl( pControl, layer ) );
114                 break;
115             }
116         }
117         // If this control is in front of all the previous ones
118         if( it == m_controlList.end() )
119         {
120             m_controlList.push_back( LayeredControl( pControl, layer ) );
121         }
122
123         // Check if it is a video control
124         if( pControl->getType() == "video" )
125         {
126             m_pVideoCtrlSet.insert( (CtrlVideo*)pControl );
127         }
128     }
129     else
130     {
131         msg_Dbg( getIntf(), "adding NULL control in the layout" );
132     }
133 }
134
135
136 const list<LayeredControl> &GenericLayout::getControlList() const
137 {
138     return m_controlList;
139 }
140
141
142 void GenericLayout::onControlUpdate( const CtrlGeneric &rCtrl,
143                                      int width, int height,
144                                      int xOffSet, int yOffSet )
145 {
146     // The size is not valid, refresh the whole layout
147     if( width <= 0 || height <= 0 )
148     {
149         refreshAll();
150         return;
151     }
152
153     const Position *pPos = rCtrl.getPosition();
154     if( pPos )
155     {
156         refreshRect( pPos->getLeft() + xOffSet,
157                      pPos->getTop() + yOffSet,
158                      width, height );
159     }
160 }
161
162
163 void GenericLayout::resize( int width, int height )
164 {
165     // Update the window size
166     m_rect = SkinsRect( 0, 0 , width, height );
167
168     // Recreate a new image
169     if( m_pImage )
170     {
171         delete m_pImage;
172         OSFactory *pOsFactory = OSFactory::instance( getIntf() );
173         m_pImage = pOsFactory->createOSGraphics( width, height );
174     }
175
176     // Notify all the controls that the size has changed and redraw them
177     list<LayeredControl>::const_iterator iter;
178     for( iter = m_controlList.begin(); iter != m_controlList.end(); iter++ )
179     {
180         iter->m_pControl->onResize();
181     }
182
183     // Resize and refresh the associated window
184     TopWindow *pWindow = getWindow();
185     if( pWindow )
186     {
187         // Resize the window
188         pWindow->resize( width, height );
189         // Change the shape of the window and redraw it
190         refreshAll();
191     }
192 }
193
194
195 void GenericLayout::refreshAll()
196 {
197     refreshRect( 0, 0, m_rect.getWidth(), m_rect.getHeight() );
198 }
199
200
201 void GenericLayout::refreshRect( int x, int y, int width, int height )
202 {
203     // Do nothing if the layout is hidden
204     if( !m_visible )
205         return;
206
207     // Draw all the controls of the layout
208     list<LayeredControl>::const_iterator iter;
209     list<LayeredControl>::const_iterator iterVideo = m_controlList.end();
210     for( iter = m_controlList.begin(); iter != m_controlList.end(); iter++ )
211     {
212         CtrlGeneric *pCtrl = (*iter).m_pControl;
213         const Position *pPos = pCtrl->getPosition();
214         if( pPos && pCtrl->isVisible() )
215         {
216             pCtrl->draw( *m_pImage, pPos->getLeft(), pPos->getTop() );
217         }
218     }
219
220     // Refresh the associated window
221     TopWindow *pWindow = getWindow();
222     if( pWindow )
223     {
224         // first apply new shape to the window
225         pWindow->updateShape();
226
227         // Check boundaries
228         if( x < 0 )
229             x = 0;
230         if( y < 0)
231             y = 0;
232         if( x + width > m_rect.getWidth() )
233             width = m_rect.getWidth() - x;
234         if( y + height > m_rect.getHeight() )
235             height = m_rect.getHeight() - y;
236
237         computeRefresh( x, y, width, height );
238     }
239 }
240
241 class rect
242 {
243 public:
244     rect( int v_x = 0, int v_y = 0, int v_width = 0, int v_height = 0 )
245         : x( v_x ), y( v_y ), width( v_width ), height( v_height ) { }
246     ~rect() { }
247     int x;
248     int y;
249     int width;
250     int height;
251
252     static bool isIncluded( rect& rect2, rect& rect1 )
253     {
254         int x1 = rect1.x;
255         int y1 = rect1.y;
256         int w1 = rect1.width;
257         int h1 = rect1.height;
258
259         int x2 = rect2.x;
260         int y2 = rect2.y;
261         int w2 = rect2.width;
262         int h2 = rect2.height;
263
264         return  x2 >= x1 && x2 < x1 + w1
265             &&  y2 >= y1 && y2 < y1 + h1
266             &&  w2 <= w1
267             &&  h2 <= h1;
268     }
269 };
270
271 void GenericLayout::computeRefresh( int x, int y, int width, int height )
272 {
273     TopWindow *pWindow = getWindow();
274
275 #ifndef WIN32
276
277     pWindow->refresh( x, y, width, height );
278
279 #else
280
281     if( !m_pVideoCtrlSet.size() )
282     {
283         // No video control, we can safely repaint the rectangle
284         pWindow->refresh( x, y, width, height );
285         return;
286     }
287
288     int w = width;
289     int h = height;
290
291     set<int> x_set;
292     set<int> y_set;
293     vector<rect> rect_set;
294
295     x_set.insert( x + w );
296     y_set.insert( y + h );
297
298     // retrieve video controls being used
299     // and remember their rectangles
300     set<CtrlVideo*>::const_iterator it;
301     for( it = m_pVideoCtrlSet.begin(); it != m_pVideoCtrlSet.end(); it++ )
302     {
303         if( (*it)->isUsed() )
304         {
305             int xx = (*it)->getPosition()->getLeft();
306             int yy = (*it)->getPosition()->getTop();
307             int ww = (*it)->getPosition()->getWidth();
308             int hh = (*it)->getPosition()->getHeight();
309
310             rect r(xx, yy, ww, hh );
311             rect_set.push_back( r );
312
313             if( xx > x && xx < x + w )
314                 x_set.insert( xx );
315             if( xx + ww > x && xx + ww < x + w )
316                 x_set.insert( xx + ww );
317             if( yy > y && yy < y + h )
318                 y_set.insert( yy );
319             if( yy + hh > y && yy + hh < y + h )
320                 y_set.insert( yy + hh );
321         }
322     }
323
324     // for each subregion, test whether they are part
325     // of the video control(s) or not
326     set<int>::const_iterator it_x;
327     set<int>::const_iterator it_y;
328     int x_prev, y_prev;
329
330     for( x_prev = x, it_x = x_set.begin();
331          it_x != x_set.end(); x_prev = *it_x, it_x++ )
332     {
333         int x0 = x_prev;
334         int w0 = *it_x - x_prev;
335
336         for( y_prev = y, it_y = y_set.begin();
337              it_y != y_set.end(); y_prev = *it_y, it_y++ )
338         {
339             int y0 = y_prev;
340             int h0 = *it_y - y_prev;
341
342             rect r( x0, y0, w0, h0 );
343             bool b_refresh = true;
344
345             vector<rect>::iterator it;
346             for( it = rect_set.begin(); it != rect_set.end(); it++ )
347             {
348                 rect r_ctrl = *it;
349                 if( rect::isIncluded( r, r_ctrl ) )
350                 {
351                     b_refresh = false;
352                     break;
353                 }
354             }
355
356             // subregion is not part of a video control
357             // needs to be refreshed
358             if( b_refresh )
359                 pWindow->refresh( x0, y0, w0 ,h0 );
360         }
361     }
362
363 #endif
364
365 }
366
367
368 const list<Anchor*>& GenericLayout::getAnchorList() const
369 {
370     return m_anchorList;
371 }
372
373
374 void GenericLayout::addAnchor( Anchor *pAnchor )
375 {
376     m_anchorList.push_back( pAnchor );
377 }
378
379
380 void GenericLayout::onShow()
381 {
382     m_visible = true;
383
384     refreshAll();
385 }
386
387
388 void GenericLayout::onHide()
389 {
390     m_visible = false;
391 }
392