diff options
-rw-r--r-- | compilerplugins/clang/badstatics.cxx | 4 | ||||
-rw-r--r-- | cui/source/tabpages/chardlg.cxx | 2 | ||||
-rw-r--r-- | include/svtools/ctrlbox.hxx | 61 | ||||
-rw-r--r-- | include/vcl/combobox.hxx | 3 | ||||
-rw-r--r-- | include/vcl/weld.hxx | 39 | ||||
-rw-r--r-- | sd/source/ui/animations/CustomAnimationList.cxx | 5 | ||||
-rw-r--r-- | svtools/inc/pch/precompiled_svt.hxx | 6 | ||||
-rw-r--r-- | svtools/source/control/ctrlbox.cxx | 503 | ||||
-rw-r--r-- | svx/UIConfig_svx.mk | 1 | ||||
-rw-r--r-- | svx/source/tbxctrls/tbcontrl.cxx | 296 | ||||
-rw-r--r-- | svx/source/tbxctrls/tbunocontroller.cxx | 8 | ||||
-rw-r--r-- | svx/uiconfig/ui/fontnamebox.ui | 30 | ||||
-rw-r--r-- | sw/inc/pch/precompiled_swui.hxx | 8 | ||||
-rw-r--r-- | vcl/source/app/salvtables.cxx | 120 | ||||
-rw-r--r-- | vcl/source/control/combobox.cxx | 12 | ||||
-rw-r--r-- | vcl/source/control/imp_listbox.cxx | 5 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 718 |
17 files changed, 1271 insertions, 550 deletions
diff --git a/compilerplugins/clang/badstatics.cxx b/compilerplugins/clang/badstatics.cxx index 60abc11c222b..e2a48f3462cc 100644 --- a/compilerplugins/clang/badstatics.cxx +++ b/compilerplugins/clang/badstatics.cxx @@ -197,7 +197,9 @@ public: // sc/source/core/tool/adiasync.cxx, would leak // ScAddInAsync* keys if that set is not empty at exit || name == "g_aWindowList" - //vcl/unx/gtk/a11y/atkutil.cxx, asserted empty at exit + //vcl/unx/gtk3/a11y/gtk3atkutil.cxx, asserted empty at exit + || name == "gFontPreviewVirDevs" + //svtools/source/control/ctrlbox.cxx, empty at exit || name == "aLogger" // FormulaLogger& FormulaLogger::get() in sc/source/core/tool/formulalogger.cxx || name == "m_aUncommittedRegistrations" // sw/source/uibase/dbui/dbmgr.cxx || (loplugin::DeclCheck(pVarDecl).Var("aAllListeners") diff --git a/cui/source/tabpages/chardlg.cxx b/cui/source/tabpages/chardlg.cxx index 68eca4fb4d48..11aaf87aa0fc 100644 --- a/cui/source/tabpages/chardlg.cxx +++ b/cui/source/tabpages/chardlg.cxx @@ -766,7 +766,7 @@ void SvxCharNamePage::Reset_Impl( const SfxItemSet& rSet, LanguageGroup eLangGrp } else { - pSizeBox->set_entry_text( OUString() ); + pSizeBox->set_active_or_entry_text(OUString()); if ( eState <= SfxItemState::READONLY ) { pSizeBox->set_sensitive(false); diff --git a/include/svtools/ctrlbox.hxx b/include/svtools/ctrlbox.hxx index a86722a7a603..3dd48f9dd985 100644 --- a/include/svtools/ctrlbox.hxx +++ b/include/svtools/ctrlbox.hxx @@ -21,10 +21,8 @@ #define INCLUDED_SVTOOLS_CTRLBOX_HXX #include <svtools/svtdllapi.h> - #include <editeng/borderline.hxx> - -#include <vcl/combobox.hxx> +#include <vcl/idle.hxx> #include <vcl/metric.hxx> #include <vcl/weld.hxx> @@ -33,6 +31,7 @@ namespace weld { class CustomWeld; } class VirtualDevice; +class BitmapEx; class BorderWidthImpl; class FontList; @@ -327,30 +326,66 @@ private: void set_label_from_date(); }; -class SVT_DLLPUBLIC FontNameBox : public ComboBox +class SVT_DLLPUBLIC FontNameBox { private: + std::unique_ptr<weld::ComboBox> m_xComboBox; std::unique_ptr<ImplFontList> mpFontList; + size_t mnPreviewProgress; bool mbWYSIWYG; OUString maFontMRUEntriesFile; + Idle maUpdateIdle; SVT_DLLPRIVATE void ImplCalcUserItemSize(); SVT_DLLPRIVATE void ImplDestroyFontList(); -protected: + DECL_LINK(CustomRenderHdl, weld::ComboBox::render_args, void); + DECL_STATIC_LINK(FontNameBox, CustomGetSizeHdl, weld::ComboBox::get_size_args, Size); + DECL_LINK(UpdateHdl, Timer*, void); + void LoadMRUEntries( const OUString& aFontMRUEntriesFile ); void SaveMRUEntries( const OUString& aFontMRUEntriesFile ) const; -public: - FontNameBox( vcl::Window* pParent, - WinBits nWinStyle ); - virtual ~FontNameBox() override; - virtual void dispose() override; - virtual void UserDraw( const UserDrawEvent& rUDEvt ) override; + OutputDevice& CachePreview(size_t nIndex, Point* pTopLeft); + +public: + FontNameBox(std::unique_ptr<weld::ComboBox> p); + ~FontNameBox(); void Fill( const FontList* pList ); - void EnableWYSIWYG( bool bEnable ); + void EnableWYSIWYG(); + + void connect_changed(const Link<weld::ComboBox&, void>& rLink) { m_xComboBox->connect_changed(rLink); } + void connect_focus_in(const Link<weld::Widget&, void>& rLink) { m_xComboBox->connect_focus_in(rLink); } + void connect_focus_out(const Link<weld::Widget&, void>& rLink) { m_xComboBox->connect_focus_out(rLink); } + void connect_key_press(const Link<const KeyEvent&, bool>& rLink) { m_xComboBox->connect_key_press(rLink); } + int get_active() const { return m_xComboBox->get_active(); } + OUString get_active_text() const { return m_xComboBox->get_active_text(); } + void set_active_or_entry_text(const OUString& rText); + int get_count() const { return m_xComboBox->get_count(); } + OUString get_text(int nIndex) const { return m_xComboBox->get_text(nIndex); } + void set_sensitive(bool bSensitive) { m_xComboBox->set_sensitive(bSensitive); } + void save_value() { m_xComboBox->save_value(); } + OUString const& get_saved_value() const { return m_xComboBox->get_saved_value(); } + void select_entry_region(int nStartPos, int nEndPos) { m_xComboBox->select_entry_region(nStartPos, nEndPos); } + bool get_entry_selection_bounds(int& rStartPos, int& rEndPos) { return m_xComboBox->get_entry_selection_bounds(rStartPos, rEndPos); } + void clear() { m_xComboBox->clear(); } + void grab_focus() { m_xComboBox->grab_focus(); } + bool has_focus() const { return m_xComboBox->has_focus(); } + void connect_entry_activate(const Link<weld::ComboBox&, bool>& rLink) { m_xComboBox->connect_entry_activate(rLink); } + void connect_get_property_tree(const Link<boost::property_tree::ptree&, void>& rLink) { m_xComboBox->connect_get_property_tree(rLink); } + void set_entry_width_chars(int nWidth) { m_xComboBox->set_entry_width_chars(nWidth); } + void set_size_request(int nWidth, int nHeight) { m_xComboBox->set_size_request(nWidth, nHeight); } + int get_max_mru_count() { return m_xComboBox->get_max_mru_count(); } + void set_max_mru_count(int nCount) { m_xComboBox->set_max_mru_count(nCount); } + + // font size is in points, not pixels, e.g. see Window::[G]etPointFont + vcl::Font get_font() { return m_xComboBox->get_font(); } + void set_entry_font(const vcl::Font& rFont) { m_xComboBox->set_entry_font(rFont); } + vcl::Font get_entry_font() { return m_xComboBox->get_entry_font(); } + + void set_tooltip_text(const OUString& rTip) { m_xComboBox->set_tooltip_text(rTip); } private: void InitFontMRUEntriesFile(); @@ -438,7 +473,7 @@ public: void connect_focus_out(const Link<weld::Widget&, void>& rLink) { m_aFocusOutHdl = rLink; } void connect_key_press(const Link<const KeyEvent&, bool>& rLink) { m_xComboBox->connect_key_press(rLink); } OUString get_active_text() const { return m_xComboBox->get_active_text(); } - void set_entry_text(const OUString& rText); + void set_active_or_entry_text(const OUString& rText); void set_sensitive(bool bSensitive) { m_xComboBox->set_sensitive(bSensitive); } int get_active() const { return m_xComboBox->get_active(); } int get_value() const; diff --git a/include/vcl/combobox.hxx b/include/vcl/combobox.hxx index 562072baa9ef..ef04fff46330 100644 --- a/include/vcl/combobox.hxx +++ b/include/vcl/combobox.hxx @@ -120,6 +120,7 @@ public: void SetDoubleClickHdl(const Link<ComboBox&,void>& rLink); const Link<ComboBox&,void>& GetDoubleClickHdl() const; void SetEntryActivateHdl(const Link<Edit&,bool>& rLink); + void SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink); Size CalcMinimumSize() const override; virtual Size GetOptimalSize() const override; @@ -183,6 +184,8 @@ public: void SetWidthInChars(sal_Int32 nWidthInChars); + long GetDropDownEntryHeight() const; + virtual bool set_property(const OString &rKey, const OUString &rValue) override; virtual FactoryFunction GetUITestFactory() const override; diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index 951b591f654b..2d94323d6434 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -591,6 +591,12 @@ class VCL_DLLPUBLIC ComboBox : virtual public Container private: OUString m_sSavedValue; +public: + // OUString is the id of the row, it may be null to measure the height of a generic line + typedef std::pair<vcl::RenderContext&, const OUString&> get_size_args; + typedef std::tuple<vcl::RenderContext&, const tools::Rectangle&, bool, const OUString&> + render_args; + protected: Link<ComboBox&, void> m_aChangeHdl; Link<ComboBox&, void> m_aPopupToggledHdl; @@ -600,6 +606,21 @@ protected: void signal_changed() { m_aChangeHdl.Call(*this); } virtual void signal_popup_toggled() { m_aPopupToggledHdl.Call(*this); } + Link<render_args, void> m_aRenderHdl; + void signal_custom_render(vcl::RenderContext& rDevice, const tools::Rectangle& rRect, + bool bSelected, const OUString& rId) + { + m_aRenderHdl.Call( + std::tuple<vcl::RenderContext&, const tools::Rectangle, bool, const OUString&>( + rDevice, rRect, bSelected, rId)); + } + + Link<get_size_args, Size> m_aGetSizeHdl; + Size signal_custom_get_size(vcl::RenderContext& rDevice, const OUString& rId) + { + return m_aGetSizeHdl.Call(std::pair<vcl::RenderContext&, const OUString&>(rDevice, rId)); + } + public: virtual void insert(int pos, const OUString& rStr, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface) @@ -694,6 +715,20 @@ public: void save_value() { m_sSavedValue = get_active_text(); } OUString const& get_saved_value() const { return m_sSavedValue; } bool get_value_changed_from_saved() const { return m_sSavedValue != get_active_text(); } + + // for custom rendering a row + void connect_custom_get_size(const Link<get_size_args, Size>& rLink) { m_aGetSizeHdl = rLink; } + void connect_custom_render(const Link<render_args, void>& rLink) { m_aRenderHdl = rLink; } + // call set_custom_renderer after setting custom callbacks + virtual void set_custom_renderer() = 0; + // create a virtual device compatible with the device passed in render_args wrt alpha + virtual VclPtr<VirtualDevice> create_render_virtual_device() const = 0; + + // for mru support + virtual int get_max_mru_count() const = 0; + virtual void set_max_mru_count(int nCount) = 0; + virtual OUString get_mru_entries() const = 0; + virtual void set_mru_entries(const OUString& rEntries) = 0; }; class VCL_DLLPUBLIC TreeIter @@ -734,6 +769,7 @@ protected: std::function<int(const weld::TreeIter&, const weld::TreeIter&)> m_aCustomSort; public: + // OUString is the id of the row, it may be null to measure the height of a generic line typedef std::pair<vcl::RenderContext&, const OUString&> get_size_args; typedef std::tuple<vcl::RenderContext&, const tools::Rectangle&, bool, const OUString&> render_args; @@ -1094,9 +1130,10 @@ public: bool get_value_changed_from_saved() const { return m_sSavedValue != get_selected_text(); } // for custom rendering a cell - virtual void set_column_custom_renderer(int nColumn) = 0; void connect_custom_get_size(const Link<get_size_args, Size>& rLink) { m_aGetSizeHdl = rLink; } void connect_custom_render(const Link<render_args, void>& rLink) { m_aRenderHdl = rLink; } + // call set_column_custom_renderer after setting custom callbacks + virtual void set_column_custom_renderer(int nColumn) = 0; // for dnd virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult) = 0; diff --git a/sd/source/ui/animations/CustomAnimationList.cxx b/sd/source/ui/animations/CustomAnimationList.cxx index 68ecf1362ce9..3b4c3b3afaa6 100644 --- a/sd/source/ui/animations/CustomAnimationList.cxx +++ b/sd/source/ui/animations/CustomAnimationList.cxx @@ -230,6 +230,7 @@ private: OUString msEffectName; CustomAnimationEffectPtr mpEffect; +public: static const long nIconWidth = 19; static const long nItemMinHeight = 38; }; @@ -275,6 +276,8 @@ IMPL_STATIC_LINK(CustomAnimationList, CustomGetSizeHdl, weld::TreeView::get_size const OUString& rId = aPayload.second; CustomAnimationListEntryItem* pItem = reinterpret_cast<CustomAnimationListEntryItem*>(rId.toInt64()); + if (!pItem) + return Size(CustomAnimationListEntryItem::nIconWidth, CustomAnimationListEntryItem::nItemMinHeight); return pItem->GetSize(rRenderContext); } @@ -426,7 +429,6 @@ CustomAnimationList::CustomAnimationList(std::unique_ptr<weld::TreeView> xTreeVi mxEmptyLabel->set_stack_background(); mxTreeView->set_selection_mode(SelectionMode::Multiple); - mxTreeView->set_column_custom_renderer(0); mxTreeView->connect_changed(LINK(this, CustomAnimationList, SelectHdl)); mxTreeView->connect_key_press(LINK(this, CustomAnimationList, KeyInputHdl)); mxTreeView->connect_popup_menu(LINK(this, CustomAnimationList, CommandHdl)); @@ -436,6 +438,7 @@ CustomAnimationList::CustomAnimationList(std::unique_ptr<weld::TreeView> xTreeVi mxTreeView->connect_drag_begin(LINK(this, CustomAnimationList, DragBeginHdl)); mxTreeView->connect_custom_get_size(LINK(this, CustomAnimationList, CustomGetSizeHdl)); mxTreeView->connect_custom_render(LINK(this, CustomAnimationList, CustomRenderHdl)); + mxTreeView->set_column_custom_renderer(0); } CustomAnimationListDropTarget::CustomAnimationListDropTarget(CustomAnimationList& rTreeView) diff --git a/svtools/inc/pch/precompiled_svt.hxx b/svtools/inc/pch/precompiled_svt.hxx index 2797de08da3a..40cfe76421db 100644 --- a/svtools/inc/pch/precompiled_svt.hxx +++ b/svtools/inc/pch/precompiled_svt.hxx @@ -13,7 +13,7 @@ manual changes will be rewritten by the next run of update_pch.sh (which presumably also fixes all possible problems, so it's usually better to use it). - Generated on 2020-02-19 12:45:36 using: + Generated on 2020-04-07 17:26:37 using: ./bin/update_pch svtools svt --cutoff=4 --exclude:system --include:module --exclude:local If after updating build fails, use the following command to locate conflicting headers: @@ -37,6 +37,7 @@ #include <math.h> #include <memory> #include <new> +#include <optional> #include <ostream> #include <set> #include <stddef.h> @@ -102,7 +103,6 @@ #include <vcl/builder.hxx> #include <vcl/button.hxx> #include <vcl/checksum.hxx> -#include <vcl/combobox.hxx> #include <vcl/commandevent.hxx> #include <vcl/ctrl.hxx> #include <vcl/dllapi.h> @@ -162,6 +162,7 @@ #include <basegfx/tuple/b2dtuple.hxx> #include <basegfx/tuple/b2ituple.hxx> #include <basegfx/tuple/b3dtuple.hxx> +#include <basegfx/utils/common.hxx> #include <basegfx/vector/b2dsize.hxx> #include <basegfx/vector/b2dvector.hxx> #include <basegfx/vector/b2enums.hxx> @@ -314,7 +315,6 @@ #include <i18nutil/transliteration.hxx> #include <o3tl/cow_wrapper.hxx> #include <o3tl/deleter.hxx> -#include <optional> #include <o3tl/safeint.hxx> #include <o3tl/strong_int.hxx> #include <o3tl/typed_flags_set.hxx> diff --git a/svtools/source/control/ctrlbox.cxx b/svtools/source/control/ctrlbox.cxx index 51895051551f..d1b9943dfd2c 100644 --- a/svtools/source/control/ctrlbox.cxx +++ b/svtools/source/control/ctrlbox.cxx @@ -61,7 +61,6 @@ #define IMGOUTERTEXTSPACE 5 #define EXTRAFONTSIZE 5 #define GAPTOEXTRAPREVIEW 10 -#define MAXPREVIEWWIDTH 120 #define MINGAPWIDTH 2 #define FONTNAMEBOXMRUENTRIESFILE "/user/config/fontnameboxmruentries" @@ -329,31 +328,43 @@ void DrawLine( OutputDevice& rDev, const basegfx::B2DPoint& rP1, const basegfx:: } -FontNameBox::FontNameBox( vcl::Window* pParent, WinBits nWinStyle ) : - ComboBox( pParent, nWinStyle ) +static Size gUserItemSz; +static int gFontNameBoxes; +static size_t gPreviewsPerDevice; +static std::vector<VclPtr<VirtualDevice>> gFontPreviewVirDevs; +static std::vector<OUString> gRenderedFontNames; + +FontNameBox::FontNameBox(std::unique_ptr<weld::ComboBox> p) + : m_xComboBox(std::move(p)) + , mnPreviewProgress(0) + , mbWYSIWYG(false) + , maUpdateIdle("FontNameBox Preview Update") { - mbWYSIWYG = false; + ++gFontNameBoxes; InitFontMRUEntriesFile(); -} -FontNameBox::~FontNameBox() -{ - disposeOnce(); + maUpdateIdle.SetPriority(TaskPriority::LOWEST); + maUpdateIdle.SetInvokeHandler(LINK(this, FontNameBox, UpdateHdl)); } -void FontNameBox::dispose() +FontNameBox::~FontNameBox() { if (mpFontList) { SaveMRUEntries (maFontMRUEntriesFile); ImplDestroyFontList(); } - ComboBox::dispose(); + --gFontNameBoxes; + if (!gFontNameBoxes) + { + gFontPreviewVirDevs.clear(); + gRenderedFontNames.clear(); + } } -void FontNameBox::SaveMRUEntries( const OUString& aFontMRUEntriesFile ) const +void FontNameBox::SaveMRUEntries(const OUString& aFontMRUEntriesFile) const { - OString aEntries(OUStringToOString(GetMRUEntries(), + OString aEntries(OUStringToOString(m_xComboBox->get_mru_entries(), RTL_TEXTENCODING_UTF8)); if (aEntries.isEmpty() || aFontMRUEntriesFile.isEmpty()) @@ -392,7 +403,7 @@ void FontNameBox::LoadMRUEntries( const OUString& aFontMRUEntriesFile ) aStream.ReadLine( aLine ); OUString aEntries = OStringToOUString(aLine, RTL_TEXTENCODING_UTF8); - SetMRUEntries( aEntries ); + m_xComboBox->set_mru_entries(aEntries); } void FontNameBox::InitFontMRUEntriesFile() @@ -415,61 +426,71 @@ void FontNameBox::ImplDestroyFontList() void FontNameBox::Fill( const FontList* pList ) { // store old text and clear box - OUString aOldText = GetText(); - OUString rEntries = GetMRUEntries(); + OUString aOldText = m_xComboBox->get_active_text(); + OUString rEntries = m_xComboBox->get_mru_entries(); bool bLoadFromFile = rEntries.isEmpty(); - Clear(); + m_xComboBox->freeze(); + m_xComboBox->clear(); ImplDestroyFontList(); mpFontList.reset(new ImplFontList); // insert fonts - sal_uInt16 nFontCount = pList->GetFontNameCount(); - for ( sal_uInt16 i = 0; i < nFontCount; i++ ) + size_t nFontCount = pList->GetFontNameCount(); + for (size_t i = 0; i < nFontCount; ++i) { - const FontMetric& rFontMetric = pList->GetFontName( i ); - sal_Int32 nIndex = InsertEntry( rFontMetric.GetFamilyName() ); - if ( nIndex < static_cast<sal_Int32>(mpFontList->size()) ) { - ImplFontList::iterator it = mpFontList->begin(); - ::std::advance( it, nIndex ); - mpFontList->insert( it, rFontMetric ); - } else { - mpFontList->push_back( rFontMetric ); - } + const FontMetric& rFontMetric = pList->GetFontName(i); + m_xComboBox->append(OUString::number(i), rFontMetric.GetFamilyName()); + mpFontList->push_back(rFontMetric); } - if ( bLoadFromFile ) - LoadMRUEntries (maFontMRUEntriesFile); + if (bLoadFromFile) + LoadMRUEntries(maFontMRUEntriesFile); else - SetMRUEntries( rEntries ); + m_xComboBox->set_mru_entries(rEntries); - ImplCalcUserItemSize(); + m_xComboBox->thaw(); + + if (mbWYSIWYG) + { + mnPreviewProgress = 0; + maUpdateIdle.Start(); + } // restore text if (!aOldText.isEmpty()) - SetText( aOldText ); + set_active_or_entry_text(aOldText); } -void FontNameBox::EnableWYSIWYG( bool bEnable ) +void FontNameBox::EnableWYSIWYG() { - if ( bEnable != mbWYSIWYG ) + if (mbWYSIWYG) + return; + mbWYSIWYG = true; + + static bool bGlobalsInited; + if (!bGlobalsInited) { - mbWYSIWYG = bEnable; - EnableUserDraw( mbWYSIWYG ); - ImplCalcUserItemSize(); + gUserItemSz = Size(m_xComboBox->get_approximate_digit_width() * 52, m_xComboBox->get_text_height()); + gUserItemSz.setHeight(gUserItemSz.Height() * 16); + gUserItemSz.setHeight(gUserItemSz.Height() / 10); + + size_t nMaxDeviceHeight = SAL_MAX_INT16 / 2; // see limitXCreatePixmap + gPreviewsPerDevice = nMaxDeviceHeight / gUserItemSz.Height(); + + bGlobalsInited = true; } + + m_xComboBox->connect_custom_get_size(LINK(this, FontNameBox, CustomGetSizeHdl)); + m_xComboBox->connect_custom_render(LINK(this, FontNameBox, CustomRenderHdl)); + m_xComboBox->set_custom_renderer(); + + mbWYSIWYG = true; } -void FontNameBox::ImplCalcUserItemSize() +IMPL_STATIC_LINK_NOARG(FontNameBox, CustomGetSizeHdl, weld::ComboBox::get_size_args, Size) { - Size aUserItemSz; - if ( mbWYSIWYG && mpFontList ) - { - aUserItemSz = Size(MAXPREVIEWWIDTH, GetTextHeight() ); - aUserItemSz.setHeight( aUserItemSz.Height() * 16 ); - aUserItemSz.setHeight( aUserItemSz.Height() / 10 ); - } - SetUserItemSize( aUserItemSz ); + return gUserItemSz; } namespace @@ -500,192 +521,275 @@ namespace } } -void FontNameBox::UserDraw( const UserDrawEvent& rUDEvt ) +IMPL_LINK_NOARG(FontNameBox, UpdateHdl, Timer*, void) { - assert( mpFontList ); + CachePreview(mnPreviewProgress++, nullptr); + if (mnPreviewProgress < mpFontList->size()) + maUpdateIdle.Start(); +} - FontMetric& rFontMetric = (*mpFontList)[ rUDEvt.GetItemId() ]; - Point aTopLeft = rUDEvt.GetRect().TopLeft(); - long nX = aTopLeft.X(); - long nH = rUDEvt.GetRect().GetHeight(); +static void DrawPreview(const FontMetric& rFontMetric, const Point& rTopLeft, OutputDevice& rDevice, bool bSelected) +{ + rDevice.Push(PushFlags::TEXTCOLOR); - if ( mbWYSIWYG ) - { - nX += IMGOUTERTEXTSPACE; + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + if (bSelected) + rDevice.SetTextColor(rStyleSettings.GetHighlightTextColor()); + else + rDevice.SetTextColor(rStyleSettings.GetDialogTextColor()); - const bool bSymbolFont = isSymbolFont(rFontMetric); - vcl::RenderContext* pRenderContext = rUDEvt.GetRenderContext(); + long nX = rTopLeft.X(); + long nH = gUserItemSz.Height(); - Color aTextColor = pRenderContext->GetTextColor(); - vcl::Font aOldFont(pRenderContext->GetFont()); - Size aSize( aOldFont.GetFontSize() ); - aSize.AdjustHeight(EXTRAFONTSIZE ); - vcl::Font aFont( rFontMetric ); - aFont.SetFontSize( aSize ); - pRenderContext->SetFont(aFont); - pRenderContext->SetTextColor(aTextColor); + nX += IMGOUTERTEXTSPACE; - bool bUsingCorrectFont = true; - tools::Rectangle aTextRect; + const bool bSymbolFont = isSymbolFont(rFontMetric); - // Preview the font name - const OUString& sFontName = rFontMetric.GetFamilyName(); + vcl::Font aOldFont(rDevice.GetFont()); + Size aSize( aOldFont.GetFontSize() ); + aSize.AdjustHeight(EXTRAFONTSIZE ); + vcl::Font aFont( rFontMetric ); + aFont.SetFontSize( aSize ); + rDevice.SetFont(aFont); - //If it shouldn't or can't draw its own name because it doesn't have the glyphs - if (!canRenderNameOfSelectedFont(*pRenderContext)) - bUsingCorrectFont = false; - else - { - //Make sure it fits in the available height, shrinking the font if necessary - bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, *pRenderContext, aTextRect) != 0; - } + bool bUsingCorrectFont = true; + tools::Rectangle aTextRect; - if (!bUsingCorrectFont) - { - pRenderContext->SetFont(aOldFont); - pRenderContext->GetTextBoundRect(aTextRect, sFontName); - } + // Preview the font name + const OUString& sFontName = rFontMetric.GetFamilyName(); - long nTextHeight = aTextRect.GetHeight(); - long nDesiredGap = (nH-nTextHeight)/2; - long nVertAdjust = nDesiredGap - aTextRect.Top(); - Point aPos( nX, aTopLeft.Y() + nVertAdjust ); - pRenderContext->DrawText(aPos, sFontName); - long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW; + //If it shouldn't or can't draw its own name because it doesn't have the glyphs + if (!canRenderNameOfSelectedFont(rDevice)) + bUsingCorrectFont = false; + else + { + //Make sure it fits in the available height, shrinking the font if necessary + bUsingCorrectFont = shrinkFontToFit(sFontName, nH, aFont, rDevice, aTextRect) != 0; + } - if (!bUsingCorrectFont) - pRenderContext->SetFont(aFont); + if (!bUsingCorrectFont) + { + rDevice.SetFont(aOldFont); + rDevice.GetTextBoundRect(aTextRect, sFontName); + } - OUString sSampleText; + long nTextHeight = aTextRect.GetHeight(); + long nDesiredGap = (nH-nTextHeight)/2; + long nVertAdjust = nDesiredGap - aTextRect.Top(); + Point aPos( nX, rTopLeft.Y() + nVertAdjust ); + rDevice.DrawText(aPos, sFontName); + long nTextX = aPos.X() + aTextRect.GetWidth() + GAPTOEXTRAPREVIEW; - if (!bSymbolFont) - { - const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z'; + if (!bUsingCorrectFont) + rDevice.SetFont(aFont); - if (bNameBeginsWithLatinText || !bUsingCorrectFont) - sSampleText = makeShortRepresentativeTextForSelectedFont(*pRenderContext); - } + OUString sSampleText; + + if (!bSymbolFont) + { + const bool bNameBeginsWithLatinText = rFontMetric.GetFamilyName()[0] <= 'z'; - //If we're not a symbol font, but could neither render our own name and - //we can't determine what script it would like to render, then try a - //few well known scripts - if (sSampleText.isEmpty() && !bUsingCorrectFont) + if (bNameBeginsWithLatinText || !bUsingCorrectFont) + sSampleText = makeShortRepresentativeTextForSelectedFont(rDevice); + } + + //If we're not a symbol font, but could neither render our own name and + //we can't determine what script it would like to render, then try a + //few well known scripts + if (sSampleText.isEmpty() && !bUsingCorrectFont) + { + static const UScriptCode aScripts[] = { - static const UScriptCode aScripts[] = - { - USCRIPT_ARABIC, - USCRIPT_HEBREW, - - USCRIPT_BENGALI, - USCRIPT_GURMUKHI, - USCRIPT_GUJARATI, - USCRIPT_ORIYA, - USCRIPT_TAMIL, - USCRIPT_TELUGU, - USCRIPT_KANNADA, - USCRIPT_MALAYALAM, - USCRIPT_SINHALA, - USCRIPT_DEVANAGARI, - - USCRIPT_THAI, - USCRIPT_LAO, - USCRIPT_GEORGIAN, - USCRIPT_TIBETAN, - USCRIPT_SYRIAC, - USCRIPT_MYANMAR, - USCRIPT_ETHIOPIC, - USCRIPT_KHMER, - USCRIPT_MONGOLIAN, - - USCRIPT_KOREAN, - USCRIPT_JAPANESE, - USCRIPT_HAN, - USCRIPT_SIMPLIFIED_HAN, - USCRIPT_TRADITIONAL_HAN, - - USCRIPT_GREEK - }; - - for (const UScriptCode& rScript : aScripts) + USCRIPT_ARABIC, + USCRIPT_HEBREW, + + USCRIPT_BENGALI, + USCRIPT_GURMUKHI, + USCRIPT_GUJARATI, + USCRIPT_ORIYA, + USCRIPT_TAMIL, + USCRIPT_TELUGU, + USCRIPT_KANNADA, + USCRIPT_MALAYALAM, + USCRIPT_SINHALA, + USCRIPT_DEVANAGARI, + + USCRIPT_THAI, + USCRIPT_LAO, + USCRIPT_GEORGIAN, + USCRIPT_TIBETAN, + USCRIPT_SYRIAC, + USCRIPT_MYANMAR, + USCRIPT_ETHIOPIC, + USCRIPT_KHMER, + USCRIPT_MONGOLIAN, + + USCRIPT_KOREAN, + USCRIPT_JAPANESE, + USCRIPT_HAN, + USCRIPT_SIMPLIFIED_HAN, + USCRIPT_TRADITIONAL_HAN, + + USCRIPT_GREEK + }; + + for (const UScriptCode& rScript : aScripts) + { + OUString sText = makeShortRepresentativeTextForScript(rScript); + if (!sText.isEmpty()) { - OUString sText = makeShortRepresentativeTextForScript(rScript); - if (!sText.isEmpty()) + bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(aFont, sText)); + if (bHasSampleTextGlyphs) { - bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText)); - if (bHasSampleTextGlyphs) - { - sSampleText = sText; - break; - } + sSampleText = sText; + break; } } + } - static const UScriptCode aMinimalScripts[] = - { - USCRIPT_HEBREW, //e.g. biblical hebrew - USCRIPT_GREEK - }; + static const UScriptCode aMinimalScripts[] = + { + USCRIPT_HEBREW, //e.g. biblical hebrew + USCRIPT_GREEK + }; - for (const UScriptCode& rMinimalScript : aMinimalScripts) + for (const UScriptCode& rMinimalScript : aMinimalScripts) + { + OUString sText = makeShortMinimalTextForScript(rMinimalScript); + if (!sText.isEmpty()) { - OUString sText = makeShortMinimalTextForScript(rMinimalScript); - if (!sText.isEmpty()) + bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(aFont, sText)); + if (bHasSampleTextGlyphs) { - bool bHasSampleTextGlyphs = (-1 == pRenderContext->HasGlyphs(aFont, sText)); - if (bHasSampleTextGlyphs) - { - sSampleText = sText; - break; - } + sSampleText = sText; + break; } } } + } - //If we're a symbol font, or for some reason the font still couldn't - //render something representative of what it would like to render then - //make up some semi-random text that it *can* display - if (bSymbolFont || (!bUsingCorrectFont && sSampleText.isEmpty())) - sSampleText = makeShortRepresentativeSymbolTextForSelectedFont(*pRenderContext); + //If we're a symbol font, or for some reason the font still couldn't + //render something representative of what it would like to render then + //make up some semi-random text that it *can* display + if (bSymbolFont || (!bUsingCorrectFont && sSampleText.isEmpty())) + sSampleText = makeShortRepresentativeSymbolTextForSelectedFont(rDevice); - if (!sSampleText.isEmpty()) - { - const Size &rItemSize = rUDEvt.GetWindow()->GetOutputSize(); + if (!sSampleText.isEmpty()) + { + const Size &rItemSize = gUserItemSz; - //leave a little border at the edge - long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE; - if (nSpace >= 0) + //leave a little border at the edge + long nSpace = rItemSize.Width() - nTextX - IMGOUTERTEXTSPACE; + if (nSpace >= 0) + { + //Make sure it fits in the available height, and get how wide that would be + long nWidth = shrinkFontToFit(sSampleText, nH, aFont, rDevice, aTextRect); + //Chop letters off until it fits in the available width + while (nWidth > nSpace || nWidth > gUserItemSz.Width()) { - //Make sure it fits in the available height, and get how wide that would be - long nWidth = shrinkFontToFit(sSampleText, nH, aFont, *pRenderContext, aTextRect); - //Chop letters off until it fits in the available width - while (nWidth > nSpace || nWidth > MAXPREVIEWWIDTH) - { - sSampleText = sSampleText.copy(0, sSampleText.getLength()-1); - nWidth = pRenderContext->GetTextBoundRect(aTextRect, sSampleText) ? - aTextRect.GetWidth() : 0; - } + sSampleText = sSampleText.copy(0, sSampleText.getLength()-1); + nWidth = rDevice.GetTextBoundRect(aTextRect, sSampleText) ? + aTextRect.GetWidth() : 0; + } - //center the text on the line - if (!sSampleText.isEmpty() && nWidth) - { - nTextHeight = aTextRect.GetHeight(); - nDesiredGap = (nH-nTextHeight)/2; - nVertAdjust = nDesiredGap - aTextRect.Top(); - aPos = Point(nTextX + nSpace - nWidth, aTopLeft.Y() + nVertAdjust); - pRenderContext->DrawText(aPos, sSampleText); - } + //center the text on the line + if (!sSampleText.isEmpty() && nWidth) + { + nTextHeight = aTextRect.GetHeight(); + nDesiredGap = (nH-nTextHeight)/2; + nVertAdjust = nDesiredGap - aTextRect.Top(); + aPos = Point(nTextX + nSpace - nWidth, rTopLeft.Y() + nVertAdjust); + rDevice.DrawText(aPos, sSampleText); } } + } + + rDevice.SetFont(aOldFont); + rDevice.Pop(); +} + +OutputDevice& FontNameBox::CachePreview(size_t nIndex, Point* pTopLeft) +{ + SolarMutexGuard aGuard; + const FontMetric& rFontMetric = (*mpFontList)[nIndex]; + const OUString& rFontName = rFontMetric.GetFamilyName(); + + size_t nPreviewIndex; + auto xFind = std::find(gRenderedFontNames.begin(), gRenderedFontNames.end(), rFontName); + bool bPreviewAvailable = xFind != gRenderedFontNames.end(); + if (!bPreviewAvailable) + { + nPreviewIndex = gRenderedFontNames.size(); + gRenderedFontNames.push_back(rFontName); + } + else + nPreviewIndex = std::distance(gRenderedFontNames.begin(), xFind); + + size_t nPage = nPreviewIndex / gPreviewsPerDevice; + size_t nIndexInPage = nPreviewIndex - (nPage * gPreviewsPerDevice); + + Point aTopLeft(0, gUserItemSz.Height() * nIndexInPage); + + if (!bPreviewAvailable) + { + if (nPage >= gFontPreviewVirDevs.size()) + { + gFontPreviewVirDevs.emplace_back(m_xComboBox->create_render_virtual_device()); + VirtualDevice& rDevice = *gFontPreviewVirDevs.back(); + rDevice.SetOutputSizePixel(Size(gUserItemSz.Width(), gUserItemSz.Height() * gPreviewsPerDevice)); + if (vcl::Window* pDefaultDevice = dynamic_cast<vcl::Window*>(Application::GetDefaultDevice())) + pDefaultDevice->SetPointFont(rDevice, m_xComboBox->get_font()); + assert(gFontPreviewVirDevs.size() == nPage + 1); + } + + DrawPreview(rFontMetric, aTopLeft, *gFontPreviewVirDevs.back(), false); + } + + if (pTopLeft) + *pTopLeft = aTopLeft; + + return *gFontPreviewVirDevs[nPage]; +} + +IMPL_LINK(FontNameBox, CustomRenderHdl, weld::ComboBox::render_args, aPayload, void) +{ + vcl::RenderContext& rRenderContext = std::get<0>(aPayload); + const ::tools::Rectangle& rRect = std::get<1>(aPayload); + bool bSelected = std::get<2>(aPayload); + const OUString& rId = std::get<3>(aPayload); + + sal_uInt32 nIndex = rId.toUInt32(); + + Point aDestPoint(rRect.TopLeft()); + auto nMargin = (rRect.GetHeight() - gUserItemSz.Height()) / 2; + aDestPoint.AdjustY(nMargin); - pRenderContext->SetFont(aOldFont); - DrawEntry( rUDEvt, false, false); // draw separator + if (bSelected) + { + const FontMetric& rFontMetric = (*mpFontList)[nIndex]; + DrawPreview(rFontMetric, aDestPoint, rRenderContext, true); } else { - DrawEntry( rUDEvt, true, true ); + // use cache of unselected entries + Point aTopLeft; + OutputDevice& rDevice = CachePreview(nIndex, &aTopLeft); + + rRenderContext.DrawOutDev(aDestPoint, gUserItemSz, + aTopLeft, gUserItemSz, + rDevice); } } +void FontNameBox::set_active_or_entry_text(const OUString& rText) +{ + const int nFound = m_xComboBox->find_text(rText); + if (nFound != -1) + m_xComboBox->set_active(nFound); + else + m_xComboBox->set_entry_text(rText); +} + FontStyleBox::FontStyleBox(std::unique_ptr<weld::ComboBox> p) : m_xComboBox(std::move(p)) { @@ -866,9 +970,13 @@ FontSizeBox::FontSizeBox(std::unique_ptr<weld::ComboBox> p) m_xComboBox->connect_changed(LINK(this, FontSizeBox, ModifyHdl)); } -void FontSizeBox::set_entry_text(const OUString& rText) +void FontSizeBox::set_active_or_entry_text(const OUString& rText) { - m_xComboBox->set_entry_text(rText); + const int nFound = m_xComboBox->find_text(rText); + if (nFound != -1) + m_xComboBox->set_active(nFound); + else + m_xComboBox->set_entry_text(rText); } IMPL_LINK(FontSizeBox, ReformatHdl, weld::Widget&, rWidget, void) @@ -1020,7 +1128,7 @@ void FontSizeBox::Fill( const FontMetric* pFontMetric, const FontList* pList ) ++pTempAry; } - m_xComboBox->set_entry_text(aStr); + set_active_or_entry_text(aStr); m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd); m_xComboBox->thaw(); } @@ -1105,7 +1213,7 @@ void FontSizeBox::SetRelative( bool bNewRelative ) Fill( &aFontMetric, pFontList ); } - m_xComboBox->set_entry_text(aStr); + set_active_or_entry_text(aStr); m_xComboBox->select_entry_region(nSelectionStart, nSelectionEnd); } @@ -1157,10 +1265,7 @@ void FontSizeBox::SetValue(int nNewValue, FieldUnit eInUnit) } } OUString aResult = format_number(nTempValue); - const int nFound = m_xComboBox->find_text(aResult); - if (nFound != -1) - m_xComboBox->set_active(nFound); - m_xComboBox->set_entry_text(aResult); + set_active_or_entry_text(aResult); } void FontSizeBox::set_value(int nNewValue) diff --git a/svx/UIConfig_svx.mk b/svx/UIConfig_svx.mk index 567464c49cb5..98ef36a88c6c 100644 --- a/svx/UIConfig_svx.mk +++ b/svx/UIConfig_svx.mk @@ -64,6 +64,7 @@ $(eval $(call gb_UIConfig_add_uifiles,svx,\ svx/uiconfig/ui/fontworkgallerydialog \ svx/uiconfig/ui/fontworkspacingdialog \ svx/uiconfig/ui/fontsizebox \ + svx/uiconfig/ui/fontnamebox \ svx/uiconfig/ui/formdatamenu \ svx/uiconfig/ui/formfielddialog \ svx/uiconfig/ui/formlinkwarndialog \ diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx index 17a956579d88..9dd130a1dcc1 100644 --- a/svx/source/tbxctrls/tbcontrl.cxx +++ b/svx/source/tbxctrls/tbcontrl.cxx @@ -25,6 +25,7 @@ #include <svl/poolitem.hxx> #include <svl/itemset.hxx> #include <vcl/commandinfoprovider.hxx> +#include <vcl/combobox.hxx> #include <vcl/event.hxx> #include <vcl/menubtn.hxx> #include <vcl/toolbox.hxx> @@ -35,6 +36,7 @@ #include <svl/style.hxx> #include <svtools/ctrltool.hxx> #include <svtools/borderhelper.hxx> +#include <sfx2/InterimItemWindow.hxx> #include <sfx2/tplpitem.hxx> #include <sfx2/sfxstatuslistener.hxx> #include <toolkit/helper/vclunohelper.hxx> @@ -176,24 +178,25 @@ private: namespace { -class SvxFontNameBox_Impl : public FontNameBox +class SvxFontNameBox_Impl final : public InterimItemWindow { private: + std::unique_ptr<FontNameBox> m_xWidget; const FontList* pFontList; ::std::unique_ptr<FontList> m_aOwnFontList; vcl::Font aCurFont; - Size aLogicalSize; OUString aCurText; sal_uInt16 nFtCount; bool bRelease; Reference< XDispatchProvider > m_xDispatchProvider; Reference< XFrame > m_xFrame; - bool mbEndPreview; bool mbCheckingUnknownFont; void ReleaseFocus_Impl(); void EnableControls_Impl(); + void Select(bool bNonTravelSelect); + void EndPreview() { Sequence< PropertyValue > aArgs; @@ -201,33 +204,44 @@ private: ".uno:CharEndPreviewFontName", aArgs ); } - DECL_LINK( CheckAndMarkUnknownFont, VclWindowEvent&, void ); + void CheckAndMarkUnknownFont(); void SetOptimalSize(); -protected: - virtual void Select() override; virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + virtual void GetFocus() override; public: - SvxFontNameBox_Impl( vcl::Window* pParent, const Reference< XDispatchProvider >& rDispatchProvider,const Reference< XFrame >& _xFrame - , WinBits nStyle - ); + SvxFontNameBox_Impl(vcl::Window* pParent, const Reference<XDispatchProvider>& rDispatchProvider, const Reference<XFrame>& _xFrame); virtual ~SvxFontNameBox_Impl() override; virtual void dispose() override; void FillList(); void Update( const css::awt::FontDescriptor* pFontDesc ); sal_uInt16 GetListCount() const { return nFtCount; } - void Clear() { FontNameBox::Clear(); nFtCount = 0; } + void Clear() { m_xWidget->clear(); nFtCount = 0; } void Fill( const FontList* pList ) - { FontNameBox::Fill( pList ); - nFtCount = pList->GetFontNameCount(); } - virtual bool PreNotify( NotifyEvent& rNEvt ) override; - virtual bool EventNotify( NotifyEvent& rNEvt ) override; + { + m_xWidget->Fill(pList); + nFtCount = pList->GetFontNameCount(); + } + virtual Reference< css::accessibility::XAccessible > CreateAccessible() override; void SetOwnFontList(::std::unique_ptr<FontList> && _aOwnFontList) { m_aOwnFontList = std::move(_aOwnFontList); } - virtual boost::property_tree::ptree DumpAsPropertyTree() override; + + void Enable() {m_xWidget->set_sensitive(true); InterimItemWindow::Enable();} + void Disable() {m_xWidget->set_sensitive(false); InterimItemWindow::Disable();} + + void set_active_or_entry_text(const OUString& rText); + + void statusChanged_Impl(const css::frame::FeatureStateEvent& rEvent); + + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(ActivateHdl, weld::ComboBox&, bool); + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + DECL_LINK(DumpAsPropertyTreeHdl, boost::property_tree::ptree&, void); }; // SelectHdl needs the Modifiers, get them in MouseButtonUp @@ -1304,22 +1318,34 @@ static bool lcl_GetDocFontList( const FontList** ppFontList, SvxFontNameBox_Impl return bChanged; } -SvxFontNameBox_Impl::SvxFontNameBox_Impl( vcl::Window* pParent, const Reference< XDispatchProvider >& rDispatchProvider,const Reference< XFrame >& _xFrame, WinBits nStyle ) : - - FontNameBox ( pParent, nStyle | WinBits( WB_DROPDOWN | WB_AUTOHSCROLL ) ), - pFontList ( nullptr ), - aLogicalSize ( 60,160 ), - nFtCount ( 0 ), - bRelease ( true ), - m_xDispatchProvider( rDispatchProvider ), - m_xFrame (_xFrame), - mbEndPreview(false), - mbCheckingUnknownFont(false) +SvxFontNameBox_Impl::SvxFontNameBox_Impl(vcl::Window* pParent, const Reference<XDispatchProvider>& rDispatchProvider, + const Reference<XFrame>& _xFrame) + : InterimItemWindow(pParent, "svx/ui/fontnamebox.ui", "FontNameBox") + , m_xWidget(new FontNameBox(m_xBuilder->weld_combo_box("fontnamecombobox"))) + , pFontList(nullptr) + , nFtCount(0) + , bRelease(true) + , m_xDispatchProvider(rDispatchProvider) + , m_xFrame(_xFrame) + , mbCheckingUnknownFont(false) { - SetOptimalSize(); EnableControls_Impl(); - GetSubEdit()->AddEventListener( LINK( this, SvxFontNameBox_Impl, CheckAndMarkUnknownFont )); set_id("fontnamecombobox"); + + m_xWidget->connect_changed(LINK(this, SvxFontNameBox_Impl, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, SvxFontNameBox_Impl, KeyInputHdl)); + m_xWidget->connect_entry_activate(LINK(this, SvxFontNameBox_Impl, ActivateHdl)); + m_xWidget->connect_focus_in(LINK(this, SvxFontNameBox_Impl, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, SvxFontNameBox_Impl, FocusOutHdl)); + m_xWidget->connect_get_property_tree(LINK(this, SvxFontNameBox_Impl, DumpAsPropertyTreeHdl)); + + const Size aLogicalSize(60, 0); + Size aSize(LogicToPixel(aLogicalSize, MapMode(MapUnit::MapAppFont))); + // set width in chars low so the size request will not be overridden + m_xWidget->set_entry_width_chars(1); + m_xWidget->set_size_request(aSize.Width(), -1); + + SetOptimalSize(); } SvxFontNameBox_Impl::~SvxFontNameBox_Impl() @@ -1329,38 +1355,41 @@ SvxFontNameBox_Impl::~SvxFontNameBox_Impl() void SvxFontNameBox_Impl::dispose() { - GetSubEdit()->RemoveEventListener( LINK( this, SvxFontNameBox_Impl, CheckAndMarkUnknownFont )); - FontNameBox::dispose(); + m_xWidget.reset(); + InterimItemWindow::dispose(); } void SvxFontNameBox_Impl::FillList() { + if (!m_xWidget) // e.g. disposed + return; // Save old Selection, set back in the end - Selection aOldSel = GetSelection(); + int nStartPos, nEndPos; + m_xWidget->get_entry_selection_bounds(nStartPos, nEndPos); + // Did Doc-Fontlist change? - lcl_GetDocFontList( &pFontList, this ); - aCurText = GetText(); - SetSelection( aOldSel ); + lcl_GetDocFontList(&pFontList, this); + + aCurText = m_xWidget->get_active_text(); + m_xWidget->select_entry_region(nStartPos, nEndPos); } -IMPL_LINK( SvxFontNameBox_Impl, CheckAndMarkUnknownFont, VclWindowEvent&, event, void ) +void SvxFontNameBox_Impl::CheckAndMarkUnknownFont() { - if( event.GetId() != VclEventId::EditModify ) - return; if (mbCheckingUnknownFont) //tdf#117537 block rentry return; mbCheckingUnknownFont = true; - OUString fontname = GetSubEdit()->GetText(); + OUString fontname = m_xWidget->get_active_text(); lcl_GetDocFontList( &pFontList, this ); // If the font is unknown, show it in italic. - vcl::Font font = GetControlFont(); + vcl::Font font = m_xWidget->get_entry_font(); if( pFontList != nullptr && pFontList->IsAvailable( fontname )) { if( font.GetItalic() != ITALIC_NONE ) { font.SetItalic( ITALIC_NONE ); - SetControlFont( font ); - SetQuickHelpText( SvxResId( RID_SVXSTR_CHARFONTNAME )); + m_xWidget->set_entry_font(font); + m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME)); } } else @@ -1368,8 +1397,8 @@ IMPL_LINK( SvxFontNameBox_Impl, CheckAndMarkUnknownFont, VclWindowEvent&, event, if( font.GetItalic() != ITALIC_NORMAL ) { font.SetItalic( ITALIC_NORMAL ); - SetControlFont( font ); - SetQuickHelpText( SvxResId( RID_SVXSTR_CHARFONTNAME_NOTAVAILABLE )); + m_xWidget->set_entry_font(font); + m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME_NOTAVAILABLE)); } } mbCheckingUnknownFont = false; @@ -1386,72 +1415,61 @@ void SvxFontNameBox_Impl::Update( const css::awt::FontDescriptor* pFontDesc ) aCurFont.SetCharSet ( rtl_TextEncoding( pFontDesc->CharSet ) ); } OUString aCurName = aCurFont.GetFamilyName(); - if ( GetText() != aCurName ) - SetText( aCurName ); + OUString aText = m_xWidget->get_active_text(); + if (aText != aCurName) + set_active_or_entry_text(aCurName); } -bool SvxFontNameBox_Impl::PreNotify( NotifyEvent& rNEvt ) +void SvxFontNameBox_Impl::set_active_or_entry_text(const OUString& rText) { - MouseNotifyEvent nType = rNEvt.GetType(); + m_xWidget->set_active_or_entry_text(rText); + CheckAndMarkUnknownFont(); +} - if ( MouseNotifyEvent::MOUSEBUTTONDOWN == nType || MouseNotifyEvent::GETFOCUS == nType ) - { - EnableControls_Impl(); - FillList(); - } - return FontNameBox::PreNotify( rNEvt ); +IMPL_LINK_NOARG(SvxFontNameBox_Impl, FocusInHdl, weld::Widget&, void) +{ + EnableControls_Impl(); + FillList(); } -bool SvxFontNameBox_Impl::EventNotify( NotifyEvent& rNEvt ) +IMPL_LINK(SvxFontNameBox_Impl, KeyInputHdl, const KeyEvent&, rKEvt, bool) { bool bHandled = false; - mbEndPreview = false; - if ( rNEvt.GetType() == MouseNotifyEvent::KEYUP ) - mbEndPreview = true; - if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT ) - { - sal_uInt16 nCode = rNEvt.GetKeyEvent()->GetKeyCode().GetCode(); + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); - switch ( nCode ) - { - case KEY_RETURN: - case KEY_TAB: - { - if ( KEY_TAB == nCode ) - bRelease = false; - else - bHandled = true; - Select(); - break; - } + switch (nCode) + { + case KEY_TAB: + bRelease = false; + Select(true); + break; - case KEY_ESCAPE: - SetText( aCurText ); - if ( typeid( *GetParent() ) != typeid( sfx2::sidebar::SidebarToolBox ) ) - ReleaseFocus_Impl(); - EndPreview(); - break; - } + case KEY_ESCAPE: + set_active_or_entry_text(aCurText); + if ( typeid( *GetParent() ) != typeid( sfx2::sidebar::SidebarToolBox ) ) + ReleaseFocus_Impl(); + EndPreview(); + bHandled = true; + break; } - else if ( MouseNotifyEvent::LOSEFOCUS == rNEvt.GetType() ) + + return bHandled || ChildKeyInput(rKEvt); +} + +IMPL_LINK_NOARG(SvxFontNameBox_Impl, FocusOutHdl, weld::Widget&, void) +{ + if (!m_xWidget->has_focus()) // a combobox can be comprised of different subwidget so double-check if none of those has focus { - vcl::Window* pFocusWin = Application::GetFocusWindow(); - if ( !HasFocus() && GetSubEdit() != pFocusWin ) - SetText( GetSavedValue() ); + set_active_or_entry_text(m_xWidget->get_saved_value()); // send EndPreview EndPreview(); } - - return bHandled || FontNameBox::EventNotify( rNEvt ); } void SvxFontNameBox_Impl::SetOptimalSize() { - Size aSize(LogicToPixel(aLogicalSize, MapMode(MapUnit::MapAppFont))); - set_width_request(aSize.Width()); - set_height_request(aSize.Height()); - SetSizePixel(aSize); + SetSizePixel(get_preferred_size()); } void SvxFontNameBox_Impl::DataChanged( const DataChangedEvent& rDCEvt ) @@ -1468,8 +1486,6 @@ void SvxFontNameBox_Impl::DataChanged( const DataChangedEvent& rDCEvt ) // the new one before doing anything further. lcl_GetDocFontList( &pFontList, this ); } - - FontNameBox::DataChanged( rDCEvt ); } void SvxFontNameBox_Impl::ReleaseFocus_Impl() @@ -1488,27 +1504,36 @@ void SvxFontNameBox_Impl::EnableControls_Impl() SvtFontOptions aFontOpt; bool bEnable = aFontOpt.IsFontHistoryEnabled(); sal_uInt16 nEntries = bEnable ? MAX_MRU_FONTNAME_ENTRIES : 0; - if ( GetMaxMRUCount() != nEntries ) + if (m_xWidget->get_max_mru_count() != nEntries) { // refill in the next GetFocus-Handler pFontList = nullptr; Clear(); - SetMaxMRUCount( nEntries ); + m_xWidget->set_max_mru_count(nEntries); } - bEnable = aFontOpt.IsFontWYSIWYGEnabled(); - EnableWYSIWYG( bEnable ); + if (aFontOpt.IsFontWYSIWYGEnabled()) + m_xWidget->EnableWYSIWYG(); } -void SvxFontNameBox_Impl::Select() +IMPL_LINK(SvxFontNameBox_Impl, SelectHdl, weld::ComboBox&, rCombo, void) { - FontNameBox::Select(); + Select(rCombo.changed_by_direct_pick()); // only when picked from the list +} +IMPL_LINK_NOARG(SvxFontNameBox_Impl, ActivateHdl, weld::ComboBox&, bool) +{ + Select(true); + return true; +} + +void SvxFontNameBox_Impl::Select(bool bNonTravelSelect) +{ Sequence< PropertyValue > aArgs( 1 ); std::unique_ptr<SvxFontItem> pFontItem; if ( pFontList ) { - FontMetric aFontMetric( pFontList->Get( GetText(), + FontMetric aFontMetric( pFontList->Get(m_xWidget->get_active_text(), aCurFont.GetWeight(), aCurFont.GetItalic() ) ); aCurFont = aFontMetric; @@ -1524,8 +1549,10 @@ void SvxFontNameBox_Impl::Select() pFontItem->QueryValue( a ); aArgs[0].Value = a; } - if ( !IsTravelSelect() ) + + if (bNonTravelSelect) { + CheckAndMarkUnknownFont(); // #i33380# DR 2004-09-03 Moved the following line above the Dispatch() call. // This instance may be deleted in the meantime (i.e. when a dialog is opened // while in Dispatch()), accessing members will crash in this case. @@ -1541,11 +1568,6 @@ void SvxFontNameBox_Impl::Select() } else { - if ( mbEndPreview ) - { - EndPreview(); - return; - } if (pFontItem) { aArgs[0].Name = "CharPreviewFontName"; @@ -1556,35 +1578,39 @@ void SvxFontNameBox_Impl::Select() } } -boost::property_tree::ptree SvxFontNameBox_Impl::DumpAsPropertyTree() +void SvxFontNameBox_Impl::GetFocus() { - boost::property_tree::ptree aTree(FontNameBox::DumpAsPropertyTree()); + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); +} +IMPL_LINK(SvxFontNameBox_Impl, DumpAsPropertyTreeHdl, boost::property_tree::ptree&, rTree, void) +{ boost::property_tree::ptree aEntries; - for (int i = 0; i < GetEntryCount(); ++i) + for (int i = 0, nEntryCount = m_xWidget->get_count(); i < nEntryCount; ++i) { boost::property_tree::ptree aEntry; - aEntry.put("", GetEntry(i)); + aEntry.put("", m_xWidget->get_text(i)); aEntries.push_back(std::make_pair("", aEntry)); } - aTree.add_child("entries", aEntries); + rTree.add_child("entries", aEntries); boost::property_tree::ptree aSelected; - for (int i = 0; i < GetSelectedEntryCount(); ++i) + int nSelectedEntry = m_xWidget->get_active(); + if (nSelectedEntry != -1) { boost::property_tree::ptree aEntry; - aEntry.put("", GetSelectedEntryPos(i)); + aEntry.put("", m_xWidget->get_text(nSelectedEntry)); aSelected.push_back(std::make_pair("", aEntry)); } - aTree.put("selectedCount", GetSelectedEntryCount()); - aTree.add_child("selectedEntries", aSelected); - aTree.put("command", ".uno:CharFontName"); - - return aTree; + rTree.put("selectedCount", nSelectedEntry == -1 ? 0 : 1); + rTree.add_child("selectedEntries", aSelected); + rTree.put("command", ".uno:CharFontName"); } ColorWindow::ColorWindow(const OUString& rCommand, @@ -2846,40 +2872,44 @@ SvxFontNameToolBoxControl::SvxFontNameToolBoxControl() { } -void SvxFontNameToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +void SvxFontNameBox_Impl::statusChanged_Impl( const css::frame::FeatureStateEvent& rEvent ) { - SolarMutexGuard aGuard; - ToolBox* pToolBox = nullptr; - sal_uInt16 nId = 0; - if ( !getToolboxId( nId, &pToolBox ) ) - return; - if ( !rEvent.IsEnabled ) { - m_pBox->Disable(); - m_pBox->Update( nullptr ); + Disable(); + Update( nullptr ); } else { - m_pBox->Enable(); + Enable(); css::awt::FontDescriptor aFontDesc; if ( rEvent.State >>= aFontDesc ) - m_pBox->Update( &aFontDesc ); + Update(&aFontDesc); else - m_pBox->SetText( "" ); - m_pBox->SaveValue(); + set_active_or_entry_text(""); + m_xWidget->save_value(); } +} + +void SvxFontNameToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent ) +{ + SolarMutexGuard aGuard; + ToolBox* pToolBox = nullptr; + sal_uInt16 nId = 0; + if ( !getToolboxId( nId, &pToolBox ) ) + return; + m_pBox->statusChanged_Impl(rEvent); pToolBox->EnableItem( nId, rEvent.IsEnabled ); } css::uno::Reference< css::awt::XWindow > SvxFontNameToolBoxControl::createItemWindow( const css::uno::Reference< css::awt::XWindow >& rParent ) { SolarMutexGuard aGuard; - m_pBox = VclPtr<SvxFontNameBox_Impl>::Create( VCLUnoHelper::GetWindow( rParent ), - Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ), - m_xFrame, 0); + m_pBox = VclPtr<SvxFontNameBox_Impl>::Create(VCLUnoHelper::GetWindow(rParent), + Reference< XDispatchProvider >(m_xFrame->getController(), UNO_QUERY), + m_xFrame); return VCLUnoHelper::GetInterface( m_pBox ); } @@ -3510,7 +3540,7 @@ com_sun_star_comp_svx_CurrencyToolBoxControl_get_implementation( Reference< css::accessibility::XAccessible > SvxFontNameBox_Impl::CreateAccessible() { FillList(); - return FontNameBox::CreateAccessible(); + return InterimItemWindow::CreateAccessible(); } //static diff --git a/svx/source/tbxctrls/tbunocontroller.cxx b/svx/source/tbxctrls/tbunocontroller.cxx index 3e3d9ce2751b..cc638c794a1f 100644 --- a/svx/source/tbxctrls/tbunocontroller.cxx +++ b/svx/source/tbxctrls/tbunocontroller.cxx @@ -180,7 +180,7 @@ SvxFontSizeBox_Base::SvxFontSizeBox_Base(std::unique_ptr<weld::ComboBox> xWidget , m_xWidget(new FontSizeBox(std::move(xWidget))) { m_xWidget->set_value(0); - m_xWidget->set_entry_text(""); + m_xWidget->set_active_or_entry_text(""); m_xWidget->disable_entry_completion(); m_xWidget->connect_changed(LINK(this, SvxFontSizeBox_Base, SelectHdl)); @@ -246,7 +246,7 @@ void SvxFontSizeBox_Base::statusChanged_Impl( long nPoint, bool bErase ) { // delete value in the display m_xWidget->set_value(-1L); - m_xWidget->set_entry_text(""); + m_xWidget->set_active_or_entry_text(""); } m_aCurText = m_xWidget->get_active_text(); } @@ -292,7 +292,7 @@ bool SvxFontSizeBox_Base::DoKeyInput(const KeyEvent& rKEvt) break; case KEY_ESCAPE: - m_xWidget->set_entry_text(m_aCurText); + m_xWidget->set_active_or_entry_text(m_aCurText); if (!m_rCtrl.IsInSidebar()) { ReleaseFocus_Impl(); @@ -312,7 +312,7 @@ bool SvxFontSizeBox_Impl::DoKeyInput(const KeyEvent& rKEvt) IMPL_LINK_NOARG(SvxFontSizeBox_Base, FocusOutHdl, weld::Widget&, void) { if (!m_xWidget->has_focus()) // a combobox can be comprised of different subwidget so double-check if none of those has focus - m_xWidget->set_entry_text(m_aCurText); + m_xWidget->set_active_or_entry_text(m_aCurText); } void SvxFontSizeBox_Impl::SetOptimalSize() diff --git a/svx/uiconfig/ui/fontnamebox.ui b/svx/uiconfig/ui/fontnamebox.ui new file mode 100644 index 000000000000..ca1d3e71f570 --- /dev/null +++ b/svx/uiconfig/ui/fontnamebox.ui @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.2 --> +<interface domain="svx"> + <requires lib="gtk+" version="3.18"/> + <object class="GtkBox" id="FontNameBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkComboBoxText" id="fontnamecombobox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="has_entry">True</property> + <property name="popup_fixed_width">False</property> + <child internal-child="entry"> + <object class="GtkEntry"> + <property name="can_focus">True</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> +</interface> diff --git a/sw/inc/pch/precompiled_swui.hxx b/sw/inc/pch/precompiled_swui.hxx index cc71c3c666e1..22537bd6e566 100644 --- a/sw/inc/pch/precompiled_swui.hxx +++ b/sw/inc/pch/precompiled_swui.hxx @@ -13,7 +13,7 @@ manual changes will be rewritten by the next run of update_pch.sh (which presumably also fixes all possible problems, so it's usually better to use it). - Generated on 2020-03-04 21:22:13 using: + Generated on 2020-04-07 17:26:59 using: ./bin/update_pch sw swui --cutoff=3 --exclude:system --include:module --include:local If after updating build fails, use the following command to locate conflicting headers: @@ -106,10 +106,8 @@ #include <vcl/bitmapex.hxx> #include <vcl/builder.hxx> #include <vcl/builderpage.hxx> -#include <vcl/button.hxx> #include <vcl/cairo.hxx> #include <vcl/checksum.hxx> -#include <vcl/combobox.hxx> #include <vcl/ctrl.hxx> #include <vcl/customweld.hxx> #include <vcl/devicecoordinate.hxx> @@ -119,7 +117,6 @@ #include <vcl/edit.hxx> #include <vcl/errcode.hxx> #include <vcl/event.hxx> -#include <vcl/fixed.hxx> #include <vcl/floatwin.hxx> #include <vcl/fntstyle.hxx> #include <vcl/font.hxx> @@ -131,10 +128,8 @@ #include <vcl/image.hxx> #include <vcl/keycod.hxx> #include <vcl/keycodes.hxx> -#include <vcl/lstbox.hxx> #include <vcl/mapmod.hxx> #include <vcl/menu.hxx> -#include <vcl/menubtn.hxx> #include <vcl/metaactiontypes.hxx> #include <vcl/metric.hxx> #include <vcl/outdev.hxx> @@ -178,6 +173,7 @@ #include <basegfx/tuple/b2dtuple.hxx> #include <basegfx/tuple/b2ituple.hxx> #include <basegfx/tuple/b3dtuple.hxx> +#include <basegfx/utils/common.hxx> #include <basegfx/vector/b2dsize.hxx> #include <basegfx/vector/b2dvector.hxx> #include <basegfx/vector/b2enums.hxx> diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index ed219871af24..7cc4d9ebbc50 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -5824,6 +5824,24 @@ public: ensure_event_listener(); } + void call_signal_custom_render(UserDrawEvent* pEvent) + { + vcl::RenderContext* pRenderContext = pEvent->GetRenderContext(); + auto nPos = pEvent->GetItemId(); + signal_custom_render(*pRenderContext, pEvent->GetRect(), pEvent->IsSelected(), get_id(nPos)); + m_xComboBox->DrawEntry(*pEvent, false, false); // draw separator + } + + Size call_signal_custom_get_size(VirtualDevice& rOutput, const OUString& rId) + { + return signal_custom_get_size(rOutput, rId); + } + + VclPtr<VirtualDevice> create_render_virtual_device() const override + { + return VclPtr<VirtualDevice>::Create(); + } + virtual void HandleEventListener(VclWindowEvent& rEvent) override { if (rEvent.GetId() == VclEventId::DropdownPreOpen @@ -5908,6 +5926,33 @@ public: virtual vcl::Font get_entry_font() override { assert(false); return vcl::Font(); } + virtual void set_custom_renderer() override + { + assert(false && "not implemented"); + } + + virtual int get_max_mru_count() const override + { + assert(false && "not implemented"); + return 0; + } + + virtual void set_max_mru_count(int) override + { + assert(false && "not implemented"); + } + + virtual OUString get_mru_entries() const override + { + assert(false && "not implemented"); + return OUString(); + } + + virtual void set_mru_entries(const OUString&) override + { + assert(false && "not implemented"); + } + virtual ~SalInstanceComboBoxWithoutEdit() override { m_xComboBox->SetSelectHdl(Link<ListBox&, void>()); @@ -5929,6 +5974,7 @@ private: DECL_LINK(ChangeHdl, Edit&, void); DECL_LINK(EntryActivateHdl, Edit&, bool); DECL_LINK(SelectHdl, ::ComboBox&, void); + DECL_LINK(UserDrawHdl, UserDrawEvent*, void); WeldTextFilter m_aTextFilter; bool m_bInSelect; public: @@ -6038,6 +6084,43 @@ public: return pEdit->GetPointFont(*pEdit); } + virtual void set_custom_renderer() override + { + auto nOldEntryHeight = m_xComboBox->GetDropDownEntryHeight(); + auto nDropDownLineCount = m_xComboBox->GetDropDownLineCount(); + + Size aRowSize(signal_custom_get_size(*m_xComboBox, OUString())); + m_xComboBox->EnableUserDraw(true); + m_xComboBox->SetUserItemSize(aRowSize); + m_xComboBox->SetUserDrawHdl(LINK(this, SalInstanceComboBoxWithEdit, UserDrawHdl)); + + // adjust the line count to fit approx the height it would have been before + // using a custom renderer + auto nNewEntryHeight = m_xComboBox->GetDropDownEntryHeight(); + double fRatio = nOldEntryHeight / static_cast<double>(nNewEntryHeight); + m_xComboBox->SetDropDownLineCount(nDropDownLineCount * fRatio); + } + + virtual int get_max_mru_count() const override + { + return m_xComboBox->GetMaxMRUCount(); + } + + virtual void set_max_mru_count(int nCount) override + { + return m_xComboBox->SetMaxMRUCount(nCount); + } + + virtual OUString get_mru_entries() const override + { + return m_xComboBox->GetMRUEntries(); + } + + virtual void set_mru_entries(const OUString& rEntries) override + { + m_xComboBox->SetMRUEntries(rEntries); + } + virtual ~SalInstanceComboBoxWithEdit() override { m_xComboBox->SetTextFilter(nullptr); @@ -6067,6 +6150,11 @@ IMPL_LINK_NOARG(SalInstanceComboBoxWithEdit, EntryActivateHdl, Edit&, bool) return m_aEntryActivateHdl.Call(*this); } +IMPL_LINK(SalInstanceComboBoxWithEdit, UserDrawHdl, UserDrawEvent*, pEvent, void) +{ + call_signal_custom_render(pEvent); +} + class SalInstanceEntryTreeView : public SalInstanceContainer, public virtual weld::EntryTreeView { private: @@ -6142,6 +6230,38 @@ public: virtual bool changed_by_direct_pick() const override { return m_bTreeChange; } + virtual void set_custom_renderer() override + { + assert(false && "not implemented"); + } + + virtual int get_max_mru_count() const override + { + assert(false && "not implemented"); + return 0; + } + + virtual void set_max_mru_count(int) override + { + assert(false && "not implemented"); + } + + virtual OUString get_mru_entries() const override + { + assert(false && "not implemented"); + return OUString(); + } + + virtual void set_mru_entries(const OUString&) override + { + assert(false && "not implemented"); + } + + VclPtr<VirtualDevice> create_render_virtual_device() const override + { + return VclPtr<VirtualDevice>::Create(); + } + virtual ~SalInstanceEntryTreeView() override { Edit& rEntry = m_pEntry->getEntry(); diff --git a/vcl/source/control/combobox.cxx b/vcl/source/control/combobox.cxx index 36f35704c4cf..ef1e3c1cebf5 100644 --- a/vcl/source/control/combobox.cxx +++ b/vcl/source/control/combobox.cxx @@ -1135,6 +1135,11 @@ Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const return aSz; } +long ComboBox::GetDropDownEntryHeight() const +{ + return m_pImpl->m_pImplLB->GetEntryHeight(); +} + void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const { long nCharWidth = GetTextWidth(OUString(u'x')); @@ -1142,7 +1147,7 @@ void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines { Size aOutSz = m_pImpl->m_pImplLB->GetMainWindow()->GetOutputSizePixel(); rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1; - rnLines = static_cast<sal_uInt16>(aOutSz.Height()/m_pImpl->m_pImplLB->GetEntryHeight()); + rnLines = static_cast<sal_uInt16>(aOutSz.Height()/GetDropDownEntryHeight()); } else { @@ -1264,6 +1269,11 @@ void ComboBox::UserDraw( const UserDrawEvent& ) { } +void ComboBox::SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink) +{ + m_pImpl->m_pImplLB->SetUserDrawHdl(rLink); +} + void ComboBox::SetUserItemSize( const Size& rSz ) { m_pImpl->m_pImplLB->GetMainWindow()->SetUserItemSize( rSz ); diff --git a/vcl/source/control/imp_listbox.cxx b/vcl/source/control/imp_listbox.cxx index a2a3a1615275..3fe90de21906 100644 --- a/vcl/source/control/imp_listbox.cxx +++ b/vcl/source/control/imp_listbox.cxx @@ -1714,7 +1714,8 @@ void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 long nY = mpEntryList->GetAddedHeight(nPos, mnTop); tools::Rectangle aRect(Point(0, nY), Size(nWidth, pEntry->getHeightWithMargin())); - if (mpEntryList->IsEntryPosSelected(nPos)) + bool bSelected = mpEntryList->IsEntryPosSelected(nPos); + if (bSelected) { rRenderContext.SetTextColor(!IsEnabled() ? rStyleSettings.GetDisableColor() : rStyleSettings.GetHighlightTextColor()); rRenderContext.SetFillColor(rStyleSettings.GetHighlightColor()); @@ -1738,7 +1739,7 @@ void ImplListBoxWindow::ImplPaint(vcl::RenderContext& rRenderContext, sal_Int32 nPos = GetEntryList()->FindEntry(GetEntryList()->GetEntryText(nPos)); nPos = nPos - GetEntryList()->GetMRUCount(); - UserDrawEvent aUDEvt(this, &rRenderContext, aRect, nPos); + UserDrawEvent aUDEvt(this, &rRenderContext, aRect, nPos, bSelected); maUserDrawHdl.Call( &aUDEvt ); mbInUserDraw = false; } diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index ff07a983813d..4a0f975b2fd1 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -11237,7 +11237,7 @@ public: } }; -void ensure_device(CustomCellRendererSurface *cellsurface, weld::TreeView* pTreeView) +void ensure_device(CustomCellRendererSurface *cellsurface, weld::Widget* pWidget) { if (!cellsurface->device) { @@ -11245,110 +11245,12 @@ void ensure_device(CustomCellRendererSurface *cellsurface, weld::TreeView* pTree cellsurface->device->SetBackground(COL_TRANSPARENT); // expand the point size of the desired font to the equivalent pixel size if (vcl::Window* pDefaultDevice = dynamic_cast<vcl::Window*>(Application::GetDefaultDevice())) - pDefaultDevice->SetPointFont(*cellsurface->device, pTreeView->get_font()); + pDefaultDevice->SetPointFont(*cellsurface->device, pWidget->get_font()); } } } -bool custom_cell_renderer_surface_get_preferred_size(GtkCellRenderer *cell, - GtkOrientation orientation, - gint *minimum_size, - gint *natural_size) -{ - GValue value = G_VALUE_INIT; - g_value_init(&value, G_TYPE_STRING); - g_object_get_property(G_OBJECT(cell), "id", &value); - - const char* pStr = g_value_get_string(&value); - - if (!pStr) - { - // this happens if we're empty - return false; - } - - OUString sId(pStr, strlen(pStr), RTL_TEXTENCODING_UTF8); - - value = G_VALUE_INIT; - g_value_init(&value, G_TYPE_POINTER); - g_object_get_property(G_OBJECT(cell), "instance", &value); - - CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell); - - GtkInstanceTreeView* pTreeView = static_cast<GtkInstanceTreeView*>(g_value_get_pointer(&value)); - - ensure_device(cellsurface, pTreeView); - - Size aSize = pTreeView->call_signal_custom_get_size(*cellsurface->device, sId); - - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (minimum_size) - *minimum_size = aSize.Width(); - - if (natural_size) - *natural_size = aSize.Width(); - } - else - { - if (minimum_size) - *minimum_size = aSize.Height(); - - if (natural_size) - *natural_size = aSize.Height(); - } - - return true; -} - -void custom_cell_renderer_surface_render(GtkCellRenderer* cell, - cairo_t* cr, - GtkWidget* /*widget*/, - const GdkRectangle* /*background_area*/, - const GdkRectangle* cell_area, - GtkCellRendererState flags) -{ - GValue value = G_VALUE_INIT; - g_value_init(&value, G_TYPE_STRING); - g_object_get_property(G_OBJECT(cell), "id", &value); - - const char* pStr = g_value_get_string(&value); - OUString sId(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8); - - value = G_VALUE_INIT; - g_value_init(&value, G_TYPE_POINTER); - g_object_get_property(G_OBJECT(cell), "instance", &value); - - CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell); - - GtkInstanceTreeView* pTreeView = static_cast<GtkInstanceTreeView*>(g_value_get_pointer(&value)); - - ensure_device(cellsurface, pTreeView); - - Size aSize(cell_area->width, cell_area->height); - // false to not bother setting the bg on resize as we'll do that - // ourself via cairo - cellsurface->device->SetOutputSizePixel(aSize, false); - - cairo_surface_t* pSurface = get_underlying_cairo_surface(*cellsurface->device); - - // fill surface as transparent so it can be blended with the potentially - // selected background - cairo_t* tempcr = cairo_create(pSurface); - cairo_set_source_rgba(tempcr, 0, 0, 0, 0); - cairo_set_operator(tempcr, CAIRO_OPERATOR_SOURCE); - cairo_paint(tempcr); - cairo_destroy(tempcr); - cairo_surface_flush(pSurface); - - pTreeView->call_signal_custom_render(*cellsurface->device, tools::Rectangle(Point(0, 0), aSize), flags & GTK_CELL_RENDERER_SELECTED, sId); - cairo_surface_mark_dirty(pSurface); - - cairo_set_source_surface(cr, pSurface, cell_area->x, cell_area->y); - cairo_paint(cr); -} - IMPL_LINK_NOARG(GtkInstanceTreeView, async_signal_changed, void*, void) { m_pChangeEvent = nullptr; @@ -12619,6 +12521,14 @@ GtkBuilder* makeComboBoxBuilder() return gtk_builder_new_from_file(OUStringToOString(aPath, RTL_TEXTENCODING_UTF8).getStr()); } +struct GtkTreeRowReferenceDeleter +{ + void operator()(GtkTreeRowReference* p) const + { + gtk_tree_row_reference_free(p); + } +}; + class GtkInstanceComboBox : public GtkInstanceContainer, public vcl::ISearchableStringList, public virtual weld::ComboBox { private: @@ -12635,7 +12545,7 @@ private: std::unique_ptr<vcl::Font> m_xFont; std::unique_ptr<comphelper::string::NaturalStringSorter> m_xSorter; vcl::QuickSelectionEngine m_aQuickSelectionEngine; - std::vector<int> m_aSeparatorRows; + std::vector<std::unique_ptr<GtkTreeRowReference, GtkTreeRowReferenceDeleter>> m_aSeparatorRows; bool m_bHoverSelection; bool m_bPopupActive; bool m_bAutoComplete; @@ -12658,6 +12568,8 @@ private: guint m_nAutoCompleteIdleId; gint m_nNonCustomLineHeight; gint m_nPrePopupCursorPos; + int m_nMRUCount; + int m_nMaxMRUCount; static gboolean idleAutoComplete(gpointer widget) { @@ -12685,6 +12597,10 @@ private: int nPos = -1; + int nZeroRow = 0; + if (m_nMRUCount) + nZeroRow += (m_nMRUCount + 1); + if (!m_bAutoCompleteCaseSensitive) { // Try match case insensitive from current position @@ -12692,7 +12608,7 @@ private: if (nPos == -1 && nStart != 0) { // Try match case insensitive, but from start - nPos = starts_with(m_pTreeModel, aStartText, 0, 0, false); + nPos = starts_with(m_pTreeModel, aStartText, 0, nZeroRow, false); } } @@ -12703,13 +12619,13 @@ private: if (nPos == -1 && nStart != 0) { // Try match case sensitive, but from start - nPos = starts_with(m_pTreeModel, aStartText, 0, 0, true); + nPos = starts_with(m_pTreeModel, aStartText, 0, nZeroRow, true); } } if (nPos != -1) { - OUString aText = get_text(nPos); + OUString aText = get_text_including_mru(nPos); if (aText != aStartText) set_active_text(aText); select_entry_region(aText.getLength(), aStartText.getLength()); @@ -12769,10 +12685,18 @@ private: pThis->signal_popup_toggled(); } - int get_popup_height() + int get_popup_height(gint& rPopupWidth) { - int nMaxRows = Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount(); - int nRows = std::min(nMaxRows, get_count()); + 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); @@ -12795,6 +12719,9 @@ private: } } + if (bAddScrollWidth) + rPopupWidth += rSettings.GetScrollBarSize(); + return nHeight; } @@ -12817,8 +12744,10 @@ private: // 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) - set_cursor(m_nPrePopupCursorPos); + tree_view_set_cursor(m_nPrePopupCursorPos); // undo show_menu tooltip blocking GtkWidget* pParent = gtk_widget_get_toplevel(m_pToggleButton); @@ -12834,14 +12763,20 @@ private: GtkRequisition size; gtk_widget_get_preferred_size(GTK_WIDGET(m_pMenuWindow), nullptr, &size); - gint nPopupWidth = std::max(size.width, nComboWidth); - gint nPopupHeight = get_popup_height(); + 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); } } @@ -12913,6 +12848,7 @@ private: if (m_aEntryActivateHdl.Call(*this)) g_signal_stop_emission_by_name(m_pEntry, "activate"); } + update_mru(); } OUString get(int pos, int col) const @@ -12939,14 +12875,22 @@ private: } } - int find(const OUString& rStr, int col) const + int find(const OUString& rStr, int col, bool bSearchMRUArea) const { GtkTreeIter iter; if (!gtk_tree_model_get_iter_first(m_pTreeModel, &iter)) return -1; - OString aStr(OUStringToOString(rStr, RTL_TEXTENCODING_UTF8).getStr()); 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; @@ -12961,22 +12905,38 @@ private: return -1; } - bool separator_function(int nIndex) + bool separator_function(GtkTreePath* path) + { + bool bFound = false; + for (auto& a : m_aSeparatorRows) + { + GtkTreePath* seppath = gtk_tree_row_reference_get_path(a.get()); + if (seppath) + { + bFound = gtk_tree_path_compare(path, seppath) == 0; + gtk_tree_path_free(seppath); + } + if (bFound) + break; + } + return bFound; + } + + bool separator_function(int pos) { - return std::find(m_aSeparatorRows.begin(), m_aSeparatorRows.end(), nIndex) != m_aSeparatorRows.end(); + 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); - - gint depth; - gint* indices = gtk_tree_path_get_indices_with_depth(path, &depth); - int nIndex = indices[depth-1]; - + bool bRet = pThis->separator_function(path); gtk_tree_path_free(path); - return pThis->separator_function(nIndex); + return bRet; } // https://gitlab.gnome.org/GNOME/gtk/issues/310 @@ -13031,12 +12991,12 @@ private: sal_uInt16 nKeyMod = aKeyCode.GetModifier(); if (!nKeyMod) { - int nCount = get_count(); - int nActive = get_active() + 1; + 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(nActive); + set_active_including_mru(nActive); bDone = true; } else if (nKeyMod == KEY_MOD2 && !m_bPopupActive) @@ -13051,11 +13011,12 @@ private: sal_uInt16 nKeyMod = aKeyCode.GetModifier(); if (!nKeyMod) { - int nActive = get_active() - 1; - while (nActive >= 0 && separator_function(nActive)) + int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1); + int nActive = get_active_including_mru() - 1; + while (nActive >= nStartBound && separator_function(nActive)) --nActive; - if (nActive >= 0) - set_active(nActive); + if (nActive >= nStartBound) + set_active_including_mru(nActive); bDone = true; } break; @@ -13065,12 +13026,13 @@ private: sal_uInt16 nKeyMod = aKeyCode.GetModifier(); if (!nKeyMod) { - int nCount = get_count(); - int nActive = 0; + 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(nActive); + set_active_including_mru(nActive); bDone = true; } break; @@ -13080,11 +13042,12 @@ private: sal_uInt16 nKeyMod = aKeyCode.GetModifier(); if (!nKeyMod) { - int nActive = get_count() - 1; - while (nActive >= 0 && separator_function(nActive)) + int nActive = get_count_including_mru() - 1; + int nStartBound = m_bPopupActive ? 0 : (m_nMRUCount + 1); + while (nActive >= nStartBound && separator_function(nActive)) --nActive; - if (nActive >= 0) - set_active(nActive); + if (nActive >= nStartBound) + set_active_including_mru(nActive); bDone = true; } break; @@ -13170,10 +13133,10 @@ private: vcl::StringEntryIdentifier typeahead_getEntry(int nPos, OUString& out_entryText) const { - int nEntryCount(get_count()); + int nEntryCount(get_count_including_mru()); if (nPos >= nEntryCount) nPos = 0; - out_entryText = get_text(nPos); + out_entryText = get_text_including_mru(nPos); // vcl::StringEntryIdentifier does not allow for 0 values, but our position is 0-based // => normalize @@ -13186,7 +13149,7 @@ private: return reinterpret_cast<sal_Int64>(entry) - 1; } - void set_cursor(int pos) + void tree_view_set_cursor(int pos) { if (pos == -1) { @@ -13228,15 +13191,15 @@ private: if (m_bPopupActive) return tree_view_get_cursor(); else - return get_active(); + return get_active_including_mru(); } void set_selected_entry(int nSelect) { if (m_bPopupActive) - set_cursor(nSelect); + tree_view_set_cursor(nSelect); else - set_active(nSelect); + set_active_including_mru(nSelect); } virtual vcl::StringEntryIdentifier CurrentEntry(OUString& out_entryText) const override @@ -13262,7 +13225,7 @@ private: } // normalize - int nCount = get_count(); + int nCount = get_count_including_mru(); if (nSelect >= nCount) nSelect = nCount ? nCount-1 : -1; @@ -13355,10 +13318,175 @@ private: if (m_pEntry) gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text(nActive), RTL_TEXTENCODING_UTF8).getStr()); else - set_cursor(nActive); + 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; + } + } + +//TODO set_active(0); + } + + 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) + { + 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(); + } + + int find_text_including_mru(const OUString& rStr, bool bSearchMRU) const + { + return find(rStr, m_nTextCol, bSearchMRU); + } + + int find_id_including_mru(const OUString& 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, const OUString& 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, "", 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, const OUString& rText, const OUString* pId, const OUString* pIconName, VirtualDevice* pImageSurface) + { + disable_notify_events(); + GtkTreeIter iter; + insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, pId, rText, pIconName, pImageSurface); + enable_notify_events(); } public: @@ -13390,6 +13518,8 @@ public: , m_nAutoCompleteIdleId(0) , m_nNonCustomLineHeight(-1) , m_nPrePopupCursorPos(-1) + , m_nMRUCount(0) + , m_nMaxMRUCount(0) { insertParent(GTK_WIDGET(m_pComboBox), GTK_WIDGET(getContainer())); gtk_widget_set_visible(GTK_WIDGET(m_pComboBox), false); @@ -13489,7 +13619,19 @@ public: virtual int get_active() const override { - return tree_view_get_cursor(); + 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 @@ -13543,20 +13685,9 @@ public: virtual void set_active(int pos) override { - disable_notify_events(); - - set_cursor(pos); - - if (m_pEntry) - { - if (pos != -1) - gtk_entry_set_text(GTK_ENTRY(m_pEntry), OUStringToOString(get_text(pos), RTL_TEXTENCODING_UTF8).getStr()); - else - gtk_entry_set_text(GTK_ENTRY(m_pEntry), ""); - } - - m_bChangedByMenu = false; - enable_notify_events(); + if (m_nMRUCount && pos != -1) + pos += (m_nMRUCount + 1); + set_active_including_mru(pos); } virtual OUString get_active_text() const override @@ -13576,17 +13707,23 @@ public: virtual OUString get_text(int pos) const override { - return get(pos, m_nTextCol); + if (m_nMRUCount) + pos += (m_nMRUCount + 1); + return get_text_including_mru(pos); } virtual OUString get_id(int pos) const override { - return get(pos, m_nIdCol); + if (m_nMRUCount) + pos += (m_nMRUCount + 1); + return get_id_including_mru(pos); } virtual void set_id(int pos, const OUString& rId) override { - set(pos, m_nIdCol, rId); + 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 @@ -13605,56 +13742,53 @@ public: virtual void remove(int pos) override { - disable_notify_events(); - GtkTreeIter iter; - gtk_tree_model_iter_nth_child(m_pTreeModel, &iter, nullptr, pos); - gtk_list_store_remove(GTK_LIST_STORE(m_pTreeModel), &iter); - m_aSeparatorRows.erase(std::remove(m_aSeparatorRows.begin(), m_aSeparatorRows.end(), pos), m_aSeparatorRows.end()); - enable_notify_events(); + 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 { - disable_notify_events(); - GtkTreeIter iter; - insert_row(GTK_LIST_STORE(m_pTreeModel), iter, pos, pId, rText, pIconName, pImageSurface); - enable_notify_events(); + if (m_nMRUCount && pos != -1) + pos += (m_nMRUCount + 1); + insert_including_mru(pos, rText, pId, pIconName, pImageSurface); } virtual void insert_separator(int pos, const OUString& rId) override { - disable_notify_events(); - GtkTreeIter iter; pos = pos == -1 ? get_count() : pos; - m_aSeparatorRows.push_back(pos); - 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, "", nullptr, nullptr); - enable_notify_events(); + if (m_nMRUCount) + pos += (m_nMRUCount + 1); + insert_separator_including_mru(pos, rId); } virtual int get_count() const override { - return gtk_tree_model_iter_n_children(m_pTreeModel, nullptr); + int nCount = get_count_including_mru(); + if (m_nMRUCount) + nCount -= (m_nMRUCount + 1); + return nCount; } virtual int find_text(const OUString& rStr) const override { - return find(rStr, m_nTextCol); + int nPos = find_text_including_mru(rStr, false); + if (m_nMRUCount) + nPos -= (m_nMRUCount + 1); + return nPos; } virtual int find_id(const OUString& rId) const override { - return find(rId, m_nIdCol); + int nPos = find_id_including_mru(rId, false); + if (m_nMRUCount) + nPos -= (m_nMRUCount + 1); + return nPos; } virtual void clear() override { - disable_notify_events(); - gtk_list_store_clear(GTK_LIST_STORE(m_pTreeModel)); - m_aSeparatorRows.clear(); - gtk_combo_box_set_row_separator_func(m_pComboBox, nullptr, nullptr, nullptr); - enable_notify_events(); + do_clear(); } virtual void make_sorted() override @@ -13860,8 +13994,88 @@ public: return m_bChangedByMenu; } + virtual void set_custom_renderer() override + { + GList* pColumns = gtk_tree_view_get_columns(m_pTreeView); + // keep the original height around for optimal popup height calculation + m_nNonCustomLineHeight = ::get_height_row(m_pTreeView, pColumns); + GtkTreeViewColumn* pColumn = GTK_TREE_VIEW_COLUMN(pColumns->data); + gtk_cell_layout_clear(GTK_CELL_LAYOUT(pColumn)); + 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); + g_list_free(pColumns); + } + + 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, const OUString& rId) + { + return signal_custom_get_size(rOutput, rId); + } + + VclPtr<VirtualDevice> create_render_virtual_device() const override + { + return create_virtual_device(); + } + + 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); + + if (nMRUCount && !m_nMRUCount) + insert_separator_including_mru(nMRUCount, "separator"); + else if (!nMRUCount && m_nMRUCount) + remove_including_mru(m_nMRUCount); // remove separator + + m_nMRUCount = nMRUCount; + } + virtual ~GtkInstanceComboBox() override { + do_clear(); if (m_nAutoCompleteIdleId) g_source_remove(m_nAutoCompleteIdleId); if (m_pEntry) @@ -13886,6 +14100,108 @@ public: } }; +} + +bool custom_cell_renderer_surface_get_preferred_size(GtkCellRenderer *cell, + GtkOrientation orientation, + gint *minimum_size, + gint *natural_size) +{ + GValue value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(cell), "id", &value); + + const char* pStr = g_value_get_string(&value); + + OUString sId(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8); + + value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_POINTER); + g_object_get_property(G_OBJECT(cell), "instance", &value); + + CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell); + + GtkInstanceWidget* pWidget = static_cast<GtkInstanceWidget*>(g_value_get_pointer(&value)); + + ensure_device(cellsurface, pWidget); + + Size aSize; + if (GtkInstanceTreeView* pTreeView = dynamic_cast<GtkInstanceTreeView*>(pWidget)) + aSize = pTreeView->call_signal_custom_get_size(*cellsurface->device, sId); + else if (GtkInstanceComboBox* pComboBox = dynamic_cast<GtkInstanceComboBox*>(pWidget)) + aSize = pComboBox->call_signal_custom_get_size(*cellsurface->device, sId); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (minimum_size) + *minimum_size = aSize.Width(); + + if (natural_size) + *natural_size = aSize.Width(); + } + else + { + if (minimum_size) + *minimum_size = aSize.Height(); + + if (natural_size) + *natural_size = aSize.Height(); + } + + return true; +} + +void custom_cell_renderer_surface_render(GtkCellRenderer* cell, + cairo_t* cr, + GtkWidget* /*widget*/, + const GdkRectangle* /*background_area*/, + const GdkRectangle* cell_area, + GtkCellRendererState flags) +{ + GValue value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_STRING); + g_object_get_property(G_OBJECT(cell), "id", &value); + + const char* pStr = g_value_get_string(&value); + OUString sId(pStr, pStr ? strlen(pStr) : 0, RTL_TEXTENCODING_UTF8); + + value = G_VALUE_INIT; + g_value_init(&value, G_TYPE_POINTER); + g_object_get_property(G_OBJECT(cell), "instance", &value); + + CustomCellRendererSurface *cellsurface = CUSTOM_CELL_RENDERER_SURFACE(cell); + + GtkInstanceWidget* pWidget = static_cast<GtkInstanceWidget*>(g_value_get_pointer(&value)); + ensure_device(cellsurface, pWidget); + + Size aSize(cell_area->width, cell_area->height); + // false to not bother setting the bg on resize as we'll do that + // ourself via cairo + cellsurface->device->SetOutputSizePixel(aSize, false); + + cairo_surface_t* pSurface = get_underlying_cairo_surface(*cellsurface->device); + + // fill surface as transparent so it can be blended with the potentially + // selected background + cairo_t* tempcr = cairo_create(pSurface); + cairo_set_source_rgba(tempcr, 0, 0, 0, 0); + cairo_set_operator(tempcr, CAIRO_OPERATOR_SOURCE); + cairo_paint(tempcr); + cairo_destroy(tempcr); + cairo_surface_flush(pSurface); + + if (GtkInstanceTreeView* pTreeView = dynamic_cast<GtkInstanceTreeView*>(pWidget)) + pTreeView->call_signal_custom_render(*cellsurface->device, tools::Rectangle(Point(0, 0), aSize), flags & GTK_CELL_RENDERER_SELECTED, sId); + else if (GtkInstanceComboBox* pComboBox = dynamic_cast<GtkInstanceComboBox*>(pWidget)) + pComboBox->call_signal_custom_render(*cellsurface->device, tools::Rectangle(Point(0, 0), aSize), flags & GTK_CELL_RENDERER_SELECTED, sId); + cairo_surface_mark_dirty(pSurface); + + cairo_set_source_surface(cr, pSurface, cell_area->x, cell_area->y); + cairo_paint(cr); +} + +namespace { + class GtkInstanceEntryTreeView : public GtkInstanceContainer, public virtual weld::EntryTreeView { private: @@ -14103,6 +14419,38 @@ public: return m_bTreeChange; } + virtual void set_custom_renderer() override + { + assert(false && "not implemented"); + } + + virtual int get_max_mru_count() const override + { + assert(false && "not implemented"); + return 0; + } + + virtual void set_max_mru_count(int) override + { + assert(false && "not implemented"); + } + + virtual OUString get_mru_entries() const override + { + assert(false && "not implemented"); + return OUString(); + } + + virtual void set_mru_entries(const OUString&) override + { + assert(false && "not implemented"); + } + + VclPtr<VirtualDevice> create_render_virtual_device() const override + { + return create_virtual_device(); + } + virtual ~GtkInstanceEntryTreeView() override { if (m_nAutoCompleteIdleId) |