[Libreoffice-commits] core.git: vcl/unx

Caolán McNamara (via logerrit) logerrit at kemper.freedesktop.org
Fri May 28 12:06:17 UTC 2021


 vcl/unx/gtk3/gtkinst.cxx | 2162 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 1942 insertions(+), 220 deletions(-)

New commits:
commit 0b212dc041c96e706b1b3321fcb9151d20d5dd1c
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Fri May 28 09:56:27 2021 +0100
Commit:     Caolán McNamara <caolanm at redhat.com>
CommitDate: Fri May 28 14:05:33 2021 +0200

    gtk4: reenable ComboBox
    
    lets find out if we can return to using the real GtkComboBox
    instead of a look-alike replacement.
    
    Change-Id: I28d005b37835f09ace095a3642f7c06be6f1e27b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116321
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index e16c22b951ba..9815ccf53967 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -11196,7 +11196,6 @@ namespace
         return found;
     }
 
-#if !GTK_CHECK_VERSION(4, 0, 0)
     void insert_row(GtkListStore* pListStore, GtkTreeIter& iter, int pos, const OUString* pId, std::u16string_view rText, const OUString* pIconName, const VirtualDevice* pDevice)
     {
         if (!pIconName && !pDevice)
@@ -11245,7 +11244,6 @@ namespace
             }
         }
     }
-#endif
 }
 
 namespace
@@ -16067,10 +16065,10 @@ void GtkInstanceDrawingArea::im_context_set_cursor_location(const tools::Rectang
 
 }
 
-#if !GTK_CHECK_VERSION(4, 0, 0)
-
 namespace {
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
+
 GtkBuilder* makeMenuToggleButtonBuilder()
 {
     OUString aUri(AllSettings::GetUIRootDir() + "vcl/ui/menutogglebutton.ui");
@@ -16105,22 +16103,26 @@ public:
     }
 };
 
-class GtkInstanceComboBox : public GtkInstanceContainer, public vcl::ISearchableStringList, public virtual weld::ComboBox
+#endif
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+
+class GtkInstanceComboBox : public GtkInstanceWidget, public vcl::ISearchableStringList, public virtual weld::ComboBox
 {
 private:
-    GtkBuilder* m_pComboBuilder;
     GtkComboBox* m_pComboBox;
-    GtkOverlay* m_pOverlay;
-    GtkTreeView* m_pTreeView;
-    GtkMenuButton* m_pOverlayButton; // button that the StyleDropdown uses on an active row
-    GtkWindow* m_pMenuWindow;
+//    GtkOverlay* m_pOverlay;
+//    GtkTreeView* m_pTreeView;
+//    GtkMenuButton* m_pOverlayButton; // button that the StyleDropdown uses on an active row
+//    GtkWindow* m_pMenuWindow;
     GtkTreeModel* m_pTreeModel;
     GtkCellRenderer* m_pButtonTextRenderer;
     GtkCellRenderer* m_pMenuTextRenderer;
-    GtkWidget* m_pToggleButton;
+//    GtkWidget* m_pToggleButton;
     GtkWidget* m_pEntry;
+    GtkEditable* m_pEditable;
     GtkCellView* m_pCellView;
-    std::unique_ptr<CustomRenderMenuButtonHelper> m_xCustomMenuButtonHelper;
+//    std::unique_ptr<CustomRenderMenuButtonHelper> m_xCustomMenuButtonHelper;
     std::unique_ptr<vcl::Font> m_xFont;
     std::unique_ptr<comphelper::string::NaturalStringSorter> m_xSorter;
     vcl::QuickSelectionEngine m_aQuickSelectionEngine;
@@ -16136,17 +16138,17 @@ private:
     bool m_bActivateCalled;
     gint m_nTextCol;
     gint m_nIdCol;
-    gulong m_nToggleFocusInSignalId;
-    gulong m_nToggleFocusOutSignalId;
+//    gulong m_nToggleFocusInSignalId;
+//    gulong m_nToggleFocusOutSignalId;
     gulong m_nRowActivatedSignalId;
     gulong m_nChangedSignalId;
     gulong m_nPopupShownSignalId;
-    gulong m_nKeyPressEventSignalId;
+//    gulong m_nKeyPressEventSignalId;
     gulong m_nEntryInsertTextSignalId;
     gulong m_nEntryActivateSignalId;
-    gulong m_nEntryFocusInSignalId;
-    gulong m_nEntryFocusOutSignalId;
-    gulong m_nEntryKeyPressEventSignalId;
+//    gulong m_nEntryFocusInSignalId;
+//    gulong m_nEntryFocusOutSignalId;
+//    gulong m_nEntryKeyPressEventSignalId;
     guint m_nAutoCompleteIdleId;
     gint m_nNonCustomLineHeight;
     gint m_nPrePopupCursorPos;
@@ -16264,12 +16266,13 @@ private:
         m_bChangedByMenu = false;
     }
 
-    static void signalPopupToggled(GtkToggleButton* /*pToggleButton*/, gpointer widget)
+    static void signalPopupToggled(GObject*, GParamSpec*, gpointer widget)
     {
         GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
         pThis->signal_popup_toggled();
     }
 
+#if 0
     int get_popup_height(gint& rPopupWidth)
     {
         const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
@@ -16309,7 +16312,9 @@ private:
 
         return nHeight;
     }
+#endif
 
+#if 0
     void toggle_menu()
     {
         if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_pToggleButton)))
@@ -16365,14 +16370,19 @@ private:
             show_menu(pComboBox, m_pMenuWindow);
         }
     }
+#endif
 
     virtual void signal_popup_toggled() override
     {
         m_aQuickSelectionEngine.Reset();
 
-        toggle_menu();
+        // toggle_menu();
+
+        GValue value = G_VALUE_INIT;
+        g_value_init(&value, G_TYPE_BOOLEAN);
+        g_object_get_property(G_OBJECT(m_pComboBox), "popup-shown", &value);
+        bool bIsShown = g_value_get_boolean(&value);
 
-        bool bIsShown = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_pToggleButton));
         if (m_bPopupActive != bIsShown)
         {
             m_bPopupActive = bIsShown;
@@ -16514,6 +16524,7 @@ private:
         return bRet;
     }
 
+#if 0
     // https://gitlab.gnome.org/GNOME/gtk/issues/310
     //
     // in the absence of a built-in solution
@@ -16525,11 +16536,13 @@ private:
         GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
         return pThis->signal_key_press(pEvent);
     }
+#endif
 
     // tdf#131076 we want return in a ComboBox to act like return in a
     // GtkEntry and activate the default dialog/assistant button
     bool combobox_activate()
     {
+#if 0
         GtkWidget *pComboBox = GTK_WIDGET(m_pToggleButton);
         GtkWidget *pToplevel = widget_get_toplevel(pComboBox);
         GtkWindow *pWindow = GTK_WINDOW(pToplevel);
@@ -16542,8 +16555,12 @@ private:
         if (pDefaultWidget && pDefaultWidget != m_pToggleButton && gtk_widget_get_sensitive(pDefaultWidget))
             bDone = gtk_widget_activate(pDefaultWidget);
         return bDone;
+#else
+        return false;
+#endif
     }
 
+#if 0
     static gboolean signalEntryKeyPress(GtkWidget*, GdkEventKey* pEvent, gpointer widget)
     {
         GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
@@ -16706,6 +16723,7 @@ private:
 
         return bDone;
     }
+#endif
 
     vcl::StringEntryIdentifier typeahead_getEntry(int nPos, OUString& out_entryText) const
     {
@@ -16727,6 +16745,7 @@ private:
 
     void tree_view_set_cursor(int pos)
     {
+#if 0
         if (pos == -1)
         {
             gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(m_pTreeView));
@@ -16743,12 +16762,13 @@ private:
                 gtk_cell_view_set_displayed_row(m_pCellView, path);
             gtk_tree_path_free(path);
         }
+#endif
     }
 
     int tree_view_get_cursor() const
     {
         int nRet = -1;
-
+#if 0
         GtkTreePath* path;
         gtk_tree_view_get_cursor(m_pTreeView, &path, nullptr);
         if (path)
@@ -16758,6 +16778,7 @@ private:
             nRet = indices[depth-1];
             gtk_tree_path_free(path);
         }
+#endif
 
         return nRet;
     }
@@ -16808,6 +16829,7 @@ private:
         set_typeahead_selected_entry(nSelect);
     }
 
+#if 0
     static void signalGrabBroken(GtkWidget*, GdkEventGrabBroken *pEvent, gpointer widget)
     {
         GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
@@ -16878,6 +16900,7 @@ private:
             m_bHoverSelection = true;
         }
     }
+#endif
 
     static void signalRowActivated(GtkTreeView*, GtkTreePath*, GtkTreeViewColumn*, gpointer widget)
     {
@@ -16891,12 +16914,12 @@ private:
         m_bChangedByMenu = true;
         disable_notify_events();
         int nActive = get_active();
-        if (m_pEntry)
-            gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text(nActive), RTL_TEXTENCODING_UTF8).getStr());
+        if (m_pEditable)
+            gtk_editable_set_text(m_pEditable, OUStringToOString(get_text(nActive), RTL_TEXTENCODING_UTF8).getStr());
         else
             tree_view_set_cursor(nActive);
         enable_notify_events();
-        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
+//        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
         fire_signal_changed();
         update_mru();
     }
@@ -16904,7 +16927,7 @@ private:
     void do_clear()
     {
         disable_notify_events();
-        gtk_tree_view_set_row_separator_func(m_pTreeView, nullptr, nullptr, nullptr);
+//        gtk_tree_view_set_row_separator_func(m_pTreeView, nullptr, nullptr, nullptr);
         m_aSeparatorRows.clear();
         gtk_list_store_clear(GTK_LIST_STORE(m_pTreeModel));
         m_nMRUCount = 0;
@@ -16963,7 +16986,7 @@ private:
 
     int get_active_including_mru() const
     {
-        return tree_view_get_cursor();
+        return gtk_combo_box_get_active(m_pComboBox);
     }
 
     void set_active_including_mru(int pos, bool bInteractive)
@@ -16972,12 +16995,12 @@ private:
 
         tree_view_set_cursor(pos);
 
-        if (m_pEntry)
+        if (m_pEditable)
         {
             if (pos != -1)
-                gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text_including_mru(pos), RTL_TEXTENCODING_UTF8).getStr());
+                gtk_editable_set_text(m_pEditable, OUStringToOString(get_text_including_mru(pos), RTL_TEXTENCODING_UTF8).getStr());
             else
-                gtk_entry_set_text(GTK_ENTRY(m_pEntry), "");
+                gtk_editable_set_text(m_pEditable, "");
         }
 
         m_bChangedByMenu = false;
@@ -17049,8 +17072,10 @@ private:
     {
         disable_notify_events();
         GtkTreeIter iter;
+#if 0
         if (!gtk_tree_view_get_row_separator_func(m_pTreeView))
             gtk_tree_view_set_row_separator_func(m_pTreeView, separatorFunction, this, nullptr);
+#endif
         insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, &rId, u"", nullptr, nullptr);
         GtkTreePath* pPath = gtk_tree_path_new_from_indices(pos, -1);
         m_aSeparatorRows.emplace_back(gtk_tree_row_reference_new(m_pTreeModel, pPath));
@@ -17066,6 +17091,7 @@ private:
         enable_notify_events();
     }
 
+#if 0
     static gboolean signalGetChildPosition(GtkOverlay*, GtkWidget*, GdkRectangle* pAllocation, gpointer widget)
     {
         GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
@@ -17121,21 +17147,7 @@ private:
         assert(nRow != -1);
         tree_view_set_cursor(nRow); // select the buttons row
     }
-
-    void signal_combo_mnemonic_activate()
-    {
-        if (m_pEntry)
-            gtk_widget_grab_focus(m_pEntry);
-        else
-            gtk_widget_grab_focus(m_pToggleButton);
-    }
-
-    static gboolean signalComboMnemonicActivate(GtkWidget*, gboolean, gpointer widget)
-    {
-        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
-        pThis->signal_combo_mnemonic_activate();
-        return true;
-    }
+#endif
 
     int include_mru(int pos)
     {
@@ -17145,18 +17157,18 @@ private:
     }
 
 public:
-    GtkInstanceComboBox(GtkBuilder* pComboBuilder, GtkComboBox* pComboBox, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
-        : GtkInstanceContainer(GTK_CONTAINER(gtk_builder_get_object(pComboBuilder, "box")), pBuilder, bTakeOwnership)
-        , m_pComboBuilder(pComboBuilder)
+    GtkInstanceComboBox(GtkComboBox* pComboBox, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
+        : GtkInstanceWidget(GTK_WIDGET(pComboBox), pBuilder, bTakeOwnership)
         , m_pComboBox(pComboBox)
-        , m_pOverlay(GTK_OVERLAY(gtk_builder_get_object(pComboBuilder, "overlay")))
-        , m_pTreeView(GTK_TREE_VIEW(gtk_builder_get_object(pComboBuilder, "treeview")))
-        , m_pOverlayButton(GTK_MENU_BUTTON(gtk_builder_get_object(pComboBuilder, "overlaybutton")))
-        , m_pMenuWindow(GTK_WINDOW(gtk_builder_get_object(pComboBuilder, "popup")))
+//        , m_pOverlay(GTK_OVERLAY(gtk_builder_get_object(pComboBuilder, "overlay")))
+//        , m_pTreeView(GTK_TREE_VIEW(gtk_builder_get_object(pComboBuilder, "treeview")))
+//        , m_pOverlayButton(GTK_MENU_BUTTON(gtk_builder_get_object(pComboBuilder, "overlaybutton")))
+//        , m_pMenuWindow(GTK_WINDOW(gtk_builder_get_object(pComboBuilder, "popup")))
         , m_pTreeModel(gtk_combo_box_get_model(pComboBox))
         , m_pButtonTextRenderer(nullptr)
-        , m_pToggleButton(GTK_WIDGET(gtk_builder_get_object(pComboBuilder, "button")))
-        , m_pEntry(GTK_WIDGET(gtk_builder_get_object(pComboBuilder, "entry")))
+//        , m_pToggleButton(GTK_WIDGET(gtk_builder_get_object(pComboBuilder, "button")))
+        , m_pEntry(GTK_IS_ENTRY(gtk_combo_box_get_child(pComboBox)) ? gtk_combo_box_get_child(pComboBox) : nullptr)
+        , m_pEditable(GTK_EDITABLE(m_pEntry))
         , m_pCellView(nullptr)
         , m_aQuickSelectionEngine(*this)
         , m_bHoverSelection(false)
@@ -17169,11 +17181,11 @@ public:
         , m_bActivateCalled(false)
         , m_nTextCol(gtk_combo_box_get_entry_text_column(pComboBox))
         , m_nIdCol(gtk_combo_box_get_id_column(pComboBox))
-        , m_nToggleFocusInSignalId(0)
-        , m_nToggleFocusOutSignalId(0)
-        , m_nRowActivatedSignalId(g_signal_connect(m_pTreeView, "row-activated", G_CALLBACK(signalRowActivated), this))
-        , m_nChangedSignalId(g_signal_connect(m_pEntry, "changed", G_CALLBACK(signalChanged), this))
-        , m_nPopupShownSignalId(g_signal_connect(m_pToggleButton, "toggled", G_CALLBACK(signalPopupToggled), this))
+//        , m_nToggleFocusInSignalId(0)
+//        , m_nToggleFocusOutSignalId(0)
+//        , m_nRowActivatedSignalId(g_signal_connect(m_pTreeView, "row-activated", G_CALLBACK(signalRowActivated), this))
+        , m_nChangedSignalId(g_signal_connect(m_pComboBox, "changed", G_CALLBACK(signalChanged), this))
+        , m_nPopupShownSignalId(g_signal_connect(m_pComboBox, "notify::popup-shown", G_CALLBACK(signalPopupToggled), this))
         , m_nAutoCompleteIdleId(0)
         , m_nNonCustomLineHeight(-1)
         , m_nPrePopupCursorPos(-1)
@@ -17182,119 +17194,41 @@ public:
     {
         int nActive = gtk_combo_box_get_active(m_pComboBox);
 
-        if (gtk_style_context_has_class(gtk_widget_get_style_context(GTK_WIDGET(m_pComboBox)), "small-button"))
-            gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(getContainer())), "small-button");
-
-        insertAsParent(GTK_WIDGET(m_pComboBox), GTK_WIDGET(getContainer()));
-        gtk_widget_set_visible(GTK_WIDGET(m_pComboBox), false);
-        gtk_widget_set_no_show_all(GTK_WIDGET(m_pComboBox), true);
-
-        gtk_tree_view_set_model(m_pTreeView, m_pTreeModel);
-        /* tdf#136455 gtk_combo_box_set_model with a null Model should be good
-           enough. But in practice, while the ComboBox model is unset, GTK
-           doesn't unset the ComboBox menus model, so that remains listening to
-           additions to the ListStore and slowing things down massively.
-           Using a new model does reset the menu to listen to that unused one instead */
-        gtk_combo_box_set_model(m_pComboBox, GTK_TREE_MODEL(gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING)));
-
-        GtkTreeViewColumn* pCol = gtk_tree_view_column_new();
-        gtk_tree_view_append_column(m_pTreeView, pCol);
-
-        bool bPixbufUsedSurface = gtk_tree_model_get_n_columns(m_pTreeModel) == 4;
-
-        GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_pComboBox));
-        // move the cell renderers from the combobox to the replacement treeview
-        m_pMenuTextRenderer = static_cast<GtkCellRenderer*>(cells->data);
-        for (GList* pRenderer = g_list_first(cells); pRenderer; pRenderer = g_list_next(pRenderer))
-        {
-            GtkCellRenderer* pCellRenderer = GTK_CELL_RENDERER(pRenderer->data);
-            bool bTextRenderer = pCellRenderer == m_pMenuTextRenderer;
-            gtk_tree_view_column_pack_end(pCol, pCellRenderer, bTextRenderer);
-            if (!bTextRenderer)
-            {
-                if (bPixbufUsedSurface)
-                    gtk_tree_view_column_set_attributes(pCol, pCellRenderer, "surface", 3, nullptr);
-                else
-                    gtk_tree_view_column_set_attributes(pCol, pCellRenderer, "pixbuf", 2, nullptr);
-            }
-        }
-
-        gtk_tree_view_column_set_attributes(pCol, m_pMenuTextRenderer, "text", m_nTextCol, nullptr);
-
         if (gtk_combo_box_get_has_entry(m_pComboBox))
         {
             m_bAutoComplete = true;
-            m_nEntryInsertTextSignalId = g_signal_connect(m_pEntry, "insert-text", G_CALLBACK(signalEntryInsertText), this);
+            m_nEntryInsertTextSignalId = g_signal_connect(m_pEditable, "insert-text", G_CALLBACK(signalEntryInsertText), this);
             m_nEntryActivateSignalId = g_signal_connect(m_pEntry, "activate", G_CALLBACK(signalEntryActivate), this);
-            m_nEntryFocusInSignalId = g_signal_connect(m_pEntry, "focus-in-event", G_CALLBACK(signalEntryFocusIn), this);
-            m_nEntryFocusOutSignalId = g_signal_connect(m_pEntry, "focus-out-event", G_CALLBACK(signalEntryFocusOut), this);
-            m_nEntryKeyPressEventSignalId = g_signal_connect(m_pEntry, "key-press-event", G_CALLBACK(signalEntryKeyPress), this);
-            m_nKeyPressEventSignalId = 0;
+//            m_nEntryFocusInSignalId = g_signal_connect(m_pEntry, "focus-in-event", G_CALLBACK(signalEntryFocusIn), this);
+//            m_nEntryFocusOutSignalId = g_signal_connect(m_pEntry, "focus-out-event", G_CALLBACK(signalEntryFocusOut), this);
+//            m_nEntryKeyPressEventSignalId = g_signal_connect(m_pEntry, "key-press-event", G_CALLBACK(signalEntryKeyPress), this);
+//            m_nKeyPressEventSignalId = 0;
         }
         else
         {
-            gtk_widget_set_visible(m_pEntry, false);
-            m_pEntry = nullptr;
-
-            GtkWidget* pArrow = GTK_WIDGET(gtk_builder_get_object(pComboBuilder, "arrow"));
-            gtk_container_child_set(getContainer(), m_pToggleButton, "expand", true, nullptr);
-
-            auto m_pCellArea = gtk_cell_area_box_new();
-            m_pCellView = GTK_CELL_VIEW(gtk_cell_view_new_with_context(m_pCellArea, nullptr));
-            gtk_widget_set_hexpand(GTK_WIDGET(m_pCellView), true);
-            GtkBox* pBox = GTK_BOX(gtk_widget_get_parent(pArrow));
-
-            gint nImageSpacing(2);
-            GtkStyleContext *pContext = gtk_widget_get_style_context(GTK_WIDGET(m_pToggleButton));
-            gtk_style_context_get_style(pContext, "image-spacing", &nImageSpacing, nullptr);
-            gtk_box_set_spacing(pBox, nImageSpacing);
-
-            gtk_box_pack_start(pBox, GTK_WIDGET(m_pCellView), false, true, 0);
-
-            gtk_cell_view_set_fit_model(m_pCellView, true);
-            gtk_cell_view_set_model(m_pCellView, m_pTreeModel);
-
-            m_pButtonTextRenderer = gtk_cell_renderer_text_new();
-            gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(m_pCellView), m_pButtonTextRenderer, true);
-            gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(m_pCellView), m_pButtonTextRenderer, "text", m_nTextCol, nullptr);
-            if (g_list_length(cells) > 1)
-            {
-                GtkCellRenderer* pCellRenderer = gtk_cell_renderer_pixbuf_new();
-                gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(m_pCellView), pCellRenderer, false);
-                if (bPixbufUsedSurface)
-                    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(m_pCellView), pCellRenderer, "surface", 3, nullptr);
-                else
-                    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(m_pCellView), pCellRenderer, "pixbuf", 2, nullptr);
-            }
-
-            gtk_widget_show_all(GTK_WIDGET(m_pCellView));
-
             m_nEntryInsertTextSignalId = 0;
             m_nEntryActivateSignalId = 0;
-            m_nEntryFocusInSignalId = 0;
-            m_nEntryFocusOutSignalId = 0;
-            m_nEntryKeyPressEventSignalId = 0;
-            m_nKeyPressEventSignalId = g_signal_connect(m_pToggleButton, "key-press-event", G_CALLBACK(signalKeyPress), this);
+//            m_nEntryFocusInSignalId = 0;
+//            m_nEntryFocusOutSignalId = 0;
+//            m_nEntryKeyPressEventSignalId = 0;
+//            m_nKeyPressEventSignalId = g_signal_connect(m_pToggleButton, "key-press-event", G_CALLBACK(signalKeyPress), this);
         }
 
-        g_list_free(cells);
-
         if (nActive != -1)
             tree_view_set_cursor(nActive);
 
-        g_signal_connect(getContainer(), "mnemonic-activate", G_CALLBACK(signalComboMnemonicActivate), this);
-
-        g_signal_connect(m_pMenuWindow, "grab-broken-event", G_CALLBACK(signalGrabBroken), this);
-        g_signal_connect(m_pMenuWindow, "button-press-event", G_CALLBACK(signalButtonPress), this);
-        g_signal_connect(m_pMenuWindow, "motion-notify-event", G_CALLBACK(signalMotion), this);
+//        g_signal_connect(m_pMenuWindow, "grab-broken-event", G_CALLBACK(signalGrabBroken), this);
+//        g_signal_connect(m_pMenuWindow, "button-press-event", G_CALLBACK(signalButtonPress), this);
+//        g_signal_connect(m_pMenuWindow, "motion-notify-event", G_CALLBACK(signalMotion), this);
         // support typeahead for the menu itself, typing into the menu will
         // select via the vcl selection engine, a matching entry.
-        g_signal_connect(m_pMenuWindow, "key-press-event", G_CALLBACK(signalKeyPress), this);
-
+//        g_signal_connect(m_pMenuWindow, "key-press-event", G_CALLBACK(signalKeyPress), this);
+#if 0
         g_signal_connect(m_pOverlay, "get-child-position", G_CALLBACK(signalGetChildPosition), this);
         gtk_overlay_add_overlay(m_pOverlay, GTK_WIDGET(m_pOverlayButton));
         g_signal_connect(m_pOverlayButton, "leave-notify-event", G_CALLBACK(signalOverlayButtonCrossing), this);
         g_signal_connect(m_pOverlayButton, "enter-notify-event", G_CALLBACK(signalOverlayButtonCrossing), this);
+#endif
     }
 
     virtual int get_active() const override
@@ -17370,9 +17304,9 @@ public:
 
     virtual OUString get_active_text() const override
     {
-        if (m_pEntry)
+        if (m_pEditable)
         {
-            const gchar* pText = gtk_entry_get_text(GTK_ENTRY(m_pEntry));
+            const gchar* pText = gtk_editable_get_text(m_pEditable);
             return OUString(pText, pText ? strlen(pText) : 0, RTL_TEXTENCODING_UTF8);
         }
 
@@ -17501,18 +17435,18 @@ public:
 
     virtual void set_entry_text(const OUString& rText) override
     {
-        assert(m_pEntry);
+        assert(m_pEditable);
         disable_notify_events();
-        gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(rText, RTL_TEXTENCODING_UTF8).getStr());
+        gtk_editable_set_text(m_pEditable, OUStringToOString(rText, RTL_TEXTENCODING_UTF8).getStr());
         enable_notify_events();
     }
 
     virtual void set_entry_width_chars(int nChars) override
     {
-        assert(m_pEntry);
+        assert(m_pEditable);
         disable_notify_events();
-        gtk_entry_set_width_chars(GTK_ENTRY(m_pEntry), nChars);
-        gtk_entry_set_max_width_chars(GTK_ENTRY(m_pEntry), nChars);
+        gtk_editable_set_width_chars(m_pEditable, nChars);
+        gtk_editable_set_max_width_chars(m_pEditable, nChars);
         enable_notify_events();
     }
 
@@ -17526,16 +17460,16 @@ public:
 
     virtual void select_entry_region(int nStartPos, int nEndPos) override
     {
-        assert(m_pEntry);
+        assert(m_pEditable);
         disable_notify_events();
-        gtk_editable_select_region(GTK_EDITABLE(m_pEntry), nStartPos, nEndPos);
+        gtk_editable_select_region(m_pEditable, nStartPos, nEndPos);
         enable_notify_events();
     }
 
     virtual bool get_entry_selection_bounds(int& rStartPos, int &rEndPos) override
     {
-        assert(m_pEntry);
-        return gtk_editable_get_selection_bounds(GTK_EDITABLE(m_pEntry), &rStartPos, &rEndPos);
+        assert(m_pEditable);
+        return gtk_editable_get_selection_bounds(m_pEditable, &rStartPos, &rEndPos);
     }
 
     virtual void set_entry_completion(bool bEnable, bool bCaseSensitive) override
@@ -17552,26 +17486,26 @@ public:
 
     virtual void set_entry_editable(bool bEditable) override
     {
-        assert(m_pEntry);
-        gtk_editable_set_editable(GTK_EDITABLE(m_pEntry), bEditable);
+        assert(m_pEditable);
+        gtk_editable_set_editable(m_pEditable, bEditable);
     }
 
     virtual void cut_entry_clipboard() override
     {
         assert(m_pEntry);
-        gtk_editable_cut_clipboard(GTK_EDITABLE(m_pEntry));
+        gtk_widget_activate_action(m_pEntry, "cut.clipboard", nullptr);
     }
 
     virtual void copy_entry_clipboard() override
     {
         assert(m_pEntry);
-        gtk_editable_copy_clipboard(GTK_EDITABLE(m_pEntry));
+        gtk_widget_activate_action(m_pEntry, "copy.clipboard", nullptr);
     }
 
     virtual void paste_entry_clipboard() override
     {
         assert(m_pEntry);
-        gtk_editable_paste_clipboard(GTK_EDITABLE(m_pEntry));
+        gtk_widget_activate_action(m_pEntry, "paste.clipboard", nullptr);
     }
 
     virtual void set_entry_font(const vcl::Font& rFont) override
@@ -17597,57 +17531,61 @@ public:
 
     virtual void disable_notify_events() override
     {
-        if (m_pEntry)
+        if (m_pEditable)
         {
-            g_signal_handler_block(m_pEntry, m_nEntryInsertTextSignalId);
+            g_signal_handler_block(m_pEditable, m_nEntryInsertTextSignalId);
             g_signal_handler_block(m_pEntry, m_nEntryActivateSignalId);
-            g_signal_handler_block(m_pEntry, m_nEntryFocusInSignalId);
-            g_signal_handler_block(m_pEntry, m_nEntryFocusOutSignalId);
-            g_signal_handler_block(m_pEntry, m_nEntryKeyPressEventSignalId);
-            g_signal_handler_block(m_pEntry, m_nChangedSignalId);
+//            g_signal_handler_block(m_pEntry, m_nEntryFocusInSignalId);
+//            g_signal_handler_block(m_pEntry, m_nEntryFocusOutSignalId);
+//            g_signal_handler_block(m_pEntry, m_nEntryKeyPressEventSignalId);
         }
+#if 0
         else
             g_signal_handler_block(m_pToggleButton, m_nKeyPressEventSignalId);
-        if (m_nToggleFocusInSignalId)
-            g_signal_handler_block(m_pToggleButton, m_nToggleFocusInSignalId);
-        if (m_nToggleFocusOutSignalId)
-            g_signal_handler_block(m_pToggleButton, m_nToggleFocusOutSignalId);
-        g_signal_handler_block(m_pTreeView, m_nRowActivatedSignalId);
-        g_signal_handler_block(m_pToggleButton, m_nPopupShownSignalId);
-        GtkInstanceContainer::disable_notify_events();
+#endif
+//        if (m_nToggleFocusInSignalId)
+//            g_signal_handler_block(m_pToggleButton, m_nToggleFocusInSignalId);
+//        if (m_nToggleFocusOutSignalId)
+//            g_signal_handler_block(m_pToggleButton, m_nToggleFocusOutSignalId);
+//        g_signal_handler_block(m_pTreeView, m_nRowActivatedSignalId);
+        g_signal_handler_block(m_pComboBox, m_nPopupShownSignalId);
+        g_signal_handler_block(m_pComboBox, m_nChangedSignalId);
+        GtkInstanceWidget::disable_notify_events();
     }
 
     virtual void enable_notify_events() override
     {
-        GtkInstanceContainer::enable_notify_events();
-        g_signal_handler_unblock(m_pToggleButton, m_nPopupShownSignalId);
-        g_signal_handler_unblock(m_pTreeView, m_nRowActivatedSignalId);
-        if (m_nToggleFocusInSignalId)
-            g_signal_handler_unblock(m_pToggleButton, m_nToggleFocusInSignalId);
-        if (m_nToggleFocusOutSignalId)
-            g_signal_handler_unblock(m_pToggleButton, m_nToggleFocusOutSignalId);
-        if (m_pEntry)
+        GtkInstanceWidget::enable_notify_events();
+        g_signal_handler_unblock(m_pComboBox, m_nChangedSignalId);
+        g_signal_handler_unblock(m_pComboBox, m_nPopupShownSignalId);
+//        g_signal_handler_unblock(m_pTreeView, m_nRowActivatedSignalId);
+//        if (m_nToggleFocusInSignalId)
+//            g_signal_handler_unblock(m_pToggleButton, m_nToggleFocusInSignalId);
+//        if (m_nToggleFocusOutSignalId)
+//            g_signal_handler_unblock(m_pToggleButton, m_nToggleFocusOutSignalId);
+        if (m_pEditable)
         {
-            g_signal_handler_unblock(m_pEntry, m_nChangedSignalId);
             g_signal_handler_unblock(m_pEntry, m_nEntryActivateSignalId);
-            g_signal_handler_unblock(m_pEntry, m_nEntryFocusInSignalId);
-            g_signal_handler_unblock(m_pEntry, m_nEntryFocusOutSignalId);
-            g_signal_handler_unblock(m_pEntry, m_nEntryKeyPressEventSignalId);
-            g_signal_handler_unblock(m_pEntry, m_nEntryInsertTextSignalId);
+//            g_signal_handler_unblock(m_pEntry, m_nEntryFocusInSignalId);
+//            g_signal_handler_unblock(m_pEntry, m_nEntryFocusOutSignalId);
+//            g_signal_handler_unblock(m_pEntry, m_nEntryKeyPressEventSignalId);
+            g_signal_handler_unblock(m_pEditable, m_nEntryInsertTextSignalId);
         }
+#if 0
         else
             g_signal_handler_unblock(m_pToggleButton, m_nKeyPressEventSignalId);
+#endif
     }
 
     virtual void freeze() override
     {
         disable_notify_events();
         bool bIsFirstFreeze = IsFirstFreeze();
-        GtkInstanceContainer::freeze();
+        GtkInstanceWidget::freeze();
         if (bIsFirstFreeze)
         {
             g_object_ref(m_pTreeModel);
-            gtk_tree_view_set_model(m_pTreeView, nullptr);
+//            gtk_tree_view_set_model(m_pTreeView, nullptr);
             g_object_freeze_notify(G_OBJECT(m_pTreeModel));
             if (m_xSorter)
             {
@@ -17669,10 +17607,10 @@ public:
                 gtk_tree_sortable_set_sort_column_id(pSortable, m_nTextCol, GTK_SORT_ASCENDING);
             }
             g_object_thaw_notify(G_OBJECT(m_pTreeModel));
-            gtk_tree_view_set_model(m_pTreeView, m_pTreeModel);
+//            gtk_tree_view_set_model(m_pTreeView, m_pTreeModel);
             g_object_unref(m_pTreeModel);
         }
-        GtkInstanceContainer::thaw();
+        GtkInstanceWidget::thaw();
         enable_notify_events();
     }
 
@@ -17683,16 +17621,16 @@ public:
 
     virtual void connect_focus_in(const Link<Widget&, void>& rLink) override
     {
-        if (!m_nToggleFocusInSignalId)
-            m_nToggleFocusInSignalId = g_signal_connect_after(m_pToggleButton, "focus-in-event", G_CALLBACK(signalFocusIn), this);
-        GtkInstanceContainer::connect_focus_in(rLink);
+//        if (!m_nToggleFocusInSignalId)
+//            m_nToggleFocusInSignalId = g_signal_connect_after(m_pToggleButton, "focus-in-event", G_CALLBACK(signalFocusIn), this);
+        GtkInstanceWidget::connect_focus_in(rLink);
     }
 
     virtual void connect_focus_out(const Link<Widget&, void>& rLink) override
     {
-        if (!m_nToggleFocusOutSignalId)
-            m_nToggleFocusOutSignalId = g_signal_connect_after(m_pToggleButton, "focus-out-event", G_CALLBACK(signalFocusOut), this);
-        GtkInstanceContainer::connect_focus_out(rLink);
+//        if (!m_nToggleFocusOutSignalId)
+//            m_nToggleFocusOutSignalId = g_signal_connect_after(m_pToggleButton, "focus-out-event", G_CALLBACK(signalFocusOut), this);
+        GtkInstanceWidget::connect_focus_out(rLink);
     }
 
     virtual void grab_focus() override
@@ -17702,7 +17640,10 @@ public:
         if (m_pEntry)
             gtk_widget_grab_focus(m_pEntry);
         else
-            gtk_widget_grab_focus(m_pToggleButton);
+        {
+//            gtk_widget_grab_focus(m_pToggleButton);
+            gtk_widget_grab_focus(GTK_WIDGET(m_pComboBox));
+        }
     }
 
     virtual bool has_focus() const override
@@ -17710,14 +17651,16 @@ public:
         if (m_pEntry && gtk_widget_has_focus(m_pEntry))
             return true;
 
-        if (gtk_widget_has_focus(m_pToggleButton))
-            return true;
+//        if (gtk_widget_has_focus(m_pToggleButton))
+//            return true;
 
+#if 0
         if (gtk_widget_get_visible(GTK_WIDGET(m_pMenuWindow)))
         {
             if (gtk_widget_has_focus(GTK_WIDGET(m_pOverlayButton)) || gtk_widget_has_focus(GTK_WIDGET(m_pTreeView)))
                 return true;
         }
+#endif
 
         return GtkInstanceWidget::has_focus();
     }
@@ -17731,6 +17674,7 @@ public:
     {
         if (bOn == m_bCustomRenderer)
             return;
+#if 0
         GList* pColumns = gtk_tree_view_get_columns(m_pTreeView);
         // keep the original height around for optimal popup height calculation
         m_nNonCustomLineHeight = bOn ? ::get_height_row(m_pTreeView, pColumns) : -1;
@@ -17755,6 +17699,7 @@ public:
         }
         g_list_free(pColumns);
         m_bCustomRenderer = bOn;
+#endif
     }
 
     void call_signal_custom_render(VirtualDevice& rOutput, const tools::Rectangle& rRect, bool bSelected, const OUString& rId)
@@ -17774,6 +17719,7 @@ public:
 
     virtual void set_item_menu(const OString& rIdent, weld::Menu* pMenu) override
     {
+#if 0
         m_xCustomMenuButtonHelper.reset();
         GtkInstanceMenu* pPopoverWidget = dynamic_cast<GtkInstanceMenu*>(pMenu);
         GtkWidget* pMenuWidget = GTK_WIDGET(pPopoverWidget ? pPopoverWidget->getMenu() : nullptr);
@@ -17783,6 +17729,7 @@ public:
         if (pMenuWidget)
             m_xCustomMenuButtonHelper.reset(new CustomRenderMenuButtonHelper(GTK_MENU(pMenuWidget), GTK_TOGGLE_BUTTON(m_pToggleButton)));
         m_sMenuButtonRow = OUString::fromUtf8(rIdent);
+#endif
     }
 
     OUString get_mru_entries() const override
@@ -17833,6 +17780,7 @@ public:
 
     int get_menu_button_width() const override
     {
+#if 0
         bool bVisible = gtk_widget_get_visible(GTK_WIDGET(m_pOverlayButton));
         if (!bVisible)
             gtk_widget_set_visible(GTK_WIDGET(m_pOverlayButton), true);
@@ -17841,22 +17789,1796 @@ public:
         if (!bVisible)
             gtk_widget_set_visible(GTK_WIDGET(m_pOverlayButton), false);
         return nWidth;
+#else
+        return 0;
+#endif
     }
 
     virtual ~GtkInstanceComboBox() override
     {
-        m_xCustomMenuButtonHelper.reset();
+//        m_xCustomMenuButtonHelper.reset();
         do_clear();
         if (m_nAutoCompleteIdleId)
             g_source_remove(m_nAutoCompleteIdleId);
-        if (m_pEntry)
+        if (m_pEditable)
         {
-            g_signal_handler_disconnect(m_pEntry, m_nChangedSignalId);
-            g_signal_handler_disconnect(m_pEntry, m_nEntryInsertTextSignalId);
+            g_signal_handler_disconnect(m_pEditable, m_nEntryInsertTextSignalId);
             g_signal_handler_disconnect(m_pEntry, m_nEntryActivateSignalId);
-            g_signal_handler_disconnect(m_pEntry, m_nEntryFocusInSignalId);
-            g_signal_handler_disconnect(m_pEntry, m_nEntryFocusOutSignalId);
-            g_signal_handler_disconnect(m_pEntry, m_nEntryKeyPressEventSignalId);
+//            g_signal_handler_disconnect(m_pEntry, m_nEntryFocusInSignalId);
+//            g_signal_handler_disconnect(m_pEntry, m_nEntryFocusOutSignalId);
+//            g_signal_handler_disconnect(m_pEntry, m_nEntryKeyPressEventSignalId);
+        }
+#if 0
+        else
+            g_signal_handler_disconnect(m_pToggleButton, m_nKeyPressEventSignalId);
+#endif
+//        if (m_nToggleFocusInSignalId)
+//            g_signal_handler_disconnect(m_pToggleButton, m_nToggleFocusInSignalId);
+//        if (m_nToggleFocusOutSignalId)
+//            g_signal_handler_disconnect(m_pToggleButton, m_nToggleFocusOutSignalId);
+//        g_signal_handler_disconnect(m_pTreeView, m_nRowActivatedSignalId);
+        g_signal_handler_disconnect(m_pComboBox, m_nPopupShownSignalId);
+        g_signal_handler_disconnect(m_pComboBox, m_nChangedSignalId);
+
+//        gtk_tree_view_set_model(m_pTreeView, nullptr);
+
+    }
+};
+
+#else
+
+class GtkInstanceComboBox : public GtkInstanceContainer, public vcl::ISearchableStringList, public virtual weld::ComboBox
+{
+private:
+    GtkBuilder* m_pComboBuilder;
+    GtkComboBox* m_pComboBox;
+    GtkOverlay* m_pOverlay;
+    GtkTreeView* m_pTreeView;
+    GtkMenuButton* m_pOverlayButton; // button that the StyleDropdown uses on an active row
+    GtkWindow* m_pMenuWindow;
+    GtkTreeModel* m_pTreeModel;
+    GtkCellRenderer* m_pButtonTextRenderer;
+    GtkCellRenderer* m_pMenuTextRenderer;
+    GtkWidget* m_pToggleButton;
+    GtkWidget* m_pEntry;
+    GtkCellView* m_pCellView;
+    std::unique_ptr<CustomRenderMenuButtonHelper> m_xCustomMenuButtonHelper;
+    std::unique_ptr<vcl::Font> m_xFont;
+    std::unique_ptr<comphelper::string::NaturalStringSorter> m_xSorter;
+    vcl::QuickSelectionEngine m_aQuickSelectionEngine;
+    std::vector<std::unique_ptr<GtkTreeRowReference, GtkTreeRowReferenceDeleter>> m_aSeparatorRows;
+    OUString m_sMenuButtonRow;
+    bool m_bHoverSelection;
+    bool m_bMouseInOverlayButton;
+    bool m_bPopupActive;
+    bool m_bAutoComplete;
+    bool m_bAutoCompleteCaseSensitive;
+    bool m_bChangedByMenu;
+    bool m_bCustomRenderer;
+    bool m_bActivateCalled;
+    gint m_nTextCol;
+    gint m_nIdCol;
+    gulong m_nToggleFocusInSignalId;
+    gulong m_nToggleFocusOutSignalId;
+    gulong m_nRowActivatedSignalId;
+    gulong m_nChangedSignalId;
+    gulong m_nPopupShownSignalId;
+    gulong m_nKeyPressEventSignalId;
+    gulong m_nEntryInsertTextSignalId;
+    gulong m_nEntryActivateSignalId;
+    gulong m_nEntryFocusInSignalId;
+    gulong m_nEntryFocusOutSignalId;
+    gulong m_nEntryKeyPressEventSignalId;
+    guint m_nAutoCompleteIdleId;
+    gint m_nNonCustomLineHeight;
+    gint m_nPrePopupCursorPos;
+    int m_nMRUCount;
+    int m_nMaxMRUCount;
+
+    static gboolean idleAutoComplete(gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->auto_complete();
+        return false;
+    }
+
+    void auto_complete()
+    {
+        m_nAutoCompleteIdleId = 0;
+        OUString aStartText = get_active_text();
+        int nStartPos, nEndPos;
+        get_entry_selection_bounds(nStartPos, nEndPos);
+        int nMaxSelection = std::max(nStartPos, nEndPos);
+        if (nMaxSelection != aStartText.getLength())
+            return;
+
+        disable_notify_events();
+        int nActive = get_active();
+        int nStart = nActive;
+
+        if (nStart == -1)
+            nStart = 0;
+
+        int nPos = -1;
+
+        int nZeroRow = 0;
+        if (m_nMRUCount)
+            nZeroRow += (m_nMRUCount + 1);
+
+        if (!m_bAutoCompleteCaseSensitive)
+        {
+            // Try match case insensitive from current position
+            nPos = starts_with(m_pTreeModel, aStartText, 0, nStart, false);
+            if (nPos == -1 && nStart != 0)
+            {
+                // Try match case insensitive, but from start
+                nPos = starts_with(m_pTreeModel, aStartText, 0, nZeroRow, false);
+            }
+        }
+
+        if (nPos == -1)
+        {
+            // Try match case sensitive from current position
+            nPos = starts_with(m_pTreeModel, aStartText, 0, nStart, true);
+            if (nPos == -1 && nStart != 0)
+            {
+                // Try match case sensitive, but from start
+                nPos = starts_with(m_pTreeModel, aStartText, 0, nZeroRow, true);
+            }
+        }
+
+        if (nPos != -1)
+        {
+            OUString aText = get_text_including_mru(nPos);
+            if (aText != aStartText)
+            {
+                SolarMutexGuard aGuard;
+                set_active_including_mru(nPos, true);
+            }
+            select_entry_region(aText.getLength(), aStartText.getLength());
+        }
+        enable_notify_events();
+    }
+
+    static void signalEntryInsertText(GtkEntry* pEntry, const gchar* pNewText, gint nNewTextLength,
+                                      gint* position, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        SolarMutexGuard aGuard;
+        pThis->signal_entry_insert_text(pEntry, pNewText, nNewTextLength, position);
+    }
+
+    void signal_entry_insert_text(GtkEntry* pEntry, const gchar* pNewText, gint nNewTextLength, gint* position)
+    {
+        // first filter inserted text
+        if (m_aEntryInsertTextHdl.IsSet())
+        {
+            OUString sText(pNewText, nNewTextLength, RTL_TEXTENCODING_UTF8);
+            const bool bContinue = m_aEntryInsertTextHdl.Call(sText);
+            if (bContinue && !sText.isEmpty())
+            {
+                OString sFinalText(OUStringToOString(sText, RTL_TEXTENCODING_UTF8));
+                g_signal_handlers_block_by_func(pEntry, reinterpret_cast<gpointer>(signalEntryInsertText), this);
+                gtk_editable_insert_text(GTK_EDITABLE(pEntry), sFinalText.getStr(), sFinalText.getLength(), position);
+                g_signal_handlers_unblock_by_func(pEntry, reinterpret_cast<gpointer>(signalEntryInsertText), this);
+            }
+            g_signal_stop_emission_by_name(pEntry, "insert-text");
+        }
+        if (m_bAutoComplete)
+        {
+            // now check for autocompletes
+            if (m_nAutoCompleteIdleId)
+                g_source_remove(m_nAutoCompleteIdleId);
+            m_nAutoCompleteIdleId = g_idle_add(idleAutoComplete, this);
+        }
+    }
+
+    static void signalChanged(GtkEntry*, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        SolarMutexGuard aGuard;
+        pThis->fire_signal_changed();
+    }
+
+    void fire_signal_changed()
+    {
+        signal_changed();
+        m_bChangedByMenu = false;
+    }
+
+    static void signalPopupToggled(GtkToggleButton* /*pToggleButton*/, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->signal_popup_toggled();
+    }
+
+    int get_popup_height(gint& rPopupWidth)
+    {
+        const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+
+        int nMaxRows = rSettings.GetListBoxMaximumLineCount();
+        bool bAddScrollWidth = false;
+        int nRows = get_count_including_mru();
+        if (nMaxRows < nRows)
+        {
+            nRows = nMaxRows;
+            bAddScrollWidth = true;
+        }
+
+        GList* pColumns = gtk_tree_view_get_columns(m_pTreeView);
+        gint nRowHeight = get_height_row(m_pTreeView, pColumns);
+        g_list_free(pColumns);
+
+        gint nSeparatorHeight = get_height_row_separator(m_pTreeView);
+        gint nHeight = get_height_rows(nRowHeight, nSeparatorHeight, nRows);
+
+        // if we're using a custom renderer, limit the height to the height nMaxRows would be
+        // for a normal renderer, and then round down to how many custom rows fit in that
+        // space
+        if (m_nNonCustomLineHeight != -1 && nRowHeight)
+        {
+            gint nNormalHeight = get_height_rows(m_nNonCustomLineHeight, nSeparatorHeight, nMaxRows);
+            if (nHeight > nNormalHeight)
+            {
+                gint nRowsOnly = nNormalHeight - get_height_rows(0, nSeparatorHeight, nMaxRows);
+                gint nCustomRows = (nRowsOnly + (nRowHeight - 1)) / nRowHeight;
+                nHeight = get_height_rows(nRowHeight, nSeparatorHeight, nCustomRows);
+            }
+        }
+
+        if (bAddScrollWidth)
+            rPopupWidth += rSettings.GetScrollBarSize();
+
+        return nHeight;
+    }
+
+    void toggle_menu()
+    {
+        if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_pToggleButton)))
+        {
+            if (m_bHoverSelection)
+            {
+                // turn hover selection back off until mouse is moved again
+                // *after* menu is shown again
+                gtk_tree_view_set_hover_selection(m_pTreeView, false);
+                m_bHoverSelection = false;
+            }
+
+            do_ungrab(GTK_WIDGET(m_pMenuWindow));
+
+            gtk_widget_hide(GTK_WIDGET(m_pMenuWindow));
+
+            // so gdk_window_move_to_rect will work again the next time
+            gtk_widget_unrealize(GTK_WIDGET(m_pMenuWindow));
+
+            gtk_widget_set_size_request(GTK_WIDGET(m_pMenuWindow), -1, -1);
+
+            if (!m_bActivateCalled)
+                tree_view_set_cursor(m_nPrePopupCursorPos);
+
+            // undo show_menu tooltip blocking
+            GtkWidget* pParent = widget_get_toplevel(m_pToggleButton);
+            GtkSalFrame* pFrame = pParent ? GtkSalFrame::getFromWindow(pParent) : nullptr;
+            if (pFrame)
+                pFrame->UnblockTooltip();
+        }
+        else
+        {
+            GtkWidget* pComboBox = GTK_WIDGET(getContainer());
+
+            gint nComboWidth = gtk_widget_get_allocated_width(pComboBox);
+            GtkRequisition size;
+            gtk_widget_get_preferred_size(GTK_WIDGET(m_pMenuWindow), nullptr, &size);
+
+            gint nPopupWidth = size.width;
+            gint nPopupHeight = get_popup_height(nPopupWidth);
+            nPopupWidth = std::max(nPopupWidth, nComboWidth);
+
+            gtk_widget_set_size_request(GTK_WIDGET(m_pMenuWindow), nPopupWidth, nPopupHeight);
+
+            m_nPrePopupCursorPos = get_active();
+
+            m_bActivateCalled = false;
+
+            // if we are in mru mode always start with the cursor at the top of the menu
+            if (m_nMaxMRUCount)
+                tree_view_set_cursor(0);
+
+            show_menu(pComboBox, m_pMenuWindow);
+        }
+    }
+
+    virtual void signal_popup_toggled() override
+    {
+        m_aQuickSelectionEngine.Reset();
+
+        toggle_menu();
+
+        bool bIsShown = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_pToggleButton));
+        if (m_bPopupActive != bIsShown)
+        {
+            m_bPopupActive = bIsShown;
+            ComboBox::signal_popup_toggled();
+            if (!m_bPopupActive && m_pEntry)
+            {
+                disable_notify_events();
+                //restore focus to the GtkEntry when the popup is gone, which
+                //is what the vcl case does, to ease the transition a little
+                gtk_widget_grab_focus(m_pEntry);
+                enable_notify_events();
+            }
+        }
+    }
+
+    static gboolean signalEntryFocusIn(GtkWidget*, GdkEvent*, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->signal_entry_focus_in();
+        return false;
+    }
+
+    void signal_entry_focus_in()
+    {
+        signal_focus_in();
+    }
+
+    static gboolean signalEntryFocusOut(GtkWidget*, GdkEvent*, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->signal_entry_focus_out();
+        return false;
+    }
+
+    void signal_entry_focus_out()
+    {
+        // if we have an untidy selection on losing focus remove the selection
+        int nStartPos, nEndPos;
+        if (get_entry_selection_bounds(nStartPos, nEndPos))
+        {
+            int nMin = std::min(nStartPos, nEndPos);
+            int nMax = std::max(nStartPos, nEndPos);
+            if (nMin != 0 || nMax != get_active_text().getLength())
+                select_entry_region(0, 0);
+        }
+        signal_focus_out();
+    }
+
+    static void signalEntryActivate(GtkEntry*, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->signal_entry_activate();
+    }
+
+    void signal_entry_activate()
+    {
+        if (m_aEntryActivateHdl.IsSet())
+        {
+            SolarMutexGuard aGuard;
+            if (m_aEntryActivateHdl.Call(*this))
+                g_signal_stop_emission_by_name(m_pEntry, "activate");
+        }
+        update_mru();
+    }
+
+    OUString get(int pos, int col) const
+    {
+        OUString sRet;
+        GtkTreeIter iter;
+        if (gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, pos))
+        {
+            gchar* pStr;
+            gtk_tree_model_get(m_pTreeModel, &iter, col, &pStr, -1);
+            sRet = OUString(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8);
+            g_free(pStr);
+        }
+        return sRet;
+    }
+
+    void set(int pos, int col, std::u16string_view rText)
+    {
+        GtkTreeIter iter;
+        if (gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, pos))
+        {
+            OString aStr(OUStringToOString(rText, RTL_TEXTENCODING_UTF8));
+            gtk_list_store_set(GTK_LIST_STORE(m_pTreeModel), &iter, col, aStr.getStr(), -1);
+        }
+    }
+
+    int find(std::u16string_view rStr, int col, bool bSearchMRUArea) const
+    {
+        GtkTreeIter iter;
+        if (!gtk_tree_model_get_iter_first(m_pTreeModel, &iter))
+            return -1;
+
+        int nRet = 0;
+
+        if (!bSearchMRUArea && m_nMRUCount)
+        {
+            if (!gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, m_nMRUCount + 1))
+                return -1;
+            nRet += (m_nMRUCount + 1);
+        }
+
+        OString aStr(OUStringToOString(rStr, RTL_TEXTENCODING_UTF8).getStr());
+        do
+        {
+            gchar* pStr;
+            gtk_tree_model_get(m_pTreeModel, &iter, col, &pStr, -1);
+            const bool bEqual = g_strcmp0(pStr, aStr.getStr()) == 0;
+            g_free(pStr);
+            if (bEqual)
+                return nRet;
+            ++nRet;
+        } while (gtk_tree_model_iter_next(m_pTreeModel, &iter));
+
+        return -1;
+    }
+
+    bool separator_function(const GtkTreePath* path)
+    {
+        return ::separator_function(path, m_aSeparatorRows);
+    }
+
+    bool separator_function(int pos)
+    {
+        GtkTreePath* path = gtk_tree_path_new_from_indices(pos, -1);
+        bool bRet = separator_function(path);
+        gtk_tree_path_free(path);
+        return bRet;
+    }
+
+    static gboolean separatorFunction(GtkTreeModel* pTreeModel, GtkTreeIter* pIter, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        GtkTreePath* path = gtk_tree_model_get_path(pTreeModel, pIter);
+        bool bRet = pThis->separator_function(path);
+        gtk_tree_path_free(path);
+        return bRet;
+    }
+
+    // https://gitlab.gnome.org/GNOME/gtk/issues/310
+    //
+    // in the absence of a built-in solution
+    // a) support typeahead for the case where there is no entry widget, typing ahead
+    // into the button itself will select via the vcl selection engine, a matching
+    // entry
+    static gboolean signalKeyPress(GtkWidget*, GdkEventKey* pEvent, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        return pThis->signal_key_press(pEvent);
+    }
+
+    // tdf#131076 we want return in a ComboBox to act like return in a
+    // GtkEntry and activate the default dialog/assistant button
+    bool combobox_activate()
+    {
+        GtkWidget *pComboBox = GTK_WIDGET(m_pToggleButton);
+        GtkWidget *pToplevel = widget_get_toplevel(pComboBox);
+        GtkWindow *pWindow = GTK_WINDOW(pToplevel);
+        if (!pWindow)
+            return false;
+        if (!GTK_IS_DIALOG(pWindow) && !GTK_IS_ASSISTANT(pWindow))
+            return false;
+        bool bDone = false;
+        GtkWidget *pDefaultWidget = gtk_window_get_default_widget(pWindow);
+        if (pDefaultWidget && pDefaultWidget != m_pToggleButton && gtk_widget_get_sensitive(pDefaultWidget))
+            bDone = gtk_widget_activate(pDefaultWidget);
+        return bDone;
+    }
+
+    static gboolean signalEntryKeyPress(GtkWidget*, GdkEventKey* pEvent, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        LocalizeDecimalSeparator(pEvent);
+        return pThis->signal_entry_key_press(pEvent);
+    }
+
+    bool signal_entry_key_press(const GdkEventKey* pEvent)
+    {
+        KeyEvent aKEvt(GtkToVcl(*pEvent));
+
+        vcl::KeyCode aKeyCode = aKEvt.GetKeyCode();
+
+        bool bDone = false;
+
+        auto nCode = aKeyCode.GetCode();
+        switch (nCode)
+        {
+            case KEY_DOWN:
+            {
+                sal_uInt16 nKeyMod = aKeyCode.GetModifier();
+                if (!nKeyMod)
+                {
+                    int nCount = get_count_including_mru();
+                    int nActive = get_active_including_mru() + 1;
+                    while (nActive < nCount && separator_function(nActive))
+                        ++nActive;
+                    if (nActive < nCount)
+                        set_active_including_mru(nActive, true);
+                    bDone = true;
+                }
+                else if (nKeyMod == KEY_MOD2 && !m_bPopupActive)
+                {
+                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), true);
+                    bDone = true;
+                }
+                break;
+            }
+            case KEY_UP:
+            {
+                sal_uInt16 nKeyMod = aKeyCode.GetModifier();
+                if (!nKeyMod)
+                {
+                    int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1);
+                    int nActive = get_active_including_mru() - 1;
+                    while (nActive >= nStartBound && separator_function(nActive))
+                        --nActive;
+                    if (nActive >= nStartBound)
+                        set_active_including_mru(nActive, true);
+                    bDone = true;
+                }
+                break;
+            }
+            case KEY_PAGEUP:
+            {
+                sal_uInt16 nKeyMod = aKeyCode.GetModifier();
+                if (!nKeyMod)
+                {
+                    int nCount = get_count_including_mru();
+                    int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1);
+                    int nActive = nStartBound;
+                    while (nActive < nCount && separator_function(nActive))
+                        ++nActive;
+                    if (nActive < nCount)
+                        set_active_including_mru(nActive, true);
+                    bDone = true;
+                }
+                break;
+            }
+            case KEY_PAGEDOWN:
+            {
+                sal_uInt16 nKeyMod = aKeyCode.GetModifier();
+                if (!nKeyMod)
+                {
+                    int nActive = get_count_including_mru() - 1;
+                    int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1);
+                    while (nActive >= nStartBound && separator_function(nActive))
+                        --nActive;
+                    if (nActive >= nStartBound)
+                        set_active_including_mru(nActive, true);
+                    bDone = true;
+                }
+                break;
+            }
+            default:
+                break;
+        }
+
+        return bDone;
+    }
+
+    bool signal_key_press(const GdkEventKey* pEvent)
+    {
+        if (m_bHoverSelection)
+        {
+            // once a key is pressed, turn off hover selection until mouse is
+            // moved again otherwise when the treeview scrolls it jumps to the
+            // position under the mouse.
+            gtk_tree_view_set_hover_selection(m_pTreeView, false);
+            m_bHoverSelection = false;
+        }
+
+        KeyEvent aKEvt(GtkToVcl(*pEvent));
+
+        vcl::KeyCode aKeyCode = aKEvt.GetKeyCode();
+
+        bool bDone = false;
+
+        auto nCode = aKeyCode.GetCode();
+        switch (nCode)
+        {
+            case KEY_DOWN:
+            case KEY_UP:
+            case KEY_PAGEUP:
+            case KEY_PAGEDOWN:
+            case KEY_HOME:
+            case KEY_END:
+            case KEY_LEFT:
+            case KEY_RIGHT:
+            case KEY_RETURN:
+            {
+                m_aQuickSelectionEngine.Reset();
+                sal_uInt16 nKeyMod = aKeyCode.GetModifier();
+                // tdf#131076 don't let bare return toggle menu popup active, but do allow deactivate
+                if (nCode == KEY_RETURN && !nKeyMod && !m_bPopupActive)
+                    bDone = combobox_activate();
+                else if (nCode == KEY_UP && nKeyMod == KEY_MOD2 && m_bPopupActive)
+                {
+                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
+                    bDone = true;
+                }
+                else if (nCode == KEY_DOWN && nKeyMod == KEY_MOD2 && !m_bPopupActive)
+                {
+                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), true);
+                    bDone = true;
+                }
+                break;
+            }
+            case KEY_ESCAPE:
+            {
+                m_aQuickSelectionEngine.Reset();
+                if (m_bPopupActive)
+                {
+                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
+                    bDone = true;
+                }
+                break;
+            }
+            default:
+                // tdf#131076 let base space toggle menu popup when it's not already visible
+                if (nCode == KEY_SPACE && !aKeyCode.GetModifier() && !m_bPopupActive)
+                    bDone = false;
+                else
+                    bDone = m_aQuickSelectionEngine.HandleKeyEvent(aKEvt);
+                break;
+        }
+
+        if (!bDone && !m_pEntry)
+            bDone = signal_entry_key_press(pEvent);
+
+        return bDone;
+    }
+
+    vcl::StringEntryIdentifier typeahead_getEntry(int nPos, OUString& out_entryText) const
+    {
+        int nEntryCount(get_count_including_mru());
+        if (nPos >= nEntryCount)
+            nPos = 0;
+        out_entryText = get_text_including_mru(nPos);
+
+        // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based
+        // => normalize
+        return reinterpret_cast<vcl::StringEntryIdentifier>(nPos + 1);
+    }
+
+    static int typeahead_getEntryPos(vcl::StringEntryIdentifier entry)
+    {
+        // our pos is 0-based, but StringEntryIdentifier does not allow for a NULL
+        return reinterpret_cast<sal_Int64>(entry) - 1;
+    }
+
+    void tree_view_set_cursor(int pos)
+    {
+        if (pos == -1)
+        {
+            gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(m_pTreeView));
+            if (m_pCellView)
+                gtk_cell_view_set_displayed_row(m_pCellView, nullptr);
+        }
+        else
+        {
+            GtkTreePath* path = gtk_tree_path_new_from_indices(pos, -1);
+            if (gtk_tree_view_get_model(m_pTreeView))
+                gtk_tree_view_scroll_to_cell(m_pTreeView, path, nullptr, false, 0, 0);
+            gtk_tree_view_set_cursor(m_pTreeView, path, nullptr, false);
+            if (m_pCellView)
+                gtk_cell_view_set_displayed_row(m_pCellView, path);
+            gtk_tree_path_free(path);
+        }
+    }
+
+    int tree_view_get_cursor() const
+    {
+        int nRet = -1;
+
+        GtkTreePath* path;
+        gtk_tree_view_get_cursor(m_pTreeView, &path, nullptr);
+        if (path)
+        {
+            gint depth;
+            gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth);
+            nRet = indices[depth-1];
+            gtk_tree_path_free(path);
+        }
+
+        return nRet;
+    }
+
+    int get_selected_entry() const
+    {
+        if (m_bPopupActive)
+            return tree_view_get_cursor();
+        else
+            return get_active_including_mru();
+    }
+
+    void set_typeahead_selected_entry(int nSelect)
+    {
+        if (m_bPopupActive)
+            tree_view_set_cursor(nSelect);
+        else
+            set_active_including_mru(nSelect, true);
+    }
+
+    virtual vcl::StringEntryIdentifier CurrentEntry(OUString& out_entryText) const override
+    {
+        int nCurrentPos = get_selected_entry();
+        return typeahead_getEntry((nCurrentPos == -1) ? 0 : nCurrentPos, out_entryText);
+    }
+
+    virtual vcl::StringEntryIdentifier NextEntry(vcl::StringEntryIdentifier currentEntry, OUString& out_entryText) const override
+    {
+        int nNextPos = typeahead_getEntryPos(currentEntry) + 1;
+        return typeahead_getEntry(nNextPos, out_entryText);
+    }
+
+    virtual void SelectEntry(vcl::StringEntryIdentifier entry) override
+    {
+        int nSelect = typeahead_getEntryPos(entry);
+        if (nSelect == get_selected_entry())
+        {
+            // ignore that. This method is a callback from the QuickSelectionEngine, which means the user attempted
+            // to select the given entry by typing its starting letters. No need to act.
+            return;
+        }
+
+        // normalize
+        int nCount = get_count_including_mru();
+        if (nSelect >= nCount)
+            nSelect = nCount ? nCount-1 : -1;
+
+        set_typeahead_selected_entry(nSelect);
+    }
+
+    static void signalGrabBroken(GtkWidget*, GdkEventGrabBroken *pEvent, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->grab_broken(pEvent);
+    }
+
+    void grab_broken(const GdkEventGrabBroken *event)
+    {
+        if (event->grab_window == nullptr)
+        {
+            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
+        }
+        else
+        {
+            //try and regrab, so when we lose the grab to the menu of the color palette
+            //combobox we regain it so the color palette doesn't itself disappear on next
+            //click on the color palette combobox
+            do_grab(GTK_WIDGET(m_pMenuWindow));
+        }
+    }
+
+    static gboolean signalButtonPress(GtkWidget* pWidget, GdkEventButton* pEvent, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        return pThis->button_press(pWidget, pEvent);
+    }
+
+    bool button_press(GtkWidget* pWidget, GdkEventButton* pEvent)
+    {
+        //we want to pop down if the button was pressed outside our popup
+        gdouble x = pEvent->x_root;
+        gdouble y = pEvent->y_root;
+        gint xoffset, yoffset;
+        gdk_window_get_root_origin(widget_get_surface(pWidget), &xoffset, &yoffset);
+
+        GtkAllocation alloc;
+        gtk_widget_get_allocation(pWidget, &alloc);
+        xoffset += alloc.x;
+        yoffset += alloc.y;
+
+        gtk_widget_get_allocation(GTK_WIDGET(m_pMenuWindow), &alloc);
+        gint x1 = alloc.x + xoffset;
+        gint y1 = alloc.y + yoffset;
+        gint x2 = x1 + alloc.width;
+        gint y2 = y1 + alloc.height;
+
+        if (x > x1 && x < x2 && y > y1 && y < y2)
+            return false;
+
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
+
+        return false;
+    }
+
+    static gboolean signalMotion(GtkWidget*, GdkEventMotion*, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->signal_motion();
+        return false;
+    }
+
+    void signal_motion()
+    {
+        // if hover-selection was disabled after pressing a key, then turn it back on again
+        if (!m_bHoverSelection && !m_bMouseInOverlayButton)
+        {
+            gtk_tree_view_set_hover_selection(m_pTreeView, true);
+            m_bHoverSelection = true;
+        }
+    }
+
+    static void signalRowActivated(GtkTreeView*, GtkTreePath*, GtkTreeViewColumn*, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->handle_row_activated();
+    }
+
+    void handle_row_activated()
+    {
+        m_bActivateCalled = true;
+        m_bChangedByMenu = true;
+        disable_notify_events();
+        int nActive = get_active();
+        if (m_pEntry)
+            gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text(nActive), RTL_TEXTENCODING_UTF8).getStr());
+        else
+            tree_view_set_cursor(nActive);
+        enable_notify_events();
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pToggleButton), false);
+        fire_signal_changed();
+        update_mru();
+    }
+
+    void do_clear()
+    {
+        disable_notify_events();
+        gtk_tree_view_set_row_separator_func(m_pTreeView, nullptr, nullptr, nullptr);
+        m_aSeparatorRows.clear();
+        gtk_list_store_clear(GTK_LIST_STORE(m_pTreeModel));
+        m_nMRUCount = 0;
+        enable_notify_events();
+    }
+
+    virtual int get_max_mru_count() const override
+    {
+        return m_nMaxMRUCount;
+    }
+
+    virtual void set_max_mru_count(int nMaxMRUCount) override
+    {
+        m_nMaxMRUCount = nMaxMRUCount;
+        update_mru();
+    }
+
+    void update_mru()
+    {
+        int nMRUCount = m_nMRUCount;
+
+        if (m_nMaxMRUCount)
+        {
+            OUString sActiveText = get_active_text();
+            OUString sActiveId = get_active_id();
+            insert_including_mru(0, sActiveText, &sActiveId, nullptr, nullptr);
+            ++m_nMRUCount;
+
+            for (int i = 1; i < m_nMRUCount - 1; ++i)
+            {
+                if (get_text_including_mru(i) == sActiveText)
+                {
+                    remove_including_mru(i);
+                    --m_nMRUCount;
+                    break;
+                }
+            }
+        }
+
+        while (m_nMRUCount > m_nMaxMRUCount)
+        {
+            remove_including_mru(m_nMRUCount - 1);
+            --m_nMRUCount;
+        }
+
+        if (m_nMRUCount && !nMRUCount)
+            insert_separator_including_mru(m_nMRUCount, "separator");
+        else if (!m_nMRUCount && nMRUCount)
+            remove_including_mru(m_nMRUCount);  // remove separator
+    }
+
+    int get_count_including_mru() const
+    {
+        return gtk_tree_model_iter_n_children(m_pTreeModel, nullptr);
+    }
+
+    int get_active_including_mru() const
+    {
+        return tree_view_get_cursor();
+    }
+
+    void set_active_including_mru(int pos, bool bInteractive)
+    {
+        disable_notify_events();
+
+        tree_view_set_cursor(pos);
+
+        if (m_pEntry)
+        {
+            if (pos != -1)
+                gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text_including_mru(pos), RTL_TEXTENCODING_UTF8).getStr());
+            else
+                gtk_entry_set_text(GTK_ENTRY(m_pEntry), "");
+        }
+
+        m_bChangedByMenu = false;
+        enable_notify_events();
+
+        if (bInteractive && !m_bPopupActive)
+            signal_changed();
+    }
+
+    int find_text_including_mru(std::u16string_view rStr, bool bSearchMRU) const
+    {
+        return find(rStr, m_nTextCol, bSearchMRU);
+    }
+
+    int find_id_including_mru(std::u16string_view rId, bool bSearchMRU) const
+    {
+        return find(rId, m_nIdCol, bSearchMRU);
+    }
+
+    OUString get_text_including_mru(int pos) const
+    {
+        return get(pos, m_nTextCol);
+    }
+
+    OUString get_id_including_mru(int pos) const
+    {
+        return get(pos, m_nIdCol);
+    }
+
+    void set_id_including_mru(int pos, std::u16string_view rId)
+    {
+        set(pos, m_nIdCol, rId);
+    }
+
+    void remove_including_mru(int pos)
+    {
+        disable_notify_events();
+        GtkTreeIter iter;
+        gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, pos);
+        if (!m_aSeparatorRows.empty())
+        {
+            bool bFound = false;
+
+            GtkTreePath* pPath = gtk_tree_path_new_from_indices(pos, -1);
+
+            for (auto aIter = m_aSeparatorRows.begin(); aIter != m_aSeparatorRows.end(); ++aIter)
+            {
+                GtkTreePath* seppath = gtk_tree_row_reference_get_path(aIter->get());
+                if (seppath)
+                {
+                    if (gtk_tree_path_compare(pPath, seppath) == 0)
+                        bFound = true;
+                    gtk_tree_path_free(seppath);
+                }
+                if (bFound)
+                {
+                    m_aSeparatorRows.erase(aIter);
+                    break;
+                }
+            }
+
+            gtk_tree_path_free(pPath);
+        }
+        gtk_list_store_remove(GTK_LIST_STORE(m_pTreeModel), &iter);
+        enable_notify_events();
+    }
+
+    void insert_separator_including_mru(int pos, const OUString& rId)
+    {
+        disable_notify_events();
+        GtkTreeIter iter;
+        if (!gtk_tree_view_get_row_separator_func(m_pTreeView))
+            gtk_tree_view_set_row_separator_func(m_pTreeView, separatorFunction, this, nullptr);
+        insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, &rId, u"", nullptr, nullptr);
+        GtkTreePath* pPath = gtk_tree_path_new_from_indices(pos, -1);
+        m_aSeparatorRows.emplace_back(gtk_tree_row_reference_new(m_pTreeModel, pPath));
+        gtk_tree_path_free(pPath);
+        enable_notify_events();
+    }
+
+    void insert_including_mru(int pos, std::u16string_view rText, const OUString* pId, const OUString* pIconName, const VirtualDevice* pImageSurface)
+    {
+        disable_notify_events();
+        GtkTreeIter iter;
+        insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, pId, rText, pIconName, pImageSurface);
+        enable_notify_events();
+    }
+
+    static gboolean signalGetChildPosition(GtkOverlay*, GtkWidget*, GdkRectangle* pAllocation, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        return pThis->signal_get_child_position(pAllocation);
+    }
+
+    bool signal_get_child_position(GdkRectangle* pAllocation)
+    {
+        if (!gtk_widget_get_visible(GTK_WIDGET(m_pOverlayButton)))
+            return false;
+        if (!gtk_widget_get_realized(GTK_WIDGET(m_pTreeView)))
+            return false;
+        int nRow = find_id_including_mru(m_sMenuButtonRow, true);
+        if (nRow == -1)
+            return false;
+
+        gtk_widget_get_preferred_width(GTK_WIDGET(m_pOverlayButton), &pAllocation->width, nullptr);
+
+        GtkTreePath* pPath = gtk_tree_path_new_from_indices(nRow, -1);
+        GList* pColumns = gtk_tree_view_get_columns(m_pTreeView);
+        tools::Rectangle aRect = get_row_area(m_pTreeView, pColumns, pPath);
+        gtk_tree_path_free(pPath);
+        g_list_free(pColumns);
+
+        pAllocation->x = aRect.Right() - pAllocation->width;
+        pAllocation->y = aRect.Top();
+        pAllocation->height = aRect.GetHeight();
+
+        return true;
+    }
+
+    static gboolean signalOverlayButtonCrossing(GtkWidget*, GdkEventCrossing* pEvent, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->signal_overlay_button_crossing(pEvent->type == GDK_ENTER_NOTIFY);
+        return false;
+    }
+
+    void signal_overlay_button_crossing(bool bEnter)
+    {
+        m_bMouseInOverlayButton = bEnter;
+        if (!bEnter)
+            return;
+
+        if (m_bHoverSelection)
+        {
+            // once toggled button is pressed, turn off hover selection until
+            // mouse leaves the overlay button
+            gtk_tree_view_set_hover_selection(m_pTreeView, false);
+            m_bHoverSelection = false;
+        }
+        int nRow = find_id_including_mru(m_sMenuButtonRow, true);
+        assert(nRow != -1);
+        tree_view_set_cursor(nRow); // select the buttons row
+    }
+
+    void signal_combo_mnemonic_activate()
+    {
+        if (m_pEntry)
+            gtk_widget_grab_focus(m_pEntry);
+        else
+            gtk_widget_grab_focus(m_pToggleButton);
+    }
+
+    static gboolean signalComboMnemonicActivate(GtkWidget*, gboolean, gpointer widget)
+    {
+        GtkInstanceComboBox* pThis = static_cast<GtkInstanceComboBox*>(widget);
+        pThis->signal_combo_mnemonic_activate();
+        return true;
+    }
+
+    int include_mru(int pos)
+    {
+        if (m_nMRUCount && pos != -1)
+            pos += (m_nMRUCount + 1);
+        return pos;
+    }
+
+public:
+    GtkInstanceComboBox(GtkBuilder* pComboBuilder, GtkComboBox* pComboBox, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
+        : GtkInstanceContainer(GTK_CONTAINER(gtk_builder_get_object(pComboBuilder, "box")), pBuilder, bTakeOwnership)
+        , m_pComboBuilder(pComboBuilder)
+        , m_pComboBox(pComboBox)
+        , m_pOverlay(GTK_OVERLAY(gtk_builder_get_object(pComboBuilder, "overlay")))
+        , m_pTreeView(GTK_TREE_VIEW(gtk_builder_get_object(pComboBuilder, "treeview")))
+        , m_pOverlayButton(GTK_MENU_BUTTON(gtk_builder_get_object(pComboBuilder, "overlaybutton")))
+        , m_pMenuWindow(GTK_WINDOW(gtk_builder_get_object(pComboBuilder, "popup")))
+        , m_pTreeModel(gtk_combo_box_get_model(pComboBox))
+        , m_pButtonTextRenderer(nullptr)
+        , m_pToggleButton(GTK_WIDGET(gtk_builder_get_object(pComboBuilder, "button")))
+        , m_pEntry(GTK_WIDGET(gtk_builder_get_object(pComboBuilder, "entry")))
+        , m_pCellView(nullptr)
+        , m_aQuickSelectionEngine(*this)
+        , m_bHoverSelection(false)
+        , m_bMouseInOverlayButton(false)
+        , m_bPopupActive(false)
+        , m_bAutoComplete(false)
+        , m_bAutoCompleteCaseSensitive(false)
+        , m_bChangedByMenu(false)
+        , m_bCustomRenderer(false)
+        , m_bActivateCalled(false)
+        , m_nTextCol(gtk_combo_box_get_entry_text_column(pComboBox))
+        , m_nIdCol(gtk_combo_box_get_id_column(pComboBox))
+        , m_nToggleFocusInSignalId(0)
+        , m_nToggleFocusOutSignalId(0)
+        , m_nRowActivatedSignalId(g_signal_connect(m_pTreeView, "row-activated", G_CALLBACK(signalRowActivated), this))
+        , m_nChangedSignalId(g_signal_connect(m_pEntry, "changed", G_CALLBACK(signalChanged), this))
+        , m_nPopupShownSignalId(g_signal_connect(m_pToggleButton, "toggled", G_CALLBACK(signalPopupToggled), this))
+        , m_nAutoCompleteIdleId(0)
+        , m_nNonCustomLineHeight(-1)
+        , m_nPrePopupCursorPos(-1)
+        , m_nMRUCount(0)
+        , m_nMaxMRUCount(0)
+    {
+        int nActive = gtk_combo_box_get_active(m_pComboBox);
+
+        if (gtk_style_context_has_class(gtk_widget_get_style_context(GTK_WIDGET(m_pComboBox)), "small-button"))
+            gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(getContainer())), "small-button");
+
+        insertAsParent(GTK_WIDGET(m_pComboBox), GTK_WIDGET(getContainer()));
+        gtk_widget_set_visible(GTK_WIDGET(m_pComboBox), false);
+        gtk_widget_set_no_show_all(GTK_WIDGET(m_pComboBox), true);
+
+        gtk_tree_view_set_model(m_pTreeView, m_pTreeModel);
+        /* tdf#136455 gtk_combo_box_set_model with a null Model should be good
+           enough. But in practice, while the ComboBox model is unset, GTK
+           doesn't unset the ComboBox menus model, so that remains listening to
+           additions to the ListStore and slowing things down massively.
+           Using a new model does reset the menu to listen to that unused one instead */
+        gtk_combo_box_set_model(m_pComboBox, GTK_TREE_MODEL(gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING)));
+
+        GtkTreeViewColumn* pCol = gtk_tree_view_column_new();
+        gtk_tree_view_append_column(m_pTreeView, pCol);
+
+        bool bPixbufUsedSurface = gtk_tree_model_get_n_columns(m_pTreeModel) == 4;
+
+        GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(m_pComboBox));
+        // move the cell renderers from the combobox to the replacement treeview
+        m_pMenuTextRenderer = static_cast<GtkCellRenderer*>(cells->data);
+        for (GList* pRenderer = g_list_first(cells); pRenderer; pRenderer = g_list_next(pRenderer))
+        {
+            GtkCellRenderer* pCellRenderer = GTK_CELL_RENDERER(pRenderer->data);
+            bool bTextRenderer = pCellRenderer == m_pMenuTextRenderer;
+            gtk_tree_view_column_pack_end(pCol, pCellRenderer, bTextRenderer);
+            if (!bTextRenderer)
+            {
+                if (bPixbufUsedSurface)
+                    gtk_tree_view_column_set_attributes(pCol, pCellRenderer, "surface", 3, nullptr);
+                else
+                    gtk_tree_view_column_set_attributes(pCol, pCellRenderer, "pixbuf", 2, nullptr);
+            }
+        }
+
+        gtk_tree_view_column_set_attributes(pCol, m_pMenuTextRenderer, "text", m_nTextCol, nullptr);
+
+        if (gtk_combo_box_get_has_entry(m_pComboBox))
+        {
+            m_bAutoComplete = true;
+            m_nEntryInsertTextSignalId = g_signal_connect(m_pEntry, "insert-text", G_CALLBACK(signalEntryInsertText), this);
+            m_nEntryActivateSignalId = g_signal_connect(m_pEntry, "activate", G_CALLBACK(signalEntryActivate), this);
+            m_nEntryFocusInSignalId = g_signal_connect(m_pEntry, "focus-in-event", G_CALLBACK(signalEntryFocusIn), this);
+            m_nEntryFocusOutSignalId = g_signal_connect(m_pEntry, "focus-out-event", G_CALLBACK(signalEntryFocusOut), this);
+            m_nEntryKeyPressEventSignalId = g_signal_connect(m_pEntry, "key-press-event", G_CALLBACK(signalEntryKeyPress), this);
+            m_nKeyPressEventSignalId = 0;
+        }
+        else
+        {
+            gtk_widget_set_visible(m_pEntry, false);
+            m_pEntry = nullptr;
+
+            GtkWidget* pArrow = GTK_WIDGET(gtk_builder_get_object(pComboBuilder, "arrow"));
+            gtk_container_child_set(getContainer(), m_pToggleButton, "expand", true, nullptr);
+
+            auto m_pCellArea = gtk_cell_area_box_new();
+            m_pCellView = GTK_CELL_VIEW(gtk_cell_view_new_with_context(m_pCellArea, nullptr));
+            gtk_widget_set_hexpand(GTK_WIDGET(m_pCellView), true);
+            GtkBox* pBox = GTK_BOX(gtk_widget_get_parent(pArrow));
+
+            gint nImageSpacing(2);
+            GtkStyleContext *pContext = gtk_widget_get_style_context(GTK_WIDGET(m_pToggleButton));
+            gtk_style_context_get_style(pContext, "image-spacing", &nImageSpacing, nullptr);
+            gtk_box_set_spacing(pBox, nImageSpacing);
+
+            gtk_box_pack_start(pBox, GTK_WIDGET(m_pCellView), false, true, 0);
+
+            gtk_cell_view_set_fit_model(m_pCellView, true);
+            gtk_cell_view_set_model(m_pCellView, m_pTreeModel);
+
+            m_pButtonTextRenderer = gtk_cell_renderer_text_new();
+            gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(m_pCellView), m_pButtonTextRenderer, true);
+            gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(m_pCellView), m_pButtonTextRenderer, "text", m_nTextCol, nullptr);
+            if (g_list_length(cells) > 1)
+            {
+                GtkCellRenderer* pCellRenderer = gtk_cell_renderer_pixbuf_new();
+                gtk_cell_layout_pack_end(GTK_CELL_LAYOUT(m_pCellView), pCellRenderer, false);
+                if (bPixbufUsedSurface)
+                    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(m_pCellView), pCellRenderer, "surface", 3, nullptr);
+                else
+                    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(m_pCellView), pCellRenderer, "pixbuf", 2, nullptr);
+            }
+
+            gtk_widget_show_all(GTK_WIDGET(m_pCellView));
+
+            m_nEntryInsertTextSignalId = 0;
+            m_nEntryActivateSignalId = 0;
+            m_nEntryFocusInSignalId = 0;
+            m_nEntryFocusOutSignalId = 0;
+            m_nEntryKeyPressEventSignalId = 0;
+            m_nKeyPressEventSignalId = g_signal_connect(m_pToggleButton, "key-press-event", G_CALLBACK(signalKeyPress), this);
+        }
+
+        g_list_free(cells);
+
+        if (nActive != -1)
+            tree_view_set_cursor(nActive);
+
+        g_signal_connect(getContainer(), "mnemonic-activate", G_CALLBACK(signalComboMnemonicActivate), this);
+
+        g_signal_connect(m_pMenuWindow, "grab-broken-event", G_CALLBACK(signalGrabBroken), this);
+        g_signal_connect(m_pMenuWindow, "button-press-event", G_CALLBACK(signalButtonPress), this);
+        g_signal_connect(m_pMenuWindow, "motion-notify-event", G_CALLBACK(signalMotion), this);
+        // support typeahead for the menu itself, typing into the menu will
+        // select via the vcl selection engine, a matching entry.
+        g_signal_connect(m_pMenuWindow, "key-press-event", G_CALLBACK(signalKeyPress), this);
+
+        g_signal_connect(m_pOverlay, "get-child-position", G_CALLBACK(signalGetChildPosition), this);
+        gtk_overlay_add_overlay(m_pOverlay, GTK_WIDGET(m_pOverlayButton));
+        g_signal_connect(m_pOverlayButton, "leave-notify-event", G_CALLBACK(signalOverlayButtonCrossing), this);
+        g_signal_connect(m_pOverlayButton, "enter-notify-event", G_CALLBACK(signalOverlayButtonCrossing), this);
+    }
+
+    virtual int get_active() const override
+    {
+        int nActive = get_active_including_mru();
+        if (nActive == -1)
+            return -1;
+
+        if (m_nMRUCount)
+        {
+            if (nActive < m_nMRUCount)
+                nActive = find_text(get_text_including_mru(nActive));
+            else
+                nActive -= (m_nMRUCount + 1);
+        }
+
+        return nActive;
+    }
+
+    virtual OUString get_active_id() const override
+    {
+        int nActive = get_active();
+        return nActive != -1 ? get_id(nActive) : OUString();
+    }
+
+    virtual void set_active_id(const OUString& rStr) override
+    {
+        set_active(find_id(rStr));
+        m_bChangedByMenu = false;
+    }
+
+    virtual void set_size_request(int nWidth, int nHeight) override
+    {
+        if (m_pButtonTextRenderer)
+        {
+            // tweak the cell render to get a narrower size to stick
+            if (nWidth != -1)
+            {
+                // this bit isn't great, I really want to be able to ellipse the text in the comboboxtext itself and let
+                // the popup menu render them in full, in the interim ellipse both of them
+                g_object_set(G_OBJECT(m_pButtonTextRenderer), "ellipsize", PANGO_ELLIPSIZE_MIDDLE, nullptr);
+
+                // to find out how much of the width of the combobox belongs to the cell, set
+                // the cell and widget to the min cell width and see what the difference is
+                int min;
+                gtk_cell_renderer_get_preferred_width(m_pButtonTextRenderer, m_pWidget, &min, nullptr);
+                gtk_cell_renderer_set_fixed_size(m_pButtonTextRenderer, min, -1);
+                gtk_widget_set_size_request(m_pWidget, min, -1);
+                int nNonCellWidth = get_preferred_size().Width() - min;
+
+                int nCellWidth = nWidth - nNonCellWidth;
+                if (nCellWidth >= 0)
+                {
+                    // now set the cell to the max width which it can be within the
+                    // requested widget width
+                    gtk_cell_renderer_set_fixed_size(m_pButtonTextRenderer, nWidth - nNonCellWidth, -1);
+                }
+            }
+            else
+            {
+                g_object_set(G_OBJECT(m_pButtonTextRenderer), "ellipsize", PANGO_ELLIPSIZE_NONE, nullptr);
+                gtk_cell_renderer_set_fixed_size(m_pButtonTextRenderer, -1, -1);
+            }
+        }
+
+        gtk_widget_set_size_request(m_pWidget, nWidth, nHeight);
+    }
+
+    virtual void set_active(int pos) override
+    {
+        set_active_including_mru(include_mru(pos), false);
+    }
+
+    virtual OUString get_active_text() const override
+    {
+        if (m_pEntry)
+        {
+            const gchar* pText = gtk_entry_get_text(GTK_ENTRY(m_pEntry));
+            return OUString(pText, pText ? strlen(pText) : 0, RTL_TEXTENCODING_UTF8);
+        }
+
+        int nActive = get_active();
+        if (nActive == -1)
+           return OUString();
+
+        return get_text(nActive);
+    }
+
+    virtual OUString get_text(int pos) const override
+    {
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        return get_text_including_mru(pos);
+    }
+
+    virtual OUString get_id(int pos) const override
+    {
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        return get_id_including_mru(pos);
+    }
+
+    virtual void set_id(int pos, const OUString& rId) override
+    {
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        set_id_including_mru(pos, rId);
+    }
+
+    virtual void insert_vector(const std::vector<weld::ComboBoxEntry>& rItems, bool bKeepExisting) override
+    {
+        freeze();
+
+        int nInsertionPoint;
+        if (!bKeepExisting)
+        {
+            clear();
+            nInsertionPoint = 0;
+        }
+        else
+            nInsertionPoint = get_count();
+
+        GtkTreeIter iter;
+        // tdf#125241 inserting backwards is faster
+        for (auto aI = rItems.rbegin(); aI != rItems.rend(); ++aI)
+        {
+            const auto& rItem = *aI;
+            insert_row(GTK_LIST_STORE(m_pTreeModel), iter, nInsertionPoint, rItem.sId.isEmpty() ? nullptr : &rItem.sId,
+                       rItem.sString, rItem.sImage.isEmpty() ? nullptr : &rItem.sImage, nullptr);
+        }
+
+        thaw();
+    }
+
+    virtual void remove(int pos) override
+    {
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        remove_including_mru(pos);
+    }
+
+    virtual void insert(int pos, const OUString& rText, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface) override
+    {
+        insert_including_mru(include_mru(pos), rText, pId, pIconName, pImageSurface);
+    }
+
+    virtual void insert_separator(int pos, const OUString& rId) override
+    {
+        pos = pos == -1 ? get_count() : pos;
+        if (m_nMRUCount)
+            pos += (m_nMRUCount + 1);
+        insert_separator_including_mru(pos, rId);
+    }
+
+    virtual int get_count() const override
+    {
+        int nCount = get_count_including_mru();
+        if (m_nMRUCount)
+            nCount -= (m_nMRUCount + 1);
+        return nCount;
+    }
+
+    virtual int find_text(const OUString& rStr) const override
+    {
+        int nPos = find_text_including_mru(rStr, false);
+        if (nPos != -1 && m_nMRUCount)
+            nPos -= (m_nMRUCount + 1);
+        return nPos;
+    }
+
+    virtual int find_id(const OUString& rId) const override
+    {
+        int nPos = find_id_including_mru(rId, false);
+        if (nPos != -1 && m_nMRUCount)
+            nPos -= (m_nMRUCount + 1);
+        return nPos;
+    }
+
+    virtual void clear() override
+    {
+        do_clear();
+    }
+
+    virtual void make_sorted() override
+    {
+        m_xSorter.reset(new comphelper::string::NaturalStringSorter(
+                            ::comphelper::getProcessComponentContext(),
+                            Application::GetSettings().GetUILanguageTag().getLocale()));
+        GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pTreeModel);
+        gtk_tree_sortable_set_sort_column_id(pSortable, m_nTextCol, GTK_SORT_ASCENDING);
+        gtk_tree_sortable_set_sort_func(pSortable, m_nTextCol, default_sort_func, m_xSorter.get(), nullptr);
+    }
+
+    virtual bool has_entry() const override
+    {
+        return gtk_combo_box_get_has_entry(m_pComboBox);
+    }
+
+    virtual void set_entry_message_type(weld::EntryMessageType eType) override
+    {
+        assert(m_pEntry);
+        ::set_entry_message_type(GTK_ENTRY(m_pEntry), eType);
+    }
+
+    virtual void set_entry_text(const OUString& rText) override
+    {
+        assert(m_pEntry);
+        disable_notify_events();
+        gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(rText, RTL_TEXTENCODING_UTF8).getStr());
+        enable_notify_events();
+    }
+
+    virtual void set_entry_width_chars(int nChars) override
+    {
+        assert(m_pEntry);
+        disable_notify_events();
+        gtk_entry_set_width_chars(GTK_ENTRY(m_pEntry), nChars);
+        gtk_entry_set_max_width_chars(GTK_ENTRY(m_pEntry), nChars);
+        enable_notify_events();
+    }
+
+    virtual void set_entry_max_length(int nChars) override
+    {
+        assert(m_pEntry);
+        disable_notify_events();
+        gtk_entry_set_max_length(GTK_ENTRY(m_pEntry), nChars);
+        enable_notify_events();
+    }
+
+    virtual void select_entry_region(int nStartPos, int nEndPos) override
+    {
+        assert(m_pEntry);
+        disable_notify_events();
+        gtk_editable_select_region(GTK_EDITABLE(m_pEntry), nStartPos, nEndPos);
+        enable_notify_events();
+    }
+
+    virtual bool get_entry_selection_bounds(int& rStartPos, int &rEndPos) override
+    {
+        assert(m_pEntry);
+        return gtk_editable_get_selection_bounds(GTK_EDITABLE(m_pEntry), &rStartPos, &rEndPos);
+    }
+
+    virtual void set_entry_completion(bool bEnable, bool bCaseSensitive) override
+    {
+        m_bAutoComplete = bEnable;
+        m_bAutoCompleteCaseSensitive = bCaseSensitive;
+    }
+
+    virtual void set_entry_placeholder_text(const OUString& rText) override
+    {
+        assert(m_pEntry);
+        gtk_entry_set_placeholder_text(GTK_ENTRY(m_pEntry), rText.toUtf8().getStr());
+    }
+
+    virtual void set_entry_editable(bool bEditable) override
+    {
+        assert(m_pEntry);
+        gtk_editable_set_editable(GTK_EDITABLE(m_pEntry), bEditable);
+    }
+
+    virtual void cut_entry_clipboard() override
+    {
+        assert(m_pEntry);
+        gtk_editable_cut_clipboard(GTK_EDITABLE(m_pEntry));
+    }
+
+    virtual void copy_entry_clipboard() override
+    {
+        assert(m_pEntry);
+        gtk_editable_copy_clipboard(GTK_EDITABLE(m_pEntry));
+    }
+
+    virtual void paste_entry_clipboard() override
+    {
+        assert(m_pEntry);
+        gtk_editable_paste_clipboard(GTK_EDITABLE(m_pEntry));
+    }
+
+    virtual void set_entry_font(const vcl::Font& rFont) override
+    {
+        m_xFont.reset(new vcl::Font(rFont));
+        assert(m_pEntry);
+        PangoAttrList* pOrigList = gtk_entry_get_attributes(GTK_ENTRY(m_pEntry));
+        PangoAttrList* pAttrList = pOrigList ? pango_attr_list_copy(pOrigList) : pango_attr_list_new();
+        update_attr_list(pAttrList, rFont);
+        gtk_entry_set_attributes(GTK_ENTRY(m_pEntry), pAttrList);
+        pango_attr_list_unref(pAttrList);
+    }
+
+    virtual vcl::Font get_entry_font() override
+    {
+        if (m_xFont)
+            return *m_xFont;
+        assert(m_pEntry);
+        PangoContext* pContext = gtk_widget_get_pango_context(m_pEntry);
+        return pango_to_vcl(pango_context_get_font_description(pContext),
+                            Application::GetSettings().GetUILanguageTag().getLocale());
+    }
+
+    virtual void disable_notify_events() override
+    {
+        if (m_pEntry)
+        {
+            g_signal_handler_block(m_pEntry, m_nEntryInsertTextSignalId);
+            g_signal_handler_block(m_pEntry, m_nEntryActivateSignalId);
+            g_signal_handler_block(m_pEntry, m_nEntryFocusInSignalId);
+            g_signal_handler_block(m_pEntry, m_nEntryFocusOutSignalId);
+            g_signal_handler_block(m_pEntry, m_nEntryKeyPressEventSignalId);
+            g_signal_handler_block(m_pEntry, m_nChangedSignalId);
+        }
+        else
+            g_signal_handler_block(m_pToggleButton, m_nKeyPressEventSignalId);
+        if (m_nToggleFocusInSignalId)
+            g_signal_handler_block(m_pToggleButton, m_nToggleFocusInSignalId);
+        if (m_nToggleFocusOutSignalId)
+            g_signal_handler_block(m_pToggleButton, m_nToggleFocusOutSignalId);
+        g_signal_handler_block(m_pTreeView, m_nRowActivatedSignalId);
+        g_signal_handler_block(m_pToggleButton, m_nPopupShownSignalId);
+        GtkInstanceContainer::disable_notify_events();
+    }
+
+    virtual void enable_notify_events() override
+    {
+        GtkInstanceContainer::enable_notify_events();
+        g_signal_handler_unblock(m_pToggleButton, m_nPopupShownSignalId);
+        g_signal_handler_unblock(m_pTreeView, m_nRowActivatedSignalId);
+        if (m_nToggleFocusInSignalId)
+            g_signal_handler_unblock(m_pToggleButton, m_nToggleFocusInSignalId);
+        if (m_nToggleFocusOutSignalId)
+            g_signal_handler_unblock(m_pToggleButton, m_nToggleFocusOutSignalId);
+        if (m_pEntry)
+        {
+            g_signal_handler_unblock(m_pEntry, m_nChangedSignalId);
+            g_signal_handler_unblock(m_pEntry, m_nEntryActivateSignalId);
+            g_signal_handler_unblock(m_pEntry, m_nEntryFocusInSignalId);
+            g_signal_handler_unblock(m_pEntry, m_nEntryFocusOutSignalId);
+            g_signal_handler_unblock(m_pEntry, m_nEntryKeyPressEventSignalId);
+            g_signal_handler_unblock(m_pEntry, m_nEntryInsertTextSignalId);
+        }
+        else
+            g_signal_handler_unblock(m_pToggleButton, m_nKeyPressEventSignalId);
+    }
+
+    virtual void freeze() override
+    {
+        disable_notify_events();
+        bool bIsFirstFreeze = IsFirstFreeze();
+        GtkInstanceContainer::freeze();
+        if (bIsFirstFreeze)
+        {
+            g_object_ref(m_pTreeModel);
+            gtk_tree_view_set_model(m_pTreeView, nullptr);
+            g_object_freeze_notify(G_OBJECT(m_pTreeModel));
+            if (m_xSorter)
+            {
+                GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pTreeModel);
+                gtk_tree_sortable_set_sort_column_id(pSortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+            }
+        }
+        enable_notify_events();
+    }
+
+    virtual void thaw() override
+    {
+        disable_notify_events();
+        if (IsLastThaw())
+        {
+            if (m_xSorter)
+            {
+                GtkTreeSortable* pSortable = GTK_TREE_SORTABLE(m_pTreeModel);
+                gtk_tree_sortable_set_sort_column_id(pSortable, m_nTextCol, GTK_SORT_ASCENDING);
+            }
+            g_object_thaw_notify(G_OBJECT(m_pTreeModel));
+            gtk_tree_view_set_model(m_pTreeView, m_pTreeModel);
+            g_object_unref(m_pTreeModel);
+        }
+        GtkInstanceContainer::thaw();
+        enable_notify_events();
+    }
+
+    virtual bool get_popup_shown() const override
+    {
+        return m_bPopupActive;
+    }
+
+    virtual void connect_focus_in(const Link<Widget&, void>& rLink) override
+    {
+        if (!m_nToggleFocusInSignalId)
+            m_nToggleFocusInSignalId = g_signal_connect_after(m_pToggleButton, "focus-in-event", G_CALLBACK(signalFocusIn), this);
+        GtkInstanceContainer::connect_focus_in(rLink);
+    }
+
+    virtual void connect_focus_out(const Link<Widget&, void>& rLink) override
+    {
+        if (!m_nToggleFocusOutSignalId)
+            m_nToggleFocusOutSignalId = g_signal_connect_after(m_pToggleButton, "focus-out-event", G_CALLBACK(signalFocusOut), this);
+        GtkInstanceContainer::connect_focus_out(rLink);
+    }
+
+    virtual void grab_focus() override
+    {
+        if (has_focus())
+            return;
+        if (m_pEntry)
+            gtk_widget_grab_focus(m_pEntry);
+        else
+            gtk_widget_grab_focus(m_pToggleButton);
+    }
+
+    virtual bool has_focus() const override
+    {
+        if (m_pEntry && gtk_widget_has_focus(m_pEntry))
+            return true;
+
+        if (gtk_widget_has_focus(m_pToggleButton))
+            return true;
+
+        if (gtk_widget_get_visible(GTK_WIDGET(m_pMenuWindow)))
+        {
+            if (gtk_widget_has_focus(GTK_WIDGET(m_pOverlayButton)) || gtk_widget_has_focus(GTK_WIDGET(m_pTreeView)))
+                return true;
+        }
+
+        return GtkInstanceWidget::has_focus();
+    }
+
+    virtual bool changed_by_direct_pick() const override
+    {
+        return m_bChangedByMenu;
+    }
+
+    virtual void set_custom_renderer(bool bOn) override
+    {
+        if (bOn == m_bCustomRenderer)
+            return;
+        GList* pColumns = gtk_tree_view_get_columns(m_pTreeView);
+        // keep the original height around for optimal popup height calculation
+        m_nNonCustomLineHeight = bOn ? ::get_height_row(m_pTreeView, pColumns) : -1;
+        GtkTreeViewColumn* pColumn = GTK_TREE_VIEW_COLUMN(pColumns->data);
+        gtk_cell_layout_clear(GTK_CELL_LAYOUT(pColumn));
+        if (bOn)
+        {
+            GtkCellRenderer *pRenderer = custom_cell_renderer_surface_new();
+            GValue value = G_VALUE_INIT;
+            g_value_init(&value, G_TYPE_POINTER);
+            g_value_set_pointer(&value, static_cast<gpointer>(this));
+            g_object_set_property(G_OBJECT(pRenderer), "instance", &value);
+            gtk_tree_view_column_pack_start(pColumn, pRenderer, true);
+            gtk_tree_view_column_add_attribute(pColumn, pRenderer, "text", m_nTextCol);
+            gtk_tree_view_column_add_attribute(pColumn, pRenderer, "id", m_nIdCol);
+        }
+        else
+        {
+            GtkCellRenderer *pRenderer = gtk_cell_renderer_text_new();
+            gtk_tree_view_column_pack_start(pColumn, pRenderer, true);
+            gtk_tree_view_column_add_attribute(pColumn, pRenderer, "text", m_nTextCol);
+        }
+        g_list_free(pColumns);
+        m_bCustomRenderer = bOn;
+    }
+
+    void call_signal_custom_render(VirtualDevice& rOutput, const tools::Rectangle& rRect, bool bSelected, const OUString& rId)
+    {
+        signal_custom_render(rOutput, rRect, bSelected, rId);
+    }
+
+    Size call_signal_custom_get_size(VirtualDevice& rOutput)
+    {
+        return signal_custom_get_size(rOutput);
+    }
+
+    VclPtr<VirtualDevice> create_render_virtual_device() const override
+    {
+        return create_virtual_device();
+    }
+
+    virtual void set_item_menu(const OString& rIdent, weld::Menu* pMenu) override
+    {
+        m_xCustomMenuButtonHelper.reset();
+        GtkInstanceMenu* pPopoverWidget = dynamic_cast<GtkInstanceMenu*>(pMenu);
+        GtkWidget* pMenuWidget = GTK_WIDGET(pPopoverWidget ? pPopoverWidget->getMenu() : nullptr);
+        gtk_menu_button_set_popup(m_pOverlayButton, pMenuWidget);
+        gtk_widget_set_visible(GTK_WIDGET(m_pOverlayButton), pMenuWidget != nullptr);
+        gtk_widget_queue_resize_no_redraw(GTK_WIDGET(m_pOverlayButton)); // force location recalc
+        if (pMenuWidget)
+            m_xCustomMenuButtonHelper.reset(new CustomRenderMenuButtonHelper(GTK_MENU(pMenuWidget), GTK_TOGGLE_BUTTON(m_pToggleButton)));
+        m_sMenuButtonRow = OUString::fromUtf8(rIdent);
+    }
+
+    OUString get_mru_entries() const override
+    {
+        const sal_Unicode cSep = ';';
+
+        OUStringBuffer aEntries;
+        for (sal_Int32 n = 0; n < m_nMRUCount; n++)
+        {
+            aEntries.append(get_text_including_mru(n));
+            if (n < m_nMRUCount - 1)
+                aEntries.append(cSep);
+        }
+        return aEntries.makeStringAndClear();
+    }
+
+    virtual void set_mru_entries(const OUString& rEntries) override
+    {
+        const sal_Unicode cSep = ';';
+
+        // Remove old MRU entries
+        for (sal_Int32 n = m_nMRUCount; n;)
+            remove_including_mru(--n);
+
+        sal_Int32 nMRUCount = 0;
+        sal_Int32 nIndex = 0;
+        do
+        {
+            OUString aEntry = rEntries.getToken(0, cSep, nIndex);
+            // Accept only existing entries
+            int nPos = find_text(aEntry);
+            if (nPos != -1)
+            {
+                OUString sId = get_id(nPos);
+                insert_including_mru(0, aEntry, &sId, nullptr, nullptr);
+                ++nMRUCount;
+            }
+        }
+        while (nIndex >= 0);
+

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list