summaryrefslogtreecommitdiff
path: root/vcl/unx
diff options
context:
space:
mode:
authorKatarina Behrens <Katarina.Behrens@cib.de>2017-08-28 18:29:20 +0200
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2017-10-25 03:23:12 +0200
commitdd7a3147f160813022d1c2724bbdf7fc46ffcc59 (patch)
tree73398c2d5aadd6366c131d27fa824e61cb215246 /vcl/unx
parent07cd70c86c8919107ef02dc12ea23ae8d72d5a1a (diff)
kde5: copy basic kde4 blocks -> kde5 and build againt qt5/kf5 libs
Change-Id: I70f0c4147721a20459e1183ff40cf0ac8adf49e6
Diffstat (limited to 'vcl/unx')
-rw-r--r--vcl/unx/kde5/FPServiceInfo.hxx28
-rw-r--r--vcl/unx/kde5/KDE5Data.cxx68
-rw-r--r--vcl/unx/kde5/KDE5Data.hxx37
-rw-r--r--vcl/unx/kde5/KDE5SalDisplay.cxx98
-rw-r--r--vcl/unx/kde5/KDE5SalDisplay.hxx47
-rw-r--r--vcl/unx/kde5/KDE5SalFrame.cxx381
-rw-r--r--vcl/unx/kde5/KDE5SalFrame.hxx53
-rw-r--r--vcl/unx/kde5/KDE5SalGraphics.cxx1020
-rw-r--r--vcl/unx/kde5/KDE5SalGraphics.hxx53
-rw-r--r--vcl/unx/kde5/KDE5SalInstance.cxx70
-rw-r--r--vcl/unx/kde5/KDE5SalInstance.hxx45
-rw-r--r--vcl/unx/kde5/KDE5XLib.cxx407
-rw-r--r--vcl/unx/kde5/KDE5XLib.hxx102
-rw-r--r--vcl/unx/kde5/VCLKDE5Application.cxx75
-rw-r--r--vcl/unx/kde5/VCLKDE5Application.hxx40
-rw-r--r--vcl/unx/kde5/main.cxx88
-rw-r--r--vcl/unx/kde5/tst_exclude_posted_events.hxx67
-rw-r--r--vcl/unx/kde5/tst_exclude_socket_notifiers.hxx80
18 files changed, 2759 insertions, 0 deletions
diff --git a/vcl/unx/kde5/FPServiceInfo.hxx b/vcl/unx/kde5/FPServiceInfo.hxx
new file mode 100644
index 000000000000..fdb285144343
--- /dev/null
+++ b/vcl/unx/kde5/FPServiceInfo.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+// the service names
+#define FILE_PICKER_SERVICE_NAME "com.sun.star.ui.dialogs.KDE4FilePicker"
+
+// the implementation names
+#define FILE_PICKER_IMPL_NAME "com.sun.star.ui.dialogs.KDE4FilePicker"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5Data.cxx b/vcl/unx/kde5/KDE5Data.cxx
new file mode 100644
index 000000000000..487a0b254af3
--- /dev/null
+++ b/vcl/unx/kde5/KDE5Data.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <QtWidgets/QStyle>
+#include <QtWidgets/QApplication>
+
+#undef Region
+
+#include "KDE5Data.hxx"
+
+#include "KDE5XLib.hxx"
+#include "KDE5SalDisplay.hxx"
+
+KDEData::~KDEData()
+{
+}
+
+void KDEData::Init()
+{
+ pXLib_ = new KDEXLib();
+ pXLib_->Init();
+ SetDisplay( SalKDEDisplay::self() );
+}
+
+void KDEData::initNWF()
+{
+ ImplSVData *pSVData = ImplGetSVData();
+
+ // draw toolbars on separate lines
+ pSVData->maNWFData.mbDockingAreaSeparateTB = true;
+ // no borders for menu, theming does that
+ pSVData->maNWFData.mbFlatMenu = true;
+ // Qt theme engines may support a rollover menubar
+ pSVData->maNWFData.mbRolloverMenubar = true;
+
+ pSVData->maNWFData.mbNoFocusRects = true;
+
+ // Styled menus need additional space
+ QStyle *style = QApplication::style();
+ pSVData->maNWFData.mnMenuFormatBorderX =
+ style->pixelMetric( QStyle::PM_MenuPanelWidth ) +
+ style->pixelMetric( QStyle::PM_MenuHMargin );
+ pSVData->maNWFData.mnMenuFormatBorderY =
+ style->pixelMetric( QStyle::PM_MenuPanelWidth ) +
+ style->pixelMetric( QStyle::PM_MenuVMargin );
+}
+
+void KDEData::deInitNWF()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5Data.hxx b/vcl/unx/kde5/KDE5Data.hxx
new file mode 100644
index 000000000000..4efd23a7df34
--- /dev/null
+++ b/vcl/unx/kde5/KDE5Data.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <unx/saldisp.hxx>
+#include <unx/saldata.hxx>
+
+class KDEData : public X11SalData
+{
+ public:
+ explicit KDEData( SalInstance *pInstance )
+ : X11SalData( SAL_DATA_KDE4, pInstance ) {}
+ virtual ~KDEData() override;
+
+ virtual void Init() override;
+ virtual void initNWF() override;
+ virtual void deInitNWF() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalDisplay.cxx b/vcl/unx/kde5/KDE5SalDisplay.cxx
new file mode 100644
index 000000000000..eddac063048f
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalDisplay.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "VCLKDE5Application.hxx"
+#include "KDE5SalDisplay.hxx"
+
+#ifdef Bool
+#undef Bool
+#endif
+
+#include "KDE5XLib.hxx"
+
+#include <assert.h>
+
+SalKDEDisplay* SalKDEDisplay::selfptr = nullptr;
+
+SalKDEDisplay::SalKDEDisplay( Display* pDisp )
+ : SalX11Display( pDisp )
+{
+ assert( selfptr == nullptr );
+ selfptr = this;
+ xim_protocol = XInternAtom( pDisp_, "_XIM_PROTOCOL", False );
+}
+
+SalKDEDisplay::~SalKDEDisplay()
+{
+ // in case never a frame opened
+ static_cast<KDEXLib*>(GetXLib())->doStartup();
+ // clean up own members
+ doDestruct();
+ selfptr = nullptr;
+ // prevent SalDisplay from closing KApplication's display
+ pDisp_ = nullptr;
+}
+
+void SalKDEDisplay::Yield()
+{
+ if( DispatchInternalEvent() )
+ return;
+
+ // Prevent blocking from Drag'n'Drop events, which may have already have processed the event
+ if (XEventsQueued( pDisp_, QueuedAfterReading ) == 0)
+ return;
+
+ DBG_ASSERT( static_cast<SalYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex())->GetThreadId() ==
+ osl::Thread::getCurrentIdentifier(),
+ "will crash soon since solar mutex not locked in SalKDEDisplay::Yield" );
+
+ /*XEvent event;
+ XNextEvent( pDisp_, &event );
+ if( checkDirectInputEvent( &event ))
+ return;
+ qApp->x11ProcessEvent( &event );*/
+}
+
+// HACK: When using Qt event loop, input methods (japanese, etc.) will get broken because
+// of XFilterEvent() getting called twice, once by Qt, once by LO (bnc#665112).
+// This function is therefore called before any XEvent is passed to Qt event handling
+// and if it is a keyboard event and no Qt widget is the active window (i.e. we are
+// processing events for some LO window), then feed the event only to LO directly and skip Qt
+// completely. Skipped events are KeyPress, KeyRelease and also _XIM_PROTOCOL client message
+// (seems to be necessary too, hopefully there are not other internal XIM messages that
+// would need this handling).
+bool SalKDEDisplay::checkDirectInputEvent( xcb_generic_event_t* ev )
+{
+ switch (ev->response_type & ~0x80)
+ {
+ case XCB_CLIENT_MESSAGE:
+ case XCB_KEY_PRESS:
+ case XCB_KEY_RELEASE:
+ if( QApplication::activeWindow() == nullptr )
+ {
+// Dispatch(ev);
+ return true;
+ }
+ break;
+ }
+ return false;
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalDisplay.hxx b/vcl/unx/kde5/KDE5SalDisplay.hxx
new file mode 100644
index 000000000000..0a31dcedc10d
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalDisplay.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <unx/saldisp.hxx>
+#include <xcb/xcb.h>
+
+#ifdef CursorShape
+#undef CursorShape
+#endif
+
+class SalKDEDisplay : public SalX11Display
+{
+ public:
+ explicit SalKDEDisplay( Display* pDisp );
+ virtual ~SalKDEDisplay() override;
+ static SalKDEDisplay* self();
+ virtual void Yield() override;
+ bool checkDirectInputEvent( xcb_generic_event_t* ev );
+ private:
+ Atom xim_protocol;
+ static SalKDEDisplay* selfptr;
+};
+
+inline SalKDEDisplay* SalKDEDisplay::self()
+{
+ return selfptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalFrame.cxx b/vcl/unx/kde5/KDE5SalFrame.cxx
new file mode 100644
index 000000000000..090abbc240d1
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalFrame.cxx
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <QtGui/QColor>
+#include <QtWidgets/QStyle>
+#include <QtCore/QDebug>
+#include <QtWidgets/QToolTip>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QMenuBar>
+
+#include <KConfig>
+#include <KConfigGroup>
+#include <KSharedConfig>
+
+#undef Region
+
+#include "KDE5SalFrame.hxx"
+#include "KDE5XLib.hxx"
+#include "KDE5SalGraphics.hxx"
+
+#include <tools/color.hxx>
+
+#include <vcl/font.hxx>
+#include <vcl/settings.hxx>
+
+#include "unx/fontmanager.hxx"
+
+#include <svdata.hxx>
+
+#include <boost/optional.hpp>
+
+
+KDESalFrame::KDESalFrame( SalFrame* pParent, SalFrameStyleFlags nState ) :
+ X11SalFrame( pParent, nState )
+{
+}
+
+void KDESalFrame::Show( bool bVisible, bool bNoActivate )
+{
+ if ( !GetParent() && ! (GetStyle() & SalFrameStyleFlags::INTRO) )
+ {
+ KDEXLib* pXLib = static_cast<KDEXLib*>(GetDisplay()->GetXLib());
+ pXLib->doStartup();
+ }
+
+ X11SalFrame::Show( bVisible, bNoActivate );
+}
+
+/** Helper function to convert colors.
+*/
+static Color toColor( const QColor &rColor )
+{
+ return Color( rColor.red(), rColor.green(), rColor.blue() );
+}
+
+
+/** Helper function to add information to Font from QFont.
+
+ Mostly grabbed from the Gtk+ vclplug (salnativewidgets-gtk.cxx).
+*/
+static vcl::Font toFont( const QFont &rQFont, const css::lang::Locale& rLocale )
+{
+ psp::FastPrintFontInfo aInfo;
+ QFontInfo qFontInfo( rQFont );
+
+ // set family name
+ aInfo.m_aFamilyName = OUString( static_cast<const char *>(rQFont.family().toUtf8()), strlen( static_cast<const char *>(rQFont.family().toUtf8()) ), RTL_TEXTENCODING_UTF8 );
+
+ // set italic
+ aInfo.m_eItalic = ( qFontInfo.italic()? ITALIC_NORMAL: ITALIC_NONE );
+
+ // set weight
+ int nWeight = qFontInfo.weight();
+ if ( nWeight <= QFont::Light )
+ aInfo.m_eWeight = WEIGHT_LIGHT;
+ else if ( nWeight <= QFont::Normal )
+ aInfo.m_eWeight = WEIGHT_NORMAL;
+ else if ( nWeight <= QFont::DemiBold )
+ aInfo.m_eWeight = WEIGHT_SEMIBOLD;
+ else if ( nWeight <= QFont::Bold )
+ aInfo.m_eWeight = WEIGHT_BOLD;
+ else
+ aInfo.m_eWeight = WEIGHT_ULTRABOLD;
+
+ // set width
+ int nStretch = rQFont.stretch();
+ if ( nStretch <= QFont::UltraCondensed )
+ aInfo.m_eWidth = WIDTH_ULTRA_CONDENSED;
+ else if ( nStretch <= QFont::ExtraCondensed )
+ aInfo.m_eWidth = WIDTH_EXTRA_CONDENSED;
+ else if ( nStretch <= QFont::Condensed )
+ aInfo.m_eWidth = WIDTH_CONDENSED;
+ else if ( nStretch <= QFont::SemiCondensed )
+ aInfo.m_eWidth = WIDTH_SEMI_CONDENSED;
+ else if ( nStretch <= QFont::Unstretched )
+ aInfo.m_eWidth = WIDTH_NORMAL;
+ else if ( nStretch <= QFont::SemiExpanded )
+ aInfo.m_eWidth = WIDTH_SEMI_EXPANDED;
+ else if ( nStretch <= QFont::Expanded )
+ aInfo.m_eWidth = WIDTH_EXPANDED;
+ else if ( nStretch <= QFont::ExtraExpanded )
+ aInfo.m_eWidth = WIDTH_EXTRA_EXPANDED;
+ else
+ aInfo.m_eWidth = WIDTH_ULTRA_EXPANDED;
+
+ SAL_INFO( "vcl.kde4", "font name BEFORE system match: \"" << aInfo.m_aFamilyName << "\"" );
+
+ // match font to e.g. resolve "Sans"
+ psp::PrintFontManager::get().matchFont( aInfo, rLocale );
+
+ SAL_INFO( "vcl.kde4", "font match " <<
+ (aInfo.m_nID != 0 ? "succeeded" : "failed") <<
+ ", name AFTER: \"" << aInfo.m_aFamilyName << "\"" );
+
+ // font height
+ int nPointHeight = qFontInfo.pointSize();
+ if ( nPointHeight <= 0 )
+ nPointHeight = rQFont.pointSize();
+
+ // Create the font
+ vcl::Font aFont( aInfo.m_aFamilyName, Size( 0, nPointHeight ) );
+ if( aInfo.m_eWeight != WEIGHT_DONTKNOW )
+ aFont.SetWeight( aInfo.m_eWeight );
+ if( aInfo.m_eWidth != WIDTH_DONTKNOW )
+ aFont.SetWidthType( aInfo.m_eWidth );
+ if( aInfo.m_eItalic != ITALIC_DONTKNOW )
+ aFont.SetItalic( aInfo.m_eItalic );
+ if( aInfo.m_ePitch != PITCH_DONTKNOW )
+ aFont.SetPitch( aInfo.m_ePitch );
+
+ return aFont;
+}
+
+/** Implementation of KDE integration's main method.
+*/
+void KDESalFrame::UpdateSettings( AllSettings& rSettings )
+{
+ StyleSettings style( rSettings.GetStyleSettings() );
+ bool bSetTitleFont = false;
+
+ // General settings
+ QPalette pal = QApplication::palette();
+
+ style.SetToolbarIconSize( ToolbarIconSize::Large );
+
+ style.SetActiveColor(toColor(pal.color(QPalette::Active, QPalette::Window)));
+ style.SetDeactiveColor(toColor(pal.color(QPalette::Inactive, QPalette::Window)));
+
+ style.SetActiveTextColor(toColor(pal.color(QPalette::Active, QPalette::WindowText)));
+ style.SetDeactiveTextColor(toColor(pal.color(QPalette::Inactive, QPalette::WindowText)));
+
+ // WM settings
+ /*KConfig *pConfig = KGlobal::config().data();
+ if ( pConfig )
+ {
+ const char *pKey;
+
+ {
+ KConfigGroup aWMGroup = pConfig->group( "WM" );
+
+ pKey = "titleFont";
+ if (aWMGroup.hasKey(pKey))
+ {
+ vcl::Font aFont = toFont(aWMGroup.readEntry(pKey, QFont()),
+ rSettings.GetUILanguageTag().getLocale());
+ style.SetTitleFont( aFont );
+ bSetTitleFont = true;
+ }
+ }
+
+ KConfigGroup aIconsGroup = pConfig->group("Icons");
+
+ pKey = "Theme";
+ if (aIconsGroup.hasKey(pKey))
+ style.SetPreferredIconTheme( readEntryUntranslated(&aIconsGroup, pKey));
+
+ //toolbar
+ pKey = "toolbarFont";
+ if (aIconsGroup.hasKey(pKey))
+ {
+ vcl::Font aFont = toFont(aIconsGroup.readEntry(pKey, QFont()),
+ rSettings.GetUILanguageTag().getLocale());
+ style.SetToolFont( aFont );
+ }
+ }*/
+
+ Color aFore = toColor( pal.color( QPalette::Active, QPalette::WindowText ) );
+ Color aBack = toColor( pal.color( QPalette::Active, QPalette::Window ) );
+ Color aText = toColor( pal.color( QPalette::Active, QPalette::Text ) );
+ Color aBase = toColor( pal.color( QPalette::Active, QPalette::Base ) );
+ Color aButn = toColor( pal.color( QPalette::Active, QPalette::ButtonText ) );
+ Color aMid = toColor( pal.color( QPalette::Active, QPalette::Mid ) );
+ Color aHigh = toColor( pal.color( QPalette::Active, QPalette::Highlight ) );
+ Color aHighText = toColor( pal.color( QPalette::Active, QPalette::HighlightedText ) );
+
+ style.SetSkipDisabledInMenus( true );
+
+ // Foreground
+ style.SetRadioCheckTextColor( aFore );
+ style.SetLabelTextColor( aFore );
+ style.SetDialogTextColor( aFore );
+ style.SetGroupTextColor( aFore );
+
+ // Text
+ style.SetFieldTextColor( aText );
+ style.SetFieldRolloverTextColor( aText );
+ style.SetWindowTextColor( aText );
+ style.SetToolTextColor( aText );
+
+ // Base
+ style.SetFieldColor( aBase );
+ style.SetWindowColor( aBase );
+ style.SetActiveTabColor( aBase );
+
+ // Buttons
+ style.SetButtonTextColor( aButn );
+ style.SetButtonRolloverTextColor( aButn );
+
+ // Tabs
+ style.SetTabTextColor( aButn );
+ style.SetTabRolloverTextColor( aButn );
+ style.SetTabHighlightTextColor( aButn );
+
+ // Disable color
+ style.SetDisableColor( toColor( pal.color( QPalette::Disabled, QPalette::WindowText ) ) );
+
+ // Workspace
+ style.SetWorkspaceColor( aMid );
+
+ // Background
+ style.Set3DColors( aBack );
+ style.SetFaceColor( aBack );
+ style.SetInactiveTabColor( aBack );
+ style.SetDialogColor( aBack );
+ style.SetCheckedColorSpecialCase( );
+
+ // Selection
+ style.SetHighlightColor( aHigh );
+ style.SetHighlightTextColor( aHighText );
+
+ // Tooltip
+ style.SetHelpColor( toColor( QToolTip::palette().color( QPalette::Active, QPalette::ToolTipBase )));
+ style.SetHelpTextColor( toColor( QToolTip::palette().color( QPalette::Active, QPalette::ToolTipText )));
+
+ // Font
+ vcl::Font aFont = toFont( QApplication::font(), rSettings.GetUILanguageTag().getLocale() );
+
+ style.SetAppFont( aFont );
+
+ style.SetMenuFont( aFont ); // will be changed according to pMenuBar
+ style.SetLabelFont( aFont );
+ style.SetRadioCheckFont( aFont );
+ style.SetPushButtonFont( aFont );
+ style.SetFieldFont( aFont );
+ style.SetIconFont( aFont );
+ style.SetTabFont( aFont );
+ style.SetGroupFont( aFont );
+
+ aFont.SetWeight( WEIGHT_BOLD );
+ if( !bSetTitleFont )
+ {
+ style.SetTitleFont( aFont );
+ }
+ style.SetFloatTitleFont( aFont );
+
+ style.SetHelpFont( toFont( QToolTip::font(), rSettings.GetUILanguageTag().getLocale()));
+
+ int flash_time = QApplication::cursorFlashTime();
+ style.SetCursorBlinkTime( flash_time != 0 ? flash_time/2 : STYLE_CURSOR_NOBLINKTIME );
+
+ // Menu
+ std::unique_ptr<QMenuBar> pMenuBar = std::unique_ptr<QMenuBar>( new QMenuBar() );
+ QPalette qMenuCG = pMenuBar->palette();
+
+ // Menu text and background color, theme specific
+ Color aMenuFore = toColor( qMenuCG.color( QPalette::WindowText ) );
+ Color aMenuBack = toColor( qMenuCG.color( QPalette::Window ) );
+
+ style.SetMenuTextColor( aMenuFore );
+ style.SetMenuBarTextColor( style.GetPersonaMenuBarTextColor().get_value_or( aMenuFore ) );
+ style.SetMenuColor( aMenuBack );
+ style.SetMenuBarColor( aMenuBack );
+ style.SetMenuHighlightColor( toColor ( qMenuCG.color( QPalette::Highlight ) ) );
+ style.SetMenuHighlightTextColor( toColor ( qMenuCG.color( QPalette::HighlightedText ) ) );
+
+ // set special menubar highlight text color
+ if ( QApplication::style()->inherits( "HighContrastStyle" ) )
+ ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor = toColor( qMenuCG.color( QPalette::HighlightedText ) );
+ else
+ ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor = aMenuFore;
+
+ // set menubar rollover color
+ if ( pMenuBar->style()->styleHint( QStyle::SH_MenuBar_MouseTracking ) )
+ {
+ style.SetMenuBarRolloverColor( toColor ( qMenuCG.color( QPalette::Highlight ) ) );
+ style.SetMenuBarRolloverTextColor( ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor );
+ }
+ else
+ {
+ style.SetMenuBarRolloverColor( aMenuBack );
+ style.SetMenuBarRolloverTextColor( aMenuFore );
+ }
+ style.SetMenuBarHighlightTextColor(style.GetMenuHighlightTextColor());
+
+ // Font
+ aFont = toFont( pMenuBar->font(), rSettings.GetUILanguageTag().getLocale() );
+ style.SetMenuFont( aFont );
+
+ // Scroll bar size
+ style.SetScrollBarSize( QApplication::style()->pixelMetric( QStyle::PM_ScrollBarExtent ) );
+ style.SetMinThumbSize( QApplication::style()->pixelMetric( QStyle::PM_ScrollBarSliderMin ));
+
+ // These colors are used for the ruler text and marks
+ style.SetShadowColor(toColor(pal.color(QPalette::Disabled, QPalette::WindowText)));
+ style.SetDarkShadowColor(toColor(pal.color(QPalette::Inactive, QPalette::WindowText)));
+
+ rSettings.SetStyleSettings( style );
+}
+
+void KDESalFrame::ReleaseGraphics( SalGraphics *pGraphics )
+{
+ for( int i = 0; i < nMaxGraphics; i++ )
+ {
+ if( m_aGraphics[i].pGraphics.get() == pGraphics )
+ {
+ m_aGraphics[i].bInUse = false;
+ break;
+ }
+ }
+}
+
+void KDESalFrame::updateGraphics( bool bClear )
+{
+ Drawable aDrawable = bClear ? None : GetWindow();
+ for( int i = 0; i < nMaxGraphics; i++ )
+ {
+ if( m_aGraphics[i].bInUse )
+ m_aGraphics[i].pGraphics->SetDrawable( aDrawable, GetScreenNumber() );
+ }
+}
+
+SalGraphics* KDESalFrame::AcquireGraphics()
+{
+ if( GetWindow() )
+ {
+ for( int i = 0; i < nMaxGraphics; i++ )
+ {
+ if( ! m_aGraphics[i].bInUse )
+ {
+ m_aGraphics[i].bInUse = true;
+ if( ! m_aGraphics[i].pGraphics )
+ {
+ m_aGraphics[i].pGraphics.reset( new KDESalGraphics );
+ m_aGraphics[i].pGraphics->Init( this, GetWindow(), GetScreenNumber() );
+ }
+ return m_aGraphics[i].pGraphics.get();
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalFrame.hxx b/vcl/unx/kde5/KDE5SalFrame.hxx
new file mode 100644
index 000000000000..114cd49bd8d2
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalFrame.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <unx/saldisp.hxx>
+#include <unx/salframe.h>
+#include <unx/salgdi.h>
+
+class KDESalFrame : public X11SalFrame
+{
+ private:
+ static const int nMaxGraphics = 2;
+
+ struct GraphicsHolder
+ {
+ std::unique_ptr<X11SalGraphics> pGraphics;
+ bool bInUse;
+
+ GraphicsHolder() : bInUse( false ) {}
+ };
+
+ GraphicsHolder m_aGraphics[ nMaxGraphics ];
+
+ public:
+ KDESalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle );
+
+ virtual SalGraphics* AcquireGraphics() override;
+ virtual void ReleaseGraphics( SalGraphics *pGraphics ) override;
+ virtual void updateGraphics( bool bClear ) override;
+ virtual void UpdateSettings( AllSettings& rSettings ) override;
+ virtual void Show( bool bVisible, bool bNoActivate = false ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalGraphics.cxx b/vcl/unx/kde5/KDE5SalGraphics.cxx
new file mode 100644
index 000000000000..85642f6f91df
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalGraphics.cxx
@@ -0,0 +1,1020 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <QtGui/QPainter>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QStyle>
+#include <QtWidgets/QStyleOption>
+#include <QtWidgets/QFrame>
+#include <QtWidgets/QLabel>
+
+#undef Region
+
+#include "KDE5SalGraphics.hxx"
+#include "KDE5SalInstance.hxx"
+
+#include <vcl/settings.hxx>
+#include <vcl/decoview.hxx>
+#include <rtl/ustrbuf.hxx>
+
+/**
+ Conversion function between VCL ControlState together with
+ ImplControlValue and Qt state flags.
+ @param nControlState State of the widget (default, focused, ...) in Native Widget Framework.
+ @param aValue Value held by the widget (on, off, ...)
+*/
+QStyle::State vclStateValue2StateFlag( ControlState nControlState,
+ const ImplControlValue& aValue )
+{
+ QStyle::State nState =
+ ( (nControlState & ControlState::ENABLED)? QStyle::State_Enabled: QStyle::State_None ) |
+ ( (nControlState & ControlState::FOCUSED)? QStyle::State_HasFocus: QStyle::State_None ) |
+ ( (nControlState & ControlState::PRESSED)? QStyle::State_Sunken: QStyle::State_None ) |
+ ( (nControlState & ControlState::SELECTED)? QStyle::State_Selected : QStyle::State_None ) |
+ ( (nControlState & ControlState::ROLLOVER)? QStyle::State_MouseOver: QStyle::State_None );
+
+ switch ( aValue.getTristateVal() )
+ {
+ case ButtonValue::On: nState |= QStyle::State_On; break;
+ case ButtonValue::Off: nState |= QStyle::State_Off; break;
+ case ButtonValue::Mixed: nState |= QStyle::State_NoChange; break;
+ default: break;
+ }
+
+ return nState;
+}
+
+/**
+ Convert tools::Rectangle to QRect.
+ @param rControlRegion The tools::Rectangle to convert.
+ @return The matching QRect
+*/
+QRect region2QRect( const tools::Rectangle& rControlRegion )
+{
+ return QRect(rControlRegion.Left(), rControlRegion.Top(), rControlRegion.GetWidth(), rControlRegion.GetHeight());
+}
+
+bool KDESalGraphics::IsNativeControlSupported( ControlType type, ControlPart part )
+{
+ switch (type)
+ {
+ case ControlType::Pushbutton:
+ case ControlType::Radiobutton:
+ case ControlType::Checkbox:
+ case ControlType::Tooltip:
+ case ControlType::Progress:
+ case ControlType::ListNode:
+ return (part == ControlPart::Entire);
+
+ case ControlType::Menubar:
+ case ControlType::MenuPopup:
+ case ControlType::Editbox:
+ case ControlType::MultilineEditbox:
+ case ControlType::Combobox:
+ case ControlType::Toolbar:
+ case ControlType::Frame:
+ case ControlType::Scrollbar:
+ case ControlType::WindowBackground:
+ case ControlType::Fixedline:
+ return true;
+
+ case ControlType::Listbox:
+ return (part == ControlPart::Entire || part == ControlPart::HasBackgroundTexture);
+
+ case ControlType::Spinbox:
+ return (part == ControlPart::Entire || part == ControlPart::HasBackgroundTexture);
+
+ case ControlType::Slider:
+ return (part == ControlPart::TrackHorzArea || part == ControlPart::TrackVertArea);
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/// helper drawing methods
+namespace
+{
+ void draw( QStyle::ControlElement element, QStyleOption* option, QImage* image, QStyle::State const & state, QRect rect = QRect())
+ {
+ option->state |= state;
+ option->rect = !rect.isNull() ? rect : image->rect();
+
+ QPainter painter(image);
+ QApplication::style()->drawControl(element, option, &painter);
+ }
+
+ void draw( QStyle::PrimitiveElement element, QStyleOption* option, QImage* image, QStyle::State const & state, QRect rect = QRect())
+ {
+ option->state |= state;
+ option->rect = !rect.isNull() ? rect : image->rect();
+
+ QPainter painter(image);
+ QApplication::style()->drawPrimitive(element, option, &painter);
+ }
+
+ void draw( QStyle::ComplexControl element, QStyleOptionComplex* option, QImage* image, QStyle::State const & state )
+ {
+ option->state |= state;
+ option->rect = image->rect();
+
+ QPainter painter(image);
+ QApplication::style()->drawComplexControl(element, option, &painter);
+ }
+
+ void lcl_drawFrame( QStyle::PrimitiveElement element, QImage* image, QStyle::State const & state,
+ QStyle::PixelMetric eLineMetric = QStyle::PM_DefaultFrameWidth )
+ {
+ #if ( QT_VERSION >= QT_VERSION_CHECK( 4, 5, 0 ) )
+ QStyleOptionFrameV3 option;
+ option.frameShape = QFrame::StyledPanel;
+ option.state = QStyle::State_Sunken;
+ option.lineWidth = QApplication::style()->pixelMetric( eLineMetric );
+ #else
+ QStyleOptionFrame option;
+
+ QFrame aFrame( nullptr );
+ aFrame.setFrameRect( QRect(0, 0, image->width(), image->height()) );
+ aFrame.setFrameStyle( QFrame::StyledPanel | QFrame::Sunken );
+ aFrame.ensurePolished();
+
+ option.initFrom( &aFrame );
+ option.lineWidth = aFrame.lineWidth();
+ option.midLineWidth = aFrame.midLineWidth();
+ #endif
+ draw(element, &option, image, state);
+ }
+}
+
+#if QT_VERSION >= QT_VERSION_CHECK( 4, 5, 0 )
+#define IMAGE_BASED_PAINTING
+#else
+#undef IMAGE_BASED_PAINTING
+#endif
+
+#ifdef IMAGE_BASED_PAINTING
+// There is a small catch with this function, although hopefully only philosophical.
+// Officially Xlib's vcl::Region is an opaque data type, with only functions for manipulating it.
+// However, whoever designed it apparently didn't give it that much thought, as it's impossible
+// to find out what exactly a region actually is (except for really weird ways like XClipBox()
+// and repeated XPointInRegion(), which would be awfully slow). Fortunately, the header file
+// describing the structure actually happens to be installed too, and there's at least one
+// widely used software using it (Compiz). So access the data directly too and assume that
+// everybody who compiles with Qt4 support has Xlib new enough and good enough to support this.
+// In case this doesn't work for somebody, try #include <X11/region.h> instead, or build
+// without IMAGE_BASED_PAINTING (in which case QApplication::setGraphicsSystem( "native" ) may
+// be needed too).
+#include <X11/Xregion.h>
+static QRegion XRegionToQRegion( Region xr )
+{
+ QRegion qr;
+ for( long i = 0;
+ i < xr->numRects;
+ ++i )
+ {
+ BOX& b = xr->rects[ i ];
+ qr |= QRect( b.x1, b.y1, b.x2 - b.x1, b.y2 - b.y1 ); // x2,y2 is outside, not the bottom-right corner
+ }
+ return qr;
+}
+#endif
+
+bool KDESalGraphics::drawNativeControl( ControlType type, ControlPart part,
+ const tools::Rectangle& rControlRegion, ControlState nControlState,
+ const ImplControlValue& value,
+ const OUString& )
+{
+ bool nativeSupport = IsNativeControlSupported( type, part );
+ if( ! nativeSupport ) {
+ assert( ! nativeSupport && "drawNativeControl called without native support!" );
+ return false;
+ }
+
+ if( lastPopupRect.isValid() && ( type != ControlType::MenuPopup || part != ControlPart::MenuItem ))
+ lastPopupRect = QRect();
+
+ bool returnVal = true;
+
+ QRect widgetRect = region2QRect(rControlRegion);
+
+ //if no image, or resized, make a new image
+ if (!m_image || m_image->size() != widgetRect.size())
+ {
+ m_image.reset(new QImage( widgetRect.width(), widgetRect.height(), QImage::Format_ARGB32 ) );
+ }
+
+ // Default image color - just once
+ switch (type)
+ {
+ case ControlType::MenuPopup:
+ if( part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark )
+ {
+ // it is necessary to fill the background transparently first, as this
+ // is painted after menuitem highlight, otherwise there would be a grey area
+ m_image->fill( Qt::transparent );
+ break;
+ }
+ SAL_FALLTHROUGH; // QPalette::Window
+ case ControlType::Menubar:
+ case ControlType::WindowBackground:
+ m_image->fill( QApplication::palette().color(QPalette::Window).rgb() );
+ break;
+ case ControlType::Tooltip:
+ m_image->fill(QApplication::palette().color(QPalette::ToolTipBase).rgb());
+ break;
+ case ControlType::Pushbutton:
+ m_image->fill(QApplication::palette().color(QPalette::Button).rgb());
+ break;
+ case ControlType::Scrollbar:
+ if ((part == ControlPart::DrawBackgroundVert)
+ || (part == ControlPart::DrawBackgroundHorz))
+ {
+ m_image->fill( QApplication::palette().color(QPalette::Window).rgb() );
+ break;
+ }
+ SAL_FALLTHROUGH; // Qt::transparent
+ default:
+ m_image->fill( Qt::transparent );
+ break;
+ }
+
+ QRegion* localClipRegion = nullptr;
+
+ if (type == ControlType::Pushbutton)
+ {
+ QStyleOptionButton option;
+ draw( QStyle::CE_PushButton, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Menubar)
+ {
+ if (part == ControlPart::MenuItem)
+ {
+ QStyleOptionMenuItem option;
+ if ( ( nControlState & ControlState::ROLLOVER )
+ && QApplication::style()->styleHint( QStyle::SH_MenuBar_MouseTracking ) )
+ option.state |= QStyle::State_Selected;
+
+ if ( nControlState & ControlState::SELECTED ) // Passing State_Sunken is currently not documented.
+ option.state |= QStyle::State_Sunken; // But some kinds of QStyle interpret it.
+
+ draw( QStyle::CE_MenuBarItem, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (part == ControlPart::Entire)
+ {
+ QStyleOptionMenuItem option;
+ draw( QStyle::CE_MenuBarEmptyArea, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else
+ {
+ returnVal = false;
+ }
+ }
+ else if (type == ControlType::MenuPopup)
+ {
+ OSL_ASSERT( part == ControlPart::MenuItem ? lastPopupRect.isValid() : !lastPopupRect.isValid());
+ if( part == ControlPart::MenuItem )
+ {
+ QStyleOptionMenuItem option;
+ draw( QStyle::CE_MenuItem, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ // HACK: LO core first paints the entire popup and only then it paints menu items,
+ // but QMenu::paintEvent() paints popup frame after all items. That means highlighted
+ // items here would paint the highlight over the frame border. Since calls to ControlPart::MenuItem
+ // are always preceded by calls to ControlPart::Entire, just remember the size for the whole
+ // popup (otherwise not possible to get here) and draw the border afterwards.
+ QRect framerect( lastPopupRect.topLeft() - widgetRect.topLeft(),
+ widgetRect.size().expandedTo( lastPopupRect.size()));
+ QStyleOptionFrame frame;
+ draw( QStyle::PE_FrameMenu, &frame, m_image.get(), vclStateValue2StateFlag( nControlState, value ), framerect );
+ }
+ else if( part == ControlPart::Separator )
+ {
+ QStyleOptionMenuItem option;
+ option.menuItemType = QStyleOptionMenuItem::Separator;
+ // Painting the whole menu item area results in different background
+ // with at least Plastique style, so clip only to the separator itself
+ // (QSize( 2, 2 ) is hardcoded in Qt)
+ option.rect = m_image->rect();
+ QSize size = QApplication::style()->sizeFromContents( QStyle::CT_MenuItem, &option, QSize( 2, 2 ));
+ QRect rect = m_image->rect();
+ QPoint center = rect.center();
+ rect.setHeight( size.height());
+ rect.moveCenter( center );
+ // don't paint over popup frame border (like the hack above, but here it can be simpler)
+ int fw = QApplication::style()->pixelMetric( QStyle::PM_MenuPanelWidth );
+ localClipRegion = new QRegion(rect.translated(widgetRect.topLeft()).adjusted(fw, 0, -fw, 0));
+ draw( QStyle::CE_MenuItem, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value), rect );
+ }
+ else if( part == ControlPart::MenuItemCheckMark || part == ControlPart::MenuItemRadioMark )
+ {
+ QStyleOptionMenuItem option;
+ option.checkType = ( part == ControlPart::MenuItemCheckMark )
+ ? QStyleOptionMenuItem::NonExclusive : QStyleOptionMenuItem::Exclusive;
+ option.checked = bool( nControlState & ControlState::PRESSED );
+ // widgetRect is now the rectangle for the checkbox/radiobutton itself, but Qt
+ // paints the whole menu item, so translate position (and it'll be clipped);
+ // it is also necessary to fill the background transparently first, as this
+ // is painted after menuitem highlight, otherwise there would be a grey area
+ assert( value.getType() == ControlType::MenuPopup );
+ const MenupopupValue* menuVal = static_cast<const MenupopupValue*>(&value);
+ QRect menuItemRect( region2QRect( menuVal->maItemRect ));
+ QRect rect( menuItemRect.topLeft() - widgetRect.topLeft(),
+ widgetRect.size().expandedTo( menuItemRect.size()));
+ // checkboxes are always displayed next to images in menus, so are never centered
+ const int focus_size = QApplication::style()->pixelMetric( QStyle::PM_FocusFrameHMargin );
+ rect.moveTo( -focus_size, rect.y() );
+ draw( QStyle::CE_MenuItem, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState & ~ControlState::PRESSED, value), rect );
+ }
+ else if( part == ControlPart::Entire )
+ {
+ QStyleOptionMenuItem option;
+ draw( QStyle::PE_PanelMenu, &option, m_image.get(), vclStateValue2StateFlag( nControlState, value ));
+ // Try hard to get any frame!
+ QStyleOptionFrame frame;
+ draw( QStyle::PE_FrameMenu, &frame, m_image.get(), vclStateValue2StateFlag( nControlState, value ));
+ draw( QStyle::PE_FrameWindow, &frame, m_image.get(), vclStateValue2StateFlag( nControlState, value ));
+ lastPopupRect = widgetRect;
+ }
+ else
+ returnVal = false;
+ }
+ else if ( (type == ControlType::Toolbar) && (part == ControlPart::Button) )
+ {
+ QStyleOptionToolButton option;
+
+ option.arrowType = Qt::NoArrow;
+ option.subControls = QStyle::SC_ToolButton;
+
+ option.state = vclStateValue2StateFlag( nControlState, value );
+ option.state |= QStyle::State_Raised | QStyle::State_Enabled | QStyle::State_AutoRaise;
+
+ draw( QStyle::CC_ToolButton, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if ( (type == ControlType::Toolbar) && (part == ControlPart::Entire) )
+ {
+ QStyleOptionToolBar option;
+
+ option.rect = QRect(0, 0, widgetRect.width(), widgetRect.height());
+ option.state = vclStateValue2StateFlag( nControlState, value );
+
+ draw( QStyle::CE_ToolBar, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if ( (type == ControlType::Toolbar)
+ && (part == ControlPart::ThumbVert || part == ControlPart::ThumbHorz) )
+ { // reduce paint area only to the handle area
+ const int handleExtend = QApplication::style()->pixelMetric(QStyle::PM_ToolBarHandleExtent);
+ QRect rect;
+ QStyleOption option;
+
+ if (part == ControlPart::ThumbVert)
+ {
+ rect = QRect( 0, 0, handleExtend, widgetRect.height());
+ localClipRegion = new QRegion(widgetRect.x(), widgetRect.y(), handleExtend, widgetRect.height());
+ option.state = QStyle::State_Horizontal;
+ }
+ else
+ {
+ rect = QRect( 0, 0, widgetRect.width(), handleExtend);
+ localClipRegion = new QRegion(widgetRect.x(), widgetRect.y(), widgetRect.width(), handleExtend);
+ }
+
+ draw( QStyle::PE_IndicatorToolBarHandle, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value), rect );
+ }
+ else if (type == ControlType::Editbox || type == ControlType::MultilineEditbox)
+ {
+ lcl_drawFrame( QStyle::PE_FrameLineEdit, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value));
+ }
+ else if (type == ControlType::Combobox)
+ {
+ QStyleOptionComboBox option;
+ option.editable = true;
+ draw( QStyle::CC_ComboBox, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Listbox)
+ {
+ QStyleOptionComboBox option;
+ option.editable = false;
+ switch (part) {
+ case ControlPart::ListboxWindow:
+ lcl_drawFrame( QStyle::PE_Frame, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value),
+ QStyle::PM_ComboBoxFrameWidth );
+ break;
+ case ControlPart::SubEdit:
+ draw( QStyle::CE_ComboBoxLabel, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ break;
+ case ControlPart::Entire:
+ draw( QStyle::CC_ComboBox, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ break;
+ case ControlPart::ButtonDown:
+ option.subControls = QStyle::SC_ComboBoxArrow;
+ draw( QStyle::CC_ComboBox, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ break;
+ default:
+ returnVal = false;
+ break;
+ }
+ }
+ else if (type == ControlType::ListNode)
+ {
+ QStyleOption option;
+ option.state = QStyle::State_Item | QStyle::State_Children;
+
+ if (value.getTristateVal() == ButtonValue::On)
+ option.state |= QStyle::State_Open;
+
+ draw( QStyle::PE_IndicatorBranch, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Checkbox)
+ {
+ QStyleOptionButton option;
+ draw( QStyle::CE_CheckBox, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Scrollbar)
+ {
+ if ((part == ControlPart::DrawBackgroundVert) || (part == ControlPart::DrawBackgroundHorz))
+ {
+ QStyleOptionSlider option;
+ OSL_ASSERT( value.getType() == ControlType::Scrollbar );
+ const ScrollbarValue* sbVal = static_cast<const ScrollbarValue *>(&value);
+
+ //if the scroll bar is active (aka not degenrate...allow for hover events
+ if (sbVal->mnVisibleSize < sbVal->mnMax)
+ option.state = QStyle::State_MouseOver;
+
+ bool horizontal = ( part == ControlPart::DrawBackgroundHorz ); //horizontal or vertical
+ option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
+ if( horizontal )
+ option.state |= QStyle::State_Horizontal;
+
+ //setup parameters from the OO values
+ option.minimum = sbVal->mnMin;
+ option.maximum = sbVal->mnMax - sbVal->mnVisibleSize;
+ option.maximum = qMax( option.maximum, option.minimum ); // bnc#619772
+ option.sliderValue = sbVal->mnCur;
+ option.sliderPosition = sbVal->mnCur;
+ option.pageStep = sbVal->mnVisibleSize;
+ if (part == ControlPart::DrawBackgroundHorz)
+ option.upsideDown = sbVal->maButton1Rect.Left() > sbVal->maButton2Rect.Left();
+
+ //setup the active control...always the slider
+ if (sbVal->mnThumbState & ControlState::ROLLOVER)
+ option.activeSubControls = QStyle::SC_ScrollBarSlider;
+
+ draw( QStyle::CC_ScrollBar, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else
+ {
+ returnVal = false;
+ }
+ }
+ else if (type == ControlType::Spinbox)
+ {
+ QStyleOptionSpinBox option;
+ option.frame = true;
+
+ // determine active control
+ if( value.getType() == ControlType::SpinButtons )
+ {
+ const SpinbuttonValue* pSpinVal = static_cast<const SpinbuttonValue *>(&value);
+ if( (pSpinVal->mnUpperState & ControlState::PRESSED) )
+ option.activeSubControls |= QStyle::SC_SpinBoxUp;
+ if( (pSpinVal->mnLowerState & ControlState::PRESSED) )
+ option.activeSubControls |= QStyle::SC_SpinBoxDown;
+ if( (pSpinVal->mnUpperState & ControlState::ENABLED) )
+ option.stepEnabled |= QAbstractSpinBox::StepUpEnabled;
+ if( (pSpinVal->mnLowerState & ControlState::ENABLED) )
+ option.stepEnabled |= QAbstractSpinBox::StepDownEnabled;
+ if( (pSpinVal->mnUpperState & ControlState::ROLLOVER) )
+ option.state = QStyle::State_MouseOver;
+ if( (pSpinVal->mnLowerState & ControlState::ROLLOVER) )
+ option.state = QStyle::State_MouseOver;
+ }
+
+ draw( QStyle::CC_SpinBox, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Radiobutton)
+ {
+ QStyleOptionButton option;
+ draw( QStyle::CE_RadioButton, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Tooltip)
+ {
+ QStyleOption option;
+ draw( QStyle::PE_PanelTipLabel, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Frame)
+ {
+ lcl_drawFrame( QStyle::PE_Frame, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ // draw just the border, see http://qa.openoffice.org/issues/show_bug.cgi?id=107945
+ int fw = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ localClipRegion = new QRegion(QRegion(widgetRect).subtracted(widgetRect.adjusted(fw, fw, -fw, -fw)));
+ }
+ else if (type == ControlType::WindowBackground)
+ {
+ // Nothing to do - see "Default image color" switch ^^
+ }
+ else if (type == ControlType::Fixedline)
+ {
+ QStyleOptionMenuItem option;
+ option.menuItemType = QStyleOptionMenuItem::Separator;
+ option.state |= QStyle::State_Item;
+
+ draw( QStyle::CE_MenuItem, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if (type == ControlType::Slider && (part == ControlPart::TrackHorzArea || part == ControlPart::TrackVertArea))
+ {
+ OSL_ASSERT( value.getType() == ControlType::Slider );
+ const SliderValue* slVal = static_cast<const SliderValue *>(&value);
+ QStyleOptionSlider option;
+
+ option.rect = QRect(0, 0, widgetRect.width(), widgetRect.height());
+ option.state = vclStateValue2StateFlag( nControlState, value );
+ option.maximum = slVal->mnMax;
+ option.minimum = slVal->mnMin;
+ option.sliderPosition = option.sliderValue = slVal->mnCur;
+ bool horizontal = ( part == ControlPart::TrackHorzArea ); //horizontal or vertical
+ option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
+ if( horizontal )
+ option.state |= QStyle::State_Horizontal;
+
+ draw( QStyle::CC_Slider, &option, m_image.get(), vclStateValue2StateFlag(nControlState, value) );
+ }
+ else if( type == ControlType::Progress && part == ControlPart::Entire )
+ {
+ QStyleOptionProgressBarV2 option;
+ option.minimum = 0;
+ option.maximum = widgetRect.width();
+ option.progress = value.getNumericVal();
+ option.rect = QRect(0, 0, widgetRect.width(), widgetRect.height());
+ option.state = vclStateValue2StateFlag( nControlState, value );
+
+ draw( QStyle::CE_ProgressBar, &option, m_image.get(),
+ vclStateValue2StateFlag(nControlState, value) );
+ }
+ else
+ {
+ returnVal = false;
+ }
+
+ if (returnVal)
+ {
+#if 0
+#ifdef IMAGE_BASED_PAINTING
+ // Create a wrapper QPixmap around the destination pixmap, allowing the use of QPainter.
+ // Using X11SalGraphics::CopyScreenArea() would require using QPixmap and if Qt uses
+ // other graphics system than native, QPixmap::handle() would be 0 (i.e. it wouldn't work),
+ // I have no idea how to create QPixmap with non-null handle() in such case, so go this way.
+ // See XRegionToQRegion() comment for a small catch (although not real hopefully).
+ QPixmap destPixmap = QPixmap::fromX11Pixmap( GetDrawable(), QPixmap::ExplicitlyShared );
+ QPainter paint( &destPixmap );
+ if (localClipRegion && mpClipRegion)
+ paint.setClipRegion(localClipRegion->intersected(XRegionToQRegion(mpClipRegion)));
+ else if (localClipRegion)
+ paint.setClipRegion(*localClipRegion);
+ else if( mpClipRegion )
+ paint.setClipRegion( XRegionToQRegion( mpClipRegion ));
+ paint.drawImage( widgetRect.left(), widgetRect.top(), *m_image,
+ 0, 0, widgetRect.width(), widgetRect.height(),
+ Qt::ColorOnly | Qt::OrderedDither | Qt::OrderedAlphaDither );
+#else
+ GC gc = GetFontGC();
+ if( gc )
+ {
+ Region pTempClipRegion = NULL;
+ if (localClipRegion)
+ {
+ pTempClipRegion = XCreateRegion();
+ foreach(const QRect& r, localClipRegion->rects())
+ {
+ XRectangle xr;
+ xr.x = r.x();
+ xr.y = r.y();
+ xr.width = r.width();
+ xr.height = r.height();
+ XUnionRectWithRegion( &xr, pTempClipRegion, pTempClipRegion );
+ }
+ if( mpClipRegion )
+ XIntersectRegion( pTempClipRegion, mpClipRegion, pTempClipRegion );
+ XSetRegion( GetXDisplay(), gc, pTempClipRegion );
+ }
+ QPixmap pixmap = QPixmap::fromImage(*m_image, Qt::ColorOnly | Qt::OrderedDither | Qt::OrderedAlphaDither);
+ X11SalGraphics::CopyScreenArea( GetXDisplay(),
+ pixmap.handle(), pixmap.x11Info().screen(), pixmap.x11Info().depth(),
+ GetDrawable(), GetScreenNumber(), GetVisual().GetDepth(),
+ gc, 0, 0, widgetRect.width(), widgetRect.height(), widgetRect.left(), widgetRect.top());
+
+ if( pTempClipRegion )
+ {
+ if( mpClipRegion )
+ XSetRegion( GetXDisplay(), gc, mpClipRegion );
+ else
+ XSetClipMask( GetXDisplay(), gc, None );
+ XDestroyRegion( pTempClipRegion );
+ }
+ }
+ else
+ returnVal = false;
+#endif
+#endif
+ }
+ delete localClipRegion;
+ return returnVal;
+}
+
+bool KDESalGraphics::getNativeControlRegion( ControlType type, ControlPart part,
+ const tools::Rectangle& controlRegion, ControlState controlState,
+ const ImplControlValue& val,
+ const OUString&,
+ tools::Rectangle &nativeBoundingRegion, tools::Rectangle &nativeContentRegion )
+{
+ bool retVal = false;
+
+ QRect boundingRect = region2QRect( controlRegion );
+ QRect contentRect = boundingRect;
+ QStyleOptionComplex styleOption;
+
+ switch ( type )
+ {
+ // Metrics of the push button
+ case ControlType::Pushbutton:
+ if (part == ControlPart::Entire)
+ {
+ styleOption.state = vclStateValue2StateFlag(controlState, val);
+
+ if ( controlState & ControlState::DEFAULT )
+ {
+ int size = QApplication::style()->pixelMetric(
+ QStyle::PM_ButtonDefaultIndicator, &styleOption );
+ boundingRect.adjust( -size, -size, size, size );
+ retVal = true;
+ }
+ }
+ break;
+ case ControlType::Editbox:
+ case ControlType::MultilineEditbox:
+ {
+ QStyleOptionFrameV3 fo;
+ fo.frameShape = QFrame::StyledPanel;
+ fo.state = QStyle::State_Sunken;
+ fo.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ QSize aMinSize = QApplication::style()->
+ sizeFromContents( QStyle::CT_LineEdit, &fo, contentRect.size() );
+ if( aMinSize.height() > boundingRect.height() )
+ {
+ int nHeight = (aMinSize.height() - boundingRect.height()) / 2;
+ assert( 0 == (aMinSize.height() - boundingRect.height()) % 2 );
+ boundingRect.adjust( 0, -nHeight, 0, nHeight );
+ }
+ if( aMinSize.width() > boundingRect.width() )
+ {
+ int nWidth = (aMinSize.width() - boundingRect.width()) / 2;
+ assert( 0 == (aMinSize.width() - boundingRect.width()) % 2 );
+ boundingRect.adjust( -nWidth, 0, nWidth, 0 );
+ }
+ retVal = true;
+ break;
+ }
+ case ControlType::Checkbox:
+ if (part == ControlPart::Entire)
+ {
+ styleOption.state = vclStateValue2StateFlag(controlState, val);
+
+ contentRect.setWidth(QApplication::style()->pixelMetric(
+ QStyle::PM_IndicatorWidth, &styleOption));
+ contentRect.setHeight(QApplication::style()->pixelMetric(
+ QStyle::PM_IndicatorHeight, &styleOption));
+
+ contentRect.adjust(0, 0,
+ 2 * QApplication::style()->pixelMetric(
+ QStyle::PM_FocusFrameHMargin, &styleOption),
+ 2 * QApplication::style()->pixelMetric(
+ QStyle::PM_FocusFrameVMargin, &styleOption)
+ );
+
+ boundingRect = contentRect;
+
+ retVal = true;
+ }
+ break;
+ case ControlType::Combobox:
+ case ControlType::Listbox:
+ {
+ QStyleOptionComboBox cbo;
+
+ cbo.rect = QRect(0, 0, contentRect.width(), contentRect.height());
+ cbo.state = vclStateValue2StateFlag(controlState, val);
+
+ switch ( part )
+ {
+ case ControlPart::Entire:
+ {
+ // find out the minimum size that should be used
+ // assume contents is a text ling
+ int nHeight = QApplication::fontMetrics().height();
+ QSize aContentSize( contentRect.width(), nHeight );
+ QSize aMinSize = QApplication::style()->
+ sizeFromContents( QStyle::CT_ComboBox, &cbo, aContentSize );
+ if( aMinSize.height() > contentRect.height() )
+ contentRect.adjust( 0, 0, 0, aMinSize.height() - contentRect.height() );
+ boundingRect = contentRect;
+ retVal = true;
+ break;
+ }
+ case ControlPart::ButtonDown:
+ contentRect = QApplication::style()->subControlRect(
+ QStyle::CC_ComboBox, &cbo, QStyle::SC_ComboBoxArrow );
+ contentRect.translate( boundingRect.left(), boundingRect.top() );
+ retVal = true;
+ break;
+ case ControlPart::SubEdit:
+ {
+ contentRect = QApplication::style()->subControlRect(
+ QStyle::CC_ComboBox, &cbo, QStyle::SC_ComboBoxEditField );
+ contentRect.translate( boundingRect.left(), boundingRect.top() );
+ retVal = true;
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case ControlType::Spinbox:
+ {
+ QStyleOptionSpinBox sbo;
+ sbo.frame = true;
+
+ sbo.rect = QRect(0, 0, contentRect.width(), contentRect.height());
+ sbo.state = vclStateValue2StateFlag(controlState, val);
+
+ switch ( part )
+ {
+ case ControlPart::Entire:
+ {
+ int nHeight = QApplication::fontMetrics().height();
+ QSize aContentSize( contentRect.width(), nHeight );
+ QSize aMinSize = QApplication::style()->
+ sizeFromContents( QStyle::CT_SpinBox, &sbo, aContentSize );
+ if( aMinSize.height() > contentRect.height() )
+ contentRect.adjust( 0, 0, 0, aMinSize.height() - contentRect.height() );
+ boundingRect = contentRect;
+ retVal = true;
+ break;
+ }
+ case ControlPart::ButtonUp:
+ contentRect = QApplication::style()->subControlRect(
+ QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxUp );
+ contentRect.translate( boundingRect.left(), boundingRect.top() );
+ retVal = true;
+ boundingRect = QRect();
+ break;
+
+ case ControlPart::ButtonDown:
+ contentRect = QApplication::style()->subControlRect(
+ QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxDown );
+ retVal = true;
+ contentRect.translate( boundingRect.left(), boundingRect.top() );
+ boundingRect = QRect();
+ break;
+
+ case ControlPart::SubEdit:
+ contentRect = QApplication::style()->subControlRect(
+ QStyle::CC_SpinBox, &sbo, QStyle::SC_SpinBoxEditField );
+ retVal = true;
+ contentRect.translate( boundingRect.left(), boundingRect.top() );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case ControlType::MenuPopup:
+ {
+ int h, w;
+ switch ( part ) {
+ case ControlPart::MenuItemCheckMark:
+ h = QApplication::style()->pixelMetric(QStyle::PM_IndicatorHeight);
+ w = QApplication::style()->pixelMetric(QStyle::PM_IndicatorWidth);
+ retVal = true;
+ break;
+ case ControlPart::MenuItemRadioMark:
+ h = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight);
+ w = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth);
+ retVal = true;
+ break;
+ default:
+ break;
+ }
+ if (retVal) {
+ contentRect = QRect(0, 0, w, h);
+ boundingRect = contentRect;
+ }
+ break;
+ }
+ case ControlType::Frame:
+ {
+ if( part == ControlPart::Border )
+ {
+ auto nStyle = static_cast<DrawFrameFlags>(
+ val.getNumericVal() & 0xFFF0);
+ if( nStyle & DrawFrameFlags::NoDraw )
+ {
+ int nFrameWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ contentRect.adjust(nFrameWidth, nFrameWidth, -nFrameWidth, -nFrameWidth);
+ }
+ retVal = true;
+ }
+ break;
+ }
+ case ControlType::Radiobutton:
+ {
+ const int h = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight);
+ const int w = QApplication::style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth);
+
+ contentRect = QRect(boundingRect.left(), boundingRect.top(), w, h);
+ contentRect.adjust(0, 0,
+ 2 * QApplication::style()->pixelMetric(
+ QStyle::PM_FocusFrameHMargin, &styleOption),
+ 2 * QApplication::style()->pixelMetric(
+ QStyle::PM_FocusFrameVMargin, &styleOption)
+ );
+ boundingRect = contentRect;
+
+ retVal = true;
+ break;
+ }
+ case ControlType::Slider:
+ {
+ const int w = QApplication::style()->pixelMetric(QStyle::PM_SliderLength);
+ if( part == ControlPart::ThumbHorz )
+ {
+ contentRect = QRect(boundingRect.left(), boundingRect.top(), w, boundingRect.height());
+ boundingRect = contentRect;
+ retVal = true;
+ }
+ else if( part == ControlPart::ThumbVert )
+ {
+ contentRect = QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), w);
+ boundingRect = contentRect;
+ retVal = true;
+ }
+ break;
+ }
+ case ControlType::Toolbar:
+ {
+ const int nWorH = QApplication::style()->pixelMetric(QStyle::PM_ToolBarHandleExtent);
+ if( part == ControlPart::ThumbHorz )
+ {
+ contentRect = QRect(boundingRect.left(), boundingRect.top(), boundingRect.width(), nWorH );
+ boundingRect = contentRect;
+ retVal = true;
+ }
+ else if( part == ControlPart::ThumbVert )
+ {
+ contentRect = QRect(boundingRect.left(), boundingRect.top(), nWorH, boundingRect.height() );
+ boundingRect = contentRect;
+ retVal = true;
+ }
+ break;
+ }
+ case ControlType::Scrollbar:
+ {
+ // core can't handle 3-button scrollbars well, so we fix that in hitTestNativeControl(),
+ // for the rest also provide the track area (i.e. area not taken by buttons)
+ if( part == ControlPart::TrackVertArea || part == ControlPart::TrackHorzArea )
+ {
+ QStyleOptionSlider option;
+ bool horizontal = ( part == ControlPart::TrackHorzArea ); //horizontal or vertical
+ option.orientation = horizontal ? Qt::Horizontal : Qt::Vertical;
+ if( horizontal )
+ option.state |= QStyle::State_Horizontal;
+ // getNativeControlRegion usually gets ImplControlValue as 'val' (i.e. not the proper
+ // subclass), so use random sensible values (doesn't matter anyway, as the wanted
+ // geometry here depends only on button sizes)
+ option.maximum = 10;
+ option.minimum = 0;
+ option.sliderPosition = option.sliderValue = 4;
+ option.pageStep = 2;
+ // Adjust coordinates to make the widget appear to be at (0,0), i.e. make
+ // widget and screen coordinates the same. QStyle functions should use screen
+ // coordinates but at least QPlastiqueStyle::subControlRect() is buggy
+ // and sometimes uses widget coordinates.
+ QRect rect = contentRect;
+ rect.moveTo( 0, 0 );
+ option.rect = rect;
+ rect = QApplication::style()->subControlRect( QStyle::CC_ScrollBar, &option,
+ QStyle::SC_ScrollBarGroove );
+ rect.translate( contentRect.topLeft()); // reverse the workaround above
+ contentRect = boundingRect = rect;
+ retVal = true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (retVal)
+ {
+ // Bounding region
+ Point aBPoint( boundingRect.x(), boundingRect.y() );
+ Size aBSize( boundingRect.width(), boundingRect.height() );
+ nativeBoundingRegion = tools::Rectangle( aBPoint, aBSize );
+
+ // vcl::Region of the content
+ Point aPoint( contentRect.x(), contentRect.y() );
+ Size aSize( contentRect.width(), contentRect.height() );
+ nativeContentRegion = tools::Rectangle( aPoint, aSize );
+ }
+
+ return retVal;
+}
+
+/** Test whether the position is in the native widget.
+ If the return value is TRUE, bIsInside contains information whether
+ aPos was or was not inside the native widget specified by the
+ nType/nPart combination.
+*/
+bool KDESalGraphics::hitTestNativeControl( ControlType nType, ControlPart nPart,
+ const tools::Rectangle& rControlRegion, const Point& rPos,
+ bool& rIsInside )
+{
+ if ( nType == ControlType::Scrollbar )
+ {
+ if( nPart != ControlPart::ButtonUp && nPart != ControlPart::ButtonDown
+ && nPart != ControlPart::ButtonLeft && nPart != ControlPart::ButtonRight )
+ { // we adjust only for buttons (because some scrollbars have 3 buttons,
+ // and LO core doesn't handle such scrollbars well)
+ return FALSE;
+ }
+ rIsInside = FALSE;
+ bool bHorizontal = ( nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight );
+ QRect rect = region2QRect( rControlRegion );
+ QPoint pos( rPos.X(), rPos.Y());
+ // Adjust coordinates to make the widget appear to be at (0,0), i.e. make
+ // widget and screen coordinates the same. QStyle functions should use screen
+ // coordinates but at least QPlastiqueStyle::subControlRect() is buggy
+ // and sometimes uses widget coordinates.
+ pos -= rect.topLeft();
+ rect.moveTo( 0, 0 );
+ QStyleOptionSlider options;
+ options.orientation = bHorizontal ? Qt::Horizontal : Qt::Vertical;
+ if( bHorizontal )
+ options.state |= QStyle::State_Horizontal;
+ options.rect = rect;
+ // some random sensible values, since we call this code only for scrollbar buttons,
+ // the slider position does not exactly matter
+ options.maximum = 10;
+ options.minimum = 0;
+ options.sliderPosition = options.sliderValue = 4;
+ options.pageStep = 2;
+ QStyle::SubControl control = QApplication::style()->hitTestComplexControl( QStyle::CC_ScrollBar, &options, pos );
+ if( nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonLeft )
+ rIsInside = ( control == QStyle::SC_ScrollBarSubLine );
+ else // DOWN, RIGHT
+ rIsInside = ( control == QStyle::SC_ScrollBarAddLine );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalGraphics.hxx b/vcl/unx/kde5/KDE5SalGraphics.hxx
new file mode 100644
index 000000000000..eb513388f369
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalGraphics.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <rtl/string.hxx>
+#include <unx/saldisp.hxx>
+#include <unx/salgdi.h>
+
+#include <QtGui/QImage>
+
+/**
+ * Handles native graphics requests and performs the needed drawing operations.
+ */
+class KDESalGraphics : public X11SalGraphics
+{
+public:
+ virtual bool IsNativeControlSupported( ControlType, ControlPart ) override;
+
+ virtual bool hitTestNativeControl( ControlType, ControlPart,
+ const tools::Rectangle&, const Point&, bool& ) override;
+
+ virtual bool drawNativeControl( ControlType, ControlPart, const tools::Rectangle&,
+ ControlState, const ImplControlValue&, const OUString& ) override;
+
+ virtual bool getNativeControlRegion( ControlType, ControlPart, const tools::Rectangle&,
+ ControlState, const ImplControlValue&,
+ const OUString&, tools::Rectangle&, tools::Rectangle& ) override;
+
+private:
+ std::unique_ptr<QImage> m_image;
+ QRect lastPopupRect;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalInstance.cxx b/vcl/unx/kde5/KDE5SalInstance.cxx
new file mode 100644
index 000000000000..5d4bc5eec5dd
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalInstance.cxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+//#include "KDE4FilePicker.hxx"
+#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QThread>
+#include <QtGui/QClipboard>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QFrame>
+#include <QtX11Extras/QX11Info>
+
+
+#include "KDE5SalInstance.hxx"
+#include "KDE5SalFrame.hxx"
+#include "KDE5XLib.hxx"
+#include "KDE5SalDisplay.hxx"
+
+using namespace com::sun::star;
+
+KDESalInstance::KDESalInstance(SalYieldMutex* pMutex)
+ : X11SalInstance(pMutex)
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ delete pSVData->maAppData.mpToolkitName;
+ pSVData->maAppData.mpToolkitName = new OUString("kde4");
+}
+
+SalFrame* KDESalInstance::CreateFrame( SalFrame *pParent, SalFrameStyleFlags nState )
+{
+ return new KDESalFrame( pParent, nState );
+}
+
+uno::Reference< ui::dialogs::XFilePicker2 > KDESalInstance::createFilePicker(
+ const uno::Reference< uno::XComponentContext >& xMSF )
+{
+ /*KDEXLib* kdeXLib = static_cast<KDEXLib*>( mpXLib );
+ if (kdeXLib->allowKdeDialogs())
+ return uno::Reference< ui::dialogs::XFilePicker2 >(
+ kdeXLib->createFilePicker(xMSF) );
+ else*/
+ return X11SalInstance::createFilePicker( xMSF );
+}
+
+SalX11Display* KDESalInstance::CreateDisplay() const
+{
+ return new SalKDEDisplay( QX11Info::display() );
+}
+
+bool KDESalInstance::IsMainThread() const
+{
+ return qApp->thread() == QThread::currentThread();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5SalInstance.hxx b/vcl/unx/kde5/KDE5SalInstance.hxx
new file mode 100644
index 000000000000..7b7417caaaf1
--- /dev/null
+++ b/vcl/unx/kde5/KDE5SalInstance.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <unx/salinst.h>
+
+class SalYieldMutex;
+class SalFrame;
+
+class KDESalInstance : public X11SalInstance
+{
+protected:
+ virtual SalX11Display* CreateDisplay() const override;
+
+public:
+ explicit KDESalInstance(SalYieldMutex* pMutex);
+ virtual SalFrame* CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ) override;
+
+ virtual bool hasNativeFileSelection() const override { return true; }
+
+ virtual css::uno::Reference< css::ui::dialogs::XFilePicker2 >
+ createFilePicker( const css::uno::Reference<
+ css::uno::XComponentContext >& ) override;
+
+ virtual bool IsMainThread() const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5XLib.cxx b/vcl/unx/kde5/KDE5XLib.cxx
new file mode 100644
index 000000000000..b05e8e6ed047
--- /dev/null
+++ b/vcl/unx/kde5/KDE5XLib.cxx
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QThread>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QFrame>
+#include <QtGui/QClipboard>
+#include <QtGui/QFrame>
+#include <QtX11Extras/QX11Info>
+
+
+#include "config_kde5.h"
+
+#include "KDE5XLib.hxx"
+#include "VCLKDE5Application.hxx"
+#include "KDE5SalInstance.hxx"
+
+#include <KAboutData>
+#include <KLocalizedString>
+
+#include "unx/i18n_im.hxx"
+#include "unx/i18n_xkb.hxx"
+#include "unx/saldata.hxx"
+#include <o3tl/make_unique.hxx>
+#include "osl/process.h"
+
+#include "KDE5SalDisplay.hxx"
+
+#undef Bool
+
+#if KF5_HAVE_GLIB
+//#include "KDE4FilePicker.hxx"
+#include "tst_exclude_socket_notifiers.moc"
+#include "tst_exclude_posted_events.moc"
+#endif
+
+KDEXLib::KDEXLib() :
+ SalXLib(), m_bStartupDone(false),
+ m_nFakeCmdLineArgs( 0 ),
+ m_isGlibEventLoopType(false), m_allowKdeDialogs(false),
+ m_timerEventId( -1 ), m_postUserEventId( -1 )
+{
+ m_timerEventId = QEvent::registerEventType();
+ m_postUserEventId = QEvent::registerEventType();
+
+ // the timers created here means they belong to the main thread.
+ // As the timeoutTimer runs the LO event queue, which may block on a dialog,
+ // the timer has to use a Qt::QueuedConnection, otherwise the nested event
+ // loop will detect the blocking timer and drop it from the polling
+ // freezing LO X11 processing.
+ timeoutTimer.setSingleShot( true );
+ connect( &timeoutTimer, SIGNAL( timeout()), this, SLOT( timeoutActivated()), Qt::QueuedConnection );
+
+ // QTimer::start() can be called only in its (here main) thread, so this will
+ // forward between threads if needed
+ connect( this, SIGNAL( startTimeoutTimerSignal()), this, SLOT( startTimeoutTimer()), Qt::QueuedConnection );
+
+ // this one needs to be blocking, so that the handling in main thread is processed before
+ // the thread emitting the signal continues
+ connect( this, SIGNAL( processYieldSignal( bool, bool )), this, SLOT( processYield( bool, bool )),
+ Qt::BlockingQueuedConnection );
+
+ // Create the File picker in the main / GUI thread and block the calling thread until
+ // the FilePicker is created.
+ connect( this, SIGNAL( createFilePickerSignal( const css::uno::Reference< css::uno::XComponentContext >&) ),
+ this, SLOT( createFilePicker( const css::uno::Reference< css::uno::XComponentContext >&) ),
+ Qt::BlockingQueuedConnection );
+}
+
+KDEXLib::~KDEXLib()
+{
+
+ // free the faked cmdline arguments no longer needed by KApplication
+ for( int i = 0; i < m_nFakeCmdLineArgs; i++ )
+ {
+ free( m_pFreeCmdLineArgs[i] );
+ }
+}
+
+void KDEXLib::Init()
+{
+ m_pInputMethod = new SalI18N_InputMethod;
+ m_pInputMethod->SetLocale();
+ XrmInitialize();
+
+ KAboutData *kAboutData = new KAboutData( I18N_NOOP("LibreOffice"),
+ i18n( "LibreOffice" ),
+ "5.1.0",
+ i18n( "LibreOffice with KDE Native Widget Support." ),
+ KAboutLicense::File,
+ i18n("Copyright (c) 2000, 2014 LibreOffice contributors" ),
+ i18n( "LibreOffice is an office suite.\n" ),
+ "http://libreoffice.org",
+ QLatin1String("libreoffice@lists.freedesktop.org"));
+
+ kAboutData->addAuthor( i18n( "Jan Holesovsky" ),
+ i18n( "Original author and maintainer of the KDE NWF." ),
+ "kendy@artax.karlin.mff.cuni.cz",
+ "http://artax.karlin.mff.cuni.cz/~kendy" );
+ kAboutData->addAuthor( i18n("Roman Shtylman"),
+ i18n( "Porting to KDE 4." ),
+ "shtylman@gmail.com", "http://shtylman.com" );
+ kAboutData->addAuthor( i18n("Eric Bischoff"),
+ i18n( "Accessibility fixes, porting to KDE 4." ),
+ "bischoff@kde.org" );
+
+ m_nFakeCmdLineArgs = 2;
+
+ sal_uInt32 nParams = osl_getCommandArgCount();
+ OString aDisplay;
+ OUString aParam, aBin;
+
+ for ( sal_uInt32 nIdx = 0; nIdx < nParams; ++nIdx )
+ {
+ osl_getCommandArg( nIdx, &aParam.pData );
+ if ( !m_pFreeCmdLineArgs && aParam == "-display" && nIdx + 1 < nParams )
+ {
+ osl_getCommandArg( nIdx + 1, &aParam.pData );
+ aDisplay = OUStringToOString( aParam, osl_getThreadTextEncoding() );
+
+ m_pFreeCmdLineArgs = o3tl::make_unique<char*[]>(m_nFakeCmdLineArgs + 2);
+ m_pFreeCmdLineArgs[ m_nFakeCmdLineArgs + 0 ] = strdup( "-display" );
+ m_pFreeCmdLineArgs[ m_nFakeCmdLineArgs + 1 ] = strdup( aDisplay.getStr() );
+ m_nFakeCmdLineArgs += 2;
+ }
+ }
+ if ( !m_pFreeCmdLineArgs )
+ m_pFreeCmdLineArgs = o3tl::make_unique<char*[]>(m_nFakeCmdLineArgs);
+
+ osl_getExecutableFile( &aParam.pData );
+ osl_getSystemPathFromFileURL( aParam.pData, &aBin.pData );
+ OString aExec = OUStringToOString( aBin, osl_getThreadTextEncoding() );
+ m_pFreeCmdLineArgs[0] = strdup( aExec.getStr() );
+ m_pFreeCmdLineArgs[1] = strdup( "--nocrashhandler" );
+
+ // make a copy of the string list for freeing it since
+ // KApplication manipulates the pointers inside the argument vector
+ // note: KApplication bad !
+ m_pAppCmdLineArgs = o3tl::make_unique<char*[]>(m_nFakeCmdLineArgs);
+ for( int i = 0; i < m_nFakeCmdLineArgs; i++ )
+ m_pAppCmdLineArgs[i] = m_pFreeCmdLineArgs[i];
+
+ // LO does its own session management, so prevent KDE/Qt from interfering
+ // (QApplication::disableSessionManagement(false) wouldn't quite do,
+ // since that still actually connects to the session manager, it just
+ // won't save the application data on session shutdown).
+ char* session_manager = nullptr;
+ if( getenv( "SESSION_MANAGER" ) != nullptr )
+ {
+ session_manager = strdup( getenv( "SESSION_MANAGER" ));
+ unsetenv( "SESSION_MANAGER" );
+ }
+ //m_pApplication.reset( new VCLKDEApplication() );
+ if( session_manager != nullptr )
+ {
+ // coverity[tainted_string] - trusted source for setenv
+ setenv( "SESSION_MANAGER", session_manager, 1 );
+ free( session_manager );
+ }
+
+ //KApplication::setQuitOnLastWindowClosed(false);
+
+#if KF5_HAVE_GLIB
+ m_isGlibEventLoopType = QAbstractEventDispatcher::instance()->inherits( "QEventDispatcherGlib" );
+ // Using KDE dialogs (and their nested event loops) works only with a proper event loop integration
+ // that will release SolarMutex when waiting for more events.
+ // Moreover there are bugs in Qt event loop code that allow QClipboard recursing because the event
+ // loop processes also events that it should not at that point, so no dialogs in that case either.
+ // https://bugreports.qt-project.org/browse/QTBUG-37380
+ // https://bugreports.qt-project.org/browse/QTBUG-34614
+ if (m_isGlibEventLoopType && (0 == tst_processEventsExcludeSocket()) && tst_excludePostedEvents() == 0 )
+ m_allowKdeDialogs = true;
+#endif
+
+ setupEventLoop();
+
+ m_pDisplay = QX11Info::display();
+}
+
+// When we use Qt event loop, it can actually use its own event loop handling, or wrap
+// the Glib event loop (the latter is the default is Qt is built with Glib support
+// and $QT_NO_GLIB is not set). We mostly do not care which one it is, as QSocketNotifier's
+// and QTimer's can handle it transparently, but it matters for the SolarMutex, which
+// needs to be unlocked shortly before entering the main sleep (e.g. select()) and locked
+// immediately after. So we need to know which event loop implementation is used and
+// hook accordingly.
+#if KF5_HAVE_GLIB
+#include <glib.h>
+
+static GPollFunc old_gpoll = nullptr;
+
+static gint gpoll_wrapper( GPollFD* ufds, guint nfds, gint timeout )
+{
+ SalYieldMutexReleaser release; // release YieldMutex (and re-acquire at block end)
+ return old_gpoll( ufds, nfds, timeout );
+}
+#endif
+
+/*static bool ( *old_qt_event_filter )( void* );
+static bool qt_event_filter( void* m )
+{
+ if( old_qt_event_filter != nullptr && old_qt_event_filter( m ))
+ return true;
+ if( SalKDEDisplay::self() && SalKDEDisplay::self()->checkDirectInputEvent( static_cast< XEvent* >( m )))
+ return true;
+ return false;
+}*/
+
+bool KDEXLib::nativeEventFilter(const QByteArray &eventType, void *message, long *)
+{
+ if (eventType == "xcb_generic_event_t") {
+ xcb_generic_event_t* ev = static_cast<xcb_generic_event_t *>(message);
+ if( SalKDEDisplay::self() && SalKDEDisplay::self()->checkDirectInputEvent( ev ))
+ return true;
+ }
+ return false;
+}
+
+void KDEXLib::setupEventLoop()
+{
+ QAbstractEventDispatcher::instance()->installNativeEventFilter( this );
+#if KF5_HAVE_GLIB
+ if( m_isGlibEventLoopType )
+ {
+ old_gpoll = g_main_context_get_poll_func( nullptr );
+ g_main_context_set_poll_func( nullptr, gpoll_wrapper );
+ if( m_allowKdeDialogs )
+ QApplication::clipboard()->setProperty( "useEventLoopWhenWaiting", true );
+ return;
+ }
+#endif
+}
+
+void KDEXLib::Insert( int fd, void* data, YieldFunc pending, YieldFunc queued, YieldFunc handle )
+{
+ if( !m_isGlibEventLoopType )
+ return SalXLib::Insert( fd, data, pending, queued, handle );
+ SocketData sdata;
+ sdata.data = data;
+ sdata.pending = pending;
+ sdata.queued = queued;
+ sdata.handle = handle;
+ // qApp as parent to make sure it uses the main thread event loop
+ sdata.notifier = new QSocketNotifier( fd, QSocketNotifier::Read, qApp );
+ connect( sdata.notifier, SIGNAL( activated( int )), this, SLOT( socketNotifierActivated( int )));
+ socketData[ fd ] = sdata;
+}
+
+void KDEXLib::Remove( int fd )
+{
+ if( !m_isGlibEventLoopType )
+ return SalXLib::Remove( fd );
+ SocketData sdata = socketData.take( fd );// according to SalXLib::Remove() this should be safe
+ delete sdata.notifier;
+}
+
+void KDEXLib::socketNotifierActivated( int fd )
+{
+ const SocketData& sdata = socketData[ fd ];
+ sdata.handle( fd, sdata.data );
+}
+
+bool KDEXLib::Yield( bool bWait, bool bHandleAllCurrentEvents )
+{
+ if( !m_isGlibEventLoopType )
+ {
+ bool wasEvent = false;
+ if( qApp->thread() == QThread::currentThread())
+ {
+ // even if we use the LO event loop, still process Qt's events,
+ // otherwise they can remain unhandled for quite a long while
+ wasEvent = processYield( false, bHandleAllCurrentEvents );
+ }
+ return SalXLib::Yield(bWait, bHandleAllCurrentEvents) || wasEvent;
+ }
+ // if we are the main thread (which is where the event processing is done),
+ // good, just do it
+ if( qApp->thread() == QThread::currentThread())
+ {
+ return processYield( bWait, bHandleAllCurrentEvents );
+ }
+ else
+ {
+ // we were called from another thread;
+ // release the yield lock to prevent deadlock with the main thread
+ // (it's ok to release it here, since even normal processYield() would
+ // temporarily do it while checking for new events)
+ SalYieldMutexReleaser aReleaser;
+ Q_EMIT processYieldSignal( bWait, bHandleAllCurrentEvents );
+ return false;
+ }
+}
+
+/**
+ * Quoting the Qt docs: [QAbstractEventDispatcher::processEvents] processes
+ * pending events that match flags until there are no more events to process.
+ */
+bool KDEXLib::processYield( bool bWait, bool )
+{
+ QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance( qApp->thread());
+ bool wasEvent = false;
+ if ( bWait )
+ wasEvent = dispatcher->processEvents( QEventLoop::WaitForMoreEvents );
+ else
+ wasEvent = dispatcher->processEvents( QEventLoop::AllEvents );
+ return wasEvent;
+}
+
+void KDEXLib::StartTimer( sal_uLong nMS )
+{
+ if( !m_isGlibEventLoopType )
+ return SalXLib::StartTimer( nMS );
+ timeoutTimer.setInterval( nMS );
+ // QTimer's can be started only in their thread (main thread here)
+ if( qApp->thread() == QThread::currentThread())
+ startTimeoutTimer();
+ else
+ Q_EMIT startTimeoutTimerSignal();
+}
+
+void KDEXLib::startTimeoutTimer()
+{
+ timeoutTimer.start();
+}
+
+void KDEXLib::StopTimer()
+{
+ if( !m_isGlibEventLoopType )
+ return SalXLib::StopTimer();
+ timeoutTimer.stop();
+}
+
+void KDEXLib::timeoutActivated()
+{
+ // don't potentially wait in timeout, as QTimer is non-recursive
+ QApplication::postEvent(this, new QEvent(QEvent::Type( m_timerEventId )));
+}
+
+void KDEXLib::customEvent(QEvent* e)
+{
+ if( e->type() == m_timerEventId )
+ X11SalData::Timeout();
+ else if( e->type() == m_postUserEventId )
+ SalKDEDisplay::self()->DispatchInternalEvent();
+}
+
+void KDEXLib::Wakeup()
+{
+ if( !m_isGlibEventLoopType )
+ return SalXLib::Wakeup();
+ QAbstractEventDispatcher::instance( qApp->thread())->wakeUp(); // main thread event loop
+}
+
+void KDEXLib::PostUserEvent()
+{
+ if( !m_isGlibEventLoopType )
+ return SalXLib::PostUserEvent();
+ QApplication::postEvent(this, new QEvent(QEvent::Type( m_postUserEventId )));
+}
+
+void KDEXLib::doStartup()
+{
+ if( ! m_bStartupDone )
+ {
+ //KStartupInfo::appStarted();
+ m_bStartupDone = true;
+ SAL_INFO( "vcl.kde4", "called KStartupInfo::appStarted()" );
+ }
+}
+
+using namespace com::sun::star;
+
+uno::Reference< ui::dialogs::XFilePicker2 > KDEXLib::createFilePicker(
+ const uno::Reference< uno::XComponentContext >& xMSF )
+{
+#if KF5_HAVE_GLIB
+ if( qApp->thread() != QThread::currentThread()) {
+ SalYieldMutexReleaser aReleaser;
+ return Q_EMIT createFilePickerSignal( xMSF );
+ }
+ return uno::Reference< ui::dialogs::XFilePicker2 >( new KDE4FilePicker( xMSF ) );
+#else
+ (void)xMSF;
+ return NULL;
+#endif
+}
+
+#include "KDE5XLib.moc"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/KDE5XLib.hxx b/vcl/unx/kde5/KDE5XLib.hxx
new file mode 100644
index 000000000000..cb506edea1c6
--- /dev/null
+++ b/vcl/unx/kde5/KDE5XLib.hxx
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <unx/saldisp.hxx>
+
+//#include <fixx11h.h>
+//
+#include <QtCore/QObject>
+#include <QtCore/QHash>
+#include <QtCore/QSocketNotifier>
+#include <QtCore/QTimer>
+#include <QtCore/QAbstractNativeEventFilter>
+
+#include <unx/salinst.h>
+
+class VCLKDEApplication;
+
+class KDEXLib : public QObject, public QAbstractNativeEventFilter, public SalXLib
+{
+ Q_OBJECT
+ private:
+ bool m_bStartupDone;
+ std::unique_ptr<VCLKDEApplication> m_pApplication;
+ std::unique_ptr<char*[]> m_pFreeCmdLineArgs;
+ std::unique_ptr<char*[]> m_pAppCmdLineArgs;
+ int m_nFakeCmdLineArgs;
+ struct SocketData
+ {
+ void* data;
+ YieldFunc pending;
+ YieldFunc queued;
+ YieldFunc handle;
+ QSocketNotifier* notifier;
+ };
+ QHash< int, SocketData > socketData; // key is fd
+ QTimer timeoutTimer;
+ bool m_isGlibEventLoopType;
+ bool m_allowKdeDialogs;
+ int m_timerEventId;
+ int m_postUserEventId;
+
+ private:
+ void setupEventLoop();
+
+ private Q_SLOTS:
+ void socketNotifierActivated( int fd );
+ void timeoutActivated();
+ void startTimeoutTimer();
+ static bool processYield( bool bWait, bool bHandleAllCurrentEvents );
+
+ Q_SIGNALS:
+ void startTimeoutTimerSignal();
+ void processYieldSignal( bool bWait, bool bHandleAllCurrentEvents );
+ css::uno::Reference< css::ui::dialogs::XFilePicker2 >
+ createFilePickerSignal( const css::uno::Reference< css::uno::XComponentContext >& );
+
+ public:
+ KDEXLib();
+ virtual ~KDEXLib() override;
+
+ virtual void Init() override;
+ virtual bool Yield( bool bWait, bool bHandleAllCurrentEvents ) override;
+ virtual void Insert( int fd, void* data, YieldFunc pending, YieldFunc queued, YieldFunc handle ) override;
+ virtual void Remove( int fd ) override;
+ virtual void StartTimer( sal_uLong nMS ) override;
+ virtual void StopTimer() override;
+ virtual void Wakeup() override;
+ virtual void PostUserEvent() override;
+
+ void doStartup();
+ bool allowKdeDialogs() { return m_allowKdeDialogs; }
+
+ virtual void customEvent(QEvent* e) override;
+
+ public Q_SLOTS:
+ css::uno::Reference< css::ui::dialogs::XFilePicker2 >
+ createFilePicker( const css::uno::Reference< css::uno::XComponentContext >& );
+ public:
+ virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/VCLKDE5Application.cxx b/vcl/unx/kde5/VCLKDE5Application.cxx
new file mode 100644
index 000000000000..cea68a3321f8
--- /dev/null
+++ b/vcl/unx/kde5/VCLKDE5Application.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "VCLKDE5Application.hxx"
+
+#include <QtGui/QClipboard>
+#include <QtCore/QEvent>
+
+#include "KDE5SalDisplay.hxx"
+
+VCLKDEApplication::VCLKDEApplication() :
+ KApplication()
+{
+}
+
+// various hacks to be performed before re-entering Qt's event loop
+// because of showing a Qt dialog
+void VCLKDEApplication::preDialogSetup()
+{
+ // KFileDialog integration requires using event loop with QClipboard.
+ // Opening the KDE file dialog here can lead to QClipboard
+ // asking for clipboard contents. If LO core is the owner of the clipboard
+ // content, without event loop use this will block for 5 seconds and timeout,
+ // since the clipboard thread will not be able to acquire SolarMutex
+ // and thus won't be able to respond. If the event loops
+ // are properly integrated and QClipboard can use a nested event loop
+ // (see the KDE VCL plug), then this won't happen.
+ // We cannot simply release SolarMutex here, because the event loop started
+ // by the file dialog would also call back to LO code.
+ assert( QApplication::clipboard()->property( "useEventLoopWhenWaiting" ).toBool() );
+}
+
+// various hacks to be performed after a Qt dialog has been closed
+void VCLKDEApplication::postDialogCleanup()
+{
+ // HACK: KFileDialog uses KConfig("kdeglobals") for saving some settings
+ // (such as the auto-extension flag), but that doesn't update KGlobal::config()
+ // (which is probably a KDE bug), so force reading the new configuration,
+ // otherwise the next opening of the dialog would use the old settings.
+ KGlobal::config()->reparseConfiguration();
+ // HACK: If Qt owns clipboard or selection, give up on their ownership now. Otherwise
+ // LO core might ask for the contents, but it would block while doing so (i.e. it
+ // doesn't seem to have an equivalent of QClipboard's "useEventLoopWhenWaiting"),
+ // therefore QClipboard wouldn't be able to respond, and whole LO would block until
+ // a timeout. Given that Klipper is most probably running, giving up clipboard/selection
+ // ownership will not only avoid the blocking, but even pasting that content in LO
+ // will in fact work, if Klipper can handle it.
+ // Technically proper solution would be of course to allow Qt to process QClipboard
+ // events while LO waits for clipboard contents, or short-circuit to QClipboard somehow
+ // (it's a mystery why LO's clipboard handling has its own thread when whole LO can
+ // get blocked by both trying to send and receive clipboard contents anyway).
+ QClipboard* clipboard = QApplication::clipboard();
+ if( clipboard->ownsSelection())
+ clipboard->clear( QClipboard::Selection );
+ if( clipboard->ownsClipboard())
+ clipboard->clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/VCLKDE5Application.hxx b/vcl/unx/kde5/VCLKDE5Application.hxx
new file mode 100644
index 000000000000..6d2b6f2fdae1
--- /dev/null
+++ b/vcl/unx/kde5/VCLKDE5Application.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef _VCLKDE5APPLICATION_HXX_
+#define _VCLKDE5APPLICATION_HXX_
+
+#pragma once
+
+#include <QtWidgets/QApplication>
+#include <QtCore/QAbstractNativeEventFilter>
+
+#undef Region
+
+class VCLKDEApplication : public QApplication, public QAbstractNativeEventFilter
+{
+ public:
+ VCLKDEApplication();
+ static void preDialogSetup();
+ static void postDialogCleanup();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/main.cxx b/vcl/unx/kde5/main.cxx
new file mode 100644
index 000000000000..96af86e54607
--- /dev/null
+++ b/vcl/unx/kde5/main.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <QtGui/QApplication>
+
+#include "KDE5Data.hxx"
+#include "KDE5SalInstance.hxx"
+
+#include <vclpluginapi.h>
+
+#include <rtl/string.hxx>
+
+/// entry point for the KDE4 VCL plugin
+extern "C" {
+ VCLPLUG_KDE4_PUBLIC SalInstance* create_SalInstance()
+ {
+ /* #i92121# workaround deadlocks in the X11 implementation
+ */
+ static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" );
+ /* #i90094#
+ from now on we know that an X connection will be
+ established, so protect X against itself
+ */
+ if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
+ {
+#if QT_VERSION >= 0x040800
+ // let Qt call XInitThreads(), so that also Qt knows it's been used
+ // (otherwise QPixmap may warn about threads not being initialized)
+ QApplication::setAttribute( Qt::AA_X11InitThreads );
+#else
+ XInitThreads();
+ // just in case somebody builds with old version and then upgrades Qt,
+ // otherwise this is a no-op
+ QApplication::setAttribute( static_cast< Qt::ApplicationAttribute >( 10 ));
+#endif
+ }
+
+#if QT_VERSION < 0x050000
+ // Qt 4.x support needs >= 4.1.0
+ OString aVersion( qVersion() );
+ SAL_INFO( "vcl.kde4", "qt version string is " << aVersion );
+
+ sal_Int32 nIndex = 0, nMajor = 0, nMinor = 0;
+ nMajor = aVersion.getToken( 0, '.', nIndex ).toInt32();
+ if( nIndex > 0 )
+ nMinor = aVersion.getToken( 0, '.', nIndex ).toInt32();
+ if( nMajor != 4 || nMinor < 1 )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ sal_Int32 nMicro = 0;
+ if( nIndex > 0 )
+ nMicro = aVersion.getToken( 0, '.', nIndex ).toInt32();
+ SAL_INFO( "vcl.kde4", "unsuitable qt version " << nMajor << "." << nMinor << "." << nMicro );
+#endif
+ return nullptr;
+ }
+#endif
+
+ KDESalInstance* pInstance = new KDESalInstance( new SalYieldMutex() );
+ SAL_INFO( "vcl.kde4", "created KDESalInstance " << &pInstance );
+
+ // initialize SalData
+ KDEData *salData = new KDEData( pInstance );
+ salData->Init();
+ salData->initNWF();
+ pInstance->SetLib(salData->GetLib());
+
+ return pInstance;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/kde5/tst_exclude_posted_events.hxx b/vcl/unx/kde5/tst_exclude_posted_events.hxx
new file mode 100644
index 000000000000..c07ca895a9dc
--- /dev/null
+++ b/vcl/unx/kde5/tst_exclude_posted_events.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ *
+ * This code is based on the SocketEventsTester from the Qt4 test suite.
+ */
+
+#pragma once
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QEventLoop>
+
+const QEvent::Type eventType = QEvent::User;
+
+class TestExcludePostedEvents
+ : public QObject
+{
+ Q_OBJECT
+ public:
+ TestExcludePostedEvents();
+ virtual bool event( QEvent* e ) override;
+ bool processed;
+};
+
+TestExcludePostedEvents::TestExcludePostedEvents()
+ : processed( false )
+{
+}
+
+bool TestExcludePostedEvents::event( QEvent* e )
+{
+ if( e->type() == eventType )
+ processed = true;
+ return QObject::event( e );
+}
+
+#define QVERIFY(a) \
+ if (!a) return 1;
+
+static int tst_excludePostedEvents()
+{
+ TestExcludePostedEvents test;
+ QCoreApplication::postEvent( &test, new QEvent( eventType ));
+ QEventLoop loop;
+ loop.processEvents(QEventLoop::ExcludeUserInputEvents
+ | QEventLoop::ExcludeSocketNotifiers
+// | QEventLoop::WaitForMoreEvents
+ | QEventLoop::X11ExcludeTimers);
+ QVERIFY( !test.processed );
+ loop.processEvents();
+ QVERIFY( test.processed );
+ return 0;
+}
diff --git a/vcl/unx/kde5/tst_exclude_socket_notifiers.hxx b/vcl/unx/kde5/tst_exclude_socket_notifiers.hxx
new file mode 100644
index 000000000000..d0acafede239
--- /dev/null
+++ b/vcl/unx/kde5/tst_exclude_socket_notifiers.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ *
+ * This code is based on the SocketEventsTester from the Qt4 test suite.
+ */
+
+#pragma once
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QEventLoop>
+#include <QtCore/QSocketNotifier>
+#include <unistd.h>
+
+class TestExcludeSocketNotifiers
+ : public QObject
+{
+ Q_OBJECT
+ public:
+ TestExcludeSocketNotifiers( const int* pipes );
+ virtual ~TestExcludeSocketNotifiers() override;
+ bool received;
+ public slots:
+ void slotReceived();
+ private:
+ const int* pipes;
+};
+
+TestExcludeSocketNotifiers::TestExcludeSocketNotifiers( const int* thePipes )
+ : received( false )
+ , pipes( thePipes )
+{
+}
+
+TestExcludeSocketNotifiers::~TestExcludeSocketNotifiers()
+{
+ close( pipes[ 0 ] );
+ close( pipes[ 1 ] );
+}
+
+void TestExcludeSocketNotifiers::slotReceived()
+{
+ received = true;
+}
+
+#define QVERIFY(a) \
+ if (!a) return 1;
+
+static int tst_processEventsExcludeSocket()
+{
+ int pipes[ 2 ];
+ if( pipe( pipes ) < 0 )
+ return 1;
+ TestExcludeSocketNotifiers test( pipes );
+ QSocketNotifier notifier( pipes[ 0 ], QSocketNotifier::Read );
+ QObject::connect( &notifier, SIGNAL( activated( int )), &test, SLOT( slotReceived()));
+ char dummy = 'a';
+ if( 1 != write( pipes[ 1 ], &dummy, 1 ) )
+ return 1;
+ QEventLoop loop;
+ loop.processEvents( QEventLoop::ExcludeSocketNotifiers );
+ QVERIFY( !test.received );
+ loop.processEvents();
+ QVERIFY( test.received );
+ return 0;
+}