1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org 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 Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "vclxtabcontrol.hxx"
29 
30 #include <com/sun/star/awt/PosSize.hpp>
31 #include <sal/macros.h>
32 #include <toolkit/helper/property.hxx>
33 #include <toolkit/helper/vclunohelper.hxx>
34 #include <vcl/tabctrl.hxx>
35 #include <vcl/tabpage.hxx>
36 
37 #include "forward.hxx"
38 
39 namespace layoutimpl
40 {
41 
42 using namespace ::com::sun::star::lang;
43 using namespace ::com::sun::star::beans;
44 using namespace ::com::sun::star;
45 
46 VCLXTabControl::ChildProps::ChildProps( VCLXTabControl::ChildData *pData )
47 {
48     addProp( RTL_CONSTASCII_USTRINGPARAM( "Title" ),
49              ::getCppuType( static_cast< const rtl::OUString* >( NULL ) ),
50              &(pData->maTitle) );
51 }
52 
53 VCLXTabControl::ChildData::ChildData( uno::Reference< awt::XLayoutConstrains > const& xChild )
54     : Box_Base::ChildData( xChild )
55     , maTitle()
56 {
57 }
58 
59 VCLXTabControl::ChildData*
60 VCLXTabControl::createChild( uno::Reference< awt::XLayoutConstrains > const& xChild )
61 {
62     return new ChildData( xChild );
63 }
64 
65 VCLXTabControl::ChildProps*
66 VCLXTabControl::createChildProps( Box_Base::ChildData *pData )
67 {
68     return new ChildProps( static_cast<VCLXTabControl::ChildData*> ( pData ) );
69 }
70 
71 DBG_NAME( VCLXTabControl );
72 
73 #if !defined (__GNUC__)
74 #define __PRETTY_FUNCTION__ __FUNCTION__
75 #endif /* !__GNUC__ */
76 
77 VCLXTabControl::VCLXTabControl()
78   : VCLXWindow()
79   , VCLXTabControl_Base()
80   , Box_Base()
81   , mTabId (1)
82   , bRealized (false)
83 {
84 #ifndef __SUNPRO_CC
85     OSL_TRACE ("\n********%s:%x", __PRETTY_FUNCTION__, this);
86 #endif
87     DBG_CTOR( VCLXTabControl, NULL );
88 }
89 
90 VCLXTabControl::~VCLXTabControl()
91 {
92     DBG_DTOR( VCLXTabControl, NULL );
93 }
94 
95 IMPLEMENT_2_FORWARD_XINTERFACE2( VCLXTabControl, VCLXWindow, Container, VCLXTabControl_Base );
96 
97 IMPLEMENT_FORWARD_XTYPEPROVIDER2( VCLXTabControl, VCLXWindow, VCLXTabControl_Base );
98 
99 void SAL_CALL VCLXTabControl::dispose( ) throw(uno::RuntimeException)
100 {
101     {
102         ::vos::OGuard aGuard( GetMutex() );
103 
104         EventObject aDisposeEvent;
105         aDisposeEvent.Source = W3K_EXPLICIT_CAST (*this);
106 //            maTabListeners.disposeAndClear( aDisposeEvent );
107     }
108 
109     VCLXWindow::dispose();
110 }
111 
112 #if 0
113 void SAL_CALL VCLXTabControl::addTabListener( const Reference< XTabListener >& listener ) throw (uno::RuntimeException)
114 {
115     if ( listener.is() )
116         maTabListeners.addInterface( listener );
117 }
118 
119 void SAL_CALL VCLXTabControl::removeTabListener( const Reference< XTabListener >& listener ) throw (uno::RuntimeException)
120 {
121     if ( listener.is() )
122         maTabListeners.removeInterface( listener );
123 }
124 #endif
125 
126 TabControl *VCLXTabControl::getTabControl() const throw (uno::RuntimeException)
127 {
128     TabControl *pTabControl = static_cast< TabControl* >( GetWindow() );
129     if ( pTabControl )
130         return pTabControl;
131     throw uno::RuntimeException();
132 }
133 
134 sal_Int32 SAL_CALL VCLXTabControl::insertTab() throw (uno::RuntimeException)
135 {
136     TabControl *pTabControl = getTabControl();
137     sal_uInt16 id = sal::static_int_cast< sal_uInt16 >( mTabId++ );
138     rtl::OUString title (RTL_CONSTASCII_USTRINGPARAM( "" ) );
139     pTabControl->InsertPage( id, title.getStr(), TAB_APPEND );
140     pTabControl->SetTabPage( id, new TabPage( pTabControl ) );
141     return id;
142 }
143 
144 void SAL_CALL VCLXTabControl::removeTab( sal_Int32 ID ) throw (uno::RuntimeException, IndexOutOfBoundsException)
145 {
146     TabControl *pTabControl = getTabControl();
147     if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == NULL )
148         throw IndexOutOfBoundsException();
149     pTabControl->RemovePage( sal::static_int_cast< sal_uInt16 >( ID ) );
150 }
151 
152 void SAL_CALL VCLXTabControl::activateTab( sal_Int32 ID ) throw (uno::RuntimeException, IndexOutOfBoundsException)
153 {
154     TabControl *pTabControl = getTabControl();
155     if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == NULL )
156         throw IndexOutOfBoundsException();
157     pTabControl->SelectTabPage( sal::static_int_cast< sal_uInt16 >( ID ) );
158 }
159 
160 sal_Int32 SAL_CALL VCLXTabControl::getActiveTabID() throw (uno::RuntimeException)
161 {
162     return getTabControl()->GetCurPageId( );
163 }
164 
165 void SAL_CALL VCLXTabControl::addTabListener( const uno::Reference< awt::XTabListener >& xListener ) throw (uno::RuntimeException)
166 {
167     for ( std::list< uno::Reference
168               < awt::XTabListener > >::const_iterator it
169               = mxTabListeners.begin(); it != mxTabListeners.end(); it++ )
170     {
171         if ( *it == xListener )
172             // already added
173             return;
174     }
175     mxTabListeners.push_back( xListener );
176 }
177 
178 void SAL_CALL VCLXTabControl::removeTabListener( const uno::Reference< awt::XTabListener >& xListener ) throw (uno::RuntimeException)
179 {
180     for ( std::list< uno::Reference
181               < awt::XTabListener > >::iterator it
182               = mxTabListeners.begin(); it != mxTabListeners.end(); it++ )
183     {
184         if ( *it == xListener )
185         {
186             mxTabListeners.erase( it );
187             break;
188         }
189     }
190 }
191 
192 void SAL_CALL VCLXTabControl::setTabProps( sal_Int32 ID, const uno::Sequence< NamedValue >& Properties ) throw (uno::RuntimeException, IndexOutOfBoundsException)
193 {
194     TabControl *pTabControl = getTabControl();
195     if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == NULL )
196         throw IndexOutOfBoundsException();
197 
198     for ( int i = 0; i < Properties.getLength(); i++ )
199     {
200         const rtl::OUString &name = Properties[i].Name;
201         const uno::Any &value = Properties[i].Value;
202 
203         if ( name  == rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ) )
204         {
205             rtl::OUString title = value.get<rtl::OUString>();
206             pTabControl->SetPageText( sal::static_int_cast< sal_uInt16 >( ID ), title.getStr() );
207         }
208     }
209 }
210 
211 uno::Sequence< NamedValue > SAL_CALL VCLXTabControl::getTabProps( sal_Int32 ID )
212     throw (IndexOutOfBoundsException, uno::RuntimeException)
213 {
214     TabControl *pTabControl = getTabControl();
215     if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( ID ) ) == NULL )
216         throw IndexOutOfBoundsException();
217 
218 #define ADD_PROP( seq, i, name, val ) {                                \
219         NamedValue value;                                                  \
220         value.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( name ) ); \
221         value.Value = uno::makeAny( val );                                      \
222         seq[i] = value;                                                    \
223     }
224 
225     uno::Sequence< NamedValue > props( 2 );
226     ADD_PROP( props, 0, "Title", rtl::OUString( pTabControl->GetPageText( sal::static_int_cast< sal_uInt16 >( ID ) ) ) );
227     ADD_PROP( props, 1, "Position", pTabControl->GetPagePos( sal::static_int_cast< sal_uInt16 >( ID ) ) );
228 #undef ADD_PROP
229     return props;
230 }
231 
232 // TODO: draw tab border here
233 void SAL_CALL VCLXTabControl::draw( sal_Int32 nX, sal_Int32 nY ) throw(uno::RuntimeException)
234 {
235     ::vos::OGuard aGuard( GetMutex() );
236 
237     TabControl *pTabControl = getTabControl();
238     TabPage *pTabPage = pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >(  getActiveTabID() ) );
239     if ( pTabPage )
240     {
241         ::Point aPos( nX, nY );
242         ::Size  aSize = pTabPage->GetSizePixel();
243 
244 		OutputDevice* pDev = VCLUnoHelper::GetOutputDevice( getGraphics() );
245         aPos  = pDev->PixelToLogic( aPos );
246         aSize = pDev->PixelToLogic( aSize );
247 
248         pTabPage->Draw( pDev, aPos, aSize, 0 );
249     }
250 
251     VCLXWindow::draw( nX, nY );
252 }
253 
254 void VCLXTabControl::AddChild (uno::Reference< awt::XLayoutConstrains > const& xChild)
255 
256 {
257 #ifndef __SUNPRO_CC
258     OSL_TRACE ("%s: children: %d", __PRETTY_FUNCTION__, maChildren.size ());
259 #endif
260     mIdMap[ xChild ] = mTabId++;
261     Box_Base::AddChild( xChild );
262 #ifndef __SUNPRO_CC
263     OSL_TRACE ("%s: children: %d", __PRETTY_FUNCTION__, maChildren.size ());
264 #endif
265 }
266 
267 void SAL_CALL VCLXTabControl::addChild(
268     const uno::Reference< awt::XLayoutConstrains > &xChild )
269     throw (uno::RuntimeException, awt::MaxChildrenException)
270 {
271     mIdMap[ xChild ] = insertTab();
272     Box_Base::addChild( xChild );
273 }
274 
275 void SAL_CALL VCLXTabControl::removeChild( const uno::Reference< awt::XLayoutConstrains > &xChild )
276     throw (uno::RuntimeException)
277 {
278     removeTab( mIdMap[xChild] );
279     mIdMap[ xChild ] = -1;
280     Box_Base::removeChild( xChild );
281 }
282 
283 static void setChildrenVisible( uno::Reference < awt::XLayoutConstrains > xChild, bool visible )
284 {
285     uno::Reference< awt::XWindow > xWin( xChild, uno::UNO_QUERY);
286     if ( xWin.is() )
287     {
288         xWin->setVisible( visible );
289     }
290 
291     uno::Reference < awt::XLayoutContainer > xCont( xChild, uno::UNO_QUERY );
292     if ( xCont.is())
293     {
294         uno::Sequence< uno::Reference < awt::XLayoutConstrains > > children = xCont->getChildren();
295         for ( int i = 0; i < children.getLength(); i++ )
296         {
297             setChildrenVisible( children[i], visible );
298         }
299     }
300 }
301 
302 void SAL_CALL VCLXTabControl::allocateArea (awt::Rectangle const &area)
303     throw (uno::RuntimeException)
304 {
305 #ifndef __SUNPRO_CC
306     OSL_TRACE ("\n%s", __PRETTY_FUNCTION__);
307 #endif
308     maAllocation = area;
309 
310     TabControl *pTabControl = getTabControl();
311 
312 // FIXME: this is wrong. We just want to set tab controls pos/size for
313 // the tabs menu, otherwise, it gets events that should go to children
314 // (I guess we could solve this by making the tabcontrol as the actual
315 // XWindow parent of its children, when importing...)  Not sure about
316 // TabPage drawing... That doesn't work on gtk+; just ignoring that.
317 // LATER: Nah, the proper fix is to get the XWindow hierarchy
318 // straight.
319 
320 #if 0
321     setPosSize( area.X, area.Y, area.Width, area.Height, awt::PosSize::POSSIZE );
322 #else
323     awt::Size currentSize = getSize();
324     awt::Size requestedSize (area.Width, area.Height);
325 //    requestedSize.Height = getHeightForWidth( area.Width );
326 
327     awt::Size minimumSize = getMinimumSize();
328     if (requestedSize.Width < minimumSize.Width)
329         requestedSize.Width = minimumSize.Width;
330     if (requestedSize.Height < minimumSize.Height)
331         requestedSize.Height = minimumSize.Height;
332 
333     Size pageSize = static_cast<TabControl*> (GetWindow ())->GetTabPageSizePixel ();
334     awt::Size pageBasedSize (0, 0);
335     pageBasedSize.Width = pageSize.Width ();
336     pageBasedSize.Height = pageSize.Height ();
337 
338     const int wc = 0;
339     const int hc = 20;
340     static int pwc = 0;
341     static int phc = 40;
342 
343     if (requestedSize.Width < pageBasedSize.Width)
344         requestedSize.Width = pageBasedSize.Width + wc;
345     if (requestedSize.Height < pageBasedSize.Height)
346         requestedSize.Height = pageBasedSize.Height + hc;
347 
348     Size windowSize = GetWindow()->GetSizePixel();
349     Window *parent = GetWindow()->GetParent();
350     Size parentSize = parent->GetSizePixel();
351 
352 #ifndef __SUNPRO_CC
353 #ifdef GCC_MAJOR
354     OSL_TRACE ("\n%s", __PRETTY_FUNCTION__);
355 #endif /* GCC_MAJOR */
356     OSL_TRACE ("%s: cursize: %d ,%d", __FUNCTION__, currentSize.Width, currentSize.Height );
357     OSL_TRACE ("%s: area: %d, %d", __FUNCTION__, area.Width, area.Height );
358     OSL_TRACE ("%s: minimum: %d, %d", __FUNCTION__, minimumSize.Width, minimumSize.Height );
359     OSL_TRACE ("%s: requestedSize: %d, %d", __FUNCTION__, requestedSize.Width, requestedSize.Height );
360     OSL_TRACE ("%s: pageBasedSize: %d, %d", __FUNCTION__, pageBasedSize.Width, pageBasedSize.Height );
361 
362     //OSL_TRACE ("%s: parent: %d, %d", __FUNCTION__, parentSize.Width(), parentSize.Height() );
363     //OSL_TRACE ("%s: window: %d, %d", __FUNCTION__, windowSize.Width(), windowSize.Height() );
364 #endif
365 
366 	//bRealized = false;
367     if (!bRealized)
368     {
369         setPosSize( area.X, area.Y, requestedSize.Width, requestedSize.Height, awt::PosSize::POSSIZE );
370         bRealized = true;
371     }
372     else
373     {
374         if ( requestedSize.Width > currentSize.Width + 10)
375             setPosSize( 0, 0, requestedSize.Width, 0, awt::PosSize::WIDTH );
376         if ( requestedSize.Height > currentSize.Height + 10)
377             setPosSize( 0, 0, 0, requestedSize.Height, awt::PosSize::HEIGHT );
378     }
379 #endif
380 
381     if (pageBasedSize.Width > parentSize.Width ()
382         || pageBasedSize.Height > parentSize.Height ())
383         //parent->SetSizePixel ( Size (pageBasedSize.Width, pageBasedSize.Height));
384         //parent->SetSizePixel ( Size (pageBasedSize.Width + pwc, pageBasedSize.Height + phc));
385         parent->SetSizePixel ( Size (requestedSize.Width + pwc, requestedSize.Height + phc));
386 
387     // FIXME: we can save cycles by setting visibility more sensibly. Having
388     // it here does makes it easier when changing tabs (just needs a recalc())
389     unsigned i = 0;
390     for ( std::list<Box_Base::ChildData *>::const_iterator it
391               = maChildren.begin(); it != maChildren.end(); it++, i++ )
392     {
393         ChildData *child = static_cast<VCLXTabControl::ChildData*> ( *it );
394         uno::Reference
395               < awt::XLayoutConstrains > xChild( child->mxChild );
396         if ( xChild.is() )
397         {
398             uno::Reference< awt::XWindow > xWin( xChild, uno::UNO_QUERY );
399             bool active = (i+1 == (unsigned) getActiveTabID());
400 
401             // HACK: since our layout:: container don't implement XWindow, we have no easy
402             // way to set them invisible; lets just set all their children as such :P
403 #if 0
404             if ( xWin.is() )
405                 xWin->setVisible( active );
406 #else
407             setChildrenVisible( xChild, active );
408 #endif
409 
410             if ( active )
411             {
412                 ::Rectangle label_rect = pTabControl->GetTabBounds( sal::static_int_cast< sal_uInt16 >( i+1 ) );
413                 ::Rectangle page_rect = pTabControl->GetTabPageBounds( sal::static_int_cast< sal_uInt16 >( i+1 ) );
414 
415                 awt::Rectangle childRect;
416                 childRect.X = page_rect.Left();
417                 childRect.Y = SAL_MAX( label_rect.Bottom(), page_rect.Top() );
418                 childRect.Width = page_rect.Right() - page_rect.Left();
419                 childRect.Height = page_rect.Bottom() - childRect.Y;
420 
421                 allocateChildAt( xChild, childRect );
422             }
423         }
424     }
425 }
426 
427 awt::Size SAL_CALL VCLXTabControl::getMinimumSize()
428     throw(uno::RuntimeException)
429 {
430     awt::Size requestedSize = VCLXWindow::getMinimumSize();
431     awt::Size childrenSize( 0, 0 );
432 
433     TabControl* pTabControl = static_cast< TabControl* >( GetWindow() );
434     if ( !pTabControl )
435         return requestedSize;
436 
437     // calculate size to accomodate all children
438     unsigned i = 0;
439     for ( std::list<Box_Base::ChildData *>::const_iterator it
440               = maChildren.begin(); it != maChildren.end(); it++, i++ )
441     {
442         ChildData *child = static_cast<VCLXTabControl::ChildData*> ( *it );
443         if ( child->mxChild.is() )
444         {
445             // set the title prop here...
446             pTabControl->SetPageText( sal::static_int_cast< sal_uInt16 >( i+1 ), child->maTitle.getStr() );
447 
448             awt::Size childSize( child->mxChild->getMinimumSize() );
449             childrenSize.Width = SAL_MAX( childSize.Width, childrenSize.Width );
450             childrenSize.Height = SAL_MAX( childSize.Height, childrenSize.Height );
451         }
452     }
453 
454 #ifndef __SUNPRO_CC
455 #ifdef GCC_MAJOR
456     OSL_TRACE ("\n%s", __PRETTY_FUNCTION__);
457 #endif /* GCC_MAJOR */
458     OSL_TRACE ("%s: children: %d", __FUNCTION__, i);
459     OSL_TRACE ("%s: childrenSize: %d, %d", __FUNCTION__, childrenSize.Width, childrenSize.Height );
460 #endif
461 
462     requestedSize.Width += childrenSize.Width;
463     requestedSize.Height += childrenSize.Height + 20;
464 
465     maRequisition = requestedSize;
466     return requestedSize;
467 }
468 
469 void VCLXTabControl::ProcessWindowEvent( const VclWindowEvent& _rVclWindowEvent )
470 {
471     ::vos::OClearableGuard aGuard( GetMutex() );
472     TabControl* pTabControl = static_cast< TabControl* >( GetWindow() );
473     if ( !pTabControl )
474         return;
475 
476     switch ( _rVclWindowEvent.GetId() )
477     {
478         case VCLEVENT_TABPAGE_ACTIVATE:
479             forceRecalc();
480         case VCLEVENT_TABPAGE_DEACTIVATE:
481         case VCLEVENT_TABPAGE_INSERTED:
482         case VCLEVENT_TABPAGE_REMOVED:
483         case VCLEVENT_TABPAGE_REMOVEDALL:
484         case VCLEVENT_TABPAGE_PAGETEXTCHANGED:
485         {
486             sal_uLong page = (sal_uLong) _rVclWindowEvent.GetData();
487             for ( std::list< uno::Reference
488                       < awt::XTabListener > >::iterator it
489                       = mxTabListeners.begin(); it != mxTabListeners.end(); it++)
490             {
491                 uno::Reference
492                     < awt::XTabListener > listener = *it;
493 
494                 switch ( _rVclWindowEvent.GetId() )
495                 {
496 
497                     case VCLEVENT_TABPAGE_ACTIVATE:
498                         listener->activated( page );
499                         break;
500                     case VCLEVENT_TABPAGE_DEACTIVATE:
501                         listener->deactivated( page );
502                         break;
503                     case VCLEVENT_TABPAGE_INSERTED:
504                         listener->inserted( page );
505                         break;
506                     case VCLEVENT_TABPAGE_REMOVED:
507                         listener->removed( page );
508                         break;
509                     case VCLEVENT_TABPAGE_REMOVEDALL:
510                         for ( int i = 1; i < mTabId; i++)
511                         {
512                             if ( pTabControl->GetTabPage( sal::static_int_cast< sal_uInt16 >( i ) ) )
513                                 listener->removed( i );
514                         }
515                         break;
516                     case VCLEVENT_TABPAGE_PAGETEXTCHANGED:
517                         listener->changed( page, getTabProps( page ) );
518                         break;
519                 }
520             }
521             break;
522         }
523         default:
524             aGuard.clear();
525             VCLXWindow::ProcessWindowEvent( _rVclWindowEvent );
526             break;
527     }
528 }
529 
530 void SAL_CALL VCLXTabControl::setProperty( const ::rtl::OUString& PropertyName, const uno::Any &Value ) throw(uno::RuntimeException)
531 {
532     VCLXWindow::setProperty( PropertyName, Value );
533 }
534 
535 uno::Any SAL_CALL VCLXTabControl::getProperty( const ::rtl::OUString& PropertyName ) throw(uno::RuntimeException)
536 {
537     return VCLXWindow::getProperty( PropertyName );
538 }
539 
540 } // namespace layoutimpl
541