diff options
author | Caolán McNamara <caolanm@redhat.com> | 2018-04-04 14:33:16 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2018-04-07 17:46:39 +0200 |
commit | c4764345e0d326c7a9d443f5af06f06854806bdc (patch) | |
tree | fa2b95cb454e3db9a683061a6a3b2bdaef11fada | |
parent | 20a35c313496b1ca63d3bb79e0a88a3d862d8747 (diff) |
weld ScLinkedAreaDlg
Change-Id: I427e5abd76f6edfa891c9186d5822173d3fa7f7e
Reviewed-on: https://gerrit.libreoffice.org/52513
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r-- | include/svtools/inettbc.hxx | 61 | ||||
-rw-r--r-- | include/vcl/combobox.hxx | 1 | ||||
-rw-r--r-- | include/vcl/edit.hxx | 3 | ||||
-rw-r--r-- | include/vcl/weld.hxx | 11 | ||||
-rw-r--r-- | sc/inc/scabstdlg.hxx | 2 | ||||
-rw-r--r-- | sc/source/ui/attrdlg/scdlgfact.cxx | 21 | ||||
-rw-r--r-- | sc/source/ui/attrdlg/scdlgfact.hxx | 10 | ||||
-rw-r--r-- | sc/source/ui/docshell/arealink.cxx | 5 | ||||
-rw-r--r-- | sc/source/ui/inc/linkarea.hxx | 47 | ||||
-rw-r--r-- | sc/source/ui/miscdlgs/linkarea.cxx | 181 | ||||
-rw-r--r-- | sc/source/ui/view/cellsh1.cxx | 2 | ||||
-rw-r--r-- | sc/uiconfig/scalc/ui/externaldata.ui | 51 | ||||
-rw-r--r-- | starmath/source/dialog.cxx | 4 | ||||
-rw-r--r-- | svtools/source/control/inettbc.cxx | 955 | ||||
-rw-r--r-- | vcl/source/app/salvtables.cxx | 34 | ||||
-rw-r--r-- | vcl/source/control/combobox.cxx | 3 | ||||
-rw-r--r-- | vcl/source/control/edit.cxx | 8 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 159 |
18 files changed, 1369 insertions, 189 deletions
diff --git a/include/svtools/inettbc.hxx b/include/svtools/inettbc.hxx index d2b6707c0a5f..133978045054 100644 --- a/include/svtools/inettbc.hxx +++ b/include/svtools/inettbc.hxx @@ -26,7 +26,10 @@ #include <tools/urlobj.hxx> #include <vcl/combobox.hxx> +#include <vcl/idle.hxx> +#include <vcl/weld.hxx> +class MatchContext_Impl; class SvtMatchContext_Impl; class SvtURLBox_Impl; class SVT_DLLPUBLIC SvtURLBox : public ComboBox @@ -88,6 +91,64 @@ public: { return ( !aPlaceHolder.isEmpty() ) && ( aPlaceHolder == sToMatch ); } }; +class SVT_DLLPUBLIC URLBox +{ + friend class MatchContext_Impl; + friend class SvtURLBox_Impl; + + Idle aChangedIdle; + OUString aBaseURL; + OUString aPlaceHolder; + rtl::Reference< MatchContext_Impl > pCtx; + std::unique_ptr<SvtURLBox_Impl> pImpl; + INetProtocol eSmartProtocol; + bool bAutoCompleteMode : 1; + bool bOnlyDirectories : 1; + bool bHistoryDisabled : 1; + + std::unique_ptr<weld::ComboBoxText> m_xWidget; + + SVT_DLLPRIVATE bool ProcessKey( const vcl::KeyCode& rCode ); + DECL_DLLPRIVATE_LINK( TryAutoComplete, Timer*, void); + SVT_DLLPRIVATE void UpdatePicklistForSmartProtocol_Impl(); + DECL_DLLPRIVATE_LINK( ChangedHdl, weld::ComboBoxText&, void); + DECL_DLLPRIVATE_LINK( FocusInHdl, weld::Widget&, void); + DECL_DLLPRIVATE_LINK( FocusOutHdl, weld::Widget&, void); + SVT_DLLPRIVATE void Init(); + +public: + URLBox(weld::ComboBoxText* pWidget); + ~URLBox(); + + void SetText(const OUString& rStr) { m_xWidget->set_entry_text(rStr); } + void Clear() { m_xWidget->clear(); } + void connect_entry_activate(const Link<weld::ComboBoxText&, void>& rLink) { m_xWidget->connect_entry_activate(rLink); } + void append_text(const OUString& rStr) { m_xWidget->append_text(rStr); } + OUString get_text(int nIndex) { return m_xWidget->get_text(nIndex); } + void select_entry_region(int nStartPos, int nEndPos) { m_xWidget->select_entry_region(nStartPos, nEndPos); } + void EnableAutocomplete() { m_xWidget->set_entry_completion(true); } + + void SetBaseURL( const OUString& rURL ); + const OUString& GetBaseURL() const { return aBaseURL; } + void SetOnlyDirectories( bool bDir ); + INetProtocol GetSmartProtocol() const { return eSmartProtocol; } + void SetSmartProtocol( INetProtocol eProt ); + OUString GetURL(); + void DisableHistory(); + + void UpdatePickList( ); + + static OUString ParseSmart( const OUString& aText, const OUString& aBaseURL ); + + void SetFilter(const OUString& _sFilter); + + void SetPlaceHolder( const OUString& sPlaceHolder ) + { aPlaceHolder = sPlaceHolder; } + const OUString& GetPlaceHolder() { return aPlaceHolder; } + bool MatchesPlaceHolder( const OUString& sToMatch ) const + { return ( !aPlaceHolder.isEmpty() ) && ( aPlaceHolder == sToMatch ); } +}; + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/vcl/combobox.hxx b/include/vcl/combobox.hxx index ddfe65186a18..2f7efd3e006e 100644 --- a/include/vcl/combobox.hxx +++ b/include/vcl/combobox.hxx @@ -121,6 +121,7 @@ public: const Link<ComboBox&,void>& GetSelectHdl() const; void SetDoubleClickHdl(const Link<ComboBox&,void>& rLink); const Link<ComboBox&,void>& GetDoubleClickHdl() const; + void SetEntryActivateHdl(const Link<Edit&,void>& rLink); Size CalcMinimumSize() const override; virtual Size GetOptimalSize() const override; diff --git a/include/vcl/edit.hxx b/include/vcl/edit.hxx index d4a9b418c7ab..c55a19eb02c0 100644 --- a/include/vcl/edit.hxx +++ b/include/vcl/edit.hxx @@ -97,6 +97,7 @@ private: Link<Edit&,void> maModifyHdl; Link<Edit&,void> maUpdateDataHdl; Link<Edit&,void> maAutocompleteHdl; + Link<Edit&,void> maActivateHdl; std::unique_ptr<VclBuilder> mpUIBuilder; css::uno::Reference<css::i18n::XExtendedInputSequenceChecker> mxISC; @@ -237,6 +238,8 @@ public: virtual const Link<Edit&,void>& GetModifyHdl() const { return maModifyHdl; } virtual void SetUpdateDataHdl( const Link<Edit&,void>& rLink ) { maUpdateDataHdl = rLink; } + void SetActivateHdl(const Link<Edit&,void>& rLink) { maActivateHdl = rLink; } + void SetSubEdit( Edit* pEdit ); Edit* GetSubEdit() const { return mpSubEdit; } diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index 52f247921289..33efcd3e0e41 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -197,6 +197,7 @@ private: protected: Link<ComboBoxText&, void> m_aChangeHdl; + Link<ComboBoxText&, void> m_aEntryActivateHdl; void signal_changed() { m_aChangeHdl.Call(*this); } @@ -227,8 +228,12 @@ public: virtual void set_entry_text(const OUString& rStr) = 0; virtual void select_entry_region(int nStartPos, int nEndPos) = 0; virtual bool get_entry_selection_bounds(int& rStartPos, int& rEndPos) = 0; + virtual void set_entry_completion(bool bEnable) = 0; - virtual void unset_entry_completion() = 0; + void connect_entry_activate(const Link<ComboBoxText&, void>& rLink) + { + m_aEntryActivateHdl = rLink; + } void save_value() { m_sSavedValue = get_active_text(); } @@ -262,6 +267,7 @@ public: virtual void set_top_entry(int pos) = 0; virtual void clear() = 0; virtual OUString get_selected() const = 0; + virtual std::vector<OUString> get_selected_rows() const = 0; OUString get_selected_id() const { return get_id(get_selected_index()); } virtual int get_selected_index() const = 0; virtual OUString get(int pos) const = 0; @@ -271,6 +277,9 @@ public: virtual void freeze() = 0; virtual void thaw() = 0; + virtual void set_selection_mode(bool bMultiple) = 0; + virtual int count_selected_rows() const = 0; + void connect_changed(const Link<TreeView&, void>& rLink) { m_aChangeHdl = rLink; } void connect_row_activated(const Link<TreeView&, void>& rLink) { m_aRowActivatedHdl = rLink; } diff --git a/sc/inc/scabstdlg.hxx b/sc/inc/scabstdlg.hxx index d90cc4ed9810..ccdd419f2a69 100644 --- a/sc/inc/scabstdlg.hxx +++ b/sc/inc/scabstdlg.hxx @@ -454,7 +454,7 @@ public: virtual VclPtr<AbstractScSelEntryDlg > CreateScSelEntryDlg ( vcl::Window* pParent, const std::vector<OUString> &rEntryList ) = 0; - virtual VclPtr<AbstractScLinkedAreaDlg> CreateScLinkedAreaDlg(vcl::Window* pParent) = 0; + virtual VclPtr<AbstractScLinkedAreaDlg> CreateScLinkedAreaDlg(weld::Window* pParent) = 0; virtual VclPtr<AbstractScMetricInputDlg> CreateScMetricInputDlg ( vcl::Window* pParent, const OString& sDialogName, diff --git a/sc/source/ui/attrdlg/scdlgfact.cxx b/sc/source/ui/attrdlg/scdlgfact.cxx index b263ca09cc94..101ad4c56d96 100644 --- a/sc/source/ui/attrdlg/scdlgfact.cxx +++ b/sc/source/ui/attrdlg/scdlgfact.cxx @@ -115,7 +115,7 @@ AbstractScLinkedAreaDlg_Impl::~AbstractScLinkedAreaDlg_Impl() } short AbstractScLinkedAreaDlg_Impl::Execute() { - return pDlg->Execute(); + return m_xDlg->run(); } void ScAbstractTabDialog_Impl::SetCurPageId( sal_uInt16 nId ) @@ -393,32 +393,32 @@ void AbstractScLinkedAreaDlg_Impl::InitFromOldLink( const OUString& rFile, const const OUString& rOptions, const OUString& rSource, sal_uLong nRefresh ) { - pDlg->InitFromOldLink( rFile, rFilter, rOptions, rSource, nRefresh); + m_xDlg->InitFromOldLink( rFile, rFilter, rOptions, rSource, nRefresh); } OUString AbstractScLinkedAreaDlg_Impl::GetURL() { - return pDlg->GetURL(); + return m_xDlg->GetURL(); } OUString AbstractScLinkedAreaDlg_Impl::GetFilter() { - return pDlg->GetFilter(); + return m_xDlg->GetFilter(); } OUString AbstractScLinkedAreaDlg_Impl::GetOptions() { - return pDlg->GetOptions(); + return m_xDlg->GetOptions(); } OUString AbstractScLinkedAreaDlg_Impl::GetSource() { - return pDlg->GetSource(); + return m_xDlg->GetSource(); } -sal_uLong AbstractScLinkedAreaDlg_Impl::GetRefresh() +sal_uLong AbstractScLinkedAreaDlg_Impl::GetRefresh() { - return pDlg->GetRefresh(); + return m_xDlg->GetRefresh(); } ScConditionalFormatList* AbstractScCondFormatManagerDlg_Impl::GetConditionalFormatList() @@ -742,10 +742,9 @@ VclPtr<AbstractScSelEntryDlg> ScAbstractDialogFactory_Impl::CreateScSelEntryDlg return VclPtr<AbstractScSelEntryDlg_Impl>::Create( pDlg ); } -VclPtr<AbstractScLinkedAreaDlg> ScAbstractDialogFactory_Impl::CreateScLinkedAreaDlg(vcl::Window* pParent) +VclPtr<AbstractScLinkedAreaDlg> ScAbstractDialogFactory_Impl::CreateScLinkedAreaDlg(weld::Window* pParent) { - VclPtr<ScLinkedAreaDlg> pDlg = VclPtr<ScLinkedAreaDlg>::Create( pParent ); - return VclPtr<AbstractScLinkedAreaDlg_Impl>::Create( pDlg ); + return VclPtr<AbstractScLinkedAreaDlg_Impl>::Create(new ScLinkedAreaDlg(pParent)); } VclPtr<AbstractScMetricInputDlg> ScAbstractDialogFactory_Impl::CreateScMetricInputDlg ( vcl::Window* pParent, diff --git a/sc/source/ui/attrdlg/scdlgfact.hxx b/sc/source/ui/attrdlg/scdlgfact.hxx index 33d471fec7af..86cf786de353 100644 --- a/sc/source/ui/attrdlg/scdlgfact.hxx +++ b/sc/source/ui/attrdlg/scdlgfact.hxx @@ -240,10 +240,12 @@ class AbstractScSelEntryDlg_Impl : public AbstractScSelEntryDlg class AbstractScLinkedAreaDlg_Impl : public AbstractScLinkedAreaDlg { - ScopedVclPtr<ScLinkedAreaDlg> pDlg; + std::unique_ptr<ScLinkedAreaDlg> m_xDlg; public: - explicit AbstractScLinkedAreaDlg_Impl( ScLinkedAreaDlg* p) - : pDlg(p) {} + explicit AbstractScLinkedAreaDlg_Impl(ScLinkedAreaDlg* p) + : m_xDlg(p) + { + } virtual ~AbstractScLinkedAreaDlg_Impl() override; virtual short Execute() override; virtual void InitFromOldLink( const OUString& rFile, const OUString& rFilter, @@ -459,7 +461,7 @@ public: virtual VclPtr<AbstractScSelEntryDlg> CreateScSelEntryDlg ( vcl::Window* pParent, const std::vector<OUString> &rEntryList ) override; - virtual VclPtr<AbstractScLinkedAreaDlg> CreateScLinkedAreaDlg(vcl::Window* pParent) override; + virtual VclPtr<AbstractScLinkedAreaDlg> CreateScLinkedAreaDlg(weld::Window* pParent) override; virtual VclPtr<AbstractScMetricInputDlg> CreateScMetricInputDlg ( vcl::Window* pParent, const OString& sDialogName, diff --git a/sc/source/ui/docshell/arealink.cxx b/sc/source/ui/docshell/arealink.cxx index 8a75234cc686..c1def55db4d3 100644 --- a/sc/source/ui/docshell/arealink.cxx +++ b/sc/source/ui/docshell/arealink.cxx @@ -73,14 +73,13 @@ ScAreaLink::~ScAreaLink() StopRefreshTimer(); } -void ScAreaLink::Edit(weld::Window*, const Link<SvBaseLink&,void>& /* rEndEditHdl */ ) +void ScAreaLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& /* rEndEditHdl */ ) { // use own dialog instead of SvBaseLink::Edit... ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); OSL_ENSURE(pFact, "ScAbstractFactory create fail!"); -//TODO ScopedVclPtr<AbstractScLinkedAreaDlg> pDlg(pFact->CreateScLinkedAreaDlg(pParent)); - ScopedVclPtr<AbstractScLinkedAreaDlg> pDlg(pFact->CreateScLinkedAreaDlg(nullptr)); + ScopedVclPtr<AbstractScLinkedAreaDlg> pDlg(pFact->CreateScLinkedAreaDlg(pParent)); OSL_ENSURE(pDlg, "Dialog create fail!"); pDlg->InitFromOldLink( aFileName, aFilterName, aOptions, aSourceArea, GetRefreshDelay() ); if ( pDlg->Execute() == RET_OK ) diff --git a/sc/source/ui/inc/linkarea.hxx b/sc/source/ui/inc/linkarea.hxx index e0fef1f20f9e..564568419d30 100644 --- a/sc/source/ui/inc/linkarea.hxx +++ b/sc/source/ui/inc/linkarea.hxx @@ -20,12 +20,7 @@ #ifndef INCLUDED_SC_SOURCE_UI_INC_LINKAREA_HXX #define INCLUDED_SC_SOURCE_UI_INC_LINKAREA_HXX -#include <vcl/dialog.hxx> - -#include <vcl/button.hxx> -#include <vcl/field.hxx> -#include <vcl/fixed.hxx> -#include <vcl/lstbox.hxx> +#include <vcl/weld.hxx> #include <sfx2/objsh.hxx> #include <svtools/inettbc.hxx> @@ -34,27 +29,26 @@ namespace sfx2 { class FileDialogHelper; } class ScDocShell; -class ScLinkedAreaDlg : public ModalDialog +class ScLinkedAreaDlg : public weld::GenericDialogController { private: - VclPtr<SvtURLBox> m_pCbUrl; - VclPtr<PushButton> m_pBtnBrowse; - VclPtr<ListBox> m_pLbRanges; - VclPtr<CheckBox> m_pBtnReload; - VclPtr<NumericField> m_pNfDelay; - VclPtr<FixedText> m_pFtSeconds; - VclPtr<OKButton> m_pBtnOk; - - ScDocShell* pSourceShell; - std::unique_ptr<sfx2::DocumentInserter> pDocInserter; - + ScDocShell* m_pSourceShell; + std::unique_ptr<sfx2::DocumentInserter> m_xDocInserter; SfxObjectShellRef aSourceRef; - DECL_LINK(FileHdl, ComboBox&, void); - DECL_LINK(BrowseHdl, Button*, void); - DECL_LINK(RangeHdl, ListBox&, void); - DECL_LINK(ReloadHdl, Button*, void); - DECL_LINK( DialogClosedHdl, sfx2::FileDialogHelper*, void ); + std::unique_ptr<URLBox> m_xCbUrl; + std::unique_ptr<weld::Button> m_xBtnBrowse; + std::unique_ptr<weld::TreeView> m_xLbRanges; + std::unique_ptr<weld::CheckButton> m_xBtnReload; + std::unique_ptr<weld::SpinButton> m_xNfDelay; + std::unique_ptr<weld::Label> m_xFtSeconds; + std::unique_ptr<weld::Button> m_xBtnOk; + + DECL_LINK(FileHdl, weld::ComboBoxText&, void); + DECL_LINK(BrowseHdl, weld::Button&, void); + DECL_LINK(RangeHdl, weld::TreeView&, void); + DECL_LINK(ReloadHdl, weld::Button&, void); + DECL_LINK(DialogClosedHdl, sfx2::FileDialogHelper*, void); void UpdateSourceRanges(); void UpdateEnable(); @@ -62,9 +56,8 @@ private: const OUString& rOptions ); public: - ScLinkedAreaDlg( vcl::Window* pParent ); - virtual ~ScLinkedAreaDlg() override; - virtual void dispose() override; + ScLinkedAreaDlg(weld::Window* pParent); + virtual ~ScLinkedAreaDlg() override; void InitFromOldLink( const OUString& rFile, const OUString& rFilter, const OUString& rOptions, const OUString& rSource, @@ -74,7 +67,7 @@ public: OUString GetFilter(); // may be empty OUString GetOptions(); // filter options OUString GetSource(); // separated by ";" - sal_uLong GetRefresh(); // 0 if disabled + sal_uLong GetRefresh(); // 0 if disabled }; #endif diff --git a/sc/source/ui/miscdlgs/linkarea.cxx b/sc/source/ui/miscdlgs/linkarea.cxx index 257f07f24747..96cfee9ab2dc 100644 --- a/sc/source/ui/miscdlgs/linkarea.cxx +++ b/sc/source/ui/miscdlgs/linkarea.cxx @@ -37,62 +37,47 @@ #include <docsh.hxx> #include <tablink.hxx> -ScLinkedAreaDlg::ScLinkedAreaDlg(vcl::Window* pParent) - : ModalDialog(pParent, "ExternalDataDialog", "modules/scalc/ui/externaldata.ui") - , pSourceShell(nullptr) - , pDocInserter(nullptr) - +ScLinkedAreaDlg::ScLinkedAreaDlg(weld::Window* pParent) + : GenericDialogController(pParent, "modules/scalc/ui/externaldata.ui", "ExternalDataDialog") + , m_pSourceShell(nullptr) + , m_xCbUrl(new URLBox(m_xBuilder->weld_combo_box_text("url"))) + , m_xBtnBrowse(m_xBuilder->weld_button("browse")) + , m_xLbRanges(m_xBuilder->weld_tree_view("ranges")) + , m_xBtnReload(m_xBuilder->weld_check_button("reload")) + , m_xNfDelay(m_xBuilder->weld_spin_button("delay")) + , m_xFtSeconds(m_xBuilder->weld_label("secondsft")) + , m_xBtnOk(m_xBuilder->weld_button("ok")) { - get(m_pCbUrl, "url"); - get(m_pLbRanges, "ranges"); - m_pLbRanges->EnableMultiSelection(true); - m_pLbRanges->SetDropDownLineCount(8); - get(m_pBtnBrowse, "browse"); - get(m_pBtnReload, "reload"); - get(m_pNfDelay, "delay"); - get(m_pFtSeconds, "secondsft"); - get(m_pBtnOk, "ok"); - - m_pCbUrl->SetSelectHdl( LINK( this, ScLinkedAreaDlg, FileHdl ) ); - m_pBtnBrowse->SetClickHdl( LINK( this, ScLinkedAreaDlg, BrowseHdl ) ); - m_pLbRanges->SetSelectHdl( LINK( this, ScLinkedAreaDlg, RangeHdl ) ); - m_pBtnReload->SetClickHdl( LINK( this, ScLinkedAreaDlg, ReloadHdl ) ); + m_xLbRanges->set_selection_mode(true); + + m_xCbUrl->connect_entry_activate(LINK( this, ScLinkedAreaDlg, FileHdl)); + m_xBtnBrowse->connect_clicked(LINK( this, ScLinkedAreaDlg, BrowseHdl)); + m_xLbRanges->connect_changed(LINK( this, ScLinkedAreaDlg, RangeHdl)); + m_xLbRanges->set_size_request(m_xLbRanges->get_approximate_digit_width() * 54, + m_xLbRanges->get_height_rows(5)); + m_xBtnReload->connect_clicked(LINK( this, ScLinkedAreaDlg, ReloadHdl)); UpdateEnable(); } ScLinkedAreaDlg::~ScLinkedAreaDlg() { - disposeOnce(); -} - -void ScLinkedAreaDlg::dispose() -{ - // pSourceShell is deleted by aSourceRef - m_pCbUrl.clear(); - m_pBtnBrowse.clear(); - m_pLbRanges.clear(); - m_pBtnReload.clear(); - m_pNfDelay.clear(); - m_pFtSeconds.clear(); - m_pBtnOk.clear(); - ModalDialog::dispose(); } #define FILTERNAME_HTML "HTML (StarCalc)" #define FILTERNAME_QUERY "calc_HTML_WebQuery" -IMPL_LINK_NOARG(ScLinkedAreaDlg, BrowseHdl, Button*, void) +IMPL_LINK_NOARG(ScLinkedAreaDlg, BrowseHdl, weld::Button&, void) { - pDocInserter.reset( new sfx2::DocumentInserter(GetFrameWeld(), ScDocShell::Factory().GetFactoryName()) ); - pDocInserter->StartExecuteModal( LINK( this, ScLinkedAreaDlg, DialogClosedHdl ) ); + m_xDocInserter.reset( new sfx2::DocumentInserter(m_xDialog.get(), ScDocShell::Factory().GetFactoryName()) ); + m_xDocInserter->StartExecuteModal( LINK( this, ScLinkedAreaDlg, DialogClosedHdl ) ); } -IMPL_LINK_NOARG(ScLinkedAreaDlg, FileHdl, ComboBox&, void) +IMPL_LINK_NOARG(ScLinkedAreaDlg, FileHdl, weld::ComboBoxText&, void) { - OUString aEntered = m_pCbUrl->GetURL(); - if (pSourceShell) + OUString aEntered = m_xCbUrl->GetURL(); + if (m_pSourceShell) { - SfxMedium* pMed = pSourceShell->GetMedium(); + SfxMedium* pMed = m_pSourceShell->GetMedium(); if ( aEntered == pMed->GetName() ) { // already loaded - nothing to do @@ -104,7 +89,7 @@ IMPL_LINK_NOARG(ScLinkedAreaDlg, FileHdl, ComboBox&, void) OUString aOptions; // get filter name by looking at the file content (bWithContent = true) // Break operation if any error occurred inside. - if (!ScDocumentLoader::GetFilterName( aEntered, aFilter, aOptions, true, true )) + if (!ScDocumentLoader::GetFilterName( aEntered, aFilter, aOptions, true, false )) return; // #i53241# replace HTML filter with DataQuery filter @@ -119,32 +104,32 @@ IMPL_LINK_NOARG(ScLinkedAreaDlg, FileHdl, ComboBox&, void) void ScLinkedAreaDlg::LoadDocument( const OUString& rFile, const OUString& rFilter, const OUString& rOptions ) { - if ( pSourceShell ) + if (m_pSourceShell) { // unload old document - pSourceShell->DoClose(); - pSourceShell = nullptr; + m_pSourceShell->DoClose(); + m_pSourceShell = nullptr; aSourceRef.clear(); } if ( !rFile.isEmpty() ) { - WaitObject aWait( this ); + weld::WaitObject aWait(m_xDialog.get()); OUString aNewFilter = rFilter; OUString aNewOptions = rOptions; SfxErrorContext aEc( ERRCTX_SFX_OPENDOC, rFile ); - ScDocumentLoader aLoader( rFile, aNewFilter, aNewOptions, 0, GetFrameWeld() ); // with interaction - pSourceShell = aLoader.GetDocShell(); - if ( pSourceShell ) + ScDocumentLoader aLoader( rFile, aNewFilter, aNewOptions, 0, m_xDialog.get() ); // with interaction + m_pSourceShell = aLoader.GetDocShell(); + if (m_pSourceShell) { - ErrCode nErr = pSourceShell->GetErrorCode(); + ErrCode nErr = m_pSourceShell->GetErrorCode(); if (nErr) ErrorHandler::HandleError( nErr ); // including warnings - aSourceRef = pSourceShell; + aSourceRef = m_pSourceShell; aLoader.ReleaseDocRef(); // don't call DoClose in DocLoader dtor } } @@ -155,13 +140,13 @@ void ScLinkedAreaDlg::InitFromOldLink( const OUString& rFile, const OUString& rF sal_uLong nRefresh ) { LoadDocument( rFile, rFilter, rOptions ); - if (pSourceShell) + if (m_pSourceShell) { - SfxMedium* pMed = pSourceShell->GetMedium(); - m_pCbUrl->SetText( pMed->GetName() ); + SfxMedium* pMed = m_pSourceShell->GetMedium(); + m_xCbUrl->SetText(pMed->GetName()); } else - m_pCbUrl->SetText( EMPTY_OUSTRING ); + m_xCbUrl->SetText(EMPTY_OUSTRING); UpdateSourceRanges(); @@ -169,23 +154,23 @@ void ScLinkedAreaDlg::InitFromOldLink( const OUString& rFile, const OUString& rF for ( sal_Int32 i=0; i<nRangeCount; i++ ) { OUString aRange = rSource.getToken(i,';'); - m_pLbRanges->SelectEntry( aRange ); + m_xLbRanges->select(aRange); } bool bDoRefresh = (nRefresh != 0); - m_pBtnReload->Check( bDoRefresh ); + m_xBtnReload->set_active(bDoRefresh); if (bDoRefresh) - m_pNfDelay->SetValue( nRefresh ); + m_xNfDelay->set_value(nRefresh); UpdateEnable(); } -IMPL_LINK_NOARG(ScLinkedAreaDlg, RangeHdl, ListBox&, void) +IMPL_LINK_NOARG(ScLinkedAreaDlg, RangeHdl, weld::TreeView&, void) { UpdateEnable(); } -IMPL_LINK_NOARG(ScLinkedAreaDlg, ReloadHdl, Button*, void) +IMPL_LINK_NOARG(ScLinkedAreaDlg, ReloadHdl, weld::Button&, void) { UpdateEnable(); } @@ -195,10 +180,10 @@ IMPL_LINK( ScLinkedAreaDlg, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, if ( _pFileDlg->GetError() != ERRCODE_NONE ) return; - SfxMedium* pMed = pDocInserter->CreateMedium(); + SfxMedium* pMed = m_xDocInserter->CreateMedium(); if ( pMed ) { - WaitObject aWait( this ); + weld::WaitObject aWait(m_xDialog.get()); // replace HTML filter with DataQuery filter const OUString aHTMLFilterName( FILTERNAME_HTML ); @@ -216,30 +201,30 @@ IMPL_LINK( ScLinkedAreaDlg, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, // ERRCTX_SFX_OPENDOC -> "Error loading document" SfxErrorContext aEc( ERRCTX_SFX_OPENDOC, pMed->GetName() ); - if (pSourceShell) - pSourceShell->DoClose(); // deleted when assigning aSourceRef + if (m_pSourceShell) + m_pSourceShell->DoClose(); // deleted when assigning aSourceRef pMed->UseInteractionHandler( true ); // to enable the filter options dialog - pSourceShell = new ScDocShell; - aSourceRef = pSourceShell; - pSourceShell->DoLoad( pMed ); + m_pSourceShell = new ScDocShell; + aSourceRef = m_pSourceShell; + m_pSourceShell->DoLoad( pMed ); - ErrCode nErr = pSourceShell->GetErrorCode(); + ErrCode nErr = m_pSourceShell->GetErrorCode(); if (nErr) ErrorHandler::HandleError( nErr ); // including warnings - if ( !pSourceShell->GetError() ) // only errors + if (!m_pSourceShell->GetError()) // only errors { - m_pCbUrl->SetText( pMed->GetName() ); + m_xCbUrl->SetText(pMed->GetName()); } else { - pSourceShell->DoClose(); - pSourceShell = nullptr; + m_pSourceShell->DoClose(); + m_pSourceShell = nullptr; aSourceRef.clear(); - m_pCbUrl->SetText( EMPTY_OUSTRING ); + m_xCbUrl->SetText(EMPTY_OUSTRING); } } @@ -252,46 +237,46 @@ IMPL_LINK( ScLinkedAreaDlg, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void ScLinkedAreaDlg::UpdateSourceRanges() { - m_pLbRanges->SetUpdateMode(false); + m_xLbRanges->freeze(); - m_pLbRanges->Clear(); - if ( pSourceShell ) + m_xLbRanges->clear(); + if ( m_pSourceShell ) { - std::shared_ptr<const SfxFilter> pFilter = pSourceShell->GetMedium()->GetFilter(); + std::shared_ptr<const SfxFilter> pFilter = m_pSourceShell->GetMedium()->GetFilter(); if (pFilter && pFilter->GetFilterName() == SC_TEXT_CSV_FILTER_NAME) { // Insert dummy All range to have something selectable. - m_pLbRanges->InsertEntry("CSV_all"); + m_xLbRanges->append_text("CSV_all"); } - ScAreaNameIterator aIter( &pSourceShell->GetDocument() ); + ScAreaNameIterator aIter(&m_pSourceShell->GetDocument()); ScRange aDummy; OUString aName; while ( aIter.Next( aName, aDummy ) ) - m_pLbRanges->InsertEntry( aName ); + m_xLbRanges->append_text(aName); } - m_pLbRanges->SetUpdateMode(true); + m_xLbRanges->thaw(); - if ( m_pLbRanges->GetEntryCount() == 1 ) - m_pLbRanges->SelectEntryPos(0); + if (m_xLbRanges->n_children() == 1) + m_xLbRanges->select(0); } void ScLinkedAreaDlg::UpdateEnable() { - bool bEnable = ( pSourceShell && m_pLbRanges->GetSelectedEntryCount() ); - m_pBtnOk->Enable( bEnable ); + bool bEnable = ( m_pSourceShell && m_xLbRanges->count_selected_rows() ); + m_xBtnOk->set_sensitive(bEnable); - bool bReload = m_pBtnReload->IsChecked(); - m_pNfDelay->Enable( bReload ); - m_pFtSeconds->Enable( bReload ); + bool bReload = m_xBtnReload->get_active(); + m_xNfDelay->set_sensitive(bReload); + m_xFtSeconds->set_sensitive(bReload); } OUString ScLinkedAreaDlg::GetURL() { - if (pSourceShell) + if (m_pSourceShell) { - SfxMedium* pMed = pSourceShell->GetMedium(); + SfxMedium* pMed = m_pSourceShell->GetMedium(); return pMed->GetName(); } return EMPTY_OUSTRING; @@ -299,9 +284,9 @@ OUString ScLinkedAreaDlg::GetURL() OUString ScLinkedAreaDlg::GetFilter() { - if (pSourceShell) + if (m_pSourceShell) { - SfxMedium* pMed = pSourceShell->GetMedium(); + SfxMedium* pMed = m_pSourceShell->GetMedium(); return pMed->GetFilter()->GetFilterName(); } return OUString(); @@ -309,9 +294,9 @@ OUString ScLinkedAreaDlg::GetFilter() OUString ScLinkedAreaDlg::GetOptions() { - if (pSourceShell) + if (m_pSourceShell) { - SfxMedium* pMed = pSourceShell->GetMedium(); + SfxMedium* pMed = m_pSourceShell->GetMedium(); return ScDocumentLoader::GetOptions( *pMed ); } return OUString(); @@ -320,20 +305,20 @@ OUString ScLinkedAreaDlg::GetOptions() OUString ScLinkedAreaDlg::GetSource() { OUStringBuffer aBuf; - const sal_Int32 nCount = m_pLbRanges->GetSelectedEntryCount(); - for (sal_Int32 i=0; i<nCount; ++i) + std::vector<OUString> aSelection = m_xLbRanges->get_selected_rows(); + for (size_t i = 0; i < aSelection.size(); ++i) { if (i > 0) aBuf.append(';'); - aBuf.append(m_pLbRanges->GetSelectedEntry(i)); + aBuf.append(aSelection[i]); } return aBuf.makeStringAndClear(); } sal_uLong ScLinkedAreaDlg::GetRefresh() { - if ( m_pBtnReload->IsChecked() ) - return sal::static_int_cast<sal_uLong>( m_pNfDelay->GetValue() ); + if (m_xBtnReload->get_active()) + return sal::static_int_cast<sal_uLong>(m_xNfDelay->get_value()); else return 0; // disabled } diff --git a/sc/source/ui/view/cellsh1.cxx b/sc/source/ui/view/cellsh1.cxx index 80b3ccf6d888..97892de03912 100644 --- a/sc/source/ui/view/cellsh1.cxx +++ b/sc/source/ui/view/cellsh1.cxx @@ -2626,7 +2626,7 @@ void ScCellShell::ExecuteEdit( SfxRequest& rReq ) pImpl->m_pLinkedDlg.disposeAndClear(); pImpl->m_pLinkedDlg = - pFact->CreateScLinkedAreaDlg(pTabViewShell->GetDialogParent()); + pFact->CreateScLinkedAreaDlg(pTabViewShell->GetFrameWeld()); OSL_ENSURE(pImpl->m_pLinkedDlg, "Dialog create fail!"); delete pImpl->m_pRequest; pImpl->m_pRequest = new SfxRequest( rReq ); diff --git a/sc/uiconfig/scalc/ui/externaldata.ui b/sc/uiconfig/scalc/ui/externaldata.ui index 040213842c3a..449cce77cd7e 100644 --- a/sc/uiconfig/scalc/ui/externaldata.ui +++ b/sc/uiconfig/scalc/ui/externaldata.ui @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.18.3 --> +<!-- Generated with glade 3.20.4 --> <interface domain="sc"> <requires lib="gtk+" version="3.18"/> - <requires lib="LibreOffice" version="1.0"/> <object class="GtkAdjustment" id="adjustment1"> <property name="lower">1</property> <property name="upper">99999</property> @@ -10,10 +9,21 @@ <property name="step_increment">1</property> <property name="page_increment">10</property> </object> + <object class="GtkListStore" id="liststore1"> + <columns> + <!-- column-name text --> + <column type="gchararray"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> <object class="GtkDialog" id="ExternalDataDialog"> <property name="can_focus">False</property> <property name="border_width">6</property> <property name="title" translatable="yes" context="externaldata|ExternalDataDialog">External Data</property> + <property name="modal">True</property> + <property name="default_width">0</property> + <property name="default_height">0</property> <property name="type_hint">dialog</property> <child internal-child="vbox"> <object class="GtkBox" id="dialog-vbox1"> @@ -82,6 +92,7 @@ <property name="visible">True</property> <property name="can_focus">False</property> <property name="hexpand">True</property> + <property name="vexpand">True</property> <property name="orientation">vertical</property> <property name="spacing">12</property> <child> @@ -112,21 +123,20 @@ <property name="hexpand">True</property> <property name="spacing">12</property> <child> - <object class="svtlo-SvtURLBox" id="url"> + <object class="GtkComboBoxText" id="url"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="tooltip_text" translatable="yes" context="externaldata|url|tooltip_text">Enter the URL of the source document in the local file system or Internet here.</property> - <property name="hexpand">True</property> <property name="has_entry">True</property> - <property name="max_width_chars">48</property> <child internal-child="entry"> - <object class="GtkEntry" id="URLBox-entry2"> - <property name="can_focus">False</property> + <object class="GtkEntry"> + <property name="visible">True</property> + <property name="can_focus">True</property> </object> </child> </object> <packing> - <property name="expand">False</property> + <property name="expand">True</property> <property name="fill">True</property> <property name="position">0</property> </packing> @@ -199,21 +209,35 @@ <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkScrolledWindow" id="scrolledwindow1"> + <object class="GtkScrolledWindow"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="hexpand">True</property> <property name="vexpand">True</property> <property name="shadow_type">in</property> <child> - <object class="GtkTreeView" id="ranges:border"> + <object class="GtkTreeView" id="ranges"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="hexpand">True</property> <property name="vexpand">True</property> + <property name="model">liststore1</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">False</property> + <property name="search_column">0</property> + <property name="show_expanders">False</property> <child internal-child="selection"> <object class="GtkTreeSelection" id="treeview-selection1"/> </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn1"> + <child> + <object class="GtkCellRendererText" id="cellrenderertext1"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> </object> </child> </object> @@ -302,9 +326,9 @@ <object class="GtkLabel" id="label2"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="xalign">0.47999998927116394</property> <property name="label" translatable="yes" context="externaldata|label2">_Available Tables/Ranges</property> <property name="use_underline">True</property> + <property name="xalign">0.47999998927116394</property> <attributes> <attribute name="weight" value="bold"/> </attributes> @@ -331,5 +355,8 @@ <action-widget response="-6">cancel</action-widget> <action-widget response="-11">help</action-widget> </action-widgets> + <child> + <placeholder/> + </child> </object> </interface> diff --git a/starmath/source/dialog.cxx b/starmath/source/dialog.cxx index 38a4415b652d..176ad2352804 100644 --- a/starmath/source/dialog.cxx +++ b/starmath/source/dialog.cxx @@ -1981,8 +1981,8 @@ SmSymDefineDialog::SmSymDefineDialog(weld::Window* pParent, OutputDevice *pFntLi { // auto completion is troublesome since that symbols character also gets automatically selected in the // display and if the user previously selected a character to define/redefine that one this is bad - m_xOldSymbols->unset_entry_completion(); - m_xSymbols->unset_entry_completion(); + m_xOldSymbols->set_entry_completion(false); + m_xSymbols->set_entry_completion(false); FillFonts(); if (m_xFonts->get_count() > 0) diff --git a/svtools/source/control/inettbc.cxx b/svtools/source/control/inettbc.cxx index 8dab51f2ba1f..a1b3d744748d 100644 --- a/svtools/source/control/inettbc.cxx +++ b/svtools/source/control/inettbc.cxx @@ -120,6 +120,39 @@ public: void Stop(); }; +class MatchContext_Impl: public salhelper::Thread +{ + static ::osl::Mutex* pDirMutex; + + std::vector<OUString> aPickList; + std::vector<OUString> aCompletions; + std::vector<OUString> aURLs; + svtools::AsynchronLink aLink; + OUString aBaseURL; + OUString aText; + URLBox* pBox; + bool bOnlyDirectories; + + osl::Mutex mutex_; + bool stopped_; + css::uno::Reference< css::ucb::XCommandProcessor > processor_; + sal_Int32 commandId_; + + DECL_LINK( Select_Impl, void*, void ); + + virtual ~MatchContext_Impl() override; + virtual void execute() override; + void doExecute(); + void Insert( const OUString& rCompletion, const OUString& rURL, bool bForce = false); + void ReadFolder( const OUString& rURL, const OUString& rMatch, bool bSmart ); + static void FillPicklist(std::vector<OUString>& rPickList); + +public: + MatchContext_Impl( URLBox* pBoxP, const OUString& rText ); + void Stop(); +}; + + namespace { struct theSvtMatchContextMutex @@ -456,6 +489,319 @@ void SvtMatchContext_Impl::ReadFolder( const OUString& rURL, } } +MatchContext_Impl::MatchContext_Impl(URLBox* pBoxP, const OUString& rText) + : Thread( "MatchContext_Impl" ) + , aLink( LINK( this, MatchContext_Impl, Select_Impl ) ) + , aBaseURL( pBoxP->aBaseURL ) + , aText( rText ) + , pBox( pBoxP ) + , bOnlyDirectories( pBoxP->bOnlyDirectories ) + , stopped_(false) + , commandId_(0) +{ + aLink.CreateMutex(); + + FillPicklist( aPickList ); +} + +MatchContext_Impl::~MatchContext_Impl() +{ + aLink.ClearPendingCall(); +} + +void MatchContext_Impl::FillPicklist(std::vector<OUString>& rPickList) +{ + // Read the history of picks + Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST ); + sal_uInt32 nCount = seqPicklist.getLength(); + + for( sal_uInt32 nItem=0; nItem < nCount; nItem++ ) + { + Sequence< PropertyValue > seqPropertySet = seqPicklist[ nItem ]; + + OUString sTitle; + INetURLObject aURL; + + sal_uInt32 nPropertyCount = seqPropertySet.getLength(); + + for( sal_uInt32 nProperty=0; nProperty < nPropertyCount; nProperty++ ) + { + if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_TITLE ) + { + seqPropertySet[nProperty].Value >>= sTitle; + aURL.SetURL( sTitle ); + rPickList.insert(rPickList.begin() + nItem, aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset)); + break; + } + } + } +} + +void MatchContext_Impl::Stop() +{ + css::uno::Reference< css::ucb::XCommandProcessor > proc; + sal_Int32 id(0); + { + osl::MutexGuard g(mutex_); + if (!stopped_) { + stopped_ = true; + proc = processor_; + id = commandId_; + } + } + if (proc.is()) { + proc->abort(id); + } + terminate(); +} + +void MatchContext_Impl::execute( ) +{ + doExecute(); + aLink.Call( this ); +} + + +// This method is called via AsynchronLink, so it has the SolarMutex and +// calling solar code ( VCL ... ) is safe. It is called when the thread is +// terminated ( finished work or stopped ). Cancelling the thread via +// Cancellable does not discard the information gained so far, it +// inserts all collected completions into the listbox. + +IMPL_LINK_NOARG( MatchContext_Impl, Select_Impl, void*, void ) +{ + // avoid recursion through cancel button + { + osl::MutexGuard g(mutex_); + if (stopped_) { + // Completion was stopped, no display: + return; + } + } + + pBox->bAutoCompleteMode = true; + + // insert all completed strings into the listbox + pBox->Clear(); + + for (auto const& completion : aCompletions) + { + // convert the file into an URL + OUString sURL; + osl::FileBase::getFileURLFromSystemPath(completion, sURL); + // note: if this doesn't work, we're not interested in: we're checking the + // untouched sCompletion then + + if ( !sURL.isEmpty() && !sURL.endsWith("/") ) + { + OUString sUpperURL( sURL.toAsciiUpperCase() ); + + if ( ::std::none_of( pBox->pImpl->m_aFilters.begin(), + pBox->pImpl->m_aFilters.end(), + FilterMatch( sUpperURL ) ) ) + { // this URL is not allowed + continue; + } + } + + pBox->append_text(completion); + } + + pBox->EnableAutocomplete(); + + // transfer string lists to listbox and forget them + pBox->pImpl->aURLs = aURLs; + pBox->pImpl->aCompletions = aCompletions; + aURLs.clear(); + aCompletions.clear(); + + // the box has this control as a member so we have to set that member + // to zero before deleting ourself. + pBox->pCtx.clear(); +} + +void MatchContext_Impl::Insert( const OUString& rCompletion, + const OUString& rURL, + bool bForce ) +{ + if( !bForce ) + { + // avoid doubles + if(find(aCompletions.begin(), aCompletions.end(), rCompletion) != aCompletions.end()) + return; + } + + aCompletions.push_back(rCompletion); + aURLs.push_back(rURL); +} + + +void MatchContext_Impl::ReadFolder( const OUString& rURL, + const OUString& rMatch, + bool bSmart ) +{ + // check folder to scan + if( !UCBContentHelper::IsFolder( rURL ) ) + return; + + bool bPureHomePath = false; +#ifdef UNX + bPureHomePath = aText.startsWith( "~" ) && aText.indexOf( '/' ) == -1; +#endif + + bool bExectMatch = bPureHomePath + || aText == "." + || aText.endsWith("/.") + || aText.endsWith("/.."); + + // for pure home paths ( ~username ) the '.' at the end of rMatch + // means that it points to root catalog + // this is done only for file contents since home paths parsing is useful only for them + if ( bPureHomePath && rMatch == "file:///." ) + { + // a home that refers to / + + OUString aNewText( aText ); + aNewText += "/"; + Insert( aNewText, rURL, true ); + + return; + } + + // string to match with + INetURLObject aMatchObj( rMatch ); + OUString aMatchName; + + if ( rURL != aMatchObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) + { + aMatchName = aMatchObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); + + // matching is always done case insensitive, but completion will be case sensitive and case preserving + aMatchName = aMatchName.toAsciiLowerCase(); + + // if the matchstring ends with a slash, we must search for this also + if ( rMatch.endsWith("/") ) + aMatchName += "/"; + } + + sal_Int32 nMatchLen = aMatchName.getLength(); + + INetURLObject aFolderObj( rURL ); + DBG_ASSERT( aFolderObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL!" ); + + try + { + Content aCnt( aFolderObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), + new ::ucbhelper::CommandEnvironment( uno::Reference< XInteractionHandler >(), + uno::Reference< XProgressHandler >() ), + comphelper::getProcessComponentContext() ); + uno::Reference< XResultSet > xResultSet; + Sequence< OUString > aProps(2); + OUString* pProps = aProps.getArray(); + pProps[0] = "Title"; + pProps[1] = "IsFolder"; + + try + { + uno::Reference< XDynamicResultSet > xDynResultSet; + ResultSetInclude eInclude = INCLUDE_FOLDERS_AND_DOCUMENTS; + if ( bOnlyDirectories ) + eInclude = INCLUDE_FOLDERS_ONLY; + + xDynResultSet = aCnt.createDynamicCursor( aProps, eInclude ); + + uno::Reference < XAnyCompareFactory > xCompare; + uno::Reference < XSortedDynamicResultSetFactory > xSRSFac = + SortedDynamicResultSetFactory::create( ::comphelper::getProcessComponentContext() ); + + Sequence< NumberedSortingInfo > aSortInfo( 2 ); + NumberedSortingInfo* pInfo = aSortInfo.getArray(); + pInfo[ 0 ].ColumnIndex = 2; + pInfo[ 0 ].Ascending = false; + pInfo[ 1 ].ColumnIndex = 1; + pInfo[ 1 ].Ascending = true; + + uno::Reference< XDynamicResultSet > xDynamicResultSet; + xDynamicResultSet = + xSRSFac->createSortedDynamicResultSet( xDynResultSet, aSortInfo, xCompare ); + + if ( xDynamicResultSet.is() ) + { + xResultSet = xDynamicResultSet->getStaticResultSet(); + } + } + catch( css::uno::Exception& ) {} + + if ( xResultSet.is() ) + { + uno::Reference< XRow > xRow( xResultSet, UNO_QUERY ); + uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY ); + + try + { + while ( schedule() && xResultSet->next() ) + { + OUString aURL = xContentAccess->queryContentIdentifierString(); + OUString aTitle = xRow->getString(1); + bool bIsFolder = xRow->getBoolean(2); + + // matching is always done case insensitive, but completion will be case sensitive and case preserving + aTitle = aTitle.toAsciiLowerCase(); + + if ( + !nMatchLen || + (bExectMatch && aMatchName == aTitle) || + (!bExectMatch && aTitle.startsWith(aMatchName)) + ) + { + // all names fit if matchstring is empty + INetURLObject aObj( aURL ); + sal_Unicode aDelimiter = '/'; + if ( bSmart ) + // when parsing is done "smart", the delimiter must be "guessed" + aObj.getFSysPath( static_cast<FSysStyle>(FSysStyle::Detect & ~FSysStyle::Vos), &aDelimiter ); + + if ( bIsFolder ) + aObj.setFinalSlash(); + + // get the last name of the URL + OUString aMatch = aObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); + OUString aInput( aText ); + if ( nMatchLen ) + { + if (aText.endsWith(".") || bPureHomePath) + { + // if a "special folder" URL was typed, don't touch the user input + aMatch = aMatch.copy( nMatchLen ); + } + else + { + // make the user input case preserving + DBG_ASSERT( aInput.getLength() >= nMatchLen, "Suspicious Matching!" ); + aInput = aInput.copy( 0, aInput.getLength() - nMatchLen ); + } + } + + aInput += aMatch; + + // folders should get a final slash automatically + if ( bIsFolder ) + aInput += OUStringLiteral1(aDelimiter); + + Insert( aInput, aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), true ); + } + } + } + catch( css::uno::Exception& ) + { + } + } + } + catch( css::uno::Exception& ) + { + } +} + OUString SvtURLBox::ParseSmart( const OUString& _aText, const OUString& _aBaseURL ) { OUString aMatch; @@ -584,9 +930,9 @@ void SvtMatchContext_Impl::doExecute() OUString aMainURL( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); // Disable autocompletion for anything but the (local) file // system (for which access is hopefully fast), as the logic of - // how SvtMatchContext_Impl is used requires this code to run to + // how MatchContext_Impl is used requires this code to run to // completion before further user input is processed, and even - // SvtMatchContext_Impl::Stop does not guarantee a speedy + // MatchContext_Impl::Stop does not guarantee a speedy // return: if ( !aMainURL.isEmpty() && aURLObject.GetProtocol() == INetProtocol::File ) @@ -661,7 +1007,252 @@ void SvtMatchContext_Impl::doExecute() } } if ( folder ) - Insert( aText, aMatch ); + Insert( aText, aMatch ); + else + // otherwise the parent folder will be taken + aURLObject.removeSegment(); + + // scan directory and insert all matches + ReadFolder( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aMatch, eProt == INetProtocol::NotValid ); + } + } + } + } + + if ( bOnlyDirectories ) + // don't scan history picklist if only directories are allowed, picklist contains only files + return; + + bool bFull = false; + + INetURLObject aCurObj; + OUString aCurString, aCurMainURL; + INetURLObject aObj; + aObj.SetSmartProtocol( eSmartProt == INetProtocol::NotValid ? INetProtocol::Http : eSmartProt ); + for( ;; ) + { + for(std::vector<OUString>::iterator i = aPickList.begin(); schedule() && i != aPickList.end(); ++i) + { + aCurObj.SetURL(*i); + aCurObj.SetSmartURL( aCurObj.GetURLNoPass()); + aCurMainURL = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if( eProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eProt ) + continue; + + if( eSmartProt != INetProtocol::NotValid && aCurObj.GetProtocol() != eSmartProt ) + continue; + + switch( aCurObj.GetProtocol() ) + { + case INetProtocol::Http: + case INetProtocol::Https: + case INetProtocol::Ftp: + { + if( eProt == INetProtocol::NotValid && !bFull ) + { + aObj.SetSmartURL( aText ); + if( aObj.GetURLPath().getLength() > 1 ) + continue; + } + + aCurString = aCurMainURL; + if( eProt == INetProtocol::NotValid ) + { + // try if text matches the scheme + OUString aScheme( INetURLObject::GetScheme( aCurObj.GetProtocol() ) ); + if ( aScheme.startsWithIgnoreAsciiCase( aText ) && aText.getLength() < aScheme.getLength() ) + { + if( bFull ) + aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + else + { + aCurObj.SetMark( "" ); + aCurObj.SetParam( "" ); + aCurObj.SetURLPath( "" ); + aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + + Insert( aMatch, aMatch ); + } + + // now try smart matching + aCurString = aCurString.copy( aScheme.getLength() ); + } + + if( aCurString.startsWithIgnoreAsciiCase( aText ) ) + { + if( bFull ) + aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + else + { + aCurObj.SetMark( "" ); + aCurObj.SetParam( "" ); + aCurObj.SetURLPath( "" ); + aMatch = aCurObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + + OUString aURL( aMatch ); + if( eProt == INetProtocol::NotValid ) + aMatch = aMatch.copy( INetURLObject::GetScheme( aCurObj.GetProtocol() ).getLength() ); + + if( aText.getLength() < aMatch.getLength() ) + Insert( aMatch, aURL ); + + continue; + } + break; + } + default: + { + if( bFull ) + continue; + + if( aCurMainURL.startsWith(aText) ) + { + if( aText.getLength() < aCurMainURL.getLength() ) + Insert( aCurMainURL, aCurMainURL ); + + continue; + } + break; + } + } + } + + if( !bFull ) + bFull = true; + else + break; + } +} + +void MatchContext_Impl::doExecute() +{ + ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() ); + { + // have we been stopped while we were waiting for the mutex? + osl::MutexGuard g(mutex_); + if (stopped_) { + return; + } + } + + // Reset match lists + aCompletions.clear(); + aURLs.clear(); + + // check for input + if ( aText.isEmpty() ) + return; + + if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 ) + // no autocompletion for wildcards + return; + + OUString aMatch; + INetProtocol eProt = INetURLObject::CompareProtocolScheme( aText ); + INetProtocol eBaseProt = INetURLObject::CompareProtocolScheme( aBaseURL ); + if ( aBaseURL.isEmpty() ) + eBaseProt = INetURLObject::CompareProtocolScheme( SvtPathOptions().GetWorkPath() ); + INetProtocol eSmartProt = pBox->GetSmartProtocol(); + + // if the user input is a valid URL, go on with it + // otherwise it could be parsed smart with a predefined smart protocol + // ( or if this is not set with the protocol of a predefined base URL ) + if( eProt == INetProtocol::NotValid || eProt == eSmartProt || (eSmartProt == INetProtocol::NotValid && eProt == eBaseProt) ) + { + // not stopped yet ? + if( schedule() ) + { + if ( eProt == INetProtocol::NotValid ) + aMatch = SvtURLBox::ParseSmart( aText, aBaseURL ); + else + aMatch = aText; + if ( !aMatch.isEmpty() ) + { + INetURLObject aURLObject( aMatch ); + OUString aMainURL( aURLObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + // Disable autocompletion for anything but the (local) file + // system (for which access is hopefully fast), as the logic of + // how MatchContext_Impl is used requires this code to run to + // completion before further user input is processed, and even + // MatchContext_Impl::Stop does not guarantee a speedy + // return: + if ( !aMainURL.isEmpty() + && aURLObject.GetProtocol() == INetProtocol::File ) + { + // if text input is a directory, it must be part of the match list! Until then it is scanned + bool folder = false; + if (aURLObject.hasFinalSlash()) { + try { + css::uno::Reference< css::uno::XComponentContext > + ctx(comphelper::getProcessComponentContext()); + css::uno::Reference< + css::ucb::XUniversalContentBroker > ucb( + css::ucb::UniversalContentBroker::create( + ctx)); + css::uno::Sequence< css::beans::Property > prop(1); + prop[0].Name = "IsFolder"; + prop[0].Handle = -1; + prop[0].Type = cppu::UnoType< bool >::get(); + css::uno::Any res; + css::uno::Reference< css::ucb::XCommandProcessor > + proc( + ucb->queryContent( + ucb->createContentIdentifier(aMainURL)), + css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::ucb::XCommandProcessor2 > + proc2(proc, css::uno::UNO_QUERY); + sal_Int32 id = proc->createCommandIdentifier(); + try { + { + osl::MutexGuard g(mutex_); + processor_ = proc; + commandId_ = id; + } + res = proc->execute( + css::ucb::Command( + "getPropertyValues", -1, + css::uno::makeAny(prop)), + id, + css::uno::Reference< + css::ucb::XCommandEnvironment >()); + } catch (...) { + if (proc2.is()) { + try { + proc2->releaseCommandIdentifier(id); + } catch (css::uno::RuntimeException & e) { + SAL_WARN("svtools.control", "ignoring " << e); + } + } + throw; + } + if (proc2.is()) { + proc2->releaseCommandIdentifier(id); + } + { + osl::MutexGuard g(mutex_); + processor_.clear(); + // At least the neon-based WebDAV UCP does not + // properly support aborting commands, so return + // anyway now if an abort request had been + // ignored and the command execution only + // returned "successfully" after some timeout: + if (stopped_) { + return; + } + } + css::uno::Reference< css::sdbc::XRow > row( + res, css::uno::UNO_QUERY_THROW); + folder = row->getBoolean(1) && !row->wasNull(); + } catch (css::uno::Exception & e) { + SAL_WARN("svtools.control", "ignoring " << e); + return; + } + } + if (folder) + Insert( aText, aMatch ); else // otherwise the parent folder will be taken aURLObject.removeSegment(); @@ -1325,4 +1916,362 @@ void SvtURLBox::SetFilter(const OUString& _sFilter) FilterMatch::createWildCardFilterList(_sFilter,pImpl->m_aFilters); } +//-- + +OUString URLBox::ParseSmart( const OUString& _aText, const OUString& _aBaseURL ) +{ + OUString aMatch; + OUString aText = _aText; + OUString aBaseURL = _aBaseURL; + + // parse ~ for Unix systems + // does nothing for Windows + if( !SvtURLBox_Impl::TildeParsing( aText, aBaseURL ) ) + return OUString(); + + if( !aBaseURL.isEmpty() ) + { + INetProtocol eBaseProt = INetURLObject::CompareProtocolScheme( aBaseURL ); + + // if a base URL is set the string may be parsed relative + if( aText.startsWith( "/" ) ) + { + // text starting with slashes means absolute file URLs + OUString aTemp = INetURLObject::GetScheme( eBaseProt ); + + // file URL must be correctly encoded! + OUString aTextURL = INetURLObject::encode( aText, INetURLObject::PART_FPATH, + INetURLObject::EncodeMechanism::All ); + aTemp += aTextURL; + + INetURLObject aTmp( aTemp ); + if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid ) + aMatch = aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + else + { + OUString aSmart( aText ); + INetURLObject aObj( aBaseURL ); + + // HRO: I suppose this hack should only be done for Windows !!!??? +#ifdef _WIN32 + // HRO: INetURLObject::smatRel2Abs does not recognize '\\' as a relative path + // but in case of "\\\\" INetURLObject is right - this is an absolute path ! + + if( aText.startsWith("\\") && (aText.getLength() < 2 || aText[ 1 ] != '\\') ) + { + // cut to first segment + OUString aTmp = INetURLObject::GetScheme( eBaseProt ); + aTmp += "/"; + aTmp += aObj.getName( 0, true, INetURLObject::DecodeMechanism::WithCharset ); + aObj.SetURL( aTmp ); + + aSmart = aSmart.copy(1); + } +#endif + // base URL must be a directory ! + aObj.setFinalSlash(); + + // take base URL and append current input + bool bWasAbsolute = false; +#ifdef UNX + // encode file URL correctly + aSmart = INetURLObject::encode( aSmart, INetURLObject::PART_FPATH, INetURLObject::EncodeMechanism::All ); +#endif + INetURLObject aTmp( aObj.smartRel2Abs( aSmart, bWasAbsolute ) ); + + if ( aText.endsWith(".") ) + // INetURLObject appends a final slash for the directories "." and "..", this is a bug! + // Remove it as a workaround + aTmp.removeFinalSlash(); + if ( !aTmp.HasError() && aTmp.GetProtocol() != INetProtocol::NotValid ) + aMatch = aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + } + else + { + OUString aTmpMatch; + osl::FileBase::getFileURLFromSystemPath( aText, aTmpMatch ); + aMatch = aTmpMatch; + } + + return aMatch; +} + +IMPL_LINK_NOARG(URLBox, TryAutoComplete, Timer *, void) +{ + OUString aCurText = m_xWidget->get_active_text(); + int nStartPos, nEndPos; + m_xWidget->get_entry_selection_bounds(nStartPos, nEndPos); + if (nEndPos != aCurText.getLength()) + return; + aCurText = aCurText.copy(0, nStartPos); + if (!aCurText.isEmpty()) + { + if (pCtx.is()) + { + pCtx->Stop(); + pCtx->join(); + pCtx.clear(); + } + pCtx = new MatchContext_Impl(this, aCurText); + pCtx->launch(); + } + else + m_xWidget->clear(); +} + +URLBox::URLBox(weld::ComboBoxText* pWidget) + : eSmartProtocol(INetProtocol::NotValid) + , bAutoCompleteMode(false) + , bOnlyDirectories(false) + , bHistoryDisabled(false) + , m_xWidget(pWidget) +{ + Init(); + + m_xWidget->connect_focus_in(LINK(this, URLBox, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, URLBox, FocusOutHdl)); + m_xWidget->connect_changed(LINK(this, URLBox, ChangedHdl)); + + aChangedIdle.SetInvokeHandler(LINK(this, URLBox, TryAutoComplete)); + aChangedIdle.SetDebugName("svtools::URLBox aChangedIdle"); +} + +void URLBox::Init() +{ + pImpl.reset( new SvtURLBox_Impl ); + + m_xWidget->set_entry_completion(false); + + UpdatePicklistForSmartProtocol_Impl(); +} + +URLBox::~URLBox() +{ + if (pCtx.is()) + { + pCtx->Stop(); + pCtx->join(); + } +} + +void URLBox::UpdatePickList( ) +{ + if (pCtx.is()) + { + pCtx->Stop(); + pCtx->join(); + pCtx.clear(); + } + OUString sText = m_xWidget->get_active_text(); + if (!sText.isEmpty()) + { + pCtx = new MatchContext_Impl( this, sText ); + pCtx->launch(); + } +} + +void URLBox::SetSmartProtocol( INetProtocol eProt ) +{ + if ( eSmartProtocol != eProt ) + { + eSmartProtocol = eProt; + UpdatePicklistForSmartProtocol_Impl(); + } +} + +void URLBox::UpdatePicklistForSmartProtocol_Impl() +{ + m_xWidget->clear(); + if ( bHistoryDisabled ) + return; + + // read history pick list + Sequence< Sequence< PropertyValue > > seqPicklist = SvtHistoryOptions().GetList( ePICKLIST ); + sal_uInt32 nCount = seqPicklist.getLength(); + INetURLObject aCurObj; + + for( sal_uInt32 nItem=0; nItem < nCount; nItem++ ) + { + Sequence< PropertyValue > seqPropertySet = seqPicklist[ nItem ]; + + OUString sURL; + + sal_uInt32 nPropertyCount = seqPropertySet.getLength(); + + for( sal_uInt32 nProperty=0; nProperty < nPropertyCount; nProperty++ ) + { + if( seqPropertySet[nProperty].Name == HISTORY_PROPERTYNAME_URL ) + { + seqPropertySet[nProperty].Value >>= sURL; + aCurObj.SetURL( sURL ); + + if ( !sURL.isEmpty() && ( eSmartProtocol != INetProtocol::NotValid ) ) + { + if( aCurObj.GetProtocol() != eSmartProtocol ) + break; + } + + OUString aURL( aCurObj.GetMainURL( INetURLObject::DecodeMechanism::WithCharset ) ); + + if ( !aURL.isEmpty() ) + { + bool bFound = aURL.endsWith("/"); + if ( !bFound ) + { + OUString aUpperURL( aURL ); + aUpperURL = aUpperURL.toAsciiUpperCase(); + + bFound = ::std::any_of(pImpl->m_aFilters.begin(), + pImpl->m_aFilters.end(), + FilterMatch( aUpperURL ) ); + } + if ( bFound ) + { + OUString aFile; + if (osl::FileBase::getSystemPathFromFileURL(aURL, aFile) == osl::FileBase::E_None) + m_xWidget->append_text(aFile); + else + m_xWidget->append_text(aURL); + } + } + break; + } + } + } +} + +IMPL_LINK_NOARG(URLBox, ChangedHdl, weld::ComboBoxText&, void) +{ + aChangedIdle.Start(); //launch this to happen on idle after cursor position will have been set +} + +IMPL_LINK_NOARG(URLBox, FocusInHdl, weld::Widget&, void) +{ +#ifndef UNX + // pb: don't select automatically on unix #93251# + m_xWidget->select_entry_region(0, -1); +#endif +} + +IMPL_LINK_NOARG(URLBox, FocusOutHdl, weld::Widget&, void) +{ + if (pCtx.is()) + { + pCtx->Stop(); + pCtx->join(); + pCtx.clear(); + } +} + +void URLBox::SetOnlyDirectories( bool bDir ) +{ + bOnlyDirectories = bDir; + if (bOnlyDirectories) + m_xWidget->clear(); +} + +OUString URLBox::GetURL() +{ + // wait for end of autocompletion + ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() ); + + OUString aText(m_xWidget->get_active_text()); + if ( MatchesPlaceHolder( aText ) ) + return aPlaceHolder; + + // try to get the right case preserving URL from the list of URLs + for(std::vector<OUString>::iterator i = pImpl->aCompletions.begin(), j = pImpl->aURLs.begin(); i != pImpl->aCompletions.end() && j != pImpl->aURLs.end(); ++i, ++j) + { + if((*i) == aText) + return *j; + } + +#ifdef _WIN32 + // erase trailing spaces on Windows since thay are invalid on this OS and + // most of the time they are inserted by accident via copy / paste + aText = comphelper::string::stripEnd(aText, ' '); + if ( aText.isEmpty() ) + return aText; + // #i9739# +#endif + + INetURLObject aObj( aText ); + if( aText.indexOf( '*' ) != -1 || aText.indexOf( '?' ) != -1 ) + { + // no autocompletion for wildcards + INetURLObject aTempObj; + if ( eSmartProtocol != INetProtocol::NotValid ) + aTempObj.SetSmartProtocol( eSmartProtocol ); + if ( aTempObj.SetSmartURL( aText ) ) + return aTempObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + else + return aText; + } + + if ( aObj.GetProtocol() == INetProtocol::NotValid ) + { + OUString aName = ParseSmart( aText, aBaseURL ); + aObj.SetURL(aName); + OUString aURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + if ( aURL.isEmpty() ) + // aText itself is invalid, and even together with aBaseURL, it could not + // made valid -> no chance + return aText; + + bool bSlash = aObj.hasFinalSlash(); + { + const OUString aPropName("CasePreservingURL"); + + OUString aFileURL; + + Any aAny = UCBContentHelper::GetProperty(aURL, aPropName); + bool success = (aAny >>= aFileURL); + OUString aTitle; + if(success) + aTitle = INetURLObject(aFileURL).getName( + INetURLObject::LAST_SEGMENT, + true, + INetURLObject::DecodeMechanism::WithCharset ); + else + success = + UCBContentHelper::GetTitle(aURL,&aTitle); + + if( success && aTitle != "/" && aTitle != "." ) + { + aObj.SetName( aTitle ); + if ( bSlash ) + aObj.setFinalSlash(); + } + } + } + + return aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); +} + +void URLBox::DisableHistory() +{ + bHistoryDisabled = true; + UpdatePicklistForSmartProtocol_Impl(); +} + +void URLBox::SetBaseURL( const OUString& rURL ) +{ + ::osl::MutexGuard aGuard( theSvtMatchContextMutex::get() ); + + // Reset match lists + pImpl->aCompletions.clear(); + pImpl->aURLs.clear(); + + aBaseURL = rURL; +} + +void URLBox::SetFilter(const OUString& _sFilter) +{ + pImpl->m_aFilters.clear(); + FilterMatch::createWildCardFilterList(_sFilter,pImpl->m_aFilters); +} + + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index 905e29f5b33d..637f5651d66b 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -1091,6 +1091,16 @@ public: return m_xTreeView->GetSelectedEntry(); } + virtual std::vector<OUString> get_selected_rows() const override + { + std::vector<OUString> aRows; + + for (sal_Int32 i = 0; i < m_xTreeView->GetSelectedEntryCount(); ++i) + aRows.push_back(m_xTreeView->GetSelectedEntry(i)); + + return aRows; + } + virtual OUString get(int pos) const override { return m_xTreeView->GetEntry(pos); @@ -1127,6 +1137,16 @@ public: m_xTreeView->SetUpdateMode(true); } + virtual void set_selection_mode(bool bMultiple) override + { + m_xTreeView->EnableMultiSelection(bMultiple); + } + + virtual int count_selected_rows() const override + { + return m_xTreeView->GetSelectedEntryCount(); + } + virtual int get_height_rows(int nRows) const override { return m_xTreeView->CalcWindowSizePixel(nRows); @@ -1597,7 +1617,7 @@ public: return false; } - virtual void unset_entry_completion() override + virtual void set_entry_completion(bool) override { assert(false); } @@ -1617,11 +1637,13 @@ class SalInstanceComboBoxTextWithEdit : public SalInstanceComboBoxText<ComboBox> { private: DECL_LINK(ChangeHdl, Edit&, void); + DECL_LINK(EntryActivateHdl, Edit&, void); public: SalInstanceComboBoxTextWithEdit(ComboBox* pComboBoxText, bool bTakeOwnership) : SalInstanceComboBoxText<ComboBox>(pComboBoxText, bTakeOwnership) { m_xComboBoxText->SetModifyHdl(LINK(this, SalInstanceComboBoxTextWithEdit, ChangeHdl)); + m_xComboBoxText->SetEntryActivateHdl(LINK(this, SalInstanceComboBoxTextWithEdit, EntryActivateHdl)); } virtual void set_entry_error(bool bError) override @@ -1642,9 +1664,9 @@ public: m_xComboBoxText->SetText(rText); } - virtual void unset_entry_completion() override + virtual void set_entry_completion(bool bEnable) override { - m_xComboBoxText->EnableAutocomplete(false); + m_xComboBoxText->EnableAutocomplete(bEnable); } virtual void select_entry_region(int nStartPos, int nEndPos) override @@ -1662,6 +1684,7 @@ public: virtual ~SalInstanceComboBoxTextWithEdit() override { + m_xComboBoxText->SetEntryActivateHdl(Link<Edit&, void>()); m_xComboBoxText->SetModifyHdl(Link<Edit&, void>()); } }; @@ -1671,6 +1694,11 @@ IMPL_LINK_NOARG(SalInstanceComboBoxTextWithEdit, ChangeHdl, Edit&, void) signal_changed(); } +IMPL_LINK_NOARG(SalInstanceComboBoxTextWithEdit, EntryActivateHdl, Edit&, void) +{ + m_aEntryActivateHdl.Call(*this); +} + class SalInstanceBuilder : public weld::Builder { private: diff --git a/vcl/source/control/combobox.cxx b/vcl/source/control/combobox.cxx index 7b14c8da64c2..1cb790dada78 100644 --- a/vcl/source/control/combobox.cxx +++ b/vcl/source/control/combobox.cxx @@ -58,6 +58,7 @@ struct ComboBox::Impl sal_Int32 m_nMaxWidthChars; Link<ComboBox&,void> m_SelectHdl; Link<ComboBox&,void> m_DoubleClickHdl; + Link<ComboBox&,void> m_EntryActivateHdl; explicit Impl(ComboBox & rThis) : m_rThis(rThis) @@ -986,6 +987,8 @@ void ComboBox::SetDoubleClickHdl(const Link<ComboBox&,void>& rLink) { m_pImpl->m const Link<ComboBox&,void>& ComboBox::GetDoubleClickHdl() const { return m_pImpl->m_DoubleClickHdl; } +void ComboBox::SetEntryActivateHdl(const Link<Edit&,void>& rLink) { m_pImpl->m_pSubEdit->SetActivateHdl(rLink); } + long ComboBox::CalcWindowSizePixel(sal_uInt16 nLines) const { return m_pImpl->m_pImplLB->GetEntryHeight() * nLines; diff --git a/vcl/source/control/edit.cxx b/vcl/source/control/edit.cxx index 42dd22296585..8d3bfa665596 100644 --- a/vcl/source/control/edit.cxx +++ b/vcl/source/control/edit.cxx @@ -1677,6 +1677,14 @@ bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt ) } break; + case KEY_RETURN: + if (maActivateHdl.IsSet()) + { + maActivateHdl.Call(*this); + bDone = true; + } + break; + default: { if ( IsCharInput( rKEvt ) ) diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index 133406925d79..269a0741973f 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -1184,12 +1184,14 @@ private: static void signalFocusIn(GtkWidget*, GdkEvent*, gpointer widget) { GtkInstanceWidget* pThis = static_cast<GtkInstanceWidget*>(widget); + SolarMutexGuard aGuard; pThis->signal_focus_in(); } static void signalFocusOut(GtkWidget*, GdkEvent*, gpointer widget) { GtkInstanceWidget* pThis = static_cast<GtkInstanceWidget*>(widget); + SolarMutexGuard aGuard; pThis->signal_focus_out(); } @@ -1446,6 +1448,7 @@ private: static void signalActivate(GtkMenuItem* pItem, gpointer widget) { GtkInstanceMenu* pThis = static_cast<GtkInstanceMenu*>(widget); + SolarMutexGuard aGuard; pThis->signal_activate(pItem); } @@ -2077,6 +2080,7 @@ private: static void signalVAdjustValueChanged(GtkAdjustment*, gpointer widget) { GtkInstanceScrolledWindow* pThis = static_cast<GtkInstanceScrolledWindow*>(widget); + SolarMutexGuard aGuard; pThis->signal_vadjustment_changed(); } @@ -2181,6 +2185,7 @@ private: static void signalSwitchPage(GtkNotebook*, GtkWidget*, guint nNewPage, gpointer widget) { GtkInstanceNotebook* pThis = static_cast<GtkInstanceNotebook*>(widget); + SolarMutexGuard aGuard; pThis->signal_switch_page(nNewPage); } @@ -2278,6 +2283,7 @@ private: static void signalClicked(GtkButton*, gpointer widget) { GtkInstanceButton* pThis = static_cast<GtkInstanceButton*>(widget); + SolarMutexGuard aGuard; pThis->signal_clicked(); } @@ -2360,6 +2366,7 @@ private: static void signalToggled(GtkToggleButton*, gpointer widget) { GtkInstanceToggleButton* pThis = static_cast<GtkInstanceToggleButton*>(widget); + SolarMutexGuard aGuard; pThis->signal_toggled(); } public: @@ -2438,6 +2445,7 @@ private: static void signalChanged(GtkEntry*, gpointer widget) { GtkInstanceEntry* pThis = static_cast<GtkInstanceEntry*>(widget); + SolarMutexGuard aGuard; pThis->signal_changed(); } @@ -2445,6 +2453,7 @@ private: gint* position, gpointer widget) { GtkInstanceEntry* pThis = static_cast<GtkInstanceEntry*>(widget); + SolarMutexGuard aGuard; pThis->signal_insert_text(pEntry, pNewText, nNewTextLength, position); } @@ -2580,12 +2589,14 @@ private: static void signalChanged(GtkTreeView*, gpointer widget) { GtkInstanceTreeView* pThis = static_cast<GtkInstanceTreeView*>(widget); + SolarMutexGuard aGuard; pThis->signal_changed(); } static void signalRowActivated(GtkTreeView*, GtkTreePath*, GtkTreeViewColumn*, gpointer widget) { GtkInstanceTreeView* pThis = static_cast<GtkInstanceTreeView*>(widget); + SolarMutexGuard aGuard; pThis->signal_row_activated(); } @@ -2721,6 +2732,8 @@ public: virtual OUString get_selected() const override { + assert(gtk_tree_selection_get_mode(gtk_tree_view_get_selection(m_pTreeView)) == GTK_SELECTION_SINGLE); + OUString sRet; GtkTreeIter iter; GtkTreeModel* pModel; @@ -2734,6 +2747,23 @@ public: return sRet; } + virtual std::vector<OUString> get_selected_rows() const override + { + std::vector<OUString> aRows; + + GtkTreeModel* pModel; + GList* pList = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(m_pTreeView), &pModel); + for (GList* pItem = g_list_first(pList); pItem; pItem = g_list_next(pItem)) + { + GtkTreePath* path = static_cast<GtkTreePath*>(pItem->data); + int nRow = gtk_tree_path_get_indices(path)[0]; + aRows.push_back(get(nRow)); + } + g_list_free_full(pList, reinterpret_cast<GDestroyNotify>(gtk_tree_path_free)); + + return aRows; + } + virtual OUString get(int pos) const override { return get(pos, 0); @@ -2808,6 +2838,16 @@ public: gtk_widget_set_size_request(m_pWidget, nWidth, nHeight); } + virtual void set_selection_mode(bool bMultiple) override + { + gtk_tree_selection_set_mode(gtk_tree_view_get_selection(m_pTreeView), bMultiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE); + } + + virtual int count_selected_rows() const override + { + return gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(m_pTreeView)); + } + virtual void disable_notify_events() override { g_signal_handler_block(gtk_tree_view_get_selection(m_pTreeView), m_nChangedSignalId); @@ -2840,12 +2880,14 @@ private: static void signalValueChanged(GtkSpinButton*, gpointer widget) { GtkInstanceSpinButton* pThis = static_cast<GtkInstanceSpinButton*>(widget); + SolarMutexGuard aGuard; pThis->signal_value_changed(); } static gboolean signalOutput(GtkSpinButton*, gpointer widget) { GtkInstanceSpinButton* pThis = static_cast<GtkInstanceSpinButton*>(widget); + SolarMutexGuard aGuard; return pThis->signal_output(); } @@ -3078,6 +3120,7 @@ private: static gboolean signalDraw(GtkWidget*, cairo_t* cr, gpointer widget) { GtkInstanceDrawingArea* pThis = static_cast<GtkInstanceDrawingArea*>(widget); + SolarMutexGuard aGuard; pThis->signal_draw(cr); return false; } @@ -3097,6 +3140,7 @@ private: static void signalSizeAllocate(GtkWidget*, GdkRectangle* allocation, gpointer widget) { GtkInstanceDrawingArea* pThis = static_cast<GtkInstanceDrawingArea*>(widget); + SolarMutexGuard aGuard; pThis->signal_size_allocate(allocation->width, allocation->height); } void signal_size_allocate(guint nWidth, guint nHeight) @@ -3119,6 +3163,7 @@ private: static gboolean signalButton(GtkWidget*, GdkEventButton* pEvent, gpointer widget) { GtkInstanceDrawingArea* pThis = static_cast<GtkInstanceDrawingArea*>(widget); + SolarMutexGuard aGuard; return pThis->signal_button(pEvent); } bool signal_button(GdkEventButton* pEvent) @@ -3187,6 +3232,7 @@ private: static gboolean signalMotion(GtkWidget*, GdkEventMotion* pEvent, gpointer widget) { GtkInstanceDrawingArea* pThis = static_cast<GtkInstanceDrawingArea*>(widget); + SolarMutexGuard aGuard; return pThis->signal_motion(pEvent); } bool signal_motion(GdkEventMotion* pEvent) @@ -3202,6 +3248,7 @@ private: static gboolean signalKey(GtkWidget*, GdkEventKey* pEvent, gpointer widget) { GtkInstanceDrawingArea* pThis = static_cast<GtkInstanceDrawingArea*>(widget); + SolarMutexGuard aGuard; return pThis->signal_key(pEvent); } gboolean signal_key(GdkEventKey* pEvent) @@ -3312,14 +3359,53 @@ class GtkInstanceComboBoxText : public GtkInstanceContainer, public virtual weld private: GtkComboBoxText* m_pComboBoxText; std::unique_ptr<comphelper::string::NaturalStringSorter> m_xSorter; - gulong m_nSignalId; + gboolean m_bPopupActive; + gulong m_nChangedSignalId; + gulong m_nPopupShownSignalId; + gulong m_nEntryActivateSignalId; static void signalChanged(GtkComboBox*, gpointer widget) { GtkInstanceComboBoxText* pThis = static_cast<GtkInstanceComboBoxText*>(widget); + SolarMutexGuard aGuard; pThis->signal_changed(); } + static void signalPopupShown(GtkComboBox*, GParamSpec*, gpointer widget) + { + GtkInstanceComboBoxText* pThis = static_cast<GtkInstanceComboBoxText*>(widget); + pThis->signal_popup_shown(); + } + + void signal_popup_shown() + { + gboolean bIsShown(false); + g_object_get(m_pComboBoxText, "popup-shown", &bIsShown, nullptr); + if (m_bPopupActive != bIsShown) + { + m_bPopupActive = bIsShown; + //restore focus to the entry wieh the popup is gone, which + //is what the vcl case does, to ease the transition a little + gtk_widget_grab_focus(m_pWidget); + } + } + + static void signalEntryActivate(GtkComboBox*, gpointer widget) + { + GtkInstanceComboBoxText* pThis = static_cast<GtkInstanceComboBoxText*>(widget); + pThis->signal_entry_activate(); + } + + void signal_entry_activate() + { + if (m_aEntryActivateHdl.IsSet()) + { + SolarMutexGuard aGuard; + m_aEntryActivateHdl.Call(*this); + g_signal_stop_emission_by_name(get_entry(), "activate"); + } + } + OUString get(int pos, int col) const { OUString sRet; @@ -3358,28 +3444,43 @@ private: return -1; } - void setup_completion() + GtkEntry* get_entry() { GtkWidget* pChild = gtk_bin_get_child(GTK_BIN(m_pComboBoxText)); - if (GTK_IS_ENTRY(pChild)) - { - GtkEntry* pEntry = GTK_ENTRY(pChild); - GtkEntryCompletion* pCompletion = gtk_entry_completion_new(); - gtk_entry_completion_set_model(pCompletion, gtk_combo_box_get_model(GTK_COMBO_BOX(m_pComboBoxText))); - gtk_entry_completion_set_text_column(pCompletion, 0); - gtk_entry_completion_set_inline_selection(pCompletion, true); - gtk_entry_set_completion(pEntry, pCompletion); - g_object_unref(pCompletion); - } + if (!GTK_IS_ENTRY(pChild)) + return nullptr; + return GTK_ENTRY(pChild); + } + + void setup_completion(GtkEntry* pEntry) + { + if (gtk_entry_get_completion(pEntry)) + return; + GtkEntryCompletion* pCompletion = gtk_entry_completion_new(); + gtk_entry_completion_set_model(pCompletion, gtk_combo_box_get_model(GTK_COMBO_BOX(m_pComboBoxText))); + gtk_entry_completion_set_text_column(pCompletion, 0); + gtk_entry_completion_set_inline_selection(pCompletion, true); + gtk_entry_completion_set_inline_completion(pCompletion, true); + gtk_entry_completion_set_popup_completion(pCompletion, false); + gtk_entry_set_completion(pEntry, pCompletion); + g_object_unref(pCompletion); } public: GtkInstanceComboBoxText(GtkComboBoxText* pComboBoxText, bool bTakeOwnership) : GtkInstanceContainer(GTK_CONTAINER(pComboBoxText), bTakeOwnership) , m_pComboBoxText(pComboBoxText) - , m_nSignalId(g_signal_connect(m_pComboBoxText, "changed", G_CALLBACK(signalChanged), this)) + , m_bPopupActive(false) + , m_nChangedSignalId(g_signal_connect(m_pComboBoxText, "changed", G_CALLBACK(signalChanged), this)) + , m_nPopupShownSignalId(g_signal_connect(m_pComboBoxText, "notify::popup-shown", G_CALLBACK(signalPopupShown), this)) { - setup_completion(); + if (GtkEntry* pEntry = get_entry()) + { + setup_completion(pEntry); + m_nEntryActivateSignalId = g_signal_connect(pEntry, "activate", G_CALLBACK(signalEntryActivate), this); + } + else + m_nEntryActivateSignalId = 0; } virtual int get_active() const override @@ -3517,29 +3618,40 @@ public: return gtk_editable_get_selection_bounds(GTK_EDITABLE(pEntry), &rStartPos, &rEndPos); } - virtual void unset_entry_completion() override + virtual void set_entry_completion(bool bEnable) override { - GtkWidget* pChild = gtk_bin_get_child(GTK_BIN(m_pComboBoxText)); - assert(pChild && GTK_IS_ENTRY(pChild)); - GtkEntry* pEntry = GTK_ENTRY(pChild); - gtk_entry_set_completion(pEntry, nullptr); + GtkEntry* pEntry = get_entry(); + assert(pEntry); + if (bEnable) + setup_completion(pEntry); + else + gtk_entry_set_completion(pEntry, nullptr); } virtual void disable_notify_events() override { - g_signal_handler_block(m_pComboBoxText, m_nSignalId); + if (GtkEntry* pEntry = get_entry()) + g_signal_handler_block(pEntry, m_nEntryActivateSignalId); + g_signal_handler_block(m_pComboBoxText, m_nChangedSignalId); + g_signal_handler_block(m_pComboBoxText, m_nPopupShownSignalId); GtkInstanceContainer::disable_notify_events(); } virtual void enable_notify_events() override { GtkInstanceContainer::disable_notify_events(); - g_signal_handler_unblock(m_pComboBoxText, m_nSignalId); + g_signal_handler_unblock(m_pComboBoxText, m_nPopupShownSignalId); + g_signal_handler_unblock(m_pComboBoxText, m_nChangedSignalId); + if (GtkEntry* pEntry = get_entry()) + g_signal_handler_unblock(pEntry, m_nEntryActivateSignalId); } virtual ~GtkInstanceComboBoxText() override { - g_signal_handler_disconnect(m_pComboBoxText, m_nSignalId); + if (GtkEntry* pEntry = get_entry()) + g_signal_handler_disconnect(pEntry, m_nEntryActivateSignalId); + g_signal_handler_disconnect(m_pComboBoxText, m_nChangedSignalId); + g_signal_handler_disconnect(m_pComboBoxText, m_nPopupShownSignalId); } }; @@ -3549,9 +3661,10 @@ private: GtkExpander* m_pExpander; gulong m_nSignalId; - static void signalExpanded(GtkExpander* pExpander, GParamSpec *, gpointer widget) + static void signalExpanded(GtkExpander* pExpander, GParamSpec*, gpointer widget) { GtkInstanceExpander* pThis = static_cast<GtkInstanceExpander*>(widget); + SolarMutexGuard aGuard; pThis->signal_expanded(); GtkWidget *pToplevel = gtk_widget_get_toplevel(GTK_WIDGET(pExpander)); |