cui/uiconfig/ui/specialcharacters.ui | 62 - include/sfx2/charwin.hxx | 23 include/svx/charmap.hxx | 2 include/svx/cuicharmap.hxx | 87 + sfx2/inc/charmapcontrol.hxx | 2 sfx2/source/control/charmapcontrol.cxx | 43 svx/source/dialog/charmap.cxx | 6 svx/source/dialog/cuicharmap.cxx | 831 ++++++++++++++----- svx/source/dialog/searchcharmap.cxx | 2 sw/qa/extras/accessibility/dialogs.cxx | 47 - sw/qa/uitest/writer_tests2/formatBulletsNumbering.py | 7 sw/qa/uitest/writer_tests3/specialCharacter.py | 8 uitest/demo_ui/char_dialog.py | 4 13 files changed, 862 insertions(+), 262 deletions(-)
New commits: commit 574892bdef37bb0b72340344827a940d2cadfa98 Author: Parth Raiyani <[email protected]> AuthorDate: Tue Nov 11 20:05:37 2025 +0530 Commit: Szymon Kłos <[email protected]> CommitDate: Wed Nov 12 05:49:52 2025 +0100 Switch to IconView from GtkDrawingArea for special characters dialog - Replaced SvxShowCharSet with weld::IconView in cuicharmap - Updated UI in specialcharacters.ui to include GtkIconView and GtkTreeStore - Added tooltip support - Introduced necessary methods to make it work as expected with iconview conversion - introduced lazy loading to load it in batch to improve performance and reduce time to render special character icons - Added iconview and rendering support for both showchar(normal) and searchchar(search) mode - Updated relevant test cases Change-Id: Ib3d6e0fad93d97368692b83bc0d58f3bf8ad57c4 Signed-off-by: Parth Raiyani <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193345 Reviewed-by: Szymon Kłos <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/cui/uiconfig/ui/specialcharacters.ui b/cui/uiconfig/ui/specialcharacters.ui index a397c2e5762d..50e8084328c1 100644 --- a/cui/uiconfig/ui/specialcharacters.ui +++ b/cui/uiconfig/ui/specialcharacters.ui @@ -2,6 +2,22 @@ <!-- Generated with glade 3.40.0 --> <interface domain="cui"> <requires lib="gtk+" version="3.20"/> + <object class="GtkTreeStore" id="showcharset_model"> + <columns> + <!-- column-name pixbuf --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkTreeStore" id="searchcharset_model"> + <columns> + <!-- column-name pixbuf --> + <column type="GdkPixbuf"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> <object class="GtkDialog" id="SpecialCharactersDialog"> <property name="can-focus">False</property> <property name="border-width">6</property> @@ -828,18 +844,19 @@ <property name="vscrollbar-policy">always</property> <property name="shadow-type">in</property> <child> - <object class="GtkViewport"> + <object class="GtkIconView" id="showcharset"> <property name="visible">True</property> - <property name="can-focus">False</property> - <child> - <object class="GtkDrawingArea" id="showcharset"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - </object> - </child> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">showcharset_model</property> + <property name="pixbuf-column">0</property> + <property name="columns">16</property> + <property name="item-padding">4</property> + <property name="margin">6</property> + <property name="column-spacing">2</property> + <property name="row-spacing">2</property> + <property name="activate-on-single-click">False</property> </object> </child> </object> @@ -859,18 +876,19 @@ <property name="vscrollbar-policy">always</property> <property name="shadow-type">in</property> <child> - <object class="GtkViewport"> + <object class="GtkIconView" id="searchcharset"> <property name="visible">True</property> - <property name="can-focus">False</property> - <child> - <object class="GtkDrawingArea" id="searchcharset"> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - </object> - </child> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">searchcharset_model</property> + <property name="pixbuf-column">0</property> + <property name="columns">16</property> + <property name="item-padding">4</property> + <property name="margin">6</property> + <property name="column-spacing">2</property> + <property name="row-spacing">2</property> + <property name="activate-on-single-click">False</property> </object> </child> </object> diff --git a/include/sfx2/charwin.hxx b/include/sfx2/charwin.hxx index 6bcafdbca9eb..7c6e6ceac116 100644 --- a/include/sfx2/charwin.hxx +++ b/include/sfx2/charwin.hxx @@ -76,6 +76,14 @@ public: class SFX2_DLLPUBLIC SfxCharmapContainer { +public: + struct CharChange { + OUString sChar; + OUString sFont; + bool bRemoved; + }; + +private: std::deque<OUString> m_aRecentCharList; std::deque<OUString> m_aRecentCharFontList; std::deque<OUString> m_aFavCharList; @@ -89,8 +97,8 @@ class SFX2_DLLPUBLIC SfxCharmapContainer std::unique_ptr<weld::Widget> m_xRecentGrid; std::unique_ptr<weld::Widget> m_xFavGrid; - Link<void*, void> m_aUpdateFavHdl; - Link<void*, void> m_aUpdateRecentHdl; + Link<CharChange*, void> m_aUpdateFavHdl; + Link<CharChange*, void> m_aUpdateRecentHdl; DECL_DLLPRIVATE_LINK(RecentClearClickHdl, SvxCharView*, void); DECL_DLLPRIVATE_LINK(FavClearClickHdl, SvxCharView*, void); @@ -101,14 +109,17 @@ public: SfxCharmapContainer(weld::Builder& rBuilder, const VclPtr<VirtualDevice>& rVirDev, bool bLockGridSizes); void init(bool bHasInsert, const Link<SvxCharView*,void> &rMouseClickHdl, - const Link<void*,void> &rUpdateFavHdl, - const Link<void*,void> &rUpdateRecentHdl, + const Link<CharChange*,void> &rUpdateFavHdl, + const Link<CharChange*,void> &rUpdateRecentHdl, const Link<SvxCharView *,void> &rFocusInHdl = Link<SvxCharView *,void>()); - void getFavCharacterList(); + void loadFavCharacterList(); void updateFavCharControl(); - void getRecentCharacterList(); //gets both recent char and recent char font list + std::deque<OUString> getFavCharList() { return m_aFavCharList; } + std::deque<OUString> getFavCharFontList() { return m_aFavCharFontList; } + + void loadRecentCharacterList(); //loads both recent char and recent char font list void updateRecentCharControl(); void updateRecentCharacterList(const OUString& sTitle, const OUString& rFont); diff --git a/include/svx/charmap.hxx b/include/svx/charmap.hxx index b5567a0c8702..1c9f9bf0523b 100644 --- a/include/svx/charmap.hxx +++ b/include/svx/charmap.hxx @@ -80,7 +80,7 @@ public: vcl::Font const & GetFont() const { return maFont; } SVX_DLLPUBLIC FontCharMapRef const & GetFontCharMap(); bool isFavChar(std::u16string_view sTitle, std::u16string_view rFont); - void getFavCharacterList(); //gets both Fav char and Fav char font list + void loadFavCharacterList(); //loads both Fav char and Fav char font list void updateFavCharacterList(const OUString& rChar, const OUString& rFont); virtual svx::SvxShowCharSetItem* ImplGetItem( int _nPos ); diff --git a/include/svx/cuicharmap.hxx b/include/svx/cuicharmap.hxx index 006cdba0a402..42628e4b75b0 100644 --- a/include/svx/cuicharmap.hxx +++ b/include/svx/cuicharmap.hxx @@ -23,22 +23,18 @@ #include <vcl/virdev.hxx> #include <sfx2/basedlgs.hxx> #include <svl/itemset.hxx> -#include <svx/charmap.hxx> -#include <svx/searchcharmap.hxx> #include <svx/ucsubset.hxx> #include <sfx2/charwin.hxx> #include <svx/svxdllapi.h> #include <com/sun/star/frame/XFrame.hpp> +#include <string_view> #include <memory> +#include <map> using namespace ::com::sun::star; -class SubsetMap; -namespace svx -{ -struct SvxShowCharSetItem; -} +struct ImplSVEvent; /// Provides the show characters or texts in a drawing area with special sizes and fonts. class SVX_DLLPUBLIC SvxShowText final : public weld::CustomWidgetController @@ -73,6 +69,40 @@ class SVX_DLLPUBLIC SvxCharacterMap final : public SfxDialogController private: void init(); + // Character bitmap generation and model population + VclPtr<VirtualDevice> generateCharGraphic(sal_UCS4 cChar); + void clearSearchCharModel(); + void populateShowCharModel(); + void populateSearchCharModel(); + + // Character selection and retrieval + void selectCharacter(sal_UCS4 cChar); + void selectSearchSetCharFromSubset(const Subset* pSubset); + static sal_UCS4 getCharacterFromId(std::u16string_view rId); + static OUString getCharacterNameFromId(std::u16string_view sId); + + void modifyFavCharacterList(const OUString& sChar, const OUString& sFont); + void rerenderCharacter(std::u16string_view favChar, std::u16string_view favCharFont); + + // Context menu and clipboard + void createContextMenu(const Point& rPos, bool bSearchMode); + void contextMenuSelect(std::u16string_view rIdent); + void contextMenuHdl(weld::IconView& rIconView, const Point& pPos, bool bSearchMode); + static void copyToClipboard(const OUString& rText); + + // Performance optimization via scheduling(lazy loading) + void scheduleShowSetBackgroundRendering(); + void scheduleSearchSetBackgroundRendering(); + void renderShowSetBatch(sal_Int32 nStartPos, sal_Int32 nCount); + void renderSearchSetBatch(sal_Int32 nStartPos, sal_Int32 nCount); + + sal_Int32 m_nShowSetRenderedCount = 0; + sal_Int32 m_nSearchSetRenderedCount = 0; + static constexpr sal_Int32 RENDER_BATCH_SIZE = 500; + + ImplSVEvent* m_nShowRenderIdleEvent = nullptr; + ImplSVEvent* m_nSearchRenderIdleEvent = nullptr; + ScopedVclPtr<VirtualDevice> m_xVirDev; vcl::Font aFont; std::unique_ptr<const SubsetMap> pSubsetMap; @@ -83,6 +113,14 @@ private: SvxShowText m_aShowChar; + FontCharMapRef mxFontCharMap; + std::map<sal_Int32, sal_UCS4> m_aSearchItemList; + + std::unordered_map<OUString, sal_Int32> m_aShowCharPos; + std::unordered_map<OUString, sal_Int32> m_aSearchCharPos; + + static sal_UCS4 m_cSelectedChar; + std::unique_ptr<weld::Button> m_xOKBtn; std::unique_ptr<weld::Label> m_xFontText; std::unique_ptr<weld::ComboBox> m_xFontLB; @@ -94,10 +132,10 @@ private: std::unique_ptr<weld::Button> m_xFavouritesBtn; std::unique_ptr<weld::Label> m_xCharName; std::unique_ptr<weld::CustomWeld> m_xShowChar; - std::unique_ptr<SvxShowCharSet> m_xShowSet; - std::unique_ptr<weld::CustomWeld> m_xShowSetArea; - std::unique_ptr<SvxSearchCharSet> m_xSearchSet; - std::unique_ptr<weld::CustomWeld> m_xSearchSetArea; + std::unique_ptr<weld::ScrolledWindow> m_xShowSetArea; + std::unique_ptr<weld::ScrolledWindow> m_xSearchSetArea; + std::unique_ptr<weld::IconView> m_xShowSet; + std::unique_ptr<weld::IconView> m_xSearchSet; std::unique_ptr<SfxAllItemSet> m_xOutputSet; @@ -107,18 +145,18 @@ private: hexadecimal = 16 }; - // inserts the character that is currently selected in the given SvxShowCharSet - void insertSelectedCharacter(const SvxShowCharSet* pCharSet); + // inserts the character that is currently selected in the given IconView + void insertSelectedCharacter(weld::IconView& rIconView); DECL_DLLPRIVATE_LINK(FontSelectHdl, weld::ComboBox&, void); DECL_DLLPRIVATE_LINK(SubsetSelectHdl, weld::ComboBox&, void); - DECL_DLLPRIVATE_LINK(CharDoubleClickHdl, SvxShowCharSet*, void); - DECL_DLLPRIVATE_LINK(CharSelectHdl, SvxShowCharSet*, void); - DECL_DLLPRIVATE_LINK(CharHighlightHdl, SvxShowCharSet*, void); - DECL_DLLPRIVATE_LINK(CharPreSelectHdl, SvxShowCharSet*, void); - DECL_DLLPRIVATE_LINK(ReturnKeypressOnCharHdl, SvxShowCharSet*, void); - DECL_DLLPRIVATE_LINK(FavClickHdl, SvxShowCharSet*, void); - DECL_DLLPRIVATE_LINK(SearchCharHighlightHdl, SvxShowCharSet*, void); + DECL_DLLPRIVATE_LINK(CharDoubleClickHdl, weld::IconView&, bool); + DECL_DLLPRIVATE_LINK(CharSelectHdl, weld::IconView&, void); + DECL_DLLPRIVATE_LINK(CharKeyPressHdl, const KeyEvent&, bool); + DECL_DLLPRIVATE_LINK(ShowCharMousePressHdl, const MouseEvent&, bool); + DECL_DLLPRIVATE_LINK(SearchCharMousePressHdl, const MouseEvent&, bool); + DECL_DLLPRIVATE_LINK(ShowCharQueryTooltipHdl, const weld::TreeIter&, OUString); + DECL_DLLPRIVATE_LINK(SearchCharQueryTooltipHdl, const weld::TreeIter&, OUString); DECL_DLLPRIVATE_LINK(DecimalCodeChangeHdl, weld::Entry&, void); DECL_DLLPRIVATE_LINK(HexCodeChangeHdl, weld::Entry&, void); DECL_DLLPRIVATE_LINK(CharClickHdl, SvxCharView*, void); @@ -126,7 +164,12 @@ private: DECL_DLLPRIVATE_LINK(FavSelectHdl, weld::Button&, void); DECL_DLLPRIVATE_LINK(SearchUpdateHdl, weld::Entry&, void); DECL_DLLPRIVATE_LINK(SearchFieldGetFocusHdl, weld::Widget&, void); - DECL_DLLPRIVATE_LINK(UpdateFavHdl, void*, void); + DECL_DLLPRIVATE_LINK(UpdateFavHdl, SfxCharmapContainer::CharChange*, void); + DECL_DLLPRIVATE_LINK(UpdateRecentHdl, SfxCharmapContainer::CharChange*, void); + DECL_DLLPRIVATE_LINK(ShowSetScrollHdl, weld::ScrolledWindow&, void); + DECL_DLLPRIVATE_LINK(ShowRenderIdleHdl, void*, void); + DECL_DLLPRIVATE_LINK(SearchSetScrollHdl, weld::ScrolledWindow&, void); + DECL_DLLPRIVATE_LINK(SearchRenderIdleHdl, void*, void); static void fillAllSubsets(weld::ComboBox& rListBox); void selectCharByCode(Radix radix); @@ -135,6 +178,8 @@ public: SvxCharacterMap(weld::Widget* pParent, const SfxItemSet* pSet, css::uno::Reference<css::frame::XFrame> xFrame); + virtual ~SvxCharacterMap() override; + // for explicit use before AsyncRun void prepForRun(); diff --git a/sfx2/inc/charmapcontrol.hxx b/sfx2/inc/charmapcontrol.hxx index 72f0328da2af..968723b631a3 100644 --- a/sfx2/inc/charmapcontrol.hxx +++ b/sfx2/inc/charmapcontrol.hxx @@ -51,7 +51,7 @@ private: DECL_LINK(CharClickHdl, SvxCharView*, void); DECL_LINK(OpenDlgHdl, weld::Button&, void); DECL_LINK(DlgBtnFocusInHdl, weld::Widget&, void); - DECL_LINK(UpdateRecentHdl, void*, void); + DECL_LINK(UpdateRecentHdl, SfxCharmapContainer::CharChange*, void); }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/control/charmapcontrol.cxx b/sfx2/source/control/charmapcontrol.cxx index 63a8cb33a739..e3502f601260 100644 --- a/sfx2/source/control/charmapcontrol.cxx +++ b/sfx2/source/control/charmapcontrol.cxx @@ -104,16 +104,16 @@ SfxCharmapContainer::SfxCharmapContainer(weld::Builder& rBuilder, const VclPtr<V } void SfxCharmapContainer::init(bool bHasInsert, const Link<SvxCharView*,void> &rMouseClickHdl, - const Link<void*, void>& rUpdateFavHdl, - const Link<void*, void>& rUpdateRecentHdl, + const Link<CharChange*, void>& rUpdateFavHdl, + const Link<CharChange*, void>& rUpdateRecentHdl, const Link<SvxCharView*,void> &rFocusInHdl) { m_aUpdateFavHdl = rUpdateFavHdl; m_aUpdateRecentHdl = rUpdateRecentHdl; - getRecentCharacterList(); + loadRecentCharacterList(); updateRecentCharControl(); - getFavCharacterList(); + loadFavCharacterList(); updateFavCharControl(); for(int i = 0; i < 16; i++) @@ -143,7 +143,7 @@ SfxCharmapCtrl::SfxCharmapCtrl(CharmapPopup* pControl, weld::Widget* pParent) m_xCharInfoLabel->set_size_request(-1, m_xCharInfoLabel->get_text_height() * 2); m_aCharmapContents.init(false, LINK(this, SfxCharmapCtrl, CharClickHdl), - Link<void*,void>(), LINK(this, SfxCharmapCtrl, UpdateRecentHdl), + Link<SfxCharmapContainer::CharChange*,void>(), LINK(this, SfxCharmapCtrl, UpdateRecentHdl), LINK(this, SfxCharmapCtrl, CharFocusInHdl)); m_xDlgBtn->connect_clicked(LINK(this, SfxCharmapCtrl, OpenDlgHdl)); @@ -154,7 +154,7 @@ SfxCharmapCtrl::~SfxCharmapCtrl() { } -void SfxCharmapContainer::getFavCharacterList() +void SfxCharmapContainer::loadFavCharacterList() { m_aFavCharList.clear(); m_aFavCharFontList.clear(); @@ -198,7 +198,7 @@ void SfxCharmapContainer::updateFavCharControl() m_aUpdateFavHdl.Call(nullptr); } -void SfxCharmapContainer::getRecentCharacterList() +void SfxCharmapContainer::loadRecentCharacterList() { m_aRecentCharList.clear(); m_aRecentCharFontList.clear(); @@ -444,11 +444,17 @@ IMPL_LINK(SfxCharmapContainer, RecentClearClickHdl, SvxCharView*, rView, void) officecfg::Office::Common::RecentCharacters::RecentCharacterFontList::set(aRecentCharFontList, batch); batch->commit(); + CharChange change{sTitle, sFont, true}; + m_aUpdateRecentHdl.Call(&change); + updateRecentCharControl(); } IMPL_LINK_NOARG(SfxCharmapContainer, RecentClearAllClickHdl, SvxCharView*, void) { + std::deque<OUString> oldChars = m_aRecentCharList; + std::deque<OUString> oldFonts = m_aRecentCharFontList; + m_aRecentCharList.clear(); m_aRecentCharFontList.clear(); @@ -457,17 +463,33 @@ IMPL_LINK_NOARG(SfxCharmapContainer, RecentClearAllClickHdl, SvxCharView*, void) officecfg::Office::Common::RecentCharacters::RecentCharacterFontList::set({ }, batch); batch->commit(); + for (size_t i = 0; i < oldChars.size(); ++i) + { + CharChange change{oldChars[i], oldFonts[i], true}; + m_aUpdateRecentHdl.Call(&change); + } + updateRecentCharControl(); } IMPL_LINK(SfxCharmapContainer, FavClearClickHdl, SvxCharView*, rView, void) { + OUString sChar = rView->GetText(); + OUString sFont = rView->GetFont().GetFamilyName(); + deleteFavCharacterFromList(rView->GetText(), rView->GetFont().GetFamilyName()); + + CharChange change{sChar, sFont, true}; + m_aUpdateFavHdl.Call(&change); + updateFavCharControl(); } IMPL_LINK_NOARG(SfxCharmapContainer, FavClearAllClickHdl, SvxCharView*, void) { + std::deque<OUString> oldChars = m_aFavCharList; + std::deque<OUString> oldFonts = m_aFavCharFontList; + m_aFavCharList.clear(); m_aFavCharFontList.clear(); @@ -476,6 +498,11 @@ IMPL_LINK_NOARG(SfxCharmapContainer, FavClearAllClickHdl, SvxCharView*, void) officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterFontList::set({ }, batch); batch->commit(); + for (size_t i = 0; i < oldChars.size(); ++i) + { + CharChange change{oldChars[i], oldFonts[i], true}; + m_aUpdateFavHdl.Call(&change); + } updateFavCharControl(); } @@ -489,7 +516,7 @@ bool SfxCharmapContainer::hasRecentChars() const return !m_aRecentCharList.empty(); } -IMPL_LINK_NOARG(SfxCharmapCtrl, UpdateRecentHdl, void*, void) +IMPL_LINK_NOARG(SfxCharmapCtrl, UpdateRecentHdl, SfxCharmapContainer::CharChange*, void) { //checking if the characters are recently used or no m_xRecentLabel->set_label(m_aCharmapContents.hasRecentChars() ? SfxResId(STR_RECENT) : SfxResId(STR_NORECENT)); diff --git a/svx/source/dialog/charmap.cxx b/svx/source/dialog/charmap.cxx index adb2749b11f8..9811f93deb5e 100644 --- a/svx/source/dialog/charmap.cxx +++ b/svx/source/dialog/charmap.cxx @@ -99,7 +99,7 @@ void SvxShowCharSet::init() m_nYGap = 0; mxScrollArea->connect_vadjustment_changed(LINK(this, SvxShowCharSet, VscrollHdl)); - getFavCharacterList(); + loadFavCharacterList(); // other settings depend on selected font => see RecalculateFont bDrag = false; @@ -216,7 +216,7 @@ sal_uInt16 SvxShowCharSet::GetRowPos(sal_uInt16 _nPos) return _nPos / COLUMN_COUNT ; } -void SvxShowCharSet::getFavCharacterList() +void SvxShowCharSet::loadFavCharacterList() { maFavCharList.clear(); maFavCharFontList.clear(); @@ -703,7 +703,7 @@ void SvxShowCharSet::RecalculateFont(vcl::RenderContext& rRenderContext) rRenderContext.SetFont(aFont); rRenderContext.GetFontCharMap(mxFontCharMap); m_aItems.clear(); - getFavCharacterList(); + loadFavCharacterList(); nX = aSize.Width() / COLUMN_COUNT; nY = aSize.Height() / ROW_COUNT; diff --git a/svx/source/dialog/cuicharmap.cxx b/svx/source/dialog/cuicharmap.cxx index 84195ccd5543..9b4eeec9633c 100644 --- a/svx/source/dialog/cuicharmap.cxx +++ b/svx/source/dialog/cuicharmap.cxx @@ -40,6 +40,11 @@ #include <comphelper/processfactory.hxx> #include <comphelper/propertyvalue.hxx> #include <comphelper/dispatchcommand.hxx> +#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp> +#include <vcl/unohelp2.hxx> +#include <vcl/event.hxx> +#include <vcl/keycod.hxx> +#include <comphelper/lok.hxx> #include <svx/dialmgr.hxx> #include <svx/cuicharmap.hxx> @@ -53,6 +58,8 @@ using namespace css; +sal_UCS4 SvxCharacterMap::m_cSelectedChar = ' '; // Initialize with space + SvxCharacterMap::SvxCharacterMap(weld::Widget* pParent, const SfxItemSet* pSet, css::uno::Reference<css::frame::XFrame> xFrame) : SfxDialogController(pParent, u"cui/ui/specialcharacters.ui"_ustr, u"SpecialCharactersDialog"_ustr) @@ -72,10 +79,10 @@ SvxCharacterMap::SvxCharacterMap(weld::Widget* pParent, const SfxItemSet* pSet, , m_xFavouritesBtn(m_xBuilder->weld_button(u"favbtn"_ustr)) , m_xCharName(m_xBuilder->weld_label(u"charname"_ustr)) , m_xShowChar(new weld::CustomWeld(*m_xBuilder, u"showchar"_ustr, m_aShowChar)) - , m_xShowSet(new SvxShowCharSet(m_xBuilder->weld_scrolled_window(u"showscroll"_ustr, true), m_xVirDev)) - , m_xShowSetArea(new weld::CustomWeld(*m_xBuilder, u"showcharset"_ustr, *m_xShowSet)) - , m_xSearchSet(new SvxSearchCharSet(m_xBuilder->weld_scrolled_window(u"searchscroll"_ustr, true), m_xVirDev)) - , m_xSearchSetArea(new weld::CustomWeld(*m_xBuilder, u"searchcharset"_ustr, *m_xSearchSet)) + , m_xShowSetArea(m_xBuilder->weld_scrolled_window(u"showscroll"_ustr)) + , m_xSearchSetArea(m_xBuilder->weld_scrolled_window(u"searchscroll"_ustr)) + , m_xShowSet(m_xBuilder->weld_icon_view(u"showcharset"_ustr)) + , m_xSearchSet(m_xBuilder->weld_icon_view(u"searchcharset"_ustr)) { m_aShowChar.SetCentered(true); m_xFontLB->make_sorted(); @@ -113,22 +120,21 @@ SvxCharacterMap::SvxCharacterMap(weld::Widget* pParent, const SfxItemSet* pSet, } m_xOutputSet.reset(new SfxAllItemSet(pSet ? *pSet->GetPool() : SfxGetpApp()->GetPool())); - m_xShowSet->Show(); - m_xSearchSet->Hide(); + m_xShowSet->show(); + m_xSearchSet->hide(); } void SvxCharacterMap::prepForRun() { - if( SvxShowCharSet::getSelectedChar() == ' ') + if (m_cSelectedChar == 0 || m_cSelectedChar == ' ') { m_xOKBtn->set_sensitive(false); setFavButtonState(u"", u""); } else { - sal_UCS4 cChar = m_xShowSet->GetSelectCharacter(); // using the new UCS4 constructor - OUString aOUStr( &cChar, 1 ); + OUString aOUStr(&m_cSelectedChar, 1); m_aShowChar.SetText(aOUStr); setFavButtonState(aOUStr, m_aShowChar.GetFont().GetFamilyName()); @@ -144,7 +150,7 @@ short SvxCharacterMap::run() void SvxCharacterMap::SetChar( sal_UCS4 c ) { - m_xShowSet->SelectCharacter( c ); + selectCharacter(c); setFavButtonState(OUString(&c, 1), aFont.GetFamilyName()); } @@ -159,14 +165,6 @@ void SvxCharacterMap::DisableFontSelection() m_xFontLB->set_sensitive(false); } -IMPL_LINK_NOARG(SvxCharacterMap, UpdateFavHdl, void*, void) -{ - m_xShowSet->getFavCharacterList(); - m_xSearchSet->getFavCharacterList(); - // tdf#109214 - redraw highlight of the favorite characters - m_xShowSet->Invalidate(); -} - void SvxCharacterMap::init() { aFont = m_xVirDev->GetFont(); @@ -212,34 +210,41 @@ void SvxCharacterMap::init() if (bFound) m_xFontLB->set_active_text(aDefStr); - else if (m_xFontLB->get_count() ) + else if (m_xFontLB->get_count()) m_xFontLB->set_active(0); + FontSelectHdl(*m_xFontLB); if (m_xSubsetLB->get_count()) m_xSubsetLB->set_active(0); - m_xFontLB->connect_changed(LINK( this, SvxCharacterMap, FontSelectHdl)); - m_xSubsetLB->connect_changed(LINK( this, SvxCharacterMap, SubsetSelectHdl)); + m_xFontLB->connect_changed(LINK(this, SvxCharacterMap, FontSelectHdl)); + m_xSubsetLB->connect_changed(LINK(this, SvxCharacterMap, SubsetSelectHdl)); m_xOKBtn->connect_clicked(LINK(this, SvxCharacterMap, InsertClickHdl)); m_xOKBtn->show(); - m_xShowSet->SetDoubleClickHdl( LINK( this, SvxCharacterMap, CharDoubleClickHdl ) ); - m_xShowSet->SetReturnKeyPressHdl(LINK(this, SvxCharacterMap, ReturnKeypressOnCharHdl)); - m_xShowSet->SetSelectHdl( LINK( this, SvxCharacterMap, CharSelectHdl ) ); - m_xShowSet->SetHighlightHdl( LINK( this, SvxCharacterMap, CharHighlightHdl ) ); - m_xShowSet->SetPreSelectHdl( LINK( this, SvxCharacterMap, CharPreSelectHdl ) ); - m_xShowSet->SetFavClickHdl( LINK( this, SvxCharacterMap, FavClickHdl ) ); - - m_xSearchSet->SetDoubleClickHdl( LINK( this, SvxCharacterMap, CharDoubleClickHdl ) ); - m_xSearchSet->SetReturnKeyPressHdl(LINK(this, SvxCharacterMap, ReturnKeypressOnCharHdl)); - m_xSearchSet->SetSelectHdl( LINK( this, SvxCharacterMap, CharSelectHdl ) ); - m_xSearchSet->SetHighlightHdl( LINK( this, SvxCharacterMap, SearchCharHighlightHdl ) ); - m_xSearchSet->SetPreSelectHdl( LINK( this, SvxCharacterMap, CharPreSelectHdl ) ); - m_xSearchSet->SetFavClickHdl( LINK( this, SvxCharacterMap, FavClickHdl ) ); + m_xShowSet->connect_item_activated(LINK(this, SvxCharacterMap, CharDoubleClickHdl)); + m_xShowSet->connect_selection_changed(LINK(this, SvxCharacterMap, CharSelectHdl)); + m_xShowSet->connect_key_press(LINK(this, SvxCharacterMap, CharKeyPressHdl)); + m_xShowSet->connect_query_tooltip(LINK(this, SvxCharacterMap, ShowCharQueryTooltipHdl)); + if(m_xShowSetArea) + m_xShowSetArea->connect_vadjustment_changed(LINK(this, SvxCharacterMap, ShowSetScrollHdl)); + + m_xSearchSet->connect_item_activated(LINK(this, SvxCharacterMap, CharDoubleClickHdl)); + m_xSearchSet->connect_selection_changed(LINK(this, SvxCharacterMap, CharSelectHdl)); + m_xSearchSet->connect_key_press(LINK(this, SvxCharacterMap, CharKeyPressHdl)); + m_xSearchSet->connect_query_tooltip(LINK(this, SvxCharacterMap, SearchCharQueryTooltipHdl)); + if(m_xSearchSetArea) + m_xSearchSetArea->connect_vadjustment_changed(LINK(this, SvxCharacterMap, SearchSetScrollHdl)); + + if (!comphelper::LibreOfficeKit::isActive()) + { + m_xShowSet->connect_mouse_press(LINK(this, SvxCharacterMap, ShowCharMousePressHdl)); + m_xSearchSet->connect_mouse_press(LINK(this, SvxCharacterMap, SearchCharMousePressHdl)); + } - m_xDecimalCodeText->connect_changed( LINK( this, SvxCharacterMap, DecimalCodeChangeHdl ) ); - m_xHexCodeText->connect_changed( LINK( this, SvxCharacterMap, HexCodeChangeHdl ) ); - m_xFavouritesBtn->connect_clicked( LINK(this, SvxCharacterMap, FavSelectHdl)); + m_xDecimalCodeText->connect_changed(LINK(this, SvxCharacterMap, DecimalCodeChangeHdl)); + m_xHexCodeText->connect_changed(LINK(this, SvxCharacterMap, HexCodeChangeHdl)); + m_xFavouritesBtn->connect_clicked(LINK(this, SvxCharacterMap, FavSelectHdl)); // tdf#117038 set the buttons width to its max possible width so it doesn't // make layout change when the label changes @@ -249,15 +254,14 @@ void SvxCharacterMap::init() nMaxWidth = std::max(nMaxWidth, m_xFavouritesBtn->get_preferred_size().Width()); m_xFavouritesBtn->set_size_request(nMaxWidth, -1); - if( SvxShowCharSet::getSelectedChar() == ' ') + if (m_cSelectedChar == 0 || m_cSelectedChar == ' ') { m_xOKBtn->set_sensitive(false); } else { - sal_UCS4 cChar = m_xShowSet->GetSelectCharacter(); // using the new UCS4 constructor - OUString aOUStr( &cChar, 1 ); + OUString aOUStr(&m_cSelectedChar, 1); m_aShowChar.SetText(aOUStr); setFavButtonState(aOUStr, aDefStr); @@ -267,17 +271,18 @@ void SvxCharacterMap::init() m_aCharmapContents.init(m_xFrame.is(), LINK(this, SvxCharacterMap, CharClickHdl), LINK(this, SvxCharacterMap, UpdateFavHdl), - Link<void*, void>()); + LINK(this, SvxCharacterMap, UpdateRecentHdl)); setCharName(90); - m_xSearchText->connect_focus_in(LINK( this, SvxCharacterMap, SearchFieldGetFocusHdl )); + m_xSearchText->connect_focus_in(LINK(this, SvxCharacterMap, SearchFieldGetFocusHdl)); m_xSearchText->connect_changed(LINK(this, SvxCharacterMap, SearchUpdateHdl)); + m_aCharmapContents.loadFavCharacterList(); } void SvxCharacterMap::setFavButtonState(std::u16string_view sTitle, std::u16string_view rFont) { - if(sTitle.empty() || rFont.empty()) + if (sTitle.empty() || rFont.empty()) { m_xFavouritesBtn->set_sensitive(false); return; @@ -298,7 +303,6 @@ void SvxCharacterMap::setFavButtonState(std::u16string_view sTitle, std::u16stri } } - void SvxCharacterMap::SetCharFont( const vcl::Font& rFont ) { // first get the underlying info in order to get font names @@ -337,7 +341,7 @@ void SvxCharacterMap::fillAllSubsets(weld::ComboBox& rListBox) void SvxCharacterMap::insertCharToDoc(const OUString& sGlyph) { - if(sGlyph.isEmpty()) + if (sGlyph.isEmpty()) return; if (m_xFrame.is()) { @@ -360,6 +364,237 @@ void SvxCharacterMap::insertCharToDoc(const OUString& sGlyph) } } +void SvxCharacterMap::populateShowCharModel() +{ + m_xShowSet->clear(); + m_xShowSet->freeze(); + + if (!mxFontCharMap.is()) + return; + + sal_Int32 nId=0; + for (sal_UCS4 cChar = mxFontCharMap->GetFirstChar(); ; cChar = mxFontCharMap->GetNextChar(cChar)) + { + OUString sId = OUString::number(cChar); + m_xShowSet->append(sId, u""_ustr, nullptr); + m_aShowCharPos[sId] = nId++; + + if (cChar == mxFontCharMap->GetLastChar()) + break; + } + + m_xShowSet->thaw(); + + m_nShowSetRenderedCount = 0; + renderShowSetBatch(0, RENDER_BATCH_SIZE); + scheduleShowSetBackgroundRendering(); +} + +void SvxCharacterMap::renderShowSetBatch(sal_Int32 nStartPos, sal_Int32 nCount) +{ + if (!mxFontCharMap.is()) + return; + + m_xShowSet->freeze(); + + sal_Int32 nTotalItems = m_xShowSet->n_children(); + sal_Int32 nEndPos = std::min(nStartPos + nCount, nTotalItems); + + for (sal_Int32 i = nStartPos; i < nEndPos; ++i) + { + OUString sId = m_xShowSet->get_id(i); + if (sId.isEmpty()) + continue; + + sal_UCS4 cChar = getCharacterFromId(sId); + VclPtr<VirtualDevice> pVDev = generateCharGraphic(cChar); + + m_xShowSet->set_image(i, pVDev); + pVDev.disposeAndClear(); + } + + m_xShowSet->thaw(); + m_nShowSetRenderedCount = nEndPos; +} + + +IMPL_LINK_NOARG(SvxCharacterMap, ShowSetScrollHdl, weld::ScrolledWindow&, void) +{ + scheduleShowSetBackgroundRendering(); +} + +void SvxCharacterMap::scheduleShowSetBackgroundRendering() +{ + // Only schedule if there are items left to render + if (m_nShowRenderIdleEvent || m_nShowSetRenderedCount >= m_xShowSet->n_children()) + return; + + m_nShowRenderIdleEvent = Application::PostUserEvent( + LINK(this, SvxCharacterMap, ShowRenderIdleHdl)); +} + +IMPL_LINK_NOARG(SvxCharacterMap, ShowRenderIdleHdl, void*, void) +{ + m_nShowRenderIdleEvent = nullptr; + + if (m_nShowSetRenderedCount >= m_xShowSet->n_children()) + return; + + renderShowSetBatch(m_nShowSetRenderedCount, RENDER_BATCH_SIZE); + + // Schedule next batch if more items remain + if (m_nShowSetRenderedCount < m_xShowSet->n_children()) + { + m_nShowRenderIdleEvent = Application::PostUserEvent( + LINK(this, SvxCharacterMap, ShowRenderIdleHdl)); + } +} + +void SvxCharacterMap::populateSearchCharModel() +{ + m_xSearchSet->clear(); + m_xSearchSet->freeze(); + + sal_Int32 nId = 0; + for (const auto& [nIndex, cChar] : m_aSearchItemList) + { + OUString sId = OUString::number(cChar); + m_xSearchSet->append(sId, u""_ustr, nullptr); + m_aSearchCharPos[sId] = nId++; + } + + m_xSearchSet->thaw(); + + m_nSearchSetRenderedCount = 0; + renderSearchSetBatch(0, RENDER_BATCH_SIZE); + scheduleSearchSetBackgroundRendering(); +} + +void SvxCharacterMap::renderSearchSetBatch(sal_Int32 nStartPos, sal_Int32 nCount) +{ + m_xSearchSet->freeze(); + + sal_Int32 nTotalItems = m_xSearchSet->n_children(); + sal_Int32 nEndPos = std::min(nStartPos + nCount, nTotalItems); + + for (sal_Int32 i = nStartPos; i < nEndPos; ++i) + { + OUString sId = m_xSearchSet->get_id(i); + if (sId.isEmpty()) + continue; + + sal_UCS4 cChar = getCharacterFromId(sId); + VclPtr<VirtualDevice> pVDev = generateCharGraphic(cChar); + + m_xSearchSet->set_image(i, pVDev); + pVDev.disposeAndClear(); + } + + m_xSearchSet->thaw(); + m_nSearchSetRenderedCount = nEndPos; +} + +IMPL_LINK_NOARG(SvxCharacterMap, SearchSetScrollHdl, weld::ScrolledWindow&, void) +{ + scheduleSearchSetBackgroundRendering(); +} + +void SvxCharacterMap::scheduleSearchSetBackgroundRendering() +{ + // Only schedule if there are items left to render + if (m_nSearchRenderIdleEvent || m_nSearchSetRenderedCount >= m_xSearchSet->n_children()) + return; + + m_nSearchRenderIdleEvent = Application::PostUserEvent( + LINK(this, SvxCharacterMap, SearchRenderIdleHdl)); +} + +IMPL_LINK_NOARG(SvxCharacterMap, SearchRenderIdleHdl, void*, void) +{ + m_nSearchRenderIdleEvent = nullptr; + + if (m_nSearchSetRenderedCount >= m_xSearchSet->n_children()) + return; + + renderSearchSetBatch(m_nSearchSetRenderedCount, RENDER_BATCH_SIZE); + + // Schedule next batch if more items remain + if (m_nSearchSetRenderedCount < m_xSearchSet->n_children()) + { + m_nSearchRenderIdleEvent = Application::PostUserEvent( + LINK(this, SvxCharacterMap, SearchRenderIdleHdl)); + } +} + +VclPtr<VirtualDevice> SvxCharacterMap::generateCharGraphic(sal_UCS4 cChar) +{ + VclPtr<VirtualDevice> pVirDev = VclPtr<VirtualDevice>::Create(); + pVirDev->SetOutputSizePixel(Size(48, 48)); + + vcl::Font aRenderFont = aFont; + Size aFontSize = aRenderFont.GetFontSize(); + aFontSize.setHeight(24); + aRenderFont.SetFontSize(aFontSize); + pVirDev->SetFont(aRenderFont); + pVirDev->SetBackground(Application::GetSettings().GetStyleSettings().GetWindowColor()); + pVirDev->Erase(); + + OUString sChar(&cChar, 1); + if (m_aCharmapContents.isFavChar(sChar, aFont.GetFamilyName())) + { + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aHighlightColor(rStyleSettings.GetHighlightColor()); + pVirDev->SetLineColor(aHighlightColor); + pVirDev->DrawRect(tools::Rectangle(Point(0, 0), Size(48, 48))); + } + Size aTextSize(pVirDev->GetTextWidth(sChar), pVirDev->GetTextHeight()); + Point aPoint((48 - aTextSize.Width()) / 2, (48 - aTextSize.Height()) / 2); + pVirDev->DrawText(aPoint, sChar); + + return pVirDev; +} + +void SvxCharacterMap::selectCharacter(sal_UCS4 cChar) +{ + if (!mxFontCharMap.is()) + { + m_xVirDev->SetFont(aFont); + m_xVirDev->GetFontCharMap(mxFontCharMap); + } + + if (!mxFontCharMap->HasChar(cChar)) + { + cChar = mxFontCharMap->GetNextChar((cChar > 0) ? cChar - 1 : cChar); + } + + OUString sId = OUString::number(cChar); + const auto& posMap = isSearchMode ? m_aSearchCharPos : m_aShowCharPos; + auto it = posMap.find(sId); + + if (it == posMap.end()) + return; // Character not in current view + + sal_Int32 pos = it->second; + + if (!isSearchMode && pos < m_xShowSet->n_children()) + { + m_xShowSet->select(pos); + m_cSelectedChar = cChar; + } + else if (isSearchMode && pos < m_xSearchSet->n_children()) + { + m_xSearchSet->select(pos); + m_cSelectedChar = cChar; + } +} + +sal_UCS4 SvxCharacterMap::getCharacterFromId(std::u16string_view rId) +{ + OUString sId(rId.data(), rId.size()); + + return static_cast<sal_UCS4>(sId.toUInt32()); +} + IMPL_LINK_NOARG(SvxCharacterMap, FontSelectHdl, weld::ComboBox&, void) { const sal_uInt32 nFont = m_xFontLB->get_active_id().toUInt32(); @@ -370,10 +605,9 @@ IMPL_LINK_NOARG(SvxCharacterMap, FontSelectHdl, weld::ComboBox&, void) aFont.SetPitch( PITCH_DONTKNOW ); aFont.SetFamily( FAMILY_DONTKNOW ); - // notify children using this font - m_xShowSet->SetFont( aFont ); - m_xSearchSet->SetFont( aFont ); m_aShowChar.SetFont( aFont ); + m_xVirDev->SetFont(aFont); + m_xVirDev->GetFontCharMap(mxFontCharMap); // setup unicode subset listbar with font specific subsets, // hide unicode subset listbar for symbol fonts @@ -384,8 +618,7 @@ IMPL_LINK_NOARG(SvxCharacterMap, FontSelectHdl, weld::ComboBox&, void) bool bNeedSubset = (aFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL); if (bNeedSubset) { - FontCharMapRef xFontCharMap = m_xShowSet->GetFontCharMap(); - pSubsetMap.reset(new SubsetMap( xFontCharMap )); + pSubsetMap.reset(new SubsetMap(mxFontCharMap)); // update subset listbox for new font's unicode subsets for (auto const& subset : pSubsetMap->GetSubsetMap()) @@ -406,11 +639,23 @@ IMPL_LINK_NOARG(SvxCharacterMap, FontSelectHdl, weld::ComboBox&, void) // tdf#137294 do this after modifying m_xSubsetLB sensitivity to // restore insensitive for the search case SearchUpdateHdl(*m_xSearchText); - SearchCharHighlightHdl(m_xSearchSet.get()); + CharSelectHdl(*m_xSearchSet); + } + else + { + populateShowCharModel(); } - // tdf#118304 reselect current glyph to see if it's still there in new font - selectCharByCode(Radix::hexadecimal); + if (m_cSelectedChar != 0 && m_cSelectedChar != ' ') + { + // Restore the previous selection + selectCharacter(m_cSelectedChar); + } + else + { + sal_UCS4 cChar = mxFontCharMap->GetFirstChar(); // Select the first char + selectCharacter(cChar); + } } void SvxCharacterMap::toggleSearchView(bool state) @@ -420,15 +665,15 @@ void SvxCharacterMap::toggleSearchView(bool state) m_xDecimalCodeText->set_editable(!state); m_xSubsetLB->set_sensitive(!state); - if(state) + if (state) { - m_xSearchSet->Show(); - m_xShowSet->Hide(); + m_xSearchSet->show(); + m_xShowSet->hide(); } else { - m_xSearchSet->Hide(); - m_xShowSet->Show(); + m_xSearchSet->hide(); + m_xShowSet->show(); } } @@ -449,28 +694,71 @@ IMPL_LINK_NOARG(SvxCharacterMap, SubsetSelectHdl, weld::ComboBox&, void) const sal_Int32 nPos = m_xSubsetLB->get_active(); const Subset* pSubset = weld::fromId<const Subset*>(m_xSubsetLB->get_active_id()); - if( pSubset && !isSearchMode) + if (pSubset && !isSearchMode) { sal_UCS4 cFirst = pSubset->GetRangeMin(); - m_xShowSet->SelectCharacter( cFirst ); + selectCharacter(cFirst); setFavButtonState(OUString(&cFirst, 1), aFont.GetFamilyName()); m_xSubsetLB->set_active(nPos); } - else if( pSubset && isSearchMode) + else if (pSubset && isSearchMode) { - m_xSearchSet->SelectCharacter( pSubset ); + selectSearchSetCharFromSubset(pSubset); const Subset* curSubset = nullptr; - if( pSubsetMap ) - curSubset = pSubsetMap->GetSubsetByUnicode( m_xSearchSet->GetSelectCharacter() ); - if( curSubset ) + if ( pSubsetMap ) + curSubset = pSubsetMap->GetSubsetByUnicode(m_cSelectedChar); + if ( curSubset ) m_xSubsetLB->set_active_text(curSubset->GetName()); else m_xSubsetLB->set_active(-1); - sal_UCS4 sChar = m_xSearchSet->GetSelectCharacter(); - setFavButtonState(OUString(&sChar, 1), aFont.GetFamilyName()); + setFavButtonState(OUString(&m_cSelectedChar, 1), aFont.GetFamilyName()); + } +} + +void SvxCharacterMap::selectSearchSetCharFromSubset(const Subset* pSubset) +{ + if (!mxFontCharMap.is()) + { + m_xVirDev->SetFont(aFont); + m_xVirDev->GetFontCharMap(mxFontCharMap); + } + + sal_UCS4 cChar = pSubset->GetRangeMin(); + sal_Int32 foundPos = -1; + + // Find first character from subset that exists in search results + while (cChar <= pSubset->GetRangeMax() && foundPos == -1) + { + auto it = std::find_if (m_aSearchItemList.begin(), m_aSearchItemList.end(), + [cChar](const auto& pair) { + return pair.second == cChar; + }); + + if (it != m_aSearchItemList.end()) + { + OUString sId = OUString::number(cChar); + auto posIt = m_aSearchCharPos.find(sId); + if (posIt != m_aSearchCharPos.end()) + { + foundPos = posIt->second; + break; + } + } + cChar++; + } + + if (foundPos >= 0 && foundPos < m_xSearchSet->n_children()) + { + m_xSearchSet->select(foundPos); + m_cSelectedChar = cChar; + } + else if (m_xSearchSet->n_children() > 0 && !m_aSearchItemList.empty()) + { + m_xSearchSet->select(0); + m_cSelectedChar = m_aSearchItemList.begin()->second; } } @@ -479,11 +767,18 @@ IMPL_LINK_NOARG(SvxCharacterMap, SearchFieldGetFocusHdl, weld::Widget&, void) m_xOKBtn->set_sensitive(false); } +void SvxCharacterMap::clearSearchCharModel() +{ + m_xSearchSet->clear(); + m_aSearchItemList.clear(); +} + IMPL_LINK_NOARG(SvxCharacterMap, SearchUpdateHdl, weld::Entry&, void) { if (!m_xSearchText->get_text().isEmpty()) { - m_xSearchSet->ClearPreviousData(); + clearSearchCharModel(); + OUString aKeyword = m_xSearchText->get_text().trim().toAsciiLowerCase(); OUString hex_code; if (std::u16string_view rest; aKeyword.startsWith("u+", &rest)) @@ -492,20 +787,20 @@ IMPL_LINK_NOARG(SvxCharacterMap, SearchUpdateHdl, weld::Entry&, void) toggleSearchView(true); - FontCharMapRef xFontCharMap = m_xSearchSet->GetFontCharMap(); - - for (sal_UCS4 ucs4 = xFontCharMap->GetFirstChar();; ucs4 = xFontCharMap->GetNextChar(ucs4)) + sal_Int32 nIndex = 0; + for (sal_UCS4 ucs4 = mxFontCharMap->GetFirstChar(); ; ucs4 = mxFontCharMap->GetNextChar(ucs4)) { bool bAdded = false; UErrorCode errorCode = U_ZERO_ERROR; char buffer[100]; u_charName(ucs4, U_UNICODE_CHAR_NAME, buffer, sizeof(buffer), &errorCode); + if (U_SUCCESS(errorCode)) { OUString sName = OUString::createFromAscii(buffer); if (!sName.isEmpty() && sName.toAsciiLowerCase().indexOf(aKeyword) >= 0) { - m_xSearchSet->AppendCharToList(ucs4); + m_aSearchItemList[nIndex++] = ucs4; bAdded = true; } } @@ -513,13 +808,13 @@ IMPL_LINK_NOARG(SvxCharacterMap, SearchUpdateHdl, weld::Entry&, void) { OUString actual_number = OUString::number(ucs4, 16); if (actual_number.startsWith(hex_code)) - m_xSearchSet->AppendCharToList(ucs4); + m_aSearchItemList[nIndex++] = ucs4; } - if (ucs4 == xFontCharMap->GetLastChar()) + if (ucs4 == mxFontCharMap->GetLastChar()) break; } - m_xSearchSet->UpdateScrollRange(); + populateSearchCharModel(); } else { @@ -527,18 +822,15 @@ IMPL_LINK_NOARG(SvxCharacterMap, SearchUpdateHdl, weld::Entry&, void) } } - IMPL_LINK(SvxCharacterMap, CharClickHdl, SvxCharView*, rView, void) { rView->GrabFocus(); SetCharFont(rView->GetFont()); - m_aShowChar.SetText( rView->GetText() ); + m_aShowChar.SetText(rView->GetText()); m_aShowChar.SetFont(rView->GetFont()); m_aShowChar.Invalidate(); - setFavButtonState(rView->GetText(), rView->GetFont().GetFamilyName());//check state - // Get the hexadecimal code OUString charValue = rView->GetText(); sal_UCS4 cChar = charValue.iterateCodePoints(&o3tl::temporary(sal_Int32(1)), -1); @@ -547,6 +839,27 @@ IMPL_LINK(SvxCharacterMap, CharClickHdl, SvxCharView*, rView, void) // Get the decimal code OUString aDecimalText = OUString::number(cChar); + auto& posMap = isSearchMode ? m_aSearchCharPos : m_aShowCharPos; + auto it = posMap.find(aDecimalText); + + if (it != posMap.end()) + { + sal_Int32 pos = it->second; + + if (!isSearchMode) + { + m_xShowSet->select(pos); + m_cSelectedChar = cChar; + } + else + { + m_xSearchSet->select(pos); + m_cSelectedChar = cChar; + } + } + + setFavButtonState(rView->GetText(), rView->GetFont().GetFamilyName());//check state + m_xHexCodeText->set_text(aHexText); m_xDecimalCodeText->set_text(aDecimalText); setCharName(cChar); @@ -555,30 +868,203 @@ IMPL_LINK(SvxCharacterMap, CharClickHdl, SvxCharView*, rView, void) m_xOKBtn->set_sensitive(true); } -void SvxCharacterMap::insertSelectedCharacter(const SvxShowCharSet* pCharSet) +void SvxCharacterMap::insertSelectedCharacter(weld::IconView& rIconView) { - assert(pCharSet); - sal_UCS4 cChar = pCharSet->GetSelectCharacter(); - // using the new UCS4 constructor - OUString aOUStr( &cChar, 1 ); - setFavButtonState(aOUStr, aFont.GetFamilyName()); - insertCharToDoc(aOUStr); + auto aSelectedId = rIconView.get_selected_id(); + if (!aSelectedId.isEmpty()) { + sal_UCS4 cChar = getCharacterFromId(aSelectedId); + m_cSelectedChar = cChar; + OUString aOUStr( &cChar, 1 ); + setFavButtonState(aOUStr, aFont.GetFamilyName()); + insertCharToDoc(aOUStr); + } } -IMPL_LINK(SvxCharacterMap, CharDoubleClickHdl, SvxShowCharSet*, pCharSet, void) +IMPL_LINK(SvxCharacterMap, CharDoubleClickHdl, weld::IconView&, rIconView, bool) { - insertSelectedCharacter(pCharSet); + insertSelectedCharacter(rIconView); + return true; } -IMPL_LINK_NOARG(SvxCharacterMap, CharSelectHdl, SvxShowCharSet*, void) +IMPL_LINK(SvxCharacterMap, CharKeyPressHdl, const KeyEvent&, rKEvt, bool) { - m_xOKBtn->set_sensitive(true); + const vcl::KeyCode& rKey = rKEvt.GetKeyCode(); + + if (rKey.GetCode() == KEY_RETURN && !rKey.GetModifier()) + { + weld::IconView* pIconView = isSearchMode ? m_xSearchSet.get() : m_xShowSet.get(); + auto aSelectedId = pIconView->get_selected_id(); + insertSelectedCharacter(*pIconView); + if (!aSelectedId.isEmpty()) + { + m_xDialog->response(RET_OK); + return true; + } + } + + return false; } -IMPL_LINK(SvxCharacterMap, ReturnKeypressOnCharHdl, SvxShowCharSet*, pCharSet, void) +IMPL_LINK(SvxCharacterMap, CharSelectHdl, weld::IconView&, rIconView, void) { - insertSelectedCharacter(pCharSet); - m_xDialog->response(RET_OK); + auto aSelectedId = rIconView.get_selected_id(); + if (!aSelectedId.isEmpty()) + { + sal_UCS4 cChar = getCharacterFromId(aSelectedId); + m_cSelectedChar = cChar; + + OUString aText(&cChar, 1); + m_aShowChar.SetText(aText); + m_aShowChar.SetFont(aFont); + m_aShowChar.Invalidate(); + + OUString aHexText = OUString::number(cChar, 16).toAsciiUpperCase(); + OUString aDecimalText = OUString::number(cChar); + + if (!m_xHexCodeText->get_text().equalsIgnoreAsciiCase(aHexText)) + m_xHexCodeText->set_text(aHexText); + if (m_xDecimalCodeText->get_text() != aDecimalText) + m_xDecimalCodeText->set_text(aDecimalText); + + setCharName(cChar); + + const Subset* pSubset = nullptr; + if (pSubsetMap) + pSubset = pSubsetMap->GetSubsetByUnicode(cChar); + if (pSubset) + m_xSubsetLB->set_active_text(pSubset->GetName()); + else + m_xSubsetLB->set_active(-1); + + setFavButtonState(aText, aFont.GetFamilyName()); + m_xOKBtn->set_sensitive(true); + } +} + +IMPL_LINK(SvxCharacterMap, ShowCharMousePressHdl, const MouseEvent&, rMEvt, bool) +{ + if (!rMEvt.IsRight()) + return false; + + const Point& pPos = rMEvt.GetPosPixel(); + contextMenuHdl(*m_xShowSet, pPos, false); + return true; +} + +IMPL_LINK(SvxCharacterMap, SearchCharMousePressHdl, const MouseEvent&, rMEvt, bool) +{ + if (!rMEvt.IsRight()) + return false; + + const Point& pPos = rMEvt.GetPosPixel(); + contextMenuHdl(*m_xSearchSet, pPos, true); + return true; +} + +void SvxCharacterMap::contextMenuHdl(weld::IconView& rIconView, const Point& pPos, bool bSearchMode) +{ + for (int i = 0; i < rIconView.n_children(); i++) + { + const ::tools::Rectangle aRect = rIconView.get_rect(i); + if (aRect.Contains(pPos)) + { + rIconView.select(i); + OUString sId = rIconView.get_selected_id(); + if (!sId.isEmpty()) + m_cSelectedChar = getCharacterFromId(sId); + createContextMenu(pPos, bSearchMode); + break; + } + } +} + +void SvxCharacterMap::createContextMenu(const Point& rPos, bool bSearchMode) +{ + ::tools::Rectangle aRect(rPos, Size(1, 1)); + + std::unique_ptr<weld::Builder> xBuilder( + Application::CreateBuilder(bSearchMode ? m_xSearchSet.get() : m_xShowSet.get(), + u"svx/ui/charsetmenu.ui"_ustr)); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu(u"charsetmenu"_ustr)); + + OUString sChar(&m_cSelectedChar, 1); + bool bIsFavorite = m_aCharmapContents.isFavChar(sChar, aFont.GetFamilyName()); + + if (bIsFavorite || m_aCharmapContents.FavCharListIsFull()) + xMenu->set_visible(u"add"_ustr, false); + else + xMenu->set_visible(u"remove"_ustr, false); + + OUString sCommand = xMenu->popup_at_rect( + bSearchMode ? m_xSearchSet.get() : m_xShowSet.get(), aRect); + + contextMenuSelect(sCommand); +} + +void SvxCharacterMap::contextMenuSelect(std::u16string_view rIdent) +{ + if (rIdent.empty()) + return; + + if (rIdent == u"copy") + { + OUString sChar(&m_cSelectedChar, 1); + copyToClipboard(sChar); + } + else if (rIdent == u"insert") + { + insertCharToDoc(OUString(&m_cSelectedChar, 1)); + } + else if (rIdent == u"add" || rIdent == u"remove") + { + OUString sChar(&m_cSelectedChar, 1); + modifyFavCharacterList(sChar, aFont.GetFamilyName()); + setFavButtonState(sChar, aFont.GetFamilyName()); + m_aCharmapContents.updateFavCharControl(); + } +} + +void SvxCharacterMap::copyToClipboard(const OUString& rText) +{ + css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard = + css::datatransfer::clipboard::SystemClipboard::create( + comphelper::getProcessComponentContext()); + + if (xClipboard.is()) + { + vcl::unohelper::TextDataObject::CopyStringTo(rText, xClipboard); + } +} + +OUString SvxCharacterMap::getCharacterNameFromId(std::u16string_view sId) +{ + OUString aStr(sId); + + if (aStr.isEmpty()) + return OUString(); + + sal_UCS4 cChar = getCharacterFromId(aStr); + + UErrorCode errorCode = U_ZERO_ERROR; + char buffer[100]; + u_charName(cChar, U_UNICODE_CHAR_NAME, buffer, sizeof(buffer), &errorCode); + + if (U_SUCCESS(errorCode) && buffer[0] != ' + { + return OUString::createFromAscii(buffer); + } + + return OUString(); +} + +IMPL_LINK(SvxCharacterMap, ShowCharQueryTooltipHdl, const weld::TreeIter&, iter, OUString) +{ + return getCharacterNameFromId(m_xShowSet->get_id(iter)); +} + +IMPL_LINK(SvxCharacterMap, SearchCharQueryTooltipHdl, const weld::TreeIter&, iter, OUString) +{ + return getCharacterNameFromId(m_xSearchSet->get_id(iter)); } IMPL_LINK_NOARG(SvxCharacterMap, InsertClickHdl, weld::Button&, void) @@ -586,21 +1072,21 @@ IMPL_LINK_NOARG(SvxCharacterMap, InsertClickHdl, weld::Button&, void) OUString sChar = m_aShowChar.GetText(); insertCharToDoc(sChar); // Need to update recent character list, when OK button does not insert - if(!m_xFrame.is()) + if (!m_xFrame.is()) m_aCharmapContents.updateRecentCharacterList(sChar, aFont.GetFamilyName()); m_xDialog->response(RET_OK); } IMPL_LINK_NOARG(SvxCharacterMap, FavSelectHdl, weld::Button&, void) { + modifyFavCharacterList(m_aShowChar.GetText(), m_aShowChar.GetFont().GetFamilyName()); + if (m_xFavouritesBtn->get_label().match(SvxResId(RID_SVXSTR_ADD_FAVORITES))) { - m_aCharmapContents.updateFavCharacterList(m_aShowChar.GetText(), m_aShowChar.GetFont().GetFamilyName()); setFavButtonState(m_aShowChar.GetText(), m_aShowChar.GetFont().GetFamilyName()); } else { - m_aCharmapContents.deleteFavCharacterFromList(m_aShowChar.GetText(), m_aShowChar.GetFont().GetFamilyName()); m_xFavouritesBtn->set_label(SvxResId(RID_SVXSTR_ADD_FAVORITES)); m_xFavouritesBtn->set_sensitive(false); } @@ -608,89 +1094,70 @@ IMPL_LINK_NOARG(SvxCharacterMap, FavSelectHdl, weld::Button&, void) m_aCharmapContents.updateFavCharControl(); } -IMPL_LINK_NOARG(SvxCharacterMap, FavClickHdl, SvxShowCharSet*, void) +void SvxCharacterMap::modifyFavCharacterList(const OUString& sChar, const OUString& sFont) { - m_aCharmapContents.getFavCharacterList(); - m_aCharmapContents.updateFavCharControl(); + if (m_aCharmapContents.isFavChar(sChar, sFont)) + { + m_aCharmapContents.deleteFavCharacterFromList(sChar, sFont); + } + else + { + m_aCharmapContents.updateFavCharacterList(sChar, sFont); + } + + rerenderCharacter(sChar, sFont); } -IMPL_LINK_NOARG(SvxCharacterMap, CharHighlightHdl, SvxShowCharSet*, void) +IMPL_LINK(SvxCharacterMap, UpdateFavHdl, SfxCharmapContainer::CharChange*, pChange, void) { - OUString aText; - sal_UCS4 cChar = m_xShowSet->GetSelectCharacter(); - bool bSelect = (cChar > 0); - - // show char sample - if ( bSelect ) + if (!pChange) { - // using the new UCS4 constructor - aText = OUString( &cChar, 1 ); - // Get the hexadecimal code - OUString aHexText = OUString::number(cChar, 16).toAsciiUpperCase(); - // Get the decimal code - OUString aDecimalText = OUString::number(cChar); - setCharName(cChar); + m_aCharmapContents.loadFavCharacterList(); + return; + } - // Update the hex and decimal codes only if necessary - if (!m_xHexCodeText->get_text().equalsIgnoreAsciiCase(aHexText)) - m_xHexCodeText->set_text(aHexText); - if (m_xDecimalCodeText->get_text() != aDecimalText) - m_xDecimalCodeText->set_text( aDecimalText ); + rerenderCharacter(pChange->sChar, pChange->sFont); - const Subset* pSubset = nullptr; - if( pSubsetMap ) - pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); - if( pSubset ) - m_xSubsetLB->set_active_text(pSubset->GetName()); - else - m_xSubsetLB->set_active(-1); - } + if (pChange->bRemoved) + setFavButtonState(pChange->sChar, pChange->sFont); +} - m_aShowChar.SetText( aText ); - m_aShowChar.SetFont( aFont ); - m_aShowChar.Invalidate(); +IMPL_LINK(SvxCharacterMap, UpdateRecentHdl, SfxCharmapContainer::CharChange*, pChange, void) +{ + if (!pChange) + { + m_aCharmapContents.loadRecentCharacterList(); + return; + } - setFavButtonState(aText, aFont.GetFamilyName()); + rerenderCharacter(pChange->sChar, pChange->sFont); } -IMPL_LINK_NOARG(SvxCharacterMap, SearchCharHighlightHdl, SvxShowCharSet*, void) +void SvxCharacterMap::rerenderCharacter(std::u16string_view sChar, std::u16string_view sFont) { - OUString aText; - sal_UCS4 cChar = m_xSearchSet->GetSelectCharacter(); - bool bSelect = (cChar > 0); + OUString favChar(sChar.data(), sChar.size()); + OUString favCharFont(sFont.data(), sFont.size()); - // show char sample - if ( bSelect ) - { - aText = OUString( &cChar, 1 ); - // Get the hexadecimal code - OUString aHexText = OUString::number(cChar, 16).toAsciiUpperCase(); - // Get the decimal code - OUString aDecimalText = OUString::number(cChar); - setCharName(cChar); + if (favCharFont != aFont.GetFamilyName()) + return; - // Update the hex and decimal codes only if necessary - if (!m_xHexCodeText->get_text().equalsIgnoreAsciiCase(aHexText)) - m_xHexCodeText->set_text(aHexText); - if (m_xDecimalCodeText->get_text() != aDecimalText) - m_xDecimalCodeText->set_text( aDecimalText ); + sal_UCS4 cChar = favChar.iterateCodePoints(&o3tl::temporary(sal_Int32(0))); + OUString sId = OUString::number(cChar); - const Subset* pSubset = nullptr; - if( pSubsetMap ) - pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); - if( pSubset ) - m_xSubsetLB->set_active_text(pSubset->GetName()); - else - m_xSubsetLB->set_active(-1); - } + auto& posMap = isSearchMode ? m_aSearchCharPos : m_aShowCharPos; + auto it = posMap.find(sId); - if(m_xSearchSet->HasFocus()) + if (it != posMap.end()) { - m_aShowChar.SetText( aText ); - m_aShowChar.SetFont( aFont ); - m_aShowChar.Invalidate(); + sal_Int32 pos = it->second; + VclPtr<VirtualDevice> pVDev = generateCharGraphic(cChar); - setFavButtonState(aText, aFont.GetFamilyName()); + if (!isSearchMode) + m_xShowSet->set_image(pos, pVDev); + else + m_xSearchSet->set_image(pos, pVDev); + + pVDev.disposeAndClear(); } } @@ -707,10 +1174,9 @@ void SvxCharacterMap::selectCharByCode(Radix radix) break; } // Convert the code back to a character using the appropriate radix - sal_UCS4 cChar = aCodeString.toUInt32(static_cast<sal_Int16> (radix)); + sal_UCS4 cChar = aCodeString.toUInt32(static_cast<sal_Int16>(radix)); // Use FontCharMap::HasChar(sal_UCS4 cChar) to see if the desired character is in the font - FontCharMapRef xFontCharMap = m_xShowSet->GetFontCharMap(); - if (xFontCharMap->HasChar(cChar)) + if (mxFontCharMap->HasChar(cChar)) // Select the corresponding character SetChar(cChar); else { @@ -728,6 +1194,18 @@ void SvxCharacterMap::selectCharByCode(Radix radix) } } +SvxCharacterMap::~SvxCharacterMap() +{ + if (!comphelper::LibreOfficeKit::isActive()) + { + if (m_nShowRenderIdleEvent) + Application::RemoveUserEvent(m_nShowRenderIdleEvent); + if (m_nSearchRenderIdleEvent) + Application::RemoveUserEvent(m_nSearchRenderIdleEvent); + } + +} + IMPL_LINK_NOARG(SvxCharacterMap, DecimalCodeChangeHdl, weld::Entry&, void) { selectCharByCode(Radix::decimal); @@ -738,23 +1216,6 @@ IMPL_LINK_NOARG(SvxCharacterMap, HexCodeChangeHdl, weld::Entry&, void) selectCharByCode(Radix::hexadecimal); } -IMPL_LINK(SvxCharacterMap, CharPreSelectHdl, SvxShowCharSet*, pCharSet, void) -{ - assert(pCharSet); - // adjust subset selection - if( pSubsetMap ) - { - sal_UCS4 cChar = pCharSet->GetSelectCharacter(); - - setFavButtonState(OUString(&cChar, 1), aFont.GetFamilyName()); - const Subset* pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); - if( pSubset ) - m_xSubsetLB->set_active_text(pSubset->GetName()); - } - - m_xOKBtn->set_sensitive(true); -} - // class SvxShowText ===================================================== SvxShowText::SvxShowText(const VclPtr<VirtualDevice>& rVirDev) : m_xVirDev(rVirDev) @@ -836,9 +1297,9 @@ void SvxShowText::Paint(vcl::RenderContext& rRenderContext, const tools::Rectang // shift back vertically if needed int nYLDelta = aBoundRect.Top(); int nYHDelta = aSize.Height() - aBoundRect.Bottom(); - if( nYLDelta <= 0 ) + if ( nYLDelta <= 0 ) aPoint.AdjustY( -(nYLDelta - 1) ); - else if( nYHDelta <= 0 ) + else if ( nYHDelta <= 0 ) aPoint.AdjustY(nYHDelta - 1 ); if (mbCenter) @@ -851,9 +1312,9 @@ void SvxShowText::Paint(vcl::RenderContext& rRenderContext, const tools::Rectang // shift back horizontally if needed int nXLDelta = aBoundRect.Left(); int nXHDelta = aSize.Width() - aBoundRect.Right(); - if( nXLDelta <= 0 ) + if ( nXLDelta <= 0 ) aPoint.AdjustX( -(nXLDelta - 1) ); - else if( nXHDelta <= 0 ) + else if ( nXHDelta <= 0 ) aPoint.AdjustX(nXHDelta - 1 ); } } diff --git a/svx/source/dialog/searchcharmap.cxx b/svx/source/dialog/searchcharmap.cxx index 16acbc61e985..b883ac39694e 100644 --- a/svx/source/dialog/searchcharmap.cxx +++ b/svx/source/dialog/searchcharmap.cxx @@ -180,7 +180,7 @@ void SvxSearchCharSet::RecalculateFont(vcl::RenderContext& rRenderContext) rRenderContext.SetFont(aFont); rRenderContext.GetFontCharMap(mxFontCharMap); m_aItems.clear(); - getFavCharacterList(); + loadFavCharacterList(); nX = aSize.Width() / COLUMN_COUNT; nY = aSize.Height() / ROW_COUNT; diff --git a/sw/qa/extras/accessibility/dialogs.cxx b/sw/qa/extras/accessibility/dialogs.cxx index 75d5f15bdc54..71b427e0843e 100644 --- a/sw/qa/extras/accessibility/dialogs.cxx +++ b/sw/qa/extras/accessibility/dialogs.cxx @@ -13,6 +13,7 @@ #include <com/sun/star/linguistic2/LinguServiceManager.hpp> #include <com/sun/star/linguistic2/XLinguServiceManager2.hpp> #include <com/sun/star/linguistic2/XSpellChecker1.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> #include <vcl/scheduler.hxx> @@ -41,7 +42,25 @@ CPPUNIT_TEST_FIXTURE(test::AccessibleTestBase, BasicTestSpecialCharactersDialog) dialog.postExtTextEventAsync(u"copyright"_ustr); Scheduler::ProcessEventsToIdle(); - CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::TABLE_CELL, u"©")); + CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::LIST, u"")); + + auto xList = getFocusedObject(dialog.getAccessible()); + dialog.postKeyEventAsync(0, awt::Key::DOWN); + Scheduler::ProcessEventsToIdle(); + + uno::Reference<css::accessibility::XAccessibleSelection> xSelection(xList, uno::UNO_QUERY); + CPPUNIT_ASSERT(xSelection.is()); + + sal_Int64 nSelected = xSelection->getSelectedAccessibleChildCount(); + CPPUNIT_ASSERT(nSelected > 0); + + auto xSelectedChild = xSelection->getSelectedAccessibleChild(0); + CPPUNIT_ASSERT(xSelectedChild.is()); + + auto xSelectedContext = xSelectedChild->getAccessibleContext(); + CPPUNIT_ASSERT(xSelectedContext.is()); + + CPPUNIT_ASSERT_EQUAL(u"COPYRIGHT SIGN"_ustr, xSelectedContext->getAccessibleDescription()); /* there was a focus issue in this dialog: the table holding the characters always had the * selected element as focused, even when tabbing outside. @@ -68,19 +87,35 @@ CPPUNIT_TEST_FIXTURE(test::AccessibleTestBase, TestSpecialCharactersDialogFocus) load(u"private:factory/swriter"_ustr); auto dialogWaiter = awaitDialog(u"Special Characters", [](Dialog& dialog) { - CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::TABLE_CELL, u" ")); + CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::LIST, u"")); /* as there is a bug that focusing the character table doesn't enable the Insert button * (https://bugs.documentfoundation.org/show_bug.cgi?id=153806), we move to another cell * so it works. */ // tdf#153918: Check that '!' char has correct accessible name and insert it + auto xList = getFocusedObject(dialog.getAccessible()); + + dialog.postKeyEventAsync(0, awt::Key::HOME); + Scheduler::ProcessEventsToIdle(); + dialog.postKeyEventAsync(0, awt::Key::RIGHT); Scheduler::ProcessEventsToIdle(); - CPPUNIT_ASSERT_EQUAL( - AccessibilityTools::getAccessibleObjectForName( - dialog.getAccessible(), accessibility::AccessibleRole::TABLE_CELL, u"!"), - getFocusedObject(dialog.getAccessible())); + + uno::Reference<css::accessibility::XAccessibleSelection> xSelection(xList, uno::UNO_QUERY); + CPPUNIT_ASSERT(xSelection.is()); + + sal_Int64 nSelected = xSelection->getSelectedAccessibleChildCount(); + CPPUNIT_ASSERT(nSelected > 0); + + auto xSelectedChild = xSelection->getSelectedAccessibleChild(0); + CPPUNIT_ASSERT(xSelectedChild.is()); + + auto xSelectedContext = xSelectedChild->getAccessibleContext(); + CPPUNIT_ASSERT(xSelectedContext.is()); + + CPPUNIT_ASSERT_EQUAL(u"EXCLAMATION MARK"_ustr, + xSelectedContext->getAccessibleDescription()); CPPUNIT_ASSERT(dialog.tabTo(accessibility::AccessibleRole::PUSH_BUTTON, u"Insert")); dialog.postKeyEventAsync(0, awt::Key::RETURN); diff --git a/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py b/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py index 31810c8af751..ca2457103a29 100644 --- a/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py +++ b/sw/qa/uitest/writer_tests2/formatBulletsNumbering.py @@ -36,7 +36,8 @@ class formatBulletsNumbering(UITestCase): xChangeBulletBtn = xBulletPage.getChild("changeBulletBtn") with self.ui_test.execute_blocking_action(xChangeBulletBtn.executeAction, args=('CLICK', ())) as xCharSetDialog: xCharSet = xCharSetDialog.getChild("showcharset") - xCharSet.executeAction("SELECT", mkPropertyValues({"COLUMN": "21", "ROW": "1"})) + character6 = xCharSet.getChild("5") + character6.executeAction("SELECT", mkPropertyValues({})) # Check that the "black down-pointing triangle" bullet is the third item with self.ui_test.execute_dialog_through_command(".uno:BulletsAndNumberingDialog") as xDialog: @@ -56,8 +57,8 @@ class formatBulletsNumbering(UITestCase): xHexText = xCharSetDialog.getChild("hexvalue") xDecText = xCharSetDialog.getChild("decimalvalue") # Check the "black down-pointing triangle" bullet Hex and Decimal value - self.assertEqual(get_state_as_dict(xHexText)["Text"], "25BC") - self.assertEqual(get_state_as_dict(xDecText)["Text"], "9660") + self.assertEqual(get_state_as_dict(xHexText)["Text"], "A2") + self.assertEqual(get_state_as_dict(xDecText)["Text"], "162") def test_bullets_and_numbering_dialog_tab_position(self): diff --git a/sw/qa/uitest/writer_tests3/specialCharacter.py b/sw/qa/uitest/writer_tests3/specialCharacter.py index 02a105835c44..d9a276afcce9 100644 --- a/sw/qa/uitest/writer_tests3/specialCharacter.py +++ b/sw/qa/uitest/writer_tests3/specialCharacter.py @@ -38,7 +38,8 @@ class specialCharacter(UITestCase): with self.ui_test.execute_dialog_through_command(".uno:InsertSymbol", close_button="cancel") as xDialog: xCharSet = xDialog.getChild("showcharset") # default charset - xCharSet.executeAction("SELECT", mkPropertyValues({"COLUMN": "1", "ROW": "4"})) # digit 4 selected + element21 = xCharSet.getChild("20") # digit 4 selected + element21.executeAction("SELECT", mkPropertyValues({})) xHexText = xDialog.getChild("hexvalue") xDecText = xDialog.getChild("decimalvalue") @@ -70,9 +71,10 @@ class specialCharacter(UITestCase): # Markus: Actually after a round of debugging I think the problem is actually that the test depends on the used font. # Therefore, if the font is not available or not selected by default the test fails. # xCharSet = xDialog.getChild("searchcharset") #another charset -> search charset - # xCharSet.executeAction("SELECT", mkPropertyValues({"COLUMN": "0", "ROW": "0"})) #digit 4 selected, we have only one result; + # element = xCharSet.getChild("0") #digit 4 selected, we have only one result; + # element.executeAction("SELECT", mkPropertyValues({})) # sleep(1) #try sleep here - # xCharSet.executeAction("SELECT", mkPropertyValues({"COLUMN": "0", "ROW": "0"})) #try it twice, because it works at local,but fail on gerrit + # element.executeAction("SELECT", mkPropertyValues({})) #try it twice, because it works at local,but fail on gerrit ##gerrit:self.assertEqual(get_state_as_dict(xHexText)["Text"], "34") # check the values for digit 4; AssertionError: '1' != '34' # xHexText = xDialog.getChild("hexvalue") diff --git a/uitest/demo_ui/char_dialog.py b/uitest/demo_ui/char_dialog.py index 9475f3b16edd..a1bf5a2ef617 100644 --- a/uitest/demo_ui/char_dialog.py +++ b/uitest/demo_ui/char_dialog.py @@ -18,8 +18,8 @@ class CharDialogText(UITestCase): with self.ui_test.execute_dialog_through_command(".uno:InsertSymbol", close_button="cancel") as xCharDialog: xCharSet = xCharDialog.getChild("showcharset") - - xCharSet.executeAction("SELECT", mkPropertyValues({"COLUMN": "2", "ROW": "2"})) + element35 = xCharSet.getChild("34") + element35.executeAction("SELECT", mkPropertyValues({}))
