1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
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"
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()
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
) )
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
);
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
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
)
247 if( m_direction
== kResizeE
)
250 int newWidth
= rLayout
.getWidth() + xOffset
;
251 int newHeight
= rLayout
.getHeight() + yOffset
;
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() )
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
,
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();
325 startMove( rWindow
);
326 move( rWindow
, workArea
.getLeft(), workArea
.getTop() );
329 // FIXME: Ugly const_cast
330 GenericLayout
&rLayout
= (GenericLayout
&)rWindow
.getActiveLayout();
331 startResize( rLayout
, kResizeSE
);
332 resize( rLayout
, workArea
.getWidth(), workArea
.getHeight() );
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 );
347 // FIXME: Ugly const_cast
348 GenericLayout
&rLayout
= (GenericLayout
&)rWindow
.getActiveLayout();
349 startResize( rLayout
, kResizeSE
);
350 resize( rLayout
, m_maximizeRect
.getWidth(), m_maximizeRect
.getHeight() );
353 startMove( rWindow
);
354 move( rWindow
, m_maximizeRect
.getLeft(), m_maximizeRect
.getTop() );
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() )
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
++)
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
++ )
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
)
432 void WindowManager::hideAll() const
434 WinSet_t::const_iterator it
;
435 for( it
= m_allWindows
.begin(); it
!= m_allWindows
.end(); it
++ )
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
,
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() )
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() )
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() )
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...
577 // Temporary variables
578 int xOffsetTemp
= -xOffset
;
579 int yOffsetTemp
= -yOffset
;
580 if( (*itAncMov
)->canHang( **itAncSta
, xOffsetTemp
,
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...
600 void WindowManager::createTooltip( const GenericFont
&rTipFont
)
602 // Create the tooltip window
605 m_pTooltip
= new Tooltip( getIntf(), rTipFont
, 500 );
609 msg_Warn( getIntf(), "tooltip already created!" );
614 void WindowManager::showTooltip()
623 void WindowManager::hideTooltip()
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