diff options
author | Akshay Deep <akshaydeepiitr@gmail.com> | 2017-07-30 15:03:56 +0530 |
---|---|---|
committer | Heiko Tietze <tietze.heiko@googlemail.com> | 2017-08-10 10:28:19 +0200 |
commit | e74be9ad773c7769c5d8765bb2ac234967e420ec (patch) | |
tree | 09699787837732c4103e7666ce21f4c6824fe10c | |
parent | 86bc72072ece83c3560dbf874d6469f771c95289 (diff) |
Search feature for Special Characters
1. Name data stored in map
2. Retrive the data to compare with search string
3. create search control
4. populate search results
5. Read-only subset listbox meanwhile
Change-Id: I689bbee0dd9a226261c37a5824af7f83a510167d
Reviewed-on: https://gerrit.libreoffice.org/40563
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Heiko Tietze <tietze.heiko@googlemail.com>
Tested-by: Heiko Tietze <tietze.heiko@googlemail.com>
-rw-r--r-- | cui/source/dialogs/cuicharmap.cxx | 192 | ||||
-rw-r--r-- | cui/source/inc/cuicharmap.hxx | 16 | ||||
-rw-r--r-- | cui/uiconfig/ui/specialcharacters.ui | 104 | ||||
-rw-r--r-- | extras/source/glade/libreoffice-catalog.xml.in | 3 | ||||
-rw-r--r-- | include/svx/charmap.hxx | 18 | ||||
-rw-r--r-- | include/svx/searchcharmap.hxx | 96 | ||||
-rw-r--r-- | include/svx/ucsubset.hxx | 1 | ||||
-rw-r--r-- | svx/Library_svx.mk | 1 | ||||
-rw-r--r-- | svx/source/dialog/charmap.cxx | 14 | ||||
-rw-r--r-- | svx/source/dialog/searchcharmap.cxx | 475 |
10 files changed, 875 insertions, 45 deletions
diff --git a/cui/source/dialogs/cuicharmap.cxx b/cui/source/dialogs/cuicharmap.cxx index b535fd95b525..9ae777a7affe 100644 --- a/cui/source/dialogs/cuicharmap.cxx +++ b/cui/source/dialogs/cuicharmap.cxx @@ -57,9 +57,11 @@ using namespace css; SvxCharacterMap::SvxCharacterMap( vcl::Window* pParent, const SfxItemSet* pSet ) : SfxModalDialog(pParent, "SpecialCharactersDialog", "cui/ui/specialcharacters.ui") , pSubsetMap( nullptr ) + , isSearchMode(true) , mxContext(comphelper::getProcessComponentContext()) { get(m_pShowSet, "showcharset"); + get(m_pSearchSet, "searchcharset"); get(m_pShowChar, "showchar"); m_pShowChar->SetCentered(true); get(m_pOKBtn, "ok"); @@ -76,6 +78,7 @@ SvxCharacterMap::SvxCharacterMap( vcl::Window* pParent, const SfxItemSet* pSet ) get(m_pCharName, "charname"); m_pCharName->set_height_request(m_pCharName->GetTextHeight()*3); m_pCharName->SetPaintTransparent(true); + get(m_pSearchText, "search"); //lock the size request of this widget to the width of the original .ui string m_pHexCodeText->set_width_request(m_pHexCodeText->get_preferred_size().Width()); @@ -140,6 +143,8 @@ SvxCharacterMap::SvxCharacterMap( vcl::Window* pParent, const SfxItemSet* pSet ) } CreateOutputItemSet( pSet ? *pSet->GetPool() : SfxGetpApp()->GetPool() ); + m_pShowSet->Show(); + m_pSearchSet->Hide(); } SvxCharacterMap::~SvxCharacterMap() @@ -174,6 +179,7 @@ void SvxCharacterMap::dispose() m_pRecentCharView[i].clear(); m_pShowSet.clear(); + m_pSearchSet.clear(); m_pOKBtn.clear(); m_pFontText.clear(); m_pFontLB.clear(); @@ -190,6 +196,7 @@ void SvxCharacterMap::dispose() maFavCharFontList.clear(); m_pFavouritesBtn.clear(); + m_pSearchText.clear(); SfxModalDialog::dispose(); } @@ -199,7 +206,7 @@ void SvxCharacterMap::SetChar( sal_UCS4 c ) { m_pShowSet->SelectCharacter( c ); - setFavButtonState(OUString(&c, 1), aFont.GetFamilyName()); + setFavButtonState(OUString(&c, 1), aFont.GetFamilyName()); } @@ -464,10 +471,17 @@ void SvxCharacterMap::init() m_pFontLB->SetSelectHdl( LINK( this, SvxCharacterMap, FontSelectHdl ) ); m_pSubsetLB->SetSelectHdl( LINK( this, SvxCharacterMap, SubsetSelectHdl ) ); m_pOKBtn->SetClickHdl( LINK( this, SvxCharacterMap, InsertClickHdl ) ); + m_pShowSet->SetDoubleClickHdl( LINK( this, SvxCharacterMap, CharDoubleClickHdl ) ); m_pShowSet->SetSelectHdl( LINK( this, SvxCharacterMap, CharSelectHdl ) ); m_pShowSet->SetHighlightHdl( LINK( this, SvxCharacterMap, CharHighlightHdl ) ); m_pShowSet->SetPreSelectHdl( LINK( this, SvxCharacterMap, CharPreSelectHdl ) ); + + m_pSearchSet->SetDoubleClickHdl( LINK( this, SvxCharacterMap, SearchCharDoubleClickHdl ) ); + m_pSearchSet->SetSelectHdl( LINK( this, SvxCharacterMap, SearchCharSelectHdl ) ); + m_pSearchSet->SetHighlightHdl( LINK( this, SvxCharacterMap, SearchCharHighlightHdl ) ); + m_pSearchSet->SetPreSelectHdl( LINK( this, SvxCharacterMap, SearchCharPreSelectHdl ) ); + m_pDecimalCodeText->SetModifyHdl( LINK( this, SvxCharacterMap, DecimalCodeChangeHdl ) ); m_pHexCodeText->SetModifyHdl( LINK( this, SvxCharacterMap, HexCodeChangeHdl ) ); m_pFavouritesBtn->SetClickHdl( LINK(this, SvxCharacterMap, FavSelectHdl)); @@ -506,6 +520,10 @@ void SvxCharacterMap::init() } setCharName(90); + + m_pSearchText->SetGetFocusHdl(LINK( this, SvxCharacterMap, SearchFieldGetFocusHdl )); + m_pSearchText->SetUpdateDataHdl(LINK( this, SvxCharacterMap, SearchUpdateHdl )); + m_pSearchText->EnableUpdateData(); } bool SvxCharacterMap::isFavChar(const OUString& sTitle, const OUString& rFont) @@ -622,7 +640,13 @@ IMPL_LINK_NOARG(SvxCharacterMap, FontSelectHdl, ListBox&, void) // notify children using this font m_pShowSet->SetFont( aFont ); + m_pSearchSet->SetFont( aFont ); m_pShowChar->SetFont( aFont ); + if(isSearchMode) + { + SearchUpdateHdl(*m_pSearchText); + SearchCharHighlightHdl(m_pSearchSet); + } // setup unicode subset listbar with font specific subsets, // hide unicode subset listbar for symbol fonts @@ -659,6 +683,26 @@ IMPL_LINK_NOARG(SvxCharacterMap, FontSelectHdl, ListBox&, void) m_pSubsetLB->Enable(bNeedSubset); } +void SvxCharacterMap::toggleSearchView(bool state) +{ + isSearchMode = state; + m_pHexCodeText->SetReadOnly(state); + m_pDecimalCodeText->SetReadOnly(state); + m_pSubsetLB->SetReadOnly(state); + m_pSubsetLB->Invalidate(); + + if(state) + { + m_pSearchSet->Show(); + m_pShowSet->Hide(); + } + else + { + m_pSearchSet->Hide(); + m_pShowSet->Show(); + } +} + void SvxCharacterMap::setCharName(sal_UCS4 nDecimalValue) { /* get the character name */ @@ -675,14 +719,29 @@ IMPL_LINK_NOARG(SvxCharacterMap, SubsetSelectHdl, ListBox&, void) { const sal_Int32 nPos = m_pSubsetLB->GetSelectEntryPos(); const Subset* pSubset = static_cast<const Subset*> (m_pSubsetLB->GetEntryData(nPos)); - if( pSubset ) + if( pSubset && !isSearchMode) { sal_UCS4 cFirst = pSubset->GetRangeMin(); m_pShowSet->SelectCharacter( cFirst ); setFavButtonState(OUString(&cFirst, 1), aFont.GetFamilyName()); + m_pSubsetLB->SelectEntryPos( nPos ); + } + else if( pSubset && isSearchMode) + { + m_pSearchSet->SelectCharacter( pSubset ); + + const Subset* curSubset = nullptr; + if( pSubsetMap ) + curSubset = pSubsetMap->GetSubsetByUnicode( m_pSearchSet->GetSelectCharacter() ); + if( curSubset ) + m_pSubsetLB->SelectEntry( curSubset->GetName() ); + else + m_pSubsetLB->SetNoSelection(); + + sal_UCS4 sChar = m_pSearchSet->GetSelectCharacter(); + setFavButtonState(OUString(&sChar, 1), aFont.GetFamilyName()); } - m_pSubsetLB->SelectEntryPos( nPos ); } IMPL_LINK(SvxCharacterMap, RecentClearClickHdl, SvxCharView*, rView, void) @@ -760,6 +819,59 @@ IMPL_LINK_NOARG(SvxCharacterMap, FavClearAllClickHdl, SvxCharView*, void) updateFavCharControl(); } +IMPL_LINK_NOARG(SvxCharacterMap, SearchFieldGetFocusHdl, Control&, void) +{ + m_pOKBtn->Disable(); +} + + +IMPL_LINK_NOARG(SvxCharacterMap, SearchUpdateHdl, Edit&, void) +{ + if(!m_pSearchText->GetText().isEmpty()) + { + m_pSearchSet->ClearPreviousData(); + OUString aKeyword = m_pSearchText->GetText(); + + toggleSearchView(true); + + FontCharMapRef xFontCharMap(new FontCharMap()); + m_pSearchSet->GetFontCharMap(xFontCharMap); + + sal_UCS4 sChar = xFontCharMap->GetFirstChar(); + while(sChar != xFontCharMap->GetLastChar()) + { + UErrorCode errorCode = U_ZERO_ERROR; + char buffer[100]; + u_charName(sChar, U_UNICODE_CHAR_NAME, buffer, sizeof(buffer), &errorCode); + if (U_SUCCESS(errorCode)) + { + OUString sName = OUString::createFromAscii(buffer); + if(!sName.isEmpty() && sName.toAsciiLowerCase().indexOf(aKeyword.toAsciiLowerCase()) >= 0) + m_pSearchSet->AppendCharToList(sChar); + } + sChar = xFontCharMap->GetNextChar(sChar); + } + //for last char + UErrorCode errorCode = U_ZERO_ERROR; + char buffer[100]; + u_charName(sChar, U_UNICODE_CHAR_NAME, buffer, sizeof(buffer), &errorCode); + if (U_SUCCESS(errorCode)) + { + OUString sName = OUString::createFromAscii(buffer); + if(!sName.isEmpty() && sName.toAsciiLowerCase().indexOf(aKeyword.toAsciiLowerCase()) >= 0) + m_pSearchSet->AppendCharToList(sChar); + } + + m_pSearchSet->Resize(); + + } + else + { + toggleSearchView(false); + } +} + + IMPL_LINK(SvxCharacterMap, CharClickHdl, SvxCharView*, rView, void) { m_pShowChar->SetText( rView->GetText() ); @@ -795,11 +907,25 @@ IMPL_LINK_NOARG(SvxCharacterMap, CharDoubleClickHdl, SvxShowCharSet*, void) insertCharToDoc(aOUStr); } +IMPL_LINK_NOARG(SvxCharacterMap, SearchCharDoubleClickHdl, SvxShowCharSet*, void) +{ + sal_UCS4 cChar = m_pSearchSet->GetSelectCharacter(); + // using the new UCS4 constructor + OUString aOUStr( &cChar, 1 ); + setFavButtonState(aOUStr, aFont.GetFamilyName()); + insertCharToDoc(aOUStr); +} + IMPL_LINK_NOARG(SvxCharacterMap, CharSelectHdl, SvxShowCharSet*, void) { m_pOKBtn->Enable(); } +IMPL_LINK_NOARG(SvxCharacterMap, SearchCharSelectHdl, SvxShowCharSet*, void) +{ + m_pOKBtn->Enable(); +} + IMPL_LINK_NOARG(SvxCharacterMap, InsertClickHdl, Button*, void) { insertCharToDoc(m_pShowChar->GetText()); @@ -875,6 +1001,49 @@ IMPL_LINK_NOARG(SvxCharacterMap, CharHighlightHdl, SvxShowCharSet*, void) m_pDecimalCodeText->SetText( aDecimalText ); } +IMPL_LINK_NOARG(SvxCharacterMap, SearchCharHighlightHdl, SvxShowCharSet*, void) +{ + OUString aText; + OUString aHexText; + OUString aDecimalText; + sal_UCS4 cChar = m_pSearchSet->GetSelectCharacter(); + bool bSelect = (cChar > 0); + + // show char sample + if ( bSelect ) + { + aText = OUString( &cChar, 1 ); + // Get the hexadecimal code + aHexText = OUString::number(cChar, 16).toAsciiUpperCase(); + // Get the decimal code + aDecimalText = OUString::number(cChar); + setCharName(cChar); + + // Update the hex and decimal codes only if necessary + if (m_pHexCodeText->GetText() != aHexText) + m_pHexCodeText->SetText( aHexText ); + if (m_pDecimalCodeText->GetText() != aDecimalText) + m_pDecimalCodeText->SetText( aDecimalText ); + + const Subset* pSubset = nullptr; + if( pSubsetMap ) + pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); + if( pSubset ) + m_pSubsetLB->SelectEntry( pSubset->GetName() ); + else + m_pSubsetLB->SetNoSelection(); + } + + if(m_pSearchSet->HasFocus()) + { + m_pShowChar->SetText( aText ); + m_pShowChar->SetFont( aFont ); + m_pShowChar->Update(); + + setFavButtonState(aText, aFont.GetFamilyName()); + } +} + void SvxCharacterMap::selectCharByCode(Radix radix) { OUString aCodeString; @@ -923,6 +1092,23 @@ IMPL_LINK_NOARG(SvxCharacterMap, CharPreSelectHdl, SvxShowCharSet*, void) m_pOKBtn->Enable(); } +IMPL_LINK_NOARG(SvxCharacterMap, SearchCharPreSelectHdl, SvxShowCharSet*, void) +{ + // adjust subset selection + if( pSubsetMap ) + { + sal_UCS4 cChar = m_pSearchSet->GetSelectCharacter(); + + setFavButtonState(OUString(&cChar, 1), aFont.GetFamilyName()); + const Subset* pSubset = pSubsetMap->GetSubsetByUnicode( cChar ); + if( pSubset ) + m_pSubsetLB->SelectEntry( pSubset->GetName() ); + } + + m_pOKBtn->Enable(); +} + + // class SvxShowText ===================================================== diff --git a/cui/source/inc/cuicharmap.hxx b/cui/source/inc/cuicharmap.hxx index 9f4f2e9a0fb2..e7c63c29429c 100644 --- a/cui/source/inc/cuicharmap.hxx +++ b/cui/source/inc/cuicharmap.hxx @@ -26,6 +26,7 @@ #include <vcl/lstbox.hxx> #include <sfx2/basedlgs.hxx> #include <svx/charmap.hxx> +#include <svx/searchcharmap.hxx> #include <sfx2/charwin.hxx> using namespace ::com::sun::star; @@ -70,12 +71,14 @@ private: void init(); VclPtr<SvxShowCharSet> m_pShowSet; + VclPtr<SvxSearchCharSet> m_pSearchSet; VclPtr<PushButton> m_pOKBtn; VclPtr<FixedText> m_pFontText; VclPtr<ListBox> m_pFontLB; VclPtr<FixedText> m_pSubsetText; VclPtr<ListBox> m_pSubsetLB; VclPtr<SvxShowText> m_pShowChar; + VclPtr<Edit> m_pSearchText; VclPtr<Edit> m_pHexCodeText; VclPtr<Edit> m_pDecimalCodeText; VclPtr<Button> m_pFavouritesBtn; @@ -83,8 +86,9 @@ private: VclPtr<SvxCharView> m_pFavCharView[16]; VclPtr<VclMultiLineEdit> m_pCharName; - vcl::Font aFont; - const SubsetMap* pSubsetMap; + vcl::Font aFont; + const SubsetMap* pSubsetMap; + bool isSearchMode; std::deque<OUString> maRecentCharList; std::deque<OUString> maRecentCharFontList; @@ -102,6 +106,10 @@ private: DECL_LINK(CharSelectHdl, SvxShowCharSet*, void); DECL_LINK(CharHighlightHdl, SvxShowCharSet*, void); DECL_LINK(CharPreSelectHdl, SvxShowCharSet*, void); + DECL_LINK(SearchCharDoubleClickHdl, SvxShowCharSet*,void); + DECL_LINK(SearchCharSelectHdl, SvxShowCharSet*, void); + DECL_LINK(SearchCharHighlightHdl, SvxShowCharSet*, void); + DECL_LINK(SearchCharPreSelectHdl, SvxShowCharSet*, void); DECL_LINK(DecimalCodeChangeHdl, Edit&, void); DECL_LINK(HexCodeChangeHdl, Edit&, void); DECL_LINK(CharClickHdl, SvxCharView*, void); @@ -112,6 +120,8 @@ private: DECL_LINK(InsertClickHdl, Button*, void); DECL_STATIC_LINK(SvxCharacterMap, LoseFocusHdl, Control&, void); DECL_LINK(FavSelectHdl, Button*, void); + DECL_LINK(SearchUpdateHdl, Edit&, void); + DECL_LINK(SearchFieldGetFocusHdl, Control&, void); static void fillAllSubsets(ListBox &rListBox); void selectCharByCode(Radix radix); @@ -145,6 +155,8 @@ public: void setFavButtonState(const OUString& sTitle, const OUString& rFont); void setCharName(sal_UCS4 nDecimalValue); + + void toggleSearchView(bool state); }; #endif diff --git a/cui/uiconfig/ui/specialcharacters.ui b/cui/uiconfig/ui/specialcharacters.ui index b321dfc21185..f210b8a25551 100644 --- a/cui/uiconfig/ui/specialcharacters.ui +++ b/cui/uiconfig/ui/specialcharacters.ui @@ -86,36 +86,36 @@ <property name="hexpand">True</property> <property name="column_spacing">12</property> <child> - <object class="GtkLabel" id="fontft"> + <object class="GtkLabel" id="subsetft"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes" context="specialcharacters|fontft">Font:</property> + <property name="halign">start</property> + <property name="label" translatable="yes" context="specialcharacters|subsetft">Subset:</property> <property name="use_underline">True</property> - <property name="mnemonic_widget">fontlb</property> </object> <packing> - <property name="left_attach">0</property> + <property name="left_attach">2</property> <property name="top_attach">0</property> </packing> </child> <child> - <object class="GtkLabel" id="subsetft"> + <object class="GtkComboBox" id="fontlb"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes" context="specialcharacters|subsetft">Subset:</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">subsetlb</property> + <property name="hexpand">True</property> </object> <packing> - <property name="left_attach">2</property> - <property name="top_attach">0</property> + <property name="left_attach">1</property> + <property name="top_attach">1</property> </packing> </child> <child> - <object class="GtkComboBox" id="fontlb"> + <object class="GtkLabel" id="fontft"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="hexpand">True</property> + <property name="halign">start</property> + <property name="label" translatable="yes" context="specialcharacters|fontft">Font:</property> + <property name="use_underline">True</property> </object> <packing> <property name="left_attach">1</property> @@ -129,10 +129,33 @@ <property name="hexpand">True</property> </object> <packing> - <property name="left_attach">3</property> + <property name="left_attach">2</property> + <property name="top_attach">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="srchft"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Search:</property> + </object> + <packing> + <property name="left_attach">0</property> <property name="top_attach">0</property> </packing> </child> + <child> + <object class="GtkEntry" id="search"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> </object> <packing> <property name="left_attach">0</property> @@ -141,20 +164,6 @@ </packing> </child> <child> - <object class="svxlo-SvxShowCharSet" id="showcharset"> - <property name="width_request">580</property> - <property name="height_request">250</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - </object> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> - </packing> - </child> - <child> <object class="GtkGrid" id="grid3"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -745,6 +754,47 @@ <property name="width">2</property> </packing> </child> + <child> + <object class="GtkBox" id="box6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="svxlo-SvxShowCharSet" id="showcharset"> + <property name="width_request">580</property> + <property name="height_request">250</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="svxlo-SvxSearchCharSet" id="searchcharset"> + <property name="width_request">580</property> + <property name="height_request">250</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> diff --git a/extras/source/glade/libreoffice-catalog.xml.in b/extras/source/glade/libreoffice-catalog.xml.in index 93b8d81ad22a..83d1bc211779 100644 --- a/extras/source/glade/libreoffice-catalog.xml.in +++ b/extras/source/glade/libreoffice-catalog.xml.in @@ -275,6 +275,9 @@ <glade-widget-class title="Math Char Selection" name="svxlo-SvxShowCharSet" generic-name="Math Char Selection" parent="GtkDrawingArea" icon-name="widget-gtk-drawingarea"/> + <glade-widget-class title="Math Char Selection" name="svxlo-SvxSearchCharSet" + generic-name="Math Char Selection" parent="GtkDrawingArea" + icon-name="widget-gtk-drawingarea"/> <glade-widget-class title="Number Preview" name="cuilo-SvxNumberPreview" generic-name="Number Preview Window" parent="GtkDrawingArea" icon-name="widget-gtk-drawingarea"/> diff --git a/include/svx/charmap.hxx b/include/svx/charmap.hxx index ade653b697be..86c95fc0cad5 100644 --- a/include/svx/charmap.hxx +++ b/include/svx/charmap.hxx @@ -60,10 +60,10 @@ public: virtual void dispose() override; virtual void ApplySettings(vcl::RenderContext& rRenderContext) override; - void RecalculateFont(vcl::RenderContext& rRenderContext); + virtual void RecalculateFont(vcl::RenderContext& rRenderContext); void SelectCharacter( sal_uInt32 cNew ); - sal_UCS4 GetSelectCharacter() const; + virtual sal_UCS4 GetSelectCharacter() const; void SetDoubleClickHdl( const Link<SvxShowCharSet*,void>& rLink ) { aDoubleClkHdl = rLink; } void SetSelectHdl( const Link<SvxShowCharSet*,void>& rHdl ) { aSelectHdl = rHdl; } @@ -72,11 +72,11 @@ public: static sal_uInt32& getSelectedChar(); void SetFont( const vcl::Font& rFont ); - svx::SvxShowCharSetItem* ImplGetItem( int _nPos ); + virtual svx::SvxShowCharSetItem* ImplGetItem( int _nPos ); int FirstInView() const; - int LastInView() const; + virtual int LastInView() const; int PixelToMapIndex( const Point&) const; - void SelectIndex( int index, bool bFocus = false ); + virtual void SelectIndex( int index, bool bFocus = false ); void OutputIndex( int index ); void DeSelect(); bool IsSelected(sal_uInt16 _nPos) const { return _nPos == nSelectedIndex; } @@ -86,7 +86,7 @@ public: ScrollBar& getScrollBar() { return *aVscrollSB.get();} void ReleaseAccessible(); - sal_Int32 getMaxCharCount() const; + virtual sal_Int32 getMaxCharCount() const; virtual void Resize() override; @@ -106,7 +106,7 @@ protected: virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override; -private: +protected: typedef std::map<sal_Int32, std::shared_ptr<svx::SvxShowCharSetItem> > ItemsMap; ItemsMap m_aItems; Link<SvxShowCharSet*,void> aDoubleClkHdl; @@ -131,8 +131,8 @@ private: bool mbUpdateBackground : 1; -private: - void DrawChars_Impl(vcl::RenderContext& rRenderContext, int n1, int n2); +protected: + virtual void DrawChars_Impl(vcl::RenderContext& rRenderContext, int n1, int n2); void InitSettings(vcl::RenderContext& rRenderContext); // abstraction layers are: Unicode<->MapIndex<->Pixel Point MapIndexToPixel( int) const; diff --git a/include/svx/searchcharmap.hxx b/include/svx/searchcharmap.hxx new file mode 100644 index 000000000000..bf66a1cf55fa --- /dev/null +++ b/include/svx/searchcharmap.hxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_SVX_SEARCHCHARMAP_HXX +#define INCLUDED_SVX_SEARCHCHARMAP_HXX + +#include <map> +#include <memory> + +#include <sal/types.h> +#include <rtl/ref.hxx> +#include <svx/svxdllapi.h> +#include <svx/charmap.hxx> +#include <tools/gen.hxx> +#include <tools/link.hxx> +#include <vcl/ctrl.hxx> +#include <vcl/event.hxx> +#include <vcl/outdev.hxx> +#include <svx/ucsubset.hxx> +#include <vcl/metric.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/window.hxx> +#include <vector> +#include <unordered_map> + +namespace com { namespace sun { namespace star { + namespace accessibility { class XAccessible; } +} } } + +namespace vcl { class Font; } + +#define COLUMN_COUNT 16 +#define ROW_COUNT 8 + +class CommandEvent; +class ScrollBar; + +namespace svx +{ + struct SvxShowCharSetItem; + class SvxShowCharSetVirtualAcc; +} + +class SVX_DLLPUBLIC SvxSearchCharSet : public SvxShowCharSet +{ +public: + SvxSearchCharSet( vcl::Window* pParent ); + virtual ~SvxSearchCharSet() override; + virtual void dispose() override; + + virtual void RecalculateFont(vcl::RenderContext& rRenderContext) override; + + void SelectCharacter( const Subset* sub); + virtual sal_UCS4 GetSelectCharacter() const override; + + virtual svx::SvxShowCharSetItem* ImplGetItem( int _nPos ) override; + virtual int LastInView() const override; + virtual void SelectIndex( int index, bool bFocus = false ) override; + void AppendCharToList(sal_UCS4 cChar); + void AppendCharList(std::vector<sal_UCS4> cCharList); + void ClearPreviousData(); + + virtual sal_Int32 getMaxCharCount() const override; + +protected: + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) override; + virtual void KeyInput( const KeyEvent& rKEvt ) override; + +private: + sal_Int32 nCount; + + //index to char code mapping for the search + //to uniquely identify each appended element + std::unordered_map<sal_Int32, sal_UCS4> m_aItemList; +private: + virtual void DrawChars_Impl(vcl::RenderContext& rRenderContext, int n1, int n2) override; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/svx/ucsubset.hxx b/include/svx/ucsubset.hxx index 570913f3bd18..f5dc7847ab98 100644 --- a/include/svx/ucsubset.hxx +++ b/include/svx/ucsubset.hxx @@ -22,6 +22,7 @@ #include <svx/svxdllapi.h> +#include <unordered_map> #include <vcl/metric.hxx> #include <list> diff --git a/svx/Library_svx.mk b/svx/Library_svx.mk index d326ad7365eb..d1c310fe29af 100644 --- a/svx/Library_svx.mk +++ b/svx/Library_svx.mk @@ -108,6 +108,7 @@ $(eval $(call gb_Library_add_exception_objects,svx,\ svx/source/customshapes/EnhancedCustomShapeHandle \ svx/source/dialog/_bmpmask \ svx/source/dialog/charmap \ + svx/source/dialog/searchcharmap \ svx/source/dialog/connctrl \ svx/source/dialog/_contdlg \ svx/source/dialog/contwnd \ diff --git a/svx/source/dialog/charmap.cxx b/svx/source/dialog/charmap.cxx index 5f4d17043f30..4904de3ab689 100644 --- a/svx/source/dialog/charmap.cxx +++ b/svx/source/dialog/charmap.cxx @@ -25,6 +25,8 @@ #include <rtl/textenc.h> #include <svx/ucsubset.hxx> +#include <unordered_map> + #include <svx/strings.hrc> @@ -146,7 +148,7 @@ void SvxShowCharSet::MouseButtonDown( const MouseEvent& rMEvt ) CaptureMouse(); int nIndex = PixelToMapIndex( rMEvt.GetPosPixel() ); - // Fire the focus event + // Fire the focus event SelectIndex( nIndex, true); } @@ -231,7 +233,7 @@ int SvxShowCharSet::LastInView() const } -inline Point SvxShowCharSet::MapIndexToPixel( int nIndex ) const +Point SvxShowCharSet::MapIndexToPixel( int nIndex ) const { const int nBase = FirstInView(); int x = ((nIndex - nBase) % COLUMN_COUNT) * nX; @@ -650,7 +652,6 @@ void SvxShowCharSet::OutputIndex( int nNewIndex ) { SelectIndex( nNewIndex, true ); aSelectHdl.Call( this ); - } @@ -763,9 +764,13 @@ SubsetMap::SubsetMap( const FontCharMapRef& rxFontCharMap ) const Subset* SubsetMap::GetNextSubset( bool bFirst ) const { if( bFirst ) + { maSubsetIterator = maSubsets.begin(); + } + if( maSubsetIterator == maSubsets.end() ) return nullptr; + const Subset* s = &*(maSubsetIterator++); return s; } @@ -781,7 +786,8 @@ const Subset* SubsetMap::GetSubsetByUnicode( sal_UCS4 cChar ) const inline Subset::Subset(sal_UCS4 nMin, sal_UCS4 nMax, const OUString& rName) : mnRangeMin(nMin), mnRangeMax(nMax), maRangeName(rName) -{} +{ +} void SubsetMap::InitList() { diff --git a/svx/source/dialog/searchcharmap.cxx b/svx/source/dialog/searchcharmap.cxx new file mode 100644 index 000000000000..2e4379440015 --- /dev/null +++ b/svx/source/dialog/searchcharmap.cxx @@ -0,0 +1,475 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/builderfactory.hxx> +#include <vcl/fontcharmap.hxx> +#include <svtools/colorcfg.hxx> + +#include <rtl/textenc.h> +#include <svx/ucsubset.hxx> +#include <unordered_map> + + +#include <svx/strings.hrc> + +#include <svx/searchcharmap.hxx> +#include <svx/dialmgr.hxx> +#include <svx/svxdlg.hxx> + +#include "charmapacc.hxx" +#include "uiobject.hxx" + +#include <com/sun/star/accessibility/AccessibleEventObject.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <comphelper/types.hxx> +#include <svl/itemset.hxx> +#include <unicode/uchar.h> +#include "rtl/ustrbuf.hxx" + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + + +SvxSearchCharSet::SvxSearchCharSet(vcl::Window* pParent) + : SvxShowCharSet(pParent), + nCount(0) +{ +} + +VCL_BUILDER_FACTORY(SvxSearchCharSet) + + +int SvxSearchCharSet::LastInView() const +{ + sal_uIntPtr nIndex = FirstInView(); + nIndex += ROW_COUNT * COLUMN_COUNT - 1; + sal_uIntPtr nCompare = sal::static_int_cast<sal_uIntPtr>(nCount - 1); + if (nIndex > nCompare) + nIndex = nCompare; + return nIndex; +} + + +void SvxSearchCharSet::KeyInput(const KeyEvent& rKEvt) +{ + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if (aCode.GetModifier()) + { + Control::KeyInput(rKEvt); + return; + } + + int tmpSelected = nSelectedIndex; + + switch (aCode.GetCode()) + { + case KEY_SPACE: + aSelectHdl.Call( this ); + break; + case KEY_LEFT: + --tmpSelected; + break; + case KEY_RIGHT: + ++tmpSelected; + break; + case KEY_UP: + tmpSelected -= COLUMN_COUNT; + break; + case KEY_DOWN: + tmpSelected += COLUMN_COUNT; + break; + case KEY_PAGEUP: + tmpSelected -= ROW_COUNT * COLUMN_COUNT; + break; + case KEY_PAGEDOWN: + tmpSelected += ROW_COUNT * COLUMN_COUNT; + break; + case KEY_HOME: + tmpSelected = 0; + break; + case KEY_END: + tmpSelected = nCount - 1; + break; + case KEY_TAB: // some fonts have a character at these unicode control codes + case KEY_ESCAPE: + case KEY_RETURN: + Control::KeyInput(rKEvt); + tmpSelected = - 1; // mark as invalid + break; + default: + { + tmpSelected = -1; + } + } + + if ( tmpSelected >= 0 ) + { + SelectIndex( tmpSelected, true ); + aPreSelectHdl.Call( this ); + } +} + +void SvxSearchCharSet::SelectCharacter( const Subset* sub ) +{ + if ( !mxFontCharMap.is() ) + RecalculateFont( *this ); + + // get next available char of current font + sal_UCS4 cChar = sub->GetRangeMin(); + int nMapIndex = 0; + + while(cChar <= sub->GetRangeMax() && nMapIndex == 0) + { + for(auto it = m_aItemList.begin(); it!= m_aItemList.end(); it++) + if(it->second == cChar) + { + nMapIndex = it->first; + break; + } + cChar++; + } + + if(nMapIndex == 0) + SelectIndex( 0 ); + else + SelectIndex( nMapIndex ); + aHighHdl.Call(this); + // move selected item to top row if not in focusf + aVscrollSB->SetThumbPos( nMapIndex / COLUMN_COUNT ); + Invalidate(); +} + +void SvxSearchCharSet::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) +{ + InitSettings(rRenderContext); + RecalculateFont(rRenderContext); + DrawChars_Impl(rRenderContext, FirstInView(), LastInView()); +} + +void SvxSearchCharSet::DrawChars_Impl(vcl::RenderContext& rRenderContext, int n1, int n2) +{ + if (n1 > LastInView() || n2 < FirstInView()) + return; + + Size aOutputSize(GetOutputSizePixel()); + if (aVscrollSB->IsVisible()) + aOutputSize.Width() -= aVscrollSB->GetOptimalSize().Width(); + + int i; + for (i = 1; i < COLUMN_COUNT; ++i) + { + rRenderContext.DrawLine(Point(nX * i + m_nXGap, 0), + Point(nX * i + m_nXGap, aOutputSize.Height())); + } + for (i = 1; i < ROW_COUNT; ++i) + { + rRenderContext.DrawLine(Point(0, nY * i + m_nYGap), + Point(aOutputSize.Width(), nY * i + m_nYGap)); + } + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const Color aWindowTextColor(rStyleSettings.GetFieldTextColor()); + Color aHighlightColor(rStyleSettings.GetHighlightColor()); + Color aHighlightTextColor(rStyleSettings.GetHighlightTextColor()); + Color aFaceColor(rStyleSettings.GetFaceColor()); + Color aLightColor(rStyleSettings.GetLightColor()); + Color aShadowColor(rStyleSettings.GetShadowColor()); + + int nTextHeight = rRenderContext.GetTextHeight(); + tools::Rectangle aBoundRect; + for (i = n1; i <= n2; ++i) + { + Point pix = MapIndexToPixel(i); + int x = pix.X(); + int y = pix.Y(); + + OUStringBuffer buf; + std::unordered_map<sal_Int32, sal_UCS4>::const_iterator got = m_aItemList.find (i); + sal_UCS4 sName; + + if(got == m_aItemList.end()) + continue; + else + sName = got->second; + + buf.appendUtf32(sName); + OUString aCharStr(buf.makeStringAndClear()); + int nTextWidth = rRenderContext.GetTextWidth(aCharStr); + int tx = x + (nX - nTextWidth + 1) / 2; + int ty = y + (nY - nTextHeight + 1) / 2; + Point aPointTxTy(tx, ty); + + // adjust position before it gets out of bounds + if (rRenderContext.GetTextBoundRect(aBoundRect, aCharStr) && !aBoundRect.IsEmpty()) + { + // zero advance width => use ink width to center glyph + if (!nTextWidth) + { + aPointTxTy.X() = x - aBoundRect.Left() + (nX - aBoundRect.GetWidth() + 1) / 2; + } + + aBoundRect += aPointTxTy; + + // shift back vertically if needed + int nYLDelta = aBoundRect.Top() - y; + int nYHDelta = (y + nY) - aBoundRect.Bottom(); + if (nYLDelta <= 0) + aPointTxTy.Y() -= nYLDelta - 1; + else if (nYHDelta <= 0) + aPointTxTy.Y() += nYHDelta - 1; + + // shift back horizontally if needed + int nXLDelta = aBoundRect.Left() - x; + int nXHDelta = (x + nX) - aBoundRect.Right(); + if (nXLDelta <= 0) + aPointTxTy.X() -= nXLDelta - 1; + else if (nXHDelta <= 0) + aPointTxTy.X() += nXHDelta - 1; + } + + Color aTextCol = rRenderContext.GetTextColor(); + if (i != nSelectedIndex) + { + rRenderContext.SetTextColor(aWindowTextColor); + rRenderContext.DrawText(aPointTxTy, aCharStr); + } + else + { + Color aLineCol = rRenderContext.GetLineColor(); + Color aFillCol = rRenderContext.GetFillColor(); + rRenderContext.SetLineColor(); + Point aPointUL(x + 1, y + 1); + if (HasFocus()) + { + rRenderContext.SetFillColor(aHighlightColor); + rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize)); + + rRenderContext.SetTextColor(aHighlightTextColor); + rRenderContext.DrawText(aPointTxTy, aCharStr); + } + else + { + rRenderContext.SetFillColor(aFaceColor); + rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize)); + + rRenderContext.SetLineColor(aLightColor); + rRenderContext.DrawLine(aPointUL, Point(x + nX - 1, y + 1)); + rRenderContext.DrawLine(aPointUL, Point(x + 1, y + nY - 1)); + + rRenderContext.SetLineColor(aShadowColor); + rRenderContext.DrawLine(Point(x + 1, y + nY - 1), Point(x + nX - 1, y + nY - 1)); + rRenderContext.DrawLine(Point(x + nX - 1, y + nY - 1), Point(x + nX - 1, y + 1)); + + rRenderContext.DrawText(aPointTxTy, aCharStr); + } + rRenderContext.SetLineColor(aLineCol); + rRenderContext.SetFillColor(aFillCol); + } + rRenderContext.SetTextColor(aTextCol); + } +} + +sal_UCS4 SvxSearchCharSet::GetSelectCharacter() const +{ + if( nSelectedIndex >= 0 ) + { + std::unordered_map<sal_Int32,sal_UCS4>::const_iterator got = m_aItemList.find (nSelectedIndex); + + if(got == m_aItemList.end()) + return 1; + else + return got->second; + } + return 1; +} + +void SvxSearchCharSet::RecalculateFont(vcl::RenderContext& rRenderContext) +{ + if (!mbRecalculateFont) + return; + + Size aSize(GetOutputSizePixel()); + long nSBWidth = aVscrollSB->GetOptimalSize().Width(); + aSize.Width() -= nSBWidth; + + vcl::Font aFont = rRenderContext.GetFont(); + aFont.SetWeight(WEIGHT_LIGHT); + aFont.SetAlignment(ALIGN_TOP); + int nFontHeight = (aSize.Height() - 5) * 2 / (3 * ROW_COUNT); + maFontSize = rRenderContext.PixelToLogic(Size(0, nFontHeight)); + aFont.SetFontSize(maFontSize); + aFont.SetTransparent(true); + rRenderContext.SetFont(aFont); + rRenderContext.GetFontCharMap(mxFontCharMap); + + nX = aSize.Width() / COLUMN_COUNT; + nY = aSize.Height() / ROW_COUNT; + + //scrollbar settings -- error + aVscrollSB->setPosSizePixel(aSize.Width(), 0, nSBWidth, aSize.Height()); + aVscrollSB->SetRangeMin(0); + int nLastRow = (nCount - 1 + COLUMN_COUNT) / COLUMN_COUNT; + aVscrollSB->SetRangeMax(nLastRow); + aVscrollSB->SetPageSize(ROW_COUNT - 1); + aVscrollSB->SetVisibleSize(ROW_COUNT); + + aVscrollSB->Show(); + + // rearrange CharSet element in sync with nX- and nY-multiples + Size aDrawSize(nX * COLUMN_COUNT, nY * ROW_COUNT); + m_nXGap = (aSize.Width() - aDrawSize.Width()) / 2; + m_nYGap = (aSize.Height() - aDrawSize.Height()) / 2; + + mbRecalculateFont = false; +} + +void SvxSearchCharSet::SelectIndex( int nNewIndex, bool bFocus ) +{ + if( !aVscrollSB ) + return; + + if ( !mxFontCharMap.is() ) + RecalculateFont( *this ); + + if( nNewIndex < 0 ) + { + aVscrollSB->SetThumbPos( 0 ); + nSelectedIndex = bFocus ? 0 : -1; + Invalidate(); + } + else if( nNewIndex < FirstInView() ) + { + // need to scroll up to see selected item + int nOldPos = aVscrollSB->GetThumbPos(); + int nDelta = (FirstInView() - nNewIndex + COLUMN_COUNT-1) / COLUMN_COUNT; + aVscrollSB->SetThumbPos( nOldPos - nDelta ); + nSelectedIndex = nNewIndex; + Invalidate(); + } + else if( nNewIndex > LastInView() ) + { + // need to scroll down to see selected item + int nOldPos = aVscrollSB->GetThumbPos(); + int nDelta = (nNewIndex - LastInView() + COLUMN_COUNT) / COLUMN_COUNT; + aVscrollSB->SetThumbPos( nOldPos + nDelta ); + if( nNewIndex < nCount ) + { + nSelectedIndex = nNewIndex; + Invalidate(); + } + else if (nOldPos != aVscrollSB->GetThumbPos()) + { + Invalidate(); + } + } + else + { + nSelectedIndex = nNewIndex; + Invalidate(); + } + + if( nSelectedIndex >= 0 ) + { + if( m_xAccessible.is() ) + { + svx::SvxShowCharSetItem* pItem = ImplGetItem(nSelectedIndex); + // Don't fire the focus event. + if ( bFocus ) + m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), makeAny(pItem->GetAccessible()) ); // this call assures that m_pItem is set + else + m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS, Any(), makeAny(pItem->GetAccessible()) ); // this call assures that m_pItem is set + + assert(pItem->m_xItem.is() && "No accessible created!"); + Any aOldAny, aNewAny; + aNewAny <<= AccessibleStateType::FOCUSED; + // Don't fire the focus event. + if ( bFocus ) + pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny ); + + aNewAny <<= AccessibleStateType::SELECTED; + pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny ); + } + } + aHighHdl.Call( this ); +} + +SvxSearchCharSet::~SvxSearchCharSet() +{ + disposeOnce(); +} + +void SvxSearchCharSet::dispose() +{ + m_aItemList.clear(); + + SvxShowCharSet::dispose(); +} + +svx::SvxShowCharSetItem* SvxSearchCharSet::ImplGetItem( int _nPos ) +{ + ItemsMap::iterator aFind = m_aItems.find(_nPos); + if ( aFind == m_aItems.end() ) + { + OSL_ENSURE(m_xAccessible.is(), "Who wants to create a child of my table without a parent?"); + std::shared_ptr<svx::SvxShowCharSetItem> xItem(new svx::SvxShowCharSetItem(*this, + m_xAccessible->getTable(), sal::static_int_cast< sal_uInt16 >(_nPos))); + aFind = m_aItems.insert(ItemsMap::value_type(_nPos, xItem)).first; + OUStringBuffer buf; + std::unordered_map<sal_Int32,sal_UCS4>::const_iterator got = m_aItemList.find (_nPos); + buf.appendUtf32( got->second ); + aFind->second->maText = buf.makeStringAndClear(); + Point pix = MapIndexToPixel( _nPos ); + aFind->second->maRect = tools::Rectangle( Point( pix.X() + 1, pix.Y() + 1 ), Size(nX-1,nY-1) ); + } + + return aFind->second.get(); +} + +sal_Int32 SvxSearchCharSet::getMaxCharCount() const +{ + return nCount; +} + +void SvxSearchCharSet::ClearPreviousData() +{ + m_aItemList.clear(); + nCount = 0; + Invalidate(); +} + +void SvxSearchCharSet::AppendCharToList(sal_UCS4 sChar) +{ + m_aItemList.insert(std::make_pair(nCount++, sChar)); +} + +void SvxSearchCharSet::AppendCharList(std::vector<sal_UCS4> sList) +{ + std::vector<sal_UCS4>::iterator list_iter = sList.begin(); + while(list_iter != sList.end()) + { + AppendCharToList(*list_iter); + list_iter++; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |