skins2: fix RadialSlider
[vlc/asuraparaju-public.git] / modules / gui / skins2 / src / window_manager.cpp
blob1081e1a61e62ad3193c52bb1a6d2d4c9fe70e046
1 /*****************************************************************************
2 * window_manager.cpp
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
5 * $Id$
7 * Authors: Cyril Deguet <asmax@via.ecp.fr>
8 * Olivier Teulière <ipkiss@via.ecp.fr>
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.
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.
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 *****************************************************************************/
25 #include "window_manager.hpp"
26 #include "generic_layout.hpp"
27 #include "generic_window.hpp"
28 #include "os_factory.hpp"
29 #include "anchor.hpp"
30 #include "tooltip.hpp"
31 #include "var_manager.hpp"
34 WindowManager::WindowManager( intf_thread_t *pIntf ):
35 SkinObject( pIntf ), m_magnet( 0 ), m_direction( kNone ),
36 m_maximizeRect(0, 0, 50, 50),
37 m_pTooltip( NULL ), m_pPopup( NULL )
39 // Create and register a variable for the "on top" status
40 VarManager *pVarManager = VarManager::instance( getIntf() );
41 m_cVarOnTop = VariablePtr( new VarBoolImpl( getIntf() ) );
42 pVarManager->registerVar( m_cVarOnTop, "vlc.isOnTop" );
46 WindowManager::~WindowManager()
48 delete m_pTooltip;
52 void WindowManager::registerWindow( TopWindow &rWindow )
54 // Add the window to the set
55 m_allWindows.insert( &rWindow );
59 void WindowManager::unregisterWindow( TopWindow &rWindow )
61 // Erase every possible reference to the window
62 m_allWindows.erase( &rWindow );
63 m_movingWindows.erase( &rWindow );
64 m_dependencies.erase( &rWindow );
68 void WindowManager::startMove( TopWindow &rWindow )
70 // Rebuild the set of moving windows
71 m_movingWindows.clear();
72 buildDependSet( m_movingWindows, &rWindow );
74 if( var_InheritBool( getIntf(), "skins2-transparency" ) )
76 // Change the opacity of the moving windows
77 WinSet_t::const_iterator it;
78 for( it = m_movingWindows.begin(); it != m_movingWindows.end(); it++ )
80 (*it)->setOpacity( m_moveAlpha );
83 // FIXME: We need to refresh the windows, because if 2 windows overlap
84 // and one of them becomes transparent, the other one is not refreshed
85 // automatically. I don't know why... -- Ipkiss
86 for( it = m_allWindows.begin(); it != m_allWindows.end(); it++ )
88 (*it)->refresh( 0, 0, (*it)->getWidth(), (*it)->getHeight() );
94 void WindowManager::stopMove()
96 WinSet_t::const_iterator itWin1, itWin2;
97 AncList_t::const_iterator itAnc1, itAnc2;
99 if( var_InheritBool( getIntf(), "skins2-transparency" ) )
101 // Restore the opacity of the moving windows
102 WinSet_t::const_iterator it;
103 for( it = m_movingWindows.begin(); it != m_movingWindows.end(); it++ )
105 (*it)->setOpacity( m_alpha );
109 // Delete the dependencies
110 m_dependencies.clear();
112 // Now we rebuild the dependencies.
113 // Iterate through all the windows
114 for( itWin1 = m_allWindows.begin(); itWin1 != m_allWindows.end(); itWin1++ )
116 // Get the anchors of the layout associated to the window
117 const AncList_t &ancList1 =
118 (*itWin1)->getActiveLayout().getAnchorList();
120 // Iterate through all the windows, starting with (*itWin1)
121 for( itWin2 = itWin1; itWin2 != m_allWindows.end(); itWin2++ )
123 // A window can't anchor itself...
124 if( (*itWin2) == (*itWin1) )
125 continue;
127 // Now, check for anchoring between the 2 windows
128 const AncList_t &ancList2 =
129 (*itWin2)->getActiveLayout().getAnchorList();
130 for( itAnc1 = ancList1.begin(); itAnc1 != ancList1.end(); itAnc1++ )
132 for( itAnc2 = ancList2.begin();
133 itAnc2 != ancList2.end(); itAnc2++ )
135 if( (*itAnc1)->isHanging( **itAnc2 ) )
137 // (*itWin1) anchors (*itWin2)
138 m_dependencies[*itWin1].insert( *itWin2 );
140 else if( (*itAnc2)->isHanging( **itAnc1 ) )
142 // (*itWin2) anchors (*itWin1)
143 m_dependencies[*itWin2].insert( *itWin1 );
152 void WindowManager::move( TopWindow &rWindow, int left, int top ) const
154 // Compute the real move offset
155 int xOffset = left - rWindow.getLeft();
156 int yOffset = top - rWindow.getTop();
158 // Check anchoring; this can change the values of xOffset and yOffset
159 checkAnchors( &rWindow, xOffset, yOffset );
161 // Move all the windows
162 WinSet_t::const_iterator it;
163 for( it = m_movingWindows.begin(); it != m_movingWindows.end(); it++ )
165 (*it)->move( (*it)->getLeft() + xOffset, (*it)->getTop() + yOffset );
170 void WindowManager::startResize( GenericLayout &rLayout, Direction_t direction )
172 m_direction = direction;
174 // Rebuild the set of moving windows.
175 // From the resized window, we only take into account the anchors which
176 // are mobile with the current type of resizing, and that are hanging a
177 // window. The hanged windows will come will all their dependencies.
179 m_resizeMovingE.clear();
180 m_resizeMovingS.clear();
181 m_resizeMovingSE.clear();
183 WinSet_t::const_iterator itWin;
184 AncList_t::const_iterator itAnc1, itAnc2;
185 // Get the anchors of the layout
186 const AncList_t &ancList1 = rLayout.getAnchorList();
188 // Iterate through all the hanged windows
189 for( itWin = m_dependencies[rLayout.getWindow()].begin();
190 itWin != m_dependencies[rLayout.getWindow()].end(); itWin++ )
192 // Now, check for anchoring between the 2 windows
193 const AncList_t &ancList2 =
194 (*itWin)->getActiveLayout().getAnchorList();
195 for( itAnc1 = ancList1.begin(); itAnc1 != ancList1.end(); itAnc1++ )
197 for( itAnc2 = ancList2.begin();
198 itAnc2 != ancList2.end(); itAnc2++ )
200 if( (*itAnc1)->isHanging( **itAnc2 ) )
202 // Add the dependencies of the hanged window to one of the
203 // lists of moving windows
204 Position::Ref_t aRefPos =
205 (*itAnc1)->getPosition().getRefLeftTop();
206 if( aRefPos == Position::kRightTop )
207 buildDependSet( m_resizeMovingE, *itWin );
208 else if( aRefPos == Position::kLeftBottom )
209 buildDependSet( m_resizeMovingS, *itWin );
210 else if( aRefPos == Position::kRightBottom )
211 buildDependSet( m_resizeMovingSE, *itWin );
212 break;
218 // The checkAnchors() method will need to have m_movingWindows properly set
219 // so let's insert in it the contents of the other sets
220 m_movingWindows.clear();
221 m_movingWindows.insert( rLayout.getWindow() );
222 m_movingWindows.insert( m_resizeMovingE.begin(), m_resizeMovingE.end() );
223 m_movingWindows.insert( m_resizeMovingS.begin(), m_resizeMovingS.end() );
224 m_movingWindows.insert( m_resizeMovingSE.begin(), m_resizeMovingSE.end() );
228 void WindowManager::stopResize()
230 // Nothing different from stopMove(), luckily
231 stopMove();
235 void WindowManager::resize( GenericLayout &rLayout,
236 int width, int height ) const
238 // TODO: handle anchored windows
239 // Compute the real resizing offset
240 int xOffset = width - rLayout.getWidth();
241 int yOffset = height - rLayout.getHeight();
243 // Check anchoring; this can change the values of xOffset and yOffset
244 checkAnchors( rLayout.getWindow(), xOffset, yOffset );
245 if( m_direction == kResizeS )
246 xOffset = 0;
247 if( m_direction == kResizeE )
248 yOffset = 0;
250 int newWidth = rLayout.getWidth() + xOffset;
251 int newHeight = rLayout.getHeight() + yOffset;
253 // Check boundaries
254 if( newWidth < rLayout.getMinWidth() )
256 newWidth = rLayout.getMinWidth();
258 if( newWidth > rLayout.getMaxWidth() )
260 newWidth = rLayout.getMaxWidth();
262 if( newHeight < rLayout.getMinHeight() )
264 newHeight = rLayout.getMinHeight();
266 if( newHeight > rLayout.getMaxHeight() )
268 newHeight = rLayout.getMaxHeight();
271 if( newWidth == rLayout.getWidth() && newHeight == rLayout.getHeight() )
273 return;
276 // New offset, after the last corrections
277 int xNewOffset = newWidth - rLayout.getWidth();
278 int yNewOffset = newHeight - rLayout.getHeight();
280 // Do the actual resizing
281 rLayout.resize( newWidth, newHeight );
283 // Move all the anchored windows
284 WinSet_t::const_iterator it;
285 if( m_direction == kResizeE ||
286 m_direction == kResizeSE )
288 for( it = m_resizeMovingE.begin(); it != m_resizeMovingE.end(); it++ )
290 (*it)->move( (*it)->getLeft() + xNewOffset,
291 (*it)->getTop() );
294 if( m_direction == kResizeE ||
295 m_direction == kResizeSE )
297 for( it = m_resizeMovingS.begin(); it != m_resizeMovingS.end(); it++ )
299 (*it)->move( (*it)->getLeft(),
300 (*it)->getTop( )+ yNewOffset );
303 if( m_direction == kResizeE ||
304 m_direction == kResizeS ||
305 m_direction == kResizeSE )
307 for( it = m_resizeMovingSE.begin(); it != m_resizeMovingSE.end(); it++ )
309 (*it)->move( (*it)->getLeft() + xNewOffset,
310 (*it)->getTop() + yNewOffset );
316 void WindowManager::maximize( TopWindow &rWindow )
318 // Save the current position/size of the window, to be able to restore it
319 m_maximizeRect = SkinsRect( rWindow.getLeft(), rWindow.getTop(),
320 rWindow.getLeft() + rWindow.getWidth(),
321 rWindow.getTop() + rWindow.getHeight() );
323 SkinsRect workArea = OSFactory::instance( getIntf() )->getWorkArea();
324 // Move the window
325 startMove( rWindow );
326 move( rWindow, workArea.getLeft(), workArea.getTop() );
327 stopMove();
328 // Now resize it
329 // FIXME: Ugly const_cast
330 GenericLayout &rLayout = (GenericLayout&)rWindow.getActiveLayout();
331 startResize( rLayout, kResizeSE );
332 resize( rLayout, workArea.getWidth(), workArea.getHeight() );
333 stopResize();
334 rWindow.m_pVarMaximized->set( true );
336 // Make the window unmovable by unregistering it
337 // unregisterWindow( rWindow );
341 void WindowManager::unmaximize( TopWindow &rWindow )
343 // Register the window to allow moving it
344 // registerWindow( rWindow );
346 // Resize the window
347 // FIXME: Ugly const_cast
348 GenericLayout &rLayout = (GenericLayout&)rWindow.getActiveLayout();
349 startResize( rLayout, kResizeSE );
350 resize( rLayout, m_maximizeRect.getWidth(), m_maximizeRect.getHeight() );
351 stopResize();
352 // Now move it
353 startMove( rWindow );
354 move( rWindow, m_maximizeRect.getLeft(), m_maximizeRect.getTop() );
355 stopMove();
356 rWindow.m_pVarMaximized->set( false );
360 void WindowManager::synchVisibility() const
362 WinSet_t::const_iterator it;
363 for( it = m_allWindows.begin(); it != m_allWindows.end(); it++ )
365 // Show the window if it has to be visible
366 if( (*it)->getVisibleVar().get() )
368 (*it)->innerShow();
374 void WindowManager::saveVisibility()
376 WinSet_t::const_iterator it;
377 m_savedWindows.clear();
378 for( it = m_allWindows.begin(); it != m_allWindows.end(); it++ )
380 // Remember the window if it is visible
381 if( (*it)->getVisibleVar().get() )
383 m_savedWindows.insert( *it );
389 void WindowManager::restoreVisibility() const
391 // Warning in case we never called saveVisibility()
392 if( m_savedWindows.size() == 0 )
394 msg_Warn( getIntf(), "restoring visibility for no window" );
397 WinSet_t::const_iterator it;
398 for( it = m_savedWindows.begin(); it != m_savedWindows.end(); it++)
400 (*it)->show();
405 void WindowManager::raiseAll() const
407 // Raise all the windows
408 WinSet_t::const_iterator it;
409 for( it = m_allWindows.begin(); it != m_allWindows.end(); it++ )
411 (*it)->raise();
416 void WindowManager::showAll( bool firstTime ) const
418 // Show all the windows
419 WinSet_t::const_iterator it;
420 for( it = m_allWindows.begin(); it != m_allWindows.end(); it++ )
422 // When the theme is opened for the first time,
423 // only show the window if set as visible in the XML
424 if( (*it)->isVisible() || !firstTime )
426 (*it)->show();
432 void WindowManager::hideAll() const
434 WinSet_t::const_iterator it;
435 for( it = m_allWindows.begin(); it != m_allWindows.end(); it++ )
437 (*it)->hide();
442 void WindowManager::setOnTop( bool b_ontop )
444 // Update the boolean variable
445 VarBoolImpl *pVarOnTop = (VarBoolImpl*)m_cVarOnTop.get();
446 pVarOnTop->set( b_ontop );
448 // set/unset the "on top" status
449 WinSet_t::const_iterator it;
450 for( it = m_allWindows.begin(); it != m_allWindows.end(); it++ )
452 (*it)->toggleOnTop( b_ontop );
457 void WindowManager::toggleOnTop()
459 VarBoolImpl *pVarOnTop = (VarBoolImpl*)m_cVarOnTop.get();
461 setOnTop( !pVarOnTop->get() );
465 void WindowManager::buildDependSet( WinSet_t &rWinSet,
466 TopWindow *pWindow )
468 // pWindow is in the set
469 rWinSet.insert( pWindow );
471 // Iterate through the anchored windows
472 const WinSet_t &anchored = m_dependencies[pWindow];
473 WinSet_t::const_iterator iter;
474 for( iter = anchored.begin(); iter != anchored.end(); iter++ )
476 // Check that the window isn't already in the set before adding it
477 if( rWinSet.find( *iter ) == rWinSet.end() )
479 buildDependSet( rWinSet, *iter );
485 void WindowManager::checkAnchors( TopWindow *pWindow,
486 int &xOffset, int &yOffset ) const
488 WinSet_t::const_iterator itMov, itSta;
489 AncList_t::const_iterator itAncMov, itAncSta;
491 // Check magnetism with screen edges first (actually it is the work area)
492 SkinsRect workArea = OSFactory::instance( getIntf() )->getWorkArea();
493 // Iterate through the moving windows
494 for( itMov = m_movingWindows.begin();
495 itMov != m_movingWindows.end(); itMov++ )
497 // Skip the invisible windows
498 if( ! (*itMov)->getVisibleVar().get() )
500 continue;
503 int newLeft = (*itMov)->getLeft() + xOffset;
504 int newTop = (*itMov)->getTop() + yOffset;
505 if( newLeft > workArea.getLeft() - m_magnet &&
506 newLeft < workArea.getLeft() + m_magnet )
508 xOffset = workArea.getLeft() - (*itMov)->getLeft();
510 if( newTop > workArea.getTop() - m_magnet &&
511 newTop < workArea.getTop() + m_magnet )
513 yOffset = workArea.getTop() - (*itMov)->getTop();
515 int right = workArea.getLeft() + workArea.getWidth();
516 if( newLeft + (*itMov)->getWidth() > right - m_magnet &&
517 newLeft + (*itMov)->getWidth() < right + m_magnet )
519 xOffset = right - (*itMov)->getLeft() - (*itMov)->getWidth();
521 int bottom = workArea.getTop() + workArea.getHeight();
522 if( newTop + (*itMov)->getHeight() > bottom - m_magnet &&
523 newTop + (*itMov)->getHeight() < bottom + m_magnet )
525 yOffset = bottom - (*itMov)->getTop() - (*itMov)->getHeight();
529 // Iterate through the moving windows
530 for( itMov = m_movingWindows.begin();
531 itMov != m_movingWindows.end(); itMov++ )
533 // Skip the invisible windows
534 if( ! (*itMov)->getVisibleVar().get() )
536 continue;
539 // Get the anchors in the main layout of this moving window
540 const AncList_t &movAnchors =
541 (*itMov)->getActiveLayout().getAnchorList();
543 // Iterate through the static windows
544 for( itSta = m_allWindows.begin();
545 itSta != m_allWindows.end(); itSta++ )
547 // Skip the moving windows and the invisible ones
548 if( m_movingWindows.find( (*itSta) ) != m_movingWindows.end() ||
549 ! (*itSta)->getVisibleVar().get() )
551 continue;
554 // Get the anchors in the main layout of this static window
555 const AncList_t &staAnchors =
556 (*itSta)->getActiveLayout().getAnchorList();
558 // Check if there is an anchoring between one of the movAnchors
559 // and one of the staAnchors
560 for( itAncMov = movAnchors.begin();
561 itAncMov != movAnchors.end(); itAncMov++ )
563 for( itAncSta = staAnchors.begin();
564 itAncSta != staAnchors.end(); itAncSta++ )
566 if( (*itAncSta)->canHang( **itAncMov, xOffset, yOffset ) )
568 // We have found an anchoring!
569 // There is nothing to do here, since xOffset and
570 // yOffset are automatically modified by canHang()
572 // Don't check the other anchors, one is enough...
573 return;
575 else
577 // Temporary variables
578 int xOffsetTemp = -xOffset;
579 int yOffsetTemp = -yOffset;
580 if( (*itAncMov)->canHang( **itAncSta, xOffsetTemp,
581 yOffsetTemp ) )
583 // We have found an anchoring!
584 // xOffsetTemp and yOffsetTemp have been updated,
585 // we just need to change xOffset and yOffset
586 xOffset = -xOffsetTemp;
587 yOffset = -yOffsetTemp;
589 // Don't check the other anchors, one is enough...
590 return;
600 void WindowManager::createTooltip( const GenericFont &rTipFont )
602 // Create the tooltip window
603 if( !m_pTooltip )
605 m_pTooltip = new Tooltip( getIntf(), rTipFont, 500 );
607 else
609 msg_Warn( getIntf(), "tooltip already created!" );
614 void WindowManager::showTooltip()
616 if( m_pTooltip )
618 m_pTooltip->show();
623 void WindowManager::hideTooltip()
625 if( m_pTooltip )
627 m_pTooltip->hide();
632 void WindowManager::addLayout( TopWindow &rWindow, GenericLayout &rLayout )
634 rWindow.setActiveLayout( &rLayout );
638 void WindowManager::setActiveLayout( TopWindow &rWindow,
639 GenericLayout &rLayout )
641 rWindow.setActiveLayout( &rLayout );
642 // Rebuild the dependencies
643 stopMove();