1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 #include <osl/process.h>
13 #include <unx/gtk/gtkdata.hxx>
14 #include <unx/gtk/gtkinst.hxx>
15 #include <unx/salobj.h>
16 #include <unx/gtk/gtkgdi.hxx>
17 #include <unx/gtk/gtkframe.hxx>
18 #include <unx/gtk/gtkobject.hxx>
19 #include <unx/gtk/atkbridge.hxx>
20 #include <unx/gtk/gtkprn.hxx>
21 #include <unx/gtk/gtksalmenu.hxx>
22 #include <headless/svpvd.hxx>
23 #include <headless/svpbmp.hxx>
24 #include <vcl/inputtypes.hxx>
25 #include <unx/genpspgraphics.h>
26 #include <rtl/strbuf.hxx>
27 #include <sal/log.hxx>
28 #include <rtl/uri.hxx>
30 #include <vcl/settings.hxx>
36 #include <unx/gtk/gtkprintwrapper.hxx>
38 #include "a11y/atkwrapper.hxx"
39 #include <com/sun/star/lang/IllegalArgumentException.hpp>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <com/sun/star/lang/XServiceInfo.hpp>
42 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
43 #include <com/sun/star/lang/XInitialization.hpp>
44 #include <com/sun/star/datatransfer/XTransferable.hpp>
45 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
46 #include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp>
47 #include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
48 #include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp>
49 #include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
50 #include <com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp>
51 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
52 #include <comphelper/processfactory.hxx>
53 #include <comphelper/sequence.hxx>
54 #include <cppuhelper/compbase.hxx>
55 #include <comphelper/string.hxx>
56 #include <cppuhelper/implbase.hxx>
57 #include <cppuhelper/supportsservice.hxx>
58 #include <officecfg/Office/Common.hxx>
59 #include <rtl/bootstrap.hxx>
60 #include <svl/zforlist.hxx>
61 #include <svl/zformat.hxx>
62 #include <tools/helpers.hxx>
63 #include <tools/fract.hxx>
64 #include <tools/stream.hxx>
65 #include <unotools/resmgr.hxx>
66 #include <unx/gstsink.hxx>
67 #include <vcl/ImageTree.hxx>
68 #include <vcl/abstdlg.hxx>
69 #include <vcl/button.hxx>
70 #include <vcl/event.hxx>
71 #include <vcl/i18nhelp.hxx>
72 #include <vcl/quickselectionengine.hxx>
73 #include <vcl/mnemonic.hxx>
74 #include <vcl/pngwrite.hxx>
75 #include <vcl/stdtext.hxx>
76 #include <vcl/syswin.hxx>
77 #include <vcl/virdev.hxx>
78 #include <vcl/weld.hxx>
79 #include <vcl/wrkwin.hxx>
80 #include <strings.hrc>
84 using namespace com::sun::star
;
85 using namespace com::sun::star::uno
;
86 using namespace com::sun::star::lang
;
90 #define GET_YIELD_MUTEX() static_cast<GtkYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex())
91 static void GdkThreadsEnter()
93 GtkYieldMutex
*pYieldMutex
= GET_YIELD_MUTEX();
94 pYieldMutex
->ThreadsEnter();
96 static void GdkThreadsLeave()
98 GtkYieldMutex
*pYieldMutex
= GET_YIELD_MUTEX();
99 pYieldMutex
->ThreadsLeave();
102 VCLPLUG_GTK_PUBLIC SalInstance
* create_SalInstance()
106 "create vcl plugin instance with gtk version " << gtk_major_version
107 << " " << gtk_minor_version
<< " " << gtk_micro_version
);
109 if (gtk_major_version
== 3 && gtk_minor_version
< 18)
111 g_warning("require gtk >= 3.18 for theme expectations");
115 // for gtk2 it is always built with X support, so this is always called
116 // for gtk3 it is normally built with X and Wayland support, if
117 // X is supported GDK_WINDOWING_X11 is defined and this is always
118 // called, regardless of if we're running under X or Wayland.
119 // We can't use (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) to only do it under
120 // X, because we need to do it earlier than we have a display
121 #if defined(GDK_WINDOWING_X11)
122 /* #i92121# workaround deadlocks in the X11 implementation
124 static const char* pNoXInitThreads
= getenv( "SAL_NO_XINITTHREADS" );
126 from now on we know that an X connection will be
127 established, so protect X against itself
129 if( ! ( pNoXInitThreads
&& *pNoXInitThreads
) )
133 // init gdk thread protection
134 bool const sup
= g_thread_supported();
135 // extracted from the 'if' to avoid Clang -Wunreachable-code
137 g_thread_init( nullptr );
139 gdk_threads_set_lock_functions (GdkThreadsEnter
, GdkThreadsLeave
);
140 SAL_INFO("vcl.gtk", "Hooked gdk threads locks");
142 auto pYieldMutex
= std::make_unique
<GtkYieldMutex
>();
146 GtkInstance
* pInstance
= new GtkInstance( std::move(pYieldMutex
) );
147 SAL_INFO("vcl.gtk", "creating GtkInstance " << pInstance
);
149 // Create SalData, this does not leak
150 new GtkSalData( pInstance
);
156 static VclInputFlags
categorizeEvent(const GdkEvent
*pEvent
)
158 VclInputFlags nType
= VclInputFlags::NONE
;
159 switch( pEvent
->type
)
161 case GDK_MOTION_NOTIFY
:
162 case GDK_BUTTON_PRESS
:
163 case GDK_2BUTTON_PRESS
:
164 case GDK_3BUTTON_PRESS
:
165 case GDK_BUTTON_RELEASE
:
166 case GDK_ENTER_NOTIFY
:
167 case GDK_LEAVE_NOTIFY
:
169 nType
= VclInputFlags::MOUSE
;
172 // case GDK_KEY_RELEASE: //similar to the X11SalInstance one
173 nType
= VclInputFlags::KEYBOARD
;
176 nType
= VclInputFlags::PAINT
;
179 nType
= VclInputFlags::OTHER
;
185 GtkInstance::GtkInstance( std::unique_ptr
<SalYieldMutex
> pMutex
)
186 : SvpSalInstance( std::move(pMutex
) )
189 , m_pLastCairoFontOptions(nullptr)
193 //We want to defer initializing gtk until we are after uno has been
194 //bootstrapped so we can ask the config what the UI language is so that we can
195 //force that in as $LANGUAGE to get gtk to render widgets RTL if we have a RTL
197 void GtkInstance::AfterAppInit()
202 void GtkInstance::EnsureInit()
206 // initialize SalData
207 GtkSalData
*pSalData
= GetGtkSalData();
209 GtkSalData::initNWF();
213 ImplSVData
* pSVData
= ImplGetSVData();
214 #ifdef GTK_TOOLKIT_NAME
215 pSVData
->maAppData
.mxToolkitName
= OUString(GTK_TOOLKIT_NAME
);
217 pSVData
->maAppData
.mxToolkitName
= OUString("gtk3");
223 GtkInstance::~GtkInstance()
225 assert( nullptr == m_pTimer
);
227 ResetLastSeenCairoFontOptions(nullptr);
230 SalFrame
* GtkInstance::CreateFrame( SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
233 return new GtkSalFrame( pParent
, nStyle
);
236 SalFrame
* GtkInstance::CreateChildFrame( SystemParentData
* pParentData
, SalFrameStyleFlags
)
239 return new GtkSalFrame( pParentData
);
242 SalObject
* GtkInstance::CreateObject( SalFrame
* pParent
, SystemWindowData
* /*pWindowData*/, bool bShow
)
245 //FIXME: Missing CreateObject functionality ...
246 return new GtkSalObject( static_cast<GtkSalFrame
*>(pParent
), bShow
);
251 typedef void*(* getDefaultFnc
)();
252 typedef void(* addItemFnc
)(void *, const char *);
255 void GtkInstance::AddToRecentDocumentList(const OUString
& rFileUrl
, const OUString
&, const OUString
&)
259 rtl_TextEncoding aSystemEnc
= osl_getThreadTextEncoding();
260 if ((aSystemEnc
== RTL_TEXTENCODING_UTF8
) || !rFileUrl
.startsWith( "file://" ))
261 sGtkURL
= OUStringToOString(rFileUrl
, RTL_TEXTENCODING_UTF8
);
264 //Non-utf8 locales are a bad idea if trying to work with non-ascii filenames
265 //Decode %XX components
266 OUString sDecodedUri
= rtl::Uri::decode(rFileUrl
.copy(7), rtl_UriDecodeToIuri
, RTL_TEXTENCODING_UTF8
);
267 //Convert back to system locale encoding
268 OString sSystemUrl
= OUStringToOString(sDecodedUri
, aSystemEnc
);
269 //Encode to an escaped ASCII-encoded URI
270 gchar
*g_uri
= g_filename_to_uri(sSystemUrl
.getStr(), nullptr, nullptr);
271 sGtkURL
= OString(g_uri
);
274 GtkRecentManager
*manager
= gtk_recent_manager_get_default ();
275 gtk_recent_manager_add_item (manager
, sGtkURL
.getStr());
278 SalInfoPrinter
* GtkInstance::CreateInfoPrinter( SalPrinterQueueInfo
* pQueueInfo
,
279 ImplJobSetup
* pSetupData
)
282 mbPrinterInit
= true;
283 // create and initialize SalInfoPrinter
284 PspSalInfoPrinter
* pPrinter
= new GtkSalInfoPrinter
;
285 configurePspInfoPrinter(pPrinter
, pQueueInfo
, pSetupData
);
289 std::unique_ptr
<SalPrinter
> GtkInstance::CreatePrinter( SalInfoPrinter
* pInfoPrinter
)
292 mbPrinterInit
= true;
293 return std::unique_ptr
<SalPrinter
>(new GtkSalPrinter( pInfoPrinter
));
297 * These methods always occur in pairs
298 * A ThreadsEnter is followed by a ThreadsLeave
299 * We need to queue up the recursive lock count
300 * for each pair, so we can accurately restore
303 thread_local
std::stack
<sal_uInt32
> GtkYieldMutex::yieldCounts
;
305 void GtkYieldMutex::ThreadsEnter()
308 if (!yieldCounts
.empty()) {
309 auto n
= yieldCounts
.top();
318 void GtkYieldMutex::ThreadsLeave()
320 assert(m_nCount
!= 0);
321 yieldCounts
.push(m_nCount
);
325 std::unique_ptr
<SalVirtualDevice
> GtkInstance::CreateVirtualDevice( SalGraphics
*pG
,
326 long &nDX
, long &nDY
,
327 DeviceFormat eFormat
,
328 const SystemGraphicsData
* pGd
)
331 SvpSalGraphics
*pSvpSalGraphics
= dynamic_cast<SvpSalGraphics
*>(pG
);
332 assert(pSvpSalGraphics
);
333 // tdf#127529 see SvpSalInstance::CreateVirtualDevice for the rare case of a non-null pPreExistingTarget
334 cairo_surface_t
* pPreExistingTarget
= pGd
? static_cast<cairo_surface_t
*>(pGd
->pSurface
) : nullptr;
335 std::unique_ptr
<SalVirtualDevice
> pNew(new SvpSalVirtualDevice(eFormat
, pSvpSalGraphics
->getSurface(), pPreExistingTarget
));
336 pNew
->SetSize( nDX
, nDY
);
340 std::shared_ptr
<SalBitmap
> GtkInstance::CreateSalBitmap()
343 return SvpSalInstance::CreateSalBitmap();
346 std::unique_ptr
<SalMenu
> GtkInstance::CreateMenu( bool bMenuBar
, Menu
* pVCLMenu
)
349 GtkSalMenu
* pSalMenu
= new GtkSalMenu( bMenuBar
);
350 pSalMenu
->SetMenu( pVCLMenu
);
351 return std::unique_ptr
<SalMenu
>(pSalMenu
);
354 std::unique_ptr
<SalMenuItem
> GtkInstance::CreateMenuItem( const SalItemParams
& rItemData
)
357 return std::unique_ptr
<SalMenuItem
>(new GtkSalMenuItem( &rItemData
));
360 SalTimer
* GtkInstance::CreateSalTimer()
363 assert( nullptr == m_pTimer
);
364 if ( nullptr == m_pTimer
)
365 m_pTimer
= new GtkSalTimer();
369 void GtkInstance::RemoveTimer ()
375 bool GtkInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
378 return GetGtkSalData()->Yield( bWait
, bHandleAllCurrentEvents
);
381 bool GtkInstance::IsTimerExpired()
384 return (m_pTimer
&& m_pTimer
->Expired());
387 bool GtkInstance::AnyInput( VclInputFlags nType
)
390 if( (nType
& VclInputFlags::TIMER
) && IsTimerExpired() )
392 if (!gdk_events_pending())
395 if (nType
== VCL_INPUT_ANY
)
399 std::stack
<GdkEvent
*> aEvents
;
400 GdkEvent
*pEvent
= nullptr;
401 while ((pEvent
= gdk_event_get()))
403 aEvents
.push(pEvent
);
404 VclInputFlags nEventType
= categorizeEvent(pEvent
);
405 if ( (nEventType
& nType
) || ( nEventType
== VclInputFlags::NONE
&& (nType
& VclInputFlags::OTHER
) ) )
412 while (!aEvents
.empty())
414 pEvent
= aEvents
.top();
415 gdk_event_put(pEvent
);
416 gdk_event_free(pEvent
);
422 std::unique_ptr
<GenPspGraphics
> GtkInstance::CreatePrintGraphics()
425 return std::make_unique
<GenPspGraphics
>();
428 std::shared_ptr
<vcl::unx::GtkPrintWrapper
> const &
429 GtkInstance::getPrintWrapper() const
431 if (!m_xPrintWrapper
)
432 m_xPrintWrapper
.reset(new vcl::unx::GtkPrintWrapper
);
433 return m_xPrintWrapper
;
436 const cairo_font_options_t
* GtkInstance::GetCairoFontOptions()
438 const cairo_font_options_t
* pCairoFontOptions
= gdk_screen_get_font_options(gdk_screen_get_default());
439 if (!m_pLastCairoFontOptions
&& pCairoFontOptions
)
440 m_pLastCairoFontOptions
= cairo_font_options_copy(pCairoFontOptions
);
441 return pCairoFontOptions
;
444 const cairo_font_options_t
* GtkInstance::GetLastSeenCairoFontOptions() const
446 return m_pLastCairoFontOptions
;
449 void GtkInstance::ResetLastSeenCairoFontOptions(const cairo_font_options_t
* pCairoFontOptions
)
451 if (m_pLastCairoFontOptions
)
452 cairo_font_options_destroy(m_pLastCairoFontOptions
);
453 if (pCairoFontOptions
)
454 m_pLastCairoFontOptions
= cairo_font_options_copy(pCairoFontOptions
);
456 m_pLastCairoFontOptions
= nullptr;
464 const char* pNativeType
; // string corresponding to nAtom for the case of nAtom being uninitialized
465 const char* pType
; // Mime encoding on our side
468 static const TypeEntry aConversionTab
[] =
470 { "ISO10646-1", "text/plain;charset=utf-16" },
471 { "UTF8_STRING", "text/plain;charset=utf-8" },
472 { "UTF-8", "text/plain;charset=utf-8" },
473 { "text/plain;charset=UTF-8", "text/plain;charset=utf-8" },
475 { "ISO8859-2", "text/plain;charset=iso8859-2" },
476 { "ISO8859-3", "text/plain;charset=iso8859-3" },
477 { "ISO8859-4", "text/plain;charset=iso8859-4" },
478 { "ISO8859-5", "text/plain;charset=iso8859-5" },
479 { "ISO8859-6", "text/plain;charset=iso8859-6" },
480 { "ISO8859-7", "text/plain;charset=iso8859-7" },
481 { "ISO8859-8", "text/plain;charset=iso8859-8" },
482 { "ISO8859-9", "text/plain;charset=iso8859-9" },
483 { "ISO8859-10", "text/plain;charset=iso8859-10" },
484 { "ISO8859-13", "text/plain;charset=iso8859-13" },
485 { "ISO8859-14", "text/plain;charset=iso8859-14" },
486 { "ISO8859-15", "text/plain;charset=iso8859-15" },
488 { "JISX0201.1976-0", "text/plain;charset=jisx0201.1976-0" },
489 { "JISX0208.1983-0", "text/plain;charset=jisx0208.1983-0" },
490 { "JISX0208.1990-0", "text/plain;charset=jisx0208.1990-0" },
491 { "JISX0212.1990-0", "text/plain;charset=jisx0212.1990-0" },
492 { "GB2312.1980-0", "text/plain;charset=gb2312.1980-0" },
493 { "KSC5601.1992-0", "text/plain;charset=ksc5601.1992-0" },
494 // eastern european encodings
495 { "KOI8-R", "text/plain;charset=koi8-r" },
496 { "KOI8-U", "text/plain;charset=koi8-u" },
497 // String (== iso8859-1)
498 { "STRING", "text/plain;charset=iso8859-1" },
499 // special for compound text
500 { "COMPOUND_TEXT", "text/plain;charset=compound_text" },
503 { "PIXMAP", "image/bmp" }
509 const css::datatransfer::DataFlavor
& m_rData
;
511 explicit DataFlavorEq(const css::datatransfer::DataFlavor
& rData
) : m_rData(rData
) {}
512 bool operator() (const css::datatransfer::DataFlavor
& rData
) const
514 return rData
.MimeType
== m_rData
.MimeType
&&
515 rData
.DataType
== m_rData
.DataType
;
520 std::vector
<css::datatransfer::DataFlavor
> GtkTransferable::getTransferDataFlavorsAsVector(GdkAtom
*targets
, gint n_targets
)
522 std::vector
<css::datatransfer::DataFlavor
> aVector
;
524 bool bHaveText
= false, bHaveUTF16
= false;
526 for (gint i
= 0; i
< n_targets
; ++i
)
528 gchar
* pName
= gdk_atom_name(targets
[i
]);
529 const char* pFinalName
= pName
;
530 css::datatransfer::DataFlavor aFlavor
;
532 // omit text/plain;charset=unicode since it is not well defined
533 if (rtl_str_compare(pName
, "text/plain;charset=unicode") == 0)
539 for (size_t j
= 0; j
< SAL_N_ELEMENTS(aConversionTab
); ++j
)
541 if (rtl_str_compare(pName
, aConversionTab
[j
].pNativeType
) == 0)
543 pFinalName
= aConversionTab
[j
].pType
;
548 // There are more non-MIME-types reported that are not translated by
549 // aConversionTab, like "SAVE_TARGETS", "INTEGER", "ATOM"; just filter
550 // them out for now before they confuse this code's clients:
551 if (rtl_str_indexOfChar(pFinalName
, '/') == -1)
557 aFlavor
.MimeType
= OUString(pFinalName
,
559 RTL_TEXTENCODING_UTF8
);
561 m_aMimeTypeToAtom
[aFlavor
.MimeType
] = targets
[i
];
563 aFlavor
.DataType
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
566 if (aFlavor
.MimeType
.getToken(0, ';', nIndex
) == "text/plain")
569 OUString
aToken(aFlavor
.MimeType
.getToken(0, ';', nIndex
));
570 if (aToken
== "charset=utf-16")
573 aFlavor
.DataType
= cppu::UnoType
<OUString
>::get();
576 aVector
.push_back(aFlavor
);
580 //If we have text, but no UTF-16 format which is basically the only
581 //text-format LibreOffice supports for cnp then claim we do and we
582 //will convert on demand
583 if (bHaveText
&& !bHaveUTF16
)
585 css::datatransfer::DataFlavor aFlavor
;
586 aFlavor
.MimeType
= "text/plain;charset=utf-16";
587 aFlavor
.DataType
= cppu::UnoType
<OUString
>::get();
588 aVector
.push_back(aFlavor
);
595 css::uno::Sequence
<css::datatransfer::DataFlavor
> SAL_CALL
GtkTransferable::getTransferDataFlavors()
597 return comphelper::containerToSequence(getTransferDataFlavorsAsVector());
600 sal_Bool SAL_CALL
GtkTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor
& rFlavor
)
602 const std::vector
<css::datatransfer::DataFlavor
> aAll
=
603 getTransferDataFlavorsAsVector();
605 return std::any_of(aAll
.begin(), aAll
.end(), DataFlavorEq(rFlavor
));
608 class GtkClipboardTransferable
: public GtkTransferable
611 GdkAtom m_nSelection
;
614 explicit GtkClipboardTransferable(GdkAtom nSelection
)
615 : m_nSelection(nSelection
)
623 virtual css::uno::Any SAL_CALL
getTransferData(const css::datatransfer::DataFlavor
& rFlavor
) override
625 GtkClipboard
* clipboard
= gtk_clipboard_get(m_nSelection
);
626 if (rFlavor
.MimeType
== "text/plain;charset=utf-16")
629 gchar
*pText
= gtk_clipboard_wait_for_text(clipboard
);
631 aStr
= OUString(pText
, strlen(pText
), RTL_TEXTENCODING_UTF8
);
634 aRet
<<= aStr
.replaceAll("\r\n", "\n");
638 auto it
= m_aMimeTypeToAtom
.find(rFlavor
.MimeType
);
639 if (it
== m_aMimeTypeToAtom
.end())
640 return css::uno::Any();
643 GtkSelectionData
* data
= gtk_clipboard_wait_for_contents(clipboard
,
647 return css::uno::Any();
650 const guchar
*rawdata
= gtk_selection_data_get_data_with_length(data
,
652 Sequence
<sal_Int8
> aSeq(reinterpret_cast<const sal_Int8
*>(rawdata
), length
);
653 gtk_selection_data_free(data
);
658 std::vector
<css::datatransfer::DataFlavor
> getTransferDataFlavorsAsVector()
661 std::vector
<css::datatransfer::DataFlavor
> aVector
;
663 GtkClipboard
* clipboard
= gtk_clipboard_get(m_nSelection
);
667 if (gtk_clipboard_wait_for_targets(clipboard
, &targets
, &n_targets
))
669 aVector
= GtkTransferable::getTransferDataFlavorsAsVector(targets
, n_targets
);
677 class VclGtkClipboard
:
678 public cppu::WeakComponentImplHelper
<
679 datatransfer::clipboard::XSystemClipboard
,
680 datatransfer::clipboard::XFlushableClipboard
,
683 GdkAtom m_nSelection
;
685 gulong m_nOwnerChangedSignalId
;
686 Reference
<css::datatransfer::XTransferable
> m_aContents
;
687 Reference
<css::datatransfer::clipboard::XClipboardOwner
> m_aOwner
;
688 std::vector
< Reference
<css::datatransfer::clipboard::XClipboardListener
> > m_aListeners
;
689 std::vector
<GtkTargetEntry
> m_aGtkTargets
;
690 VclToGtkHelper m_aConversionHelper
;
694 explicit VclGtkClipboard(GdkAtom nSelection
);
695 virtual ~VclGtkClipboard() override
;
701 virtual OUString SAL_CALL
getImplementationName() override
;
702 virtual sal_Bool SAL_CALL
supportsService( const OUString
& ServiceName
) override
;
703 virtual Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
709 virtual Reference
< css::datatransfer::XTransferable
> SAL_CALL
getContents() override
;
711 virtual void SAL_CALL
setContents(
712 const Reference
< css::datatransfer::XTransferable
>& xTrans
,
713 const Reference
< css::datatransfer::clipboard::XClipboardOwner
>& xClipboardOwner
) override
;
715 virtual OUString SAL_CALL
getName() override
;
721 virtual sal_Int8 SAL_CALL
getRenderingCapabilities() override
;
724 * XFlushableClipboard
726 virtual void SAL_CALL
flushClipboard() override
;
731 virtual void SAL_CALL
addClipboardListener(
732 const Reference
< css::datatransfer::clipboard::XClipboardListener
>& listener
) override
;
734 virtual void SAL_CALL
removeClipboardListener(
735 const Reference
< css::datatransfer::clipboard::XClipboardListener
>& listener
) override
;
737 void ClipboardGet(GtkSelectionData
*selection_data
, guint info
);
738 void ClipboardClear();
739 void OwnerPossiblyChanged(GtkClipboard
*clipboard
);
742 OUString
VclGtkClipboard::getImplementationName()
744 return "com.sun.star.datatransfer.VclGtkClipboard";
747 Sequence
< OUString
> VclGtkClipboard::getSupportedServiceNames()
749 Sequence
<OUString
> aRet
{ "com.sun.star.datatransfer.clipboard.SystemClipboard" };
753 sal_Bool
VclGtkClipboard::supportsService( const OUString
& ServiceName
)
755 return cppu::supportsService(this, ServiceName
);
758 Reference
< css::datatransfer::XTransferable
> VclGtkClipboard::getContents()
760 if (!m_aContents
.is())
762 //tdf#93887 This is the system clipboard/selection. We fetch it when we are not
763 //the owner of the clipboard and have not already fetched it.
764 m_aContents
= new GtkClipboardTransferable(m_nSelection
);
770 void VclGtkClipboard::ClipboardGet(GtkSelectionData
*selection_data
, guint info
)
772 if (!m_aContents
.is())
774 m_aConversionHelper
.setSelectionData(m_aContents
, selection_data
, info
);
779 const OString
& getPID()
782 if (!sPID
.getLength())
784 oslProcessIdentifier aProcessId
= 0;
786 info
.Size
= sizeof (oslProcessInfo
);
787 if (osl_getProcessInfo(nullptr, osl_Process_IDENTIFIER
, &info
) == osl_Process_E_None
)
788 aProcessId
= info
.Ident
;
789 sPID
= OString::number(aProcessId
);
797 void ClipboardGetFunc(GtkClipboard
* /*clipboard*/, GtkSelectionData
*selection_data
,
799 gpointer user_data_or_owner
)
801 VclGtkClipboard
* pThis
= static_cast<VclGtkClipboard
*>(user_data_or_owner
);
802 pThis
->ClipboardGet(selection_data
, info
);
805 void ClipboardClearFunc(GtkClipboard
* /*clipboard*/, gpointer user_data_or_owner
)
807 VclGtkClipboard
* pThis
= static_cast<VclGtkClipboard
*>(user_data_or_owner
);
808 pThis
->ClipboardClear();
811 void handle_owner_change(GtkClipboard
*clipboard
, GdkEvent
* /*event*/, gpointer user_data
)
813 VclGtkClipboard
* pThis
= static_cast<VclGtkClipboard
*>(user_data
);
814 pThis
->OwnerPossiblyChanged(clipboard
);
818 void VclGtkClipboard::OwnerPossiblyChanged(GtkClipboard
* clipboard
)
820 if (!m_aContents
.is())
823 //if gdk_display_supports_selection_notification is not supported, e.g. like
824 //right now under wayland, then you only get owner-changed notifications at
825 //opportune times when the selection might have changed. So here
826 //we see if the selection supports a dummy selection type identifying
827 //our pid, in which case it's us.
830 //disconnect and reconnect after gtk_clipboard_wait_for_targets to
831 //avoid possible recursion
832 g_signal_handler_disconnect(clipboard
, m_nOwnerChangedSignalId
);
834 OString sTunnel
= "application/x-libreoffice-internal-id-" + getPID();
837 if (gtk_clipboard_wait_for_targets(clipboard
, &targets
, &n_targets
))
839 for (gint i
= 0; i
< n_targets
&& !bSelf
; ++i
)
841 gchar
* pName
= gdk_atom_name(targets
[i
]);
842 if (strcmp(pName
, sTunnel
.getStr()) == 0)
852 m_nOwnerChangedSignalId
= g_signal_connect(clipboard
, "owner-change",
853 G_CALLBACK(handle_owner_change
), this);
857 //null out m_aContents to return control to the system-one which
858 //will be retrieved if getContents is called again
859 setContents(Reference
<css::datatransfer::XTransferable
>(),
860 Reference
<css::datatransfer::clipboard::XClipboardOwner
>());
864 void VclGtkClipboard::ClipboardClear()
866 for (auto &a
: m_aGtkTargets
)
868 m_aGtkTargets
.clear();
871 GtkTargetEntry
VclToGtkHelper::makeGtkTargetEntry(const css::datatransfer::DataFlavor
& rFlavor
)
873 GtkTargetEntry aEntry
;
875 g_strdup(OUStringToOString(rFlavor
.MimeType
, RTL_TEXTENCODING_UTF8
).getStr());
877 auto it
= std::find_if(aInfoToFlavor
.begin(), aInfoToFlavor
.end(),
878 DataFlavorEq(rFlavor
));
879 if (it
!= aInfoToFlavor
.end())
880 aEntry
.info
= std::distance(aInfoToFlavor
.begin(), it
);
883 aEntry
.info
= aInfoToFlavor
.size();
884 aInfoToFlavor
.push_back(rFlavor
);
889 void VclToGtkHelper::setSelectionData(const Reference
<css::datatransfer::XTransferable
> &rTrans
,
890 GtkSelectionData
*selection_data
, guint info
)
892 GdkAtom
type(gdk_atom_intern(OUStringToOString(aInfoToFlavor
[info
].MimeType
,
893 RTL_TEXTENCODING_UTF8
).getStr(),
896 css::datatransfer::DataFlavor
aFlavor(aInfoToFlavor
[info
]);
897 if (aFlavor
.MimeType
== "UTF8_STRING" || aFlavor
.MimeType
== "STRING")
898 aFlavor
.MimeType
= "text/plain;charset=utf-8";
900 Sequence
<sal_Int8
> aData
;
905 aValue
= rTrans
->getTransferData(aFlavor
);
911 if (aValue
.getValueTypeClass() == TypeClass_STRING
)
915 aData
= Sequence
< sal_Int8
>( reinterpret_cast<sal_Int8
const *>(aString
.getStr()), aString
.getLength() * sizeof( sal_Unicode
) );
917 else if (aValue
.getValueType() == cppu::UnoType
<Sequence
< sal_Int8
>>::get())
921 else if (aFlavor
.MimeType
== "text/plain;charset=utf-8")
923 //didn't have utf-8, try utf-16 and convert
924 aFlavor
.MimeType
= "text/plain;charset=utf-16";
925 aFlavor
.DataType
= cppu::UnoType
<OUString
>::get();
928 aValue
= rTrans
->getTransferData(aFlavor
);
935 OString
aUTF8String(OUStringToOString(aString
, RTL_TEXTENCODING_UTF8
));
936 gtk_selection_data_set(selection_data
, type
, 8,
937 reinterpret_cast<const guchar
*>(aUTF8String
.getStr()),
938 aUTF8String
.getLength());
942 gtk_selection_data_set(selection_data
, type
, 8,
943 reinterpret_cast<const guchar
*>(aData
.getArray()),
947 VclGtkClipboard::VclGtkClipboard(GdkAtom nSelection
)
948 : cppu::WeakComponentImplHelper
<datatransfer::clipboard::XSystemClipboard
,
949 datatransfer::clipboard::XFlushableClipboard
, XServiceInfo
>
951 , m_nSelection(nSelection
)
953 GtkClipboard
* clipboard
= gtk_clipboard_get(m_nSelection
);
954 m_nOwnerChangedSignalId
= g_signal_connect(clipboard
, "owner-change",
955 G_CALLBACK(handle_owner_change
), this);
958 void VclGtkClipboard::flushClipboard()
960 SolarMutexGuard aGuard
;
962 if (GDK_SELECTION_CLIPBOARD
!= m_nSelection
)
965 GtkClipboard
* clipboard
= gtk_clipboard_get(m_nSelection
);
966 gtk_clipboard_store(clipboard
);
969 VclGtkClipboard::~VclGtkClipboard()
971 GtkClipboard
* clipboard
= gtk_clipboard_get(m_nSelection
);
972 g_signal_handler_disconnect(clipboard
, m_nOwnerChangedSignalId
);
973 if (!m_aGtkTargets
.empty())
975 gtk_clipboard_clear(clipboard
);
978 assert(m_aGtkTargets
.empty());
981 std::vector
<GtkTargetEntry
> VclToGtkHelper::FormatsToGtk(const css::uno::Sequence
<css::datatransfer::DataFlavor
> &rFormats
)
983 std::vector
<GtkTargetEntry
> aGtkTargets
;
985 bool bHaveText(false), bHaveUTF8(false);
986 for (const css::datatransfer::DataFlavor
& rFlavor
: rFormats
)
989 if (rFlavor
.MimeType
.getToken(0, ';', nIndex
) == "text/plain")
992 OUString
aToken(rFlavor
.MimeType
.getToken(0, ';', nIndex
));
993 if (aToken
== "charset=utf-8")
998 GtkTargetEntry
aEntry(makeGtkTargetEntry(rFlavor
));
999 aGtkTargets
.push_back(aEntry
);
1004 css::datatransfer::DataFlavor aFlavor
;
1005 aFlavor
.DataType
= cppu::UnoType
<Sequence
< sal_Int8
>>::get();
1008 aFlavor
.MimeType
= "text/plain;charset=utf-8";
1009 aGtkTargets
.push_back(makeGtkTargetEntry(aFlavor
));
1011 aFlavor
.MimeType
= "UTF8_STRING";
1012 aGtkTargets
.push_back(makeGtkTargetEntry(aFlavor
));
1013 aFlavor
.MimeType
= "STRING";
1014 aGtkTargets
.push_back(makeGtkTargetEntry(aFlavor
));
1020 void VclGtkClipboard::setContents(
1021 const Reference
< css::datatransfer::XTransferable
>& xTrans
,
1022 const Reference
< css::datatransfer::clipboard::XClipboardOwner
>& xClipboardOwner
)
1024 css::uno::Sequence
<css::datatransfer::DataFlavor
> aFormats
;
1027 aFormats
= xTrans
->getTransferDataFlavors();
1030 osl::ClearableMutexGuard
aGuard( m_aMutex
);
1031 Reference
< datatransfer::clipboard::XClipboardOwner
> xOldOwner( m_aOwner
);
1032 Reference
< datatransfer::XTransferable
> xOldContents( m_aContents
);
1033 m_aContents
= xTrans
;
1034 m_aOwner
= xClipboardOwner
;
1036 std::vector
< Reference
< datatransfer::clipboard::XClipboardListener
> > aListeners( m_aListeners
);
1037 datatransfer::clipboard::ClipboardEvent aEv
;
1039 GtkClipboard
* clipboard
= gtk_clipboard_get(m_nSelection
);
1040 if (!m_aGtkTargets
.empty())
1042 gtk_clipboard_clear(clipboard
);
1045 assert(m_aGtkTargets
.empty());
1046 if (m_aContents
.is())
1048 std::vector
<GtkTargetEntry
> aGtkTargets(m_aConversionHelper
.FormatsToGtk(aFormats
));
1049 if (!aGtkTargets
.empty())
1051 GtkTargetEntry aEntry
;
1052 OString sTunnel
= "application/x-libreoffice-internal-id-" + getPID();
1053 aEntry
.target
= g_strdup(sTunnel
.getStr());
1056 aGtkTargets
.push_back(aEntry
);
1058 gtk_clipboard_set_with_data(clipboard
, aGtkTargets
.data(), aGtkTargets
.size(),
1059 ClipboardGetFunc
, ClipboardClearFunc
, this);
1060 gtk_clipboard_set_can_store(clipboard
, aGtkTargets
.data(), aGtkTargets
.size());
1063 m_aGtkTargets
= aGtkTargets
;
1066 aEv
.Contents
= getContents();
1070 if (xOldOwner
.is() && xOldOwner
!= xClipboardOwner
)
1071 xOldOwner
->lostOwnership( this, xOldContents
);
1072 for (auto const& listener
: aListeners
)
1074 listener
->changedContents( aEv
);
1078 OUString
VclGtkClipboard::getName()
1080 return (m_nSelection
== GDK_SELECTION_CLIPBOARD
) ? OUString("CLIPBOARD") : OUString("PRIMARY");
1083 sal_Int8
VclGtkClipboard::getRenderingCapabilities()
1088 void VclGtkClipboard::addClipboardListener( const Reference
< datatransfer::clipboard::XClipboardListener
>& listener
)
1090 osl::ClearableMutexGuard
aGuard( m_aMutex
);
1092 m_aListeners
.push_back( listener
);
1095 void VclGtkClipboard::removeClipboardListener( const Reference
< datatransfer::clipboard::XClipboardListener
>& listener
)
1097 osl::ClearableMutexGuard
aGuard( m_aMutex
);
1099 m_aListeners
.erase(std::remove(m_aListeners
.begin(), m_aListeners
.end(), listener
), m_aListeners
.end());
1102 Reference
< XInterface
> GtkInstance::CreateClipboard(const Sequence
< Any
>& arguments
)
1105 if (!arguments
.hasElements()) {
1107 } else if (arguments
.getLength() != 1 || !(arguments
[0] >>= sel
)) {
1108 throw css::lang::IllegalArgumentException(
1109 "bad GtkInstance::CreateClipboard arguments",
1110 css::uno::Reference
<css::uno::XInterface
>(), -1);
1113 GdkAtom nSelection
= (sel
== "CLIPBOARD") ? GDK_SELECTION_CLIPBOARD
: GDK_SELECTION_PRIMARY
;
1115 auto it
= m_aClipboards
.find(nSelection
);
1116 if (it
!= m_aClipboards
.end())
1119 Reference
<XInterface
> xClipboard(static_cast<cppu::OWeakObject
*>(new VclGtkClipboard(nSelection
)));
1120 m_aClipboards
[nSelection
] = xClipboard
;
1125 GtkDropTarget::GtkDropTarget()
1126 : WeakComponentImplHelper(m_aMutex
)
1128 , m_pFormatConversionRequest(nullptr)
1131 , m_nDefaultActions(0)
1135 OUString SAL_CALL
GtkDropTarget::getImplementationName()
1137 return "com.sun.star.datatransfer.dnd.VclGtkDropTarget";
1140 sal_Bool SAL_CALL
GtkDropTarget::supportsService(OUString
const & ServiceName
)
1142 return cppu::supportsService(this, ServiceName
);
1145 css::uno::Sequence
<OUString
> SAL_CALL
GtkDropTarget::getSupportedServiceNames()
1147 Sequence
<OUString
> aRet
{ "com.sun.star.datatransfer.dnd.GtkDropTarget" };
1151 GtkDropTarget::~GtkDropTarget()
1154 m_pFrame
->deregisterDropTarget(this);
1157 void GtkDropTarget::deinitialize()
1163 void GtkDropTarget::initialize(const Sequence
<Any
>& rArguments
)
1165 if (rArguments
.getLength() < 2)
1167 throw RuntimeException("DropTarget::initialize: Cannot install window event handler",
1168 static_cast<OWeakObject
*>(this));
1171 sal_IntPtr nFrame
= 0;
1172 rArguments
.getConstArray()[1] >>= nFrame
;
1176 throw RuntimeException("DropTarget::initialize: missing SalFrame",
1177 static_cast<OWeakObject
*>(this));
1180 m_pFrame
= reinterpret_cast<GtkSalFrame
*>(nFrame
);
1181 m_pFrame
->registerDropTarget(this);
1185 void GtkDropTarget::addDropTargetListener( const Reference
< css::datatransfer::dnd::XDropTargetListener
>& xListener
)
1187 ::osl::Guard
< ::osl::Mutex
> aGuard( m_aMutex
);
1189 m_aListeners
.push_back( xListener
);
1192 void GtkDropTarget::removeDropTargetListener( const Reference
< css::datatransfer::dnd::XDropTargetListener
>& xListener
)
1194 ::osl::Guard
< ::osl::Mutex
> aGuard( m_aMutex
);
1196 m_aListeners
.erase(std::remove(m_aListeners
.begin(), m_aListeners
.end(), xListener
), m_aListeners
.end());
1199 void GtkDropTarget::fire_drop(const css::datatransfer::dnd::DropTargetDropEvent
& dtde
)
1201 osl::ClearableGuard
<osl::Mutex
> aGuard( m_aMutex
);
1202 std::vector
<css::uno::Reference
<css::datatransfer::dnd::XDropTargetListener
>> aListeners(m_aListeners
);
1205 for (auto const& listener
: aListeners
)
1207 listener
->drop( dtde
);
1211 void GtkDropTarget::fire_dragEnter(const css::datatransfer::dnd::DropTargetDragEnterEvent
& dtde
)
1213 osl::ClearableGuard
< ::osl::Mutex
> aGuard( m_aMutex
);
1214 std::vector
<css::uno::Reference
<css::datatransfer::dnd::XDropTargetListener
>> aListeners(m_aListeners
);
1217 for (auto const& listener
: aListeners
)
1219 listener
->dragEnter( dtde
);
1223 void GtkDropTarget::fire_dragOver(const css::datatransfer::dnd::DropTargetDragEvent
& dtde
)
1225 osl::ClearableGuard
< ::osl::Mutex
> aGuard( m_aMutex
);
1226 std::vector
<css::uno::Reference
<css::datatransfer::dnd::XDropTargetListener
>> aListeners(m_aListeners
);
1229 for (auto const& listener
: aListeners
)
1231 listener
->dragOver( dtde
);
1235 void GtkDropTarget::fire_dragExit(const css::datatransfer::dnd::DropTargetEvent
& dte
)
1237 osl::ClearableGuard
< ::osl::Mutex
> aGuard( m_aMutex
);
1238 std::vector
<css::uno::Reference
<css::datatransfer::dnd::XDropTargetListener
>> aListeners(m_aListeners
);
1241 for (auto const& listener
: aListeners
)
1243 listener
->dragExit( dte
);
1247 sal_Bool
GtkDropTarget::isActive()
1252 void GtkDropTarget::setActive(sal_Bool bActive
)
1254 m_bActive
= bActive
;
1257 sal_Int8
GtkDropTarget::getDefaultActions()
1259 return m_nDefaultActions
;
1262 void GtkDropTarget::setDefaultActions(sal_Int8 nDefaultActions
)
1264 m_nDefaultActions
= nDefaultActions
;
1267 Reference
< XInterface
> GtkInstance::CreateDropTarget()
1269 return Reference
<XInterface
>(static_cast<cppu::OWeakObject
*>(new GtkDropTarget
));
1272 GtkDragSource::~GtkDragSource()
1275 m_pFrame
->deregisterDragSource(this);
1277 if (GtkDragSource::g_ActiveDragSource
== this)
1279 SAL_WARN( "vcl.gtk", "dragEnd should have been called on GtkDragSource before dtor");
1280 GtkDragSource::g_ActiveDragSource
= nullptr;
1284 void GtkDragSource::deinitialize()
1289 sal_Bool
GtkDragSource::isDragImageSupported()
1294 sal_Int32
GtkDragSource::getDefaultCursor( sal_Int8
)
1299 void GtkDragSource::initialize(const css::uno::Sequence
<css::uno::Any
>& rArguments
)
1301 if (rArguments
.getLength() < 2)
1303 throw RuntimeException("DragSource::initialize: Cannot install window event handler",
1304 static_cast<OWeakObject
*>(this));
1307 sal_IntPtr nFrame
= 0;
1308 rArguments
.getConstArray()[1] >>= nFrame
;
1312 throw RuntimeException("DragSource::initialize: missing SalFrame",
1313 static_cast<OWeakObject
*>(this));
1316 m_pFrame
= reinterpret_cast<GtkSalFrame
*>(nFrame
);
1317 m_pFrame
->registerDragSource(this);
1320 OUString SAL_CALL
GtkDragSource::getImplementationName()
1322 return "com.sun.star.datatransfer.dnd.VclGtkDragSource";
1325 sal_Bool SAL_CALL
GtkDragSource::supportsService(OUString
const & ServiceName
)
1327 return cppu::supportsService(this, ServiceName
);
1330 css::uno::Sequence
<OUString
> SAL_CALL
GtkDragSource::getSupportedServiceNames()
1332 Sequence
<OUString
> aRet
{ "com.sun.star.datatransfer.dnd.GtkDragSource" };
1336 Reference
< XInterface
> GtkInstance::CreateDragSource()
1338 return Reference
< XInterface
>( static_cast<cppu::OWeakObject
*>(new GtkDragSource()) );
1341 class GtkOpenGLContext
: public OpenGLContext
1344 GtkWidget
*m_pGLArea
;
1345 GdkGLContext
*m_pContext
;
1346 guint m_nAreaFrameBuffer
;
1347 guint m_nFrameBuffer
;
1348 guint m_nRenderBuffer
;
1349 guint m_nDepthBuffer
;
1350 guint m_nFrameScratchBuffer
;
1351 guint m_nRenderScratchBuffer
;
1352 guint m_nDepthScratchBuffer
;
1357 , m_pGLArea(nullptr)
1358 , m_pContext(nullptr)
1359 , m_nAreaFrameBuffer(0)
1361 , m_nRenderBuffer(0)
1363 , m_nFrameScratchBuffer(0)
1364 , m_nRenderScratchBuffer(0)
1365 , m_nDepthScratchBuffer(0)
1369 virtual void initWindow() override
1371 if( !m_pChildWindow
)
1373 SystemWindowData winData
= generateWinData(mpWindow
, mbRequestLegacyContext
);
1374 m_pChildWindow
= VclPtr
<SystemChildWindow
>::Create(mpWindow
, 0, &winData
, false);
1379 InitChildWindow(m_pChildWindow
.get());
1384 virtual const GLWindow
& getOpenGLWindow() const override
{ return m_aGLWin
; }
1385 virtual GLWindow
& getModifiableOpenGLWindow() override
{ return m_aGLWin
; }
1387 static void signalDestroy(GtkWidget
*, gpointer context
)
1389 GtkOpenGLContext
* pThis
= static_cast<GtkOpenGLContext
*>(context
);
1390 pThis
->m_pGLArea
= nullptr;
1393 static gboolean
signalRender(GtkGLArea
*, GdkGLContext
*, gpointer window
)
1395 GtkOpenGLContext
* pThis
= static_cast<GtkOpenGLContext
*>(window
);
1397 int scale
= gtk_widget_get_scale_factor(pThis
->m_pGLArea
);
1398 int width
= pThis
->m_aGLWin
.Width
* scale
;
1399 int height
= pThis
->m_aGLWin
.Height
* scale
;
1401 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT
);
1403 glBindFramebuffer(GL_READ_FRAMEBUFFER
, pThis
->m_nAreaFrameBuffer
);
1404 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT
);
1406 glBlitFramebuffer(0, 0, width
, height
, 0, 0, width
, height
,
1407 GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
, GL_NEAREST
);
1409 gdk_gl_context_make_current(pThis
->m_pContext
);
1413 virtual void adjustToNewSize() override
1418 int scale
= gtk_widget_get_scale_factor(m_pGLArea
);
1419 int width
= m_aGLWin
.Width
* scale
;
1420 int height
= m_aGLWin
.Height
* scale
;
1422 // seen in tdf#124729 width/height of 0 leading to GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
1423 int allocwidth
= std::max(width
, 1);
1424 int allocheight
= std::max(height
, 1);
1426 gtk_gl_area_make_current(GTK_GL_AREA(m_pGLArea
));
1427 if (GError
*pError
= gtk_gl_area_get_error(GTK_GL_AREA(m_pGLArea
)))
1429 SAL_WARN("vcl.gtk", "gtk gl area error: " << pError
->message
);
1433 glBindRenderbuffer(GL_RENDERBUFFER
, m_nRenderBuffer
);
1434 glRenderbufferStorage(GL_RENDERBUFFER
, GL_RGB8
, allocwidth
, allocheight
);
1435 glBindRenderbuffer(GL_RENDERBUFFER
, m_nDepthBuffer
);
1436 glRenderbufferStorage(GL_RENDERBUFFER
, GL_DEPTH_COMPONENT24
, allocwidth
, allocheight
);
1437 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, m_nAreaFrameBuffer
);
1439 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
,
1440 GL_RENDERBUFFER_EXT
, m_nRenderBuffer
);
1441 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_DEPTH_ATTACHMENT_EXT
,
1442 GL_RENDERBUFFER_EXT
, m_nDepthBuffer
);
1444 gdk_gl_context_make_current(m_pContext
);
1445 glBindRenderbuffer(GL_RENDERBUFFER
, m_nRenderBuffer
);
1446 glBindRenderbuffer(GL_RENDERBUFFER
, m_nDepthBuffer
);
1447 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, m_nFrameBuffer
);
1449 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
,
1450 GL_RENDERBUFFER_EXT
, m_nRenderBuffer
);
1451 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_DEPTH_ATTACHMENT_EXT
,
1452 GL_RENDERBUFFER_EXT
, m_nDepthBuffer
);
1453 glViewport(0, 0, width
, height
);
1455 glBindRenderbuffer(GL_RENDERBUFFER
, m_nRenderScratchBuffer
);
1456 glRenderbufferStorage(GL_RENDERBUFFER
, GL_RGB8
, allocwidth
, allocheight
);
1457 glBindRenderbuffer(GL_RENDERBUFFER
, m_nDepthScratchBuffer
);
1458 glRenderbufferStorage(GL_RENDERBUFFER
, GL_DEPTH_COMPONENT24
, allocwidth
, allocheight
);
1459 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, m_nFrameScratchBuffer
);
1461 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
,
1462 GL_RENDERBUFFER_EXT
, m_nRenderScratchBuffer
);
1463 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_DEPTH_ATTACHMENT_EXT
,
1464 GL_RENDERBUFFER_EXT
, m_nDepthScratchBuffer
);
1466 glViewport(0, 0, width
, height
);
1469 virtual bool ImplInit() override
1471 const SystemEnvData
* pEnvData
= m_pChildWindow
->GetSystemData();
1472 GtkWidget
*pParent
= static_cast<GtkWidget
*>(pEnvData
->pWidget
);
1473 m_pGLArea
= gtk_gl_area_new();
1474 g_signal_connect(G_OBJECT(m_pGLArea
), "destroy", G_CALLBACK(signalDestroy
), this);
1475 g_signal_connect(G_OBJECT(m_pGLArea
), "render", G_CALLBACK(signalRender
), this);
1476 gtk_gl_area_set_has_depth_buffer(GTK_GL_AREA(m_pGLArea
), true);
1477 gtk_gl_area_set_auto_render(GTK_GL_AREA(m_pGLArea
), false);
1478 gtk_widget_set_hexpand(m_pGLArea
, true);
1479 gtk_widget_set_vexpand(m_pGLArea
, true);
1480 gtk_container_add(GTK_CONTAINER(pParent
), m_pGLArea
);
1481 gtk_widget_show_all(pParent
);
1483 gtk_gl_area_make_current(GTK_GL_AREA(m_pGLArea
));
1484 if (GError
*pError
= gtk_gl_area_get_error(GTK_GL_AREA(m_pGLArea
)))
1486 SAL_WARN("vcl.gtk", "gtk gl area error: " << pError
->message
);
1490 gtk_gl_area_attach_buffers(GTK_GL_AREA(m_pGLArea
));
1491 glGenFramebuffersEXT(1, &m_nAreaFrameBuffer
);
1493 GdkWindow
*pWindow
= gtk_widget_get_window(pParent
);
1494 m_pContext
= gdk_window_create_gl_context(pWindow
, nullptr);
1498 if (!gdk_gl_context_realize(m_pContext
, nullptr))
1501 gdk_gl_context_make_current(m_pContext
);
1502 glGenFramebuffersEXT(1, &m_nFrameBuffer
);
1503 glGenRenderbuffersEXT(1, &m_nRenderBuffer
);
1504 glGenRenderbuffersEXT(1, &m_nDepthBuffer
);
1505 glGenFramebuffersEXT(1, &m_nFrameScratchBuffer
);
1506 glGenRenderbuffersEXT(1, &m_nRenderScratchBuffer
);
1507 glGenRenderbuffersEXT(1, &m_nDepthScratchBuffer
);
1509 bool bRet
= InitGL();
1514 virtual void restoreDefaultFramebuffer() override
1516 OpenGLContext::restoreDefaultFramebuffer();
1517 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, m_nFrameScratchBuffer
);
1518 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
,
1519 GL_RENDERBUFFER_EXT
, m_nRenderScratchBuffer
);
1522 virtual void makeCurrent() override
1531 int scale
= gtk_widget_get_scale_factor(m_pGLArea
);
1532 int width
= m_aGLWin
.Width
* scale
;
1533 int height
= m_aGLWin
.Height
* scale
;
1535 gdk_gl_context_make_current(m_pContext
);
1537 glBindRenderbuffer(GL_RENDERBUFFER
, m_nRenderScratchBuffer
);
1538 glBindRenderbuffer(GL_RENDERBUFFER
, m_nDepthScratchBuffer
);
1539 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, m_nFrameScratchBuffer
);
1540 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
,
1541 GL_RENDERBUFFER_EXT
, m_nRenderScratchBuffer
);
1542 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT
, GL_DEPTH_ATTACHMENT_EXT
,
1543 GL_RENDERBUFFER_EXT
, m_nDepthScratchBuffer
);
1544 glViewport(0, 0, width
, height
);
1547 registerAsCurrent();
1550 virtual void destroyCurrentContext() override
1552 gdk_gl_context_clear_current();
1555 virtual bool isCurrent() override
1557 return m_pGLArea
&& gdk_gl_context_get_current() == m_pContext
;
1560 virtual void sync() override
1564 virtual void resetCurrent() override
1567 gdk_gl_context_clear_current();
1570 virtual void swapBuffers() override
1572 int scale
= gtk_widget_get_scale_factor(m_pGLArea
);
1573 int width
= m_aGLWin
.Width
* scale
;
1574 int height
= m_aGLWin
.Height
* scale
;
1576 glBindFramebuffer(GL_DRAW_FRAMEBUFFER
, m_nFrameBuffer
);
1577 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT
);
1579 glBindFramebuffer(GL_READ_FRAMEBUFFER
, m_nFrameScratchBuffer
);
1580 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT
);
1582 glBlitFramebuffer(0, 0, width
, height
, 0, 0, width
, height
,
1583 GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
, GL_NEAREST
);
1585 glBindFramebuffer(GL_DRAW_FRAMEBUFFER
, m_nFrameScratchBuffer
);
1586 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT
);
1588 gtk_gl_area_queue_render(GTK_GL_AREA(m_pGLArea
));
1592 virtual ~GtkOpenGLContext() override
1596 g_clear_object(&m_pContext
);
1601 OpenGLContext
* GtkInstance::CreateOpenGLContext()
1603 return new GtkOpenGLContext
;
1606 // tdf#123800 avoid requiring wayland at runtime just because it existed at buildtime
1607 bool DLSYM_GDK_IS_WAYLAND_DISPLAY(GdkDisplay
* pDisplay
)
1609 auto get_type
= reinterpret_cast<GType (*) (void)>(dlsym(nullptr, "gdk_wayland_display_get_type"));
1612 return G_TYPE_CHECK_INSTANCE_TYPE(pDisplay
, get_type());
1615 bool DLSYM_GDK_IS_X11_DISPLAY(GdkDisplay
* pDisplay
)
1617 auto get_type
= reinterpret_cast<GType (*) (void)>(dlsym(nullptr, "gdk_x11_display_get_type"));
1620 return G_TYPE_CHECK_INSTANCE_TYPE(pDisplay
, get_type());
1623 class GtkInstanceBuilder
;
1627 void set_help_id(const GtkWidget
*pWidget
, const OString
& rHelpId
)
1629 gchar
*helpid
= g_strdup(rHelpId
.getStr());
1630 g_object_set_data_full(G_OBJECT(pWidget
), "g-lo-helpid", helpid
, g_free
);
1633 OString
get_help_id(const GtkWidget
*pWidget
)
1635 void* pData
= g_object_get_data(G_OBJECT(pWidget
), "g-lo-helpid");
1636 const gchar
* pStr
= static_cast<const gchar
*>(pData
);
1637 return OString(pStr
, pStr
? strlen(pStr
) : 0);
1640 KeyEvent
GtkToVcl(const GdkEventKey
& rEvent
)
1642 sal_uInt16 nKeyCode
= GtkSalFrame::GetKeyCode(rEvent
.keyval
);
1645 guint updated_keyval
= GtkSalFrame::GetKeyValFor(gdk_keymap_get_default(), rEvent
.hardware_keycode
, rEvent
.group
);
1646 nKeyCode
= GtkSalFrame::GetKeyCode(updated_keyval
);
1648 nKeyCode
|= GtkSalFrame::GetKeyModCode(rEvent
.state
);
1649 return KeyEvent(gdk_keyval_to_unicode(rEvent
.keyval
), nKeyCode
, 0);
1653 static MouseEventModifiers
ImplGetMouseButtonMode(sal_uInt16 nButton
, sal_uInt16 nCode
)
1655 MouseEventModifiers nMode
= MouseEventModifiers::NONE
;
1656 if ( nButton
== MOUSE_LEFT
)
1657 nMode
|= MouseEventModifiers::SIMPLECLICK
;
1658 if ( (nButton
== MOUSE_LEFT
) && !(nCode
& (MOUSE_MIDDLE
| MOUSE_RIGHT
)) )
1659 nMode
|= MouseEventModifiers::SELECT
;
1660 if ( (nButton
== MOUSE_LEFT
) && (nCode
& KEY_MOD1
) &&
1661 !(nCode
& (MOUSE_MIDDLE
| MOUSE_RIGHT
| KEY_SHIFT
)) )
1662 nMode
|= MouseEventModifiers::MULTISELECT
;
1663 if ( (nButton
== MOUSE_LEFT
) && (nCode
& KEY_SHIFT
) &&
1664 !(nCode
& (MOUSE_MIDDLE
| MOUSE_RIGHT
| KEY_MOD1
)) )
1665 nMode
|= MouseEventModifiers::RANGESELECT
;
1669 static MouseEventModifiers
ImplGetMouseMoveMode(sal_uInt16 nCode
)
1671 MouseEventModifiers nMode
= MouseEventModifiers::NONE
;
1673 nMode
|= MouseEventModifiers::SIMPLEMOVE
;
1674 if ( (nCode
& MOUSE_LEFT
) && !(nCode
& KEY_MOD1
) )
1675 nMode
|= MouseEventModifiers::DRAGMOVE
;
1676 if ( (nCode
& MOUSE_LEFT
) && (nCode
& KEY_MOD1
) )
1677 nMode
|= MouseEventModifiers::DRAGCOPY
;
1683 #if GTK_CHECK_VERSION(3,22,0)
1684 bool SwapForRTL(GtkWidget
* pWidget
)
1686 GtkTextDirection eDir
= gtk_widget_get_direction(pWidget
);
1687 if (eDir
== GTK_TEXT_DIR_RTL
)
1689 if (eDir
== GTK_TEXT_DIR_LTR
)
1691 return AllSettings::GetLayoutRTL();
1695 GtkWidget
* ensureEventWidget(GtkWidget
* pWidget
)
1700 GtkWidget
* pMouseEventBox
;
1701 // not every widget has a GdkWindow and can get any event, so if we
1702 // want an event it doesn't have, insert a GtkEventBox so we can get
1704 if (gtk_widget_get_has_window(pWidget
))
1705 pMouseEventBox
= pWidget
;
1708 // remove the widget and replace it with an eventbox and put the old
1710 GtkWidget
* pParent
= gtk_widget_get_parent(pWidget
);
1712 g_object_ref(pWidget
);
1714 gint
nTopAttach(0), nLeftAttach(0), nHeight(1), nWidth(1);
1715 if (GTK_IS_GRID(pParent
))
1717 gtk_container_child_get(GTK_CONTAINER(pParent
), pWidget
,
1718 "left-attach", &nTopAttach
,
1719 "top-attach", &nLeftAttach
,
1725 gboolean
bExpand(false), bFill(false);
1726 GtkPackType
ePackType(GTK_PACK_START
);
1729 if (GTK_IS_BOX(pParent
))
1731 gtk_container_child_get(GTK_CONTAINER(pParent
), pWidget
,
1734 "pack-type", &ePackType
,
1735 "padding", &nPadding
,
1736 "position", &nPosition
,
1740 gtk_container_remove(GTK_CONTAINER(pParent
), pWidget
);
1742 pMouseEventBox
= gtk_event_box_new();
1743 gtk_event_box_set_above_child(GTK_EVENT_BOX(pMouseEventBox
), false);
1744 gtk_event_box_set_visible_window(GTK_EVENT_BOX(pMouseEventBox
), false);
1745 gtk_widget_set_visible(pMouseEventBox
, gtk_widget_get_visible(pWidget
));
1747 gtk_container_add(GTK_CONTAINER(pParent
), pMouseEventBox
);
1749 if (GTK_IS_GRID(pParent
))
1751 gtk_container_child_set(GTK_CONTAINER(pParent
), pMouseEventBox
,
1752 "left-attach", nTopAttach
,
1753 "top-attach", nLeftAttach
,
1759 if (GTK_IS_BOX(pParent
))
1761 gtk_container_child_set(GTK_CONTAINER(pParent
), pMouseEventBox
,
1764 "pack-type", ePackType
,
1765 "padding", nPadding
,
1766 "position", nPosition
,
1770 gtk_container_add(GTK_CONTAINER(pMouseEventBox
), pWidget
);
1771 g_object_unref(pWidget
);
1773 gtk_widget_set_hexpand(pMouseEventBox
, gtk_widget_get_hexpand(pWidget
));
1774 gtk_widget_set_vexpand(pMouseEventBox
, gtk_widget_get_vexpand(pWidget
));
1777 return pMouseEventBox
;
1781 class GtkInstanceWidget
: public virtual weld::Widget
1784 GtkWidget
* m_pWidget
;
1785 GtkWidget
* m_pMouseEventBox
;
1786 GtkInstanceBuilder
* m_pBuilder
;
1788 DECL_LINK(async_signal_focus_in
, void*, void);
1789 DECL_LINK(async_signal_focus_out
, void*, void);
1791 void launch_signal_focus_in()
1793 // in e.g. function wizard RefEdits we want to select all when we get focus
1794 // but there are pending gtk handlers which change selection after our handler
1795 // post our focus in event to happen after those finish
1796 if (m_pFocusInEvent
)
1797 Application::RemoveUserEvent(m_pFocusInEvent
);
1798 m_pFocusInEvent
= Application::PostUserEvent(LINK(this, GtkInstanceWidget
, async_signal_focus_in
));
1801 static gboolean
signalFocusIn(GtkWidget
*, GdkEvent
*, gpointer widget
)
1803 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
1804 pThis
->launch_signal_focus_in();
1808 void signal_focus_in()
1810 m_aFocusInHdl
.Call(*this);
1813 static gboolean
signalMnemonicActivate(GtkWidget
*, gboolean
, gpointer widget
)
1815 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
1816 SolarMutexGuard aGuard
;
1817 return pThis
->signal_mnemonic_activate();
1820 bool signal_mnemonic_activate()
1822 return m_aMnemonicActivateHdl
.Call(*this);
1825 void launch_signal_focus_out()
1827 // tdf#127262 because focus in is async, focus out must not appear out
1828 // of sequence to focus in
1829 if (m_pFocusOutEvent
)
1830 Application::RemoveUserEvent(m_pFocusOutEvent
);
1831 m_pFocusOutEvent
= Application::PostUserEvent(LINK(this, GtkInstanceWidget
, async_signal_focus_out
));
1834 static gboolean
signalFocusOut(GtkWidget
*, GdkEvent
*, gpointer widget
)
1836 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
1837 SolarMutexGuard aGuard
;
1838 pThis
->launch_signal_focus_out();
1842 void signal_focus_out()
1844 m_aFocusOutHdl
.Call(*this);
1847 void ensureEventWidget()
1849 if (!m_pMouseEventBox
)
1850 m_pMouseEventBox
= ::ensureEventWidget(m_pWidget
);
1853 void ensureButtonPressSignal()
1855 if (!m_nButtonPressSignalId
)
1857 ensureEventWidget();
1858 m_nButtonPressSignalId
= g_signal_connect(m_pMouseEventBox
, "button-press-event", G_CALLBACK(signalButton
), this);
1862 static gboolean
signalPopupMenu(GtkWidget
* pWidget
, gpointer widget
)
1864 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
1865 SolarMutexGuard aGuard
;
1866 //center it when we don't know where else to use
1867 Point
aPos(gtk_widget_get_allocated_width(pWidget
) / 2,
1868 gtk_widget_get_allocated_height(pWidget
) / 2);
1869 CommandEvent
aCEvt(aPos
, CommandEventId::ContextMenu
, false);
1870 return pThis
->signal_popup_menu(aCEvt
);
1873 bool SwapForRTL() const
1875 GtkTextDirection eDir
= gtk_widget_get_direction(m_pWidget
);
1876 if (eDir
== GTK_TEXT_DIR_RTL
)
1878 if (eDir
== GTK_TEXT_DIR_LTR
)
1880 return AllSettings::GetLayoutRTL();
1884 bool m_bTakeOwnership
;
1886 bool m_bDraggedOver
;
1887 sal_uInt16 m_nLastMouseButton
;
1888 sal_uInt16 m_nLastMouseClicks
;
1889 ImplSVEvent
* m_pFocusInEvent
;
1890 ImplSVEvent
* m_pFocusOutEvent
;
1891 GtkCssProvider
* m_pBgCssProvider
;
1892 gulong m_nFocusInSignalId
;
1893 gulong m_nMnemonicActivateSignalId
;
1894 gulong m_nFocusOutSignalId
;
1895 gulong m_nKeyPressSignalId
;
1896 gulong m_nKeyReleaseSignalId
;
1897 gulong m_nSizeAllocateSignalId
;
1898 gulong m_nButtonPressSignalId
;
1899 gulong m_nMotionSignalId
;
1900 gulong m_nLeaveSignalId
;
1901 gulong m_nEnterSignalId
;
1902 gulong m_nButtonReleaseSignalId
;
1903 gulong m_nDragMotionSignalId
;
1904 gulong m_nDragDropSignalId
;
1905 gulong m_nDragDropReceivedSignalId
;
1906 gulong m_nDragLeaveSignalId
;
1908 rtl::Reference
<GtkDropTarget
> m_xDropTarget
;
1909 std::vector
<AtkRelation
*> m_aExtraAtkRelations
;
1911 static void signalSizeAllocate(GtkWidget
*, GdkRectangle
* allocation
, gpointer widget
)
1913 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
1914 SolarMutexGuard aGuard
;
1915 pThis
->signal_size_allocate(allocation
->width
, allocation
->height
);
1918 static gboolean
signalKey(GtkWidget
*, GdkEventKey
* pEvent
, gpointer widget
)
1920 // #i1820# use locale specific decimal separator
1921 if (pEvent
->keyval
== GDK_KEY_KP_Decimal
&& Application::GetSettings().GetMiscSettings().GetEnableLocalizedDecimalSep())
1923 OUString
aSep(Application::GetSettings().GetLocaleDataWrapper().getNumDecimalSep());
1924 pEvent
->keyval
= aSep
[0];
1927 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
1928 return pThis
->signal_key(pEvent
);
1931 virtual bool signal_popup_menu(const CommandEvent
&)
1936 static gboolean
signalButton(GtkWidget
*, GdkEventButton
* pEvent
, gpointer widget
)
1938 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
1939 SolarMutexGuard aGuard
;
1940 return pThis
->signal_button(pEvent
);
1943 bool signal_button(GdkEventButton
* pEvent
)
1945 Point
aPos(pEvent
->x
, pEvent
->y
);
1947 aPos
.setX(gtk_widget_get_allocated_width(m_pWidget
) - 1 - aPos
.X());
1949 if (gdk_event_triggers_context_menu(reinterpret_cast<GdkEvent
*>(pEvent
)) && pEvent
->type
== GDK_BUTTON_PRESS
)
1951 //if handled for context menu, stop processing
1952 CommandEvent
aCEvt(aPos
, CommandEventId::ContextMenu
, true);
1953 if (signal_popup_menu(aCEvt
))
1957 if (!m_aMousePressHdl
.IsSet() && !m_aMouseReleaseHdl
.IsSet())
1960 SalEvent nEventType
= SalEvent::NONE
;
1961 switch (pEvent
->type
)
1963 case GDK_BUTTON_PRESS
:
1964 if (GdkEvent
* pPeekEvent
= gdk_event_peek())
1966 bool bSkip
= pPeekEvent
->type
== GDK_2BUTTON_PRESS
||
1967 pPeekEvent
->type
== GDK_3BUTTON_PRESS
;
1968 gdk_event_free(pPeekEvent
);
1974 nEventType
= SalEvent::MouseButtonDown
;
1975 m_nLastMouseClicks
= 1;
1977 case GDK_2BUTTON_PRESS
:
1978 m_nLastMouseClicks
= 2;
1979 nEventType
= SalEvent::MouseButtonDown
;
1981 case GDK_3BUTTON_PRESS
:
1982 m_nLastMouseClicks
= 3;
1983 nEventType
= SalEvent::MouseButtonDown
;
1985 case GDK_BUTTON_RELEASE
:
1986 nEventType
= SalEvent::MouseButtonUp
;
1992 switch (pEvent
->button
)
1995 m_nLastMouseButton
= MOUSE_LEFT
;
1998 m_nLastMouseButton
= MOUSE_MIDDLE
;
2001 m_nLastMouseButton
= MOUSE_RIGHT
;
2007 sal_uInt32 nModCode
= GtkSalFrame::GetMouseModCode(pEvent
->state
);
2008 sal_uInt16 nCode
= m_nLastMouseButton
| (nModCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
));
2009 MouseEvent
aMEvt(aPos
, m_nLastMouseClicks
, ImplGetMouseButtonMode(m_nLastMouseButton
, nModCode
), nCode
, nCode
);
2011 if (nEventType
== SalEvent::MouseButtonDown
)
2013 if (!m_aMousePressHdl
.IsSet())
2015 return m_aMousePressHdl
.Call(aMEvt
);
2018 if (!m_aMouseReleaseHdl
.IsSet())
2020 return m_aMouseReleaseHdl
.Call(aMEvt
);
2023 static gboolean
signalMotion(GtkWidget
*, GdkEventMotion
* pEvent
, gpointer widget
)
2025 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
2026 SolarMutexGuard aGuard
;
2027 return pThis
->signal_motion(pEvent
);
2030 bool signal_motion(const GdkEventMotion
* pEvent
)
2032 if (!m_aMouseMotionHdl
.IsSet())
2035 Point
aPos(pEvent
->x
, pEvent
->y
);
2037 aPos
.setX(gtk_widget_get_allocated_width(m_pWidget
) - 1 - aPos
.X());
2038 sal_uInt32 nModCode
= GtkSalFrame::GetMouseModCode(pEvent
->state
);
2039 sal_uInt16 nCode
= m_nLastMouseButton
| (nModCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
));
2040 MouseEvent
aMEvt(aPos
, 0, ImplGetMouseMoveMode(nModCode
), nCode
, nCode
);
2042 m_aMouseMotionHdl
.Call(aMEvt
);
2046 static gboolean
signalCrossing(GtkWidget
*, GdkEventCrossing
* pEvent
, gpointer widget
)
2048 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
2049 SolarMutexGuard aGuard
;
2050 return pThis
->signal_crossing(pEvent
);
2053 bool signal_crossing(const GdkEventCrossing
* pEvent
)
2055 if (!m_aMouseMotionHdl
.IsSet())
2058 Point
aPos(pEvent
->x
, pEvent
->y
);
2060 aPos
.setX(gtk_widget_get_allocated_width(m_pWidget
) - 1 - aPos
.X());
2061 sal_uInt32 nModCode
= GtkSalFrame::GetMouseModCode(pEvent
->state
);
2062 sal_uInt16 nCode
= m_nLastMouseButton
| (nModCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
));
2063 MouseEventModifiers eModifiers
= ImplGetMouseMoveMode(nModCode
);
2064 eModifiers
= eModifiers
| (pEvent
->type
== GDK_ENTER_NOTIFY
? MouseEventModifiers::ENTERWINDOW
: MouseEventModifiers::LEAVEWINDOW
);
2065 MouseEvent
aMEvt(aPos
, 0, eModifiers
, nCode
, nCode
);
2067 m_aMouseMotionHdl
.Call(aMEvt
);
2071 virtual void drag_started()
2075 static gboolean
signalDragMotion(GtkWidget
*pWidget
, GdkDragContext
*context
, gint x
, gint y
, guint time
, gpointer widget
)
2077 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
2078 if (!pThis
->m_bDraggedOver
)
2080 pThis
->m_bDraggedOver
= true;
2081 pThis
->drag_started();
2083 return pThis
->m_xDropTarget
->signalDragMotion(pWidget
, context
, x
, y
, time
);
2086 static gboolean
signalDragDrop(GtkWidget
* pWidget
, GdkDragContext
* context
, gint x
, gint y
, guint time
, gpointer widget
)
2088 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
2089 return pThis
->m_xDropTarget
->signalDragDrop(pWidget
, context
, x
, y
, time
);
2092 static void signalDragDropReceived(GtkWidget
* pWidget
, GdkDragContext
* context
, gint x
, gint y
, GtkSelectionData
* data
, guint ttype
, guint time
, gpointer widget
)
2094 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
2095 pThis
->m_xDropTarget
->signalDragDropReceived(pWidget
, context
, x
, y
, data
, ttype
, time
);
2098 virtual void drag_ended()
2102 static void signalDragLeave(GtkWidget
*pWidget
, GdkDragContext
*context
, guint time
, gpointer widget
)
2104 GtkInstanceWidget
* pThis
= static_cast<GtkInstanceWidget
*>(widget
);
2105 pThis
->m_xDropTarget
->signalDragLeave(pWidget
, context
, time
);
2106 if (pThis
->m_bDraggedOver
)
2108 pThis
->m_bDraggedOver
= false;
2109 pThis
->drag_ended();
2113 void set_background(const OUString
* pColor
)
2115 if (!pColor
&& !m_pBgCssProvider
)
2117 GtkStyleContext
*pWidgetContext
= gtk_widget_get_style_context(GTK_WIDGET(m_pWidget
));
2118 if (m_pBgCssProvider
)
2120 gtk_style_context_remove_provider(pWidgetContext
, GTK_STYLE_PROVIDER(m_pBgCssProvider
));
2121 m_pBgCssProvider
= nullptr;
2125 m_pBgCssProvider
= gtk_css_provider_new();
2126 OUString aBuffer
= "* { background-color: #" + *pColor
+ "; }";
2127 OString aResult
= OUStringToOString(aBuffer
, RTL_TEXTENCODING_UTF8
);
2128 gtk_css_provider_load_from_data(m_pBgCssProvider
, aResult
.getStr(), aResult
.getLength(), nullptr);
2129 gtk_style_context_add_provider(pWidgetContext
, GTK_STYLE_PROVIDER(m_pBgCssProvider
),
2130 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
2134 GtkInstanceWidget(GtkWidget
* pWidget
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
2135 : m_pWidget(pWidget
)
2136 , m_pMouseEventBox(nullptr)
2137 , m_pBuilder(pBuilder
)
2138 , m_bTakeOwnership(bTakeOwnership
)
2140 , m_bDraggedOver(false)
2141 , m_nLastMouseButton(0)
2142 , m_nLastMouseClicks(0)
2143 , m_pFocusInEvent(nullptr)
2144 , m_pFocusOutEvent(nullptr)
2145 , m_pBgCssProvider(nullptr)
2146 , m_nFocusInSignalId(0)
2147 , m_nMnemonicActivateSignalId(0)
2148 , m_nFocusOutSignalId(0)
2149 , m_nKeyPressSignalId(0)
2150 , m_nKeyReleaseSignalId(0)
2151 , m_nSizeAllocateSignalId(0)
2152 , m_nButtonPressSignalId(0)
2153 , m_nMotionSignalId(0)
2154 , m_nLeaveSignalId(0)
2155 , m_nEnterSignalId(0)
2156 , m_nButtonReleaseSignalId(0)
2157 , m_nDragMotionSignalId(0)
2158 , m_nDragDropSignalId(0)
2159 , m_nDragDropReceivedSignalId(0)
2160 , m_nDragLeaveSignalId(0)
2164 virtual void connect_key_press(const Link
<const KeyEvent
&, bool>& rLink
) override
2166 m_nKeyPressSignalId
= g_signal_connect(m_pWidget
, "key-press-event", G_CALLBACK(signalKey
), this);
2167 weld::Widget::connect_key_press(rLink
);
2170 virtual void connect_key_release(const Link
<const KeyEvent
&, bool>& rLink
) override
2172 m_nKeyReleaseSignalId
= g_signal_connect(m_pWidget
, "key-release-event", G_CALLBACK(signalKey
), this);
2173 weld::Widget::connect_key_release(rLink
);
2176 virtual void connect_mouse_press(const Link
<const MouseEvent
&, bool>& rLink
) override
2178 ensureButtonPressSignal();
2179 weld::Widget::connect_mouse_press(rLink
);
2182 virtual void connect_mouse_move(const Link
<const MouseEvent
&, bool>& rLink
) override
2184 ensureEventWidget();
2185 m_nMotionSignalId
= g_signal_connect(m_pMouseEventBox
, "motion-notify-event", G_CALLBACK(signalMotion
), this);
2186 m_nLeaveSignalId
= g_signal_connect(m_pMouseEventBox
, "leave-notify-event", G_CALLBACK(signalCrossing
), this);
2187 m_nEnterSignalId
= g_signal_connect(m_pMouseEventBox
, "enter-notify-event", G_CALLBACK(signalCrossing
), this);
2188 weld::Widget::connect_mouse_move(rLink
);
2191 virtual void connect_mouse_release(const Link
<const MouseEvent
&, bool>& rLink
) override
2193 ensureEventWidget();
2194 m_nButtonReleaseSignalId
= g_signal_connect(m_pMouseEventBox
, "button-release-event", G_CALLBACK(signalButton
), this);
2195 weld::Widget::connect_mouse_release(rLink
);
2198 virtual void set_sensitive(bool sensitive
) override
2200 gtk_widget_set_sensitive(m_pWidget
, sensitive
);
2203 virtual bool get_sensitive() const override
2205 return gtk_widget_get_sensitive(m_pWidget
);
2208 virtual bool get_visible() const override
2210 return gtk_widget_get_visible(m_pWidget
);
2213 virtual bool is_visible() const override
2215 return gtk_widget_is_visible(m_pWidget
);
2218 virtual void set_can_focus(bool bCanFocus
) override
2220 gtk_widget_set_can_focus(m_pWidget
, bCanFocus
);
2223 virtual void grab_focus() override
2225 disable_notify_events();
2226 gtk_widget_grab_focus(m_pWidget
);
2227 enable_notify_events();
2230 virtual bool has_focus() const override
2232 return gtk_widget_has_focus(m_pWidget
);
2235 virtual bool is_active() const override
2237 GtkWindow
* pTopLevel
= GTK_WINDOW(gtk_widget_get_toplevel(m_pWidget
));
2238 return pTopLevel
&& gtk_window_is_active(pTopLevel
) && has_focus();
2241 virtual void set_has_default(bool has_default
) override
2243 g_object_set(G_OBJECT(m_pWidget
), "has-default", has_default
, nullptr);
2246 virtual bool get_has_default() const override
2248 gboolean
has_default(false);
2249 g_object_get(G_OBJECT(m_pWidget
), "has-default", &has_default
, nullptr);
2253 virtual void show() override
2255 gtk_widget_show(m_pWidget
);
2258 virtual void hide() override
2260 gtk_widget_hide(m_pWidget
);
2263 virtual void set_size_request(int nWidth
, int nHeight
) override
2265 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
2266 if (GTK_IS_VIEWPORT(pParent
))
2267 pParent
= gtk_widget_get_parent(pParent
);
2268 if (GTK_IS_SCROLLED_WINDOW(pParent
))
2270 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(pParent
), nWidth
);
2271 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(pParent
), nHeight
);
2273 gtk_widget_set_size_request(m_pWidget
, nWidth
, nHeight
);
2276 virtual Size
get_size_request() const override
2278 int nWidth
, nHeight
;
2279 gtk_widget_get_size_request(m_pWidget
, &nWidth
, &nHeight
);
2280 return Size(nWidth
, nHeight
);
2283 virtual Size
get_preferred_size() const override
2285 GtkRequisition size
;
2286 gtk_widget_get_preferred_size(m_pWidget
, nullptr, &size
);
2287 return Size(size
.width
, size
.height
);
2290 virtual float get_approximate_digit_width() const override
2292 PangoContext
* pContext
= gtk_widget_get_pango_context(m_pWidget
);
2293 PangoFontMetrics
* pMetrics
= pango_context_get_metrics(pContext
,
2294 pango_context_get_font_description(pContext
),
2295 pango_context_get_language(pContext
));
2296 float nDigitWidth
= pango_font_metrics_get_approximate_digit_width(pMetrics
);
2297 pango_font_metrics_unref(pMetrics
);
2299 return nDigitWidth
/ PANGO_SCALE
;
2302 virtual int get_text_height() const override
2304 PangoContext
* pContext
= gtk_widget_get_pango_context(m_pWidget
);
2305 PangoFontMetrics
* pMetrics
= pango_context_get_metrics(pContext
,
2306 pango_context_get_font_description(pContext
),
2307 pango_context_get_language(pContext
));
2308 int nLineHeight
= pango_font_metrics_get_ascent(pMetrics
) + pango_font_metrics_get_descent(pMetrics
);
2309 pango_font_metrics_unref(pMetrics
);
2310 return nLineHeight
/ PANGO_SCALE
;
2313 virtual Size
get_pixel_size(const OUString
& rText
) const override
2315 OString
aStr(OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
));
2316 PangoLayout
* pLayout
= gtk_widget_create_pango_layout(m_pWidget
, aStr
.getStr());
2317 gint nWidth
, nHeight
;
2318 pango_layout_get_pixel_size(pLayout
, &nWidth
, &nHeight
);
2319 g_object_unref(pLayout
);
2320 return Size(nWidth
, nHeight
);
2323 virtual vcl::Font
get_font() override
2325 PangoContext
* pContext
= gtk_widget_get_pango_context(m_pWidget
);
2326 return pango_to_vcl(pango_context_get_font_description(pContext
),
2327 Application::GetSettings().GetUILanguageTag().getLocale());
2330 virtual void set_grid_left_attach(int nAttach
) override
2332 GtkContainer
* pParent
= GTK_CONTAINER(gtk_widget_get_parent(m_pWidget
));
2333 gtk_container_child_set(pParent
, m_pWidget
, "left-attach", nAttach
, nullptr);
2336 virtual int get_grid_left_attach() const override
2338 GtkContainer
* pParent
= GTK_CONTAINER(gtk_widget_get_parent(m_pWidget
));
2340 gtk_container_child_get(pParent
, m_pWidget
, "left-attach", &nAttach
, nullptr);
2344 virtual void set_grid_width(int nCols
) override
2346 GtkContainer
* pParent
= GTK_CONTAINER(gtk_widget_get_parent(m_pWidget
));
2347 gtk_container_child_set(pParent
, m_pWidget
, "width", nCols
, nullptr);
2350 virtual void set_grid_top_attach(int nAttach
) override
2352 GtkContainer
* pParent
= GTK_CONTAINER(gtk_widget_get_parent(m_pWidget
));
2353 gtk_container_child_set(pParent
, m_pWidget
, "top-attach", nAttach
, nullptr);
2356 virtual int get_grid_top_attach() const override
2358 GtkContainer
* pParent
= GTK_CONTAINER(gtk_widget_get_parent(m_pWidget
));
2360 gtk_container_child_get(pParent
, m_pWidget
, "top-attach", &nAttach
, nullptr);
2364 virtual void set_hexpand(bool bExpand
) override
2366 gtk_widget_set_hexpand(m_pWidget
, bExpand
);
2369 virtual bool get_hexpand() const override
2371 return gtk_widget_get_hexpand(m_pWidget
);
2374 virtual void set_vexpand(bool bExpand
) override
2376 gtk_widget_set_vexpand(m_pWidget
, bExpand
);
2379 virtual bool get_vexpand() const override
2381 return gtk_widget_get_vexpand(m_pWidget
);
2384 virtual void set_secondary(bool bSecondary
) override
2386 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
2387 if (pParent
&& GTK_IS_BUTTON_BOX(pParent
))
2388 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(pParent
), m_pWidget
, bSecondary
);
2391 virtual void set_margin_top(int nMargin
) override
2393 gtk_widget_set_margin_top(m_pWidget
, nMargin
);
2396 virtual void set_margin_bottom(int nMargin
) override
2398 gtk_widget_set_margin_bottom(m_pWidget
, nMargin
);
2401 virtual void set_margin_left(int nMargin
) override
2403 gtk_widget_set_margin_left(m_pWidget
, nMargin
);
2406 virtual void set_margin_right(int nMargin
) override
2408 gtk_widget_set_margin_right(m_pWidget
, nMargin
);
2411 virtual int get_margin_top() const override
2413 return gtk_widget_get_margin_top(m_pWidget
);
2416 virtual int get_margin_bottom() const override
2418 return gtk_widget_get_margin_bottom(m_pWidget
);
2421 virtual int get_margin_left() const override
2423 return gtk_widget_get_margin_left(m_pWidget
);
2426 virtual int get_margin_right() const override
2428 return gtk_widget_get_margin_right(m_pWidget
);
2431 virtual void set_accessible_name(const OUString
& rName
) override
2433 AtkObject
* pAtkObject
= gtk_widget_get_accessible(m_pWidget
);
2436 atk_object_set_name(pAtkObject
, OUStringToOString(rName
, RTL_TEXTENCODING_UTF8
).getStr());
2439 virtual OUString
get_accessible_name() const override
2441 AtkObject
* pAtkObject
= gtk_widget_get_accessible(m_pWidget
);
2442 const char* pStr
= pAtkObject
? atk_object_get_name(pAtkObject
) : nullptr;
2443 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
2446 virtual OUString
get_accessible_description() const override
2448 AtkObject
* pAtkObject
= gtk_widget_get_accessible(m_pWidget
);
2449 const char* pStr
= pAtkObject
? atk_object_get_description(pAtkObject
) : nullptr;
2450 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
2453 virtual void set_accessible_relation_labeled_by(weld::Widget
* pLabel
) override
2455 AtkObject
* pAtkObject
= gtk_widget_get_accessible(m_pWidget
);
2458 AtkObject
*pAtkLabel
= pLabel
? gtk_widget_get_accessible(dynamic_cast<GtkInstanceWidget
&>(*pLabel
).getWidget()) : nullptr;
2459 AtkRelationSet
*pRelationSet
= atk_object_ref_relation_set(pAtkObject
);
2460 AtkRelation
*pRelation
= atk_relation_set_get_relation_by_type(pRelationSet
, ATK_RELATION_LABELLED_BY
);
2462 atk_relation_set_remove(pRelationSet
, pRelation
);
2465 AtkObject
*obj_array
[1];
2466 obj_array
[0] = pAtkLabel
;
2467 pRelation
= atk_relation_new(obj_array
, 1, ATK_RELATION_LABELLED_BY
);
2468 atk_relation_set_add(pRelationSet
, pRelation
);
2470 g_object_unref(pRelationSet
);
2473 virtual void set_accessible_relation_label_for(weld::Widget
* pLabeled
) override
2475 AtkObject
* pAtkObject
= gtk_widget_get_accessible(m_pWidget
);
2478 AtkObject
*pAtkLabeled
= pLabeled
? gtk_widget_get_accessible(dynamic_cast<GtkInstanceWidget
&>(*pLabeled
).getWidget()) : nullptr;
2479 AtkRelationSet
*pRelationSet
= atk_object_ref_relation_set(pAtkObject
);
2480 AtkRelation
*pRelation
= atk_relation_set_get_relation_by_type(pRelationSet
, ATK_RELATION_LABEL_FOR
);
2482 atk_relation_set_remove(pRelationSet
, pRelation
);
2485 AtkObject
*obj_array
[1];
2486 obj_array
[0] = pAtkLabeled
;
2487 pRelation
= atk_relation_new(obj_array
, 1, ATK_RELATION_LABEL_FOR
);
2488 atk_relation_set_add(pRelationSet
, pRelation
);
2490 g_object_unref(pRelationSet
);
2493 virtual void add_extra_accessible_relation(const css::accessibility::AccessibleRelation
&rRelation
) override
2495 AtkObject
* pAtkObject
= gtk_widget_get_accessible(m_pWidget
);
2499 AtkRelationSet
*pRelationSet
= atk_object_ref_relation_set(pAtkObject
);
2500 AtkRelation
*pRel
= atk_object_wrapper_relation_new(rRelation
);
2501 m_aExtraAtkRelations
.push_back(pRel
);
2502 atk_relation_set_add(pRelationSet
, pRel
);
2503 g_object_unref(pRel
);
2504 g_object_unref(pRelationSet
);
2507 virtual void clear_extra_accessible_relations() override
2509 AtkObject
* pAtkObject
= gtk_widget_get_accessible(m_pWidget
);
2513 AtkRelationSet
*pRelationSet
= atk_object_ref_relation_set(pAtkObject
);
2514 for (AtkRelation
* pRel
: m_aExtraAtkRelations
)
2515 atk_relation_set_remove(pRelationSet
, pRel
);
2516 m_aExtraAtkRelations
.clear();
2517 g_object_unref(pRelationSet
);
2520 virtual bool get_extents_relative_to(weld::Widget
& rRelative
, int& x
, int &y
, int& width
, int &height
) override
2522 //for toplevel windows this is sadly futile under wayland, so we can't tell where a dialog is in order to allow
2523 //the document underneath to auto-scroll to place content in a visible location
2524 bool ret
= gtk_widget_translate_coordinates(m_pWidget
,
2525 dynamic_cast<GtkInstanceWidget
&>(rRelative
).getWidget(),
2527 width
= gtk_widget_get_allocated_width(m_pWidget
);
2528 height
= gtk_widget_get_allocated_height(m_pWidget
);
2532 virtual void set_tooltip_text(const OUString
& rTip
) override
2534 gtk_widget_set_tooltip_text(m_pWidget
, OUStringToOString(rTip
, RTL_TEXTENCODING_UTF8
).getStr());
2537 virtual OUString
get_tooltip_text() const override
2539 const gchar
* pStr
= gtk_widget_get_tooltip_text(m_pWidget
);
2540 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
2543 virtual std::unique_ptr
<weld::Container
> weld_parent() const override
;
2545 virtual OString
get_buildable_name() const override
2547 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(m_pWidget
));
2548 return OString(pStr
, pStr
? strlen(pStr
) : 0);
2551 virtual void set_help_id(const OString
& rHelpId
) override
2553 ::set_help_id(m_pWidget
, rHelpId
);
2556 virtual OString
get_help_id() const override
2558 OString sRet
= ::get_help_id(m_pWidget
);
2560 sRet
= OString("null");
2564 GtkWidget
* getWidget()
2569 GtkWindow
* getWindow()
2571 return GTK_WINDOW(gtk_widget_get_toplevel(m_pWidget
));
2574 virtual void connect_focus_in(const Link
<Widget
&, void>& rLink
) override
2576 m_nFocusInSignalId
= g_signal_connect(m_pWidget
, "focus-in-event", G_CALLBACK(signalFocusIn
), this);
2577 weld::Widget::connect_focus_in(rLink
);
2580 virtual void connect_mnemonic_activate(const Link
<Widget
&, bool>& rLink
) override
2582 m_nMnemonicActivateSignalId
= g_signal_connect(m_pWidget
, "mnemonic-activate", G_CALLBACK(signalMnemonicActivate
), this);
2583 weld::Widget::connect_mnemonic_activate(rLink
);
2586 virtual void connect_focus_out(const Link
<Widget
&, void>& rLink
) override
2588 m_nFocusOutSignalId
= g_signal_connect(m_pWidget
, "focus-out-event", G_CALLBACK(signalFocusOut
), this);
2589 weld::Widget::connect_focus_out(rLink
);
2592 virtual void connect_size_allocate(const Link
<const Size
&, void>& rLink
) override
2594 m_nSizeAllocateSignalId
= g_signal_connect(m_pWidget
, "size_allocate", G_CALLBACK(signalSizeAllocate
), this);
2595 weld::Widget::connect_size_allocate(rLink
);
2598 virtual void signal_size_allocate(guint nWidth
, guint nHeight
)
2600 m_aSizeAllocateHdl
.Call(Size(nWidth
, nHeight
));
2603 gboolean
signal_key(const GdkEventKey
* pEvent
)
2605 if (pEvent
->type
== GDK_KEY_PRESS
&& m_aKeyPressHdl
.IsSet())
2607 SolarMutexGuard aGuard
;
2608 return m_aKeyPressHdl
.Call(GtkToVcl(*pEvent
));
2610 if (pEvent
->type
== GDK_KEY_RELEASE
&& m_aKeyReleaseHdl
.IsSet())
2612 SolarMutexGuard aGuard
;
2613 return m_aKeyReleaseHdl
.Call(GtkToVcl(*pEvent
));
2618 virtual void grab_add() override
2620 gtk_grab_add(m_pWidget
);
2623 virtual bool has_grab() const override
2625 return gtk_widget_has_grab(m_pWidget
);
2628 virtual void grab_remove() override
2630 gtk_grab_remove(m_pWidget
);
2633 virtual bool get_direction() const override
2635 return gtk_widget_get_direction(m_pWidget
) == GTK_TEXT_DIR_RTL
;
2638 virtual void set_direction(bool bRTL
) override
2640 gtk_widget_set_direction(m_pWidget
, bRTL
? GTK_TEXT_DIR_RTL
: GTK_TEXT_DIR_LTR
);
2643 virtual void freeze() override
2645 gtk_widget_freeze_child_notify(m_pWidget
);
2649 virtual void thaw() override
2651 gtk_widget_thaw_child_notify(m_pWidget
);
2655 bool get_frozen() const { return m_bFrozen
; }
2657 virtual css::uno::Reference
<css::datatransfer::dnd::XDropTarget
> get_drop_target() override
2661 m_xDropTarget
.set(new GtkDropTarget
);
2662 if (!gtk_drag_dest_get_track_motion(m_pWidget
))
2664 gtk_drag_dest_set(m_pWidget
, GtkDestDefaults(0), nullptr, 0, GdkDragAction(0));
2665 gtk_drag_dest_set_track_motion(m_pWidget
, true);
2667 m_nDragMotionSignalId
= g_signal_connect(m_pWidget
, "drag-motion", G_CALLBACK(signalDragMotion
), this);
2668 m_nDragDropSignalId
= g_signal_connect(m_pWidget
, "drag-drop", G_CALLBACK(signalDragDrop
), this);
2669 m_nDragDropReceivedSignalId
= g_signal_connect(m_pWidget
, "drag-data-received", G_CALLBACK(signalDragDropReceived
), this);
2670 m_nDragLeaveSignalId
= g_signal_connect(m_pWidget
, "drag-leave", G_CALLBACK(signalDragLeave
), this);
2672 return m_xDropTarget
.get();
2675 virtual void set_stack_background() override
2677 OUString sColor
= Application::GetSettings().GetStyleSettings().GetWindowColor().AsRGBHexString();
2678 set_background(&sColor
);
2681 virtual void set_highlight_background() override
2683 OUString sColor
= Application::GetSettings().GetStyleSettings().GetHighlightColor().AsRGBHexString();
2684 set_background(&sColor
);
2687 virtual ~GtkInstanceWidget() override
2689 if (m_pFocusInEvent
)
2690 Application::RemoveUserEvent(m_pFocusInEvent
);
2691 if (m_pFocusOutEvent
)
2692 Application::RemoveUserEvent(m_pFocusOutEvent
);
2693 if (m_nDragMotionSignalId
)
2694 g_signal_handler_disconnect(m_pWidget
, m_nDragMotionSignalId
);
2695 if (m_nDragDropSignalId
)
2696 g_signal_handler_disconnect(m_pWidget
, m_nDragDropSignalId
);
2697 if (m_nDragDropReceivedSignalId
)
2698 g_signal_handler_disconnect(m_pWidget
, m_nDragDropReceivedSignalId
);
2699 if (m_nDragLeaveSignalId
)
2700 g_signal_handler_disconnect(m_pWidget
, m_nDragLeaveSignalId
);
2701 if (m_nKeyPressSignalId
)
2702 g_signal_handler_disconnect(m_pWidget
, m_nKeyPressSignalId
);
2703 if (m_nKeyReleaseSignalId
)
2704 g_signal_handler_disconnect(m_pWidget
, m_nKeyReleaseSignalId
);
2705 if (m_nButtonPressSignalId
)
2706 g_signal_handler_disconnect(m_pMouseEventBox
, m_nButtonPressSignalId
);
2707 if (m_nMotionSignalId
)
2708 g_signal_handler_disconnect(m_pMouseEventBox
, m_nMotionSignalId
);
2709 if (m_nLeaveSignalId
)
2710 g_signal_handler_disconnect(m_pMouseEventBox
, m_nLeaveSignalId
);
2711 if (m_nEnterSignalId
)
2712 g_signal_handler_disconnect(m_pMouseEventBox
, m_nEnterSignalId
);
2713 if (m_nButtonReleaseSignalId
)
2714 g_signal_handler_disconnect(m_pMouseEventBox
, m_nButtonReleaseSignalId
);
2715 if (m_nFocusInSignalId
)
2716 g_signal_handler_disconnect(m_pWidget
, m_nFocusInSignalId
);
2717 if (m_nMnemonicActivateSignalId
)
2718 g_signal_handler_disconnect(m_pWidget
, m_nMnemonicActivateSignalId
);
2719 if (m_nFocusOutSignalId
)
2720 g_signal_handler_disconnect(m_pWidget
, m_nFocusOutSignalId
);
2721 if (m_nSizeAllocateSignalId
)
2722 g_signal_handler_disconnect(m_pWidget
, m_nSizeAllocateSignalId
);
2724 set_background(nullptr);
2726 if (m_pMouseEventBox
&& m_pMouseEventBox
!= m_pWidget
)
2728 // put things back they way we found them
2729 GtkWidget
* pParent
= gtk_widget_get_parent(m_pMouseEventBox
);
2731 g_object_ref(m_pWidget
);
2732 gtk_container_remove(GTK_CONTAINER(m_pMouseEventBox
), m_pWidget
);
2734 gtk_widget_destroy(m_pMouseEventBox
);
2736 gtk_container_add(GTK_CONTAINER(pParent
), m_pWidget
);
2737 g_object_unref(m_pWidget
);
2740 if (m_bTakeOwnership
)
2741 gtk_widget_destroy(m_pWidget
);
2744 virtual void disable_notify_events()
2746 if (m_nFocusInSignalId
)
2747 g_signal_handler_block(m_pWidget
, m_nFocusInSignalId
);
2748 if (m_nMnemonicActivateSignalId
)
2749 g_signal_handler_block(m_pWidget
, m_nMnemonicActivateSignalId
);
2750 if (m_nFocusOutSignalId
)
2751 g_signal_handler_block(m_pWidget
, m_nFocusOutSignalId
);
2752 if (m_nSizeAllocateSignalId
)
2753 g_signal_handler_block(m_pWidget
, m_nSizeAllocateSignalId
);
2756 virtual void enable_notify_events()
2758 if (m_nSizeAllocateSignalId
)
2759 g_signal_handler_unblock(m_pWidget
, m_nSizeAllocateSignalId
);
2760 if (m_nFocusOutSignalId
)
2761 g_signal_handler_unblock(m_pWidget
, m_nFocusOutSignalId
);
2762 if (m_nMnemonicActivateSignalId
)
2763 g_signal_handler_unblock(m_pWidget
, m_nMnemonicActivateSignalId
);
2764 if (m_nFocusInSignalId
)
2765 g_signal_handler_unblock(m_pWidget
, m_nFocusInSignalId
);
2768 virtual void help_hierarchy_foreach(const std::function
<bool(const OString
&)>& func
) override
;
2770 virtual OUString
strip_mnemonic(const OUString
&rLabel
) const override
2772 return rLabel
.replaceFirst("_", "");
2775 virtual VclPtr
<VirtualDevice
> create_virtual_device() const override
2777 // create with no separate alpha layer like everything sane does
2778 auto xRet
= VclPtr
<VirtualDevice
>::Create();
2779 xRet
->SetBackground(COL_TRANSPARENT
);
2784 IMPL_LINK_NOARG(GtkInstanceWidget
, async_signal_focus_in
, void*, void)
2786 m_pFocusInEvent
= nullptr;
2790 IMPL_LINK_NOARG(GtkInstanceWidget
, async_signal_focus_out
, void*, void)
2792 m_pFocusOutEvent
= nullptr;
2798 OString
MapToGtkAccelerator(const OUString
&rStr
)
2800 return OUStringToOString(rStr
.replaceFirst("~", "_"), RTL_TEXTENCODING_UTF8
);
2803 OUString
get_label(GtkLabel
* pLabel
)
2805 const gchar
* pStr
= gtk_label_get_label(pLabel
);
2806 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
2809 void set_label(GtkLabel
* pLabel
, const OUString
& rText
)
2811 gtk_label_set_label(pLabel
, MapToGtkAccelerator(rText
).getStr());
2814 OUString
get_label(GtkButton
* pButton
)
2816 const gchar
* pStr
= gtk_button_get_label(pButton
);
2817 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
2820 void set_label(GtkButton
* pButton
, const OUString
& rText
)
2822 gtk_button_set_label(pButton
, MapToGtkAccelerator(rText
).getStr());
2825 OUString
get_title(GtkWindow
* pWindow
)
2827 const gchar
* pStr
= gtk_window_get_title(pWindow
);
2828 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
2831 void set_title(GtkWindow
* pWindow
, const OUString
& rTitle
)
2833 gtk_window_set_title(pWindow
, OUStringToOString(rTitle
, RTL_TEXTENCODING_UTF8
).getStr());
2836 OUString
get_primary_text(GtkMessageDialog
* pMessageDialog
)
2838 gchar
* pText
= nullptr;
2839 g_object_get(G_OBJECT(pMessageDialog
), "text", &pText
, nullptr);
2840 return OUString(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
2843 void set_primary_text(GtkMessageDialog
* pMessageDialog
, const OUString
& rText
)
2845 g_object_set(G_OBJECT(pMessageDialog
), "text",
2846 OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr(),
2850 void set_secondary_text(GtkMessageDialog
* pMessageDialog
, const OUString
& rText
)
2852 g_object_set(G_OBJECT(pMessageDialog
), "secondary-text",
2853 OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr(),
2857 OUString
get_secondary_text(GtkMessageDialog
* pMessageDialog
)
2859 gchar
* pText
= nullptr;
2860 g_object_get(G_OBJECT(pMessageDialog
), "secondary-text", &pText
, nullptr);
2861 return OUString(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
2867 GdkPixbuf
* load_icon_from_stream(SvMemoryStream
& rStream
)
2869 GdkPixbufLoader
*pixbuf_loader
= gdk_pixbuf_loader_new();
2870 gdk_pixbuf_loader_write(pixbuf_loader
, static_cast<const guchar
*>(rStream
.GetData()),
2871 rStream
.TellEnd(), nullptr);
2872 gdk_pixbuf_loader_close(pixbuf_loader
, nullptr);
2873 GdkPixbuf
* pixbuf
= gdk_pixbuf_loader_get_pixbuf(pixbuf_loader
);
2875 g_object_ref(pixbuf
);
2876 g_object_unref(pixbuf_loader
);
2880 GdkPixbuf
* load_icon_by_name(const OUString
& rIconName
, const OUString
& rIconTheme
, const OUString
& rUILang
)
2882 auto xMemStm
= ImageTree::get().getImageStream(rIconName
, rIconTheme
, rUILang
);
2885 return load_icon_from_stream(*xMemStm
);
2889 GdkPixbuf
* load_icon_by_name(const OUString
& rIconName
)
2891 OUString sIconTheme
= Application::GetSettings().GetStyleSettings().DetermineIconTheme();
2892 OUString sUILang
= Application::GetSettings().GetUILanguageTag().getBcp47();
2893 return load_icon_by_name(rIconName
, sIconTheme
, sUILang
);
2898 GdkPixbuf
* getPixbuf(const css::uno::Reference
<css::graphic::XGraphic
>& rImage
)
2900 Image
aImage(rImage
);
2902 std::unique_ptr
<SvMemoryStream
> xMemStm(new SvMemoryStream
);
2903 vcl::PNGWriter
aWriter(aImage
.GetBitmapEx());
2904 aWriter
.Write(*xMemStm
);
2906 return load_icon_from_stream(*xMemStm
);
2909 GdkPixbuf
* getPixbuf(const VirtualDevice
& rDevice
)
2911 Size
aSize(rDevice
.GetOutputSizePixel());
2912 cairo_surface_t
* orig_surface
= get_underlying_cairo_surface(rDevice
);
2913 double m_fXScale
, m_fYScale
;
2914 dl_cairo_surface_get_device_scale(orig_surface
, &m_fXScale
, &m_fYScale
);
2916 cairo_surface_t
* surface
;
2917 if (m_fXScale
!= 1.0 || m_fYScale
!= -1)
2919 surface
= cairo_surface_create_similar_image(orig_surface
,
2920 CAIRO_FORMAT_ARGB32
,
2923 cairo_t
* cr
= cairo_create(surface
);
2924 cairo_set_source_surface(cr
, orig_surface
, 0, 0);
2929 surface
= orig_surface
;
2931 GdkPixbuf
* pRet
= gdk_pixbuf_get_from_surface(surface
, 0, 0, aSize
.Width(), aSize
.Height());
2933 if (surface
!= orig_surface
)
2934 cairo_surface_destroy(surface
);
2939 GtkWidget
* image_new_from_virtual_device(const VirtualDevice
& rImageSurface
)
2941 GtkWidget
* pImage
= nullptr;
2942 if (gtk_check_version(3, 20, 0) == nullptr)
2944 cairo_surface_t
* surface
= get_underlying_cairo_surface(rImageSurface
);
2946 Size
aSize(rImageSurface
.GetOutputSizePixel());
2947 cairo_surface_t
* target
= cairo_surface_create_similar(surface
,
2948 cairo_surface_get_content(surface
),
2952 cairo_t
* cr
= cairo_create(target
);
2953 cairo_set_source_surface(cr
, surface
, 0, 0);
2956 pImage
= gtk_image_new_from_surface(target
);
2957 cairo_surface_destroy(target
);
2961 GdkPixbuf
* pixbuf
= getPixbuf(rImageSurface
);
2962 pImage
= gtk_image_new_from_pixbuf(pixbuf
);
2963 g_object_unref(pixbuf
);
2973 bool m_bTakeOwnership
;
2974 std::map
<OString
, GtkMenuItem
*> m_aMap
;
2977 static void collect(GtkWidget
* pItem
, gpointer widget
)
2979 GtkMenuItem
* pMenuItem
= GTK_MENU_ITEM(pItem
);
2980 if (GtkWidget
* pSubMenu
= gtk_menu_item_get_submenu(pMenuItem
))
2981 gtk_container_foreach(GTK_CONTAINER(pSubMenu
), collect
, widget
);
2982 MenuHelper
* pThis
= static_cast<MenuHelper
*>(widget
);
2983 pThis
->add_to_map(pMenuItem
);
2986 static void signalActivate(GtkMenuItem
* pItem
, gpointer widget
)
2988 MenuHelper
* pThis
= static_cast<MenuHelper
*>(widget
);
2989 SolarMutexGuard aGuard
;
2990 pThis
->signal_activate(pItem
);
2993 virtual void signal_activate(GtkMenuItem
* pItem
) = 0;
2996 MenuHelper(GtkMenu
* pMenu
, bool bTakeOwnership
)
2998 , m_bTakeOwnership(bTakeOwnership
)
3002 gtk_container_foreach(GTK_CONTAINER(m_pMenu
), collect
, this);
3005 void add_to_map(GtkMenuItem
* pMenuItem
)
3007 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pMenuItem
));
3008 OString
id(pStr
, pStr
? strlen(pStr
) : 0);
3009 m_aMap
[id
] = pMenuItem
;
3010 g_signal_connect(pMenuItem
, "activate", G_CALLBACK(signalActivate
), this);
3013 void remove_from_map(GtkMenuItem
* pMenuItem
)
3015 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pMenuItem
));
3016 OString
id(pStr
, pStr
? strlen(pStr
) : 0);
3017 auto iter
= m_aMap
.find(id
);
3018 g_signal_handlers_disconnect_by_data(pMenuItem
, this);
3022 void disable_item_notify_events()
3024 for (auto& a
: m_aMap
)
3025 g_signal_handlers_block_by_func(a
.second
, reinterpret_cast<void*>(signalActivate
), this);
3028 void enable_item_notify_events()
3030 for (auto& a
: m_aMap
)
3031 g_signal_handlers_unblock_by_func(a
.second
, reinterpret_cast<void*>(signalActivate
), this);
3034 void insert_item(int pos
, const OUString
& rId
, const OUString
& rStr
,
3035 const OUString
* pIconName
, const VirtualDevice
* pImageSurface
,
3038 GtkWidget
* pImage
= nullptr;
3039 if (pIconName
&& !pIconName
->isEmpty())
3041 GdkPixbuf
* pixbuf
= load_icon_by_name(*pIconName
);
3044 pImage
= gtk_image_new_from_pixbuf(pixbuf
);
3045 g_object_unref(pixbuf
);
3048 else if (pImageSurface
)
3049 pImage
= image_new_from_virtual_device(*pImageSurface
);
3054 GtkWidget
*pBox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
3055 GtkWidget
*pLabel
= gtk_label_new(MapToGtkAccelerator(rStr
).getStr());
3056 pItem
= bCheck
? gtk_check_menu_item_new() : gtk_menu_item_new();
3057 gtk_container_add(GTK_CONTAINER(pBox
), pImage
);
3058 gtk_container_add(GTK_CONTAINER(pBox
), pLabel
);
3059 gtk_container_add(GTK_CONTAINER(pItem
), pBox
);
3060 gtk_widget_show_all(pItem
);
3064 pItem
= bCheck
? gtk_check_menu_item_new_with_mnemonic(MapToGtkAccelerator(rStr
).getStr())
3065 : gtk_menu_item_new_with_mnemonic(MapToGtkAccelerator(rStr
).getStr());
3067 gtk_buildable_set_name(GTK_BUILDABLE(pItem
), OUStringToOString(rId
, RTL_TEXTENCODING_UTF8
).getStr());
3068 gtk_menu_shell_append(GTK_MENU_SHELL(m_pMenu
), pItem
);
3069 gtk_widget_show(pItem
);
3070 add_to_map(GTK_MENU_ITEM(pItem
));
3072 gtk_menu_reorder_child(m_pMenu
, pItem
, pos
);
3075 void insert_separator(int pos
, const OUString
& rId
)
3077 GtkWidget
* pItem
= gtk_separator_menu_item_new();
3078 gtk_buildable_set_name(GTK_BUILDABLE(pItem
), OUStringToOString(rId
, RTL_TEXTENCODING_UTF8
).getStr());
3079 gtk_menu_shell_append(GTK_MENU_SHELL(m_pMenu
), pItem
);
3080 gtk_widget_show(pItem
);
3081 add_to_map(GTK_MENU_ITEM(pItem
));
3083 gtk_menu_reorder_child(m_pMenu
, pItem
, pos
);
3086 void remove_item(const OString
& rIdent
)
3088 GtkMenuItem
* pMenuItem
= m_aMap
[rIdent
];
3089 remove_from_map(pMenuItem
);
3090 gtk_widget_destroy(GTK_WIDGET(pMenuItem
));
3093 void set_item_sensitive(const OString
& rIdent
, bool bSensitive
)
3095 gtk_widget_set_sensitive(GTK_WIDGET(m_aMap
[rIdent
]), bSensitive
);
3098 void set_item_active(const OString
& rIdent
, bool bActive
)
3100 disable_item_notify_events();
3101 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(m_aMap
[rIdent
]), bActive
);
3102 enable_item_notify_events();
3105 bool get_item_active(const OString
& rIdent
) const
3107 return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(m_aMap
.find(rIdent
)->second
));
3110 void set_item_label(const OString
& rIdent
, const OUString
& rText
)
3112 gtk_menu_item_set_label(m_aMap
[rIdent
], MapToGtkAccelerator(rText
).getStr());
3115 OUString
get_item_label(const OString
& rIdent
) const
3117 const gchar
* pText
= gtk_menu_item_get_label(m_aMap
.find(rIdent
)->second
);
3118 return OUString(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
3121 void set_item_help_id(const OString
& rIdent
, const OString
& rHelpId
)
3123 set_help_id(GTK_WIDGET(m_aMap
[rIdent
]), rHelpId
);
3126 OString
get_item_help_id(const OString
& rIdent
) const
3128 return get_help_id(GTK_WIDGET(m_aMap
.find(rIdent
)->second
));
3131 void set_item_visible(const OString
& rIdent
, bool bShow
)
3133 GtkWidget
* pWidget
= GTK_WIDGET(m_aMap
[rIdent
]);
3135 gtk_widget_show(pWidget
);
3137 gtk_widget_hide(pWidget
);
3142 for (const auto& a
: m_aMap
)
3144 GtkMenuItem
* pMenuItem
= a
.second
;
3145 g_signal_handlers_disconnect_by_data(pMenuItem
, this);
3146 gtk_widget_destroy(GTK_WIDGET(pMenuItem
));
3151 GtkMenu
* getMenu() const
3156 virtual ~MenuHelper()
3158 for (auto& a
: m_aMap
)
3159 g_signal_handlers_disconnect_by_data(a
.second
, this);
3160 if (m_bTakeOwnership
)
3161 gtk_widget_destroy(GTK_WIDGET(m_pMenu
));
3165 class GtkInstanceSizeGroup
: public weld::SizeGroup
3168 GtkSizeGroup
* m_pGroup
;
3170 GtkInstanceSizeGroup()
3171 : m_pGroup(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
))
3174 virtual void add_widget(weld::Widget
* pWidget
) override
3176 GtkInstanceWidget
* pVclWidget
= dynamic_cast<GtkInstanceWidget
*>(pWidget
);
3178 gtk_size_group_add_widget(m_pGroup
, pVclWidget
->getWidget());
3180 virtual void set_mode(VclSizeGroupMode eVclMode
) override
3182 GtkSizeGroupMode
eGtkMode(GTK_SIZE_GROUP_NONE
);
3185 case VclSizeGroupMode::NONE
:
3186 eGtkMode
= GTK_SIZE_GROUP_NONE
;
3188 case VclSizeGroupMode::Horizontal
:
3189 eGtkMode
= GTK_SIZE_GROUP_HORIZONTAL
;
3191 case VclSizeGroupMode::Vertical
:
3192 eGtkMode
= GTK_SIZE_GROUP_VERTICAL
;
3194 case VclSizeGroupMode::Both
:
3195 eGtkMode
= GTK_SIZE_GROUP_BOTH
;
3198 gtk_size_group_set_mode(m_pGroup
, eGtkMode
);
3200 virtual ~GtkInstanceSizeGroup() override
3202 g_object_unref(m_pGroup
);
3208 class ChildFrame
: public WorkWindow
3211 ChildFrame(vcl::Window
* pParent
, WinBits nStyle
)
3212 : WorkWindow(pParent
, nStyle
)
3215 virtual void Resize() override
3217 WorkWindow::Resize();
3218 if (vcl::Window
*pChild
= GetWindow(GetWindowType::FirstChild
))
3219 pChild
->SetPosSizePixel(Point(0, 0), GetSizePixel());
3224 class GtkInstanceContainer
: public GtkInstanceWidget
, public virtual weld::Container
3227 GtkContainer
* m_pContainer
;
3229 static void implResetDefault(GtkWidget
*pWidget
, gpointer user_data
)
3231 if (GTK_IS_BUTTON(pWidget
))
3232 g_object_set(G_OBJECT(pWidget
), "has-default", false, nullptr);
3233 if (GTK_IS_CONTAINER(pWidget
))
3234 gtk_container_forall(GTK_CONTAINER(pWidget
), implResetDefault
, user_data
);
3238 GtkInstanceContainer(GtkContainer
* pContainer
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
3239 : GtkInstanceWidget(GTK_WIDGET(pContainer
), pBuilder
, bTakeOwnership
)
3240 , m_pContainer(pContainer
)
3244 GtkContainer
* getContainer() { return m_pContainer
; }
3246 virtual void move(weld::Widget
* pWidget
, weld::Container
* pNewParent
) override
3248 GtkInstanceWidget
* pGtkWidget
= dynamic_cast<GtkInstanceWidget
*>(pWidget
);
3250 GtkWidget
* pChild
= pGtkWidget
->getWidget();
3251 g_object_ref(pChild
);
3252 gtk_container_remove(getContainer(), pChild
);
3254 GtkInstanceContainer
* pNewGtkParent
= dynamic_cast<GtkInstanceContainer
*>(pNewParent
);
3255 assert(!pNewParent
|| pNewGtkParent
);
3257 gtk_container_add(pNewGtkParent
->getContainer(), pChild
);
3258 g_object_unref(pChild
);
3261 virtual void recursively_unset_default_buttons() override
3263 implResetDefault(GTK_WIDGET(m_pContainer
), nullptr);
3266 virtual css::uno::Reference
<css::awt::XWindow
> CreateChildFrame() override
3268 // This will cause a GtkSalFrame to be created. With WB_SYSTEMCHILDWINDOW set it
3269 // will create a toplevel GtkEventBox window
3270 auto xEmbedWindow
= VclPtr
<ChildFrame
>::Create(ImplGetDefaultWindow(), WB_SYSTEMCHILDWINDOW
| WB_DIALOGCONTROL
| WB_CHILDDLGCTRL
);
3271 SalFrame
* pFrame
= xEmbedWindow
->ImplGetFrame();
3272 GtkSalFrame
* pGtkFrame
= dynamic_cast<GtkSalFrame
*>(pFrame
);
3275 // relocate that toplevel GtkEventBox into this widget
3276 GtkWidget
* pWindow
= pGtkFrame
->getWindow();
3278 GtkWidget
* pParent
= gtk_widget_get_parent(pWindow
);
3280 g_object_ref(pWindow
);
3281 gtk_container_remove(GTK_CONTAINER(pParent
), pWindow
);
3282 gtk_container_add(m_pContainer
, pWindow
);
3283 gtk_container_child_set(m_pContainer
, pWindow
, "expand", true, "fill", true, nullptr);
3284 gtk_widget_set_hexpand(pWindow
, true);
3285 gtk_widget_set_vexpand(pWindow
, true);
3286 gtk_widget_realize(pWindow
);
3287 gtk_widget_set_can_focus(pWindow
, true);
3288 g_object_unref(pWindow
);
3290 xEmbedWindow
->Show();
3291 css::uno::Reference
<css::awt::XWindow
> xWindow(xEmbedWindow
->GetComponentInterface(), css::uno::UNO_QUERY
);
3296 std::unique_ptr
<weld::Container
> GtkInstanceWidget::weld_parent() const
3298 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
3301 return std::make_unique
<GtkInstanceContainer
>(GTK_CONTAINER(pParent
), m_pBuilder
, false);
3304 class GtkInstanceBox
: public GtkInstanceContainer
, public virtual weld::Box
3310 GtkInstanceBox(GtkBox
* pBox
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
3311 : GtkInstanceContainer(GTK_CONTAINER(pBox
), pBuilder
, bTakeOwnership
)
3316 virtual void reorder_child(weld::Widget
* pWidget
, int nNewPosition
) override
3318 GtkInstanceWidget
* pGtkWidget
= dynamic_cast<GtkInstanceWidget
*>(pWidget
);
3320 GtkWidget
* pChild
= pGtkWidget
->getWidget();
3321 gtk_box_reorder_child(m_pBox
, pChild
, nNewPosition
);
3327 void set_cursor(GtkWidget
* pWidget
, const char *pName
)
3329 if (!gtk_widget_get_realized(pWidget
))
3330 gtk_widget_realize(pWidget
);
3331 GdkDisplay
*pDisplay
= gtk_widget_get_display(pWidget
);
3332 GdkCursor
*pCursor
= pName
? gdk_cursor_new_from_name(pDisplay
, pName
) : nullptr;
3333 gdk_window_set_cursor(gtk_widget_get_window(pWidget
), pCursor
);
3334 gdk_display_flush(pDisplay
);
3336 g_object_unref(pCursor
);
3344 const char * m_aType
;
3348 int getButtonPriority(const OString
&rType
)
3350 static const size_t N_TYPES
= 7;
3351 static const ButtonOrder aDiscardCancelSave
[N_TYPES
] =
3362 static const ButtonOrder aSaveDiscardCancel
[N_TYPES
] =
3373 const ButtonOrder
* pOrder
= &aDiscardCancelSave
[0];
3375 const OUString
&rEnv
= Application::GetDesktopEnvironment();
3377 if (rEnv
.equalsIgnoreAsciiCase("windows") ||
3378 rEnv
.equalsIgnoreAsciiCase("tde") ||
3379 rEnv
.startsWithIgnoreAsciiCase("kde"))
3381 pOrder
= &aSaveDiscardCancel
[0];
3384 for (size_t i
= 0; i
< N_TYPES
; ++i
, ++pOrder
)
3386 if (rType
.endsWith(pOrder
->m_aType
))
3387 return pOrder
->m_nPriority
;
3393 bool sortButtons(const GtkWidget
* pA
, const GtkWidget
* pB
)
3395 //order within groups according to platform rules
3396 return getButtonPriority(::get_help_id(pA
)) < getButtonPriority(::get_help_id(pB
));
3399 void sort_native_button_order(GtkBox
* pContainer
)
3401 std::vector
<GtkWidget
*> aChildren
;
3402 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(pContainer
));
3403 for (GList
* pChild
= g_list_first(pChildren
); pChild
; pChild
= g_list_next(pChild
))
3404 aChildren
.push_back(static_cast<GtkWidget
*>(pChild
->data
));
3405 g_list_free(pChildren
);
3407 //sort child order within parent so that we match the platform button order
3408 std::stable_sort(aChildren
.begin(), aChildren
.end(), sortButtons
);
3410 for (size_t pos
= 0; pos
< aChildren
.size(); ++pos
)
3411 gtk_box_reorder_child(pContainer
, aChildren
[pos
], pos
);
3414 Point
get_csd_offset(GtkWidget
* pTopLevel
)
3416 // try and omit drawing CSD under wayland
3417 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(pTopLevel
));
3418 GList
* pChild
= g_list_first(pChildren
);
3421 gtk_widget_translate_coordinates(GTK_WIDGET(pChild
->data
),
3422 GTK_WIDGET(pTopLevel
),
3425 int innerborder
= gtk_container_get_border_width(GTK_CONTAINER(pChild
->data
));
3426 g_list_free(pChildren
);
3428 int outerborder
= gtk_container_get_border_width(GTK_CONTAINER(pTopLevel
));
3429 int totalborder
= outerborder
+ innerborder
;
3436 void do_collect_screenshot_data(GtkWidget
* pItem
, gpointer data
)
3438 GtkWidget
* pTopLevel
= gtk_widget_get_toplevel(pItem
);
3441 gtk_widget_translate_coordinates(pItem
, pTopLevel
, 0, 0, &x
, &y
);
3443 Point aOffset
= get_csd_offset(pTopLevel
);
3445 GtkAllocation alloc
;
3446 gtk_widget_get_allocation(pItem
, &alloc
);
3448 const basegfx::B2IPoint
aCurrentTopLeft(x
- aOffset
.X(), y
- aOffset
.Y());
3449 const basegfx::B2IRange
aCurrentRange(aCurrentTopLeft
, aCurrentTopLeft
+ basegfx::B2IPoint(alloc
.width
, alloc
.height
));
3451 if (!aCurrentRange
.isEmpty())
3453 weld::ScreenShotCollection
* pCollection
= static_cast<weld::ScreenShotCollection
*>(data
);
3454 pCollection
->emplace_back(::get_help_id(pItem
), aCurrentRange
);
3457 if (GTK_IS_CONTAINER(pItem
))
3458 gtk_container_forall(GTK_CONTAINER(pItem
), do_collect_screenshot_data
, data
);
3462 class GtkInstanceWindow
: public GtkInstanceContainer
, public virtual weld::Window
3465 GtkWindow
* m_pWindow
;
3466 rtl::Reference
<SalGtkXWindow
> m_xWindow
; //uno api
3467 gulong m_nToplevelFocusChangedSignalId
;
3469 static void help_pressed(GtkAccelGroup
*, GObject
*, guint
, GdkModifierType
, gpointer widget
)
3471 GtkInstanceWindow
* pThis
= static_cast<GtkInstanceWindow
*>(widget
);
3475 static void signalToplevelFocusChanged(GtkWindow
*, GParamSpec
*, gpointer widget
)
3477 GtkInstanceWindow
* pThis
= static_cast<GtkInstanceWindow
*>(widget
);
3478 pThis
->signal_toplevel_focus_changed();
3484 GtkInstanceWindow(GtkWindow
* pWindow
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
3485 : GtkInstanceContainer(GTK_CONTAINER(pWindow
), pBuilder
, bTakeOwnership
)
3486 , m_pWindow(pWindow
)
3487 , m_nToplevelFocusChangedSignalId(0)
3489 //hook up F1 to show help
3490 GtkAccelGroup
*pGroup
= gtk_accel_group_new();
3491 GClosure
* closure
= g_cclosure_new(G_CALLBACK(help_pressed
), this, nullptr);
3492 gtk_accel_group_connect(pGroup
, GDK_KEY_F1
, static_cast<GdkModifierType
>(0), GTK_ACCEL_LOCKED
, closure
);
3493 gtk_window_add_accel_group(pWindow
, pGroup
);
3496 virtual void set_title(const OUString
& rTitle
) override
3498 ::set_title(m_pWindow
, rTitle
);
3501 virtual OUString
get_title() const override
3503 return ::get_title(m_pWindow
);
3506 virtual css::uno::Reference
<css::awt::XWindow
> GetXWindow() override
3508 if (!m_xWindow
.is())
3509 m_xWindow
.set(new SalGtkXWindow(this, m_pWidget
));
3510 return css::uno::Reference
<css::awt::XWindow
>(m_xWindow
.get());
3513 virtual void set_busy_cursor(bool bBusy
) override
3515 set_cursor(m_pWidget
, bBusy
? "progress" : nullptr);
3518 virtual void set_modal(bool bModal
) override
3520 gtk_window_set_modal(m_pWindow
, bModal
);
3523 virtual bool get_modal() const override
3525 return gtk_window_get_modal(m_pWindow
);
3528 virtual void resize_to_request() override
3530 gtk_window_resize(m_pWindow
, 1, 1);
3533 virtual void window_move(int x
, int y
) override
3535 gtk_window_move(m_pWindow
, x
, y
);
3538 virtual SystemEnvData
get_system_data() const override
3540 assert(false && "nothing should call this impl, yet anyway, if ever");
3541 return SystemEnvData();
3544 virtual Size
get_size() const override
3546 int current_width
, current_height
;
3547 gtk_window_get_size(m_pWindow
, ¤t_width
, ¤t_height
);
3548 return Size(current_width
, current_height
);
3551 virtual Point
get_position() const override
3553 int current_x
, current_y
;
3554 gtk_window_get_position(m_pWindow
, ¤t_x
, ¤t_y
);
3555 return Point(current_x
, current_y
);
3558 virtual tools::Rectangle
get_monitor_workarea() const override
3560 GdkScreen
* pScreen
= gtk_widget_get_screen(GTK_WIDGET(m_pWindow
));
3561 gint nMonitor
= gdk_screen_get_monitor_at_window(pScreen
, gtk_widget_get_window(GTK_WIDGET(m_pWindow
)));
3563 gdk_screen_get_monitor_workarea(pScreen
, nMonitor
, &aRect
);
3564 return tools::Rectangle(aRect
.x
, aRect
.y
, aRect
.x
+ aRect
.width
, aRect
.y
+ aRect
.height
);
3567 virtual void set_centered_on_parent(bool bTrackGeometryRequests
) override
3569 if (bTrackGeometryRequests
)
3570 gtk_window_set_position(m_pWindow
, GTK_WIN_POS_CENTER_ALWAYS
);
3572 gtk_window_set_position(m_pWindow
, GTK_WIN_POS_CENTER_ON_PARENT
);
3575 virtual bool get_resizable() const override
3577 return gtk_window_get_resizable(m_pWindow
);
3580 virtual bool has_toplevel_focus() const override
3582 return gtk_window_has_toplevel_focus(m_pWindow
);
3585 virtual void present() override
3587 gtk_window_present(m_pWindow
);
3590 virtual void set_window_state(const OString
& rStr
) override
3592 WindowStateData aData
;
3593 ImplWindowStateFromStr( aData
, rStr
);
3595 auto nMask
= aData
.GetMask();
3596 auto nState
= aData
.GetState() & WindowStateState::SystemMask
;
3598 if (nMask
& WindowStateMask::Width
&& nMask
& WindowStateMask::Height
)
3600 gtk_window_set_default_size(m_pWindow
, aData
.GetWidth(), aData
.GetHeight());
3602 if (nMask
& WindowStateMask::State
)
3604 if (nState
& WindowStateState::Maximized
)
3605 gtk_window_maximize(m_pWindow
);
3607 gtk_window_unmaximize(m_pWindow
);
3611 virtual OString
get_window_state(WindowStateMask nMask
) const override
3613 bool bPositioningAllowed
= true;
3614 #if defined(GDK_WINDOWING_WAYLAND)
3615 // drop x/y when under wayland
3616 GdkDisplay
*pDisplay
= gtk_widget_get_display(m_pWidget
);
3617 bPositioningAllowed
= !DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay
);
3620 WindowStateData aData
;
3621 WindowStateMask nAvailable
= WindowStateMask::State
|
3622 WindowStateMask::Width
| WindowStateMask::Height
;
3623 if (bPositioningAllowed
)
3624 nAvailable
|= WindowStateMask::X
| WindowStateMask::Y
;
3625 aData
.SetMask(nMask
& nAvailable
);
3627 if (nMask
& WindowStateMask::State
)
3629 WindowStateState nState
= WindowStateState::Normal
;
3630 if (gtk_window_is_maximized(m_pWindow
))
3631 nState
|= WindowStateState::Maximized
;
3632 aData
.SetState(nState
);
3635 if (bPositioningAllowed
&& (nMask
& (WindowStateMask::X
| WindowStateMask::Y
)))
3637 auto aPos
= get_position();
3638 aData
.SetX(aPos
.X());
3639 aData
.SetY(aPos
.Y());
3642 if (nMask
& (WindowStateMask::Width
| WindowStateMask::Height
))
3644 auto aSize
= get_size();
3645 aData
.SetWidth(aSize
.Width());
3646 aData
.SetHeight(aSize
.Height());
3649 return aData
.ToStr();
3652 virtual void connect_toplevel_focus_changed(const Link
<weld::Widget
&, void>& rLink
) override
3654 assert(!m_nToplevelFocusChangedSignalId
);
3655 m_nToplevelFocusChangedSignalId
= g_signal_connect(m_pWindow
, "notify::has-toplevel-focus", G_CALLBACK(signalToplevelFocusChanged
), this);
3656 weld::Window::connect_toplevel_focus_changed(rLink
);
3659 virtual void disable_notify_events() override
3661 if (m_nToplevelFocusChangedSignalId
)
3662 g_signal_handler_block(m_pWidget
, m_nToplevelFocusChangedSignalId
);
3663 GtkInstanceContainer::disable_notify_events();
3666 virtual void enable_notify_events() override
3668 GtkInstanceContainer::enable_notify_events();
3669 if (m_nToplevelFocusChangedSignalId
)
3670 g_signal_handler_unblock(m_pWidget
, m_nToplevelFocusChangedSignalId
);
3673 virtual void draw(VirtualDevice
& rOutput
) override
3675 // detect if we have to manually setup its size
3676 bool bAlreadyRealized
= gtk_widget_get_realized(GTK_WIDGET(m_pWindow
));
3677 // has to be visible for draw to work
3678 bool bAlreadyVisible
= gtk_widget_get_visible(GTK_WIDGET(m_pWindow
));
3679 if (!bAlreadyVisible
)
3681 if (GTK_IS_DIALOG(m_pWindow
))
3682 sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(m_pWindow
))));
3683 gtk_widget_show(GTK_WIDGET(m_pWindow
));
3686 if (!bAlreadyRealized
)
3688 GtkAllocation allocation
;
3689 gtk_widget_realize(GTK_WIDGET(m_pWindow
));
3690 gtk_widget_get_allocation(GTK_WIDGET(m_pWindow
), &allocation
);
3691 gtk_widget_size_allocate(GTK_WIDGET(m_pWindow
), &allocation
);
3694 rOutput
.SetOutputSizePixel(get_size());
3695 cairo_surface_t
* pSurface
= get_underlying_cairo_surface(rOutput
);
3696 cairo_t
* cr
= cairo_create(pSurface
);
3698 Point aOffset
= get_csd_offset(GTK_WIDGET(m_pWindow
));
3700 #if defined(GDK_WINDOWING_X11)
3701 GdkDisplay
*pDisplay
= gtk_widget_get_display(GTK_WIDGET(m_pWindow
));
3702 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay
))
3703 assert(aOffset
.X() == 0 && aOffset
.Y() == 0 && "expected offset of 0 under X");
3706 cairo_translate(cr
, -aOffset
.X(), -aOffset
.Y());
3708 gtk_widget_draw(GTK_WIDGET(m_pWindow
), cr
);
3712 if (!bAlreadyVisible
)
3713 gtk_widget_hide(GTK_WIDGET(m_pWindow
));
3714 if (!bAlreadyRealized
)
3715 gtk_widget_unrealize(GTK_WIDGET(m_pWindow
));
3718 virtual weld::ScreenShotCollection
collect_screenshot_data() override
3720 weld::ScreenShotCollection aRet
;
3722 gtk_container_foreach(GTK_CONTAINER(m_pWindow
), do_collect_screenshot_data
, &aRet
);
3727 virtual ~GtkInstanceWindow() override
3729 if (m_nToplevelFocusChangedSignalId
)
3730 g_signal_handler_disconnect(m_pWindow
, m_nToplevelFocusChangedSignalId
);
3736 class GtkInstanceDialog
;
3740 GtkWindow
* m_pDialog
;
3741 GtkInstanceDialog
*m_pInstance
;
3744 VclPtr
<vcl::Window
> m_xFrameWindow
;
3747 DialogRunner(GtkWindow
* pDialog
, GtkInstanceDialog
* pInstance
)
3748 : m_pDialog(pDialog
)
3749 , m_pInstance(pInstance
)
3750 , m_nResponseId(GTK_RESPONSE_NONE
)
3754 GtkWindow
* pParent
= gtk_window_get_transient_for(m_pDialog
);
3755 GtkSalFrame
* pFrame
= pParent
? GtkSalFrame::getFromWindow(pParent
) : nullptr;
3756 m_xFrameWindow
= pFrame
? pFrame
->GetWindow() : nullptr;
3759 bool loop_is_running() const
3761 return m_pLoop
&& g_main_loop_is_running(m_pLoop
);
3766 if (g_main_loop_is_running(m_pLoop
))
3767 g_main_loop_quit(m_pLoop
);
3770 static void signal_response(GtkDialog
*, gint nResponseId
, gpointer data
);
3771 static void signal_cancel(GtkAssistant
*, gpointer data
);
3773 static gboolean
signal_delete(GtkDialog
* pDialog
, GdkEventAny
*, gpointer data
)
3775 DialogRunner
* pThis
= static_cast<DialogRunner
*>(data
);
3776 if (GTK_IS_ASSISTANT(pThis
->m_pDialog
))
3778 // An assistant isn't a dialog, but we want to treat it like one
3779 signal_response(pDialog
, GTK_RESPONSE_DELETE_EVENT
, data
);
3783 return true; /* Do not destroy */
3786 static void signal_destroy(GtkDialog
*, gpointer data
)
3788 DialogRunner
* pThis
= static_cast<DialogRunner
*>(data
);
3792 void inc_modal_count()
3796 m_xFrameWindow
->IncModalCount();
3797 if (m_nModalDepth
== 0)
3798 m_xFrameWindow
->ImplGetFrame()->NotifyModalHierarchy(true);
3803 void dec_modal_count()
3807 m_xFrameWindow
->DecModalCount();
3809 if (m_nModalDepth
== 0)
3810 m_xFrameWindow
->ImplGetFrame()->NotifyModalHierarchy(false);
3814 // same as gtk_dialog_run except that unmap doesn't auto-respond
3815 // so we can hide the dialog and restore it without a response getting
3819 g_object_ref(m_pDialog
);
3823 bool bWasModal
= gtk_window_get_modal(m_pDialog
);
3825 gtk_window_set_modal(m_pDialog
, true);
3827 if (!gtk_widget_get_visible(GTK_WIDGET(m_pDialog
)))
3828 gtk_widget_show(GTK_WIDGET(m_pDialog
));
3830 gulong nSignalResponseId
= GTK_IS_DIALOG(m_pDialog
) ? g_signal_connect(m_pDialog
, "response", G_CALLBACK(signal_response
), this) : 0;
3831 gulong nSignalCancelId
= GTK_IS_ASSISTANT(m_pDialog
) ? g_signal_connect(m_pDialog
, "cancel", G_CALLBACK(signal_cancel
), this) : 0;
3832 gulong nSignalDeleteId
= g_signal_connect(m_pDialog
, "delete-event", G_CALLBACK(signal_delete
), this);
3833 gulong nSignalDestroyId
= g_signal_connect(m_pDialog
, "destroy", G_CALLBACK(signal_destroy
), this);
3835 m_pLoop
= g_main_loop_new(nullptr, false);
3836 m_nResponseId
= GTK_RESPONSE_NONE
;
3838 gdk_threads_leave();
3839 g_main_loop_run(m_pLoop
);
3840 gdk_threads_enter();
3842 g_main_loop_unref(m_pLoop
);
3847 gtk_window_set_modal(m_pDialog
, false);
3849 if (nSignalResponseId
)
3850 g_signal_handler_disconnect(m_pDialog
, nSignalResponseId
);
3851 if (nSignalCancelId
)
3852 g_signal_handler_disconnect(m_pDialog
, nSignalCancelId
);
3853 g_signal_handler_disconnect(m_pDialog
, nSignalDeleteId
);
3854 g_signal_handler_disconnect(m_pDialog
, nSignalDestroyId
);
3858 g_object_unref(m_pDialog
);
3860 return m_nResponseId
;
3865 if (m_xFrameWindow
&& m_nModalDepth
)
3867 // if, like the calc validation dialog does, the modality was
3868 // toggled off during execution ensure that on cleanup the parent
3869 // is left in the state it was found
3870 while (m_nModalDepth
++ < 0)
3871 m_xFrameWindow
->IncModalCount();
3876 typedef std::set
<GtkWidget
*> winset
;
3880 void hideUnless(GtkContainer
*pTop
, const winset
& rVisibleWidgets
,
3881 std::vector
<GtkWidget
*> &rWasVisibleWidgets
)
3883 GList
* pChildren
= gtk_container_get_children(pTop
);
3884 for (GList
* pEntry
= g_list_first(pChildren
); pEntry
; pEntry
= g_list_next(pEntry
))
3886 GtkWidget
* pChild
= static_cast<GtkWidget
*>(pEntry
->data
);
3887 if (!gtk_widget_get_visible(pChild
))
3889 if (rVisibleWidgets
.find(pChild
) == rVisibleWidgets
.end())
3891 g_object_ref(pChild
);
3892 rWasVisibleWidgets
.emplace_back(pChild
);
3893 gtk_widget_hide(pChild
);
3895 else if (GTK_IS_CONTAINER(pChild
))
3897 hideUnless(GTK_CONTAINER(pChild
), rVisibleWidgets
, rWasVisibleWidgets
);
3900 g_list_free(pChildren
);
3904 class GtkInstanceButton
;
3906 class GtkInstanceDialog
: public GtkInstanceWindow
, public virtual weld::Dialog
3909 GtkWindow
* m_pDialog
;
3910 DialogRunner m_aDialogRun
;
3911 std::shared_ptr
<weld::DialogController
> m_xDialogController
;
3912 // Used to keep ourself alive during a runAsync(when doing runAsync without a DialogController)
3913 std::shared_ptr
<weld::Dialog
> m_xRunAsyncSelf
;
3914 std::function
<void(sal_Int32
)> m_aFunc
;
3915 gulong m_nCloseSignalId
;
3916 gulong m_nResponseSignalId
;
3917 gulong m_nCancelSignalId
;
3918 gulong m_nSignalDeleteId
;
3920 // for calc ref dialog that shrink to range selection widgets and resize back
3921 GtkWidget
* m_pRefEdit
;
3922 std::vector
<GtkWidget
*> m_aHiddenWidgets
; // vector of hidden Controls
3923 int m_nOldEditWidth
; // Original width of the input field
3924 int m_nOldEditWidthReq
; // Original width request of the input field
3925 int m_nOldBorderWidth
; // border width for expanded dialog
3932 static void signalClose(GtkWidget
*, gpointer widget
)
3934 GtkInstanceDialog
* pThis
= static_cast<GtkInstanceDialog
*>(widget
);
3935 pThis
->signal_close();
3938 static void signalAsyncResponse(GtkWidget
*, gint ret
, gpointer widget
)
3940 GtkInstanceDialog
* pThis
= static_cast<GtkInstanceDialog
*>(widget
);
3941 pThis
->asyncresponse(ret
);
3944 static void signalAsyncCancel(GtkAssistant
*, gpointer widget
)
3946 GtkInstanceDialog
* pThis
= static_cast<GtkInstanceDialog
*>(widget
);
3947 // make esc in an assistant act as if cancel button was pressed
3948 pThis
->close(false);
3951 static gboolean
signalAsyncDelete(GtkWidget
* pDialog
, GdkEventAny
*, gpointer widget
)
3953 GtkInstanceDialog
* pThis
= static_cast<GtkInstanceDialog
*>(widget
);
3954 if (GTK_IS_ASSISTANT(pThis
->m_pDialog
))
3956 // An assistant isn't a dialog, but we want to treat it like one
3957 signalAsyncResponse(pDialog
, GTK_RESPONSE_DELETE_EVENT
, widget
);
3959 return true; /* Do not destroy */
3962 static int GtkToVcl(int ret
)
3964 if (ret
== GTK_RESPONSE_OK
)
3966 else if (ret
== GTK_RESPONSE_CANCEL
)
3968 else if (ret
== GTK_RESPONSE_DELETE_EVENT
)
3970 else if (ret
== GTK_RESPONSE_CLOSE
)
3972 else if (ret
== GTK_RESPONSE_YES
)
3974 else if (ret
== GTK_RESPONSE_NO
)
3976 else if (ret
== GTK_RESPONSE_HELP
)
3981 static int VclToGtk(int nResponse
)
3983 if (nResponse
== RET_OK
)
3984 return GTK_RESPONSE_OK
;
3985 else if (nResponse
== RET_CANCEL
)
3986 return GTK_RESPONSE_CANCEL
;
3987 else if (nResponse
== RET_CLOSE
)
3988 return GTK_RESPONSE_CLOSE
;
3989 else if (nResponse
== RET_YES
)
3990 return GTK_RESPONSE_YES
;
3991 else if (nResponse
== RET_NO
)
3992 return GTK_RESPONSE_NO
;
3993 else if (nResponse
== RET_HELP
)
3994 return GTK_RESPONSE_HELP
;
3998 void asyncresponse(gint ret
);
4000 static void signalActivate(GtkMenuItem
*, gpointer data
)
4002 bool* pActivate
= static_cast<bool*>(data
);
4006 bool signal_screenshot_popup_menu(GdkEventButton
* pEvent
)
4008 GtkWidget
*pMenu
= gtk_menu_new();
4010 GtkWidget
* pMenuItem
= gtk_menu_item_new_with_mnemonic(MapToGtkAccelerator(VclResId(SV_BUTTONTEXT_SCREENSHOT
)).getStr());
4011 gtk_menu_shell_append(GTK_MENU_SHELL(pMenu
), pMenuItem
);
4012 bool bActivate(false);
4013 g_signal_connect(pMenuItem
, "activate", G_CALLBACK(signalActivate
), &bActivate
);
4014 gtk_widget_show(pMenuItem
);
4016 int button
, event_time
;
4019 button
= pEvent
->button
;
4020 event_time
= pEvent
->time
;
4025 event_time
= gtk_get_current_event_time();
4028 gtk_menu_attach_to_widget(GTK_MENU(pMenu
), GTK_WIDGET(m_pDialog
), nullptr);
4030 GMainLoop
* pLoop
= g_main_loop_new(nullptr, true);
4031 gulong nSignalId
= g_signal_connect_swapped(G_OBJECT(pMenu
), "deactivate", G_CALLBACK(g_main_loop_quit
), pLoop
);
4033 gtk_menu_popup(GTK_MENU(pMenu
), nullptr, nullptr, nullptr, nullptr, button
, event_time
);
4035 if (g_main_loop_is_running(pLoop
))
4037 gdk_threads_leave();
4038 g_main_loop_run(pLoop
);
4039 gdk_threads_enter();
4042 g_main_loop_unref(pLoop
);
4043 g_signal_handler_disconnect(pMenu
, nSignalId
);
4044 gtk_menu_detach(GTK_MENU(pMenu
));
4048 // open screenshot annotation dialog
4049 VclAbstractDialogFactory
* pFact
= VclAbstractDialogFactory::Create();
4050 VclPtr
<AbstractScreenshotAnnotationDlg
> xTmp
= pFact
->CreateScreenshotAnnotationDlg(*this);
4051 ScopedVclPtr
<AbstractScreenshotAnnotationDlg
> xDialog(xTmp
);
4058 static gboolean
signalScreenshotPopupMenu(GtkWidget
*, gpointer widget
)
4060 GtkInstanceDialog
* pThis
= static_cast<GtkInstanceDialog
*>(widget
);
4061 return pThis
->signal_screenshot_popup_menu(nullptr);
4064 static gboolean
signalScreenshotButton(GtkWidget
*, GdkEventButton
* pEvent
, gpointer widget
)
4066 GtkInstanceDialog
* pThis
= static_cast<GtkInstanceDialog
*>(widget
);
4067 SolarMutexGuard aGuard
;
4068 return pThis
->signal_screenshot_button(pEvent
);
4071 bool signal_screenshot_button(GdkEventButton
* pEvent
)
4073 if (gdk_event_triggers_context_menu(reinterpret_cast<GdkEvent
*>(pEvent
)) && pEvent
->type
== GDK_BUTTON_PRESS
)
4075 //if handled for context menu, stop processing
4076 return signal_screenshot_popup_menu(pEvent
);
4082 GtkInstanceDialog(GtkWindow
* pDialog
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
4083 : GtkInstanceWindow(pDialog
, pBuilder
, bTakeOwnership
)
4084 , m_pDialog(pDialog
)
4085 , m_aDialogRun(pDialog
, this)
4086 , m_nResponseSignalId(0)
4087 , m_nCancelSignalId(0)
4088 , m_nSignalDeleteId(0)
4089 , m_pRefEdit(nullptr)
4090 , m_nOldEditWidth(0)
4091 , m_nOldEditWidthReq(0)
4092 , m_nOldBorderWidth(0)
4094 if (GTK_IS_DIALOG(m_pDialog
) || GTK_IS_ASSISTANT(m_pDialog
))
4095 m_nCloseSignalId
= g_signal_connect(m_pDialog
, "close", G_CALLBACK(signalClose
), this);
4097 m_nCloseSignalId
= 0;
4098 const bool bScreenshotMode(officecfg::Office::Common::Misc::ScreenshotMode::get());
4099 if (bScreenshotMode
)
4101 g_signal_connect(m_pDialog
, "popup-menu", G_CALLBACK(signalScreenshotPopupMenu
), this);
4102 g_signal_connect(m_pDialog
, "button-press-event", G_CALLBACK(signalScreenshotButton
), this);
4106 virtual bool runAsync(std::shared_ptr
<weld::DialogController
> rDialogController
, const std::function
<void(sal_Int32
)>& func
) override
4108 assert(!m_nResponseSignalId
&& !m_nCancelSignalId
&& !m_nSignalDeleteId
);
4110 m_xDialogController
= rDialogController
;
4114 m_aDialogRun
.inc_modal_count();
4117 m_nResponseSignalId
= GTK_IS_DIALOG(m_pDialog
) ? g_signal_connect(m_pDialog
, "response", G_CALLBACK(signalAsyncResponse
), this) : 0;
4118 m_nCancelSignalId
= GTK_IS_ASSISTANT(m_pDialog
) ? g_signal_connect(m_pDialog
, "cancel", G_CALLBACK(signalAsyncCancel
), this) : 0;
4119 m_nSignalDeleteId
= g_signal_connect(m_pDialog
, "delete-event", G_CALLBACK(signalAsyncDelete
), this);
4124 virtual bool runAsync(std::shared_ptr
<Dialog
> const & rxSelf
, const std::function
<void(sal_Int32
)>& func
) override
4126 assert( rxSelf
.get() == this );
4127 assert(!m_nResponseSignalId
&& !m_nCancelSignalId
&& !m_nSignalDeleteId
);
4129 // In order to store a shared_ptr to ourself, we have to have been constructed by make_shared,
4130 // which is that rxSelf enforces.
4131 m_xRunAsyncSelf
= rxSelf
;
4135 m_aDialogRun
.inc_modal_count();
4138 m_nResponseSignalId
= GTK_IS_DIALOG(m_pDialog
) ? g_signal_connect(m_pDialog
, "response", G_CALLBACK(signalAsyncResponse
), this) : 0;
4139 m_nCancelSignalId
= GTK_IS_ASSISTANT(m_pDialog
) ? g_signal_connect(m_pDialog
, "cancel", G_CALLBACK(signalAsyncCancel
), this) : 0;
4140 m_nSignalDeleteId
= g_signal_connect(m_pDialog
, "delete-event", G_CALLBACK(signalAsyncDelete
), this);
4145 GtkInstanceButton
* has_click_handler(int nResponse
);
4147 virtual int run() override
;
4149 virtual void show() override
4151 if (gtk_widget_get_visible(m_pWidget
))
4153 if (GTK_IS_DIALOG(m_pDialog
))
4154 sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog
))));
4155 GtkInstanceWindow::show();
4158 virtual void set_modal(bool bModal
) override
4160 if (get_modal() == bModal
)
4162 GtkInstanceWindow::set_modal(bModal
);
4163 /* if change the dialog modality while its running, then also change the parent LibreOffice window
4164 modal count, we typically expect the dialog modality to be restored to its original state
4166 This change modality while running case is for...
4168 a) the calc/chart dialogs which put up an extra range chooser
4169 dialog, hides the original, the user can select a range of cells and
4170 on completion the original dialog is restored
4172 b) the validity dialog in calc
4174 if (m_aDialogRun
.loop_is_running())
4177 m_aDialogRun
.inc_modal_count();
4179 m_aDialogRun
.dec_modal_count();
4183 virtual void response(int nResponse
) override
;
4185 virtual void add_button(const OUString
& rText
, int nResponse
, const OString
& rHelpId
) override
4187 GtkWidget
* pWidget
= gtk_dialog_add_button(GTK_DIALOG(m_pDialog
), MapToGtkAccelerator(rText
).getStr(), VclToGtk(nResponse
));
4188 if (!rHelpId
.isEmpty())
4189 ::set_help_id(pWidget
, rHelpId
);
4192 virtual void set_default_response(int nResponse
) override
4194 gtk_dialog_set_default_response(GTK_DIALOG(m_pDialog
), VclToGtk(nResponse
));
4197 virtual GtkButton
* get_widget_for_response(int nGtkResponse
)
4199 return GTK_BUTTON(gtk_dialog_get_widget_for_response(GTK_DIALOG(m_pDialog
), nGtkResponse
));
4202 virtual weld::Button
* weld_widget_for_response(int nVclResponse
) override
;
4204 virtual Container
* weld_content_area() override
4206 return new GtkInstanceContainer(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(m_pDialog
))), m_pBuilder
, false);
4209 virtual void collapse(weld::Widget
* pEdit
, weld::Widget
* pButton
) override
4211 GtkInstanceWidget
* pVclEdit
= dynamic_cast<GtkInstanceWidget
*>(pEdit
);
4213 GtkInstanceWidget
* pVclButton
= dynamic_cast<GtkInstanceWidget
*>(pButton
);
4215 GtkWidget
* pRefEdit
= pVclEdit
->getWidget();
4216 GtkWidget
* pRefBtn
= pVclButton
? pVclButton
->getWidget() : nullptr;
4218 m_nOldEditWidth
= gtk_widget_get_allocated_width(pRefEdit
);
4220 gtk_widget_get_size_request(pRefEdit
, &m_nOldEditWidthReq
, nullptr);
4222 //We want just pRefBtn and pRefEdit to be shown
4223 //mark widgets we want to be visible, starting with pRefEdit
4224 //and all its direct parents.
4225 winset aVisibleWidgets
;
4226 GtkWidget
*pContentArea
= gtk_dialog_get_content_area(GTK_DIALOG(m_pDialog
));
4227 for (GtkWidget
*pCandidate
= pRefEdit
;
4228 pCandidate
&& pCandidate
!= pContentArea
&& gtk_widget_get_visible(pCandidate
);
4229 pCandidate
= gtk_widget_get_parent(pCandidate
))
4231 aVisibleWidgets
.insert(pCandidate
);
4233 //same again with pRefBtn, except stop if there's a
4234 //shared parent in the existing widgets
4235 for (GtkWidget
*pCandidate
= pRefBtn
;
4236 pCandidate
&& pCandidate
!= pContentArea
&& gtk_widget_get_visible(pCandidate
);
4237 pCandidate
= gtk_widget_get_parent(pCandidate
))
4239 if (aVisibleWidgets
.insert(pCandidate
).second
)
4243 //hide everything except the aVisibleWidgets
4244 hideUnless(GTK_CONTAINER(pContentArea
), aVisibleWidgets
, m_aHiddenWidgets
);
4246 gtk_widget_set_size_request(pRefEdit
, m_nOldEditWidth
, -1);
4247 m_nOldBorderWidth
= gtk_container_get_border_width(GTK_CONTAINER(m_pDialog
));
4248 gtk_container_set_border_width(GTK_CONTAINER(m_pDialog
), 0);
4249 if (GtkWidget
* pActionArea
= gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog
)))
4250 gtk_widget_hide(pActionArea
);
4252 // calc's insert->function is springing back to its original size if the ref-button
4253 // is used to shrink the dialog down and then the user clicks in the calc area to do
4255 #if defined(GDK_WINDOWING_WAYLAND)
4256 bool bWorkaroundSizeSpringingBack
= DLSYM_GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(m_pWidget
));
4257 if (bWorkaroundSizeSpringingBack
)
4258 gtk_widget_unmap(GTK_WIDGET(m_pDialog
));
4261 resize_to_request();
4263 #if defined(GDK_WINDOWING_WAYLAND)
4264 if (bWorkaroundSizeSpringingBack
)
4265 gtk_widget_map(GTK_WIDGET(m_pDialog
));
4268 m_pRefEdit
= pRefEdit
;
4271 virtual void undo_collapse() override
4273 // All others: Show();
4274 for (GtkWidget
* pWindow
: m_aHiddenWidgets
)
4276 gtk_widget_show(pWindow
);
4277 g_object_unref(pWindow
);
4279 m_aHiddenWidgets
.clear();
4281 gtk_widget_set_size_request(m_pRefEdit
, m_nOldEditWidthReq
, -1);
4282 m_pRefEdit
= nullptr;
4283 gtk_container_set_border_width(GTK_CONTAINER(m_pDialog
), m_nOldBorderWidth
);
4284 if (GtkWidget
* pActionArea
= gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog
)))
4285 gtk_widget_show(pActionArea
);
4286 resize_to_request();
4290 void close(bool bCloseSignal
);
4292 virtual void SetInstallLOKNotifierHdl(const Link
<void*, vcl::ILibreOfficeKitNotifier
*>&) override
4294 //not implemented for the gtk variant
4297 virtual ~GtkInstanceDialog() override
4299 if (!m_aHiddenWidgets
.empty())
4301 for (GtkWidget
* pWindow
: m_aHiddenWidgets
)
4302 g_object_unref(pWindow
);
4303 m_aHiddenWidgets
.clear();
4306 if (m_nCloseSignalId
)
4307 g_signal_handler_disconnect(m_pDialog
, m_nCloseSignalId
);
4308 assert(!m_nResponseSignalId
&& !m_nCancelSignalId
&& !m_nSignalDeleteId
);
4312 void DialogRunner::signal_response(GtkDialog
*, gint nResponseId
, gpointer data
)
4314 DialogRunner
* pThis
= static_cast<DialogRunner
*>(data
);
4316 // make GTK_RESPONSE_DELETE_EVENT act as if cancel button was pressed
4317 if (nResponseId
== GTK_RESPONSE_DELETE_EVENT
)
4319 pThis
->m_pInstance
->close(false);
4323 pThis
->m_nResponseId
= nResponseId
;
4327 void DialogRunner::signal_cancel(GtkAssistant
*, gpointer data
)
4329 DialogRunner
* pThis
= static_cast<DialogRunner
*>(data
);
4331 // make esc in an assistant act as if cancel button was pressed
4332 pThis
->m_pInstance
->close(false);
4335 class GtkInstanceMessageDialog
: public GtkInstanceDialog
, public virtual weld::MessageDialog
4338 GtkMessageDialog
* m_pMessageDialog
;
4340 GtkInstanceMessageDialog(GtkMessageDialog
* pMessageDialog
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
4341 : GtkInstanceDialog(GTK_WINDOW(pMessageDialog
), pBuilder
, bTakeOwnership
)
4342 , m_pMessageDialog(pMessageDialog
)
4346 virtual void set_primary_text(const OUString
& rText
) override
4348 ::set_primary_text(m_pMessageDialog
, rText
);
4351 virtual OUString
get_primary_text() const override
4353 return ::get_primary_text(m_pMessageDialog
);
4356 virtual void set_secondary_text(const OUString
& rText
) override
4358 ::set_secondary_text(m_pMessageDialog
, rText
);
4361 virtual OUString
get_secondary_text() const override
4363 return ::get_secondary_text(m_pMessageDialog
);
4366 virtual Container
* weld_message_area() override
4368 return new GtkInstanceContainer(GTK_CONTAINER(gtk_message_dialog_get_message_area(m_pMessageDialog
)), m_pBuilder
, false);
4372 class GtkInstanceAboutDialog final
: public GtkInstanceDialog
, public virtual weld::AboutDialog
4375 GtkAboutDialog
* m_pAboutDialog
;
4376 GtkCssProvider
* m_pCssProvider
;
4377 std::unique_ptr
<utl::TempFile
> mxBackgroundImage
;
4379 GtkInstanceAboutDialog(GtkAboutDialog
* pAboutDialog
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
4380 : GtkInstanceDialog(GTK_WINDOW(pAboutDialog
), pBuilder
, bTakeOwnership
)
4381 , m_pAboutDialog(pAboutDialog
)
4382 , m_pCssProvider(nullptr)
4384 // in GtkAboutDialog apply_use_header_bar if headerbar is false it
4385 // automatically adds a default close button which it doesn't if
4386 // headerbar is true and which doesn't appear in the .ui
4387 if (GtkWidget
* pDefaultButton
= gtk_dialog_get_widget_for_response(GTK_DIALOG(pAboutDialog
), GTK_RESPONSE_DELETE_EVENT
))
4388 gtk_widget_destroy(pDefaultButton
);
4391 virtual void set_version(const OUString
& rVersion
) override
4393 gtk_about_dialog_set_version(m_pAboutDialog
, OUStringToOString(rVersion
, RTL_TEXTENCODING_UTF8
).getStr());
4396 virtual void set_copyright(const OUString
& rCopyright
) override
4398 gtk_about_dialog_set_copyright(m_pAboutDialog
, OUStringToOString(rCopyright
, RTL_TEXTENCODING_UTF8
).getStr());
4401 virtual void set_website(const OUString
& rURL
) override
4403 OString
sURL(OUStringToOString(rURL
, RTL_TEXTENCODING_UTF8
));
4404 gtk_about_dialog_set_website(m_pAboutDialog
, sURL
.isEmpty() ? nullptr : sURL
.getStr());
4407 virtual void set_website_label(const OUString
& rLabel
) override
4409 OString
sLabel(OUStringToOString(rLabel
, RTL_TEXTENCODING_UTF8
));
4410 gtk_about_dialog_set_website_label(m_pAboutDialog
, sLabel
.isEmpty() ? nullptr : sLabel
.getStr());
4413 virtual OUString
get_website_label() const override
4415 const gchar
* pText
= gtk_about_dialog_get_website_label(m_pAboutDialog
);
4416 return OUString(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
4419 virtual void set_logo(const css::uno::Reference
<css::graphic::XGraphic
>& rImage
) override
4421 GdkPixbuf
* pixbuf
= rImage
.is() ? getPixbuf(rImage
) : nullptr;
4423 gtk_about_dialog_set_logo(m_pAboutDialog
, nullptr);
4426 gtk_about_dialog_set_logo(m_pAboutDialog
, pixbuf
);
4427 g_object_unref(pixbuf
);
4431 virtual void set_background(const css::uno::Reference
<css::graphic::XGraphic
>& rImage
) override
4433 GtkStyleContext
*pStyleContext
= gtk_widget_get_style_context(GTK_WIDGET(m_pAboutDialog
));
4436 gtk_style_context_remove_provider(pStyleContext
, GTK_STYLE_PROVIDER(m_pCssProvider
));
4437 m_pCssProvider
= nullptr;
4440 mxBackgroundImage
.reset();
4444 mxBackgroundImage
.reset(new utl::TempFile());
4445 mxBackgroundImage
->EnableKillingFile(true);
4447 Image
aImage(rImage
);
4449 vcl::PNGWriter
aPNGWriter(aImage
.GetBitmapEx());
4450 SvStream
* pStream
= mxBackgroundImage
->GetStream(StreamMode::WRITE
);
4451 aPNGWriter
.Write(*pStream
);
4452 mxBackgroundImage
->CloseStream();
4454 m_pCssProvider
= gtk_css_provider_new();
4455 OUString aBuffer
= "* { background-image: url(\"" + mxBackgroundImage
->GetURL() + "\"); }";
4456 OString aResult
= OUStringToOString(aBuffer
, RTL_TEXTENCODING_UTF8
);
4457 gtk_css_provider_load_from_data(m_pCssProvider
, aResult
.getStr(), aResult
.getLength(), nullptr);
4458 gtk_style_context_add_provider(pStyleContext
, GTK_STYLE_PROVIDER(m_pCssProvider
),
4459 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
4463 virtual ~GtkInstanceAboutDialog() override
4465 set_background(nullptr);
4469 class GtkInstanceAssistant
: public GtkInstanceDialog
, public virtual weld::Assistant
4472 GtkAssistant
* m_pAssistant
;
4473 GtkWidget
* m_pSidebar
;
4474 GtkWidget
* m_pSidebarEventBox
;
4475 GtkButtonBox
* m_pButtonBox
;
4479 GtkButton
* m_pFinish
;
4480 GtkButton
* m_pCancel
;
4481 gulong m_nButtonPressSignalId
;
4482 std::vector
<std::unique_ptr
<GtkInstanceContainer
>> m_aPages
;
4483 std::map
<OString
, bool> m_aNotClickable
;
4485 int find_page(const OString
& rIdent
) const
4487 int nPages
= gtk_assistant_get_n_pages(m_pAssistant
);
4488 for (int i
= 0; i
< nPages
; ++i
)
4490 GtkWidget
* pPage
= gtk_assistant_get_nth_page(m_pAssistant
, i
);
4491 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pPage
));
4492 if (g_strcmp0(pStr
, rIdent
.getStr()) == 0)
4498 static void wrap_sidebar_label(GtkWidget
*pWidget
, gpointer
/*user_data*/)
4500 if (GTK_IS_LABEL(pWidget
))
4502 gtk_label_set_line_wrap(GTK_LABEL(pWidget
), true);
4503 gtk_label_set_width_chars(GTK_LABEL(pWidget
), 22);
4504 gtk_label_set_max_width_chars(GTK_LABEL(pWidget
), 22);
4508 static void find_sidebar(GtkWidget
*pWidget
, gpointer user_data
)
4510 if (g_strcmp0(gtk_buildable_get_name(GTK_BUILDABLE(pWidget
)), "sidebar") == 0)
4512 GtkWidget
**ppSidebar
= static_cast<GtkWidget
**>(user_data
);
4513 *ppSidebar
= pWidget
;
4515 if (GTK_IS_CONTAINER(pWidget
))
4516 gtk_container_forall(GTK_CONTAINER(pWidget
), find_sidebar
, user_data
);
4519 static void signalHelpClicked(GtkButton
*, gpointer widget
)
4521 GtkInstanceAssistant
* pThis
= static_cast<GtkInstanceAssistant
*>(widget
);
4522 pThis
->signal_help_clicked();
4525 void signal_help_clicked()
4530 static gboolean
signalButton(GtkWidget
*, GdkEventButton
* pEvent
, gpointer widget
)
4532 GtkInstanceAssistant
* pThis
= static_cast<GtkInstanceAssistant
*>(widget
);
4533 SolarMutexGuard aGuard
;
4534 return pThis
->signal_button(pEvent
);
4537 bool signal_button(GdkEventButton
* pEvent
)
4539 int nNewCurrentPage
= -1;
4541 GtkAllocation allocation
;
4544 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(m_pSidebar
));
4545 for (GList
* pChild
= g_list_first(pChildren
); pChild
; pChild
= g_list_next(pChild
))
4547 GtkWidget
* pWidget
= static_cast<GtkWidget
*>(pChild
->data
);
4548 if (!gtk_widget_get_visible(pWidget
))
4551 gtk_widget_get_allocation(pWidget
, &allocation
);
4553 gint dest_x1
, dest_y1
;
4554 gtk_widget_translate_coordinates(pWidget
,
4561 gint dest_x2
, dest_y2
;
4562 gtk_widget_translate_coordinates(pWidget
,
4570 if (pEvent
->x
>= dest_x1
&& pEvent
->x
<= dest_x2
&& pEvent
->y
>= dest_y1
&& pEvent
->y
<= dest_y2
)
4572 nNewCurrentPage
= nPageIndex
;
4578 g_list_free(pChildren
);
4580 if (nNewCurrentPage
!= -1 && nNewCurrentPage
!= get_current_page())
4582 OString sIdent
= get_page_ident(nNewCurrentPage
);
4583 if (!m_aNotClickable
[sIdent
] && !signal_jump_page(sIdent
))
4584 set_current_page(nNewCurrentPage
);
4591 GtkInstanceAssistant(GtkAssistant
* pAssistant
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
4592 : GtkInstanceDialog(GTK_WINDOW(pAssistant
), pBuilder
, bTakeOwnership
)
4593 , m_pAssistant(pAssistant
)
4594 , m_pSidebar(nullptr)
4596 m_pButtonBox
= GTK_BUTTON_BOX(gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
));
4597 gtk_button_box_set_layout(m_pButtonBox
, GTK_BUTTONBOX_END
);
4598 gtk_box_set_spacing(GTK_BOX(m_pButtonBox
), 6);
4600 m_pBack
= GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(GetStandardText(StandardButtonType::Back
)).getStr()));
4601 gtk_widget_set_can_default(GTK_WIDGET(m_pBack
), true);
4602 gtk_buildable_set_name(GTK_BUILDABLE(m_pBack
), "previous");
4603 gtk_box_pack_end(GTK_BOX(m_pButtonBox
), GTK_WIDGET(m_pBack
), false, false, 0);
4605 m_pNext
= GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(GetStandardText(StandardButtonType::Next
)).getStr()));
4606 gtk_widget_set_can_default(GTK_WIDGET(m_pNext
), true);
4607 gtk_buildable_set_name(GTK_BUILDABLE(m_pNext
), "next");
4608 gtk_box_pack_end(GTK_BOX(m_pButtonBox
), GTK_WIDGET(m_pNext
), false, false, 0);
4610 m_pCancel
= GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(GetStandardText(StandardButtonType::Cancel
)).getStr()));
4611 gtk_widget_set_can_default(GTK_WIDGET(m_pCancel
), true);
4612 gtk_box_pack_end(GTK_BOX(m_pButtonBox
), GTK_WIDGET(m_pCancel
), false, false, 0);
4614 m_pFinish
= GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(GetStandardText(StandardButtonType::Finish
)).getStr()));
4615 gtk_widget_set_can_default(GTK_WIDGET(m_pFinish
), true);
4616 gtk_buildable_set_name(GTK_BUILDABLE(m_pFinish
), "finish");
4617 gtk_box_pack_end(GTK_BOX(m_pButtonBox
), GTK_WIDGET(m_pFinish
), false, false, 0);
4619 m_pHelp
= GTK_BUTTON(gtk_button_new_with_mnemonic(MapToGtkAccelerator(GetStandardText(StandardButtonType::Help
)).getStr()));
4620 gtk_widget_set_can_default(GTK_WIDGET(m_pHelp
), true);
4621 g_signal_connect(m_pHelp
, "clicked", G_CALLBACK(signalHelpClicked
), this);
4622 gtk_box_pack_end(GTK_BOX(m_pButtonBox
), GTK_WIDGET(m_pHelp
), false, false, 0);
4624 gtk_assistant_add_action_widget(pAssistant
, GTK_WIDGET(m_pButtonBox
));
4625 gtk_button_box_set_child_secondary(m_pButtonBox
, GTK_WIDGET(m_pHelp
), true);
4626 gtk_widget_set_hexpand(GTK_WIDGET(m_pButtonBox
), true);
4628 GtkWidget
* pParent
= gtk_widget_get_parent(GTK_WIDGET(m_pButtonBox
));
4629 gtk_container_child_set(GTK_CONTAINER(pParent
), GTK_WIDGET(m_pButtonBox
), "expand", true, "fill", true, nullptr);
4630 gtk_widget_set_halign(pParent
, GTK_ALIGN_FILL
);
4632 // Hide the built-in ones early so we get a nice optimal size for the width without
4633 // including the unused contents
4634 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(pParent
));
4635 for (GList
* pChild
= g_list_first(pChildren
); pChild
; pChild
= g_list_next(pChild
))
4637 GtkWidget
* pWidget
= static_cast<GtkWidget
*>(pChild
->data
);
4638 gtk_widget_hide(pWidget
);
4640 g_list_free(pChildren
);
4642 gtk_widget_show_all(GTK_WIDGET(m_pButtonBox
));
4644 find_sidebar(GTK_WIDGET(m_pAssistant
), &m_pSidebar
);
4646 m_pSidebarEventBox
= ::ensureEventWidget(m_pSidebar
);
4647 m_nButtonPressSignalId
= m_pSidebarEventBox
? g_signal_connect(m_pSidebarEventBox
, "button-press-event", G_CALLBACK(signalButton
), this) : 0;
4650 virtual int get_current_page() const override
4652 return gtk_assistant_get_current_page(m_pAssistant
);
4655 virtual int get_n_pages() const override
4657 return gtk_assistant_get_n_pages(m_pAssistant
);
4660 virtual OString
get_page_ident(int nPage
) const override
4662 const GtkWidget
* pWidget
= gtk_assistant_get_nth_page(m_pAssistant
, nPage
);
4663 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pWidget
));
4664 return OString(pStr
, pStr
? strlen(pStr
) : 0);
4667 virtual OString
get_current_page_ident() const override
4669 return get_page_ident(get_current_page());
4672 virtual void set_current_page(int nPage
) override
4674 OString
sDialogTitle(gtk_window_get_title(GTK_WINDOW(m_pAssistant
)));
4676 gtk_assistant_set_current_page(m_pAssistant
, nPage
);
4678 // if the page doesn't have a title, then the dialog will now have no
4679 // title, so restore the original title as a fallback
4680 GtkWidget
* pPage
= gtk_assistant_get_nth_page(m_pAssistant
, nPage
);
4681 if (!gtk_assistant_get_page_title(m_pAssistant
, pPage
))
4682 gtk_window_set_title(GTK_WINDOW(m_pAssistant
), sDialogTitle
.getStr());
4685 virtual void set_current_page(const OString
& rIdent
) override
4687 int nPage
= find_page(rIdent
);
4690 set_current_page(nPage
);
4693 virtual void set_page_title(const OString
& rIdent
, const OUString
& rTitle
) override
4695 int nIndex
= find_page(rIdent
);
4698 GtkWidget
* pPage
= gtk_assistant_get_nth_page(m_pAssistant
, nIndex
);
4699 gtk_assistant_set_page_title(m_pAssistant
, pPage
,
4700 OUStringToOString(rTitle
, RTL_TEXTENCODING_UTF8
).getStr());
4701 gtk_container_forall(GTK_CONTAINER(m_pSidebar
), wrap_sidebar_label
, nullptr);
4704 virtual OUString
get_page_title(const OString
& rIdent
) const override
4706 int nIndex
= find_page(rIdent
);
4709 GtkWidget
* pPage
= gtk_assistant_get_nth_page(m_pAssistant
, nIndex
);
4710 const gchar
* pStr
= gtk_assistant_get_page_title(m_pAssistant
, pPage
);
4711 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
4714 virtual void set_page_sensitive(const OString
& rIdent
, bool bSensitive
) override
4716 m_aNotClickable
[rIdent
] = !bSensitive
;
4719 virtual void set_page_index(const OString
& rIdent
, int nNewIndex
) override
4721 int nOldIndex
= find_page(rIdent
);
4722 if (nOldIndex
== -1)
4725 if (nOldIndex
== nNewIndex
)
4728 GtkWidget
* pPage
= gtk_assistant_get_nth_page(m_pAssistant
, nOldIndex
);
4730 g_object_ref(pPage
);
4731 OString
sTitle(gtk_assistant_get_page_title(m_pAssistant
, pPage
));
4732 gtk_assistant_remove_page(m_pAssistant
, nOldIndex
);
4733 gtk_assistant_insert_page(m_pAssistant
, pPage
, nNewIndex
);
4734 gtk_assistant_set_page_type(m_pAssistant
, pPage
, GTK_ASSISTANT_PAGE_CUSTOM
);
4735 gtk_assistant_set_page_title(m_pAssistant
, pPage
, sTitle
.getStr());
4736 gtk_container_forall(GTK_CONTAINER(m_pSidebar
), wrap_sidebar_label
, nullptr);
4737 g_object_unref(pPage
);
4740 virtual weld::Container
* append_page(const OString
& rIdent
) override
4742 disable_notify_events();
4744 GtkWidget
*pChild
= gtk_grid_new();
4745 gtk_buildable_set_name(GTK_BUILDABLE(pChild
), rIdent
.getStr());
4746 gtk_assistant_append_page(m_pAssistant
, pChild
);
4747 gtk_assistant_set_page_type(m_pAssistant
, pChild
, GTK_ASSISTANT_PAGE_CUSTOM
);
4748 gtk_widget_show(pChild
);
4750 enable_notify_events();
4752 m_aPages
.emplace_back(new GtkInstanceContainer(GTK_CONTAINER(pChild
), m_pBuilder
, false));
4754 return m_aPages
.back().get();
4757 virtual void set_page_side_help_id(const OString
& rHelpId
) override
4761 ::set_help_id(m_pSidebar
, rHelpId
);
4764 virtual GtkButton
* get_widget_for_response(int nGtkResponse
) override
4766 GtkButton
* pButton
= nullptr;
4767 if (nGtkResponse
== GTK_RESPONSE_YES
)
4769 else if (nGtkResponse
== GTK_RESPONSE_NO
)
4771 else if (nGtkResponse
== GTK_RESPONSE_OK
)
4772 pButton
= m_pFinish
;
4773 else if (nGtkResponse
== GTK_RESPONSE_CANCEL
)
4774 pButton
= m_pCancel
;
4775 else if (nGtkResponse
== GTK_RESPONSE_HELP
)
4780 virtual ~GtkInstanceAssistant() override
4782 if (m_nButtonPressSignalId
)
4783 g_signal_handler_disconnect(m_pSidebarEventBox
, m_nButtonPressSignalId
);
4787 class GtkInstanceFrame
: public GtkInstanceContainer
, public virtual weld::Frame
4792 GtkInstanceFrame(GtkFrame
* pFrame
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
4793 : GtkInstanceContainer(GTK_CONTAINER(pFrame
), pBuilder
, bTakeOwnership
)
4798 virtual void set_label(const OUString
& rText
) override
4800 gtk_label_set_label(GTK_LABEL(gtk_frame_get_label_widget(m_pFrame
)), rText
.replaceFirst("~", "").toUtf8().getStr());
4803 virtual OUString
get_label() const override
4805 const gchar
* pStr
= gtk_frame_get_label(m_pFrame
);
4806 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
4809 virtual std::unique_ptr
<weld::Label
> weld_label_widget() const override
;
4812 static GType
crippled_viewport_get_type();
4814 #define CRIPPLED_TYPE_VIEWPORT (crippled_viewport_get_type ())
4815 #define CRIPPLED_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CRIPPLED_TYPE_VIEWPORT, CrippledViewport))
4817 # define CRIPPLED_IS_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CRIPPLED_TYPE_VIEWPORT))
4820 struct CrippledViewport
4822 GtkViewport viewport
;
4824 GtkAdjustment
*hadjustment
;
4825 GtkAdjustment
*vadjustment
;
4833 PROP_HSCROLL_POLICY
,
4834 PROP_VSCROLL_POLICY
,
4838 static void viewport_set_adjustment(CrippledViewport
*viewport
,
4839 GtkOrientation orientation
,
4840 GtkAdjustment
*adjustment
)
4843 adjustment
= gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
4845 if (orientation
== GTK_ORIENTATION_HORIZONTAL
)
4847 if (viewport
->hadjustment
)
4848 g_object_unref(viewport
->hadjustment
);
4849 viewport
->hadjustment
= adjustment
;
4853 if (viewport
->vadjustment
)
4854 g_object_unref(viewport
->vadjustment
);
4855 viewport
->vadjustment
= adjustment
;
4858 g_object_ref_sink(adjustment
);
4862 crippled_viewport_set_property(GObject
* object
,
4864 const GValue
* value
,
4865 GParamSpec
* /*pspec*/)
4867 CrippledViewport
*viewport
= CRIPPLED_VIEWPORT(object
);
4871 case PROP_HADJUSTMENT
:
4872 viewport_set_adjustment(viewport
, GTK_ORIENTATION_HORIZONTAL
, GTK_ADJUSTMENT(g_value_get_object(value
)));
4874 case PROP_VADJUSTMENT
:
4875 viewport_set_adjustment(viewport
, GTK_ORIENTATION_VERTICAL
, GTK_ADJUSTMENT(g_value_get_object(value
)));
4877 case PROP_HSCROLL_POLICY
:
4878 case PROP_VSCROLL_POLICY
:
4881 SAL_WARN( "vcl.gtk", "unknown property\n");
4887 crippled_viewport_get_property(GObject
* object
,
4890 GParamSpec
* /*pspec*/)
4892 CrippledViewport
*viewport
= CRIPPLED_VIEWPORT(object
);
4896 case PROP_HADJUSTMENT
:
4897 g_value_set_object(value
, viewport
->hadjustment
);
4899 case PROP_VADJUSTMENT
:
4900 g_value_set_object(value
, viewport
->vadjustment
);
4902 case PROP_HSCROLL_POLICY
:
4903 g_value_set_enum(value
, GTK_SCROLL_MINIMUM
);
4905 case PROP_VSCROLL_POLICY
:
4906 g_value_set_enum(value
, GTK_SCROLL_MINIMUM
);
4909 SAL_WARN( "vcl.gtk", "unknown property\n");
4914 static void crippled_viewport_class_init(GtkViewportClass
*klass
)
4916 GObjectClass
* o_class
= G_OBJECT_CLASS(klass
);
4918 /* GObject signals */
4919 o_class
->set_property
= crippled_viewport_set_property
;
4920 o_class
->get_property
= crippled_viewport_get_property
;
4923 g_object_class_override_property(o_class
, PROP_HADJUSTMENT
, "hadjustment");
4924 g_object_class_override_property(o_class
, PROP_VADJUSTMENT
, "vadjustment");
4925 g_object_class_override_property(o_class
, PROP_HSCROLL_POLICY
, "hscroll-policy");
4926 g_object_class_override_property(o_class
, PROP_VSCROLL_POLICY
, "vscroll-policy");
4929 GType
crippled_viewport_get_type()
4931 static GType type
= 0;
4935 static const GTypeInfo tinfo
=
4937 sizeof (GtkViewportClass
),
4938 nullptr, /* base init */
4939 nullptr, /* base finalize */
4940 reinterpret_cast<GClassInitFunc
>(crippled_viewport_class_init
), /* class init */
4941 nullptr, /* class finalize */
4942 nullptr, /* class data */
4943 sizeof (CrippledViewport
), /* instance size */
4944 0, /* nb preallocs */
4945 nullptr, /* instance init */
4946 nullptr /* value table */
4949 type
= g_type_register_static( GTK_TYPE_VIEWPORT
, "CrippledViewport",
4950 &tinfo
, GTypeFlags(0));
4956 static VclPolicyType
GtkToVcl(GtkPolicyType eType
)
4958 VclPolicyType
eRet(VclPolicyType::NEVER
);
4961 case GTK_POLICY_ALWAYS
:
4962 eRet
= VclPolicyType::ALWAYS
;
4964 case GTK_POLICY_AUTOMATIC
:
4965 eRet
= VclPolicyType::AUTOMATIC
;
4967 case GTK_POLICY_EXTERNAL
:
4968 case GTK_POLICY_NEVER
:
4969 eRet
= VclPolicyType::NEVER
;
4975 static GtkPolicyType
VclToGtk(VclPolicyType eType
)
4977 GtkPolicyType
eRet(GTK_POLICY_ALWAYS
);
4980 case VclPolicyType::ALWAYS
:
4981 eRet
= GTK_POLICY_ALWAYS
;
4983 case VclPolicyType::AUTOMATIC
:
4984 eRet
= GTK_POLICY_AUTOMATIC
;
4986 case VclPolicyType::NEVER
:
4987 eRet
= GTK_POLICY_NEVER
;
4993 static GtkMessageType
VclToGtk(VclMessageType eType
)
4995 GtkMessageType
eRet(GTK_MESSAGE_INFO
);
4998 case VclMessageType::Info
:
4999 eRet
= GTK_MESSAGE_INFO
;
5001 case VclMessageType::Warning
:
5002 eRet
= GTK_MESSAGE_WARNING
;
5004 case VclMessageType::Question
:
5005 eRet
= GTK_MESSAGE_QUESTION
;
5007 case VclMessageType::Error
:
5008 eRet
= GTK_MESSAGE_ERROR
;
5014 static GtkButtonsType
VclToGtk(VclButtonsType eType
)
5016 GtkButtonsType
eRet(GTK_BUTTONS_NONE
);
5019 case VclButtonsType::NONE
:
5020 eRet
= GTK_BUTTONS_NONE
;
5022 case VclButtonsType::Ok
:
5023 eRet
= GTK_BUTTONS_OK
;
5025 case VclButtonsType::Close
:
5026 eRet
= GTK_BUTTONS_CLOSE
;
5028 case VclButtonsType::Cancel
:
5029 eRet
= GTK_BUTTONS_CANCEL
;
5031 case VclButtonsType::YesNo
:
5032 eRet
= GTK_BUTTONS_YES_NO
;
5034 case VclButtonsType::OkCancel
:
5035 eRet
= GTK_BUTTONS_OK_CANCEL
;
5041 static GtkSelectionMode
VclToGtk(SelectionMode eType
)
5043 GtkSelectionMode
eRet(GTK_SELECTION_NONE
);
5046 case SelectionMode::NONE
:
5047 eRet
= GTK_SELECTION_NONE
;
5049 case SelectionMode::Single
:
5050 eRet
= GTK_SELECTION_SINGLE
;
5052 case SelectionMode::Range
:
5053 eRet
= GTK_SELECTION_BROWSE
;
5055 case SelectionMode::Multiple
:
5056 eRet
= GTK_SELECTION_MULTIPLE
;
5062 class GtkInstanceScrolledWindow final
: public GtkInstanceContainer
, public virtual weld::ScrolledWindow
5065 GtkScrolledWindow
* m_pScrolledWindow
;
5066 GtkWidget
*m_pOrigViewport
;
5067 GtkAdjustment
* m_pVAdjustment
;
5068 GtkAdjustment
* m_pHAdjustment
;
5069 gulong m_nVAdjustChangedSignalId
;
5070 gulong m_nHAdjustChangedSignalId
;
5072 static void signalVAdjustValueChanged(GtkAdjustment
*, gpointer widget
)
5074 GtkInstanceScrolledWindow
* pThis
= static_cast<GtkInstanceScrolledWindow
*>(widget
);
5075 SolarMutexGuard aGuard
;
5076 pThis
->signal_vadjustment_changed();
5079 static void signalHAdjustValueChanged(GtkAdjustment
*, gpointer widget
)
5081 GtkInstanceScrolledWindow
* pThis
= static_cast<GtkInstanceScrolledWindow
*>(widget
);
5082 SolarMutexGuard aGuard
;
5083 pThis
->signal_hadjustment_changed();
5087 GtkInstanceScrolledWindow(GtkScrolledWindow
* pScrolledWindow
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
5088 : GtkInstanceContainer(GTK_CONTAINER(pScrolledWindow
), pBuilder
, bTakeOwnership
)
5089 , m_pScrolledWindow(pScrolledWindow
)
5090 , m_pOrigViewport(nullptr)
5091 , m_pVAdjustment(gtk_scrolled_window_get_vadjustment(m_pScrolledWindow
))
5092 , m_pHAdjustment(gtk_scrolled_window_get_hadjustment(m_pScrolledWindow
))
5093 , m_nVAdjustChangedSignalId(g_signal_connect(m_pVAdjustment
, "value-changed", G_CALLBACK(signalVAdjustValueChanged
), this))
5094 , m_nHAdjustChangedSignalId(g_signal_connect(m_pHAdjustment
, "value-changed", G_CALLBACK(signalHAdjustValueChanged
), this))
5098 virtual void set_user_managed_scrolling() override
5100 disable_notify_events();
5101 //remove the original viewport and replace it with our bodged one which
5102 //doesn't do any scrolling and expects its child to figure it out somehow
5103 assert(!m_pOrigViewport
);
5104 GtkWidget
*pViewport
= gtk_bin_get_child(GTK_BIN(m_pScrolledWindow
));
5105 assert(GTK_IS_VIEWPORT(pViewport
));
5106 GtkWidget
*pChild
= gtk_bin_get_child(GTK_BIN(pViewport
));
5107 g_object_ref(pChild
);
5108 gtk_container_remove(GTK_CONTAINER(pViewport
), pChild
);
5109 g_object_ref(pViewport
);
5110 gtk_container_remove(GTK_CONTAINER(m_pScrolledWindow
), pViewport
);
5111 GtkWidget
* pNewViewport
= GTK_WIDGET(g_object_new(crippled_viewport_get_type(), nullptr));
5112 gtk_widget_show(pNewViewport
);
5113 gtk_container_add(GTK_CONTAINER(m_pScrolledWindow
), pNewViewport
);
5114 gtk_container_add(GTK_CONTAINER(pNewViewport
), pChild
);
5115 g_object_unref(pChild
);
5116 m_pOrigViewport
= pViewport
;
5117 enable_notify_events();
5120 virtual void hadjustment_configure(int value
, int lower
, int upper
,
5121 int step_increment
, int page_increment
,
5122 int page_size
) override
5124 disable_notify_events();
5126 value
= upper
- (value
- lower
+ page_size
);
5127 gtk_adjustment_configure(m_pHAdjustment
, value
, lower
, upper
, step_increment
, page_increment
, page_size
);
5128 enable_notify_events();
5131 virtual int hadjustment_get_value() const override
5133 int value
= gtk_adjustment_get_value(m_pHAdjustment
);
5137 int upper
= gtk_adjustment_get_upper(m_pHAdjustment
);
5138 int lower
= gtk_adjustment_get_lower(m_pHAdjustment
);
5139 int page_size
= gtk_adjustment_get_page_size(m_pHAdjustment
);
5140 value
= lower
+ (upper
- value
- page_size
);
5146 virtual void hadjustment_set_value(int value
) override
5148 disable_notify_events();
5152 int upper
= gtk_adjustment_get_upper(m_pHAdjustment
);
5153 int lower
= gtk_adjustment_get_lower(m_pHAdjustment
);
5154 int page_size
= gtk_adjustment_get_page_size(m_pHAdjustment
);
5155 value
= upper
- (value
- lower
+ page_size
);
5158 gtk_adjustment_set_value(m_pHAdjustment
, value
);
5159 enable_notify_events();
5162 virtual int hadjustment_get_upper() const override
5164 return gtk_adjustment_get_upper(m_pHAdjustment
);
5167 virtual void hadjustment_set_upper(int upper
) override
5169 disable_notify_events();
5170 gtk_adjustment_set_upper(m_pHAdjustment
, upper
);
5171 enable_notify_events();
5174 virtual int hadjustment_get_page_size() const override
5176 return gtk_adjustment_get_page_size(m_pHAdjustment
);
5179 virtual void hadjustment_set_page_size(int size
) override
5181 gtk_adjustment_set_page_size(m_pHAdjustment
, size
);
5184 virtual void hadjustment_set_page_increment(int size
) override
5186 gtk_adjustment_set_page_increment(m_pHAdjustment
, size
);
5189 virtual void set_hpolicy(VclPolicyType eHPolicy
) override
5191 GtkPolicyType eGtkVPolicy
;
5192 gtk_scrolled_window_get_policy(m_pScrolledWindow
, nullptr, &eGtkVPolicy
);
5193 gtk_scrolled_window_set_policy(m_pScrolledWindow
, eGtkVPolicy
, VclToGtk(eHPolicy
));
5196 virtual VclPolicyType
get_hpolicy() const override
5198 GtkPolicyType eGtkHPolicy
;
5199 gtk_scrolled_window_get_policy(m_pScrolledWindow
, &eGtkHPolicy
, nullptr);
5200 return GtkToVcl(eGtkHPolicy
);
5203 virtual int get_hscroll_height() const override
5205 if (gtk_scrolled_window_get_overlay_scrolling(m_pScrolledWindow
))
5207 return gtk_widget_get_allocated_height(gtk_scrolled_window_get_hscrollbar(m_pScrolledWindow
));
5210 virtual void vadjustment_configure(int value
, int lower
, int upper
,
5211 int step_increment
, int page_increment
,
5212 int page_size
) override
5214 disable_notify_events();
5215 gtk_adjustment_configure(m_pVAdjustment
, value
, lower
, upper
, step_increment
, page_increment
, page_size
);
5216 enable_notify_events();
5219 virtual int vadjustment_get_value() const override
5221 return gtk_adjustment_get_value(m_pVAdjustment
);
5224 virtual void vadjustment_set_value(int value
) override
5226 disable_notify_events();
5227 gtk_adjustment_set_value(m_pVAdjustment
, value
);
5228 enable_notify_events();
5231 virtual int vadjustment_get_upper() const override
5233 return gtk_adjustment_get_upper(m_pVAdjustment
);
5236 virtual void vadjustment_set_upper(int upper
) override
5238 disable_notify_events();
5239 gtk_adjustment_set_upper(m_pVAdjustment
, upper
);
5240 enable_notify_events();
5243 virtual int vadjustment_get_lower() const override
5245 return gtk_adjustment_get_lower(m_pVAdjustment
);
5248 virtual void vadjustment_set_lower(int lower
) override
5250 disable_notify_events();
5251 gtk_adjustment_set_lower(m_pVAdjustment
, lower
);
5252 enable_notify_events();
5255 virtual int vadjustment_get_page_size() const override
5257 return gtk_adjustment_get_page_size(m_pVAdjustment
);
5260 virtual void vadjustment_set_page_size(int size
) override
5262 gtk_adjustment_set_page_size(m_pVAdjustment
, size
);
5265 virtual void vadjustment_set_page_increment(int size
) override
5267 gtk_adjustment_set_page_increment(m_pVAdjustment
, size
);
5270 virtual void set_vpolicy(VclPolicyType eVPolicy
) override
5272 GtkPolicyType eGtkHPolicy
;
5273 gtk_scrolled_window_get_policy(m_pScrolledWindow
, &eGtkHPolicy
, nullptr);
5274 gtk_scrolled_window_set_policy(m_pScrolledWindow
, eGtkHPolicy
, VclToGtk(eVPolicy
));
5277 virtual VclPolicyType
get_vpolicy() const override
5279 GtkPolicyType eGtkVPolicy
;
5280 gtk_scrolled_window_get_policy(m_pScrolledWindow
, nullptr, &eGtkVPolicy
);
5281 return GtkToVcl(eGtkVPolicy
);
5284 virtual int get_vscroll_width() const override
5286 if (gtk_scrolled_window_get_overlay_scrolling(m_pScrolledWindow
))
5288 return gtk_widget_get_allocated_width(gtk_scrolled_window_get_vscrollbar(m_pScrolledWindow
));
5291 virtual void disable_notify_events() override
5293 g_signal_handler_block(m_pVAdjustment
, m_nVAdjustChangedSignalId
);
5294 g_signal_handler_block(m_pHAdjustment
, m_nHAdjustChangedSignalId
);
5295 GtkInstanceContainer::disable_notify_events();
5298 virtual void enable_notify_events() override
5300 GtkInstanceContainer::enable_notify_events();
5301 g_signal_handler_unblock(m_pVAdjustment
, m_nVAdjustChangedSignalId
);
5302 g_signal_handler_unblock(m_pHAdjustment
, m_nHAdjustChangedSignalId
);
5305 virtual ~GtkInstanceScrolledWindow() override
5307 //put it back the way it was
5308 if (m_pOrigViewport
)
5310 disable_notify_events();
5311 GtkWidget
*pViewport
= gtk_bin_get_child(GTK_BIN(m_pScrolledWindow
));
5312 assert(CRIPPLED_IS_VIEWPORT(pViewport
));
5313 GtkWidget
*pChild
= gtk_bin_get_child(GTK_BIN(pViewport
));
5314 g_object_ref(pChild
);
5315 gtk_container_remove(GTK_CONTAINER(pViewport
), pChild
);
5316 g_object_ref(pViewport
);
5317 gtk_container_remove(GTK_CONTAINER(m_pScrolledWindow
), pViewport
);
5318 gtk_container_add(GTK_CONTAINER(m_pScrolledWindow
), m_pOrigViewport
);
5319 g_object_unref(m_pOrigViewport
);
5320 gtk_container_add(GTK_CONTAINER(m_pOrigViewport
), pChild
);
5321 g_object_unref(pChild
);
5322 gtk_widget_destroy(pViewport
);
5323 g_object_unref(pViewport
);
5324 m_pOrigViewport
= nullptr;
5325 enable_notify_events();
5327 g_signal_handler_disconnect(m_pVAdjustment
, m_nVAdjustChangedSignalId
);
5328 g_signal_handler_disconnect(m_pHAdjustment
, m_nHAdjustChangedSignalId
);
5332 class GtkInstanceNotebook
: public GtkInstanceContainer
, public virtual weld::Notebook
5335 GtkNotebook
* m_pNotebook
;
5336 GtkBox
* m_pOverFlowBox
;
5337 GtkNotebook
* m_pOverFlowNotebook
;
5338 gulong m_nSwitchPageSignalId
;
5339 gulong m_nOverFlowSwitchPageSignalId
;
5340 gulong m_nSizeAllocateSignalId
;
5341 gulong m_nFocusSignalId
;
5342 gulong m_nChangeCurrentPageId
;
5343 guint m_nLaunchSplitTimeoutId
;
5344 bool m_bOverFlowBoxActive
;
5345 bool m_bOverFlowBoxIsStart
;
5346 int m_nStartTabCount
;
5348 mutable std::vector
<std::unique_ptr
<GtkInstanceContainer
>> m_aPages
;
5350 static void signalSwitchPage(GtkNotebook
*, GtkWidget
*, guint nNewPage
, gpointer widget
)
5352 GtkInstanceNotebook
* pThis
= static_cast<GtkInstanceNotebook
*>(widget
);
5353 SolarMutexGuard aGuard
;
5354 pThis
->signal_switch_page(nNewPage
);
5357 static gboolean
launch_overflow_switch_page(GtkInstanceNotebook
* pThis
)
5359 SolarMutexGuard aGuard
;
5360 pThis
->signal_overflow_switch_page();
5364 static void signalOverFlowSwitchPage(GtkNotebook
*, GtkWidget
*, guint
, gpointer widget
)
5366 g_timeout_add_full(G_PRIORITY_HIGH_IDLE
, 0, reinterpret_cast<GSourceFunc
>(launch_overflow_switch_page
), widget
, nullptr);
5369 void signal_switch_page(int nNewPage
)
5371 if (m_bOverFlowBoxIsStart
)
5373 auto nOverFlowLen
= m_bOverFlowBoxActive
? gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1 : 0;
5374 // add count of overflow pages, minus the extra tab
5375 nNewPage
+= nOverFlowLen
;
5378 bool bAllow
= !m_aLeavePageHdl
.IsSet() || m_aLeavePageHdl
.Call(get_current_page_ident());
5381 g_signal_stop_emission_by_name(m_pNotebook
, "switch-page");
5384 if (m_bOverFlowBoxActive
)
5385 gtk_notebook_set_current_page(m_pOverFlowNotebook
, gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1);
5386 OString
sNewIdent(get_page_ident(nNewPage
));
5387 m_aEnterPageHdl
.Call(sNewIdent
);
5390 void unsplit_notebooks()
5392 int nOverFlowPages
= gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1;
5393 int nMainPages
= gtk_notebook_get_n_pages(m_pNotebook
);
5395 if (!m_bOverFlowBoxIsStart
)
5396 nPageIndex
+= nMainPages
;
5398 // take the overflow pages, and put them back at the end of the normal one
5400 while (nOverFlowPages
)
5402 OString
sIdent(get_page_ident(m_pOverFlowNotebook
, 0));
5403 OUString
sLabel(get_tab_label_text(m_pOverFlowNotebook
, 0));
5404 remove_page(m_pOverFlowNotebook
, sIdent
);
5406 GtkWidget
* pPage
= m_aPages
[nPageIndex
]->getWidget();
5407 append_page(m_pNotebook
, sIdent
, sLabel
, pPage
);
5409 GtkWidget
* pTabWidget
= gtk_notebook_get_tab_label(m_pNotebook
,
5410 gtk_notebook_get_nth_page(m_pNotebook
, i
));
5411 gtk_widget_set_hexpand(pTabWidget
, true);
5417 // remove the dangling placeholder tab page
5418 remove_page(m_pOverFlowNotebook
, "useless");
5421 // a tab has been selected on the overflow notebook
5422 void signal_overflow_switch_page()
5424 int nNewPage
= gtk_notebook_get_current_page(m_pOverFlowNotebook
);
5425 int nOverFlowPages
= gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1;
5426 if (nNewPage
== nOverFlowPages
)
5428 // the useless tab which is there because there has to be an active tab
5432 // check if we are allowed leave before attempting to resplit the notebooks
5433 bool bAllow
= !m_aLeavePageHdl
.IsSet() || m_aLeavePageHdl
.Call(get_current_page_ident());
5437 disable_notify_events();
5439 // take the overflow pages, and put them back at the end of the normal one
5440 unsplit_notebooks();
5442 // now redo the split, the pages will be split the other way around this time
5443 std::swap(m_nStartTabCount
, m_nEndTabCount
);
5446 gtk_notebook_set_current_page(m_pNotebook
, nNewPage
);
5448 enable_notify_events();
5450 // trigger main notebook switch-page callback
5451 OString
sNewIdent(get_page_ident(m_pNotebook
, nNewPage
));
5452 m_aEnterPageHdl
.Call(sNewIdent
);
5455 static OString
get_page_ident(GtkNotebook
*pNotebook
, guint nPage
)
5457 const GtkWidget
* pTabWidget
= gtk_notebook_get_tab_label(pNotebook
, gtk_notebook_get_nth_page(pNotebook
, nPage
));
5458 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pTabWidget
));
5459 return OString(pStr
, pStr
? strlen(pStr
) : 0);
5462 static gint
get_page_number(GtkNotebook
*pNotebook
, const OString
& rIdent
)
5464 gint nPages
= gtk_notebook_get_n_pages(pNotebook
);
5465 for (gint i
= 0; i
< nPages
; ++i
)
5467 const GtkWidget
* pTabWidget
= gtk_notebook_get_tab_label(pNotebook
, gtk_notebook_get_nth_page(pNotebook
, i
));
5468 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pTabWidget
));
5469 if (pStr
&& strcmp(pStr
, rIdent
.getStr()) == 0)
5475 int remove_page(GtkNotebook
*pNotebook
, const OString
& rIdent
)
5477 disable_notify_events();
5478 int nPageNumber
= get_page_number(pNotebook
, rIdent
);
5479 gtk_notebook_remove_page(pNotebook
, nPageNumber
);
5480 enable_notify_events();
5484 static OUString
get_tab_label_text(GtkNotebook
*pNotebook
, guint nPage
)
5486 const gchar
* pStr
= gtk_notebook_get_tab_label_text(pNotebook
, gtk_notebook_get_nth_page(pNotebook
, nPage
));
5487 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
5490 static void set_tab_label_text(GtkNotebook
*pNotebook
, guint nPage
, const OUString
& rText
)
5492 OString
sUtf8(rText
.toUtf8());
5494 GtkWidget
* pPage
= gtk_notebook_get_nth_page(pNotebook
, nPage
);
5496 // tdf#128241 if there's already a label here, reuse it so the buildable
5497 // name remains the same, gtk_notebook_set_tab_label_text will replace
5498 // the label widget with a new one
5499 GtkWidget
* pTabWidget
= gtk_notebook_get_tab_label(pNotebook
, pPage
);
5500 if (pTabWidget
&& GTK_IS_LABEL(pTabWidget
))
5502 gtk_label_set_label(GTK_LABEL(pTabWidget
), sUtf8
.getStr());
5506 gtk_notebook_set_tab_label_text(pNotebook
, pPage
, sUtf8
.getStr());
5509 void append_useless_page(GtkNotebook
*pNotebook
)
5511 disable_notify_events();
5513 GtkWidget
*pTabWidget
= gtk_fixed_new();
5514 gtk_buildable_set_name(GTK_BUILDABLE(pTabWidget
), "useless");
5516 GtkWidget
*pChild
= gtk_grid_new();
5517 gtk_notebook_append_page(pNotebook
, pChild
, pTabWidget
);
5518 gtk_widget_show(pChild
);
5519 gtk_widget_show(pTabWidget
);
5521 enable_notify_events();
5524 void append_page(GtkNotebook
*pNotebook
, const OString
& rIdent
, const OUString
& rLabel
, GtkWidget
*pChild
)
5526 disable_notify_events();
5528 GtkWidget
*pTabWidget
= gtk_label_new(MapToGtkAccelerator(rLabel
).getStr());
5529 gtk_buildable_set_name(GTK_BUILDABLE(pTabWidget
), rIdent
.getStr());
5531 gtk_notebook_append_page(pNotebook
, pChild
, pTabWidget
);
5532 gtk_widget_show(pChild
);
5533 gtk_widget_show(pTabWidget
);
5535 enable_notify_events();
5538 gint
get_page_number(const OString
& rIdent
) const
5540 auto nMainIndex
= get_page_number(m_pNotebook
, rIdent
);
5541 auto nOverFlowIndex
= get_page_number(m_pOverFlowNotebook
, rIdent
);
5543 if (nMainIndex
== -1 && nOverFlowIndex
== -1)
5546 if (m_bOverFlowBoxIsStart
)
5548 if (nOverFlowIndex
!= -1)
5549 return nOverFlowIndex
;
5552 auto nOverFlowLen
= m_bOverFlowBoxActive
? gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1 : 0;
5553 return nMainIndex
+ nOverFlowLen
;
5558 if (nMainIndex
!= -1)
5562 auto nMainLen
= gtk_notebook_get_n_pages(m_pNotebook
);
5563 return nOverFlowIndex
+ nMainLen
;
5568 void make_overflow_boxes()
5570 m_pOverFlowBox
= GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0));
5571 GtkWidget
* pParent
= gtk_widget_get_parent(GTK_WIDGET(m_pNotebook
));
5572 gtk_container_add(GTK_CONTAINER(pParent
), GTK_WIDGET(m_pOverFlowBox
));
5573 gtk_box_pack_start(m_pOverFlowBox
, GTK_WIDGET(m_pOverFlowNotebook
), false, false, 0);
5574 g_object_ref(m_pNotebook
);
5575 gtk_container_remove(GTK_CONTAINER(pParent
), GTK_WIDGET(m_pNotebook
));
5576 gtk_box_pack_start(m_pOverFlowBox
, GTK_WIDGET(m_pNotebook
), true, true, 0);
5577 g_object_unref(m_pNotebook
);
5578 gtk_widget_show(GTK_WIDGET(m_pOverFlowBox
));
5581 void split_notebooks()
5583 // get the original preferred size for the notebook, the sane width
5584 // expected here depends on the notebooks all initially having
5585 // scrollable tabs enabled
5586 GtkAllocation alloc
;
5587 gtk_widget_get_allocation(GTK_WIDGET(m_pNotebook
), &alloc
);
5589 // toggle the direction of the split since the last time
5590 m_bOverFlowBoxIsStart
= !m_bOverFlowBoxIsStart
;
5591 if (!m_pOverFlowBox
)
5592 make_overflow_boxes();
5594 // don't scroll the tabs anymore
5595 gtk_notebook_set_scrollable(m_pNotebook
, false);
5597 gtk_widget_freeze_child_notify(GTK_WIDGET(m_pNotebook
));
5598 gtk_widget_freeze_child_notify(GTK_WIDGET(m_pOverFlowNotebook
));
5600 gtk_widget_show(GTK_WIDGET(m_pOverFlowNotebook
));
5604 GtkRequisition size1
, size2
;
5606 if (!m_nStartTabCount
&& !m_nEndTabCount
)
5608 nPages
= gtk_notebook_get_n_pages(m_pNotebook
);
5610 std::vector
<int> aLabelWidths
;
5611 //move tabs to the overflow notebook
5612 for (int i
= 0; i
< nPages
; ++i
)
5614 OUString
sLabel(get_tab_label_text(m_pNotebook
, i
));
5615 aLabelWidths
.push_back(get_pixel_size(sLabel
).Width());
5617 int row_width
= std::accumulate(aLabelWidths
.begin(), aLabelWidths
.end(), 0) / 2;
5619 for (int i
= 0; i
< nPages
; ++i
)
5621 count
+= aLabelWidths
[i
];
5622 if (count
>= row_width
)
5624 m_nStartTabCount
= i
;
5629 m_nEndTabCount
= nPages
- m_nStartTabCount
;
5632 //move the tabs to the overflow notebook
5634 int nOverFlowPages
= m_nStartTabCount
;
5635 while (nOverFlowPages
)
5637 OString
sIdent(get_page_ident(m_pNotebook
, 0));
5638 OUString
sLabel(get_tab_label_text(m_pNotebook
, 0));
5639 remove_page(m_pNotebook
, sIdent
);
5640 append_page(m_pOverFlowNotebook
, sIdent
, sLabel
, gtk_grid_new());
5641 GtkWidget
* pTabWidget
= gtk_notebook_get_tab_label(m_pOverFlowNotebook
,
5642 gtk_notebook_get_nth_page(m_pOverFlowNotebook
, i
));
5643 gtk_widget_set_hexpand(pTabWidget
, true);
5649 for (i
= 0; i
< m_nEndTabCount
; ++i
)
5651 GtkWidget
* pTabWidget
= gtk_notebook_get_tab_label(m_pNotebook
,
5652 gtk_notebook_get_nth_page(m_pNotebook
, i
));
5653 gtk_widget_set_hexpand(pTabWidget
, true);
5656 // have to have some tab as the active tab of the overflow notebook
5657 append_useless_page(m_pOverFlowNotebook
);
5658 gtk_notebook_set_current_page(m_pOverFlowNotebook
, gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1);
5659 if (gtk_widget_has_focus(GTK_WIDGET(m_pOverFlowNotebook
)))
5660 gtk_widget_grab_focus(GTK_WIDGET(m_pNotebook
));
5662 // add this temporarily to the normal notebook to measure how wide
5663 // the row would be if switched to the other notebook
5664 append_useless_page(m_pNotebook
);
5666 gtk_widget_get_preferred_size(GTK_WIDGET(m_pNotebook
), nullptr, &size1
);
5667 gtk_widget_get_preferred_size(GTK_WIDGET(m_pOverFlowNotebook
), nullptr, &size2
);
5669 auto nWidth
= std::max(size1
.width
, size2
.width
);
5670 gtk_widget_set_size_request(GTK_WIDGET(m_pNotebook
), nWidth
, alloc
.height
);
5671 gtk_widget_set_size_request(GTK_WIDGET(m_pOverFlowNotebook
), nWidth
, -1);
5673 // remove it once we've measured it
5674 remove_page(m_pNotebook
, "useless");
5676 gtk_widget_thaw_child_notify(GTK_WIDGET(m_pOverFlowNotebook
));
5677 gtk_widget_thaw_child_notify(GTK_WIDGET(m_pNotebook
));
5679 m_bOverFlowBoxActive
= true;
5682 static gboolean
launch_split_notebooks(GtkInstanceNotebook
* pThis
)
5684 int nCurrentPage
= pThis
->get_current_page();
5685 pThis
->split_notebooks();
5686 pThis
->set_current_page(nCurrentPage
);
5687 pThis
->m_nLaunchSplitTimeoutId
= 0;
5692 // https://developer.gnome.org/hig-book/unstable/controls-notebooks.html.en#controls-too-many-tabs
5693 // if no of tabs > 6, but only if the notebook would auto-scroll, then split the tabs over
5694 // two notebooks. Checking for the auto-scroll allows themes like Ambience under Ubuntu 16.04 to keep
5695 // tabs in a single row when they would fit
5696 void signal_notebook_size_allocate()
5698 if (m_bOverFlowBoxActive
|| m_nLaunchSplitTimeoutId
)
5700 disable_notify_events();
5701 gint nPages
= gtk_notebook_get_n_pages(m_pNotebook
);
5702 if (nPages
> 6 && gtk_notebook_get_tab_pos(m_pNotebook
) == GTK_POS_TOP
)
5704 for (gint i
= 0; i
< nPages
; ++i
)
5706 GtkWidget
* pTabWidget
= gtk_notebook_get_tab_label(m_pNotebook
, gtk_notebook_get_nth_page(m_pNotebook
, i
));
5707 if (!gtk_widget_get_child_visible(pTabWidget
))
5709 m_nLaunchSplitTimeoutId
= g_timeout_add_full(G_PRIORITY_HIGH_IDLE
, 0, reinterpret_cast<GSourceFunc
>(launch_split_notebooks
), this, nullptr);
5714 enable_notify_events();
5717 static void signalSizeAllocate(GtkWidget
*, GdkRectangle
*, gpointer widget
)
5719 GtkInstanceNotebook
* pThis
= static_cast<GtkInstanceNotebook
*>(widget
);
5720 pThis
->signal_notebook_size_allocate();
5723 bool signal_focus(GtkDirectionType direction
)
5725 if (!m_bOverFlowBoxActive
)
5728 int nPage
= gtk_notebook_get_current_page(m_pNotebook
);
5729 if (direction
== GTK_DIR_LEFT
&& nPage
== 0)
5731 auto nOverFlowLen
= gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1;
5732 gtk_notebook_set_current_page(m_pOverFlowNotebook
, nOverFlowLen
- 1);
5735 else if (direction
== GTK_DIR_RIGHT
&& nPage
== gtk_notebook_get_n_pages(m_pNotebook
) - 1)
5737 gtk_notebook_set_current_page(m_pOverFlowNotebook
, 0);
5744 static gboolean
signalFocus(GtkNotebook
* notebook
, GtkDirectionType direction
, gpointer widget
)
5746 // if the notebook widget itself has focus
5747 if (gtk_widget_is_focus(GTK_WIDGET(notebook
)))
5749 GtkInstanceNotebook
* pThis
= static_cast<GtkInstanceNotebook
*>(widget
);
5750 return pThis
->signal_focus(direction
);
5755 // ctrl + page_up/ page_down
5756 bool signal_change_current_page(gint arg1
)
5758 bool bHandled
= signal_focus(arg1
< 0 ? GTK_DIR_LEFT
: GTK_DIR_RIGHT
);
5760 g_signal_stop_emission_by_name(m_pNotebook
, "change-current-page");
5764 static gboolean
signalChangeCurrentPage(GtkNotebook
*, gint arg1
, gpointer widget
)
5768 GtkInstanceNotebook
* pThis
= static_cast<GtkInstanceNotebook
*>(widget
);
5769 return pThis
->signal_change_current_page(arg1
);
5773 GtkInstanceNotebook(GtkNotebook
* pNotebook
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
5774 : GtkInstanceContainer(GTK_CONTAINER(pNotebook
), pBuilder
, bTakeOwnership
)
5775 , m_pNotebook(pNotebook
)
5776 , m_pOverFlowBox(nullptr)
5777 , m_pOverFlowNotebook(GTK_NOTEBOOK(gtk_notebook_new()))
5778 , m_nSwitchPageSignalId(g_signal_connect(pNotebook
, "switch-page", G_CALLBACK(signalSwitchPage
), this))
5779 , m_nOverFlowSwitchPageSignalId(g_signal_connect(m_pOverFlowNotebook
, "switch-page", G_CALLBACK(signalOverFlowSwitchPage
), this))
5780 , m_nFocusSignalId(g_signal_connect(pNotebook
, "focus", G_CALLBACK(signalFocus
), this))
5781 , m_nChangeCurrentPageId(g_signal_connect(pNotebook
, "change-current-page", G_CALLBACK(signalChangeCurrentPage
), this))
5782 , m_nLaunchSplitTimeoutId(0)
5783 , m_bOverFlowBoxActive(false)
5784 , m_bOverFlowBoxIsStart(false)
5785 , m_nStartTabCount(0)
5788 gtk_widget_add_events(GTK_WIDGET(pNotebook
), GDK_SCROLL_MASK
);
5789 if (get_n_pages() > 6)
5790 m_nSizeAllocateSignalId
= g_signal_connect_after(pNotebook
, "size-allocate", G_CALLBACK(signalSizeAllocate
), this);
5792 m_nSizeAllocateSignalId
= 0;
5793 gtk_notebook_set_show_border(m_pOverFlowNotebook
, false);
5795 // tdf#122623 it's nigh impossible to have a GtkNotebook without an active (checked) tab, so try and theme
5796 // the unwanted tab into invisibility
5797 GtkStyleContext
*pNotebookContext
= gtk_widget_get_style_context(GTK_WIDGET(m_pOverFlowNotebook
));
5798 GtkCssProvider
*pProvider
= gtk_css_provider_new();
5799 static const gchar data
[] = "header.top > tabs > tab:checked { box-shadow: none; padding: 0 0 0 0; margin: 0 0 0 0; border-image: none; border-image-width: 0 0 0 0; background-image: none; background-color: transparent; border-radius: 0 0 0 0; border-width: 0 0 0 0; border-style: none; border-color: transparent; opacity: 0; min-height: 0; min-width: 0; }";
5800 static const gchar olddata
[] = "tab.top:active { box-shadow: none; padding: 0 0 0 0; margin: 0 0 0 0; border-image: none; border-image-width: 0 0 0 0; background-image: none; background-color: transparent; border-radius: 0 0 0 0; border-width: 0 0 0 0; border-style: none; border-color: transparent; opacity: 0; }";
5801 gtk_css_provider_load_from_data(pProvider
, gtk_check_version(3, 20, 0) == nullptr ? data
: olddata
, -1, nullptr);
5802 gtk_style_context_add_provider(pNotebookContext
, GTK_STYLE_PROVIDER(pProvider
),
5803 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
5806 virtual int get_current_page() const override
5808 int nPage
= gtk_notebook_get_current_page(m_pNotebook
);
5811 if (m_bOverFlowBoxIsStart
)
5813 auto nOverFlowLen
= m_bOverFlowBoxActive
? gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1 : 0;
5814 // add count of overflow pages, minus the extra tab
5815 nPage
+= nOverFlowLen
;
5820 virtual OString
get_page_ident(int nPage
) const override
5822 auto nMainLen
= gtk_notebook_get_n_pages(m_pNotebook
);
5823 auto nOverFlowLen
= m_bOverFlowBoxActive
? gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1 : 0;
5824 if (m_bOverFlowBoxIsStart
)
5826 if (nPage
< nOverFlowLen
)
5827 return get_page_ident(m_pOverFlowNotebook
, nPage
);
5828 nPage
-= nOverFlowLen
;
5829 return get_page_ident(m_pNotebook
, nPage
);
5833 if (nPage
< nMainLen
)
5834 return get_page_ident(m_pNotebook
, nPage
);
5836 return get_page_ident(m_pOverFlowNotebook
, nPage
);
5840 virtual OString
get_current_page_ident() const override
5842 return get_page_ident(get_current_page());
5845 virtual weld::Container
* get_page(const OString
& rIdent
) const override
5847 int nPage
= get_page_number(rIdent
);
5851 GtkContainer
* pChild
;
5852 if (m_bOverFlowBoxIsStart
)
5854 auto nOverFlowLen
= m_bOverFlowBoxActive
? gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1 : 0;
5855 if (nPage
< nOverFlowLen
)
5856 pChild
= GTK_CONTAINER(gtk_notebook_get_nth_page(m_pOverFlowNotebook
, nPage
));
5859 nPage
-= nOverFlowLen
;
5860 pChild
= GTK_CONTAINER(gtk_notebook_get_nth_page(m_pNotebook
, nPage
));
5865 auto nMainLen
= gtk_notebook_get_n_pages(m_pNotebook
);
5866 if (nPage
< nMainLen
)
5867 pChild
= GTK_CONTAINER(gtk_notebook_get_nth_page(m_pNotebook
, nPage
));
5871 pChild
= GTK_CONTAINER(gtk_notebook_get_nth_page(m_pOverFlowNotebook
, nPage
));
5875 unsigned int nPageIndex
= static_cast<unsigned int>(nPage
);
5876 if (m_aPages
.size() < nPageIndex
+ 1)
5877 m_aPages
.resize(nPageIndex
+ 1);
5878 if (!m_aPages
[nPageIndex
])
5879 m_aPages
[nPageIndex
].reset(new GtkInstanceContainer(pChild
, m_pBuilder
, false));
5880 return m_aPages
[nPageIndex
].get();
5883 virtual void set_current_page(int nPage
) override
5885 if (m_bOverFlowBoxIsStart
)
5887 auto nOverFlowLen
= m_bOverFlowBoxActive
? gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1 : 0;
5888 if (nPage
< nOverFlowLen
)
5889 gtk_notebook_set_current_page(m_pOverFlowNotebook
, nPage
);
5892 nPage
-= nOverFlowLen
;
5893 gtk_notebook_set_current_page(m_pNotebook
, nPage
);
5898 auto nMainLen
= gtk_notebook_get_n_pages(m_pNotebook
);
5899 if (nPage
< nMainLen
)
5900 gtk_notebook_set_current_page(m_pNotebook
, nPage
);
5904 gtk_notebook_set_current_page(m_pOverFlowNotebook
, nPage
);
5909 virtual void set_current_page(const OString
& rIdent
) override
5911 gint nPage
= get_page_number(rIdent
);
5912 set_current_page(nPage
);
5915 virtual int get_n_pages() const override
5917 int nLen
= gtk_notebook_get_n_pages(m_pNotebook
);
5918 if (m_bOverFlowBoxActive
)
5919 nLen
+= gtk_notebook_get_n_pages(m_pOverFlowNotebook
) - 1;
5923 virtual OUString
get_tab_label_text(const OString
& rIdent
) const override
5925 gint nPageNum
= get_page_number(m_pNotebook
, rIdent
);
5927 return get_tab_label_text(m_pNotebook
, nPageNum
);
5928 nPageNum
= get_page_number(m_pOverFlowNotebook
, rIdent
);
5930 return get_tab_label_text(m_pOverFlowNotebook
, nPageNum
);
5934 virtual void set_tab_label_text(const OString
& rIdent
, const OUString
& rText
) override
5936 gint nPageNum
= get_page_number(m_pNotebook
, rIdent
);
5939 set_tab_label_text(m_pNotebook
, nPageNum
, rText
);
5942 nPageNum
= get_page_number(m_pOverFlowNotebook
, rIdent
);
5945 set_tab_label_text(m_pOverFlowNotebook
, nPageNum
, rText
);
5949 virtual void disable_notify_events() override
5951 g_signal_handler_block(m_pNotebook
, m_nSwitchPageSignalId
);
5952 g_signal_handler_block(m_pNotebook
, m_nFocusSignalId
);
5953 g_signal_handler_block(m_pNotebook
, m_nChangeCurrentPageId
);
5954 g_signal_handler_block(m_pOverFlowNotebook
, m_nOverFlowSwitchPageSignalId
);
5955 gtk_widget_freeze_child_notify(GTK_WIDGET(m_pOverFlowNotebook
));
5956 GtkInstanceContainer::disable_notify_events();
5959 virtual void enable_notify_events() override
5961 GtkInstanceContainer::enable_notify_events();
5962 gtk_widget_thaw_child_notify(GTK_WIDGET(m_pOverFlowNotebook
));
5963 g_signal_handler_unblock(m_pOverFlowNotebook
, m_nOverFlowSwitchPageSignalId
);
5964 g_signal_handler_unblock(m_pNotebook
, m_nSwitchPageSignalId
);
5965 g_signal_handler_unblock(m_pNotebook
, m_nFocusSignalId
);
5966 g_signal_handler_unblock(m_pNotebook
, m_nChangeCurrentPageId
);
5969 void reset_split_data()
5971 // reset overflow and allow it to be recalculated if necessary
5972 gtk_widget_hide(GTK_WIDGET(m_pOverFlowNotebook
));
5973 m_bOverFlowBoxActive
= false;
5974 m_nStartTabCount
= 0;
5978 virtual void remove_page(const OString
& rIdent
) override
5980 if (m_bOverFlowBoxActive
)
5982 unsplit_notebooks();
5986 unsigned int nPageIndex
= remove_page(m_pNotebook
, rIdent
);
5987 if (nPageIndex
< m_aPages
.size())
5988 m_aPages
.erase(m_aPages
.begin() + nPageIndex
);
5991 virtual void append_page(const OString
& rIdent
, const OUString
& rLabel
) override
5993 if (m_bOverFlowBoxActive
)
5995 unsplit_notebooks();
5999 // reset overflow and allow it to be recalculated if necessary
6000 gtk_widget_hide(GTK_WIDGET(m_pOverFlowNotebook
));
6001 m_bOverFlowBoxActive
= false;
6003 append_page(m_pNotebook
, rIdent
, rLabel
, gtk_grid_new());
6006 virtual ~GtkInstanceNotebook() override
6008 if (m_nLaunchSplitTimeoutId
)
6009 g_source_remove(m_nLaunchSplitTimeoutId
);
6010 if (m_nSizeAllocateSignalId
)
6011 g_signal_handler_disconnect(m_pNotebook
, m_nSizeAllocateSignalId
);
6012 g_signal_handler_disconnect(m_pNotebook
, m_nSwitchPageSignalId
);
6013 g_signal_handler_disconnect(m_pNotebook
, m_nFocusSignalId
);
6014 g_signal_handler_disconnect(m_pNotebook
, m_nChangeCurrentPageId
);
6015 g_signal_handler_disconnect(m_pOverFlowNotebook
, m_nOverFlowSwitchPageSignalId
);
6016 gtk_widget_destroy(GTK_WIDGET(m_pOverFlowNotebook
));
6018 gtk_widget_destroy(GTK_WIDGET(m_pOverFlowBox
));
6022 class GtkInstanceButton
: public GtkInstanceContainer
, public virtual weld::Button
6025 GtkButton
* m_pButton
;
6028 static void signalClicked(GtkButton
*, gpointer widget
)
6030 GtkInstanceButton
* pThis
= static_cast<GtkInstanceButton
*>(widget
);
6031 SolarMutexGuard aGuard
;
6032 pThis
->signal_clicked();
6036 GtkInstanceButton(GtkButton
* pButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
6037 : GtkInstanceContainer(GTK_CONTAINER(pButton
), pBuilder
, bTakeOwnership
)
6038 , m_pButton(pButton
)
6039 , m_nSignalId(g_signal_connect(pButton
, "clicked", G_CALLBACK(signalClicked
), this))
6041 g_object_set_data(G_OBJECT(m_pButton
), "g-lo-GtkInstanceButton", this);
6044 virtual void set_label(const OUString
& rText
) override
6046 ::set_label(m_pButton
, rText
);
6049 virtual void set_image(VirtualDevice
* pDevice
) override
6051 gtk_button_set_always_show_image(m_pButton
, true);
6052 gtk_button_set_image_position(m_pButton
, GTK_POS_LEFT
);
6054 gtk_button_set_image(m_pButton
, image_new_from_virtual_device(*pDevice
));
6056 gtk_button_set_image(m_pButton
, nullptr);
6059 virtual void set_from_icon_name(const OUString
& rIconName
) override
6061 GdkPixbuf
* pixbuf
= load_icon_by_name(rIconName
);
6063 gtk_button_set_image(m_pButton
, nullptr);
6066 gtk_button_set_image(m_pButton
, gtk_image_new_from_pixbuf(pixbuf
));
6067 g_object_unref(pixbuf
);
6071 virtual void set_image(const css::uno::Reference
<css::graphic::XGraphic
>& rImage
) override
6073 GdkPixbuf
* pixbuf
= getPixbuf(rImage
);
6075 gtk_button_set_image(m_pButton
, nullptr);
6078 gtk_button_set_image(m_pButton
, gtk_image_new_from_pixbuf(pixbuf
));
6079 g_object_unref(pixbuf
);
6083 virtual OUString
get_label() const override
6085 return ::get_label(m_pButton
);
6088 virtual void set_label_line_wrap(bool wrap
) override
6090 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pButton
));
6091 gtk_label_set_line_wrap(GTK_LABEL(pChild
), wrap
);
6094 // allow us to block buttons with click handlers making dialogs return a response
6095 bool has_click_handler() const
6097 return m_aClickHdl
.IsSet();
6100 void clear_click_handler()
6102 m_aClickHdl
= Link
<Button
&, void>();
6105 virtual void disable_notify_events() override
6107 g_signal_handler_block(m_pButton
, m_nSignalId
);
6108 GtkInstanceContainer::disable_notify_events();
6111 virtual void enable_notify_events() override
6113 GtkInstanceContainer::enable_notify_events();
6114 g_signal_handler_unblock(m_pButton
, m_nSignalId
);
6117 virtual ~GtkInstanceButton() override
6119 g_object_steal_data(G_OBJECT(m_pButton
), "g-lo-GtkInstanceButton");
6120 g_signal_handler_disconnect(m_pButton
, m_nSignalId
);
6124 void GtkInstanceDialog::asyncresponse(gint ret
)
6126 if (ret
== GTK_RESPONSE_HELP
)
6132 GtkInstanceButton
* pClickHandler
= has_click_handler(ret
);
6135 // make GTK_RESPONSE_DELETE_EVENT act as if cancel button was pressed
6136 if (ret
== GTK_RESPONSE_DELETE_EVENT
)
6142 m_aDialogRun
.dec_modal_count();
6145 // move the self pointer, otherwise it might be de-allocated by time we try to reset it
6146 auto xRunAsyncSelf
= std::move(m_xRunAsyncSelf
);
6147 auto xDialogController
= std::move(m_xDialogController
);
6148 auto aFunc
= std::move(m_aFunc
);
6150 auto nResponseSignalId
= m_nResponseSignalId
;
6151 auto nCancelSignalId
= m_nCancelSignalId
;
6152 auto nSignalDeleteId
= m_nSignalDeleteId
;
6153 m_nResponseSignalId
= 0;
6154 m_nCancelSignalId
= 0;
6155 m_nSignalDeleteId
= 0;
6157 aFunc(GtkToVcl(ret
));
6159 if (nResponseSignalId
)
6160 g_signal_handler_disconnect(m_pDialog
, nResponseSignalId
);
6161 if (nCancelSignalId
)
6162 g_signal_handler_disconnect(m_pDialog
, nCancelSignalId
);
6163 if (m_nSignalDeleteId
)
6164 g_signal_handler_disconnect(m_pDialog
, nSignalDeleteId
);
6166 xDialogController
.reset();
6167 xRunAsyncSelf
.reset();
6170 int GtkInstanceDialog::run()
6172 if (GTK_IS_DIALOG(m_pDialog
))
6173 sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(m_pDialog
))));
6177 ret
= m_aDialogRun
.run();
6178 if (ret
== GTK_RESPONSE_HELP
)
6183 else if (has_click_handler(ret
))
6188 return GtkToVcl(ret
);
6191 weld::Button
* GtkInstanceDialog::weld_widget_for_response(int nVclResponse
)
6193 GtkButton
* pButton
= get_widget_for_response(VclToGtk(nVclResponse
));
6196 return new GtkInstanceButton(pButton
, m_pBuilder
, false);
6199 void GtkInstanceDialog::response(int nResponse
)
6201 int nGtkResponse
= VclToGtk(nResponse
);
6202 //unblock this response now when activated through code
6203 if (GtkButton
* pWidget
= get_widget_for_response(nGtkResponse
))
6205 void* pData
= g_object_get_data(G_OBJECT(pWidget
), "g-lo-GtkInstanceButton");
6206 GtkInstanceButton
* pButton
= static_cast<GtkInstanceButton
*>(pData
);
6208 pButton
->clear_click_handler();
6210 if (GTK_IS_DIALOG(m_pDialog
))
6211 gtk_dialog_response(GTK_DIALOG(m_pDialog
), nGtkResponse
);
6212 else if (GTK_IS_ASSISTANT(m_pDialog
))
6214 if (!m_aDialogRun
.loop_is_running())
6215 asyncresponse(nGtkResponse
);
6218 m_aDialogRun
.m_nResponseId
= nGtkResponse
;
6219 m_aDialogRun
.loop_quit();
6224 void GtkInstanceDialog::close(bool bCloseSignal
)
6226 GtkInstanceButton
* pClickHandler
= has_click_handler(GTK_RESPONSE_CANCEL
);
6230 g_signal_stop_emission_by_name(m_pDialog
, "close");
6231 // make esc (bCloseSignal == true) or window-delete (bCloseSignal == false)
6232 // act as if cancel button was pressed
6233 pClickHandler
->clicked();
6236 response(RET_CANCEL
);
6239 GtkInstanceButton
* GtkInstanceDialog::has_click_handler(int nResponse
)
6241 GtkInstanceButton
* pButton
= nullptr;
6242 // e.g. map GTK_RESPONSE_DELETE_EVENT to GTK_RESPONSE_CANCEL
6243 nResponse
= VclToGtk(GtkToVcl(nResponse
));
6244 if (GtkButton
* pWidget
= get_widget_for_response(nResponse
))
6246 void* pData
= g_object_get_data(G_OBJECT(pWidget
), "g-lo-GtkInstanceButton");
6247 pButton
= static_cast<GtkInstanceButton
*>(pData
);
6248 if (pButton
&& !pButton
->has_click_handler())
6254 class GtkInstanceToggleButton
: public GtkInstanceButton
, public virtual weld::ToggleButton
6257 GtkToggleButton
* m_pToggleButton
;
6260 static void signalToggled(GtkToggleButton
*, gpointer widget
)
6262 GtkInstanceToggleButton
* pThis
= static_cast<GtkInstanceToggleButton
*>(widget
);
6263 SolarMutexGuard aGuard
;
6264 pThis
->signal_toggled();
6267 GtkInstanceToggleButton(GtkToggleButton
* pButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
6268 : GtkInstanceButton(GTK_BUTTON(pButton
), pBuilder
, bTakeOwnership
)
6269 , m_pToggleButton(pButton
)
6270 , m_nSignalId(g_signal_connect(m_pToggleButton
, "toggled", G_CALLBACK(signalToggled
), this))
6274 virtual void set_active(bool active
) override
6276 disable_notify_events();
6277 gtk_toggle_button_set_inconsistent(m_pToggleButton
, false);
6278 gtk_toggle_button_set_active(m_pToggleButton
, active
);
6279 enable_notify_events();
6282 virtual bool get_active() const override
6284 return gtk_toggle_button_get_active(m_pToggleButton
);
6287 virtual void set_inconsistent(bool inconsistent
) override
6289 gtk_toggle_button_set_inconsistent(m_pToggleButton
, inconsistent
);
6292 virtual bool get_inconsistent() const override
6294 return gtk_toggle_button_get_inconsistent(m_pToggleButton
);
6297 virtual void disable_notify_events() override
6299 g_signal_handler_block(m_pToggleButton
, m_nSignalId
);
6300 GtkInstanceButton::disable_notify_events();
6303 virtual void enable_notify_events() override
6305 GtkInstanceButton::enable_notify_events();
6306 g_signal_handler_unblock(m_pToggleButton
, m_nSignalId
);
6309 virtual ~GtkInstanceToggleButton() override
6311 g_signal_handler_disconnect(m_pToggleButton
, m_nSignalId
);
6315 class GtkInstanceMenuButton
: public GtkInstanceToggleButton
, public MenuHelper
, public virtual weld::MenuButton
6318 GtkMenuButton
* m_pMenuButton
;
6321 GtkWidget
* m_pLabel
;
6322 //popover cannot escape dialog under X so stick up own window instead
6323 GtkWindow
* m_pMenuHack
;
6324 GtkWidget
* m_pPopover
;
6327 static void signalToggled(GtkWidget
*, gpointer widget
)
6329 GtkInstanceMenuButton
* pThis
= static_cast<GtkInstanceMenuButton
*>(widget
);
6330 SolarMutexGuard aGuard
;
6331 pThis
->toggle_menu();
6336 GdkDisplay
*pDisplay
= gtk_widget_get_display(GTK_WIDGET(m_pMenuHack
));
6337 #if GTK_CHECK_VERSION(3, 20, 0)
6338 if (gtk_check_version(3, 20, 0) == nullptr)
6340 GdkSeat
* pSeat
= gdk_display_get_default_seat(pDisplay
);
6341 gdk_seat_grab(pSeat
, gtk_widget_get_window(GTK_WIDGET(m_pMenuHack
)),
6342 GDK_SEAT_CAPABILITY_ALL
, true, nullptr, nullptr, nullptr, nullptr);
6347 const int nMask
= (GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
);
6349 GdkDeviceManager
* pDeviceManager
= gdk_display_get_device_manager(pDisplay
);
6350 GdkDevice
* pPointer
= gdk_device_manager_get_client_pointer(pDeviceManager
);
6351 gdk_device_grab(pPointer
, gtk_widget_get_window(GTK_WIDGET(m_pMenuHack
)), GDK_OWNERSHIP_NONE
,
6352 true, GdkEventMask(nMask
), nullptr, gtk_get_current_event_time());
6357 GdkDisplay
*pDisplay
= gtk_widget_get_display(GTK_WIDGET(m_pMenuHack
));
6358 #if GTK_CHECK_VERSION(3, 20, 0)
6359 if (gtk_check_version(3, 20, 0) == nullptr)
6361 GdkSeat
* pSeat
= gdk_display_get_default_seat(pDisplay
);
6362 gdk_seat_ungrab(pSeat
);
6367 GdkDeviceManager
* pDeviceManager
= gdk_display_get_device_manager(pDisplay
);
6368 GdkDevice
* pPointer
= gdk_device_manager_get_client_pointer(pDeviceManager
);
6369 gdk_device_ungrab(pPointer
, gtk_get_current_event_time());
6380 gtk_widget_hide(GTK_WIDGET(m_pMenuHack
));
6381 //put contents back from where the came from
6382 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pMenuHack
));
6383 g_object_ref(pChild
);
6384 gtk_container_remove(GTK_CONTAINER(m_pMenuHack
), pChild
);
6385 gtk_container_add(GTK_CONTAINER(m_pPopover
), pChild
);
6386 g_object_unref(pChild
);
6391 gtk_container_set_border_width(GTK_CONTAINER(m_pMenuHack
), gtk_container_get_border_width(GTK_CONTAINER(m_pPopover
)));
6393 //steal popover contents and smuggle into toplevel display window
6394 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pPopover
));
6395 g_object_ref(pChild
);
6396 gtk_container_remove(GTK_CONTAINER(m_pPopover
), pChild
);
6397 gtk_container_add(GTK_CONTAINER(m_pMenuHack
), pChild
);
6398 g_object_unref(pChild
);
6400 //place the toplevel just below its launcher button
6401 GtkWidget
* pToplevel
= gtk_widget_get_toplevel(GTK_WIDGET(m_pMenuButton
));
6402 gint x
, y
, absx
, absy
;
6403 gtk_widget_translate_coordinates(GTK_WIDGET(m_pMenuButton
), pToplevel
, 0, 0, &x
, &y
);
6404 GdkWindow
*pWindow
= gtk_widget_get_window(pToplevel
);
6405 gdk_window_get_position(pWindow
, &absx
, &absy
);
6407 gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(pToplevel
)), m_pMenuHack
);
6408 gtk_window_set_transient_for(m_pMenuHack
, GTK_WINDOW(pToplevel
));
6410 gtk_widget_show_all(GTK_WIDGET(m_pMenuHack
));
6411 gtk_window_move(m_pMenuHack
, x
+ absx
, y
+ absy
+ gtk_widget_get_allocated_height(GTK_WIDGET(m_pMenuButton
)));
6413 gtk_widget_grab_focus(GTK_WIDGET(m_pMenuHack
));
6419 static void signalGrabBroken(GtkWidget
*, GdkEventGrabBroken
*pEvent
, gpointer widget
)
6421 GtkInstanceMenuButton
* pThis
= static_cast<GtkInstanceMenuButton
*>(widget
);
6422 pThis
->grab_broken(pEvent
);
6425 void grab_broken(const GdkEventGrabBroken
*event
)
6427 if (event
->grab_window
== nullptr)
6433 //try and regrab, so when we lose the grab to the menu of the color palette
6434 //combobox we regain it so the color palette doesn't itself disappear on next
6435 //click on the color palette combobox
6440 static gboolean
signalButtonRelease(GtkWidget
* pWidget
, GdkEventButton
* pEvent
, gpointer widget
)
6442 GtkInstanceMenuButton
* pThis
= static_cast<GtkInstanceMenuButton
*>(widget
);
6443 return pThis
->button_release(pWidget
, pEvent
);
6446 bool button_release(GtkWidget
* pWidget
, GdkEventButton
* pEvent
)
6448 //we want to pop down if the button was released outside our popup
6449 gdouble x
= pEvent
->x_root
;
6450 gdouble y
= pEvent
->y_root
;
6451 gint xoffset
, yoffset
;
6452 gdk_window_get_root_origin(gtk_widget_get_window(pWidget
), &xoffset
, &yoffset
);
6454 GtkAllocation alloc
;
6455 gtk_widget_get_allocation(pWidget
, &alloc
);
6459 gtk_widget_get_allocation(GTK_WIDGET(m_pMenuHack
), &alloc
);
6460 gint x1
= alloc
.x
+ xoffset
;
6461 gint y1
= alloc
.y
+ yoffset
;
6462 gint x2
= x1
+ alloc
.width
;
6463 gint y2
= y1
+ alloc
.height
;
6465 if (x
> x1
&& x
< x2
&& y
> y1
&& y
< y2
)
6473 static gboolean
keyPress(GtkWidget
*, GdkEventKey
* pEvent
, gpointer widget
)
6475 GtkInstanceMenuButton
* pThis
= static_cast<GtkInstanceMenuButton
*>(widget
);
6476 return pThis
->key_press(pEvent
);
6479 bool key_press(const GdkEventKey
* pEvent
)
6481 if (pEvent
->keyval
== GDK_KEY_Escape
)
6489 void ensure_image_widget()
6493 m_pImage
= GTK_IMAGE(gtk_image_new());
6494 gtk_box_pack_start(m_pBox
, GTK_WIDGET(m_pImage
), false, false, 0);
6495 gtk_box_reorder_child(m_pBox
, GTK_WIDGET(m_pImage
), 0);
6496 gtk_widget_show(GTK_WIDGET(m_pImage
));
6501 GtkInstanceMenuButton(GtkMenuButton
* pMenuButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
6502 : GtkInstanceToggleButton(GTK_TOGGLE_BUTTON(pMenuButton
), pBuilder
, bTakeOwnership
)
6503 , MenuHelper(gtk_menu_button_get_popup(pMenuButton
), false)
6504 , m_pMenuButton(pMenuButton
)
6506 , m_pMenuHack(nullptr)
6507 , m_pPopover(nullptr)
6510 m_pLabel
= gtk_bin_get_child(GTK_BIN(m_pMenuButton
));
6511 //do it "manually" so we can have the dropdown image in GtkMenuButtons shown
6512 //on the right at the same time as this image is shown on the left
6513 g_object_ref(m_pLabel
);
6514 gtk_container_remove(GTK_CONTAINER(m_pMenuButton
), m_pLabel
);
6516 gint
nImageSpacing(2);
6517 GtkStyleContext
*pContext
= gtk_widget_get_style_context(GTK_WIDGET(m_pMenuButton
));
6518 gtk_style_context_get_style(pContext
, "image-spacing", &nImageSpacing
, nullptr);
6519 m_pBox
= GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, nImageSpacing
));
6521 gtk_box_pack_start(m_pBox
, m_pLabel
, false, false, 0);
6522 g_object_unref(m_pLabel
);
6524 if (gtk_toggle_button_get_mode(GTK_TOGGLE_BUTTON(m_pMenuButton
)))
6525 gtk_box_pack_end(m_pBox
, gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_BUTTON
), false, false, 0);
6527 gtk_container_add(GTK_CONTAINER(m_pMenuButton
), GTK_WIDGET(m_pBox
));
6528 gtk_widget_show_all(GTK_WIDGET(m_pBox
));
6531 virtual void set_size_request(int nWidth
, int nHeight
) override
6533 // tweak the label to get a narrower size to stick
6534 if (GTK_IS_LABEL(m_pLabel
))
6535 gtk_label_set_ellipsize(GTK_LABEL(m_pLabel
), PANGO_ELLIPSIZE_MIDDLE
);
6536 gtk_widget_set_size_request(m_pWidget
, nWidth
, nHeight
);
6539 virtual void set_label(const OUString
& rText
) override
6541 ::set_label(GTK_LABEL(m_pLabel
), rText
);
6544 virtual OUString
get_label() const override
6546 return ::get_label(GTK_LABEL(m_pLabel
));
6549 virtual void set_image(VirtualDevice
* pDevice
) override
6551 ensure_image_widget();
6554 if (gtk_check_version(3, 20, 0) == nullptr)
6555 gtk_image_set_from_surface(m_pImage
, get_underlying_cairo_surface(*pDevice
));
6558 GdkPixbuf
* pixbuf
= getPixbuf(*pDevice
);
6559 gtk_image_set_from_pixbuf(m_pImage
, pixbuf
);
6560 g_object_unref(pixbuf
);
6564 gtk_image_set_from_surface(m_pImage
, nullptr);
6567 virtual void set_image(const css::uno::Reference
<css::graphic::XGraphic
>& rImage
) override
6569 ensure_image_widget();
6570 GdkPixbuf
* pixbuf
= getPixbuf(rImage
);
6573 gtk_image_set_from_pixbuf(m_pImage
, pixbuf
);
6574 g_object_unref(pixbuf
);
6577 gtk_image_set_from_surface(m_pImage
, nullptr);
6580 virtual void insert_item(int pos
, const OUString
& rId
, const OUString
& rStr
,
6581 const OUString
* pIconName
, VirtualDevice
* pImageSurface
, bool bCheck
) override
6583 MenuHelper::insert_item(pos
, rId
, rStr
, pIconName
, pImageSurface
, bCheck
);
6586 virtual void insert_separator(int pos
, const OUString
& rId
) override
6588 MenuHelper::insert_separator(pos
, rId
);
6591 virtual void remove_item(const OString
& rId
) override
6593 MenuHelper::remove_item(rId
);
6596 virtual void clear() override
6601 virtual void set_item_active(const OString
& rIdent
, bool bActive
) override
6603 MenuHelper::set_item_active(rIdent
, bActive
);
6606 virtual void set_item_sensitive(const OString
& rIdent
, bool bSensitive
) override
6608 MenuHelper::set_item_sensitive(rIdent
, bSensitive
);
6611 virtual void set_item_label(const OString
& rIdent
, const OUString
& rLabel
) override
6613 MenuHelper::set_item_label(rIdent
, rLabel
);
6616 virtual OUString
get_item_label(const OString
& rIdent
) const override
6618 return MenuHelper::get_item_label(rIdent
);
6621 virtual void set_item_visible(const OString
& rIdent
, bool bVisible
) override
6623 MenuHelper::set_item_visible(rIdent
, bVisible
);
6626 virtual void set_item_help_id(const OString
& rIdent
, const OString
& rHelpId
) override
6628 MenuHelper::set_item_help_id(rIdent
, rHelpId
);
6631 virtual OString
get_item_help_id(const OString
& rIdent
) const override
6633 return MenuHelper::get_item_help_id(rIdent
);
6636 virtual void signal_activate(GtkMenuItem
* pItem
) override
6638 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pItem
));
6639 signal_selected(OString(pStr
, pStr
? strlen(pStr
) : 0));
6642 virtual void set_popover(weld::Widget
* pPopover
) override
6644 GtkInstanceWidget
* pPopoverWidget
= dynamic_cast<GtkInstanceWidget
*>(pPopover
);
6645 m_pPopover
= pPopoverWidget
? pPopoverWidget
->getWidget() : nullptr;
6647 #if defined(GDK_WINDOWING_X11)
6650 //under wayland a Popover will work to "escape" the parent dialog, not
6651 //so under X, so come up with this hack to use a raw GtkWindow
6652 GdkDisplay
*pDisplay
= gtk_widget_get_display(m_pWidget
);
6653 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay
))
6655 m_pMenuHack
= GTK_WINDOW(gtk_window_new(GTK_WINDOW_POPUP
));
6656 gtk_window_set_type_hint(m_pMenuHack
, GDK_WINDOW_TYPE_HINT_COMBO
);
6657 gtk_window_set_modal(m_pMenuHack
, true);
6658 gtk_window_set_resizable(m_pMenuHack
, false);
6659 m_nSignalId
= g_signal_connect(GTK_TOGGLE_BUTTON(m_pMenuButton
), "toggled", G_CALLBACK(signalToggled
), this);
6660 g_signal_connect(m_pMenuHack
, "grab-broken-event", G_CALLBACK(signalGrabBroken
), this);
6661 g_signal_connect(m_pMenuHack
, "button-release-event", G_CALLBACK(signalButtonRelease
), this);
6662 g_signal_connect(m_pMenuHack
, "key-press-event", G_CALLBACK(keyPress
), this);
6669 gtk_menu_button_set_popover(m_pMenuButton
, gtk_popover_new(GTK_WIDGET(m_pMenuButton
)));
6673 gtk_menu_button_set_popover(m_pMenuButton
, m_pPopover
);
6675 gtk_widget_show_all(m_pPopover
);
6679 void set_menu(weld::Menu
* pMenu
);
6681 virtual ~GtkInstanceMenuButton() override
6685 g_signal_handler_disconnect(m_pMenuButton
, m_nSignalId
);
6686 gtk_menu_button_set_popover(m_pMenuButton
, nullptr);
6687 gtk_widget_destroy(GTK_WIDGET(m_pMenuHack
));
6692 class GtkInstanceMenu
: public MenuHelper
, public virtual weld::Menu
6695 std::vector
<GtkMenuItem
*> m_aExtraItems
;
6696 OString m_sActivated
;
6697 GtkInstanceMenuButton
* m_pTopLevelMenuButton
;
6700 virtual void signal_activate(GtkMenuItem
* pItem
) override
6702 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pItem
));
6703 m_sActivated
= OString(pStr
, pStr
? strlen(pStr
) : 0);
6704 weld::Menu::signal_activate(m_sActivated
);
6709 if (m_aExtraItems
.empty())
6711 if (m_pTopLevelMenuButton
)
6713 for (auto a
: m_aExtraItems
)
6714 m_pTopLevelMenuButton
->remove_from_map(a
);
6716 m_aExtraItems
.clear();
6720 GtkInstanceMenu(GtkMenu
* pMenu
, bool bTakeOwnership
)
6721 : MenuHelper(pMenu
, bTakeOwnership
)
6722 , m_pTopLevelMenuButton(nullptr)
6724 // tdf#122527 if we're welding a submenu of a menu of a MenuButton,
6725 // then find that MenuButton parent so that when adding items to this
6726 // menu we can inform the MenuButton of their addition
6727 GtkMenu
* pTopLevelMenu
= pMenu
;
6730 GtkWidget
* pAttached
= gtk_menu_get_attach_widget(pTopLevelMenu
);
6731 if (!pAttached
|| !GTK_IS_MENU_ITEM(pAttached
))
6733 GtkWidget
* pParent
= gtk_widget_get_parent(pAttached
);
6734 if (!pParent
|| !GTK_IS_MENU(pParent
))
6736 pTopLevelMenu
= GTK_MENU(pParent
);
6738 if (pTopLevelMenu
!= pMenu
)
6740 GtkWidget
* pAttached
= gtk_menu_get_attach_widget(pTopLevelMenu
);
6741 if (pAttached
&& GTK_IS_MENU_BUTTON(pAttached
))
6743 void* pData
= g_object_get_data(G_OBJECT(pAttached
), "g-lo-GtkInstanceButton");
6744 m_pTopLevelMenuButton
= dynamic_cast<GtkInstanceMenuButton
*>(static_cast<GtkInstanceButton
*>(pData
));
6749 virtual OString
popup_at_rect(weld::Widget
* pParent
, const tools::Rectangle
&rRect
) override
6751 m_sActivated
.clear();
6753 GtkInstanceWidget
* pGtkWidget
= dynamic_cast<GtkInstanceWidget
*>(pParent
);
6756 GtkWidget
* pWidget
= pGtkWidget
->getWidget();
6757 gtk_menu_attach_to_widget(m_pMenu
, pWidget
, nullptr);
6759 //run in a sub main loop because we need to keep vcl PopupMenu alive to use
6760 //it during DispatchCommand, returning now to the outer loop causes the
6761 //launching PopupMenu to be destroyed, instead run the subloop here
6762 //until the gtk menu is destroyed
6763 GMainLoop
* pLoop
= g_main_loop_new(nullptr, true);
6764 gulong nSignalId
= g_signal_connect_swapped(G_OBJECT(m_pMenu
), "deactivate", G_CALLBACK(g_main_loop_quit
), pLoop
);
6766 #if GTK_CHECK_VERSION(3,22,0)
6767 if (gtk_check_version(3, 22, 0) == nullptr)
6769 GdkRectangle aRect
{static_cast<int>(rRect
.Left()), static_cast<int>(rRect
.Top()),
6770 static_cast<int>(rRect
.GetWidth()), static_cast<int>(rRect
.GetHeight())};
6771 if (SwapForRTL(pWidget
))
6772 aRect
.x
= gtk_widget_get_allocated_width(pWidget
) - aRect
.width
- 1 - aRect
.x
;
6774 // Send a keyboard event through gtk_main_do_event to toggle any active tooltip offs
6775 // before trying to launch the menu
6776 // https://gitlab.gnome.org/GNOME/gtk/issues/1785
6777 GdkEvent
*event
= GtkSalFrame::makeFakeKeyPress(pWidget
);
6778 gtk_main_do_event(event
);
6779 gdk_event_free(event
);
6781 gtk_menu_popup_at_rect(m_pMenu
, gtk_widget_get_window(pWidget
), &aRect
, GDK_GRAVITY_NORTH_WEST
, GDK_GRAVITY_NORTH_WEST
, nullptr);
6791 //typically there is an event, and we can then distinguish if this was
6792 //launched from the keyboard (gets auto-mnemoniced) or the mouse (which
6794 GdkEvent
*pEvent
= gtk_get_current_event();
6797 gdk_event_get_button(pEvent
, &nButton
);
6798 nTime
= gdk_event_get_time(pEvent
);
6803 nTime
= GtkSalFrame::GetLastInputEventTime();
6806 gtk_menu_popup(m_pMenu
, nullptr, nullptr, nullptr, nullptr, nButton
, nTime
);
6809 if (g_main_loop_is_running(pLoop
))
6811 gdk_threads_leave();
6812 g_main_loop_run(pLoop
);
6813 gdk_threads_enter();
6815 g_main_loop_unref(pLoop
);
6816 g_signal_handler_disconnect(m_pMenu
, nSignalId
);
6817 gtk_menu_detach(m_pMenu
);
6819 return m_sActivated
;
6822 virtual void set_sensitive(const OString
& rIdent
, bool bSensitive
) override
6824 set_item_sensitive(rIdent
, bSensitive
);
6827 virtual void set_active(const OString
& rIdent
, bool bActive
) override
6829 set_item_active(rIdent
, bActive
);
6832 virtual bool get_active(const OString
& rIdent
) const override
6834 return get_item_active(rIdent
);
6837 virtual void set_visible(const OString
& rIdent
, bool bShow
) override
6839 set_item_visible(rIdent
, bShow
);
6842 virtual void set_label(const OString
& rIdent
, const OUString
& rLabel
) override
6844 set_item_label(rIdent
, rLabel
);
6847 virtual void insert_separator(int pos
, const OUString
& rId
) override
6849 MenuHelper::insert_separator(pos
, rId
);
6852 virtual void clear() override
6858 virtual void insert(int pos
, const OUString
& rId
, const OUString
& rStr
,
6859 const OUString
* pIconName
, VirtualDevice
* pImageSurface
,
6860 bool bCheck
) override
6862 GtkWidget
* pImage
= nullptr;
6865 if (GdkPixbuf
* pixbuf
= load_icon_by_name(*pIconName
))
6867 pImage
= gtk_image_new_from_pixbuf(pixbuf
);
6868 g_object_unref(pixbuf
);
6871 else if (pImageSurface
)
6873 pImage
= image_new_from_virtual_device(*pImageSurface
);
6879 GtkWidget
*pBox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 6);
6880 GtkWidget
*pLabel
= gtk_label_new(MapToGtkAccelerator(rStr
).getStr());
6881 pItem
= bCheck
? gtk_check_menu_item_new() : gtk_menu_item_new();
6882 gtk_container_add(GTK_CONTAINER(pBox
), pImage
);
6883 gtk_container_add(GTK_CONTAINER(pBox
), pLabel
);
6884 gtk_container_add(GTK_CONTAINER(pItem
), pBox
);
6885 gtk_widget_show_all(pItem
);
6889 pItem
= bCheck
? gtk_check_menu_item_new_with_mnemonic(MapToGtkAccelerator(rStr
).getStr())
6890 : gtk_menu_item_new_with_mnemonic(MapToGtkAccelerator(rStr
).getStr());
6892 gtk_buildable_set_name(GTK_BUILDABLE(pItem
), OUStringToOString(rId
, RTL_TEXTENCODING_UTF8
).getStr());
6893 gtk_menu_shell_append(GTK_MENU_SHELL(m_pMenu
), pItem
);
6894 gtk_widget_show(pItem
);
6895 GtkMenuItem
* pMenuItem
= GTK_MENU_ITEM(pItem
);
6896 m_aExtraItems
.push_back(pMenuItem
);
6897 add_to_map(pMenuItem
);
6898 if (m_pTopLevelMenuButton
)
6899 m_pTopLevelMenuButton
->add_to_map(pMenuItem
);
6901 gtk_menu_reorder_child(m_pMenu
, pItem
, pos
);
6904 virtual ~GtkInstanceMenu() override
6912 vcl::ImageType
GtkToVcl(GtkIconSize eSize
)
6914 vcl::ImageType eRet
;
6917 case GTK_ICON_SIZE_MENU
:
6918 case GTK_ICON_SIZE_SMALL_TOOLBAR
:
6919 case GTK_ICON_SIZE_BUTTON
:
6920 eRet
= vcl::ImageType::Size16
;
6922 case GTK_ICON_SIZE_LARGE_TOOLBAR
:
6923 eRet
= vcl::ImageType::Size26
;
6925 case GTK_ICON_SIZE_DND
:
6926 case GTK_ICON_SIZE_DIALOG
:
6927 eRet
= vcl::ImageType::Size32
;
6930 case GTK_ICON_SIZE_INVALID
:
6931 eRet
= vcl::ImageType::Small
;
6938 void GtkInstanceMenuButton::set_menu(weld::Menu
* pMenu
)
6940 GtkInstanceMenu
* pPopoverWidget
= dynamic_cast<GtkInstanceMenu
*>(pMenu
);
6941 m_pPopover
= nullptr;
6942 GtkWidget
* pMenuWidget
= GTK_WIDGET(pPopoverWidget
? pPopoverWidget
->getMenu() : nullptr);
6943 gtk_menu_button_set_popup(m_pMenuButton
, pMenuWidget
);
6946 class GtkInstanceToolbar
: public GtkInstanceWidget
, public virtual weld::Toolbar
6949 GtkToolbar
* m_pToolbar
;
6951 std::map
<OString
, GtkToolButton
*> m_aMap
;
6952 std::map
<OString
, std::unique_ptr
<GtkInstanceMenuButton
>> m_aMenuButtonMap
;
6954 // at the time of writing there is no gtk_menu_tool_button_set_popover available
6955 // though there will be in the future
6956 // https://gitlab.gnome.org/GNOME/gtk/commit/03e30431a8af9a947a0c4ccab545f24da16bfe17?w=1
6957 static void find_menu_button(GtkWidget
*pWidget
, gpointer user_data
)
6959 if (g_strcmp0(gtk_widget_get_name(pWidget
), "GtkMenuButton") == 0)
6961 GtkWidget
**ppToggleButton
= static_cast<GtkWidget
**>(user_data
);
6962 *ppToggleButton
= pWidget
;
6964 else if (GTK_IS_CONTAINER(pWidget
))
6965 gtk_container_forall(GTK_CONTAINER(pWidget
), find_menu_button
, user_data
);
6968 static void collect(GtkWidget
* pItem
, gpointer widget
)
6970 if (GTK_IS_TOOL_BUTTON(pItem
))
6972 GtkToolButton
* pToolItem
= GTK_TOOL_BUTTON(pItem
);
6973 GtkInstanceToolbar
* pThis
= static_cast<GtkInstanceToolbar
*>(widget
);
6975 GtkMenuButton
* pMenuButton
= nullptr;
6976 if (GTK_IS_MENU_TOOL_BUTTON(pItem
))
6977 find_menu_button(pItem
, &pMenuButton
);
6979 pThis
->add_to_map(pToolItem
, pMenuButton
);
6983 void add_to_map(GtkToolButton
* pToolItem
, GtkMenuButton
* pMenuButton
)
6985 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pToolItem
));
6986 OString
id(pStr
, pStr
? strlen(pStr
) : 0);
6987 m_aMap
[id
] = pToolItem
;
6989 m_aMenuButtonMap
[id
] = std::make_unique
<GtkInstanceMenuButton
>(pMenuButton
, m_pBuilder
, false);
6990 g_signal_connect(pToolItem
, "clicked", G_CALLBACK(signalItemClicked
), this);
6993 static void signalItemClicked(GtkToolButton
* pItem
, gpointer widget
)
6995 GtkInstanceToolbar
* pThis
= static_cast<GtkInstanceToolbar
*>(widget
);
6996 SolarMutexGuard aGuard
;
6997 pThis
->signal_item_clicked(pItem
);
7000 void signal_item_clicked(GtkToolButton
* pItem
)
7002 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pItem
));
7003 signal_clicked(OString(pStr
, pStr
? strlen(pStr
) : 0));
7007 GtkInstanceToolbar(GtkToolbar
* pToolbar
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7008 : GtkInstanceWidget(GTK_WIDGET(pToolbar
), pBuilder
, bTakeOwnership
)
7009 , m_pToolbar(pToolbar
)
7011 gtk_container_foreach(GTK_CONTAINER(pToolbar
), collect
, this);
7014 void disable_item_notify_events()
7016 for (auto& a
: m_aMap
)
7017 g_signal_handlers_block_by_func(a
.second
, reinterpret_cast<void*>(signalItemClicked
), this);
7020 void enable_item_notify_events()
7022 for (auto& a
: m_aMap
)
7023 g_signal_handlers_unblock_by_func(a
.second
, reinterpret_cast<void*>(signalItemClicked
), this);
7026 virtual void set_item_sensitive(const OString
& rIdent
, bool bSensitive
) override
7028 disable_item_notify_events();
7029 gtk_widget_set_sensitive(GTK_WIDGET(m_aMap
[rIdent
]), bSensitive
);
7030 enable_item_notify_events();
7033 virtual bool get_item_sensitive(const OString
& rIdent
) const override
7035 return gtk_widget_get_sensitive(GTK_WIDGET(m_aMap
.find(rIdent
)->second
));
7038 virtual void set_item_active(const OString
& rIdent
, bool bActive
) override
7040 disable_item_notify_events();
7042 auto aFind
= m_aMenuButtonMap
.find(rIdent
);
7043 if (aFind
!= m_aMenuButtonMap
.end())
7044 aFind
->second
->set_active(bActive
);
7047 GtkToolButton
* pToolButton
= m_aMap
.find(rIdent
)->second
;
7048 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(pToolButton
), bActive
);
7051 enable_item_notify_events();
7054 virtual bool get_item_active(const OString
& rIdent
) const override
7056 auto aFind
= m_aMenuButtonMap
.find(rIdent
);
7057 if (aFind
!= m_aMenuButtonMap
.end())
7058 return aFind
->second
->get_active();
7060 GtkToolButton
* pToolButton
= m_aMap
.find(rIdent
)->second
;
7061 return gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(pToolButton
));
7064 virtual void insert_separator(int pos
, const OUString
& rId
) override
7066 GtkToolItem
* pItem
= gtk_separator_tool_item_new();
7067 gtk_buildable_set_name(GTK_BUILDABLE(pItem
), OUStringToOString(rId
, RTL_TEXTENCODING_UTF8
).getStr());
7068 gtk_toolbar_insert(m_pToolbar
, pItem
, pos
);
7069 gtk_widget_show(GTK_WIDGET(pItem
));
7072 virtual void set_item_popover(const OString
& rIdent
, weld::Widget
* pPopover
) override
7074 m_aMenuButtonMap
[rIdent
]->set_popover(pPopover
);
7077 virtual void set_item_menu(const OString
& rIdent
, weld::Menu
* pMenu
) override
7079 m_aMenuButtonMap
[rIdent
]->set_menu(pMenu
);
7082 virtual int get_n_items() const override
7084 return gtk_toolbar_get_n_items(m_pToolbar
);
7087 virtual OString
get_item_ident(int nIndex
) const override
7089 GtkToolItem
* pItem
= gtk_toolbar_get_nth_item(m_pToolbar
, nIndex
);
7090 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pItem
));
7091 return OString(pStr
, pStr
? strlen(pStr
) : 0);
7094 virtual void set_item_label(int nIndex
, const OUString
& rLabel
) override
7096 GtkToolItem
* pItem
= gtk_toolbar_get_nth_item(m_pToolbar
, nIndex
);
7097 gtk_tool_button_set_label(GTK_TOOL_BUTTON(pItem
), MapToGtkAccelerator(rLabel
).getStr());
7100 virtual void set_item_icon(int nIndex
, const css::uno::Reference
<css::graphic::XGraphic
>& rIcon
) override
7102 GtkToolItem
* pItem
= gtk_toolbar_get_nth_item(m_pToolbar
, nIndex
);
7104 GtkWidget
* pImage
= nullptr;
7105 if (GdkPixbuf
* pixbuf
= getPixbuf(rIcon
))
7107 pImage
= gtk_image_new_from_pixbuf(pixbuf
);
7108 g_object_unref(pixbuf
);
7111 gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(pItem
), pImage
);
7114 virtual void set_item_tooltip_text(int nIndex
, const OUString
& rTip
) override
7116 GtkToolItem
* pItem
= gtk_toolbar_get_nth_item(m_pToolbar
, nIndex
);
7117 gtk_widget_set_tooltip_text(GTK_WIDGET(pItem
), OUStringToOString(rTip
, RTL_TEXTENCODING_UTF8
).getStr());
7120 virtual vcl::ImageType
get_icon_size() const override
7122 return GtkToVcl(gtk_toolbar_get_icon_size(m_pToolbar
));
7125 virtual ~GtkInstanceToolbar() override
7127 for (auto& a
: m_aMap
)
7128 g_signal_handlers_disconnect_by_data(a
.second
, this);
7132 class GtkInstanceLinkButton
: public GtkInstanceContainer
, public virtual weld::LinkButton
7135 GtkLinkButton
* m_pButton
;
7138 static bool signalActivateLink(GtkButton
*, gpointer widget
)
7140 GtkInstanceLinkButton
* pThis
= static_cast<GtkInstanceLinkButton
*>(widget
);
7141 SolarMutexGuard aGuard
;
7142 return pThis
->signal_activate_link();
7146 GtkInstanceLinkButton(GtkLinkButton
* pButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7147 : GtkInstanceContainer(GTK_CONTAINER(pButton
), pBuilder
, bTakeOwnership
)
7148 , m_pButton(pButton
)
7149 , m_nSignalId(g_signal_connect(pButton
, "activate-link", G_CALLBACK(signalActivateLink
), this))
7153 virtual void set_label(const OUString
& rText
) override
7155 ::set_label(GTK_BUTTON(m_pButton
), rText
);
7158 virtual OUString
get_label() const override
7160 return ::get_label(GTK_BUTTON(m_pButton
));
7163 virtual void set_uri(const OUString
& rText
) override
7165 gtk_link_button_set_uri(m_pButton
, OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr());
7168 virtual OUString
get_uri() const override
7170 const gchar
* pStr
= gtk_link_button_get_uri(m_pButton
);
7171 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
7174 virtual void disable_notify_events() override
7176 g_signal_handler_block(m_pButton
, m_nSignalId
);
7177 GtkInstanceContainer::disable_notify_events();
7180 virtual void enable_notify_events() override
7182 GtkInstanceContainer::enable_notify_events();
7183 g_signal_handler_unblock(m_pButton
, m_nSignalId
);
7186 virtual ~GtkInstanceLinkButton() override
7188 g_signal_handler_disconnect(m_pButton
, m_nSignalId
);
7192 class GtkInstanceRadioButton
: public GtkInstanceToggleButton
, public virtual weld::RadioButton
7195 GtkInstanceRadioButton(GtkRadioButton
* pButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7196 : GtkInstanceToggleButton(GTK_TOGGLE_BUTTON(pButton
), pBuilder
, bTakeOwnership
)
7201 class GtkInstanceCheckButton
: public GtkInstanceToggleButton
, public virtual weld::CheckButton
7204 GtkInstanceCheckButton(GtkCheckButton
* pButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7205 : GtkInstanceToggleButton(GTK_TOGGLE_BUTTON(pButton
), pBuilder
, bTakeOwnership
)
7210 class GtkInstanceScale
: public GtkInstanceWidget
, public virtual weld::Scale
7214 gulong m_nValueChangedSignalId
;
7216 static void signalValueChanged(GtkScale
*, gpointer widget
)
7218 GtkInstanceScale
* pThis
= static_cast<GtkInstanceScale
*>(widget
);
7219 SolarMutexGuard aGuard
;
7220 pThis
->signal_value_changed();
7224 GtkInstanceScale(GtkScale
* pScale
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7225 : GtkInstanceWidget(GTK_WIDGET(pScale
), pBuilder
, bTakeOwnership
)
7227 , m_nValueChangedSignalId(g_signal_connect(m_pScale
, "value-changed", G_CALLBACK(signalValueChanged
), this))
7231 virtual void disable_notify_events() override
7233 g_signal_handler_block(m_pScale
, m_nValueChangedSignalId
);
7234 GtkInstanceWidget::disable_notify_events();
7237 virtual void enable_notify_events() override
7239 GtkInstanceWidget::enable_notify_events();
7240 g_signal_handler_unblock(m_pScale
, m_nValueChangedSignalId
);
7243 virtual void set_value(int value
) override
7245 disable_notify_events();
7246 gtk_range_set_value(GTK_RANGE(m_pScale
), value
);
7247 enable_notify_events();
7250 virtual void set_range(int min
, int max
) override
7252 disable_notify_events();
7253 gtk_range_set_range(GTK_RANGE(m_pScale
), min
, max
);
7254 enable_notify_events();
7257 virtual int get_value() const override
7259 return gtk_range_get_value(GTK_RANGE(m_pScale
));
7262 virtual ~GtkInstanceScale() override
7264 g_signal_handler_disconnect(m_pScale
, m_nValueChangedSignalId
);
7268 class GtkInstanceProgressBar
: public GtkInstanceWidget
, public virtual weld::ProgressBar
7271 GtkProgressBar
* m_pProgressBar
;
7274 GtkInstanceProgressBar(GtkProgressBar
* pProgressBar
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7275 : GtkInstanceWidget(GTK_WIDGET(pProgressBar
), pBuilder
, bTakeOwnership
)
7276 , m_pProgressBar(pProgressBar
)
7280 virtual void set_percentage(int value
) override
7282 gtk_progress_bar_set_fraction(m_pProgressBar
, value
/ 100.0);
7285 virtual OUString
get_text() const override
7287 const gchar
* pText
= gtk_progress_bar_get_text(m_pProgressBar
);
7288 OUString
sRet(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
7292 virtual void set_text(const OUString
& rText
) override
7294 gtk_progress_bar_set_text(m_pProgressBar
, OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr());
7298 class GtkInstanceSpinner
: public GtkInstanceWidget
, public virtual weld::Spinner
7301 GtkSpinner
* m_pSpinner
;
7304 GtkInstanceSpinner(GtkSpinner
* pSpinner
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7305 : GtkInstanceWidget(GTK_WIDGET(pSpinner
), pBuilder
, bTakeOwnership
)
7306 , m_pSpinner(pSpinner
)
7310 virtual void start() override
7312 gtk_spinner_start(m_pSpinner
);
7315 virtual void stop() override
7317 gtk_spinner_stop(m_pSpinner
);
7321 class GtkInstanceImage
: public GtkInstanceWidget
, public virtual weld::Image
7327 GtkInstanceImage(GtkImage
* pImage
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7328 : GtkInstanceWidget(GTK_WIDGET(pImage
), pBuilder
, bTakeOwnership
)
7333 virtual void set_from_icon_name(const OUString
& rIconName
) override
7335 GdkPixbuf
* pixbuf
= load_icon_by_name(rIconName
);
7338 gtk_image_set_from_pixbuf(m_pImage
, pixbuf
);
7339 g_object_unref(pixbuf
);
7342 virtual void set_image(VirtualDevice
* pDevice
) override
7344 if (gtk_check_version(3, 20, 0) == nullptr)
7347 gtk_image_set_from_surface(m_pImage
, get_underlying_cairo_surface(*pDevice
));
7349 gtk_image_set_from_surface(m_pImage
, nullptr);
7353 GdkPixbuf
* pixbuf
= pDevice
? getPixbuf(*pDevice
) : nullptr;
7354 gtk_image_set_from_pixbuf(m_pImage
, pixbuf
);
7356 g_object_unref(pixbuf
);
7359 virtual void set_image(const css::uno::Reference
<css::graphic::XGraphic
>& rImage
) override
7361 GdkPixbuf
* pixbuf
= getPixbuf(rImage
);
7362 gtk_image_set_from_pixbuf(m_pImage
, pixbuf
);
7364 g_object_unref(pixbuf
);
7368 class GtkInstanceCalendar
: public GtkInstanceWidget
, public virtual weld::Calendar
7371 GtkCalendar
* m_pCalendar
;
7372 gulong m_nDaySelectedSignalId
;
7373 gulong m_nDaySelectedDoubleClickSignalId
;
7374 gulong m_nKeyPressEventSignalId
;
7376 static void signalDaySelected(GtkCalendar
*, gpointer widget
)
7378 GtkInstanceCalendar
* pThis
= static_cast<GtkInstanceCalendar
*>(widget
);
7379 pThis
->signal_selected();
7382 static void signalDaySelectedDoubleClick(GtkCalendar
*, gpointer widget
)
7384 GtkInstanceCalendar
* pThis
= static_cast<GtkInstanceCalendar
*>(widget
);
7385 pThis
->signal_activated();
7388 bool signal_key_press(GdkEventKey
* pEvent
)
7390 if (pEvent
->keyval
== GDK_KEY_Return
)
7398 static gboolean
signalKeyPress(GtkWidget
*, GdkEventKey
* pEvent
, gpointer widget
)
7400 GtkInstanceCalendar
* pThis
= static_cast<GtkInstanceCalendar
*>(widget
);
7401 return pThis
->signal_key_press(pEvent
);
7405 GtkInstanceCalendar(GtkCalendar
* pCalendar
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7406 : GtkInstanceWidget(GTK_WIDGET(pCalendar
), pBuilder
, bTakeOwnership
)
7407 , m_pCalendar(pCalendar
)
7408 , m_nDaySelectedSignalId(g_signal_connect(pCalendar
, "day-selected", G_CALLBACK(signalDaySelected
), this))
7409 , m_nDaySelectedDoubleClickSignalId(g_signal_connect(pCalendar
, "day-selected-double-click", G_CALLBACK(signalDaySelectedDoubleClick
), this))
7410 , m_nKeyPressEventSignalId(g_signal_connect(pCalendar
, "key-press-event", G_CALLBACK(signalKeyPress
), this))
7414 virtual void set_date(const Date
& rDate
) override
7416 disable_notify_events();
7417 gtk_calendar_select_month(m_pCalendar
, rDate
.GetMonth() - 1, rDate
.GetYear());
7418 gtk_calendar_select_day(m_pCalendar
, rDate
.GetDay());
7419 enable_notify_events();
7422 virtual Date
get_date() const override
7424 guint year
, month
, day
;
7425 gtk_calendar_get_date(m_pCalendar
, &year
, &month
, &day
);
7426 return Date(day
, month
+ 1, year
);
7429 virtual void disable_notify_events() override
7431 g_signal_handler_block(m_pCalendar
, m_nDaySelectedDoubleClickSignalId
);
7432 g_signal_handler_block(m_pCalendar
, m_nDaySelectedSignalId
);
7433 GtkInstanceWidget::disable_notify_events();
7436 virtual void enable_notify_events() override
7438 GtkInstanceWidget::enable_notify_events();
7439 g_signal_handler_unblock(m_pCalendar
, m_nDaySelectedSignalId
);
7440 g_signal_handler_unblock(m_pCalendar
, m_nDaySelectedDoubleClickSignalId
);
7443 virtual ~GtkInstanceCalendar() override
7445 g_signal_handler_disconnect(m_pCalendar
, m_nKeyPressEventSignalId
);
7446 g_signal_handler_disconnect(m_pCalendar
, m_nDaySelectedDoubleClickSignalId
);
7447 g_signal_handler_disconnect(m_pCalendar
, m_nDaySelectedSignalId
);
7453 PangoAttrList
* create_attr_list(const vcl::Font
& rFont
)
7455 PangoAttrList
* pAttrList
= pango_attr_list_new();
7456 pango_attr_list_insert(pAttrList
, pango_attr_family_new(OUStringToOString(rFont
.GetFamilyName(), RTL_TEXTENCODING_UTF8
).getStr()));
7457 pango_attr_list_insert(pAttrList
, pango_attr_size_new(rFont
.GetFontSize().Height() * PANGO_SCALE
));
7458 switch (rFont
.GetItalic())
7461 pango_attr_list_insert(pAttrList
, pango_attr_style_new(PANGO_STYLE_NORMAL
));
7464 pango_attr_list_insert(pAttrList
, pango_attr_style_new(PANGO_STYLE_ITALIC
));
7466 case ITALIC_OBLIQUE
:
7467 pango_attr_list_insert(pAttrList
, pango_attr_style_new(PANGO_STYLE_OBLIQUE
));
7472 switch (rFont
.GetWeight())
7474 case WEIGHT_ULTRALIGHT
:
7475 pango_attr_list_insert(pAttrList
, pango_attr_weight_new(PANGO_WEIGHT_ULTRALIGHT
));
7478 pango_attr_list_insert(pAttrList
, pango_attr_weight_new(PANGO_WEIGHT_LIGHT
));
7481 pango_attr_list_insert(pAttrList
, pango_attr_weight_new(PANGO_WEIGHT_NORMAL
));
7484 pango_attr_list_insert(pAttrList
, pango_attr_weight_new(PANGO_WEIGHT_BOLD
));
7486 case WEIGHT_ULTRABOLD
:
7487 pango_attr_list_insert(pAttrList
, pango_attr_weight_new(PANGO_WEIGHT_ULTRABOLD
));
7492 switch (rFont
.GetWidthType())
7494 case WIDTH_ULTRA_CONDENSED
:
7495 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_ULTRA_CONDENSED
));
7497 case WIDTH_EXTRA_CONDENSED
:
7498 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_EXTRA_CONDENSED
));
7500 case WIDTH_CONDENSED
:
7501 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_CONDENSED
));
7503 case WIDTH_SEMI_CONDENSED
:
7504 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_SEMI_CONDENSED
));
7507 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_NORMAL
));
7509 case WIDTH_SEMI_EXPANDED
:
7510 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_SEMI_EXPANDED
));
7512 case WIDTH_EXPANDED
:
7513 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_EXPANDED
));
7515 case WIDTH_EXTRA_EXPANDED
:
7516 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_EXTRA_EXPANDED
));
7518 case WIDTH_ULTRA_EXPANDED
:
7519 pango_attr_list_insert(pAttrList
, pango_attr_stretch_new(PANGO_STRETCH_ULTRA_EXPANDED
));
7530 void set_entry_message_type(GtkEntry
* pEntry
, weld::EntryMessageType eType
)
7532 if (eType
== weld::EntryMessageType::Error
)
7533 gtk_entry_set_icon_from_icon_name(pEntry
, GTK_ENTRY_ICON_SECONDARY
, "dialog-error");
7534 else if (eType
== weld::EntryMessageType::Warning
)
7535 gtk_entry_set_icon_from_icon_name(pEntry
, GTK_ENTRY_ICON_SECONDARY
, "dialog-warning");
7537 gtk_entry_set_icon_from_icon_name(pEntry
, GTK_ENTRY_ICON_SECONDARY
, nullptr);
7541 class GtkInstanceEntry
: public GtkInstanceWidget
, public virtual weld::Entry
7545 gulong m_nChangedSignalId
;
7546 gulong m_nInsertTextSignalId
;
7547 gulong m_nCursorPosSignalId
;
7548 gulong m_nSelectionPosSignalId
;
7549 gulong m_nActivateSignalId
;
7551 static void signalChanged(GtkEntry
*, gpointer widget
)
7553 GtkInstanceEntry
* pThis
= static_cast<GtkInstanceEntry
*>(widget
);
7554 SolarMutexGuard aGuard
;
7555 pThis
->signal_changed();
7558 static void signalInsertText(GtkEntry
* pEntry
, const gchar
* pNewText
, gint nNewTextLength
,
7559 gint
* position
, gpointer widget
)
7561 GtkInstanceEntry
* pThis
= static_cast<GtkInstanceEntry
*>(widget
);
7562 SolarMutexGuard aGuard
;
7563 pThis
->signal_insert_text(pEntry
, pNewText
, nNewTextLength
, position
);
7566 void signal_insert_text(GtkEntry
* pEntry
, const gchar
* pNewText
, gint nNewTextLength
, gint
* position
)
7568 if (!m_aInsertTextHdl
.IsSet())
7570 OUString
sText(pNewText
, nNewTextLength
, RTL_TEXTENCODING_UTF8
);
7571 const bool bContinue
= m_aInsertTextHdl
.Call(sText
);
7572 if (bContinue
&& !sText
.isEmpty())
7574 OString
sFinalText(OUStringToOString(sText
, RTL_TEXTENCODING_UTF8
));
7575 g_signal_handlers_block_by_func(pEntry
, gpointer(signalInsertText
), this);
7576 gtk_editable_insert_text(GTK_EDITABLE(pEntry
), sFinalText
.getStr(), sFinalText
.getLength(), position
);
7577 g_signal_handlers_unblock_by_func(pEntry
, gpointer(signalInsertText
), this);
7579 g_signal_stop_emission_by_name(pEntry
, "insert-text");
7582 static void signalCursorPosition(GtkEntry
*, GParamSpec
*, gpointer widget
)
7584 GtkInstanceEntry
* pThis
= static_cast<GtkInstanceEntry
*>(widget
);
7585 pThis
->signal_cursor_position();
7588 static void signalActivate(GtkEntry
*, gpointer widget
)
7590 GtkInstanceEntry
* pThis
= static_cast<GtkInstanceEntry
*>(widget
);
7591 pThis
->signal_activate();
7594 void signal_activate()
7596 if (m_aActivateHdl
.IsSet())
7598 SolarMutexGuard aGuard
;
7599 if (m_aActivateHdl
.Call(*this))
7600 g_signal_stop_emission_by_name(m_pEntry
, "activate");
7605 GtkInstanceEntry(GtkEntry
* pEntry
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
7606 : GtkInstanceWidget(GTK_WIDGET(pEntry
), pBuilder
, bTakeOwnership
)
7608 , m_nChangedSignalId(g_signal_connect(pEntry
, "changed", G_CALLBACK(signalChanged
), this))
7609 , m_nInsertTextSignalId(g_signal_connect(pEntry
, "insert-text", G_CALLBACK(signalInsertText
), this))
7610 , m_nCursorPosSignalId(g_signal_connect(pEntry
, "notify::cursor-position", G_CALLBACK(signalCursorPosition
), this))
7611 , m_nSelectionPosSignalId(g_signal_connect(pEntry
, "notify::selection-bound", G_CALLBACK(signalCursorPosition
), this))
7612 , m_nActivateSignalId(g_signal_connect(pEntry
, "activate", G_CALLBACK(signalActivate
), this))
7616 virtual void set_text(const OUString
& rText
) override
7618 disable_notify_events();
7619 gtk_entry_set_text(m_pEntry
, OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr());
7620 enable_notify_events();
7623 virtual OUString
get_text() const override
7625 const gchar
* pText
= gtk_entry_get_text(m_pEntry
);
7626 OUString
sRet(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
7630 virtual void set_width_chars(int nChars
) override
7632 disable_notify_events();
7633 gtk_entry_set_width_chars(m_pEntry
, nChars
);
7634 gtk_entry_set_max_width_chars(m_pEntry
, nChars
);
7635 enable_notify_events();
7638 virtual int get_width_chars() const override
7640 return gtk_entry_get_width_chars(m_pEntry
);
7643 virtual void set_max_length(int nChars
) override
7645 disable_notify_events();
7646 gtk_entry_set_max_length(m_pEntry
, nChars
);
7647 enable_notify_events();
7650 virtual void select_region(int nStartPos
, int nEndPos
) override
7652 disable_notify_events();
7653 gtk_editable_select_region(GTK_EDITABLE(m_pEntry
), nStartPos
, nEndPos
);
7654 enable_notify_events();
7657 bool get_selection_bounds(int& rStartPos
, int& rEndPos
) override
7659 return gtk_editable_get_selection_bounds(GTK_EDITABLE(m_pEntry
), &rStartPos
, &rEndPos
);
7662 virtual void replace_selection(const OUString
& rText
) override
7664 gtk_editable_delete_selection(GTK_EDITABLE(m_pEntry
));
7665 OString
sText(OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
));
7666 gint position
= gtk_editable_get_position(GTK_EDITABLE(m_pEntry
));
7667 gtk_editable_insert_text(GTK_EDITABLE(m_pEntry
), sText
.getStr(), sText
.getLength(),
7671 virtual void set_position(int nCursorPos
) override
7673 disable_notify_events();
7674 gtk_editable_set_position(GTK_EDITABLE(m_pEntry
), nCursorPos
);
7675 enable_notify_events();
7678 virtual int get_position() const override
7680 return gtk_editable_get_position(GTK_EDITABLE(m_pEntry
));
7683 virtual void set_editable(bool bEditable
) override
7685 gtk_editable_set_editable(GTK_EDITABLE(m_pEntry
), bEditable
);
7688 virtual bool get_editable() const override
7690 return gtk_editable_get_editable(GTK_EDITABLE(m_pEntry
));
7693 virtual void set_message_type(weld::EntryMessageType eType
) override
7695 ::set_entry_message_type(m_pEntry
, eType
);
7698 virtual void disable_notify_events() override
7700 g_signal_handler_block(m_pEntry
, m_nActivateSignalId
);
7701 g_signal_handler_block(m_pEntry
, m_nSelectionPosSignalId
);
7702 g_signal_handler_block(m_pEntry
, m_nCursorPosSignalId
);
7703 g_signal_handler_block(m_pEntry
, m_nInsertTextSignalId
);
7704 g_signal_handler_block(m_pEntry
, m_nChangedSignalId
);
7705 GtkInstanceWidget::disable_notify_events();
7708 virtual void enable_notify_events() override
7710 GtkInstanceWidget::enable_notify_events();
7711 g_signal_handler_unblock(m_pEntry
, m_nChangedSignalId
);
7712 g_signal_handler_unblock(m_pEntry
, m_nInsertTextSignalId
);
7713 g_signal_handler_unblock(m_pEntry
, m_nCursorPosSignalId
);
7714 g_signal_handler_unblock(m_pEntry
, m_nSelectionPosSignalId
);
7715 g_signal_handler_unblock(m_pEntry
, m_nActivateSignalId
);
7718 virtual void set_font(const vcl::Font
& rFont
) override
7720 PangoAttrList
* pAttrList
= create_attr_list(rFont
);
7721 gtk_entry_set_attributes(m_pEntry
, pAttrList
);
7722 pango_attr_list_unref(pAttrList
);
7725 void fire_signal_changed()
7730 virtual void cut_clipboard() override
7732 gtk_editable_cut_clipboard(GTK_EDITABLE(m_pEntry
));
7735 virtual void copy_clipboard() override
7737 gtk_editable_copy_clipboard(GTK_EDITABLE(m_pEntry
));
7740 virtual void paste_clipboard() override
7742 gtk_editable_paste_clipboard(GTK_EDITABLE(m_pEntry
));
7745 virtual ~GtkInstanceEntry() override
7747 g_signal_handler_disconnect(m_pEntry
, m_nActivateSignalId
);
7748 g_signal_handler_disconnect(m_pEntry
, m_nSelectionPosSignalId
);
7749 g_signal_handler_disconnect(m_pEntry
, m_nCursorPosSignalId
);
7750 g_signal_handler_disconnect(m_pEntry
, m_nInsertTextSignalId
);
7751 g_signal_handler_disconnect(m_pEntry
, m_nChangedSignalId
);
7762 Search(const OUString
& rText
, int nCol
)
7763 : str(OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
))
7770 gboolean
foreach_find(GtkTreeModel
* model
, GtkTreePath
* path
, GtkTreeIter
* iter
, gpointer data
)
7772 Search
* search
= static_cast<Search
*>(data
);
7773 gchar
*pStr
= nullptr;
7774 gtk_tree_model_get(model
, iter
, search
->col
, &pStr
, -1);
7775 bool found
= strcmp(pStr
, search
->str
.getStr()) == 0;
7779 gint
* indices
= gtk_tree_path_get_indices_with_depth(path
, &depth
);
7780 search
->index
= indices
[depth
-1];
7786 GdkPixbuf
* getPixbuf(const OUString
& rIconName
)
7788 if (rIconName
.isEmpty())
7791 GdkPixbuf
* pixbuf
= nullptr;
7793 if (rIconName
.lastIndexOf('.') != rIconName
.getLength() - 4)
7795 assert((rIconName
== "dialog-warning" || rIconName
== "dialog-error" || rIconName
== "dialog-information") &&
7796 "unknown stock image");
7798 GError
*error
= nullptr;
7799 GtkIconTheme
*icon_theme
= gtk_icon_theme_get_default();
7800 pixbuf
= gtk_icon_theme_load_icon(icon_theme
, OUStringToOString(rIconName
, RTL_TEXTENCODING_UTF8
).getStr(),
7801 16, GTK_ICON_LOOKUP_USE_BUILTIN
, &error
);
7805 const AllSettings
& rSettings
= Application::GetSettings();
7806 pixbuf
= load_icon_by_name(rIconName
,
7807 rSettings
.GetStyleSettings().DetermineIconTheme(),
7808 rSettings
.GetUILanguageTag().getBcp47());
7814 void insert_row(GtkListStore
* pListStore
, GtkTreeIter
& iter
, int pos
, const OUString
* pId
, const OUString
& rText
, const OUString
* pIconName
, const VirtualDevice
* pDevice
)
7816 if (!pIconName
&& !pDevice
)
7818 gtk_list_store_insert_with_values(pListStore
, &iter
, pos
,
7819 0, OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr(),
7820 1, !pId
? nullptr : OUStringToOString(*pId
, RTL_TEXTENCODING_UTF8
).getStr(),
7827 GdkPixbuf
* pixbuf
= getPixbuf(*pIconName
);
7829 gtk_list_store_insert_with_values(pListStore
, &iter
, pos
,
7830 0, OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr(),
7831 1, !pId
? nullptr : OUStringToOString(*pId
, RTL_TEXTENCODING_UTF8
).getStr(),
7836 g_object_unref(pixbuf
);
7840 cairo_surface_t
* surface
= get_underlying_cairo_surface(*pDevice
);
7842 Size
aSize(pDevice
->GetOutputSizePixel());
7843 cairo_surface_t
* target
= cairo_surface_create_similar(surface
,
7844 cairo_surface_get_content(surface
),
7848 cairo_t
* cr
= cairo_create(target
);
7849 cairo_set_source_surface(cr
, surface
, 0, 0);
7853 gtk_list_store_insert_with_values(pListStore
, &iter
, pos
,
7854 0, OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr(),
7855 1, !pId
? nullptr : OUStringToOString(*pId
, RTL_TEXTENCODING_UTF8
).getStr(),
7858 cairo_surface_destroy(target
);
7866 gint
default_sort_func(GtkTreeModel
* pModel
, GtkTreeIter
* a
, GtkTreeIter
* b
, gpointer data
)
7868 comphelper::string::NaturalStringSorter
* pSorter
= static_cast<comphelper::string::NaturalStringSorter
*>(data
);
7871 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(pModel
);
7872 gint
sort_column_id(0);
7873 gtk_tree_sortable_get_sort_column_id(pSortable
, &sort_column_id
, nullptr);
7874 gtk_tree_model_get(pModel
, a
, sort_column_id
, &pName1
, -1);
7875 gtk_tree_model_get(pModel
, b
, sort_column_id
, &pName2
, -1);
7876 gint ret
= pSorter
->compare(OUString(pName1
, pName1
? strlen(pName1
) : 0, RTL_TEXTENCODING_UTF8
),
7877 OUString(pName2
, pName2
? strlen(pName2
) : 0, RTL_TEXTENCODING_UTF8
));
7883 int starts_with(GtkTreeModel
* pTreeModel
, const OUString
& rStr
, int col
, int nStartRow
, bool bCaseSensitive
)
7886 if (!gtk_tree_model_iter_nth_child(pTreeModel
, &iter
, nullptr, nStartRow
))
7889 const vcl::I18nHelper
& rI18nHelper
= Application::GetSettings().GetUILocaleI18nHelper();
7890 int nRet
= nStartRow
;
7894 gtk_tree_model_get(pTreeModel
, &iter
, col
, &pStr
, -1);
7895 OUString
aStr(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
7897 const bool bMatch
= !bCaseSensitive
? rI18nHelper
.MatchString(rStr
, aStr
) : aStr
.startsWith(rStr
);
7901 } while (gtk_tree_model_iter_next(pTreeModel
, &iter
));
7907 struct GtkInstanceTreeIter
: public weld::TreeIter
7909 GtkInstanceTreeIter(const GtkInstanceTreeIter
* pOrig
)
7914 memset(&iter
, 0, sizeof(iter
));
7916 GtkInstanceTreeIter(const GtkTreeIter
& rOrig
)
7918 memcpy(&iter
, &rOrig
, sizeof(iter
));
7920 virtual bool equal(const TreeIter
& rOther
) const override
7922 return memcmp(&iter
, &static_cast<const GtkInstanceTreeIter
&>(rOther
).iter
, sizeof(GtkTreeIter
)) == 0;
7927 class GtkInstanceTreeView
;
7929 static GtkInstanceTreeView
* g_DragSource
;
7931 class GtkInstanceTreeView
: public GtkInstanceContainer
, public virtual weld::TreeView
7934 GtkTreeView
* m_pTreeView
;
7935 GtkTreeStore
* m_pTreeStore
;
7936 std::unique_ptr
<comphelper::string::NaturalStringSorter
> m_xSorter
;
7938 std::vector
<gulong
> m_aColumnSignalIds
;
7939 // map from toggle column to toggle visibility column
7940 std::map
<int, int> m_aToggleVisMap
;
7941 // map from toggle column to tristate column
7942 std::map
<int, int> m_aToggleTriStateMap
;
7943 // map from text column to text weight column
7944 std::map
<int, int> m_aWeightMap
;
7945 // map from text column to sensitive column
7946 std::map
<int, int> m_aSensitiveMap
;
7947 std::vector
<GtkSortType
> m_aSavedSortTypes
;
7948 std::vector
<int> m_aSavedSortColumns
;
7949 std::vector
<int> m_aViewColToModelCol
;
7950 std::vector
<int> m_aModelColToViewCol
;
7951 bool m_bWorkAroundBadDragRegion
;
7955 gint m_nExpanderImageCol
;
7957 gulong m_nChangedSignalId
;
7958 gulong m_nRowActivatedSignalId
;
7959 gulong m_nTestExpandRowSignalId
;
7960 gulong m_nVAdjustmentChangedSignalId
;
7961 gulong m_nRowDeletedSignalId
;
7962 gulong m_nRowInsertedSignalId
;
7963 gulong m_nPopupMenuSignalId
;
7964 gulong m_nDragBeginSignalId
;
7965 gulong m_nDragEndSignalId
;
7966 gulong m_nKeyPressSignalId
;
7967 ImplSVEvent
* m_pChangeEvent
;
7969 DECL_LINK(async_signal_changed
, void*, void);
7971 void launch_signal_changed()
7973 //tdf#117991 selection change is sent before the focus change, and focus change
7974 //is what will cause a spinbutton that currently has the focus to set its contents
7975 //as the spin button value. So any LibreOffice callbacks on
7976 //signal-change would happen before the spinbutton value-change occurs.
7977 //To avoid this, send the signal-change to LibreOffice to occur after focus-change
7978 //has been processed
7980 Application::RemoveUserEvent(m_pChangeEvent
);
7981 m_pChangeEvent
= Application::PostUserEvent(LINK(this, GtkInstanceTreeView
, async_signal_changed
));
7984 static void signalChanged(GtkTreeView
*, gpointer widget
)
7986 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
7987 pThis
->launch_signal_changed();
7990 void handle_row_activated()
7992 if (signal_row_activated())
7994 GtkInstanceTreeIter
aIter(nullptr);
7995 if (!get_cursor(&aIter
))
7997 if (iter_has_child(aIter
))
7998 get_row_expanded(aIter
) ? collapse_row(aIter
) : expand_row(aIter
);
8001 static void signalRowActivated(GtkTreeView
*, GtkTreePath
*, GtkTreeViewColumn
*, gpointer widget
)
8003 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8004 SolarMutexGuard aGuard
;
8005 pThis
->handle_row_activated();
8008 virtual bool signal_popup_menu(const CommandEvent
& rCEvt
) override
8010 return m_aPopupMenuHdl
.Call(rCEvt
);
8013 void insert_row(GtkTreeIter
& iter
, const GtkTreeIter
* parent
, int pos
, const OUString
* pId
, const OUString
* pText
,
8014 const OUString
* pIconName
, const VirtualDevice
* pDevice
, const OUString
* pExpanderName
)
8016 gtk_tree_store_insert_with_values(m_pTreeStore
, &iter
, const_cast<GtkTreeIter
*>(parent
), pos
,
8017 m_nTextCol
, !pText
? nullptr : OUStringToOString(*pText
, RTL_TEXTENCODING_UTF8
).getStr(),
8018 m_nIdCol
, !pId
? nullptr : OUStringToOString(*pId
, RTL_TEXTENCODING_UTF8
).getStr(),
8022 GdkPixbuf
* pixbuf
= getPixbuf(*pIconName
);
8023 gtk_tree_store_set(m_pTreeStore
, &iter
, m_nImageCol
, pixbuf
, -1);
8025 g_object_unref(pixbuf
);
8029 cairo_surface_t
* surface
= get_underlying_cairo_surface(*pDevice
);
8031 Size
aSize(pDevice
->GetOutputSizePixel());
8032 cairo_surface_t
* target
= cairo_surface_create_similar(surface
,
8033 cairo_surface_get_content(surface
),
8037 cairo_t
* cr
= cairo_create(target
);
8038 cairo_set_source_surface(cr
, surface
, 0, 0);
8042 gtk_tree_store_set(m_pTreeStore
, &iter
, m_nImageCol
, target
, -1);
8043 cairo_surface_destroy(target
);
8048 GdkPixbuf
* pixbuf
= getPixbuf(*pExpanderName
);
8049 gtk_tree_store_set(m_pTreeStore
, &iter
, m_nExpanderImageCol
, pixbuf
, -1);
8051 g_object_unref(pixbuf
);
8055 OUString
get(const GtkTreeIter
& iter
, int col
) const
8057 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8059 gtk_tree_model_get(pModel
, const_cast<GtkTreeIter
*>(&iter
), col
, &pStr
, -1);
8060 OUString
sRet(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
8065 OUString
get(int pos
, int col
) const
8068 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8070 if (gtk_tree_model_iter_nth_child(pModel
, &iter
, nullptr, pos
))
8071 sRet
= get(iter
, col
);
8075 gint
get_int(const GtkTreeIter
& iter
, int col
) const
8078 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8079 gtk_tree_model_get(pModel
, const_cast<GtkTreeIter
*>(&iter
), col
, &nRet
, -1);
8083 gint
get_int(int pos
, int col
) const
8086 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8088 if (gtk_tree_model_iter_nth_child(pModel
, &iter
, nullptr, pos
))
8089 nRet
= get_int(iter
, col
);
8090 gtk_tree_model_get(pModel
, &iter
, col
, &nRet
, -1);
8094 bool get_bool(const GtkTreeIter
& iter
, int col
) const
8096 gboolean
bRet(false);
8097 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8098 gtk_tree_model_get(pModel
, const_cast<GtkTreeIter
*>(&iter
), col
, &bRet
, -1);
8102 bool get_bool(int pos
, int col
) const
8105 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8107 if (gtk_tree_model_iter_nth_child(pModel
, &iter
, nullptr, pos
))
8108 bRet
= get_bool(iter
, col
);
8112 void set(const GtkTreeIter
& iter
, int col
, const OUString
& rText
)
8114 OString
aStr(OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
));
8115 gtk_tree_store_set(m_pTreeStore
, const_cast<GtkTreeIter
*>(&iter
), col
, aStr
.getStr(), -1);
8118 void set(int pos
, int col
, const OUString
& rText
)
8120 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8122 if (gtk_tree_model_iter_nth_child(pModel
, &iter
, nullptr, pos
))
8123 set(iter
, col
, rText
);
8126 void set(const GtkTreeIter
& iter
, int col
, bool bOn
)
8128 gtk_tree_store_set(m_pTreeStore
, const_cast<GtkTreeIter
*>(&iter
), col
, bOn
, -1);
8131 void set(int pos
, int col
, bool bOn
)
8133 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8135 if (gtk_tree_model_iter_nth_child(pModel
, &iter
, nullptr, pos
))
8136 set(iter
, col
, bOn
);
8139 void set(const GtkTreeIter
& iter
, int col
, gint bInt
)
8141 gtk_tree_store_set(m_pTreeStore
, const_cast<GtkTreeIter
*>(&iter
), col
, bInt
, -1);
8144 void set(int pos
, int col
, gint bInt
)
8146 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8148 if (gtk_tree_model_iter_nth_child(pModel
, &iter
, nullptr, pos
))
8149 set(iter
, col
, bInt
);
8152 static gboolean
signalTestExpandRow(GtkTreeView
*, GtkTreeIter
* iter
, GtkTreePath
*, gpointer widget
)
8154 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8155 return !pThis
->signal_test_expand_row(*iter
);
8158 bool signal_test_expand_row(GtkTreeIter
& iter
)
8160 disable_notify_events();
8161 GtkInstanceTreeIter
aIter(nullptr);
8163 // if there's a preexisting placeholder child, required to make this
8164 // potentially expandable in the first place, now we remove it
8165 bool bPlaceHolder
= false;
8166 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8168 if (gtk_tree_model_iter_children(pModel
, &tmp
, &iter
))
8171 if (get_text(aIter
, -1) == "<dummy>")
8173 gtk_tree_store_remove(m_pTreeStore
, &tmp
);
8174 bPlaceHolder
= true;
8179 bool bRet
= signal_expanding(aIter
);
8181 //expand disallowed, restore placeholder
8182 if (!bRet
&& bPlaceHolder
)
8184 GtkTreeIter subiter
;
8185 OUString
sDummy("<dummy>");
8186 insert_row(subiter
, &iter
, -1, nullptr, &sDummy
, nullptr, nullptr, nullptr);
8189 enable_notify_events();
8193 static void signalCellToggled(GtkCellRendererToggle
* pCell
, const gchar
*path
, gpointer widget
)
8195 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8196 void* pData
= g_object_get_data(G_OBJECT(pCell
), "g-lo-CellIndex");
8197 pThis
->signal_cell_toggled(path
, reinterpret_cast<sal_IntPtr
>(pData
));
8200 void signal_cell_toggled(const gchar
*path
, int nCol
)
8202 GtkTreePath
*tree_path
= gtk_tree_path_new_from_string(path
);
8204 // toggled signal handlers can query get_cursor to get which
8206 gtk_tree_view_set_cursor(m_pTreeView
, tree_path
, nullptr, false);
8208 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8210 gtk_tree_model_get_iter(pModel
, &iter
, tree_path
);
8212 gboolean
bRet(false);
8213 gtk_tree_model_get(pModel
, &iter
, nCol
, &bRet
, -1);
8215 gtk_tree_store_set(m_pTreeStore
, &iter
, nCol
, bRet
, -1);
8218 gint
* indices
= gtk_tree_path_get_indices_with_depth(tree_path
, &depth
);
8219 int nRow
= indices
[depth
-1];
8221 set(iter
, m_aToggleTriStateMap
[nCol
], false);
8223 signal_toggled(std::make_pair(nRow
, nCol
));
8225 gtk_tree_path_free(tree_path
);
8228 DECL_LINK(async_stop_cell_editing
, void*, void);
8230 static void signalCellEditingStarted(GtkCellRenderer
*, GtkCellEditable
*, const gchar
*path
, gpointer widget
)
8232 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8233 if (!pThis
->signal_cell_editing_started(path
))
8234 Application::PostUserEvent(LINK(pThis
, GtkInstanceTreeView
, async_stop_cell_editing
));
8237 bool signal_cell_editing_started(const gchar
*path
)
8239 GtkTreePath
*tree_path
= gtk_tree_path_new_from_string(path
);
8241 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8242 GtkInstanceTreeIter
aGtkIter(nullptr);
8243 gtk_tree_model_get_iter(pModel
, &aGtkIter
.iter
, tree_path
);
8244 gtk_tree_path_free(tree_path
);
8246 return signal_editing_started(aGtkIter
);
8249 static void signalCellEdited(GtkCellRendererText
* pCell
, const gchar
*path
, const gchar
*pNewText
, gpointer widget
)
8251 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8252 pThis
->signal_cell_edited(pCell
, path
, pNewText
);
8255 static void restoreNonEditable(GObject
* pCell
)
8257 if (g_object_get_data(pCell
, "g-lo-RestoreNonEditable"))
8259 g_object_set(pCell
, "editable", false, "editable-set", false, nullptr);
8260 g_object_set_data(pCell
, "g-lo-RestoreNonEditable", reinterpret_cast<gpointer
>(false));
8264 void signal_cell_edited(GtkCellRendererText
* pCell
, const gchar
*path
, const gchar
* pNewText
)
8266 GtkTreePath
*tree_path
= gtk_tree_path_new_from_string(path
);
8268 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8269 GtkInstanceTreeIter
aGtkIter(nullptr);
8270 gtk_tree_model_get_iter(pModel
, &aGtkIter
.iter
, tree_path
);
8271 gtk_tree_path_free(tree_path
);
8273 OUString
sText(pNewText
, pNewText
? strlen(pNewText
) : 0, RTL_TEXTENCODING_UTF8
);
8274 if (signal_editing_done(std::pair
<const weld::TreeIter
&, OUString
>(aGtkIter
, sText
)))
8276 void* pData
= g_object_get_data(G_OBJECT(pCell
), "g-lo-CellIndex");
8277 set(aGtkIter
.iter
, reinterpret_cast<sal_IntPtr
>(pData
), sText
);
8280 restoreNonEditable(G_OBJECT(pCell
));
8283 static void signalCellEditingCanceled(GtkCellRenderer
* pCell
, gpointer
/*widget*/)
8285 restoreNonEditable(G_OBJECT(pCell
));
8288 void signal_column_clicked(GtkTreeViewColumn
* pClickedColumn
)
8291 for (GList
* pEntry
= g_list_first(m_pColumns
); pEntry
; pEntry
= g_list_next(pEntry
))
8293 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(pEntry
->data
);
8294 if (pColumn
== pClickedColumn
)
8296 TreeView::signal_column_clicked(nIndex
);
8303 static void signalColumnClicked(GtkTreeViewColumn
* pColumn
, gpointer widget
)
8305 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8306 pThis
->signal_column_clicked(pColumn
);
8309 static void signalVAdjustmentChanged(GtkAdjustment
*, gpointer widget
)
8311 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8312 pThis
->signal_visible_range_changed();
8315 int get_model_col(int viewcol
) const
8317 return m_aViewColToModelCol
[viewcol
];
8320 int get_view_col(int modelcol
) const
8322 return m_aModelColToViewCol
[modelcol
];
8325 static void signalRowDeleted(GtkTreeModel
*, GtkTreePath
*, gpointer widget
)
8327 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8328 pThis
->signal_model_changed();
8331 static void signalRowInserted(GtkTreeModel
*, GtkTreePath
*, GtkTreeIter
*, gpointer widget
)
8333 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8334 pThis
->signal_model_changed();
8337 static gint
sortFunc(GtkTreeModel
* pModel
, GtkTreeIter
* a
, GtkTreeIter
* b
, gpointer widget
)
8339 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8340 return pThis
->sort_func(pModel
, a
, b
);
8343 gint
sort_func(GtkTreeModel
* pModel
, GtkTreeIter
* a
, GtkTreeIter
* b
)
8346 return m_aCustomSort(GtkInstanceTreeIter(*a
), GtkInstanceTreeIter(*b
));
8347 return default_sort_func(pModel
, a
, b
, m_xSorter
.get());
8350 static void signalDragBegin(GtkWidget
*, GdkDragContext
*, gpointer widget
)
8352 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8353 g_DragSource
= pThis
;
8356 static void signalDragEnd(GtkWidget
*, GdkDragContext
*, gpointer
)
8358 g_DragSource
= nullptr;
8361 bool signal_key_press(GdkEventKey
* pEvent
)
8363 if (pEvent
->keyval
!= GDK_KEY_Left
&& pEvent
->keyval
!= GDK_KEY_Right
)
8366 GtkInstanceTreeIter
aIter(nullptr);
8367 if (!get_cursor(&aIter
))
8370 if (pEvent
->keyval
== GDK_KEY_Right
)
8372 if (iter_has_child(aIter
) && !get_row_expanded(aIter
))
8380 if (iter_has_child(aIter
) && get_row_expanded(aIter
))
8382 collapse_row(aIter
);
8386 if (iter_parent(aIter
))
8397 static gboolean
signalKeyPress(GtkWidget
*, GdkEventKey
* pEvent
, gpointer widget
)
8399 GtkInstanceTreeView
* pThis
= static_cast<GtkInstanceTreeView
*>(widget
);
8400 return pThis
->signal_key_press(pEvent
);
8404 GtkInstanceTreeView(GtkTreeView
* pTreeView
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
8405 : GtkInstanceContainer(GTK_CONTAINER(pTreeView
), pBuilder
, bTakeOwnership
)
8406 , m_pTreeView(pTreeView
)
8407 , m_pTreeStore(GTK_TREE_STORE(gtk_tree_view_get_model(m_pTreeView
)))
8408 , m_bWorkAroundBadDragRegion(false)
8412 , m_nExpanderImageCol(-1)
8413 , m_nChangedSignalId(g_signal_connect(gtk_tree_view_get_selection(pTreeView
), "changed",
8414 G_CALLBACK(signalChanged
), this))
8415 , m_nRowActivatedSignalId(g_signal_connect(pTreeView
, "row-activated", G_CALLBACK(signalRowActivated
), this))
8416 , m_nTestExpandRowSignalId(g_signal_connect(pTreeView
, "test-expand-row", G_CALLBACK(signalTestExpandRow
), this))
8417 , m_nVAdjustmentChangedSignalId(0)
8418 , m_nPopupMenuSignalId(g_signal_connect(pTreeView
, "popup-menu", G_CALLBACK(signalPopupMenu
), this))
8419 , m_nDragBeginSignalId(g_signal_connect(pTreeView
, "drag-begin", G_CALLBACK(signalDragBegin
), this))
8420 , m_nDragEndSignalId(g_signal_connect(pTreeView
, "drag-end", G_CALLBACK(signalDragEnd
), this))
8421 , m_nKeyPressSignalId(g_signal_connect(pTreeView
, "key-press-event", G_CALLBACK(signalKeyPress
), this))
8422 , m_pChangeEvent(nullptr)
8424 m_pColumns
= gtk_tree_view_get_columns(m_pTreeView
);
8426 for (GList
* pEntry
= g_list_first(m_pColumns
); pEntry
; pEntry
= g_list_next(pEntry
))
8428 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(pEntry
->data
);
8429 m_aColumnSignalIds
.push_back(g_signal_connect(pColumn
, "clicked", G_CALLBACK(signalColumnClicked
), this));
8430 GList
*pRenderers
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(pColumn
));
8431 for (GList
* pRenderer
= g_list_first(pRenderers
); pRenderer
; pRenderer
= g_list_next(pRenderer
))
8433 GtkCellRenderer
* pCellRenderer
= GTK_CELL_RENDERER(pRenderer
->data
);
8434 g_object_set_data(G_OBJECT(pCellRenderer
), "g-lo-CellIndex", reinterpret_cast<gpointer
>(nIndex
));
8435 if (GTK_IS_CELL_RENDERER_TEXT(pCellRenderer
))
8437 if (m_nTextCol
== -1)
8438 m_nTextCol
= nIndex
;
8439 m_aWeightMap
[nIndex
] = -1;
8440 m_aSensitiveMap
[nIndex
] = -1;
8441 g_signal_connect(G_OBJECT(pCellRenderer
), "editing-started", G_CALLBACK(signalCellEditingStarted
), this);
8442 g_signal_connect(G_OBJECT(pCellRenderer
), "editing-canceled", G_CALLBACK(signalCellEditingCanceled
), this);
8443 g_signal_connect(G_OBJECT(pCellRenderer
), "edited", G_CALLBACK(signalCellEdited
), this);
8445 else if (GTK_IS_CELL_RENDERER_TOGGLE(pCellRenderer
))
8447 g_signal_connect(G_OBJECT(pCellRenderer
), "toggled", G_CALLBACK(signalCellToggled
), this);
8448 m_aToggleVisMap
[nIndex
] = -1;
8449 m_aToggleTriStateMap
[nIndex
] = -1;
8451 else if (GTK_IS_CELL_RENDERER_PIXBUF(pCellRenderer
))
8453 const bool bExpander
= g_list_next(pRenderer
) != nullptr;
8454 if (bExpander
&& m_nExpanderImageCol
== -1)
8455 m_nExpanderImageCol
= nIndex
;
8456 else if (m_nImageCol
== -1)
8457 m_nImageCol
= nIndex
;
8459 m_aModelColToViewCol
.push_back(m_aViewColToModelCol
.size());
8462 g_list_free(pRenderers
);
8463 m_aViewColToModelCol
.push_back(nIndex
- 1);
8466 m_nIdCol
= nIndex
++;
8468 for (auto& a
: m_aToggleVisMap
)
8469 a
.second
= nIndex
++;
8470 for (auto& a
: m_aToggleTriStateMap
)
8471 a
.second
= nIndex
++;
8472 for (auto& a
: m_aWeightMap
)
8473 a
.second
= nIndex
++;
8474 for (auto& a
: m_aSensitiveMap
)
8475 a
.second
= nIndex
++;
8477 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8478 m_nRowDeletedSignalId
= g_signal_connect(pModel
, "row-deleted", G_CALLBACK(signalRowDeleted
), this);
8479 m_nRowInsertedSignalId
= g_signal_connect(pModel
, "row-inserted", G_CALLBACK(signalRowInserted
), this);
8482 virtual void columns_autosize() override
8484 gtk_tree_view_columns_autosize(m_pTreeView
);
8487 virtual void set_column_fixed_widths(const std::vector
<int>& rWidths
) override
8489 GList
* pEntry
= g_list_first(m_pColumns
);
8490 for (auto nWidth
: rWidths
)
8492 assert(pEntry
&& "wrong count");
8493 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(pEntry
->data
);
8494 gtk_tree_view_column_set_fixed_width(pColumn
, nWidth
);
8495 pEntry
= g_list_next(pEntry
);
8499 virtual void set_centered_column(int nCol
) override
8501 for (GList
* pEntry
= g_list_first(m_pColumns
); pEntry
; pEntry
= g_list_next(pEntry
))
8503 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(pEntry
->data
);
8504 GList
*pRenderers
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(pColumn
));
8505 for (GList
* pRenderer
= g_list_first(pRenderers
); pRenderer
; pRenderer
= g_list_next(pRenderer
))
8507 GtkCellRenderer
* pCellRenderer
= GTK_CELL_RENDERER(pRenderer
->data
);
8508 void* pData
= g_object_get_data(G_OBJECT(pCellRenderer
), "g-lo-CellIndex");
8509 if (reinterpret_cast<sal_IntPtr
>(pData
) == nCol
)
8511 g_object_set(G_OBJECT(pCellRenderer
), "xalign", 0.5, nullptr);
8515 g_list_free(pRenderers
);
8519 virtual int get_column_width(int nColumn
) const override
8521 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(g_list_nth_data(m_pColumns
, nColumn
));
8522 assert(pColumn
&& "wrong count");
8523 int nWidth
= gtk_tree_view_column_get_width(pColumn
);
8524 // https://github.com/exaile/exaile/issues/580
8525 // after setting fixed_width on a column and requesting width before
8526 // gtk has a chance to do its layout of the column means that the width
8527 // request hasn't come into effect
8529 nWidth
= gtk_tree_view_column_get_fixed_width(pColumn
);
8533 virtual OUString
get_column_title(int nColumn
) const override
8535 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(g_list_nth_data(m_pColumns
, nColumn
));
8536 assert(pColumn
&& "wrong count");
8537 const gchar
* pTitle
= gtk_tree_view_column_get_title(pColumn
);
8538 OUString
sRet(pTitle
, pTitle
? strlen(pTitle
) : 0, RTL_TEXTENCODING_UTF8
);
8542 virtual void set_column_title(int nColumn
, const OUString
& rTitle
) override
8544 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(g_list_nth_data(m_pColumns
, nColumn
));
8545 assert(pColumn
&& "wrong count");
8546 gtk_tree_view_column_set_title(pColumn
, OUStringToOString(rTitle
, RTL_TEXTENCODING_UTF8
).getStr());
8549 virtual void insert(const weld::TreeIter
* pParent
, int pos
, const OUString
* pText
, const OUString
* pId
, const OUString
* pIconName
,
8550 VirtualDevice
* pImageSurface
, const OUString
* pExpanderName
,
8551 bool bChildrenOnDemand
, weld::TreeIter
* pRet
) override
8553 disable_notify_events();
8555 const GtkInstanceTreeIter
* pGtkIter
= static_cast<const GtkInstanceTreeIter
*>(pParent
);
8556 insert_row(iter
, pGtkIter
? &pGtkIter
->iter
: nullptr, pos
, pId
, pText
, pIconName
, pImageSurface
, pExpanderName
);
8557 if (bChildrenOnDemand
)
8559 GtkTreeIter subiter
;
8560 OUString
sDummy("<dummy>");
8561 insert_row(subiter
, &iter
, -1, nullptr, &sDummy
, nullptr, nullptr, nullptr);
8565 GtkInstanceTreeIter
* pGtkRetIter
= static_cast<GtkInstanceTreeIter
*>(pRet
);
8566 pGtkRetIter
->iter
= iter
;
8568 enable_notify_events();
8571 void set_font_color(const GtkTreeIter
& iter
, const Color
& rColor
) const
8573 GdkRGBA aColor
{rColor
.GetRed()/255.0, rColor
.GetGreen()/255.0, rColor
.GetBlue()/255.0, 0};
8574 gtk_tree_store_set(m_pTreeStore
, const_cast<GtkTreeIter
*>(&iter
), m_nIdCol
+ 1, &aColor
, -1);
8577 virtual void set_font_color(int pos
, const Color
& rColor
) const override
8580 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pTreeStore
), &iter
, nullptr, pos
);
8581 set_font_color(iter
, rColor
);
8584 virtual void set_font_color(const weld::TreeIter
& rIter
, const Color
& rColor
) const override
8586 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
8587 set_font_color(rGtkIter
.iter
, rColor
);
8590 virtual void remove(int pos
) override
8592 disable_notify_events();
8594 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pTreeStore
), &iter
, nullptr, pos
);
8595 gtk_tree_store_remove(m_pTreeStore
, &iter
);
8596 enable_notify_events();
8599 virtual int find_text(const OUString
& rText
) const override
8601 Search
aSearch(rText
, m_nTextCol
);
8602 gtk_tree_model_foreach(GTK_TREE_MODEL(m_pTreeStore
), foreach_find
, &aSearch
);
8603 return aSearch
.index
;
8606 virtual int find_id(const OUString
& rId
) const override
8608 Search
aSearch(rId
, m_nIdCol
);
8609 gtk_tree_model_foreach(GTK_TREE_MODEL(m_pTreeStore
), foreach_find
, &aSearch
);
8610 return aSearch
.index
;
8613 virtual void bulk_insert_for_each(int nSourceCount
, const std::function
<void(weld::TreeIter
&, int nSourceIndex
)>& func
,
8614 const std::vector
<int>* pFixedWidths
) override
8618 GtkInstanceTreeIter
aGtkIter(nullptr);
8621 set_column_fixed_widths(*pFixedWidths
);
8623 while (nSourceCount
)
8625 // tdf#125241 inserting backwards is massively faster
8626 gtk_tree_store_prepend(m_pTreeStore
, &aGtkIter
.iter
, nullptr);
8627 func(aGtkIter
, --nSourceCount
);
8633 virtual void swap(int pos1
, int pos2
) override
8635 disable_notify_events();
8637 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8640 gtk_tree_model_iter_nth_child(pModel
, &iter1
, nullptr, pos1
);
8643 gtk_tree_model_iter_nth_child(pModel
, &iter2
, nullptr, pos2
);
8645 gtk_tree_store_swap(m_pTreeStore
, &iter1
, &iter2
);
8647 enable_notify_events();
8650 virtual void clear() override
8652 disable_notify_events();
8653 gtk_tree_store_clear(m_pTreeStore
);
8654 enable_notify_events();
8657 virtual void make_sorted() override
8659 // thaw wants to restore sort state of freeze
8660 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't select when frozen");
8661 m_xSorter
.reset(new comphelper::string::NaturalStringSorter(
8662 ::comphelper::getProcessComponentContext(),
8663 Application::GetSettings().GetUILanguageTag().getLocale()));
8664 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
8665 gtk_tree_sortable_set_sort_func(pSortable
, m_nTextCol
, sortFunc
, this, nullptr);
8666 gtk_tree_sortable_set_sort_column_id(pSortable
, m_nTextCol
, GTK_SORT_ASCENDING
);
8669 virtual void make_unsorted() override
8673 GtkSortType eSortType
;
8674 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
8675 gtk_tree_sortable_get_sort_column_id(pSortable
, &nSortColumn
, &eSortType
);
8676 gtk_tree_sortable_set_sort_column_id(pSortable
, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
, eSortType
);
8679 virtual void set_sort_order(bool bAscending
) override
8681 GtkSortType eSortType
= bAscending
? GTK_SORT_ASCENDING
: GTK_SORT_DESCENDING
;
8683 gint
sort_column_id(0);
8684 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
8685 gtk_tree_sortable_get_sort_column_id(pSortable
, &sort_column_id
, nullptr);
8686 gtk_tree_sortable_set_sort_column_id(pSortable
, sort_column_id
, eSortType
);
8689 virtual bool get_sort_order() const override
8691 GtkSortType eSortType
;
8693 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
8694 gtk_tree_sortable_get_sort_column_id(pSortable
, nullptr, &eSortType
);
8695 return eSortType
== GTK_SORT_ASCENDING
;
8698 virtual void set_sort_indicator(TriState eState
, int col
) override
8701 col
= get_view_col(m_nTextCol
);
8703 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(g_list_nth_data(m_pColumns
, col
));
8704 assert(pColumn
&& "wrong count");
8705 if (eState
== TRISTATE_INDET
)
8706 gtk_tree_view_column_set_sort_indicator(pColumn
, false);
8709 gtk_tree_view_column_set_sort_indicator(pColumn
, true);
8710 GtkSortType eSortType
= eState
== TRISTATE_TRUE
? GTK_SORT_ASCENDING
: GTK_SORT_DESCENDING
;
8711 gtk_tree_view_column_set_sort_order(pColumn
, eSortType
);
8715 virtual TriState
get_sort_indicator(int col
) const override
8718 col
= get_view_col(m_nTextCol
);
8720 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(g_list_nth_data(m_pColumns
, col
));
8721 if (!gtk_tree_view_column_get_sort_indicator(pColumn
))
8722 return TRISTATE_INDET
;
8723 return gtk_tree_view_column_get_sort_order(pColumn
) == GTK_SORT_ASCENDING
? TRISTATE_TRUE
: TRISTATE_FALSE
;
8726 virtual int get_sort_column() const override
8728 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
8729 gint
sort_column_id(0);
8730 if (!gtk_tree_sortable_get_sort_column_id(pSortable
, &sort_column_id
, nullptr))
8732 return get_view_col(sort_column_id
);
8735 virtual void set_sort_column(int nColumn
) override
8742 GtkSortType eSortType
;
8743 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
8744 gtk_tree_sortable_get_sort_column_id(pSortable
, nullptr, &eSortType
);
8745 int nSortCol
= get_model_col(nColumn
);
8746 gtk_tree_sortable_set_sort_func(pSortable
, nSortCol
, sortFunc
, this, nullptr);
8747 gtk_tree_sortable_set_sort_column_id(pSortable
, nSortCol
, eSortType
);
8750 virtual void set_sort_func(const std::function
<int(const weld::TreeIter
&, const weld::TreeIter
&)>& func
) override
8752 weld::TreeView::set_sort_func(func
);
8753 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
8754 gtk_tree_sortable_sort_column_changed(pSortable
);
8757 virtual int n_children() const override
8759 return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_pTreeStore
), nullptr);
8762 virtual void select(int pos
) override
8764 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't select when frozen");
8765 disable_notify_events();
8766 if (pos
== -1 || (pos
== 0 && n_children() == 0))
8768 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(m_pTreeView
));
8772 GtkTreePath
* path
= gtk_tree_path_new_from_indices(pos
, -1);
8773 gtk_tree_selection_select_path(gtk_tree_view_get_selection(m_pTreeView
), path
);
8774 gtk_tree_view_scroll_to_cell(m_pTreeView
, path
, nullptr, false, 0, 0);
8775 gtk_tree_path_free(path
);
8777 enable_notify_events();
8780 virtual void set_cursor(int pos
) override
8782 GtkTreePath
* path
= gtk_tree_path_new_from_indices(pos
, -1);
8783 gtk_tree_view_set_cursor(m_pTreeView
, path
, nullptr, false);
8784 gtk_tree_path_free(path
);
8787 virtual void scroll_to_row(int pos
) override
8789 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't select when frozen");
8790 disable_notify_events();
8791 GtkTreePath
* path
= gtk_tree_path_new_from_indices(pos
, -1);
8792 gtk_tree_view_scroll_to_cell(m_pTreeView
, path
, nullptr, false, 0, 0);
8793 gtk_tree_path_free(path
);
8794 enable_notify_events();
8797 virtual bool is_selected(int pos
) const override
8800 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(m_pTreeStore
), &iter
, nullptr, pos
);
8801 return gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(m_pTreeView
), &iter
);
8804 virtual void unselect(int pos
) override
8806 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't select when frozen");
8807 disable_notify_events();
8808 if (pos
== -1 || (pos
== 0 && n_children() == 0))
8810 gtk_tree_selection_select_all(gtk_tree_view_get_selection(m_pTreeView
));
8814 GtkTreePath
* path
= gtk_tree_path_new_from_indices(pos
, -1);
8815 gtk_tree_selection_unselect_path(gtk_tree_view_get_selection(m_pTreeView
), path
);
8816 gtk_tree_path_free(path
);
8818 enable_notify_events();
8821 virtual std::vector
<int> get_selected_rows() const override
8823 std::vector
<int> aRows
;
8825 GList
* pList
= gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(m_pTreeView
), nullptr);
8826 for (GList
* pItem
= g_list_first(pList
); pItem
; pItem
= g_list_next(pItem
))
8828 GtkTreePath
* path
= static_cast<GtkTreePath
*>(pItem
->data
);
8831 gint
* indices
= gtk_tree_path_get_indices_with_depth(path
, &depth
);
8832 int nRow
= indices
[depth
-1];
8834 aRows
.push_back(nRow
);
8836 g_list_free_full(pList
, reinterpret_cast<GDestroyNotify
>(gtk_tree_path_free
));
8841 virtual void all_foreach(const std::function
<bool(weld::TreeIter
&)>& func
) override
8843 GtkInstanceTreeIter
aGtkIter(nullptr);
8844 if (get_iter_first(aGtkIter
))
8850 } while (iter_next(aGtkIter
));
8854 virtual void selected_foreach(const std::function
<bool(weld::TreeIter
&)>& func
) override
8856 GtkInstanceTreeIter
aGtkIter(nullptr);
8858 GtkTreeModel
* pModel
;
8859 GList
* pList
= gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(m_pTreeView
), &pModel
);
8860 for (GList
* pItem
= g_list_first(pList
); pItem
; pItem
= g_list_next(pItem
))
8862 GtkTreePath
* path
= static_cast<GtkTreePath
*>(pItem
->data
);
8863 gtk_tree_model_get_iter(pModel
, &aGtkIter
.iter
, path
);
8867 g_list_free_full(pList
, reinterpret_cast<GDestroyNotify
>(gtk_tree_path_free
));
8870 virtual void visible_foreach(const std::function
<bool(weld::TreeIter
&)>& func
) override
8872 GtkTreePath
* start_path
;
8873 GtkTreePath
* end_path
;
8875 if (gtk_tree_view_get_visible_range(m_pTreeView
, &start_path
, &end_path
))
8877 GtkInstanceTreeIter
aGtkIter(nullptr);
8878 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
8879 gtk_tree_model_get_iter(pModel
, &aGtkIter
.iter
, start_path
);
8885 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, &aGtkIter
.iter
);
8886 bool bContinue
= gtk_tree_path_compare(path
, end_path
) != 0;
8887 gtk_tree_path_free(path
);
8890 if (!iter_next(aGtkIter
))
8894 gtk_tree_path_free(start_path
);
8895 gtk_tree_path_free(end_path
);
8899 virtual void connect_visible_range_changed(const Link
<weld::TreeView
&, void>& rLink
) override
8901 weld::TreeView::connect_visible_range_changed(rLink
);
8902 GtkAdjustment
* pVAdjustment
= gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(m_pTreeView
));
8903 m_nVAdjustmentChangedSignalId
= g_signal_connect(pVAdjustment
, "value-changed", G_CALLBACK(signalVAdjustmentChanged
), this);
8906 virtual bool is_selected(const weld::TreeIter
& rIter
) const override
8908 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
8909 return gtk_tree_selection_iter_is_selected(gtk_tree_view_get_selection(m_pTreeView
), const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
8912 virtual OUString
get_text(int pos
, int col
) const override
8915 return get(pos
, m_nTextCol
);
8916 return get(pos
, get_model_col(col
));
8919 virtual void set_text(int pos
, const OUString
& rText
, int col
) override
8924 col
= get_model_col(col
);
8925 set(pos
, col
, rText
);
8928 virtual TriState
get_toggle(int pos
, int col
) const override
8930 col
= get_model_col(col
);
8931 if (get_bool(pos
, m_aToggleTriStateMap
.find(col
)->second
))
8932 return TRISTATE_INDET
;
8933 return get_bool(pos
, col
) ? TRISTATE_TRUE
: TRISTATE_FALSE
;
8936 virtual TriState
get_toggle(const weld::TreeIter
& rIter
, int col
) const override
8938 col
= get_model_col(col
);
8939 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
8940 if (get_bool(rGtkIter
.iter
, m_aToggleTriStateMap
.find(col
)->second
))
8941 return TRISTATE_INDET
;
8942 return get_bool(rGtkIter
.iter
, col
) ? TRISTATE_TRUE
: TRISTATE_FALSE
;
8945 virtual void set_toggle(int pos
, TriState eState
, int col
) override
8947 col
= get_model_col(col
);
8948 // checkbuttons are invisible until toggled on or off
8949 set(pos
, m_aToggleVisMap
[col
], true);
8950 if (eState
== TRISTATE_INDET
)
8951 set(pos
, m_aToggleTriStateMap
[col
], true);
8954 set(pos
, m_aToggleTriStateMap
[col
], false);
8955 set(pos
, col
, eState
== TRISTATE_TRUE
);
8959 virtual void set_toggle(const weld::TreeIter
& rIter
, TriState eState
, int col
) override
8961 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
8962 col
= get_model_col(col
);
8963 // checkbuttons are invisible until toggled on or off
8964 set(rGtkIter
.iter
, m_aToggleVisMap
[col
], true);
8965 if (eState
== TRISTATE_INDET
)
8966 set(rGtkIter
.iter
, m_aToggleTriStateMap
[col
], true);
8969 set(rGtkIter
.iter
, m_aToggleTriStateMap
[col
], false);
8970 set(rGtkIter
.iter
, col
, eState
== TRISTATE_TRUE
);
8974 virtual void set_text_emphasis(const weld::TreeIter
& rIter
, bool bOn
, int col
) override
8976 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
8977 col
= get_model_col(col
);
8978 set(rGtkIter
.iter
, m_aWeightMap
[col
], bOn
? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL
);
8981 virtual void set_text_emphasis(int pos
, bool bOn
, int col
) override
8983 col
= get_model_col(col
);
8984 set(pos
, m_aWeightMap
[col
], bOn
? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL
);
8987 virtual bool get_text_emphasis(const weld::TreeIter
& rIter
, int col
) const override
8989 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
8990 col
= get_model_col(col
);
8991 return get_int(rGtkIter
.iter
, m_aWeightMap
.find(col
)->second
) == PANGO_WEIGHT_BOLD
;
8994 virtual bool get_text_emphasis(int pos
, int col
) const override
8996 col
= get_model_col(col
);
8997 return get_int(pos
, m_aWeightMap
.find(col
)->second
) == PANGO_WEIGHT_BOLD
;
9000 using GtkInstanceWidget::set_sensitive
;
9002 virtual void set_sensitive(int pos
, bool bSensitive
, int col
) override
9007 col
= get_model_col(col
);
9008 set(pos
, m_aSensitiveMap
[col
], bSensitive
);
9011 virtual void set_sensitive(const weld::TreeIter
& rIter
, bool bSensitive
, int col
) override
9016 col
= get_model_col(col
);
9017 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9018 set(rGtkIter
.iter
, m_aSensitiveMap
[col
], bSensitive
);
9021 void set_image(const GtkTreeIter
& iter
, int col
, GdkPixbuf
* pixbuf
)
9024 col
= m_nExpanderImageCol
;
9026 col
= get_model_col(col
);
9027 gtk_tree_store_set(m_pTreeStore
, const_cast<GtkTreeIter
*>(&iter
), col
, pixbuf
, -1);
9029 g_object_unref(pixbuf
);
9032 void set_image(int pos
, GdkPixbuf
* pixbuf
, int col
)
9034 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9036 if (gtk_tree_model_iter_nth_child(pModel
, &iter
, nullptr, pos
))
9038 set_image(iter
, col
, pixbuf
);
9042 virtual void set_image(int pos
, const css::uno::Reference
<css::graphic::XGraphic
>& rImage
, int col
) override
9044 set_image(pos
, getPixbuf(rImage
), col
);
9047 virtual void set_image(int pos
, const OUString
& rImage
, int col
) override
9049 set_image(pos
, getPixbuf(rImage
), col
);
9052 virtual void set_image(int pos
, VirtualDevice
& rImage
, int col
) override
9054 set_image(pos
, getPixbuf(rImage
), col
);
9057 virtual void set_image(const weld::TreeIter
& rIter
, const css::uno::Reference
<css::graphic::XGraphic
>& rImage
, int col
) override
9059 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9060 set_image(rGtkIter
.iter
, col
, getPixbuf(rImage
));
9063 virtual void set_image(const weld::TreeIter
& rIter
, const OUString
& rImage
, int col
) override
9065 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9066 set_image(rGtkIter
.iter
, col
, getPixbuf(rImage
));
9069 virtual OUString
get_id(int pos
) const override
9071 return get(pos
, m_nIdCol
);
9074 virtual void set_id(int pos
, const OUString
& rId
) override
9076 return set(pos
, m_nIdCol
, rId
);
9079 virtual int get_iter_index_in_parent(const weld::TreeIter
& rIter
) const override
9081 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9083 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9084 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9087 gint
* indices
= gtk_tree_path_get_indices_with_depth(path
, &depth
);
9088 int nRet
= indices
[depth
-1];
9090 gtk_tree_path_free(path
);
9095 virtual int iter_compare(const weld::TreeIter
& a
, const weld::TreeIter
& b
) const override
9097 const GtkInstanceTreeIter
& rGtkIterA
= static_cast<const GtkInstanceTreeIter
&>(a
);
9098 const GtkInstanceTreeIter
& rGtkIterB
= static_cast<const GtkInstanceTreeIter
&>(b
);
9100 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9101 GtkTreePath
* pathA
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIterA
.iter
));
9102 GtkTreePath
* pathB
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIterB
.iter
));
9104 int nRet
= gtk_tree_path_compare(pathA
, pathB
);
9106 gtk_tree_path_free(pathB
);
9107 gtk_tree_path_free(pathA
);
9112 // by copy and delete of old copy
9113 void move_subtree(GtkTreeIter
& rFromIter
, GtkTreeIter
* pGtkParentIter
, int nIndexInNewParent
)
9115 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9117 int nCols
= gtk_tree_model_get_n_columns(pModel
);
9121 gtk_tree_store_insert(m_pTreeStore
, &toiter
, pGtkParentIter
, nIndexInNewParent
);
9123 for (int i
= 0; i
< nCols
; ++i
)
9125 memset(&value
, 0, sizeof(GValue
));
9126 gtk_tree_model_get_value(pModel
, &rFromIter
, i
, &value
);
9127 gtk_tree_store_set_value(m_pTreeStore
, &toiter
, i
, &value
);
9128 g_value_unset(&value
);
9131 GtkTreeIter tmpfromiter
;
9132 if (gtk_tree_model_iter_children(pModel
, &tmpfromiter
, &rFromIter
))
9137 move_subtree(tmpfromiter
, &toiter
, j
++);
9138 } while (gtk_tree_model_iter_next(pModel
, &tmpfromiter
));
9141 gtk_tree_store_remove(m_pTreeStore
, &rFromIter
);
9144 virtual void move_subtree(weld::TreeIter
& rNode
, const weld::TreeIter
* pNewParent
, int nIndexInNewParent
) override
9146 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rNode
);
9147 const GtkInstanceTreeIter
* pGtkParentIter
= static_cast<const GtkInstanceTreeIter
*>(pNewParent
);
9148 move_subtree(rGtkIter
.iter
, pGtkParentIter
? const_cast<GtkTreeIter
*>(&pGtkParentIter
->iter
) : nullptr, nIndexInNewParent
);
9151 virtual int get_selected_index() const override
9153 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't request selection when frozen");
9155 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(m_pTreeView
);
9156 if (gtk_tree_selection_get_mode(selection
) != GTK_SELECTION_MULTIPLE
)
9159 GtkTreeModel
* pModel
;
9160 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(m_pTreeView
), &pModel
, &iter
))
9162 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, &iter
);
9165 gint
* indices
= gtk_tree_path_get_indices_with_depth(path
, &depth
);
9166 nRet
= indices
[depth
-1];
9168 gtk_tree_path_free(path
);
9173 auto vec
= get_selected_rows();
9174 return vec
.empty() ? -1 : vec
[0];
9179 bool get_selected_iterator(GtkTreeIter
* pIter
) const
9181 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't request selection when frozen");
9183 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(m_pTreeView
);
9184 if (gtk_tree_selection_get_mode(selection
) != GTK_SELECTION_MULTIPLE
)
9185 bRet
= gtk_tree_selection_get_selected(gtk_tree_view_get_selection(m_pTreeView
), nullptr, pIter
);
9188 GtkTreeModel
* pModel
;
9189 GList
* pList
= gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(m_pTreeView
), &pModel
);
9190 for (GList
* pItem
= g_list_first(pList
); pItem
; pItem
= g_list_next(pItem
))
9194 GtkTreePath
* path
= static_cast<GtkTreePath
*>(pItem
->data
);
9195 gtk_tree_model_get_iter(pModel
, pIter
, path
);
9200 g_list_free_full(pList
, reinterpret_cast<GDestroyNotify
>(gtk_tree_path_free
));
9205 virtual OUString
get_selected_text() const override
9207 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't request selection when frozen");
9209 if (get_selected_iterator(&iter
))
9210 return get(iter
, m_nTextCol
);
9214 virtual OUString
get_selected_id() const override
9216 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't request selection when frozen");
9218 if (get_selected_iterator(&iter
))
9219 return get(iter
, m_nIdCol
);
9223 virtual std::unique_ptr
<weld::TreeIter
> make_iterator(const weld::TreeIter
* pOrig
) const override
9225 return std::unique_ptr
<weld::TreeIter
>(new GtkInstanceTreeIter(static_cast<const GtkInstanceTreeIter
*>(pOrig
)));
9228 virtual void copy_iterator(const weld::TreeIter
& rSource
, weld::TreeIter
& rDest
) const override
9230 const GtkInstanceTreeIter
& rGtkSource(static_cast<const GtkInstanceTreeIter
&>(rSource
));
9231 GtkInstanceTreeIter
& rGtkDest(static_cast<GtkInstanceTreeIter
&>(rDest
));
9232 rGtkDest
.iter
= rGtkSource
.iter
;
9235 virtual bool get_selected(weld::TreeIter
* pIter
) const override
9237 GtkInstanceTreeIter
* pGtkIter
= static_cast<GtkInstanceTreeIter
*>(pIter
);
9238 return get_selected_iterator(pGtkIter
? &pGtkIter
->iter
: nullptr);
9241 virtual bool get_cursor(weld::TreeIter
* pIter
) const override
9243 GtkInstanceTreeIter
* pGtkIter
= static_cast<GtkInstanceTreeIter
*>(pIter
);
9245 gtk_tree_view_get_cursor(m_pTreeView
, &path
, nullptr);
9246 if (pGtkIter
&& path
)
9248 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9249 gtk_tree_model_get_iter(pModel
, &pGtkIter
->iter
, path
);
9251 return path
!= nullptr;
9254 virtual int get_cursor_index() const override
9259 gtk_tree_view_get_cursor(m_pTreeView
, &path
, nullptr);
9263 gint
* indices
= gtk_tree_path_get_indices_with_depth(path
, &depth
);
9264 nRet
= indices
[depth
-1];
9265 gtk_tree_path_free(path
);
9271 virtual void set_cursor(const weld::TreeIter
& rIter
) override
9273 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9274 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9275 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9276 gtk_tree_view_set_cursor(m_pTreeView
, path
, nullptr, false);
9277 gtk_tree_path_free(path
);
9280 virtual bool get_iter_first(weld::TreeIter
& rIter
) const override
9282 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rIter
);
9283 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9284 return gtk_tree_model_get_iter_first(pModel
, &rGtkIter
.iter
);
9287 virtual bool iter_next_sibling(weld::TreeIter
& rIter
) const override
9289 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rIter
);
9290 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9291 return gtk_tree_model_iter_next(pModel
, &rGtkIter
.iter
);
9294 virtual bool iter_next(weld::TreeIter
& rIter
) const override
9296 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rIter
);
9297 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9298 GtkTreeIter iter
= rGtkIter
.iter
;
9299 if (iter_children(rGtkIter
))
9301 GtkTreeIter tmp
= iter
;
9302 if (gtk_tree_model_iter_next(pModel
, &tmp
))
9304 rGtkIter
.iter
= tmp
;
9307 // Move up level(s) until we find the level where the next sibling exists.
9308 while (gtk_tree_model_iter_parent(pModel
, &tmp
, &iter
))
9311 if (gtk_tree_model_iter_next(pModel
, &tmp
))
9313 rGtkIter
.iter
= tmp
;
9320 virtual bool iter_children(weld::TreeIter
& rIter
) const override
9322 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rIter
);
9323 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9325 bool ret
= gtk_tree_model_iter_children(pModel
, &tmp
, &rGtkIter
.iter
);
9326 rGtkIter
.iter
= tmp
;
9329 //on-demand dummy entry doesn't count
9330 return get_text(rGtkIter
, -1) != "<dummy>";
9335 virtual bool iter_parent(weld::TreeIter
& rIter
) const override
9337 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rIter
);
9338 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9340 auto ret
= gtk_tree_model_iter_parent(pModel
, &tmp
, &rGtkIter
.iter
);
9341 rGtkIter
.iter
= tmp
;
9345 virtual void remove(const weld::TreeIter
& rIter
) override
9347 disable_notify_events();
9348 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9349 gtk_tree_store_remove(m_pTreeStore
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9350 enable_notify_events();
9353 virtual void remove_selection() override
9355 disable_notify_events();
9357 std::vector
<GtkTreeIter
> aIters
;
9358 GtkTreeModel
* pModel
;
9359 GList
* pList
= gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(m_pTreeView
), &pModel
);
9360 for (GList
* pItem
= g_list_first(pList
); pItem
; pItem
= g_list_next(pItem
))
9362 GtkTreePath
* path
= static_cast<GtkTreePath
*>(pItem
->data
);
9363 aIters
.emplace_back();
9364 gtk_tree_model_get_iter(pModel
, &aIters
.back(), path
);
9366 g_list_free_full(pList
, reinterpret_cast<GDestroyNotify
>(gtk_tree_path_free
));
9368 for (auto& iter
: aIters
)
9369 gtk_tree_store_remove(m_pTreeStore
, &iter
);
9371 enable_notify_events();
9374 virtual void select(const weld::TreeIter
& rIter
) override
9376 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't select when frozen");
9377 disable_notify_events();
9378 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9379 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(m_pTreeView
), const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9380 enable_notify_events();
9383 virtual void scroll_to_row(const weld::TreeIter
& rIter
) override
9385 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't select when frozen");
9386 disable_notify_events();
9387 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9388 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9389 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9390 gtk_tree_view_scroll_to_cell(m_pTreeView
, path
, nullptr, false, 0, 0);
9391 gtk_tree_path_free(path
);
9392 enable_notify_events();
9395 virtual void unselect(const weld::TreeIter
& rIter
) override
9397 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't select when frozen");
9398 disable_notify_events();
9399 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9400 gtk_tree_selection_unselect_iter(gtk_tree_view_get_selection(m_pTreeView
), const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9401 enable_notify_events();
9404 virtual int get_iter_depth(const weld::TreeIter
& rIter
) const override
9406 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9407 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9408 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9409 int ret
= gtk_tree_path_get_depth(path
) - 1;
9410 gtk_tree_path_free(path
);
9414 virtual bool iter_has_child(const weld::TreeIter
& rIter
) const override
9416 weld::TreeIter
& rNonConstIter
= const_cast<weld::TreeIter
&>(rIter
);
9417 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rNonConstIter
);
9418 GtkTreeIter
restore(rGtkIter
.iter
);
9419 bool ret
= iter_children(rNonConstIter
);
9420 rGtkIter
.iter
= restore
;
9424 virtual bool get_row_expanded(const weld::TreeIter
& rIter
) const override
9426 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9427 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9428 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9429 bool ret
= gtk_tree_view_row_expanded(m_pTreeView
, path
);
9430 gtk_tree_path_free(path
);
9434 virtual void expand_row(const weld::TreeIter
& rIter
) override
9436 assert(gtk_tree_view_get_model(m_pTreeView
) && "don't expand when frozen");
9438 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9439 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9440 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9441 if (!gtk_tree_view_row_expanded(m_pTreeView
, path
))
9442 gtk_tree_view_expand_to_path(m_pTreeView
, path
);
9443 gtk_tree_path_free(path
);
9446 virtual void collapse_row(const weld::TreeIter
& rIter
) override
9448 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9449 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9450 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9451 if (gtk_tree_view_row_expanded(m_pTreeView
, path
))
9452 gtk_tree_view_collapse_row(m_pTreeView
, path
);
9453 gtk_tree_path_free(path
);
9456 virtual OUString
get_text(const weld::TreeIter
& rIter
, int col
) const override
9458 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9462 col
= get_model_col(col
);
9463 return get(rGtkIter
.iter
, col
);
9466 virtual void set_text(const weld::TreeIter
& rIter
, const OUString
& rText
, int col
) override
9468 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9472 col
= get_model_col(col
);
9473 set(rGtkIter
.iter
, col
, rText
);
9476 virtual OUString
get_id(const weld::TreeIter
& rIter
) const override
9478 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9479 return get(rGtkIter
.iter
, m_nIdCol
);
9482 virtual void set_id(const weld::TreeIter
& rIter
, const OUString
& rId
) override
9484 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9485 set(rGtkIter
.iter
, m_nIdCol
, rId
);
9488 virtual void freeze() override
9490 disable_notify_events();
9491 g_object_ref(m_pTreeStore
);
9492 GtkInstanceContainer::freeze();
9493 gtk_tree_view_set_model(m_pTreeView
, nullptr);
9497 GtkSortType eSortType
;
9498 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
9499 gtk_tree_sortable_get_sort_column_id(pSortable
, &nSortColumn
, &eSortType
);
9500 gtk_tree_sortable_set_sort_column_id(pSortable
, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
, eSortType
);
9502 m_aSavedSortColumns
.push_back(nSortColumn
);
9503 m_aSavedSortTypes
.push_back(eSortType
);
9505 enable_notify_events();
9508 virtual void thaw() override
9510 disable_notify_events();
9513 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeStore
);
9514 gtk_tree_sortable_set_sort_column_id(pSortable
, m_aSavedSortColumns
.back(), m_aSavedSortTypes
.back());
9515 m_aSavedSortTypes
.pop_back();
9516 m_aSavedSortColumns
.pop_back();
9518 gtk_tree_view_set_model(m_pTreeView
, GTK_TREE_MODEL(m_pTreeStore
));
9519 GtkInstanceContainer::thaw();
9520 g_object_unref(m_pTreeStore
);
9521 enable_notify_events();
9524 virtual int get_height_rows(int nRows
) const override
9526 gint nMaxRowHeight
= 0;
9527 for (GList
* pEntry
= g_list_first(m_pColumns
); pEntry
; pEntry
= g_list_next(pEntry
))
9529 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(pEntry
->data
);
9530 GList
*pRenderers
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(pColumn
));
9531 for (GList
* pRenderer
= g_list_first(pRenderers
); pRenderer
; pRenderer
= g_list_next(pRenderer
))
9533 GtkCellRenderer
* pCellRenderer
= GTK_CELL_RENDERER(pRenderer
->data
);
9535 gtk_cell_renderer_get_preferred_height(pCellRenderer
, GTK_WIDGET(m_pTreeView
), nullptr, &nRowHeight
);
9536 nMaxRowHeight
= std::max(nMaxRowHeight
, nRowHeight
);
9538 g_list_free(pRenderers
);
9541 gint nVerticalSeparator
;
9542 gtk_widget_style_get(GTK_WIDGET(m_pTreeView
), "vertical-separator", &nVerticalSeparator
, nullptr);
9544 return (nMaxRowHeight
* nRows
) + (nVerticalSeparator
* (nRows
+ 1));
9547 virtual Size
get_size_request() const override
9549 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
9550 if (GTK_IS_SCROLLED_WINDOW(pParent
))
9552 return Size(gtk_scrolled_window_get_min_content_width(GTK_SCROLLED_WINDOW(pParent
)),
9553 gtk_scrolled_window_get_min_content_height(GTK_SCROLLED_WINDOW(pParent
)));
9555 int nWidth
, nHeight
;
9556 gtk_widget_get_size_request(m_pWidget
, &nWidth
, &nHeight
);
9557 return Size(nWidth
, nHeight
);
9560 virtual Size
get_preferred_size() const override
9563 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
9564 if (GTK_IS_SCROLLED_WINDOW(pParent
))
9566 aRet
= Size(gtk_scrolled_window_get_min_content_width(GTK_SCROLLED_WINDOW(pParent
)),
9567 gtk_scrolled_window_get_min_content_height(GTK_SCROLLED_WINDOW(pParent
)));
9569 GtkRequisition size
;
9570 gtk_widget_get_preferred_size(m_pWidget
, nullptr, &size
);
9571 if (aRet
.Width() == -1)
9572 aRet
.setWidth(size
.width
);
9573 if (aRet
.Height() == -1)
9574 aRet
.setHeight(size
.height
);
9578 virtual void show() override
9580 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
9581 if (GTK_IS_SCROLLED_WINDOW(pParent
))
9582 gtk_widget_show(pParent
);
9583 gtk_widget_show(m_pWidget
);
9586 virtual void hide() override
9588 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
9589 if (GTK_IS_SCROLLED_WINDOW(pParent
))
9590 gtk_widget_hide(pParent
);
9591 gtk_widget_hide(m_pWidget
);
9594 virtual void set_selection_mode(SelectionMode eMode
) override
9596 disable_notify_events();
9597 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(m_pTreeView
), VclToGtk(eMode
));
9598 enable_notify_events();
9601 virtual int count_selected_rows() const override
9603 return gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(m_pTreeView
));
9606 int starts_with(const OUString
& rStr
, int col
, int nStartRow
, bool bCaseSensitive
)
9608 return ::starts_with(GTK_TREE_MODEL(m_pTreeStore
), rStr
, get_model_col(col
), nStartRow
, bCaseSensitive
);
9611 virtual void disable_notify_events() override
9613 g_signal_handler_block(gtk_tree_view_get_selection(m_pTreeView
), m_nChangedSignalId
);
9614 g_signal_handler_block(m_pTreeView
, m_nRowActivatedSignalId
);
9616 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9617 g_signal_handler_block(pModel
, m_nRowDeletedSignalId
);
9618 g_signal_handler_block(pModel
, m_nRowInsertedSignalId
);
9620 GtkInstanceContainer::disable_notify_events();
9623 virtual void enable_notify_events() override
9625 GtkInstanceContainer::enable_notify_events();
9627 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9628 g_signal_handler_unblock(pModel
, m_nRowDeletedSignalId
);
9629 g_signal_handler_unblock(pModel
, m_nRowInsertedSignalId
);
9631 g_signal_handler_unblock(m_pTreeView
, m_nRowActivatedSignalId
);
9632 g_signal_handler_unblock(gtk_tree_view_get_selection(m_pTreeView
), m_nChangedSignalId
);
9635 virtual void connect_popup_menu(const Link
<const CommandEvent
&, bool>& rLink
) override
9637 ensureButtonPressSignal();
9638 weld::TreeView::connect_popup_menu(rLink
);
9641 virtual bool get_dest_row_at_pos(const Point
&rPos
, weld::TreeIter
* pResult
) override
9643 const bool bAsTree
= gtk_tree_view_get_enable_tree_lines(m_pTreeView
);
9645 // to keep it simple we'll default to always drop before the current row
9646 // except for the special edge cases
9647 GtkTreeViewDropPosition pos
= bAsTree
? GTK_TREE_VIEW_DROP_INTO_OR_BEFORE
: GTK_TREE_VIEW_DROP_BEFORE
;
9649 // unhighlight current highlighted row
9650 gtk_tree_view_set_drag_dest_row(m_pTreeView
, nullptr, pos
);
9652 if (m_bWorkAroundBadDragRegion
)
9653 gtk_drag_unhighlight(GTK_WIDGET(m_pTreeView
));
9655 GtkTreePath
*path
= nullptr;
9656 GtkTreeViewDropPosition gtkpos
= bAsTree
? GTK_TREE_VIEW_DROP_INTO_OR_BEFORE
: GTK_TREE_VIEW_DROP_BEFORE
;
9657 bool ret
= gtk_tree_view_get_dest_row_at_pos(m_pTreeView
, rPos
.X(), rPos
.Y(),
9660 // find the last entry in the model for comparison
9661 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9662 int nChildren
= gtk_tree_model_iter_n_children(pModel
, nullptr);
9663 GtkTreePath
*lastpath
;
9665 lastpath
= gtk_tree_path_new_from_indices(nChildren
- 1, -1);
9667 lastpath
= gtk_tree_path_new_from_indices(0, -1);
9671 // empty space, draw an indicator at the last entry
9673 path
= gtk_tree_path_copy(lastpath
);
9674 pos
= GTK_TREE_VIEW_DROP_AFTER
;
9676 else if (gtk_tree_path_compare(path
, lastpath
) == 0)
9678 // if we're on the last entry, see if gtk thinks
9679 // the drop should be before or after it, and if
9680 // its after, treat it like a drop into empty
9681 // space, i.e. append it
9682 if (gtkpos
== GTK_TREE_VIEW_DROP_AFTER
||
9683 gtkpos
== GTK_TREE_VIEW_DROP_INTO_OR_AFTER
)
9686 pos
= bAsTree
? gtkpos
: GTK_TREE_VIEW_DROP_AFTER
;
9692 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(*pResult
);
9693 gtk_tree_model_get_iter(pModel
, &rGtkIter
.iter
, path
);
9698 // highlight the row
9699 gtk_tree_view_set_drag_dest_row(m_pTreeView
, path
, pos
);
9703 gtk_tree_path_free(path
);
9704 gtk_tree_path_free(lastpath
);
9706 // auto scroll if we're close to the edges
9707 GtkAdjustment
* pVAdjustment
= gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(m_pTreeView
));
9708 double fStep
= gtk_adjustment_get_step_increment(pVAdjustment
);
9709 if (rPos
.Y() < fStep
)
9711 double fValue
= gtk_adjustment_get_value(pVAdjustment
) - fStep
;
9714 gtk_adjustment_set_value(pVAdjustment
, fValue
);
9719 gtk_tree_view_get_visible_rect(m_pTreeView
, &aRect
);
9720 if (rPos
.Y() > aRect
.height
- fStep
)
9722 double fValue
= gtk_adjustment_get_value(pVAdjustment
) + fStep
;
9723 double fMax
= gtk_adjustment_get_upper(pVAdjustment
);
9726 gtk_adjustment_set_value(pVAdjustment
, fValue
);
9733 virtual void start_editing(const weld::TreeIter
& rIter
) override
9735 int col
= get_view_col(m_nTextCol
);
9736 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(g_list_nth_data(m_pColumns
, col
));
9737 assert(pColumn
&& "wrong column");
9739 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
9740 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9741 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
9743 // allow editing of cells which are not usually editable, so we can have double click
9744 // do its usual row-activate but if we explicitly want to edit (remote files dialog)
9745 // we can still do that
9746 GList
*pRenderers
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(pColumn
));
9747 for (GList
* pRenderer
= g_list_first(pRenderers
); pRenderer
; pRenderer
= g_list_next(pRenderer
))
9749 GtkCellRenderer
* pCellRenderer
= GTK_CELL_RENDERER(pRenderer
->data
);
9750 if (GTK_IS_CELL_RENDERER_TEXT(pCellRenderer
))
9752 gboolean
is_editable(false);
9753 g_object_get(pCellRenderer
, "editable", &is_editable
, nullptr);
9756 g_object_set(pCellRenderer
, "editable", true, "editable-set", true, nullptr);
9757 g_object_set_data(G_OBJECT(pCellRenderer
), "g-lo-RestoreNonEditable", reinterpret_cast<gpointer
>(true));
9762 g_list_free(pRenderers
);
9764 gtk_tree_view_set_cursor(m_pTreeView
, path
, pColumn
, true);
9766 gtk_tree_path_free(path
);
9769 virtual void end_editing() override
9771 GtkTreeViewColumn
*focus_column
= nullptr;
9772 gtk_tree_view_get_cursor(m_pTreeView
, nullptr, &focus_column
);
9774 gtk_cell_area_stop_editing(gtk_cell_layout_get_area(GTK_CELL_LAYOUT(focus_column
)), true);
9777 virtual TreeView
* get_drag_source() const override
9779 return g_DragSource
;
9782 // Under gtk 3.24.8 dragging into the TreeView is not highlighting
9783 // entire TreeView widget, just the rectangle which has no entries
9784 // in it, so as a workaround highlight the parent container
9785 // on drag start, and undo it on drag end, and trigger removal
9786 // of the treeview's highlight effort
9787 virtual void drag_started() override
9790 GtkWidget
* pWidget
= GTK_WIDGET(m_pTreeView
);
9791 GtkWidget
* pParent
= gtk_widget_get_parent(pWidget
);
9792 if (GTK_IS_SCROLLED_WINDOW(pParent
))
9794 gtk_drag_unhighlight(pWidget
);
9795 gtk_drag_highlight(pParent
);
9796 m_bWorkAroundBadDragRegion
= true;
9800 virtual void drag_ended() override
9803 if (m_bWorkAroundBadDragRegion
)
9805 GtkWidget
* pWidget
= GTK_WIDGET(m_pTreeView
);
9806 GtkWidget
* pParent
= gtk_widget_get_parent(pWidget
);
9807 gtk_drag_unhighlight(pParent
);
9808 m_bWorkAroundBadDragRegion
= false;
9810 // unhighlight the row
9811 gtk_tree_view_set_drag_dest_row(m_pTreeView
, nullptr, GTK_TREE_VIEW_DROP_BEFORE
);
9814 virtual ~GtkInstanceTreeView() override
9817 Application::RemoveUserEvent(m_pChangeEvent
);
9818 g_signal_handler_disconnect(m_pTreeView
, m_nKeyPressSignalId
);
9819 g_signal_handler_disconnect(m_pTreeView
, m_nDragEndSignalId
);
9820 g_signal_handler_disconnect(m_pTreeView
, m_nDragBeginSignalId
);
9821 g_signal_handler_disconnect(m_pTreeView
, m_nPopupMenuSignalId
);
9822 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9823 g_signal_handler_disconnect(pModel
, m_nRowDeletedSignalId
);
9824 g_signal_handler_disconnect(pModel
, m_nRowInsertedSignalId
);
9826 if (m_nVAdjustmentChangedSignalId
)
9828 GtkAdjustment
* pVAdjustment
= gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(m_pTreeView
));
9829 g_signal_handler_disconnect(pVAdjustment
, m_nVAdjustmentChangedSignalId
);
9832 g_signal_handler_disconnect(m_pTreeView
, m_nTestExpandRowSignalId
);
9833 g_signal_handler_disconnect(m_pTreeView
, m_nRowActivatedSignalId
);
9834 g_signal_handler_disconnect(gtk_tree_view_get_selection(m_pTreeView
), m_nChangedSignalId
);
9836 for (GList
* pEntry
= g_list_last(m_pColumns
); pEntry
; pEntry
= g_list_previous(pEntry
))
9838 GtkTreeViewColumn
* pColumn
= GTK_TREE_VIEW_COLUMN(pEntry
->data
);
9839 g_signal_handler_disconnect(pColumn
, m_aColumnSignalIds
.back());
9840 m_aColumnSignalIds
.pop_back();
9842 g_list_free(m_pColumns
);
9846 IMPL_LINK_NOARG(GtkInstanceTreeView
, async_signal_changed
, void*, void)
9848 m_pChangeEvent
= nullptr;
9852 IMPL_LINK_NOARG(GtkInstanceTreeView
, async_stop_cell_editing
, void*, void)
9857 class GtkInstanceIconView
: public GtkInstanceContainer
, public virtual weld::IconView
9860 GtkIconView
* m_pIconView
;
9861 GtkTreeStore
* m_pTreeStore
;
9865 gulong m_nSelectionChangedSignalId
;
9866 gulong m_nItemActivatedSignalId
;
9867 ImplSVEvent
* m_pSelectionChangeEvent
;
9869 DECL_LINK(async_signal_selection_changed
, void*, void);
9871 void launch_signal_selection_changed()
9873 //tdf#117991 selection change is sent before the focus change, and focus change
9874 //is what will cause a spinbutton that currently has the focus to set its contents
9875 //as the spin button value. So any LibreOffice callbacks on
9876 //signal-change would happen before the spinbutton value-change occurs.
9877 //To avoid this, send the signal-change to LibreOffice to occur after focus-change
9878 //has been processed
9879 if (m_pSelectionChangeEvent
)
9880 Application::RemoveUserEvent(m_pSelectionChangeEvent
);
9881 m_pSelectionChangeEvent
= Application::PostUserEvent(LINK(this, GtkInstanceIconView
, async_signal_selection_changed
));
9884 static void signalSelectionChanged(GtkIconView
*, gpointer widget
)
9886 GtkInstanceIconView
* pThis
= static_cast<GtkInstanceIconView
*>(widget
);
9887 pThis
->launch_signal_selection_changed();
9890 void handle_item_activated()
9892 if (signal_item_activated())
9896 static void signalItemActivated(GtkIconView
*, GtkTreePath
*, gpointer widget
)
9898 GtkInstanceIconView
* pThis
= static_cast<GtkInstanceIconView
*>(widget
);
9899 SolarMutexGuard aGuard
;
9900 pThis
->handle_item_activated();
9903 void insert_item(GtkTreeIter
& iter
, int pos
, const OUString
* pId
, const OUString
* pText
, const OUString
* pIconName
)
9905 gtk_tree_store_insert_with_values(m_pTreeStore
, &iter
, nullptr, pos
,
9906 m_nTextCol
, !pText
? nullptr : OUStringToOString(*pText
, RTL_TEXTENCODING_UTF8
).getStr(),
9907 m_nIdCol
, !pId
? nullptr : OUStringToOString(*pId
, RTL_TEXTENCODING_UTF8
).getStr(),
9911 GdkPixbuf
* pixbuf
= getPixbuf(*pIconName
);
9912 gtk_tree_store_set(m_pTreeStore
, &iter
, m_nImageCol
, pixbuf
, -1);
9914 g_object_unref(pixbuf
);
9918 OUString
get(const GtkTreeIter
& iter
, int col
) const
9920 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9922 gtk_tree_model_get(pModel
, const_cast<GtkTreeIter
*>(&iter
), col
, &pStr
, -1);
9923 OUString
sRet(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
9928 bool get_selected_iterator(GtkTreeIter
* pIter
) const
9930 assert(gtk_icon_view_get_model(m_pIconView
) && "don't request selection when frozen");
9933 GtkTreeModel
* pModel
= GTK_TREE_MODEL(m_pTreeStore
);
9934 GList
* pList
= gtk_icon_view_get_selected_items(m_pIconView
);
9935 for (GList
* pItem
= g_list_first(pList
); pItem
; pItem
= g_list_next(pItem
))
9939 GtkTreePath
* path
= static_cast<GtkTreePath
*>(pItem
->data
);
9940 gtk_tree_model_get_iter(pModel
, pIter
, path
);
9945 g_list_free_full(pList
, reinterpret_cast<GDestroyNotify
>(gtk_tree_path_free
));
9951 GtkInstanceIconView(GtkIconView
* pIconView
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
9952 : GtkInstanceContainer(GTK_CONTAINER(pIconView
), pBuilder
, bTakeOwnership
)
9953 , m_pIconView(pIconView
)
9954 , m_pTreeStore(GTK_TREE_STORE(gtk_icon_view_get_model(m_pIconView
)))
9955 , m_nTextCol(gtk_icon_view_get_text_column(m_pIconView
))
9956 , m_nImageCol(gtk_icon_view_get_pixbuf_column(m_pIconView
))
9957 , m_nSelectionChangedSignalId(g_signal_connect(pIconView
, "selection-changed",
9958 G_CALLBACK(signalSelectionChanged
), this))
9959 , m_nItemActivatedSignalId(g_signal_connect(pIconView
, "item-activated", G_CALLBACK(signalItemActivated
), this))
9960 , m_pSelectionChangeEvent(nullptr)
9962 m_nIdCol
= m_nTextCol
+ 1;
9965 virtual void insert(int pos
, const OUString
* pText
, const OUString
* pId
, const OUString
* pIconName
, weld::TreeIter
* pRet
) override
9967 disable_notify_events();
9969 insert_item(iter
, pos
, pId
, pText
, pIconName
);
9972 GtkInstanceTreeIter
* pGtkRetIter
= static_cast<GtkInstanceTreeIter
*>(pRet
);
9973 pGtkRetIter
->iter
= iter
;
9975 enable_notify_events();
9978 virtual OUString
get_selected_id() const override
9980 assert(gtk_icon_view_get_model(m_pIconView
) && "don't request selection when frozen");
9982 if (get_selected_iterator(&iter
))
9983 return get(iter
, m_nIdCol
);
9987 virtual void clear() override
9989 disable_notify_events();
9990 gtk_tree_store_clear(m_pTreeStore
);
9991 enable_notify_events();
9994 virtual void freeze() override
9996 disable_notify_events();
9997 g_object_ref(m_pTreeStore
);
9998 GtkInstanceContainer::freeze();
9999 gtk_icon_view_set_model(m_pIconView
, nullptr);
10000 enable_notify_events();
10003 virtual void thaw() override
10005 disable_notify_events();
10006 gtk_icon_view_set_model(m_pIconView
, GTK_TREE_MODEL(m_pTreeStore
));
10007 GtkInstanceContainer::thaw();
10008 g_object_unref(m_pTreeStore
);
10009 enable_notify_events();
10012 virtual Size
get_size_request() const override
10014 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10015 if (GTK_IS_SCROLLED_WINDOW(pParent
))
10017 return Size(gtk_scrolled_window_get_min_content_width(GTK_SCROLLED_WINDOW(pParent
)),
10018 gtk_scrolled_window_get_min_content_height(GTK_SCROLLED_WINDOW(pParent
)));
10020 int nWidth
, nHeight
;
10021 gtk_widget_get_size_request(m_pWidget
, &nWidth
, &nHeight
);
10022 return Size(nWidth
, nHeight
);
10025 virtual Size
get_preferred_size() const override
10028 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10029 if (GTK_IS_SCROLLED_WINDOW(pParent
))
10031 aRet
= Size(gtk_scrolled_window_get_min_content_width(GTK_SCROLLED_WINDOW(pParent
)),
10032 gtk_scrolled_window_get_min_content_height(GTK_SCROLLED_WINDOW(pParent
)));
10034 GtkRequisition size
;
10035 gtk_widget_get_preferred_size(m_pWidget
, nullptr, &size
);
10036 if (aRet
.Width() == -1)
10037 aRet
.setWidth(size
.width
);
10038 if (aRet
.Height() == -1)
10039 aRet
.setHeight(size
.height
);
10043 virtual void show() override
10045 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10046 if (GTK_IS_SCROLLED_WINDOW(pParent
))
10047 gtk_widget_show(pParent
);
10048 gtk_widget_show(m_pWidget
);
10051 virtual void hide() override
10053 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10054 if (GTK_IS_SCROLLED_WINDOW(pParent
))
10055 gtk_widget_hide(pParent
);
10056 gtk_widget_hide(m_pWidget
);
10059 virtual OUString
get_selected_text() const override
10061 assert(gtk_icon_view_get_model(m_pIconView
) && "don't request selection when frozen");
10063 if (get_selected_iterator(&iter
))
10064 return get(iter
, m_nTextCol
);
10068 virtual int count_selected_items() const override
10070 GList
* pList
= gtk_icon_view_get_selected_items(m_pIconView
);
10071 int nRet
= g_list_length(pList
);
10072 g_list_free_full(pList
, reinterpret_cast<GDestroyNotify
>(gtk_tree_path_free
));
10076 virtual void select(int pos
) override
10078 assert(gtk_icon_view_get_model(m_pIconView
) && "don't select when frozen");
10079 disable_notify_events();
10080 if (pos
== -1 || (pos
== 0 && n_children() == 0))
10082 gtk_icon_view_unselect_all(m_pIconView
);
10086 GtkTreePath
* path
= gtk_tree_path_new_from_indices(pos
, -1);
10087 gtk_icon_view_select_path(m_pIconView
, path
);
10088 gtk_icon_view_scroll_to_path(m_pIconView
, path
, false, 0, 0);
10089 gtk_tree_path_free(path
);
10091 enable_notify_events();
10094 virtual void unselect(int pos
) override
10096 assert(gtk_icon_view_get_model(m_pIconView
) && "don't select when frozen");
10097 disable_notify_events();
10098 if (pos
== -1 || (pos
== 0 && n_children() == 0))
10100 gtk_icon_view_select_all(m_pIconView
);
10104 GtkTreePath
* path
= gtk_tree_path_new_from_indices(pos
, -1);
10105 gtk_icon_view_select_path(m_pIconView
, path
);
10106 gtk_tree_path_free(path
);
10108 enable_notify_events();
10111 virtual bool get_selected(weld::TreeIter
* pIter
) const override
10113 GtkInstanceTreeIter
* pGtkIter
= static_cast<GtkInstanceTreeIter
*>(pIter
);
10114 return get_selected_iterator(pGtkIter
? &pGtkIter
->iter
: nullptr);
10117 virtual bool get_cursor(weld::TreeIter
* pIter
) const override
10119 GtkInstanceTreeIter
* pGtkIter
= static_cast<GtkInstanceTreeIter
*>(pIter
);
10121 gtk_icon_view_get_cursor(m_pIconView
, &path
, nullptr);
10122 if (pGtkIter
&& path
)
10124 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
10125 gtk_tree_model_get_iter(pModel
, &pGtkIter
->iter
, path
);
10127 return path
!= nullptr;
10130 virtual void set_cursor(const weld::TreeIter
& rIter
) override
10132 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
10133 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
10134 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
10135 gtk_icon_view_set_cursor(m_pIconView
, path
, nullptr, false);
10136 gtk_tree_path_free(path
);
10139 virtual bool get_iter_first(weld::TreeIter
& rIter
) const override
10141 GtkInstanceTreeIter
& rGtkIter
= static_cast<GtkInstanceTreeIter
&>(rIter
);
10142 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
10143 return gtk_tree_model_get_iter_first(pModel
, &rGtkIter
.iter
);
10146 virtual void scroll_to_item(const weld::TreeIter
& rIter
) override
10148 assert(gtk_icon_view_get_model(m_pIconView
) && "don't select when frozen");
10149 disable_notify_events();
10150 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
10151 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
10152 GtkTreePath
* path
= gtk_tree_model_get_path(pModel
, const_cast<GtkTreeIter
*>(&rGtkIter
.iter
));
10153 gtk_icon_view_scroll_to_path(m_pIconView
, path
, false, 0, 0);
10154 gtk_tree_path_free(path
);
10155 enable_notify_events();
10158 virtual std::unique_ptr
<weld::TreeIter
> make_iterator(const weld::TreeIter
* pOrig
) const override
10160 return std::unique_ptr
<weld::TreeIter
>(new GtkInstanceTreeIter(static_cast<const GtkInstanceTreeIter
*>(pOrig
)));
10163 virtual void selected_foreach(const std::function
<bool(weld::TreeIter
&)>& func
) override
10165 GtkInstanceTreeIter
aGtkIter(nullptr);
10167 GtkTreeModel
*pModel
= GTK_TREE_MODEL(m_pTreeStore
);
10168 GList
* pList
= gtk_icon_view_get_selected_items(m_pIconView
);
10169 for (GList
* pItem
= g_list_first(pList
); pItem
; pItem
= g_list_next(pItem
))
10171 GtkTreePath
* path
= static_cast<GtkTreePath
*>(pItem
->data
);
10172 gtk_tree_model_get_iter(pModel
, &aGtkIter
.iter
, path
);
10173 if (func(aGtkIter
))
10176 g_list_free_full(pList
, reinterpret_cast<GDestroyNotify
>(gtk_tree_path_free
));
10179 virtual int n_children() const override
10181 return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_pTreeStore
), nullptr);
10184 virtual OUString
get_id(const weld::TreeIter
& rIter
) const override
10186 const GtkInstanceTreeIter
& rGtkIter
= static_cast<const GtkInstanceTreeIter
&>(rIter
);
10187 return get(rGtkIter
.iter
, m_nIdCol
);
10190 virtual void disable_notify_events() override
10192 g_signal_handler_block(m_pIconView
, m_nSelectionChangedSignalId
);
10193 g_signal_handler_block(m_pIconView
, m_nItemActivatedSignalId
);
10195 GtkInstanceContainer::disable_notify_events();
10198 virtual void enable_notify_events() override
10200 GtkInstanceContainer::enable_notify_events();
10202 g_signal_handler_unblock(m_pIconView
, m_nItemActivatedSignalId
);
10203 g_signal_handler_unblock(m_pIconView
, m_nSelectionChangedSignalId
);
10206 virtual ~GtkInstanceIconView() override
10208 if (m_pSelectionChangeEvent
)
10209 Application::RemoveUserEvent(m_pSelectionChangeEvent
);
10211 g_signal_handler_disconnect(m_pIconView
, m_nItemActivatedSignalId
);
10212 g_signal_handler_disconnect(m_pIconView
, m_nSelectionChangedSignalId
);
10216 IMPL_LINK_NOARG(GtkInstanceIconView
, async_signal_selection_changed
, void*, void)
10218 m_pSelectionChangeEvent
= nullptr;
10219 signal_selection_changed();
10222 class GtkInstanceSpinButton
: public GtkInstanceEntry
, public virtual weld::SpinButton
10225 GtkSpinButton
* m_pButton
;
10226 gulong m_nValueChangedSignalId
;
10227 gulong m_nOutputSignalId
;
10228 gulong m_nInputSignalId
;
10229 bool m_bFormatting
;
10230 bool m_bBlockOutput
;
10233 static void signalValueChanged(GtkSpinButton
*, gpointer widget
)
10235 GtkInstanceSpinButton
* pThis
= static_cast<GtkInstanceSpinButton
*>(widget
);
10236 SolarMutexGuard aGuard
;
10237 pThis
->m_bBlank
= false;
10238 pThis
->signal_value_changed();
10241 bool guarded_signal_output()
10243 if (m_bBlockOutput
)
10245 m_bFormatting
= true;
10246 bool bRet
= signal_output();
10247 m_bFormatting
= false;
10251 static gboolean
signalOutput(GtkSpinButton
*, gpointer widget
)
10253 GtkInstanceSpinButton
* pThis
= static_cast<GtkInstanceSpinButton
*>(widget
);
10254 SolarMutexGuard aGuard
;
10255 return pThis
->guarded_signal_output();
10258 static gint
signalInput(GtkSpinButton
*, gdouble
* new_value
, gpointer widget
)
10260 GtkInstanceSpinButton
* pThis
= static_cast<GtkInstanceSpinButton
*>(widget
);
10261 SolarMutexGuard aGuard
;
10263 TriState eHandled
= pThis
->signal_input(&result
);
10264 if (eHandled
== TRISTATE_INDET
)
10266 if (eHandled
== TRISTATE_TRUE
)
10268 *new_value
= pThis
->toGtk(result
);
10271 return GTK_INPUT_ERROR
;
10274 double toGtk(int nValue
) const
10276 return static_cast<double>(nValue
) / Power10(get_digits());
10279 int fromGtk(double fValue
) const
10281 return FRound(fValue
* Power10(get_digits()));
10285 GtkInstanceSpinButton(GtkSpinButton
* pButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
10286 : GtkInstanceEntry(GTK_ENTRY(pButton
), pBuilder
, bTakeOwnership
)
10287 , m_pButton(pButton
)
10288 , m_nValueChangedSignalId(g_signal_connect(pButton
, "value-changed", G_CALLBACK(signalValueChanged
), this))
10289 , m_nOutputSignalId(g_signal_connect(pButton
, "output", G_CALLBACK(signalOutput
), this))
10290 , m_nInputSignalId(g_signal_connect(pButton
, "input", G_CALLBACK(signalInput
), this))
10291 , m_bFormatting(false)
10292 , m_bBlockOutput(false)
10297 virtual int get_value() const override
10299 return fromGtk(gtk_spin_button_get_value(m_pButton
));
10302 virtual void set_value(int value
) override
10304 disable_notify_events();
10306 gtk_spin_button_set_value(m_pButton
, toGtk(value
));
10307 enable_notify_events();
10310 virtual void set_text(const OUString
& rText
) override
10312 disable_notify_events();
10313 // tdf#122786 if we're just formatting a value, then we're done,
10314 // however if set_text has been called directly we want to update our
10315 // value from this new text, but don't want to reformat with that value
10316 if (!m_bFormatting
)
10318 gtk_entry_set_text(GTK_ENTRY(m_pButton
), OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr());
10320 m_bBlockOutput
= true;
10321 gtk_spin_button_update(m_pButton
);
10322 m_bBlank
= rText
.isEmpty();
10323 m_bBlockOutput
= false;
10327 bool bKeepBlank
= m_bBlank
&& get_value() == 0;
10330 gtk_entry_set_text(GTK_ENTRY(m_pButton
), OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr());
10334 enable_notify_events();
10337 virtual void set_range(int min
, int max
) override
10339 disable_notify_events();
10340 gtk_spin_button_set_range(m_pButton
, toGtk(min
), toGtk(max
));
10341 enable_notify_events();
10344 virtual void get_range(int& min
, int& max
) const override
10346 double gtkmin
, gtkmax
;
10347 gtk_spin_button_get_range(m_pButton
, >kmin
, >kmax
);
10348 min
= fromGtk(gtkmin
);
10349 max
= fromGtk(gtkmax
);
10352 virtual void set_increments(int step
, int page
) override
10354 disable_notify_events();
10355 gtk_spin_button_set_increments(m_pButton
, toGtk(step
), toGtk(page
));
10356 enable_notify_events();
10359 virtual void get_increments(int& step
, int& page
) const override
10361 double gtkstep
, gtkpage
;
10362 gtk_spin_button_get_increments(m_pButton
, >kstep
, >kpage
);
10363 step
= fromGtk(gtkstep
);
10364 page
= fromGtk(gtkpage
);
10367 virtual void set_digits(unsigned int digits
) override
10369 disable_notify_events();
10370 gtk_spin_button_set_digits(m_pButton
, digits
);
10371 enable_notify_events();
10374 virtual unsigned int get_digits() const override
10376 return gtk_spin_button_get_digits(m_pButton
);
10379 virtual void disable_notify_events() override
10381 g_signal_handler_block(m_pButton
, m_nValueChangedSignalId
);
10382 GtkInstanceEntry::disable_notify_events();
10385 virtual void enable_notify_events() override
10387 GtkInstanceEntry::enable_notify_events();
10388 g_signal_handler_unblock(m_pButton
, m_nValueChangedSignalId
);
10391 virtual ~GtkInstanceSpinButton() override
10393 g_signal_handler_disconnect(m_pButton
, m_nInputSignalId
);
10394 g_signal_handler_disconnect(m_pButton
, m_nOutputSignalId
);
10395 g_signal_handler_disconnect(m_pButton
, m_nValueChangedSignalId
);
10399 class GtkInstanceFormattedSpinButton
: public GtkInstanceEntry
, public virtual weld::FormattedSpinButton
10402 GtkSpinButton
* m_pButton
;
10403 SvNumberFormatter
* m_pFormatter
;
10404 Color
* m_pLastOutputColor
;
10405 sal_uInt32 m_nFormatKey
;
10406 gulong m_nValueChangedSignalId
;
10407 gulong m_nOutputSignalId
;
10408 gulong m_nInputSignalId
;
10410 bool signal_output()
10414 double dVal
= get_value();
10416 if (m_pFormatter
->IsTextFormat(m_nFormatKey
))
10418 // first convert the number as string in standard format
10420 m_pFormatter
->GetOutputString(dVal
, 0, sTemp
, &m_pLastOutputColor
);
10421 // then encode the string in the corresponding text format
10422 m_pFormatter
->GetOutputString(sTemp
, m_nFormatKey
, sNewText
, &m_pLastOutputColor
);
10426 m_pFormatter
->GetInputLineString(dVal
, m_nFormatKey
, sNewText
);
10428 set_text(sNewText
);
10432 static gboolean
signalOutput(GtkSpinButton
*, gpointer widget
)
10434 GtkInstanceFormattedSpinButton
* pThis
= static_cast<GtkInstanceFormattedSpinButton
*>(widget
);
10435 SolarMutexGuard aGuard
;
10436 return pThis
->signal_output();
10439 gint
signal_input(double* value
)
10444 sal_uInt32 nFormatKey
= m_nFormatKey
; // IsNumberFormat changes the FormatKey!
10446 if (m_pFormatter
->IsTextFormat(nFormatKey
))
10447 // for detection of values like "1,1" in fields that are formatted as text
10450 OUString
sText(get_text());
10452 // special treatment for percentage formatting
10453 if (m_pFormatter
->GetType(m_nFormatKey
) == SvNumFormatType::PERCENT
)
10455 // the language of our format
10456 LanguageType eLanguage
= m_pFormatter
->GetEntry(m_nFormatKey
)->GetLanguage();
10457 // the default number format for this language
10458 sal_uLong nStandardNumericFormat
= m_pFormatter
->GetStandardFormat(SvNumFormatType::NUMBER
, eLanguage
);
10460 sal_uInt32 nTempFormat
= nStandardNumericFormat
;
10462 if (m_pFormatter
->IsNumberFormat(sText
, nTempFormat
, dTemp
) &&
10463 SvNumFormatType::NUMBER
== m_pFormatter
->GetType(nTempFormat
))
10464 // the string is equivalent to a number formatted one (has no % sign) -> append it
10466 // (with this, an input of '3' becomes '3%', which then by the formatter is translated
10467 // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
10468 // which equals 300 percent.
10470 if (!m_pFormatter
->IsNumberFormat(sText
, nFormatKey
, *value
))
10471 return GTK_INPUT_ERROR
;
10476 static gint
signalInput(GtkSpinButton
*, gdouble
* new_value
, gpointer widget
)
10478 GtkInstanceFormattedSpinButton
* pThis
= static_cast<GtkInstanceFormattedSpinButton
*>(widget
);
10479 SolarMutexGuard aGuard
;
10480 return pThis
->signal_input(new_value
);
10483 static void signalValueChanged(GtkSpinButton
*, gpointer widget
)
10485 GtkInstanceFormattedSpinButton
* pThis
= static_cast<GtkInstanceFormattedSpinButton
*>(widget
);
10486 SolarMutexGuard aGuard
;
10487 pThis
->signal_value_changed();
10491 GtkInstanceFormattedSpinButton(GtkSpinButton
* pButton
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
10492 : GtkInstanceEntry(GTK_ENTRY(pButton
), pBuilder
, bTakeOwnership
)
10493 , m_pButton(pButton
)
10494 , m_pFormatter(nullptr)
10495 , m_pLastOutputColor(nullptr)
10497 , m_nValueChangedSignalId(g_signal_connect(pButton
, "value-changed", G_CALLBACK(signalValueChanged
), this))
10498 , m_nOutputSignalId(g_signal_connect(pButton
, "output", G_CALLBACK(signalOutput
), this))
10499 , m_nInputSignalId(g_signal_connect(pButton
, "input", G_CALLBACK(signalInput
), this))
10503 virtual double get_value() const override
10505 return gtk_spin_button_get_value(m_pButton
);
10508 virtual void set_value(double value
) override
10510 disable_notify_events();
10511 gtk_spin_button_set_value(m_pButton
, value
);
10512 enable_notify_events();
10515 virtual void set_range(double min
, double max
) override
10517 disable_notify_events();
10518 gtk_spin_button_set_range(m_pButton
, min
, max
);
10519 enable_notify_events();
10522 virtual void get_range(double& min
, double& max
) const override
10524 gtk_spin_button_get_range(m_pButton
, &min
, &max
);
10527 virtual void set_formatter(SvNumberFormatter
* pFormatter
) override
10529 m_pFormatter
= pFormatter
;
10531 // calc the default format key from the Office's UI locale
10534 // get the Office's locale and translate
10535 LanguageType eSysLanguage
= Application::GetSettings().GetUILanguageTag().getLanguageType( false);
10536 // get the standard numeric format for this language
10537 m_nFormatKey
= m_pFormatter
->GetStandardFormat( SvNumFormatType::NUMBER
, eSysLanguage
);
10544 virtual sal_Int32
get_format_key() const override
10546 return m_nFormatKey
;
10549 virtual void set_format_key(sal_Int32 nFormatKey
) override
10551 m_nFormatKey
= nFormatKey
;
10554 virtual ~GtkInstanceFormattedSpinButton() override
10556 g_signal_handler_disconnect(m_pButton
, m_nInputSignalId
);
10557 g_signal_handler_disconnect(m_pButton
, m_nOutputSignalId
);
10558 g_signal_handler_disconnect(m_pButton
, m_nValueChangedSignalId
);
10562 class GtkInstanceLabel
: public GtkInstanceWidget
, public virtual weld::Label
10565 GtkLabel
* m_pLabel
;
10567 void set_text_color(const Color
& rColor
)
10569 guint16 nRed
= rColor
.GetRed() << 8;
10570 guint16 nGreen
= rColor
.GetRed() << 8;
10571 guint16 nBlue
= rColor
.GetBlue() << 8;
10573 PangoAttrList
* pAttrs
= pango_attr_list_new();
10574 pango_attr_list_insert(pAttrs
, pango_attr_background_new(nRed
, nGreen
, nBlue
));
10575 gtk_label_set_attributes(m_pLabel
, pAttrs
);
10576 pango_attr_list_unref(pAttrs
);
10580 GtkInstanceLabel(GtkLabel
* pLabel
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
10581 : GtkInstanceWidget(GTK_WIDGET(pLabel
), pBuilder
, bTakeOwnership
)
10586 virtual void set_label(const OUString
& rText
) override
10588 ::set_label(m_pLabel
, rText
);
10591 virtual OUString
get_label() const override
10593 return ::get_label(m_pLabel
);
10596 virtual void set_mnemonic_widget(Widget
* pTarget
) override
10598 assert(!gtk_label_get_selectable(m_pLabel
) && "don't use set_mnemonic_widget on selectable labels, for consistency with gen backend");
10599 GtkInstanceWidget
* pTargetWidget
= dynamic_cast<GtkInstanceWidget
*>(pTarget
);
10600 gtk_label_set_mnemonic_widget(m_pLabel
, pTargetWidget
? pTargetWidget
->getWidget() : nullptr);
10603 virtual void set_message_type(weld::EntryMessageType eType
) override
10605 if (eType
== weld::EntryMessageType::Error
)
10606 set_text_color(Application::GetSettings().GetStyleSettings().GetHighlightColor());
10607 else if (eType
== weld::EntryMessageType::Warning
)
10608 set_text_color(COL_YELLOW
);
10610 gtk_label_set_attributes(m_pLabel
, nullptr);
10613 virtual void set_font(const vcl::Font
& rFont
) override
10615 PangoAttrList
* pAttrList
= create_attr_list(rFont
);
10616 gtk_label_set_attributes(m_pLabel
, pAttrList
);
10617 pango_attr_list_unref(pAttrList
);
10621 std::unique_ptr
<weld::Label
> GtkInstanceFrame::weld_label_widget() const
10623 GtkWidget
* pLabel
= gtk_frame_get_label_widget(m_pFrame
);
10624 if (!pLabel
|| !GTK_IS_LABEL(pLabel
))
10626 return std::make_unique
<GtkInstanceLabel
>(GTK_LABEL(pLabel
), m_pBuilder
, false);
10629 class GtkInstanceTextView
: public GtkInstanceContainer
, public virtual weld::TextView
10632 GtkTextView
* m_pTextView
;
10633 GtkTextBuffer
* m_pTextBuffer
;
10634 GtkAdjustment
* m_pVAdjustment
;
10635 gulong m_nChangedSignalId
;
10636 gulong m_nCursorPosSignalId
;
10637 gulong m_nVAdjustChangedSignalId
;
10639 static void signalChanged(GtkTextView
*, gpointer widget
)
10641 GtkInstanceTextView
* pThis
= static_cast<GtkInstanceTextView
*>(widget
);
10642 SolarMutexGuard aGuard
;
10643 pThis
->signal_changed();
10646 static void signalCursorPosition(GtkTextView
*, GParamSpec
*, gpointer widget
)
10648 GtkInstanceTextView
* pThis
= static_cast<GtkInstanceTextView
*>(widget
);
10649 pThis
->signal_cursor_position();
10652 static void signalVAdjustValueChanged(GtkAdjustment
*, gpointer widget
)
10654 GtkInstanceTextView
* pThis
= static_cast<GtkInstanceTextView
*>(widget
);
10655 SolarMutexGuard aGuard
;
10656 pThis
->signal_vadjustment_changed();
10660 GtkInstanceTextView(GtkTextView
* pTextView
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
10661 : GtkInstanceContainer(GTK_CONTAINER(pTextView
), pBuilder
, bTakeOwnership
)
10662 , m_pTextView(pTextView
)
10663 , m_pTextBuffer(gtk_text_view_get_buffer(pTextView
))
10664 , m_pVAdjustment(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(pTextView
)))
10665 , m_nChangedSignalId(g_signal_connect(m_pTextBuffer
, "changed", G_CALLBACK(signalChanged
), this))
10666 , m_nCursorPosSignalId(g_signal_connect(m_pTextBuffer
, "notify::cursor-position", G_CALLBACK(signalCursorPosition
), this))
10667 , m_nVAdjustChangedSignalId(g_signal_connect(m_pVAdjustment
, "value-changed", G_CALLBACK(signalVAdjustValueChanged
), this))
10671 virtual void set_size_request(int nWidth
, int nHeight
) override
10673 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10674 if (GTK_IS_SCROLLED_WINDOW(pParent
))
10676 gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(pParent
), nWidth
);
10677 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(pParent
), nHeight
);
10680 gtk_widget_set_size_request(m_pWidget
, nWidth
, nHeight
);
10683 virtual void set_text(const OUString
& rText
) override
10685 disable_notify_events();
10686 GtkTextBuffer
* pBuffer
= gtk_text_view_get_buffer(m_pTextView
);
10687 OString
sText(OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
));
10688 gtk_text_buffer_set_text(pBuffer
, sText
.getStr(), sText
.getLength());
10689 enable_notify_events();
10692 virtual OUString
get_text() const override
10694 GtkTextBuffer
* pBuffer
= gtk_text_view_get_buffer(m_pTextView
);
10695 GtkTextIter start
, end
;
10696 gtk_text_buffer_get_bounds(pBuffer
, &start
, &end
);
10697 char* pStr
= gtk_text_buffer_get_text(pBuffer
, &start
, &end
, true);
10698 OUString
sRet(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
10703 virtual void replace_selection(const OUString
& rText
) override
10705 disable_notify_events();
10706 GtkTextBuffer
* pBuffer
= gtk_text_view_get_buffer(m_pTextView
);
10707 gtk_text_buffer_delete_selection(pBuffer
, false, gtk_text_view_get_editable(m_pTextView
));
10708 OString
sText(OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
));
10709 gtk_text_buffer_insert_at_cursor(pBuffer
, sText
.getStr(), sText
.getLength());
10710 enable_notify_events();
10713 virtual bool get_selection_bounds(int& rStartPos
, int& rEndPos
) override
10715 GtkTextBuffer
* pBuffer
= gtk_text_view_get_buffer(m_pTextView
);
10716 GtkTextIter start
, end
;
10717 gtk_text_buffer_get_selection_bounds(pBuffer
, &start
, &end
);
10718 rStartPos
= gtk_text_iter_get_offset(&start
);
10719 rEndPos
= gtk_text_iter_get_offset(&end
);
10720 return rStartPos
!= rEndPos
;
10723 virtual void select_region(int nStartPos
, int nEndPos
) override
10725 disable_notify_events();
10726 GtkTextBuffer
* pBuffer
= gtk_text_view_get_buffer(m_pTextView
);
10727 GtkTextIter start
, end
;
10728 gtk_text_buffer_get_iter_at_offset(pBuffer
, &start
, nStartPos
);
10729 gtk_text_buffer_get_iter_at_offset(pBuffer
, &end
, nEndPos
);
10730 gtk_text_buffer_select_range(pBuffer
, &start
, &end
);
10731 GtkTextMark
* mark
= gtk_text_buffer_create_mark(pBuffer
, "scroll", &end
, true);
10732 gtk_text_view_scroll_mark_onscreen(m_pTextView
, mark
);
10733 enable_notify_events();
10736 virtual void set_editable(bool bEditable
) override
10738 gtk_text_view_set_editable(m_pTextView
, bEditable
);
10741 virtual void set_monospace(bool bMonospace
) override
10743 gtk_text_view_set_monospace(m_pTextView
, bMonospace
);
10746 virtual void disable_notify_events() override
10748 g_signal_handler_block(m_pVAdjustment
, m_nVAdjustChangedSignalId
);
10749 g_signal_handler_block(m_pTextBuffer
, m_nCursorPosSignalId
);
10750 g_signal_handler_block(m_pTextBuffer
, m_nChangedSignalId
);
10751 GtkInstanceContainer::disable_notify_events();
10754 virtual void enable_notify_events() override
10756 GtkInstanceContainer::enable_notify_events();
10757 g_signal_handler_unblock(m_pTextBuffer
, m_nChangedSignalId
);
10758 g_signal_handler_unblock(m_pTextBuffer
, m_nCursorPosSignalId
);
10759 g_signal_handler_unblock(m_pVAdjustment
, m_nVAdjustChangedSignalId
);
10762 virtual int vadjustment_get_value() const override
10764 return gtk_adjustment_get_value(m_pVAdjustment
);
10767 virtual void vadjustment_set_value(int value
) override
10769 disable_notify_events();
10770 gtk_adjustment_set_value(m_pVAdjustment
, value
);
10771 enable_notify_events();
10774 virtual int vadjustment_get_upper() const override
10776 return gtk_adjustment_get_upper(m_pVAdjustment
);
10779 virtual int vadjustment_get_lower() const override
10781 return gtk_adjustment_get_lower(m_pVAdjustment
);
10784 virtual int vadjustment_get_page_size() const override
10786 return gtk_adjustment_get_page_size(m_pVAdjustment
);
10789 virtual void show() override
10791 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10792 if (GTK_IS_SCROLLED_WINDOW(pParent
))
10793 gtk_widget_show(pParent
);
10794 gtk_widget_show(m_pWidget
);
10797 virtual void hide() override
10799 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10800 if (GTK_IS_SCROLLED_WINDOW(pParent
))
10801 gtk_widget_hide(pParent
);
10802 gtk_widget_hide(m_pWidget
);
10805 virtual ~GtkInstanceTextView() override
10807 g_signal_handler_disconnect(m_pVAdjustment
, m_nVAdjustChangedSignalId
);
10808 g_signal_handler_disconnect(m_pTextBuffer
, m_nChangedSignalId
);
10809 g_signal_handler_disconnect(m_pTextBuffer
, m_nCursorPosSignalId
);
10815 AtkObject
* (*default_drawing_area_get_accessible
)(GtkWidget
*widget
);
10818 class GtkInstanceDrawingArea
: public GtkInstanceWidget
, public virtual weld::DrawingArea
10821 GtkDrawingArea
* m_pDrawingArea
;
10822 a11yref m_xAccessible
;
10823 AtkObject
*m_pAccessible
;
10824 ScopedVclPtrInstance
<VirtualDevice
> m_xDevice
;
10825 cairo_surface_t
* m_pSurface
;
10826 gulong m_nDrawSignalId
;
10827 gulong m_nStyleUpdatedSignalId
;
10828 gulong m_nQueryTooltip
;
10829 gulong m_nPopupMenu
;
10830 gulong m_nScrollEvent
;
10832 static gboolean
signalDraw(GtkWidget
*, cairo_t
* cr
, gpointer widget
)
10834 GtkInstanceDrawingArea
* pThis
= static_cast<GtkInstanceDrawingArea
*>(widget
);
10835 SolarMutexGuard aGuard
;
10836 pThis
->signal_draw(cr
);
10839 void signal_draw(cairo_t
* cr
)
10842 if (!m_pSurface
|| !gdk_cairo_get_clip_rectangle(cr
, &rect
))
10844 tools::Rectangle
aRect(Point(rect
.x
, rect
.y
), Size(rect
.width
, rect
.height
));
10845 aRect
= m_xDevice
->PixelToLogic(aRect
);
10846 m_xDevice
->Erase(aRect
);
10847 m_aDrawHdl
.Call(std::pair
<vcl::RenderContext
&, const tools::Rectangle
&>(*m_xDevice
, aRect
));
10848 cairo_surface_mark_dirty(m_pSurface
);
10850 cairo_set_source_surface(cr
, m_pSurface
, 0, 0);
10853 tools::Rectangle
aFocusRect(m_aGetFocusRectHdl
.Call(*this));
10854 if (!aFocusRect
.IsEmpty())
10856 gtk_render_focus(gtk_widget_get_style_context(GTK_WIDGET(m_pDrawingArea
)), cr
,
10857 aFocusRect
.Left(), aFocusRect
.Top(), aFocusRect
.GetWidth(), aFocusRect
.GetHeight());
10860 virtual void signal_size_allocate(guint nWidth
, guint nHeight
) override
10862 m_xDevice
->SetOutputSizePixel(Size(nWidth
, nHeight
));
10863 m_pSurface
= get_underlying_cairo_surface(*m_xDevice
);
10864 GtkInstanceWidget::signal_size_allocate(nWidth
, nHeight
);
10866 static void signalStyleUpdated(GtkWidget
*, gpointer widget
)
10868 GtkInstanceDrawingArea
* pThis
= static_cast<GtkInstanceDrawingArea
*>(widget
);
10869 SolarMutexGuard aGuard
;
10870 return pThis
->signal_style_updated();
10872 void signal_style_updated()
10874 m_aStyleUpdatedHdl
.Call(*this);
10876 static gboolean
signalQueryTooltip(GtkWidget
* pGtkWidget
, gint x
, gint y
,
10877 gboolean
/*keyboard_mode*/, GtkTooltip
*tooltip
,
10880 GtkInstanceDrawingArea
* pThis
= static_cast<GtkInstanceDrawingArea
*>(widget
);
10881 tools::Rectangle
aHelpArea(x
, y
);
10882 OUString aTooltip
= pThis
->signal_query_tooltip(aHelpArea
);
10883 if (aTooltip
.isEmpty())
10885 gtk_tooltip_set_text(tooltip
, OUStringToOString(aTooltip
, RTL_TEXTENCODING_UTF8
).getStr());
10886 GdkRectangle aGdkHelpArea
;
10887 aGdkHelpArea
.x
= aHelpArea
.Left();
10888 aGdkHelpArea
.y
= aHelpArea
.Top();
10889 aGdkHelpArea
.width
= aHelpArea
.GetWidth();
10890 aGdkHelpArea
.height
= aHelpArea
.GetHeight();
10891 if (pThis
->SwapForRTL())
10892 aGdkHelpArea
.x
= gtk_widget_get_allocated_width(pGtkWidget
) - aGdkHelpArea
.width
- 1 - aGdkHelpArea
.x
;
10893 gtk_tooltip_set_tip_area(tooltip
, &aGdkHelpArea
);
10896 virtual bool signal_popup_menu(const CommandEvent
& rCEvt
) override
10898 return m_aCommandHdl
.Call(rCEvt
);
10900 bool signal_scroll(GdkEventScroll
* pEvent
)
10902 SalWheelMouseEvent
aEvt(GtkSalFrame::GetWheelEvent(*pEvent
));
10905 aEvt
.mnX
= gtk_widget_get_allocated_width(m_pWidget
) - 1 - aEvt
.mnX
;
10907 CommandWheelMode nMode
;
10908 sal_uInt16 nCode
= aEvt
.mnCode
;
10909 bool bHorz
= aEvt
.mbHorz
;
10910 if (nCode
& KEY_MOD1
)
10911 nMode
= CommandWheelMode::ZOOM
;
10912 else if (nCode
& KEY_MOD2
)
10913 nMode
= CommandWheelMode::DATAZOOM
;
10916 nMode
= CommandWheelMode::SCROLL
;
10917 // #i85450# interpret shift-wheel as horizontal wheel action
10918 if( (nCode
& (KEY_SHIFT
| KEY_MOD1
| KEY_MOD2
| KEY_MOD3
)) == KEY_SHIFT
)
10922 CommandWheelData
aWheelData(aEvt
.mnDelta
, aEvt
.mnNotchDelta
, aEvt
.mnScrollLines
,
10923 nMode
, nCode
, bHorz
, aEvt
.mbDeltaIsPixel
);
10924 CommandEvent
aCEvt(Point(aEvt
.mnX
, aEvt
.mnY
), CommandEventId::Wheel
, true, &aWheelData
);
10925 return m_aCommandHdl
.Call(aCEvt
);
10927 static gboolean
signalScroll(GtkWidget
*, GdkEventScroll
* pEvent
, gpointer widget
)
10929 GtkInstanceDrawingArea
* pThis
= static_cast<GtkInstanceDrawingArea
*>(widget
);
10930 return pThis
->signal_scroll(pEvent
);
10933 GtkInstanceDrawingArea(GtkDrawingArea
* pDrawingArea
, GtkInstanceBuilder
* pBuilder
, const a11yref
& rA11y
, bool bTakeOwnership
)
10934 : GtkInstanceWidget(GTK_WIDGET(pDrawingArea
), pBuilder
, bTakeOwnership
)
10935 , m_pDrawingArea(pDrawingArea
)
10936 , m_xAccessible(rA11y
)
10937 , m_pAccessible(nullptr)
10938 , m_xDevice(DeviceFormat::DEFAULT
)
10939 , m_pSurface(nullptr)
10940 , m_nDrawSignalId(g_signal_connect(m_pDrawingArea
, "draw", G_CALLBACK(signalDraw
), this))
10941 , m_nStyleUpdatedSignalId(g_signal_connect(m_pDrawingArea
,"style-updated", G_CALLBACK(signalStyleUpdated
), this))
10942 , m_nQueryTooltip(g_signal_connect(m_pDrawingArea
, "query-tooltip", G_CALLBACK(signalQueryTooltip
), this))
10943 , m_nPopupMenu(g_signal_connect(m_pDrawingArea
, "popup-menu", G_CALLBACK(signalPopupMenu
), this))
10944 , m_nScrollEvent(g_signal_connect(m_pDrawingArea
, "scroll-event", G_CALLBACK(signalScroll
), this))
10946 gtk_widget_set_has_tooltip(m_pWidget
, true);
10947 g_object_set_data(G_OBJECT(m_pDrawingArea
), "g-lo-GtkInstanceDrawingArea", this);
10948 m_xDevice
->EnableRTL(get_direction());
10951 AtkObject
* GetAtkObject(AtkObject
* pDefaultAccessible
)
10953 if (!m_pAccessible
&& m_xAccessible
.is())
10955 GtkWidget
* pParent
= gtk_widget_get_parent(m_pWidget
);
10956 m_pAccessible
= atk_object_wrapper_new(m_xAccessible
, gtk_widget_get_accessible(pParent
), pDefaultAccessible
);
10957 g_object_ref(m_pAccessible
);
10959 return m_pAccessible
;
10962 virtual void set_direction(bool bRTL
) override
10964 GtkInstanceWidget::set_direction(bRTL
);
10965 m_xDevice
->EnableRTL(bRTL
);
10968 virtual void set_cursor(PointerStyle ePointerStyle
) override
10970 GdkCursor
*pCursor
= GtkSalFrame::getDisplay()->getCursor(ePointerStyle
);
10971 if (!gtk_widget_get_realized(GTK_WIDGET(m_pDrawingArea
)))
10972 gtk_widget_realize(GTK_WIDGET(m_pDrawingArea
));
10973 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(m_pDrawingArea
)), pCursor
);
10976 virtual void queue_draw() override
10978 gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea
));
10981 virtual void queue_draw_area(int x
, int y
, int width
, int height
) override
10983 tools::Rectangle
aRect(Point(x
, y
), Size(width
, height
));
10984 aRect
= m_xDevice
->LogicToPixel(aRect
);
10985 gtk_widget_queue_draw_area(GTK_WIDGET(m_pDrawingArea
), aRect
.Left(), aRect
.Top(), aRect
.GetWidth(), aRect
.GetHeight());
10988 virtual void queue_resize() override
10990 gtk_widget_queue_resize(GTK_WIDGET(m_pDrawingArea
));
10993 virtual a11yref
get_accessible_parent() override
10995 //get_accessible_parent should only be needed for the vcl implementation,
10996 //in the gtk impl the native AtkObject parent set via
10997 //atk_object_wrapper_new(m_xAccessible, gtk_widget_get_accessible(pParent));
10998 //should negate the need.
10999 assert(false && "get_accessible_parent should only be called on a vcl impl");
11000 return uno::Reference
<css::accessibility::XAccessible
>();
11003 virtual a11yrelationset
get_accessible_relation_set() override
11005 //get_accessible_relation_set should only be needed for the vcl implementation,
11006 //in the gtk impl the native equivalent should negate the need.
11007 assert(false && "get_accessible_parent should only be called on a vcl impl");
11008 return uno::Reference
<css::accessibility::XAccessibleRelationSet
>();
11011 virtual Point
get_accessible_location() override
11013 AtkObject
* pAtkObject
= default_drawing_area_get_accessible(m_pWidget
);
11015 if (pAtkObject
&& ATK_IS_COMPONENT(pAtkObject
))
11016 atk_component_get_extents(ATK_COMPONENT(pAtkObject
), &x
, &y
, nullptr, nullptr, ATK_XY_WINDOW
);
11017 return Point(x
, y
);
11020 virtual void set_accessible_name(const OUString
& rName
) override
11022 AtkObject
* pAtkObject
= default_drawing_area_get_accessible(m_pWidget
);
11025 atk_object_set_name(pAtkObject
, OUStringToOString(rName
, RTL_TEXTENCODING_UTF8
).getStr());
11028 virtual OUString
get_accessible_name() const override
11030 AtkObject
* pAtkObject
= default_drawing_area_get_accessible(m_pWidget
);
11031 const char* pStr
= pAtkObject
? atk_object_get_name(pAtkObject
) : nullptr;
11032 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
11035 virtual OUString
get_accessible_description() const override
11037 AtkObject
* pAtkObject
= default_drawing_area_get_accessible(m_pWidget
);
11038 const char* pStr
= pAtkObject
? atk_object_get_description(pAtkObject
) : nullptr;
11039 return OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
11042 virtual ~GtkInstanceDrawingArea() override
11044 g_object_steal_data(G_OBJECT(m_pDrawingArea
), "g-lo-GtkInstanceDrawingArea");
11046 g_object_unref(m_pAccessible
);
11047 css::uno::Reference
<css::lang::XComponent
> xComp(m_xAccessible
, css::uno::UNO_QUERY
);
11050 g_signal_handler_disconnect(m_pDrawingArea
, m_nScrollEvent
);
11051 g_signal_handler_disconnect(m_pDrawingArea
, m_nPopupMenu
);
11052 g_signal_handler_disconnect(m_pDrawingArea
, m_nQueryTooltip
);
11053 g_signal_handler_disconnect(m_pDrawingArea
, m_nStyleUpdatedSignalId
);
11054 g_signal_handler_disconnect(m_pDrawingArea
, m_nDrawSignalId
);
11057 virtual OutputDevice
& get_ref_device() override
11063 #define g_signal_handlers_block_by_data(instance, data) \
11064 g_signal_handlers_block_matched ((instance), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, (data))
11066 /* tdf#125388 on measuring each row, the GtkComboBox GtkTreeMenu will call
11067 its area_apply_attributes_cb function on the row, but that calls
11068 gtk_tree_menu_get_path_item which then loops through each child of the
11069 menu looking for the widget of the row, so performance drops to useless.
11071 All area_apply_attributes_cb does it set menu item sensitivity, so block it from running
11072 with fragile hackery which assumes that the unwanted callback is the only one with a
11073 user_data of the ComboBox GtkTreeMenu */
11074 static void disable_area_apply_attributes_cb(GtkWidget
* pItem
, gpointer userdata
)
11076 GtkMenuItem
* pMenuItem
= GTK_MENU_ITEM(pItem
);
11077 GtkWidget
* child
= gtk_bin_get_child(GTK_BIN(pMenuItem
));
11080 GtkCellView
* pCellView
= GTK_CELL_VIEW(child
);
11081 GtkCellLayout
* pCellLayout
= GTK_CELL_LAYOUT(pCellView
);
11082 GtkCellArea
* pCellArea
= gtk_cell_layout_get_area(pCellLayout
);
11083 g_signal_handlers_block_by_data(pCellArea
, userdata
);
11086 class GtkInstanceComboBox
: public GtkInstanceContainer
, public vcl::ISearchableStringList
, public virtual weld::ComboBox
11089 GtkComboBox
* m_pComboBox
;
11090 GtkTreeModel
* m_pTreeModel
;
11091 GtkCellRenderer
* m_pTextRenderer
;
11093 GtkWidget
* m_pToggleButton
;
11094 std::unique_ptr
<comphelper::string::NaturalStringSorter
> m_xSorter
;
11095 vcl::QuickSelectionEngine m_aQuickSelectionEngine
;
11096 std::vector
<int> m_aSeparatorRows
;
11097 bool m_bPopupActive
;
11098 bool m_bAutoComplete
;
11099 bool m_bAutoCompleteCaseSensitive
;
11100 gulong m_nToggleFocusInSignalId
;
11101 gulong m_nToggleFocusOutSignalId
;
11102 gulong m_nChangedSignalId
;
11103 gulong m_nPopupShownSignalId
;
11104 gulong m_nKeyPressEventSignalId
;
11105 gulong m_nEntryInsertTextSignalId
;
11106 gulong m_nEntryActivateSignalId
;
11107 gulong m_nEntryFocusInSignalId
;
11108 gulong m_nEntryFocusOutSignalId
;
11109 guint m_nAutoCompleteIdleId
;
11111 static gboolean
idleAutoComplete(gpointer widget
)
11113 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11114 pThis
->auto_complete();
11118 void auto_complete()
11120 m_nAutoCompleteIdleId
= 0;
11121 OUString aStartText
= get_active_text();
11122 int nStartPos
, nEndPos
;
11123 get_entry_selection_bounds(nStartPos
, nEndPos
);
11124 int nMaxSelection
= std::max(nStartPos
, nEndPos
);
11125 if (nMaxSelection
!= aStartText
.getLength())
11128 disable_notify_events();
11129 int nActive
= get_active();
11130 int nStart
= nActive
;
11137 if (!m_bAutoCompleteCaseSensitive
)
11139 // Try match case insensitive from current position
11140 nPos
= starts_with(m_pTreeModel
, aStartText
, 0, nStart
, false);
11141 if (nPos
== -1 && nStart
!= 0)
11143 // Try match case insensitive, but from start
11144 nPos
= starts_with(m_pTreeModel
, aStartText
, 0, 0, false);
11150 // Try match case sensitive from current position
11151 nPos
= starts_with(m_pTreeModel
, aStartText
, 0, nStart
, true);
11152 if (nPos
== -1 && nStart
!= 0)
11154 // Try match case sensitive, but from start
11155 nPos
= starts_with(m_pTreeModel
, aStartText
, 0, 0, true);
11161 OUString aText
= get_text(nPos
);
11162 if (aText
!= aStartText
)
11163 set_active_text(aText
);
11164 select_entry_region(aText
.getLength(), aStartText
.getLength());
11166 enable_notify_events();
11169 static void signalEntryInsertText(GtkEntry
* pEntry
, const gchar
* pNewText
, gint nNewTextLength
,
11170 gint
* position
, gpointer widget
)
11172 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11173 SolarMutexGuard aGuard
;
11174 pThis
->signal_entry_insert_text(pEntry
, pNewText
, nNewTextLength
, position
);
11177 void signal_entry_insert_text(GtkEntry
* pEntry
, const gchar
* pNewText
, gint nNewTextLength
, gint
* position
)
11179 // first filter inserted text
11180 if (m_aEntryInsertTextHdl
.IsSet())
11182 OUString
sText(pNewText
, nNewTextLength
, RTL_TEXTENCODING_UTF8
);
11183 const bool bContinue
= m_aEntryInsertTextHdl
.Call(sText
);
11184 if (bContinue
&& !sText
.isEmpty())
11186 OString
sFinalText(OUStringToOString(sText
, RTL_TEXTENCODING_UTF8
));
11187 g_signal_handlers_block_by_func(pEntry
, gpointer(signalEntryInsertText
), this);
11188 gtk_editable_insert_text(GTK_EDITABLE(pEntry
), sFinalText
.getStr(), sFinalText
.getLength(), position
);
11189 g_signal_handlers_unblock_by_func(pEntry
, gpointer(signalEntryInsertText
), this);
11191 g_signal_stop_emission_by_name(pEntry
, "insert-text");
11193 if (m_bAutoComplete
)
11195 // now check for autocompletes
11196 if (m_nAutoCompleteIdleId
)
11197 g_source_remove(m_nAutoCompleteIdleId
);
11198 m_nAutoCompleteIdleId
= g_idle_add(idleAutoComplete
, this);
11202 static void signalChanged(GtkComboBox
*, gpointer widget
)
11204 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11205 SolarMutexGuard aGuard
;
11206 pThis
->signal_changed();
11209 static void signalPopupToggled(GtkComboBox
*, GParamSpec
*, gpointer widget
)
11211 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11212 pThis
->signal_popup_toggled();
11215 virtual void signal_popup_toggled() override
11217 m_aQuickSelectionEngine
.Reset();
11218 gboolean
bIsShown(false);
11219 g_object_get(m_pComboBox
, "popup-shown", &bIsShown
, nullptr);
11220 if (m_bPopupActive
!= bool(bIsShown
))
11222 m_bPopupActive
= bIsShown
;
11223 ComboBox::signal_popup_toggled();
11224 //restore focus to the entry view when the popup is gone, which
11225 //is what the vcl case does, to ease the transition a little
11226 gtk_widget_grab_focus(m_pWidget
);
11230 static void signalEntryFocusIn(GtkWidget
*, GdkEvent
*, gpointer widget
)
11232 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11233 pThis
->signal_entry_focus_in();
11236 void signal_entry_focus_in()
11241 static void signalEntryFocusOut(GtkWidget
*, GdkEvent
*, gpointer widget
)
11243 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11244 pThis
->signal_entry_focus_out();
11247 void signal_entry_focus_out()
11249 // if we have an untidy selection on losing focus remove the selection
11250 int nStartPos
, nEndPos
;
11251 if (get_entry_selection_bounds(nStartPos
, nEndPos
))
11253 int nMin
= std::min(nStartPos
, nEndPos
);
11254 int nMax
= std::max(nStartPos
, nEndPos
);
11255 if (nMin
!= 0 || nMax
!= get_active_text().getLength())
11256 select_entry_region(0, 0);
11258 signal_focus_out();
11261 static void signalEntryActivate(GtkEntry
*, gpointer widget
)
11263 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11264 pThis
->signal_entry_activate();
11267 void signal_entry_activate()
11269 if (m_aEntryActivateHdl
.IsSet())
11271 SolarMutexGuard aGuard
;
11272 if (m_aEntryActivateHdl
.Call(*this))
11273 g_signal_stop_emission_by_name(get_entry(), "activate");
11277 OUString
get(int pos
, int col
) const
11281 if (gtk_tree_model_iter_nth_child(m_pTreeModel
, &iter
, nullptr, pos
))
11284 gtk_tree_model_get(m_pTreeModel
, &iter
, col
, &pStr
, -1);
11285 sRet
= OUString(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
11291 void set(int pos
, int col
, const OUString
& rText
)
11294 if (gtk_tree_model_iter_nth_child(m_pTreeModel
, &iter
, nullptr, pos
))
11296 OString
aStr(OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
));
11297 gtk_list_store_set(GTK_LIST_STORE(m_pTreeModel
), &iter
, col
, aStr
.getStr(), -1);
11301 int find(const OUString
& rStr
, int col
) const
11304 if (!gtk_tree_model_get_iter_first(m_pTreeModel
, &iter
))
11307 OString
aStr(OUStringToOString(rStr
, RTL_TEXTENCODING_UTF8
).getStr());
11312 gtk_tree_model_get(m_pTreeModel
, &iter
, col
, &pStr
, -1);
11313 const bool bEqual
= g_strcmp0(pStr
, aStr
.getStr()) == 0;
11318 } while (gtk_tree_model_iter_next(m_pTreeModel
, &iter
));
11323 GtkEntry
* get_entry()
11325 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11326 if (!GTK_IS_ENTRY(pChild
))
11328 return GTK_ENTRY(pChild
);
11331 bool separator_function(int nIndex
)
11333 return std::find(m_aSeparatorRows
.begin(), m_aSeparatorRows
.end(), nIndex
) != m_aSeparatorRows
.end();
11336 static gboolean
separatorFunction(GtkTreeModel
* pTreeModel
, GtkTreeIter
* pIter
, gpointer widget
)
11338 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11339 GtkTreePath
* path
= gtk_tree_model_get_path(pTreeModel
, pIter
);
11342 gint
* indices
= gtk_tree_path_get_indices_with_depth(path
, &depth
);
11343 int nIndex
= indices
[depth
-1];
11345 gtk_tree_path_free(path
);
11346 return pThis
->separator_function(nIndex
);
11349 // https://gitlab.gnome.org/GNOME/gtk/issues/310
11351 // in the absence of a built-in solution
11352 // a) support typeahead for the case where there is no entry widget, typing ahead
11353 // into the button itself will select via the vcl selection engine, a matching
11355 static gboolean
signalKeyPress(GtkWidget
*, GdkEventKey
* pEvent
, gpointer widget
)
11357 GtkInstanceComboBox
* pThis
= static_cast<GtkInstanceComboBox
*>(widget
);
11358 return pThis
->signal_key_press(pEvent
);
11361 bool signal_key_press(const GdkEventKey
* pEvent
)
11363 KeyEvent
aKEvt(GtkToVcl(*pEvent
));
11365 vcl::KeyCode aKeyCode
= aKEvt
.GetKeyCode();
11367 bool bDone
= false;
11369 switch (aKeyCode
.GetCode())
11380 m_aQuickSelectionEngine
.Reset();
11383 bDone
= m_aQuickSelectionEngine
.HandleKeyEvent(aKEvt
);
11390 vcl::StringEntryIdentifier
typeahead_getEntry(int nPos
, OUString
& out_entryText
) const
11392 int nEntryCount(get_count());
11393 if (nPos
>= nEntryCount
)
11395 out_entryText
= get_text(nPos
);
11397 // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
11399 return reinterpret_cast<vcl::StringEntryIdentifier
>(nPos
+ 1);
11402 static int typeahead_getEntryPos(vcl::StringEntryIdentifier entry
)
11404 // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
11405 return reinterpret_cast<sal_Int64
>(entry
) - 1;
11408 int get_selected_entry() const
11410 if (m_bPopupActive
&& m_pMenu
)
11412 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(m_pMenu
));
11413 auto nRet
= g_list_index(pChildren
, gtk_menu_shell_get_selected_item(GTK_MENU_SHELL(m_pMenu
)));
11414 g_list_free(pChildren
);
11418 return get_active();
11421 void set_selected_entry(int nSelect
)
11423 if (m_bPopupActive
&& m_pMenu
)
11425 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(m_pMenu
));
11426 gtk_menu_shell_select_item(GTK_MENU_SHELL(m_pMenu
), GTK_WIDGET(g_list_nth_data(pChildren
, nSelect
)));
11427 g_list_free(pChildren
);
11430 set_active(nSelect
);
11433 virtual vcl::StringEntryIdentifier
CurrentEntry(OUString
& out_entryText
) const override
11435 int nCurrentPos
= get_selected_entry();
11436 return typeahead_getEntry((nCurrentPos
== -1) ? 0 : nCurrentPos
, out_entryText
);
11439 virtual vcl::StringEntryIdentifier
NextEntry(vcl::StringEntryIdentifier currentEntry
, OUString
& out_entryText
) const override
11441 int nNextPos
= typeahead_getEntryPos(currentEntry
) + 1;
11442 return typeahead_getEntry(nNextPos
, out_entryText
);
11445 virtual void SelectEntry(vcl::StringEntryIdentifier entry
) override
11447 int nSelect
= typeahead_getEntryPos(entry
);
11448 if (nSelect
== get_selected_entry())
11450 // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
11451 // to select the given entry by typing its starting letters. No need to act.
11456 int nCount
= get_count();
11457 if (nSelect
>= nCount
)
11458 nSelect
= nCount
? nCount
-1 : -1;
11460 set_selected_entry(nSelect
);
11463 // https://gitlab.gnome.org/GNOME/gtk/issues/310
11465 // in the absence of a built-in solution
11466 // b) support typeahead for the menu itself, typing into the menu will
11467 // select via the vcl selection engine, a matching entry. Clearly
11468 // this is cheating, brittle and not a long term solution.
11469 void install_menu_typeahead()
11471 AtkObject
* pAtkObj
= gtk_combo_box_get_popup_accessible(m_pComboBox
);
11474 if (!GTK_IS_ACCESSIBLE(pAtkObj
))
11476 GtkWidget
* pWidget
= gtk_accessible_get_widget(GTK_ACCESSIBLE(pAtkObj
));
11479 if (!GTK_IS_MENU(pWidget
))
11481 m_pMenu
= GTK_MENU(pWidget
);
11483 guint nSignalId
= g_signal_lookup("key-press-event", GTK_TYPE_MENU
);
11484 gulong nOriginalMenuKeyPressEventId
= g_signal_handler_find(m_pMenu
, G_SIGNAL_MATCH_DATA
, nSignalId
, 0,
11485 nullptr, nullptr, m_pComboBox
);
11487 g_signal_handler_block(m_pMenu
, nOriginalMenuKeyPressEventId
);
11488 g_signal_connect(m_pMenu
, "key-press-event", G_CALLBACK(signalKeyPress
), this);
11491 static void find_toggle_button(GtkWidget
*pWidget
, gpointer user_data
)
11493 if (g_strcmp0(gtk_widget_get_name(pWidget
), "GtkToggleButton") == 0)
11495 GtkWidget
**ppToggleButton
= static_cast<GtkWidget
**>(user_data
);
11496 *ppToggleButton
= pWidget
;
11498 else if (GTK_IS_CONTAINER(pWidget
))
11499 gtk_container_forall(GTK_CONTAINER(pWidget
), find_toggle_button
, user_data
);
11503 GtkInstanceComboBox(GtkComboBox
* pComboBox
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
11504 : GtkInstanceContainer(GTK_CONTAINER(pComboBox
), pBuilder
, bTakeOwnership
)
11505 , m_pComboBox(pComboBox
)
11506 , m_pTreeModel(gtk_combo_box_get_model(m_pComboBox
))
11508 , m_pToggleButton(nullptr)
11509 , m_aQuickSelectionEngine(*this)
11510 , m_bPopupActive(false)
11511 , m_bAutoComplete(false)
11512 , m_bAutoCompleteCaseSensitive(false)
11513 , m_nToggleFocusInSignalId(0)
11514 , m_nToggleFocusOutSignalId(0)
11515 , m_nChangedSignalId(g_signal_connect(m_pComboBox
, "changed", G_CALLBACK(signalChanged
), this))
11516 , m_nPopupShownSignalId(g_signal_connect(m_pComboBox
, "notify::popup-shown", G_CALLBACK(signalPopupToggled
), this))
11517 , m_nAutoCompleteIdleId(0)
11519 GList
* cells
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_pComboBox
));
11520 if (!g_list_length(cells
))
11522 //Always use the same text column renderer layout
11523 m_pTextRenderer
= gtk_cell_renderer_text_new();
11524 gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(m_pComboBox
), m_pTextRenderer
, true);
11525 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(m_pComboBox
), m_pTextRenderer
, "text", 0, nullptr);
11529 m_pTextRenderer
= static_cast<GtkCellRenderer
*>(cells
->data
);
11530 if (g_list_length(cells
) == 2)
11532 //The ComboBox is always going to show the column associated with
11533 //the entry when there is one, left to its own devices this image
11534 //column will be after it, but we want it before
11535 gtk_cell_layout_reorder(GTK_CELL_LAYOUT(m_pComboBox
), m_pTextRenderer
, 1);
11538 g_list_free(cells
);
11540 if (GtkEntry
* pEntry
= get_entry())
11542 m_bAutoComplete
= true;
11543 m_nEntryInsertTextSignalId
= g_signal_connect(pEntry
, "insert-text", G_CALLBACK(signalEntryInsertText
), this);
11544 m_nEntryActivateSignalId
= g_signal_connect(pEntry
, "activate", G_CALLBACK(signalEntryActivate
), this);
11545 m_nEntryFocusInSignalId
= g_signal_connect(pEntry
, "focus-in-event", G_CALLBACK(signalEntryFocusIn
), this);
11546 m_nEntryFocusOutSignalId
= g_signal_connect(pEntry
, "focus-out-event", G_CALLBACK(signalEntryFocusOut
), this);
11547 m_nKeyPressEventSignalId
= 0;
11551 m_nEntryInsertTextSignalId
= 0;
11552 m_nEntryActivateSignalId
= 0;
11553 m_nEntryFocusInSignalId
= 0;
11554 m_nEntryFocusOutSignalId
= 0;
11555 m_nKeyPressEventSignalId
= g_signal_connect(m_pWidget
, "key-press-event", G_CALLBACK(signalKeyPress
), this);
11558 find_toggle_button(GTK_WIDGET(m_pComboBox
), &m_pToggleButton
);
11560 install_menu_typeahead();
11563 virtual int get_active() const override
11565 return gtk_combo_box_get_active(m_pComboBox
);
11568 virtual OUString
get_active_id() const override
11570 const gchar
* pText
= gtk_combo_box_get_active_id(m_pComboBox
);
11571 return OUString(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
11574 virtual void set_active_id(const OUString
& rStr
) override
11576 disable_notify_events();
11577 OString
aId(OUStringToOString(rStr
, RTL_TEXTENCODING_UTF8
));
11578 gtk_combo_box_set_active_id(m_pComboBox
, aId
.getStr());
11579 enable_notify_events();
11582 virtual void set_size_request(int nWidth
, int nHeight
) override
11584 // tweak the cell render to get a narrower size to stick
11585 GList
* cells
= gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_pComboBox
));
11586 GtkCellRenderer
* cell
= static_cast<GtkCellRenderer
*>(cells
->data
);
11590 // this bit isn't great, I really want to be able to ellipse the text in the comboboxtext itself and let
11591 // the popup menu render them in full, in the interim ellipse both of them
11592 g_object_set(G_OBJECT(m_pTextRenderer
), "ellipsize", PANGO_ELLIPSIZE_MIDDLE
, nullptr);
11594 // to find out how much of the width of the combobox belongs to the cell, set
11595 // the cell and widget to the min cell width and see what the difference is
11597 gtk_cell_renderer_get_preferred_width(cell
, m_pWidget
, &min
, nullptr);
11598 gtk_cell_renderer_set_fixed_size(cell
, min
, -1);
11599 gtk_widget_set_size_request(m_pWidget
, min
, -1);
11600 int nNonCellWidth
= get_preferred_size().Width() - min
;
11602 int nCellWidth
= nWidth
- nNonCellWidth
;
11603 if (nCellWidth
>= 0)
11605 // now set the cell to the max width which it can be within the
11606 // requested widget width
11607 gtk_cell_renderer_set_fixed_size(cell
, nWidth
- nNonCellWidth
, -1);
11612 g_object_set(G_OBJECT(m_pTextRenderer
), "ellipsize", PANGO_ELLIPSIZE_NONE
, nullptr);
11613 gtk_cell_renderer_set_fixed_size(cell
, -1, -1);
11616 g_list_free(cells
);
11618 gtk_widget_set_size_request(m_pWidget
, nWidth
, nHeight
);
11621 virtual void set_active(int pos
) override
11623 disable_notify_events();
11624 gtk_combo_box_set_active(m_pComboBox
, pos
);
11625 enable_notify_events();
11628 virtual OUString
get_active_text() const override
11630 if (gtk_combo_box_get_has_entry(m_pComboBox
))
11632 GtkWidget
*pEntry
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11633 const gchar
* pText
= gtk_entry_get_text(GTK_ENTRY(pEntry
));
11634 return OUString(pText
, pText
? strlen(pText
) : 0, RTL_TEXTENCODING_UTF8
);
11638 if (!gtk_combo_box_get_active_iter(m_pComboBox
, &iter
))
11641 gint col
= gtk_combo_box_get_entry_text_column(m_pComboBox
);
11642 gchar
* pStr
= nullptr;
11643 gtk_tree_model_get(m_pTreeModel
, &iter
, col
, &pStr
, -1);
11644 OUString
sRet(pStr
, pStr
? strlen(pStr
) : 0, RTL_TEXTENCODING_UTF8
);
11650 virtual OUString
get_text(int pos
) const override
11652 return get(pos
, 0);
11655 virtual OUString
get_id(int pos
) const override
11657 gint id_column
= gtk_combo_box_get_id_column(m_pComboBox
);
11658 return get(pos
, id_column
);
11661 virtual void set_id(int pos
, const OUString
& rId
) override
11663 gint id_column
= gtk_combo_box_get_id_column(m_pComboBox
);
11664 set(pos
, id_column
, rId
);
11667 // https://gitlab.gnome.org/GNOME/gtk/issues/94
11668 // when a super tall combobox menu is activated, and the selected entry is sufficiently
11669 // far down the list, then the menu doesn't appear under wayland
11670 void bodge_wayland_menu_not_appearing()
11676 #if defined(GDK_WINDOWING_WAYLAND)
11677 GdkDisplay
*pDisplay
= gtk_widget_get_display(m_pWidget
);
11678 if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay
))
11680 gtk_combo_box_set_wrap_width(m_pComboBox
, get_count() > 30 ? 1 : 0);
11685 // https://gitlab.gnome.org/GNOME/gtk/issues/1910
11686 // has_entry long menus take forever to appear (tdf#125388)
11687 void bodge_area_apply_attributes_cb()
11689 gtk_container_foreach(GTK_CONTAINER(m_pMenu
), disable_area_apply_attributes_cb
, m_pMenu
);
11692 virtual void insert_vector(const std::vector
<weld::ComboBoxEntry
>& rItems
, bool bKeepExisting
) override
11695 if (!bKeepExisting
)
11698 for (const auto& rItem
: rItems
)
11700 insert_row(GTK_LIST_STORE(m_pTreeModel
), iter
, -1, rItem
.sId
.isEmpty() ? nullptr : &rItem
.sId
,
11701 rItem
.sString
, rItem
.sImage
.isEmpty() ? nullptr : &rItem
.sImage
, nullptr);
11706 virtual void remove(int pos
) override
11708 disable_notify_events();
11710 gtk_tree_model_iter_nth_child(m_pTreeModel
, &iter
, nullptr, pos
);
11711 gtk_list_store_remove(GTK_LIST_STORE(m_pTreeModel
), &iter
);
11712 m_aSeparatorRows
.erase(std::remove(m_aSeparatorRows
.begin(), m_aSeparatorRows
.end(), pos
), m_aSeparatorRows
.end());
11713 enable_notify_events();
11714 bodge_wayland_menu_not_appearing();
11717 virtual void insert(int pos
, const OUString
& rText
, const OUString
* pId
, const OUString
* pIconName
, VirtualDevice
* pImageSurface
) override
11719 disable_notify_events();
11721 insert_row(GTK_LIST_STORE(m_pTreeModel
), iter
, pos
, pId
, rText
, pIconName
, pImageSurface
);
11722 enable_notify_events();
11723 bodge_wayland_menu_not_appearing();
11726 virtual void insert_separator(int pos
, const OUString
& rId
) override
11728 disable_notify_events();
11730 pos
= pos
== -1 ? get_count() : pos
;
11731 m_aSeparatorRows
.push_back(pos
);
11732 if (!gtk_combo_box_get_row_separator_func(m_pComboBox
))
11733 gtk_combo_box_set_row_separator_func(m_pComboBox
, separatorFunction
, this, nullptr);
11734 insert_row(GTK_LIST_STORE(m_pTreeModel
), iter
, pos
, &rId
, "", nullptr, nullptr);
11735 enable_notify_events();
11736 bodge_wayland_menu_not_appearing();
11739 virtual int get_count() const override
11741 return gtk_tree_model_iter_n_children(m_pTreeModel
, nullptr);
11744 virtual int find_text(const OUString
& rStr
) const override
11746 return find(rStr
, 0);
11749 virtual int find_id(const OUString
& rId
) const override
11751 gint id_column
= gtk_combo_box_get_id_column(m_pComboBox
);
11752 return find(rId
, id_column
);
11755 virtual void clear() override
11757 disable_notify_events();
11758 gtk_list_store_clear(GTK_LIST_STORE(m_pTreeModel
));
11759 m_aSeparatorRows
.clear();
11760 gtk_combo_box_set_row_separator_func(m_pComboBox
, nullptr, nullptr, nullptr);
11761 enable_notify_events();
11762 bodge_wayland_menu_not_appearing();
11765 virtual void make_sorted() override
11767 m_xSorter
.reset(new comphelper::string::NaturalStringSorter(
11768 ::comphelper::getProcessComponentContext(),
11769 Application::GetSettings().GetUILanguageTag().getLocale()));
11770 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeModel
);
11771 gtk_tree_sortable_set_sort_column_id(pSortable
, 0, GTK_SORT_ASCENDING
);
11772 gtk_tree_sortable_set_sort_func(pSortable
, 0, default_sort_func
, m_xSorter
.get(), nullptr);
11775 virtual bool has_entry() const override
11777 return gtk_combo_box_get_has_entry(m_pComboBox
);
11780 virtual void set_entry_message_type(weld::EntryMessageType eType
) override
11782 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11783 assert(GTK_IS_ENTRY(pChild
));
11784 GtkEntry
* pEntry
= GTK_ENTRY(pChild
);
11785 ::set_entry_message_type(pEntry
, eType
);
11788 virtual void set_entry_text(const OUString
& rText
) override
11790 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11791 assert(pChild
&& GTK_IS_ENTRY(pChild
));
11792 GtkEntry
* pEntry
= GTK_ENTRY(pChild
);
11793 disable_notify_events();
11794 gtk_entry_set_text(pEntry
, OUStringToOString(rText
, RTL_TEXTENCODING_UTF8
).getStr());
11795 enable_notify_events();
11798 virtual void set_entry_width_chars(int nChars
) override
11800 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11801 assert(pChild
&& GTK_IS_ENTRY(pChild
));
11802 GtkEntry
* pEntry
= GTK_ENTRY(pChild
);
11803 disable_notify_events();
11804 gtk_entry_set_width_chars(pEntry
, nChars
);
11805 gtk_entry_set_max_width_chars(pEntry
, nChars
);
11806 enable_notify_events();
11809 virtual void set_entry_max_length(int nChars
) override
11811 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11812 assert(pChild
&& GTK_IS_ENTRY(pChild
));
11813 GtkEntry
* pEntry
= GTK_ENTRY(pChild
);
11814 disable_notify_events();
11815 gtk_entry_set_max_length(pEntry
, nChars
);
11816 enable_notify_events();
11819 virtual void select_entry_region(int nStartPos
, int nEndPos
) override
11821 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11822 assert(pChild
&& GTK_IS_ENTRY(pChild
));
11823 GtkEntry
* pEntry
= GTK_ENTRY(pChild
);
11824 disable_notify_events();
11825 gtk_editable_select_region(GTK_EDITABLE(pEntry
), nStartPos
, nEndPos
);
11826 enable_notify_events();
11829 virtual bool get_entry_selection_bounds(int& rStartPos
, int &rEndPos
) override
11831 GtkWidget
* pChild
= gtk_bin_get_child(GTK_BIN(m_pComboBox
));
11832 assert(pChild
&& GTK_IS_ENTRY(pChild
));
11833 GtkEntry
* pEntry
= GTK_ENTRY(pChild
);
11834 return gtk_editable_get_selection_bounds(GTK_EDITABLE(pEntry
), &rStartPos
, &rEndPos
);
11837 virtual void set_entry_completion(bool bEnable
, bool bCaseSensitive
) override
11839 m_bAutoComplete
= bEnable
;
11840 m_bAutoCompleteCaseSensitive
= bCaseSensitive
;
11843 virtual void disable_notify_events() override
11845 if (GtkEntry
* pEntry
= get_entry())
11847 g_signal_handler_block(pEntry
, m_nEntryInsertTextSignalId
);
11848 g_signal_handler_block(pEntry
, m_nEntryActivateSignalId
);
11849 g_signal_handler_block(pEntry
, m_nEntryFocusInSignalId
);
11850 g_signal_handler_block(pEntry
, m_nEntryFocusOutSignalId
);
11853 g_signal_handler_block(m_pComboBox
, m_nKeyPressEventSignalId
);
11854 if (m_nToggleFocusInSignalId
)
11855 g_signal_handler_block(m_pToggleButton
, m_nToggleFocusInSignalId
);
11856 if (m_nToggleFocusOutSignalId
)
11857 g_signal_handler_block(m_pToggleButton
, m_nToggleFocusOutSignalId
);
11858 g_signal_handler_block(m_pComboBox
, m_nChangedSignalId
);
11859 g_signal_handler_block(m_pComboBox
, m_nPopupShownSignalId
);
11860 GtkInstanceContainer::disable_notify_events();
11863 virtual void enable_notify_events() override
11865 GtkInstanceContainer::enable_notify_events();
11866 g_signal_handler_unblock(m_pComboBox
, m_nPopupShownSignalId
);
11867 g_signal_handler_unblock(m_pComboBox
, m_nChangedSignalId
);
11868 if (m_nToggleFocusInSignalId
)
11869 g_signal_handler_unblock(m_pToggleButton
, m_nToggleFocusInSignalId
);
11870 if (m_nToggleFocusOutSignalId
)
11871 g_signal_handler_unblock(m_pToggleButton
, m_nToggleFocusOutSignalId
);
11872 if (GtkEntry
* pEntry
= get_entry())
11874 g_signal_handler_unblock(pEntry
, m_nEntryActivateSignalId
);
11875 g_signal_handler_unblock(pEntry
, m_nEntryFocusInSignalId
);
11876 g_signal_handler_unblock(pEntry
, m_nEntryFocusOutSignalId
);
11877 g_signal_handler_unblock(pEntry
, m_nEntryInsertTextSignalId
);
11880 g_signal_handler_unblock(m_pComboBox
, m_nKeyPressEventSignalId
);
11883 virtual void freeze() override
11885 disable_notify_events();
11886 g_object_ref(m_pTreeModel
);
11887 GtkInstanceContainer::freeze();
11888 gtk_combo_box_set_model(m_pComboBox
, nullptr);
11891 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeModel
);
11892 gtk_tree_sortable_set_sort_column_id(pSortable
, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID
, GTK_SORT_ASCENDING
);
11894 enable_notify_events();
11897 virtual void thaw() override
11899 disable_notify_events();
11902 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(m_pTreeModel
);
11903 gtk_tree_sortable_set_sort_column_id(pSortable
, 0, GTK_SORT_ASCENDING
);
11905 gtk_combo_box_set_model(m_pComboBox
, m_pTreeModel
);
11906 GtkInstanceContainer::thaw();
11907 g_object_unref(m_pTreeModel
);
11908 enable_notify_events();
11910 bodge_wayland_menu_not_appearing();
11911 bodge_area_apply_attributes_cb();
11914 virtual bool get_popup_shown() const override
11916 return m_bPopupActive
;
11919 virtual void connect_focus_in(const Link
<Widget
&, void>& rLink
) override
11921 m_nToggleFocusInSignalId
= g_signal_connect(m_pToggleButton
, "focus-in-event", G_CALLBACK(signalFocusIn
), this);
11922 weld::Widget::connect_focus_in(rLink
);
11925 virtual void connect_focus_out(const Link
<Widget
&, void>& rLink
) override
11927 m_nToggleFocusOutSignalId
= g_signal_connect(m_pToggleButton
, "focus-out-event", G_CALLBACK(signalFocusOut
), this);
11928 weld::Widget::connect_focus_out(rLink
);
11931 virtual bool has_focus() const override
11933 return gtk_widget_has_focus(m_pToggleButton
) || GtkInstanceWidget::has_focus();
11936 virtual ~GtkInstanceComboBox() override
11938 if (m_nAutoCompleteIdleId
)
11939 g_source_remove(m_nAutoCompleteIdleId
);
11940 if (GtkEntry
* pEntry
= get_entry())
11942 g_signal_handler_disconnect(pEntry
, m_nEntryInsertTextSignalId
);
11943 g_signal_handler_disconnect(pEntry
, m_nEntryActivateSignalId
);
11944 g_signal_handler_disconnect(pEntry
, m_nEntryFocusInSignalId
);
11945 g_signal_handler_disconnect(pEntry
, m_nEntryFocusOutSignalId
);
11948 g_signal_handler_disconnect(m_pComboBox
, m_nKeyPressEventSignalId
);
11949 if (m_nToggleFocusInSignalId
)
11950 g_signal_handler_disconnect(m_pToggleButton
, m_nToggleFocusInSignalId
);
11951 if (m_nToggleFocusOutSignalId
)
11952 g_signal_handler_disconnect(m_pToggleButton
, m_nToggleFocusOutSignalId
);
11953 g_signal_handler_disconnect(m_pComboBox
, m_nChangedSignalId
);
11954 g_signal_handler_disconnect(m_pComboBox
, m_nPopupShownSignalId
);
11958 class GtkInstanceEntryTreeView
: public GtkInstanceContainer
, public virtual weld::EntryTreeView
11961 GtkInstanceEntry
* m_pEntry
;
11962 GtkInstanceTreeView
* m_pTreeView
;
11963 gulong m_nKeyPressSignalId
;
11964 gulong m_nEntryInsertTextSignalId
;
11965 guint m_nAutoCompleteIdleId
;
11966 bool m_bAutoCompleteCaseSensitive
;
11968 bool signal_key_press(GdkEventKey
* pEvent
)
11970 if (pEvent
->keyval
== GDK_KEY_KP_Up
|| pEvent
->keyval
== GDK_KEY_Up
|| pEvent
->keyval
== GDK_KEY_KP_Page_Up
|| pEvent
->keyval
== GDK_KEY_Page_Up
||
11971 pEvent
->keyval
== GDK_KEY_KP_Down
|| pEvent
->keyval
== GDK_KEY_Down
|| pEvent
->keyval
== GDK_KEY_KP_Page_Down
|| pEvent
->keyval
== GDK_KEY_Page_Down
)
11974 disable_notify_events();
11975 GtkWidget
* pWidget
= m_pTreeView
->getWidget();
11976 if (m_pTreeView
->get_selected_index() == -1)
11978 m_pTreeView
->set_cursor(0);
11979 m_pTreeView
->select(0);
11980 m_xEntry
->set_text(m_xTreeView
->get_selected_text());
11984 gtk_widget_grab_focus(pWidget
);
11985 g_signal_emit_by_name(pWidget
, "key-press-event", pEvent
, &ret
);
11986 m_xEntry
->set_text(m_xTreeView
->get_selected_text());
11987 gtk_widget_grab_focus(m_pEntry
->getWidget());
11989 m_xEntry
->select_region(0, -1);
11990 enable_notify_events();
11991 m_pEntry
->fire_signal_changed();
11997 static gboolean
signalKeyPress(GtkWidget
*, GdkEventKey
* pEvent
, gpointer widget
)
11999 GtkInstanceEntryTreeView
* pThis
= static_cast<GtkInstanceEntryTreeView
*>(widget
);
12000 return pThis
->signal_key_press(pEvent
);
12003 static gboolean
idleAutoComplete(gpointer widget
)
12005 GtkInstanceEntryTreeView
* pThis
= static_cast<GtkInstanceEntryTreeView
*>(widget
);
12006 pThis
->auto_complete();
12010 void auto_complete()
12012 m_nAutoCompleteIdleId
= 0;
12013 OUString aStartText
= get_active_text();
12014 int nStartPos
, nEndPos
;
12015 get_entry_selection_bounds(nStartPos
, nEndPos
);
12016 int nMaxSelection
= std::max(nStartPos
, nEndPos
);
12017 if (nMaxSelection
!= aStartText
.getLength())
12020 disable_notify_events();
12021 int nActive
= get_active();
12022 int nStart
= nActive
;
12027 // Try match case insensitive from current position
12028 int nPos
= m_pTreeView
->starts_with(aStartText
, 0, nStart
, true);
12029 if (nPos
== -1 && nStart
!= 0)
12031 // Try match case insensitive, but from start
12032 nPos
= m_pTreeView
->starts_with(aStartText
, 0, 0, true);
12035 if (!m_bAutoCompleteCaseSensitive
)
12037 // Try match case insensitive from current position
12038 nPos
= m_pTreeView
->starts_with(aStartText
, 0, nStart
, false);
12039 if (nPos
== -1 && nStart
!= 0)
12041 // Try match case insensitive, but from start
12042 nPos
= m_pTreeView
->starts_with(aStartText
, 0, 0, false);
12048 // Try match case sensitive from current position
12049 nPos
= m_pTreeView
->starts_with(aStartText
, 0, nStart
, true);
12050 if (nPos
== -1 && nStart
!= 0)
12052 // Try match case sensitive, but from start
12053 nPos
= m_pTreeView
->starts_with(aStartText
, 0, 0, true);
12059 OUString aText
= get_text(nPos
);
12060 if (aText
!= aStartText
)
12061 set_active_text(aText
);
12062 select_entry_region(aText
.getLength(), aStartText
.getLength());
12064 enable_notify_events();
12067 void signal_entry_insert_text(GtkEntry
*, const gchar
*, gint
, gint
*)
12069 // now check for autocompletes
12070 if (m_nAutoCompleteIdleId
)
12071 g_source_remove(m_nAutoCompleteIdleId
);
12072 m_nAutoCompleteIdleId
= g_idle_add(idleAutoComplete
, this);
12075 static void signalEntryInsertText(GtkEntry
* pEntry
, const gchar
* pNewText
, gint nNewTextLength
,
12076 gint
* position
, gpointer widget
)
12078 GtkInstanceEntryTreeView
* pThis
= static_cast<GtkInstanceEntryTreeView
*>(widget
);
12079 pThis
->signal_entry_insert_text(pEntry
, pNewText
, nNewTextLength
, position
);
12084 GtkInstanceEntryTreeView(GtkContainer
* pContainer
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
,
12085 std::unique_ptr
<weld::Entry
> xEntry
, std::unique_ptr
<weld::TreeView
> xTreeView
)
12086 : EntryTreeView(std::move(xEntry
), std::move(xTreeView
))
12087 , GtkInstanceContainer(pContainer
, pBuilder
, bTakeOwnership
)
12088 , m_pEntry(dynamic_cast<GtkInstanceEntry
*>(m_xEntry
.get()))
12089 , m_pTreeView(dynamic_cast<GtkInstanceTreeView
*>(m_xTreeView
.get()))
12090 , m_nAutoCompleteIdleId(0)
12091 , m_bAutoCompleteCaseSensitive(false)
12094 GtkWidget
* pWidget
= m_pEntry
->getWidget();
12095 m_nKeyPressSignalId
= g_signal_connect(pWidget
, "key-press-event", G_CALLBACK(signalKeyPress
), this);
12096 m_nEntryInsertTextSignalId
= g_signal_connect(pWidget
, "insert-text", G_CALLBACK(signalEntryInsertText
), this);
12099 virtual void insert_separator(int /*pos*/, const OUString
& /*rId*/) override
12104 virtual void make_sorted() override
12106 GtkWidget
* pTreeView
= m_pTreeView
->getWidget();
12107 GtkTreeModel
* pModel
= gtk_tree_view_get_model(GTK_TREE_VIEW(pTreeView
));
12108 GtkTreeSortable
* pSortable
= GTK_TREE_SORTABLE(pModel
);
12109 gtk_tree_sortable_set_sort_column_id(pSortable
, 1, GTK_SORT_ASCENDING
);
12112 virtual void set_entry_completion(bool bEnable
, bool bCaseSensitive
) override
12114 assert(!bEnable
&& "not implemented yet"); (void)bEnable
;
12115 m_bAutoCompleteCaseSensitive
= bCaseSensitive
;
12118 virtual void grab_focus() override
{ m_xEntry
->grab_focus(); }
12120 virtual void connect_focus_in(const Link
<Widget
&, void>& rLink
) override
12122 m_xEntry
->connect_focus_in(rLink
);
12125 virtual void connect_focus_out(const Link
<Widget
&, void>& rLink
) override
12127 m_xEntry
->connect_focus_out(rLink
);
12130 virtual void disable_notify_events() override
12132 GtkWidget
* pWidget
= m_pEntry
->getWidget();
12133 g_signal_handler_block(pWidget
, m_nEntryInsertTextSignalId
);
12134 g_signal_handler_block(pWidget
, m_nKeyPressSignalId
);
12135 m_pTreeView
->disable_notify_events();
12136 GtkInstanceContainer::disable_notify_events();
12139 virtual void enable_notify_events() override
12141 GtkWidget
* pWidget
= m_pEntry
->getWidget();
12142 g_signal_handler_unblock(pWidget
, m_nKeyPressSignalId
);
12143 g_signal_handler_unblock(pWidget
, m_nEntryInsertTextSignalId
);
12144 m_pTreeView
->enable_notify_events();
12145 GtkInstanceContainer::disable_notify_events();
12148 virtual ~GtkInstanceEntryTreeView() override
12150 if (m_nAutoCompleteIdleId
)
12151 g_source_remove(m_nAutoCompleteIdleId
);
12152 GtkWidget
* pWidget
= m_pEntry
->getWidget();
12153 g_signal_handler_disconnect(pWidget
, m_nKeyPressSignalId
);
12154 g_signal_handler_disconnect(pWidget
, m_nEntryInsertTextSignalId
);
12158 class GtkInstanceExpander
: public GtkInstanceContainer
, public virtual weld::Expander
12161 GtkExpander
* m_pExpander
;
12162 gulong m_nSignalId
;
12164 static void signalExpanded(GtkExpander
* pExpander
, GParamSpec
*, gpointer widget
)
12166 GtkInstanceExpander
* pThis
= static_cast<GtkInstanceExpander
*>(widget
);
12167 SolarMutexGuard aGuard
;
12168 pThis
->signal_expanded();
12170 GtkWidget
*pToplevel
= gtk_widget_get_toplevel(GTK_WIDGET(pExpander
));
12172 // https://gitlab.gnome.org/GNOME/gtk/issues/70
12173 // I imagine at some point a release with a fix will be available in which
12174 // case this can be avoided depending on version number
12175 if (pToplevel
&& GTK_IS_WINDOW(pToplevel
) && gtk_widget_get_realized(pToplevel
))
12177 int nToplevelWidth
, nToplevelHeight
;
12180 GtkWidget
* child
= gtk_bin_get_child(GTK_BIN(pExpander
));
12181 gtk_widget_get_preferred_height(child
, &nChildHeight
, nullptr);
12182 gtk_window_get_size(GTK_WINDOW(pToplevel
), &nToplevelWidth
, &nToplevelHeight
);
12184 if (pThis
->get_expanded())
12185 nToplevelHeight
+= nChildHeight
;
12187 nToplevelHeight
-= nChildHeight
;
12189 gtk_window_resize(GTK_WINDOW(pToplevel
), nToplevelWidth
, nToplevelHeight
);
12194 GtkInstanceExpander(GtkExpander
* pExpander
, GtkInstanceBuilder
* pBuilder
, bool bTakeOwnership
)
12195 : GtkInstanceContainer(GTK_CONTAINER(pExpander
), pBuilder
, bTakeOwnership
)
12196 , m_pExpander(pExpander
)
12197 , m_nSignalId(g_signal_connect(m_pExpander
, "notify::expanded", G_CALLBACK(signalExpanded
), this))
12201 virtual bool get_expanded() const override
12203 return gtk_expander_get_expanded(m_pExpander
);
12206 virtual void set_expanded(bool bExpand
) override
12208 gtk_expander_set_expanded(m_pExpander
, bExpand
);
12211 virtual ~GtkInstanceExpander() override
12213 g_signal_handler_disconnect(m_pExpander
, m_nSignalId
);
12219 gboolean
signalTooltipQuery(GtkWidget
* pWidget
, gint
/*x*/, gint
/*y*/,
12220 gboolean
/*keyboard_mode*/, GtkTooltip
*tooltip
)
12222 const ImplSVData
* pSVData
= ImplGetSVData();
12223 if (pSVData
->maHelpData
.mbBalloonHelp
)
12225 /*Current mechanism which needs help installed*/
12226 OString sHelpId
= ::get_help_id(pWidget
);
12227 Help
* pHelp
= !sHelpId
.isEmpty() ? Application::GetHelp() : nullptr;
12230 OUString sHelpText
= pHelp
->GetHelpText(OStringToOUString(sHelpId
, RTL_TEXTENCODING_UTF8
), static_cast<weld::Widget
*>(nullptr));
12231 if (!sHelpText
.isEmpty())
12233 gtk_tooltip_set_text(tooltip
, OUStringToOString(sHelpText
, RTL_TEXTENCODING_UTF8
).getStr());
12238 /*This is how I would prefer things to be, only a few like this though*/
12239 AtkObject
* pAtkObject
= gtk_widget_get_accessible(pWidget
);
12240 const char* pDesc
= pAtkObject
? atk_object_get_description(pAtkObject
) : nullptr;
12241 if (pDesc
&& pDesc
[0])
12243 gtk_tooltip_set_text(tooltip
, pDesc
);
12248 const char* pDesc
= gtk_widget_get_tooltip_text(pWidget
);
12249 if (pDesc
&& pDesc
[0])
12251 gtk_tooltip_set_text(tooltip
, pDesc
);
12262 AtkObject
* drawing_area_get_accessibity(GtkWidget
*pWidget
)
12264 AtkObject
* pDefaultAccessible
= default_drawing_area_get_accessible(pWidget
);
12265 void* pData
= g_object_get_data(G_OBJECT(pWidget
), "g-lo-GtkInstanceDrawingArea");
12266 GtkInstanceDrawingArea
* pDrawingArea
= static_cast<GtkInstanceDrawingArea
*>(pData
);
12267 AtkObject
*pAtkObj
= pDrawingArea
? pDrawingArea
->GetAtkObject(pDefaultAccessible
) : nullptr;
12270 return pDefaultAccessible
;
12273 void ensure_intercept_drawing_area_accessibility()
12278 gpointer pClass
= g_type_class_ref(GTK_TYPE_DRAWING_AREA
);
12279 GtkWidgetClass
* pWidgetClass
= GTK_WIDGET_CLASS(pClass
);
12280 default_drawing_area_get_accessible
= pWidgetClass
->get_accessible
;
12281 pWidgetClass
->get_accessible
= drawing_area_get_accessibity
;
12282 g_type_class_unref(pClass
);
12289 class GtkInstanceBuilder
: public weld::Builder
12292 ResHookProc m_pStringReplace
;
12293 OUString m_sHelpRoot
;
12294 OString m_aUtf8HelpRoot
;
12295 OUString m_aIconTheme
;
12296 OUString m_aUILang
;
12297 GtkBuilder
* m_pBuilder
;
12298 GSList
* m_pObjectList
;
12299 GtkWidget
* m_pParentWidget
;
12300 gulong m_nNotifySignalId
;
12301 std::vector
<GtkButton
*> m_aMnemonicButtons
;
12302 std::vector
<GtkLabel
*> m_aMnemonicLabels
;
12304 void postprocess_widget(GtkWidget
* pWidget
)
12307 //wanted: better way to do this, e.g. make gtk use gio for
12308 //loading from a filename and provide gio protocol handler
12309 //for our image in a zip urls
12311 //unpack the images and keep them as dirs and just
12312 //add the paths to the gtk icon theme dir
12313 if (GTK_IS_IMAGE(pWidget
))
12315 GtkImage
* pImage
= GTK_IMAGE(pWidget
);
12316 const gchar
* icon_name
;
12317 gtk_image_get_icon_name(pImage
, &icon_name
, nullptr);
12320 OUString
aIconName(icon_name
, strlen(icon_name
), RTL_TEXTENCODING_UTF8
);
12321 GdkPixbuf
* pixbuf
= load_icon_by_name(aIconName
, m_aIconTheme
, m_aUILang
);
12324 gtk_image_set_from_pixbuf(pImage
, pixbuf
);
12325 g_object_unref(pixbuf
);
12329 else if (GTK_IS_TOOL_BUTTON(pWidget
))
12331 GtkToolButton
* pToolButton
= GTK_TOOL_BUTTON(pWidget
);
12332 const gchar
* icon_name
= gtk_tool_button_get_icon_name(pToolButton
);
12335 OUString
aIconName(icon_name
, strlen(icon_name
), RTL_TEXTENCODING_UTF8
);
12336 GdkPixbuf
* pixbuf
= load_icon_by_name(aIconName
, m_aIconTheme
, m_aUILang
);
12339 GtkWidget
* pImage
= gtk_image_new_from_pixbuf(pixbuf
);
12340 g_object_unref(pixbuf
);
12341 gtk_tool_button_set_icon_widget(pToolButton
, pImage
);
12342 gtk_widget_show(pImage
);
12346 // if no tooltip reuse the label as default tooltip
12347 if (!gtk_widget_get_tooltip_text(pWidget
))
12349 if (const gchar
* label
= gtk_tool_button_get_label(pToolButton
))
12350 gtk_widget_set_tooltip_text(pWidget
, label
);
12355 const gchar
* pStr
= gtk_buildable_get_name(GTK_BUILDABLE(pWidget
));
12356 size_t nLen
= pStr
? strlen(pStr
) : 0;
12359 OString sHelpId
= m_aUtf8HelpRoot
+ OString(pStr
, nLen
);
12360 set_help_id(pWidget
, sHelpId
);
12361 //hook up for extended help
12362 const ImplSVData
* pSVData
= ImplGetSVData();
12363 if (pSVData
->maHelpData
.mbBalloonHelp
&& !GTK_IS_DIALOG(pWidget
) && !GTK_IS_ASSISTANT(pWidget
))
12365 gtk_widget_set_has_tooltip(pWidget
, true);
12366 g_signal_connect(pWidget
, "query-tooltip", G_CALLBACK(signalTooltipQuery
), nullptr);
12369 // expand placeholder and collect potentially missing mnemonics
12370 if (GTK_IS_BUTTON(pWidget
))
12372 GtkButton
* pButton
= GTK_BUTTON(pWidget
);
12373 if (m_pStringReplace
!= nullptr)
12375 OUString
aLabel(get_label(pButton
));
12376 if (!aLabel
.isEmpty())
12377 set_label(pButton
, (*m_pStringReplace
)(aLabel
));
12379 if (gtk_button_get_use_underline(pButton
) && !gtk_button_get_use_stock(pButton
))
12380 m_aMnemonicButtons
.push_back(pButton
);
12382 else if (GTK_IS_LABEL(pWidget
))
12384 GtkLabel
* pLabel
= GTK_LABEL(pWidget
);
12385 if (m_pStringReplace
!= nullptr)
12387 OUString
aLabel(get_label(pLabel
));
12388 if (!aLabel
.isEmpty())
12389 set_label(pLabel
, (*m_pStringReplace
)(aLabel
));
12391 if (gtk_label_get_use_underline(pLabel
))
12392 m_aMnemonicLabels
.push_back(pLabel
);
12394 else if (GTK_IS_TEXT_VIEW(pWidget
))
12396 GtkTextView
* pTextView
= GTK_TEXT_VIEW(pWidget
);
12397 if (m_pStringReplace
!= nullptr)
12399 GtkTextBuffer
* pBuffer
= gtk_text_view_get_buffer(pTextView
);
12400 GtkTextIter start
, end
;
12401 gtk_text_buffer_get_bounds(pBuffer
, &start
, &end
);
12402 char* pTextStr
= gtk_text_buffer_get_text(pBuffer
, &start
, &end
, true);
12403 int nTextLen
= pTextStr
? strlen(pTextStr
) : 0;
12406 OUString
sOldText(pTextStr
, nTextLen
, RTL_TEXTENCODING_UTF8
);
12407 OString
sText(OUStringToOString((*m_pStringReplace
)(sOldText
), RTL_TEXTENCODING_UTF8
));
12408 gtk_text_buffer_set_text(pBuffer
, sText
.getStr(), sText
.getLength());
12413 else if (GTK_IS_WINDOW(pWidget
))
12415 if (m_pStringReplace
!= nullptr) {
12416 GtkWindow
* pWindow
= GTK_WINDOW(pWidget
);
12417 set_title(pWindow
, (*m_pStringReplace
)(get_title(pWindow
)));
12418 if (GTK_IS_MESSAGE_DIALOG(pWindow
))
12420 GtkMessageDialog
* pMessageDialog
= GTK_MESSAGE_DIALOG(pWindow
);
12421 set_primary_text(pMessageDialog
, (*m_pStringReplace
)(get_primary_text(pMessageDialog
)));
12422 set_secondary_text(pMessageDialog
, (*m_pStringReplace
)(get_secondary_text(pMessageDialog
)));
12424 else if (GTK_IS_ABOUT_DIALOG(pWindow
))
12426 GtkAboutDialog
* pAboutDialog
= GTK_ABOUT_DIALOG(pWindow
);
12427 const gchar
*pComments
= gtk_about_dialog_get_comments(pAboutDialog
);
12430 OUString
sComments(pComments
, strlen(pComments
), RTL_TEXTENCODING_UTF8
);
12431 sComments
= (*m_pStringReplace
)(sComments
);
12432 gtk_about_dialog_set_comments(pAboutDialog
, OUStringToOString(sComments
, RTL_TEXTENCODING_UTF8
).getStr());
12434 const gchar
*pProgramName
= gtk_about_dialog_get_program_name(pAboutDialog
);
12437 OUString
sProgramName(pProgramName
, strlen(pProgramName
), RTL_TEXTENCODING_UTF8
);
12438 sProgramName
= (*m_pStringReplace
)(sProgramName
);
12439 gtk_about_dialog_set_program_name(pAboutDialog
, OUStringToOString(sProgramName
, RTL_TEXTENCODING_UTF8
).getStr());
12446 //GtkBuilder sets translation domain during parse, and unsets it again afterwards.
12447 //In order for GtkBuilder to find the translations bindtextdomain has to be called
12448 //for the domain. So here on the first setting of "domain" we call Translate::Create
12449 //to make sure that happens. Without this, if some other part of LibreOffice has
12450 //used the translation machinery for this domain it will still work, but if it
12451 //hasn't, e.g. tdf#119929, then the translation fails
12452 void translation_domain_set()
12454 Translate::Create(gtk_builder_get_translation_domain(m_pBuilder
), LanguageTag(m_aUILang
));
12455 g_signal_handler_disconnect(m_pBuilder
, m_nNotifySignalId
);
12458 static void signalNotify(GObject
*, GParamSpec
*pSpec
, gpointer pData
)
12460 g_return_if_fail(pSpec
!= nullptr);
12461 if (strcmp(pSpec
->name
, "translation-domain") == 0)
12463 GtkInstanceBuilder
* pBuilder
= static_cast<GtkInstanceBuilder
*>(pData
);
12464 pBuilder
->translation_domain_set();
12468 static void postprocess(gpointer data
, gpointer user_data
)
12470 GObject
* pObject
= static_cast<GObject
*>(data
);
12471 if (!GTK_IS_WIDGET(pObject
))
12473 GtkInstanceBuilder
* pThis
= static_cast<GtkInstanceBuilder
*>(user_data
);
12474 pThis
->postprocess_widget(GTK_WIDGET(pObject
));
12477 GtkInstanceBuilder(GtkWidget
* pParent
, const OUString
& rUIRoot
, const OUString
& rUIFile
)
12478 : weld::Builder(rUIFile
)
12479 , m_pStringReplace(Translate::GetReadStringHook())
12480 , m_sHelpRoot(rUIFile
)
12481 , m_pParentWidget(pParent
)
12482 , m_nNotifySignalId(0)
12484 ensure_intercept_drawing_area_accessibility();
12486 sal_Int32 nIdx
= m_sHelpRoot
.lastIndexOf('.');
12488 m_sHelpRoot
= m_sHelpRoot
.copy(0, nIdx
);
12489 m_sHelpRoot
+= OUString('/');
12490 m_aUtf8HelpRoot
= OUStringToOString(m_sHelpRoot
, RTL_TEXTENCODING_UTF8
);
12491 m_aIconTheme
= Application::GetSettings().GetStyleSettings().DetermineIconTheme();
12492 m_aUILang
= Application::GetSettings().GetUILanguageTag().getBcp47();
12494 OUString
aUri(rUIRoot
+ rUIFile
);
12496 osl::FileBase::getSystemPathFromFileURL(aUri
, aPath
);
12497 m_pBuilder
= gtk_builder_new();
12498 m_nNotifySignalId
= g_signal_connect_data(G_OBJECT(m_pBuilder
), "notify", G_CALLBACK(signalNotify
), this, nullptr, G_CONNECT_AFTER
);
12499 auto rc
= gtk_builder_add_from_file(m_pBuilder
, OUStringToOString(aPath
, RTL_TEXTENCODING_UTF8
).getStr(), nullptr);
12500 assert(rc
&& "could not load UI file");
12503 m_pObjectList
= gtk_builder_get_objects(m_pBuilder
);
12504 g_slist_foreach(m_pObjectList
, postprocess
, this);
12506 GenerateMissingMnemonics();
12509 void GenerateMissingMnemonics()
12511 MnemonicGenerator
aMnemonicGenerator('_');
12512 for (const auto a
: m_aMnemonicButtons
)
12513 aMnemonicGenerator
.RegisterMnemonic(get_label(a
));
12514 for (const auto a
: m_aMnemonicLabels
)
12515 aMnemonicGenerator
.RegisterMnemonic(get_label(a
));
12517 for (const auto a
: m_aMnemonicButtons
)
12519 OUString
aLabel(get_label(a
));
12520 OUString aNewLabel
= aMnemonicGenerator
.CreateMnemonic(aLabel
);
12521 if (aLabel
== aNewLabel
)
12523 set_label(a
, aNewLabel
);
12525 for (const auto a
: m_aMnemonicLabels
)
12527 OUString
aLabel(get_label(a
));
12528 OUString aNewLabel
= aMnemonicGenerator
.CreateMnemonic(aLabel
);
12529 if (aLabel
== aNewLabel
)
12531 set_label(a
, aNewLabel
);
12534 m_aMnemonicLabels
.clear();
12535 m_aMnemonicButtons
.clear();
12538 OString
get_current_page_help_id()
12540 OString sPageHelpId
;
12541 // check to see if there is a notebook called tabcontrol and get the
12542 // helpid for the current page of that
12543 std::unique_ptr
<weld::Notebook
> xNotebook(weld_notebook("tabcontrol", false));
12546 if (GtkInstanceContainer
* pPage
= dynamic_cast<GtkInstanceContainer
*>(xNotebook
->get_page(xNotebook
->get_current_page_ident())))
12548 GtkWidget
* pContainer
= pPage
->getWidget();
12549 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(pContainer
));
12550 GList
* pChild
= g_list_first(pChildren
);
12553 GtkWidget
* pPageWidget
= static_cast<GtkWidget
*>(pChild
->data
);
12554 sPageHelpId
= ::get_help_id(pPageWidget
);
12556 g_list_free(pChildren
);
12559 return sPageHelpId
;
12562 virtual ~GtkInstanceBuilder() override
12564 g_slist_free(m_pObjectList
);
12565 g_object_unref(m_pBuilder
);
12568 //ideally we would have/use weld::Container add and explicitly
12569 //call add when we want to do this, but in the vcl impl the
12570 //parent has to be set when the child is created, so for the
12571 //gtk impl emulate this by doing this implicitly at weld time
12572 void auto_add_parentless_widgets_to_container(GtkWidget
* pWidget
)
12574 if (gtk_widget_get_toplevel(pWidget
) == pWidget
&& !GTK_IS_POPOVER(pWidget
))
12575 gtk_container_add(GTK_CONTAINER(m_pParentWidget
), pWidget
);
12578 virtual std::unique_ptr
<weld::MessageDialog
> weld_message_dialog(const OString
&id
, bool bTakeOwnership
) override
12580 GtkMessageDialog
* pMessageDialog
= GTK_MESSAGE_DIALOG(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12581 if (!pMessageDialog
)
12583 gtk_window_set_transient_for(GTK_WINDOW(pMessageDialog
), GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget
)));
12584 return std::make_unique
<GtkInstanceMessageDialog
>(pMessageDialog
, this, bTakeOwnership
);
12587 virtual std::unique_ptr
<weld::AboutDialog
> weld_about_dialog(const OString
&id
, bool bTakeOwnership
) override
12589 GtkAboutDialog
* pAboutDialog
= GTK_ABOUT_DIALOG(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12592 gtk_window_set_transient_for(GTK_WINDOW(pAboutDialog
), GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget
)));
12593 return std::make_unique
<GtkInstanceAboutDialog
>(pAboutDialog
, this, bTakeOwnership
);
12596 virtual std::unique_ptr
<weld::Assistant
> weld_assistant(const OString
&id
, bool bTakeOwnership
) override
12598 GtkAssistant
* pAssistant
= GTK_ASSISTANT(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12601 if (m_pParentWidget
)
12602 gtk_window_set_transient_for(GTK_WINDOW(pAssistant
), GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget
)));
12603 return std::make_unique
<GtkInstanceAssistant
>(pAssistant
, this, bTakeOwnership
);
12606 virtual std::unique_ptr
<weld::Dialog
> weld_dialog(const OString
&id
, bool bTakeOwnership
) override
12608 GtkWindow
* pDialog
= GTK_WINDOW(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12611 if (m_pParentWidget
)
12612 gtk_window_set_transient_for(pDialog
, GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget
)));
12613 return std::make_unique
<GtkInstanceDialog
>(pDialog
, this, bTakeOwnership
);
12616 virtual std::unique_ptr
<weld::Window
> create_screenshot_window() override
12618 GtkWidget
* pTopLevel
= nullptr;
12620 for (GSList
* l
= m_pObjectList
; l
; l
= g_slist_next(l
))
12622 GObject
* pObj
= static_cast<GObject
*>(l
->data
);
12624 if (!GTK_IS_WIDGET(pObj
) || gtk_widget_get_parent(GTK_WIDGET(pObj
)))
12628 pTopLevel
= GTK_WIDGET(pObj
);
12629 else if (GTK_IS_WINDOW(pObj
))
12630 pTopLevel
= GTK_WIDGET(pObj
);
12636 GtkWindow
* pDialog
;
12637 if (GTK_IS_WINDOW(pTopLevel
))
12638 pDialog
= GTK_WINDOW(pTopLevel
);
12641 pDialog
= GTK_WINDOW(gtk_dialog_new());
12642 ::set_help_id(GTK_WIDGET(pDialog
), ::get_help_id(pTopLevel
));
12644 GtkWidget
* pContentArea
= gtk_dialog_get_content_area(GTK_DIALOG(pDialog
));
12645 gtk_container_add(GTK_CONTAINER(pContentArea
), pTopLevel
);
12646 gtk_widget_show_all(pTopLevel
);
12649 if (m_pParentWidget
)
12650 gtk_window_set_transient_for(pDialog
, GTK_WINDOW(gtk_widget_get_toplevel(m_pParentWidget
)));
12651 return std::make_unique
<GtkInstanceDialog
>(pDialog
, this, true);
12654 virtual std::unique_ptr
<weld::Window
> weld_window(const OString
&id
, bool bTakeOwnership
) override
12656 GtkWindow
* pWindow
= GTK_WINDOW(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12657 return pWindow
? std::make_unique
<GtkInstanceWindow
>(pWindow
, this, bTakeOwnership
) : nullptr;
12660 virtual std::unique_ptr
<weld::Widget
> weld_widget(const OString
&id
, bool bTakeOwnership
) override
12662 GtkWidget
* pWidget
= GTK_WIDGET(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12665 auto_add_parentless_widgets_to_container(pWidget
);
12666 return std::make_unique
<GtkInstanceWidget
>(pWidget
, this, bTakeOwnership
);
12669 virtual std::unique_ptr
<weld::Container
> weld_container(const OString
&id
, bool bTakeOwnership
) override
12671 GtkContainer
* pContainer
= GTK_CONTAINER(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12674 auto_add_parentless_widgets_to_container(GTK_WIDGET(pContainer
));
12675 return std::make_unique
<GtkInstanceContainer
>(pContainer
, this, bTakeOwnership
);
12678 virtual std::unique_ptr
<weld::Box
> weld_box(const OString
&id
, bool bTakeOwnership
) override
12680 GtkBox
* pBox
= GTK_BOX(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12683 auto_add_parentless_widgets_to_container(GTK_WIDGET(pBox
));
12684 return std::make_unique
<GtkInstanceBox
>(pBox
, this, bTakeOwnership
);
12687 virtual std::unique_ptr
<weld::Frame
> weld_frame(const OString
&id
, bool bTakeOwnership
) override
12689 GtkFrame
* pFrame
= GTK_FRAME(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12692 auto_add_parentless_widgets_to_container(GTK_WIDGET(pFrame
));
12693 return std::make_unique
<GtkInstanceFrame
>(pFrame
, this, bTakeOwnership
);
12696 virtual std::unique_ptr
<weld::ScrolledWindow
> weld_scrolled_window(const OString
&id
, bool bTakeOwnership
) override
12698 GtkScrolledWindow
* pScrolledWindow
= GTK_SCROLLED_WINDOW(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12699 if (!pScrolledWindow
)
12701 auto_add_parentless_widgets_to_container(GTK_WIDGET(pScrolledWindow
));
12702 return std::make_unique
<GtkInstanceScrolledWindow
>(pScrolledWindow
, this, bTakeOwnership
);
12705 virtual std::unique_ptr
<weld::Notebook
> weld_notebook(const OString
&id
, bool bTakeOwnership
) override
12707 GtkNotebook
* pNotebook
= GTK_NOTEBOOK(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12710 auto_add_parentless_widgets_to_container(GTK_WIDGET(pNotebook
));
12711 return std::make_unique
<GtkInstanceNotebook
>(pNotebook
, this, bTakeOwnership
);
12714 virtual std::unique_ptr
<weld::Button
> weld_button(const OString
&id
, bool bTakeOwnership
) override
12716 GtkButton
* pButton
= GTK_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12719 auto_add_parentless_widgets_to_container(GTK_WIDGET(pButton
));
12720 return std::make_unique
<GtkInstanceButton
>(pButton
, this, bTakeOwnership
);
12723 virtual std::unique_ptr
<weld::MenuButton
> weld_menu_button(const OString
&id
, bool bTakeOwnership
) override
12725 GtkMenuButton
* pButton
= GTK_MENU_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12728 auto_add_parentless_widgets_to_container(GTK_WIDGET(pButton
));
12729 return std::make_unique
<GtkInstanceMenuButton
>(pButton
, this, bTakeOwnership
);
12732 virtual std::unique_ptr
<weld::LinkButton
> weld_link_button(const OString
&id
, bool bTakeOwnership
) override
12734 GtkLinkButton
* pButton
= GTK_LINK_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12737 auto_add_parentless_widgets_to_container(GTK_WIDGET(pButton
));
12738 return std::make_unique
<GtkInstanceLinkButton
>(pButton
, this, bTakeOwnership
);
12741 virtual std::unique_ptr
<weld::ToggleButton
> weld_toggle_button(const OString
&id
, bool bTakeOwnership
) override
12743 GtkToggleButton
* pToggleButton
= GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12744 if (!pToggleButton
)
12746 auto_add_parentless_widgets_to_container(GTK_WIDGET(pToggleButton
));
12747 return std::make_unique
<GtkInstanceToggleButton
>(pToggleButton
, this, bTakeOwnership
);
12750 virtual std::unique_ptr
<weld::RadioButton
> weld_radio_button(const OString
&id
, bool bTakeOwnership
) override
12752 GtkRadioButton
* pRadioButton
= GTK_RADIO_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12755 auto_add_parentless_widgets_to_container(GTK_WIDGET(pRadioButton
));
12756 return std::make_unique
<GtkInstanceRadioButton
>(pRadioButton
, this, bTakeOwnership
);
12759 virtual std::unique_ptr
<weld::CheckButton
> weld_check_button(const OString
&id
, bool bTakeOwnership
) override
12761 GtkCheckButton
* pCheckButton
= GTK_CHECK_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12764 auto_add_parentless_widgets_to_container(GTK_WIDGET(pCheckButton
));
12765 return std::make_unique
<GtkInstanceCheckButton
>(pCheckButton
, this, bTakeOwnership
);
12768 virtual std::unique_ptr
<weld::Scale
> weld_scale(const OString
&id
, bool bTakeOwnership
) override
12770 GtkScale
* pScale
= GTK_SCALE(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12773 auto_add_parentless_widgets_to_container(GTK_WIDGET(pScale
));
12774 return std::make_unique
<GtkInstanceScale
>(pScale
, this, bTakeOwnership
);
12777 virtual std::unique_ptr
<weld::ProgressBar
> weld_progress_bar(const OString
&id
, bool bTakeOwnership
) override
12779 GtkProgressBar
* pProgressBar
= GTK_PROGRESS_BAR(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12782 auto_add_parentless_widgets_to_container(GTK_WIDGET(pProgressBar
));
12783 return std::make_unique
<GtkInstanceProgressBar
>(pProgressBar
, this, bTakeOwnership
);
12786 virtual std::unique_ptr
<weld::Spinner
> weld_spinner(const OString
&id
, bool bTakeOwnership
) override
12788 GtkSpinner
* pSpinner
= GTK_SPINNER(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12791 auto_add_parentless_widgets_to_container(GTK_WIDGET(pSpinner
));
12792 return std::make_unique
<GtkInstanceSpinner
>(pSpinner
, this, bTakeOwnership
);
12795 virtual std::unique_ptr
<weld::Image
> weld_image(const OString
&id
, bool bTakeOwnership
) override
12797 GtkImage
* pImage
= GTK_IMAGE(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12800 auto_add_parentless_widgets_to_container(GTK_WIDGET(pImage
));
12801 return std::make_unique
<GtkInstanceImage
>(pImage
, this, bTakeOwnership
);
12804 virtual std::unique_ptr
<weld::Calendar
> weld_calendar(const OString
&id
, bool bTakeOwnership
) override
12806 GtkCalendar
* pCalendar
= GTK_CALENDAR(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12809 auto_add_parentless_widgets_to_container(GTK_WIDGET(pCalendar
));
12810 return std::make_unique
<GtkInstanceCalendar
>(pCalendar
, this, bTakeOwnership
);
12813 virtual std::unique_ptr
<weld::Entry
> weld_entry(const OString
&id
, bool bTakeOwnership
) override
12815 GtkEntry
* pEntry
= GTK_ENTRY(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12818 auto_add_parentless_widgets_to_container(GTK_WIDGET(pEntry
));
12819 return std::make_unique
<GtkInstanceEntry
>(pEntry
, this, bTakeOwnership
);
12822 virtual std::unique_ptr
<weld::SpinButton
> weld_spin_button(const OString
&id
, bool bTakeOwnership
) override
12824 GtkSpinButton
* pSpinButton
= GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12827 auto_add_parentless_widgets_to_container(GTK_WIDGET(pSpinButton
));
12828 return std::make_unique
<GtkInstanceSpinButton
>(pSpinButton
, this, bTakeOwnership
);
12831 virtual std::unique_ptr
<weld::MetricSpinButton
> weld_metric_spin_button(const OString
& id
, FieldUnit eUnit
,
12832 bool bTakeOwnership
) override
12834 return std::make_unique
<weld::MetricSpinButton
>(weld_spin_button(id
, bTakeOwnership
), eUnit
);
12837 virtual std::unique_ptr
<weld::FormattedSpinButton
> weld_formatted_spin_button(const OString
&id
, bool bTakeOwnership
) override
12839 GtkSpinButton
* pSpinButton
= GTK_SPIN_BUTTON(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12842 auto_add_parentless_widgets_to_container(GTK_WIDGET(pSpinButton
));
12843 return std::make_unique
<GtkInstanceFormattedSpinButton
>(pSpinButton
, this, bTakeOwnership
);
12846 virtual std::unique_ptr
<weld::TimeSpinButton
> weld_time_spin_button(const OString
& id
, TimeFieldFormat eFormat
,
12847 bool bTakeOwnership
) override
12849 return std::make_unique
<weld::TimeSpinButton
>(weld_spin_button(id
, bTakeOwnership
), eFormat
);
12852 virtual std::unique_ptr
<weld::ComboBox
> weld_combo_box(const OString
&id
, bool bTakeOwnership
) override
12854 GtkComboBox
* pComboBox
= GTK_COMBO_BOX(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12857 auto_add_parentless_widgets_to_container(GTK_WIDGET(pComboBox
));
12858 return std::make_unique
<GtkInstanceComboBox
>(pComboBox
, this, bTakeOwnership
);
12861 virtual std::unique_ptr
<weld::TreeView
> weld_tree_view(const OString
&id
, bool bTakeOwnership
) override
12863 GtkTreeView
* pTreeView
= GTK_TREE_VIEW(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12866 auto_add_parentless_widgets_to_container(GTK_WIDGET(pTreeView
));
12867 return std::make_unique
<GtkInstanceTreeView
>(pTreeView
, this, bTakeOwnership
);
12870 virtual std::unique_ptr
<weld::IconView
> weld_icon_view(const OString
&id
, bool bTakeOwnership
) override
12872 GtkIconView
* pIconView
= GTK_ICON_VIEW(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12875 auto_add_parentless_widgets_to_container(GTK_WIDGET(pIconView
));
12876 return std::make_unique
<GtkInstanceIconView
>(pIconView
, this, bTakeOwnership
);
12879 virtual std::unique_ptr
<weld::EntryTreeView
> weld_entry_tree_view(const OString
& containerid
, const OString
& entryid
, const OString
& treeviewid
, bool bTakeOwnership
) override
12881 GtkContainer
* pContainer
= GTK_CONTAINER(gtk_builder_get_object(m_pBuilder
, containerid
.getStr()));
12884 auto_add_parentless_widgets_to_container(GTK_WIDGET(pContainer
));
12885 return std::make_unique
<GtkInstanceEntryTreeView
>(pContainer
, this, bTakeOwnership
,
12886 weld_entry(entryid
, bTakeOwnership
),
12887 weld_tree_view(treeviewid
, bTakeOwnership
));
12890 virtual std::unique_ptr
<weld::Label
> weld_label(const OString
&id
, bool bTakeOwnership
) override
12892 GtkLabel
* pLabel
= GTK_LABEL(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12895 auto_add_parentless_widgets_to_container(GTK_WIDGET(pLabel
));
12896 return std::make_unique
<GtkInstanceLabel
>(pLabel
, this, bTakeOwnership
);
12899 virtual std::unique_ptr
<weld::TextView
> weld_text_view(const OString
&id
, bool bTakeOwnership
) override
12901 GtkTextView
* pTextView
= GTK_TEXT_VIEW(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12904 auto_add_parentless_widgets_to_container(GTK_WIDGET(pTextView
));
12905 return std::make_unique
<GtkInstanceTextView
>(pTextView
, this, bTakeOwnership
);
12908 virtual std::unique_ptr
<weld::Expander
> weld_expander(const OString
&id
, bool bTakeOwnership
) override
12910 GtkExpander
* pExpander
= GTK_EXPANDER(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12913 auto_add_parentless_widgets_to_container(GTK_WIDGET(pExpander
));
12914 return std::make_unique
<GtkInstanceExpander
>(pExpander
, this, bTakeOwnership
);
12917 virtual std::unique_ptr
<weld::DrawingArea
> weld_drawing_area(const OString
&id
, const a11yref
& rA11y
,
12918 FactoryFunction
/*pUITestFactoryFunction*/, void* /*pUserData*/, bool bTakeOwnership
) override
12920 GtkDrawingArea
* pDrawingArea
= GTK_DRAWING_AREA(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12923 auto_add_parentless_widgets_to_container(GTK_WIDGET(pDrawingArea
));
12924 return std::make_unique
<GtkInstanceDrawingArea
>(pDrawingArea
, this, rA11y
, bTakeOwnership
);
12927 virtual std::unique_ptr
<weld::Menu
> weld_menu(const OString
&id
, bool bTakeOwnership
) override
12929 GtkMenu
* pMenu
= GTK_MENU(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12932 return std::make_unique
<GtkInstanceMenu
>(pMenu
, bTakeOwnership
);
12935 virtual std::unique_ptr
<weld::Toolbar
> weld_toolbar(const OString
&id
, bool bTakeOwnership
) override
12937 GtkToolbar
* pToolbar
= GTK_TOOLBAR(gtk_builder_get_object(m_pBuilder
, id
.getStr()));
12940 auto_add_parentless_widgets_to_container(GTK_WIDGET(pToolbar
));
12941 return std::make_unique
<GtkInstanceToolbar
>(pToolbar
, this, bTakeOwnership
);
12944 virtual std::unique_ptr
<weld::SizeGroup
> create_size_group() override
12946 return std::make_unique
<GtkInstanceSizeGroup
>();
12950 void GtkInstanceWindow::help()
12952 //show help for widget with keyboard focus
12953 GtkWidget
* pWidget
= gtk_window_get_focus(m_pWindow
);
12955 pWidget
= GTK_WIDGET(m_pWindow
);
12956 OString sHelpId
= ::get_help_id(pWidget
);
12957 while (sHelpId
.isEmpty())
12959 pWidget
= gtk_widget_get_parent(pWidget
);
12962 sHelpId
= ::get_help_id(pWidget
);
12964 std::unique_ptr
<weld::Widget
> xTemp(pWidget
!= m_pWidget
? new GtkInstanceWidget(pWidget
, m_pBuilder
, false) : nullptr);
12965 weld::Widget
* pSource
= xTemp
? xTemp
.get() : this;
12966 bool bRunNormalHelpRequest
= !m_aHelpRequestHdl
.IsSet() || m_aHelpRequestHdl
.Call(*pSource
);
12967 Help
* pHelp
= bRunNormalHelpRequest
? Application::GetHelp() : nullptr;
12970 // tdf#126007, there's a nice fallback route for offline help where
12971 // the current page of a notebook will get checked when the help
12972 // button is pressed and there was no help for the dialog found.
12974 // But for online help that route doesn't get taken, so bodge this here
12975 // by using the page help id if available and if the help button itself
12976 // was the original id
12977 if (m_pBuilder
&& sHelpId
.endsWith("/help"))
12979 OString sPageId
= m_pBuilder
->get_current_page_help_id();
12980 if (!sPageId
.isEmpty())
12983 pHelp
->Start(OStringToOUString(sHelpId
, RTL_TEXTENCODING_UTF8
), pSource
);
12987 //iterate upwards through the hierarchy from this widgets through its parents
12988 //calling func with their helpid until func returns true or we run out of parents
12989 void GtkInstanceWidget::help_hierarchy_foreach(const std::function
<bool(const OString
&)>& func
)
12991 GtkWidget
* pParent
= m_pWidget
;
12992 while ((pParent
= gtk_widget_get_parent(pParent
)))
12994 if (func(::get_help_id(pParent
)))
12999 weld::Builder
* GtkInstance::CreateBuilder(weld::Widget
* pParent
, const OUString
& rUIRoot
, const OUString
& rUIFile
)
13001 GtkInstanceWidget
* pParentWidget
= dynamic_cast<GtkInstanceWidget
*>(pParent
);
13002 if (pParent
&& !pParentWidget
) //remove when complete
13003 return SalInstance::CreateBuilder(pParent
, rUIRoot
, rUIFile
);
13004 GtkWidget
* pBuilderParent
= pParentWidget
? pParentWidget
->getWidget() : nullptr;
13005 return new GtkInstanceBuilder(pBuilderParent
, rUIRoot
, rUIFile
);
13008 weld::MessageDialog
* GtkInstance::CreateMessageDialog(weld::Widget
* pParent
, VclMessageType eMessageType
, VclButtonsType eButtonsType
, const OUString
&rPrimaryMessage
)
13010 GtkInstanceWidget
* pParentInstance
= dynamic_cast<GtkInstanceWidget
*>(pParent
);
13011 GtkWindow
* pParentWindow
= pParentInstance
? pParentInstance
->getWindow() : nullptr;
13012 GtkMessageDialog
* pMessageDialog
= GTK_MESSAGE_DIALOG(gtk_message_dialog_new(pParentWindow
, GTK_DIALOG_MODAL
,
13013 VclToGtk(eMessageType
), VclToGtk(eButtonsType
), "%s",
13014 OUStringToOString(rPrimaryMessage
, RTL_TEXTENCODING_UTF8
).getStr()));
13015 return new GtkInstanceMessageDialog(pMessageDialog
, nullptr, true);
13018 weld::Window
* GtkInstance::GetFrameWeld(const css::uno::Reference
<css::awt::XWindow
>& rWindow
)
13020 if (SalGtkXWindow
* pGtkXWindow
= dynamic_cast<SalGtkXWindow
*>(rWindow
.get()))
13021 return pGtkXWindow
->getFrameWeld();
13022 return SalInstance::GetFrameWeld(rWindow
);
13025 weld::Window
* GtkSalFrame::GetFrameWeld() const
13028 m_xFrameWeld
.reset(new GtkInstanceWindow(GTK_WINDOW(gtk_widget_get_toplevel(getWindow())), nullptr, false));
13029 return m_xFrameWeld
.get();
13032 void* GtkInstance::CreateGStreamerSink(const SystemChildWindow
*pWindow
)
13034 #if ENABLE_GSTREAMER_1_0
13035 auto aSymbol
= gstElementFactoryNameSymbol();
13039 const SystemEnvData
* pEnvData
= pWindow
->GetSystemData();
13043 GstElement
* pVideosink
= aSymbol("gtksink", "gtksink");
13047 GtkWidget
*pGstWidget
;
13048 g_object_get(pVideosink
, "widget", &pGstWidget
, nullptr);
13049 gtk_widget_set_vexpand(pGstWidget
, true);
13050 gtk_widget_set_hexpand(pGstWidget
, true);
13052 GtkWidget
*pParent
= static_cast<GtkWidget
*>(pEnvData
->pWidget
);
13053 gtk_container_add(GTK_CONTAINER(pParent
), pGstWidget
);
13054 g_object_unref(pGstWidget
);
13055 gtk_widget_show_all(pParent
);
13064 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */