blob: 18d3bcf71b90aabfdd8b179b303644ddec6d81c7 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sd.hxx"
#include "ViewTabBar.hxx"
#define USE_TAB_CONTROL
#include "ViewShell.hxx"
#include "ViewShellBase.hxx"
#include "DrawViewShell.hxx"
#include "FrameView.hxx"
#include "EventMultiplexer.hxx"
#include "framework/FrameworkHelper.hxx"
#include "framework/Pane.hxx"
#include "DrawController.hxx"
#include "sdresid.hxx"
#include "strings.hrc"
#include "helpids.h"
#include "Client.hxx"
#include <vcl/svapp.hxx>
#include <vcl/tabpage.hxx>
#include <vos/mutex.hxx>
#include <sfx2/viewfrm.hxx>
#include <com/sun/star/drawing/framework/ResourceId.hpp>
#include <com/sun/star/drawing/framework/XControllerManager.hpp>
#include <com/sun/star/lang/XUnoTunnel.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <comphelper/processfactory.hxx>
#include <tools/diagnose_ex.h>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing::framework;
using ::sd::framework::FrameworkHelper;
using ::sd::tools::EventMultiplexerEvent;
using ::rtl::OUString;
namespace sd {
namespace {
bool IsEqual (const TabBarButton& rButton1, const TabBarButton& rButton2)
{
return (
(rButton1.ResourceId.is()
&& rButton2.ResourceId.is()
&& rButton1.ResourceId->compareTo(rButton2.ResourceId)==0)
|| rButton1.ButtonLabel == rButton2.ButtonLabel);
}
class TabBarControl : public ::TabControl
{
public:
TabBarControl (
::Window* pParentWindow,
const ::rtl::Reference<ViewTabBar>& rpViewTabBar);
virtual void Paint (const Rectangle& rRect);
virtual void ActivatePage (void);
private:
::rtl::Reference<ViewTabBar> mpViewTabBar;
};
} // end of anonymous namespace
class ViewTabPage : public TabPage
{
public:
ViewTabPage (Window* pParent) : TabPage(pParent) {}
virtual void Resize (void)
{ SetPosSizePixel(Point(0,0),GetParent()->GetOutputSizePixel()); }
};
//===== ViewTabBar ============================================================
ViewTabBar::ViewTabBar (
const Reference<XResourceId>& rxViewTabBarId,
const Reference<frame::XController>& rxController)
: ViewTabBarInterfaceBase(maMutex),
mpTabControl(new TabBarControl(GetAnchorWindow(rxViewTabBarId,rxController), this)),
mxController(rxController),
maTabBarButtons(),
mpTabPage(NULL),
mxViewTabBarId(rxViewTabBarId),
mpViewShellBase(NULL)
{
// Set one new tab page for all tab entries. We need it only to
// determine the height of the tab bar.
mpTabPage.reset(new TabPage (mpTabControl.get()));
mpTabPage->Hide();
// add some space before the tabitems
mpTabControl->SetItemsOffset(Point(5, 3));
// Tunnel through the controller and use the ViewShellBase to obtain the
// view frame.
try
{
Reference<lang::XUnoTunnel> xTunnel (mxController, UNO_QUERY_THROW);
DrawController* pController = reinterpret_cast<DrawController*>(
xTunnel->getSomething(DrawController::getUnoTunnelId()));
mpViewShellBase = pController->GetViewShellBase();
}
catch(RuntimeException&)
{}
// Register as listener at XConfigurationController.
Reference<XControllerManager> xControllerManager (mxController, UNO_QUERY);
if (xControllerManager.is())
{
mxConfigurationController = xControllerManager->getConfigurationController();
if (mxConfigurationController.is())
{
mxConfigurationController->addConfigurationChangeListener(
this,
FrameworkHelper::msResourceActivationEvent,
Any());
}
}
mpTabControl->Show();
if (mpViewShellBase != NULL
&& rxViewTabBarId->isBoundToURL(
FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT))
{
mpViewShellBase->SetViewTabBar(this);
}
}
ViewTabBar::~ViewTabBar (void)
{
}
void ViewTabBar::disposing (void)
{
if (mpViewShellBase != NULL
&& mxViewTabBarId->isBoundToURL(
FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT))
{
mpViewShellBase->SetViewTabBar(NULL);
}
if (mxConfigurationController.is())
{
// Unregister listener from XConfigurationController.
try
{
mxConfigurationController->removeConfigurationChangeListener(this);
}
catch (lang::DisposedException e)
{
// Receiving a disposed exception is the normal case. Is there
// a way to avoid it?
}
mxConfigurationController = NULL;
}
{
const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
// Set all references to the one tab page to NULL and delete the page.
for (sal_uInt16 nIndex=0; nIndex<mpTabControl->GetPageCount(); ++nIndex)
mpTabControl->SetTabPage(nIndex, NULL);
mpTabPage.reset();
mpTabControl.reset();
}
mxController = NULL;
}
::boost::shared_ptr< ::TabControl> ViewTabBar::GetTabControl (void) const
{
return mpTabControl;
}
::Window* ViewTabBar::GetAnchorWindow(
const Reference<XResourceId>& rxViewTabBarId,
const Reference<frame::XController>& rxController)
{
::Window* pWindow = NULL;
ViewShellBase* pBase = NULL;
// Tunnel through the controller and use the ViewShellBase to obtain the
// view frame.
try
{
Reference<lang::XUnoTunnel> xTunnel (rxController, UNO_QUERY_THROW);
DrawController* pController = reinterpret_cast<DrawController*>(
xTunnel->getSomething(DrawController::getUnoTunnelId()));
pBase = pController->GetViewShellBase();
}
catch(RuntimeException&)
{}
// The ViewTabBar supports at the moment only the center pane.
if (rxViewTabBarId.is()
&& rxViewTabBarId->isBoundToURL(
FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT))
{
if (pBase != NULL && pBase->GetViewFrame() != NULL)
pWindow = &pBase->GetViewFrame()->GetWindow();
}
// The rest is (at the moment) just for the emergency case.
if (pWindow == NULL)
{
Reference<XPane> xPane;
try
{
Reference<XControllerManager> xControllerManager (rxController, UNO_QUERY_THROW);
Reference<XConfigurationController> xCC (
xControllerManager->getConfigurationController());
if (xCC.is())
xPane = Reference<XPane>(xCC->getResource(rxViewTabBarId->getAnchor()), UNO_QUERY);
}
catch (RuntimeException&)
{}
// Tunnel through the XWindow to the VCL side.
try
{
Reference<lang::XUnoTunnel> xTunnel (xPane, UNO_QUERY_THROW);
framework::Pane* pPane = reinterpret_cast<framework::Pane*>(
xTunnel->getSomething(framework::Pane::getUnoTunnelId()));
if (pPane != NULL)
pWindow = pPane->GetWindow()->GetParent();
}
catch (RuntimeException&)
{}
}
return pWindow;
}
//----- XConfigurationChangeListener ------------------------------------------
void SAL_CALL ViewTabBar::notifyConfigurationChange (
const ConfigurationChangeEvent& rEvent)
throw (RuntimeException)
{
if (rEvent.Type.equals(FrameworkHelper::msResourceActivationEvent)
&& rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)
&& rEvent.ResourceId->isBoundTo(mxViewTabBarId->getAnchor(), AnchorBindingMode_DIRECT))
{
UpdateActiveButton();
}
}
//----- XEventListener --------------------------------------------------------
void SAL_CALL ViewTabBar::disposing(
const lang::EventObject& rEvent)
throw (RuntimeException)
{
if (rEvent.Source == mxConfigurationController)
{
mxConfigurationController = NULL;
mxController = NULL;
}
}
//----- XTabBar ---------------------------------------------------------------
void SAL_CALL ViewTabBar::addTabBarButtonAfter (
const TabBarButton& rButton,
const TabBarButton& rAnchor)
throw (::com::sun::star::uno::RuntimeException)
{
const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
AddTabBarButton(rButton, rAnchor);
}
void SAL_CALL ViewTabBar::appendTabBarButton (const TabBarButton& rButton)
throw (::com::sun::star::uno::RuntimeException)
{
const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
AddTabBarButton(rButton);
}
void SAL_CALL ViewTabBar::removeTabBarButton (const TabBarButton& rButton)
throw (::com::sun::star::uno::RuntimeException)
{
const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
RemoveTabBarButton(rButton);
}
sal_Bool SAL_CALL ViewTabBar::hasTabBarButton (const TabBarButton& rButton)
throw (::com::sun::star::uno::RuntimeException)
{
const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
return HasTabBarButton(rButton);
}
Sequence<TabBarButton> SAL_CALL ViewTabBar::getTabBarButtons (void)
throw (::com::sun::star::uno::RuntimeException)
{
const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
return GetTabBarButtons();
}
//----- XResource -------------------------------------------------------------
Reference<XResourceId> SAL_CALL ViewTabBar::getResourceId (void)
throw (RuntimeException)
{
return mxViewTabBarId;
}
sal_Bool SAL_CALL ViewTabBar::isAnchorOnly (void)
throw (RuntimeException)
{
return false;
}
//----- XUnoTunnel ------------------------------------------------------------
const Sequence<sal_Int8>& ViewTabBar::getUnoTunnelId (void)
{
static Sequence<sal_Int8>* pSequence = NULL;
if (pSequence == NULL)
{
const ::vos::OGuard aSolarGuard (Application::GetSolarMutex());
if (pSequence == NULL)
{
static ::com::sun::star::uno::Sequence<sal_Int8> aSequence (16);
rtl_createUuid((sal_uInt8*)aSequence.getArray(), 0, sal_True);
pSequence = &aSequence;
}
}
return *pSequence;
}
sal_Int64 SAL_CALL ViewTabBar::getSomething (const Sequence<sal_Int8>& rId)
throw (RuntimeException)
{
sal_Int64 nResult = 0;
if (rId.getLength() == 16
&& rtl_compareMemory(getUnoTunnelId().getConstArray(), rId.getConstArray(), 16) == 0)
{
nResult = reinterpret_cast<sal_Int64>(this);
}
return nResult;
}
//-----------------------------------------------------------------------------
bool ViewTabBar::ActivatePage (void)
{
try
{
Reference<XControllerManager> xControllerManager (mxController,UNO_QUERY_THROW);
Reference<XConfigurationController> xConfigurationController (
xControllerManager->getConfigurationController());
if ( ! xConfigurationController.is())
throw RuntimeException();
Reference<XView> xView;
try
{
xView = Reference<XView>(xConfigurationController->getResource(
ResourceId::create(
::comphelper::getProcessComponentContext(),
FrameworkHelper::msCenterPaneURL)),
UNO_QUERY);
}
catch (DeploymentException)
{
}
Client* pIPClient = NULL;
if (mpViewShellBase != NULL)
pIPClient = dynamic_cast<Client*>(mpViewShellBase->GetIPClient());
if (pIPClient==NULL || ! pIPClient->IsObjectInPlaceActive())
{
sal_uInt16 nIndex (mpTabControl->GetCurPageId() - 1);
if (nIndex < maTabBarButtons.size())
{
xConfigurationController->requestResourceActivation(
maTabBarButtons[nIndex].ResourceId,
ResourceActivationMode_REPLACE);
}
return true;
}
else
{
// When we run into this else branch then we have an active OLE
// object. We ignore the request to switch views. Additionally
// we put the active tab back to the one for the current view.
UpdateActiveButton();
}
}
catch (RuntimeException&)
{
DBG_UNHANDLED_EXCEPTION();
}
return false;
}
int ViewTabBar::GetHeight (void)
{
int nHeight (0);
if (!maTabBarButtons.empty())
{
TabPage* pActivePage (mpTabControl->GetTabPage(
mpTabControl->GetCurPageId()));
if (pActivePage!=NULL && mpTabControl->IsReallyVisible())
nHeight = pActivePage->GetPosPixel().Y();
if (nHeight <= 0)
// Using a default when the real height can not be determined.
// To get correct height this method should be called when the
// control is visible.
nHeight = 21;
}
return nHeight;
}
void ViewTabBar::AddTabBarButton (
const ::com::sun::star::drawing::framework::TabBarButton& rButton,
const ::com::sun::star::drawing::framework::TabBarButton& rAnchor)
{
sal_uInt32 nIndex;
if ( ! rAnchor.ResourceId.is()
|| (rAnchor.ResourceId->getResourceURL().getLength() == 0
&& rAnchor.ButtonLabel.getLength() == 0))
{
nIndex = 0;
}
else
{
for (nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex)
{
if (IsEqual(maTabBarButtons[nIndex], rAnchor))
{
++nIndex;
break;
}
}
}
AddTabBarButton(rButton,nIndex);
}
void ViewTabBar::AddTabBarButton (
const ::com::sun::star::drawing::framework::TabBarButton& rButton)
{
AddTabBarButton(rButton, maTabBarButtons.size());
}
void ViewTabBar::AddTabBarButton (
const ::com::sun::star::drawing::framework::TabBarButton& rButton,
sal_Int32 nPosition)
{
if (nPosition>=0
&& nPosition<=mpTabControl->GetPageCount())
{
sal_uInt16 nIndex ((sal_uInt16)nPosition);
// Insert the button into our local array.
maTabBarButtons.insert(maTabBarButtons.begin()+nIndex, rButton);
UpdateTabBarButtons();
UpdateActiveButton();
}
}
void ViewTabBar::RemoveTabBarButton (
const ::com::sun::star::drawing::framework::TabBarButton& rButton)
{
sal_uInt16 nIndex;
for (nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex)
{
if (IsEqual(maTabBarButtons[nIndex], rButton))
{
maTabBarButtons.erase(maTabBarButtons.begin()+nIndex);
UpdateTabBarButtons();
UpdateActiveButton();
break;
}
}
}
bool ViewTabBar::HasTabBarButton (
const ::com::sun::star::drawing::framework::TabBarButton& rButton)
{
bool bResult (false);
for (sal_uInt32 nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex)
{
if (IsEqual(maTabBarButtons[nIndex], rButton))
{
bResult = true;
break;
}
}
return bResult;
}
::com::sun::star::uno::Sequence<com::sun::star::drawing::framework::TabBarButton>
ViewTabBar::GetTabBarButtons (void)
{
sal_uInt32 nCount (maTabBarButtons.size());
::com::sun::star::uno::Sequence<com::sun::star::drawing::framework::TabBarButton>
aList (nCount);
for (sal_uInt32 nIndex=0; nIndex<nCount; ++nIndex)
aList[nIndex] = maTabBarButtons[nIndex];
return aList;
}
void ViewTabBar::UpdateActiveButton (void)
{
Reference<XView> xView;
if (mpViewShellBase != NULL)
xView = FrameworkHelper::Instance(*mpViewShellBase)->GetView(
mxViewTabBarId->getAnchor());
if (xView.is())
{
Reference<XResourceId> xViewId (xView->getResourceId());
for (sal_uInt16 nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex)
{
if (maTabBarButtons[nIndex].ResourceId->compareTo(xViewId) == 0)
{
mpTabControl->SetCurPageId(nIndex+1);
mpTabControl->::TabControl::ActivatePage();
break;
}
}
}
}
void ViewTabBar::UpdateTabBarButtons (void)
{
TabBarButtonList::const_iterator iTab;
sal_uInt16 nPageCount (mpTabControl->GetPageCount());
sal_uInt16 nIndex;
for (iTab=maTabBarButtons.begin(),nIndex=1; iTab!=maTabBarButtons.end(); ++iTab,++nIndex)
{
// Create a new tab when there are not enough.
if (nPageCount < nIndex)
mpTabControl->InsertPage(nIndex, iTab->ButtonLabel);
// Update the tab.
mpTabControl->SetPageText(nIndex, iTab->ButtonLabel);
mpTabControl->SetHelpText(nIndex, iTab->HelpText);
mpTabControl->SetTabPage(nIndex, mpTabPage.get());
}
// Delete tabs that are no longer used.
for (; nIndex<=nPageCount; ++nIndex)
mpTabControl->RemovePage(nIndex);
mpTabPage->Hide();
}
//===== TabBarControl =========================================================
TabBarControl::TabBarControl (
::Window* pParentWindow,
const ::rtl::Reference<ViewTabBar>& rpViewTabBar)
: ::TabControl(pParentWindow),
mpViewTabBar(rpViewTabBar)
{
}
void TabBarControl::Paint (const Rectangle& rRect)
{
Color aOriginalFillColor (GetFillColor());
Color aOriginalLineColor (GetLineColor());
// Because the actual window background is transparent--to avoid
// flickering due to multiple background paintings by this and by child
// windows--we have to paint the background for this control explicitly:
// the actual control is not painted over its whole bounding box.
SetFillColor (GetSettings().GetStyleSettings().GetDialogColor());
SetLineColor ();
DrawRect (rRect);
::TabControl::Paint (rRect);
SetFillColor (aOriginalFillColor);
SetLineColor (aOriginalLineColor);
}
void TabBarControl::ActivatePage (void)
{
if (mpViewTabBar->ActivatePage())
{
// Call the parent so that the correct tab is highlighted.
this->::TabControl::ActivatePage();
}
}
} // end of namespace sd