1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <searchresults.hxx>
11 #include <sfx2/bindings.hxx>
12 #include <sfx2/dispatch.hxx>
13 #include <dociter.hxx>
14 #include <document.hxx>
15 #include <tabvwsh.hxx>
16 #include <strings.hrc>
17 #include <sc.hrc>
18 #include <scresid.hxx>
19 
20 namespace sc {
21 
SearchResultsDlg(SfxBindings * _pBindings,weld::Window * pParent)22 SearchResultsDlg::SearchResultsDlg(SfxBindings* _pBindings, weld::Window* pParent)
23     : SfxDialogController(pParent, "modules/scalc/ui/searchresults.ui", "SearchResultsDialog")
24     , aSkipped(ScResId(SCSTR_SKIPPED))
25     , mpBindings(_pBindings)
26     , mpDoc(nullptr)
27     , mbSorted(false)
28     , mxList(m_xBuilder->weld_tree_view("results"))
29     , mxSearchResults(m_xBuilder->weld_label("lbSearchResults"))
30     , mxShowDialog(m_xBuilder->weld_check_button("cbShow"))
31 {
32     mxList->set_size_request(mxList->get_approximate_digit_width() * 50, mxList->get_height_rows(15));
33     mxShowDialog->connect_toggled(LINK(this, SearchResultsDlg, OnShowToggled));
34     std::vector<int> aWidths;
35     aWidths.push_back(mxList->get_approximate_digit_width() * 10);
36     aWidths.push_back(mxList->get_approximate_digit_width() * 10);
37     mxList->set_column_fixed_widths(aWidths);
38     mxList->connect_changed(LINK(this, SearchResultsDlg, ListSelectHdl));
39     mxList->connect_column_clicked(LINK(this, SearchResultsDlg, HeaderBarClick));
40 }
41 
~SearchResultsDlg()42 SearchResultsDlg::~SearchResultsDlg()
43 {
44 }
45 
46 namespace
47 {
48     class ListWrapper {
49         weld::TreeView& mrList;
50     public:
51         size_t mnCount = 0;
52         static const size_t mnMaximum = 1000;
ListWrapper(weld::TreeView & rList)53         ListWrapper(weld::TreeView& rList)
54             : mrList(rList)
55         {
56             mrList.clear();
57             mrList.freeze();
58         }
~ListWrapper()59         ~ListWrapper()
60         {
61             mrList.thaw();
62         }
Insert(const OUString & rTabName,const ScAddress & rPos,formula::FormulaGrammar::AddressConvention eConvention,const OUString & rText)63         void Insert(const OUString &rTabName,
64                     const ScAddress &rPos,
65                     formula::FormulaGrammar::AddressConvention eConvention,
66                     const OUString &rText)
67         {
68             if (mnCount++ < mnMaximum)
69             {
70                 mrList.append_text(rTabName);
71                 int nPos = mrList.n_children() - 1;
72                 mrList.set_text(nPos, rPos.Format(ScRefFlags::ADDR_ABS,
73                                       nullptr, eConvention), 1);
74                 mrList.set_text(nPos, rText, 2);
75             }
76         }
77     };
78 }
79 
FillResults(ScDocument * pDoc,const ScRangeList & rMatchedRanges,bool bCellNotes)80 void SearchResultsDlg::FillResults( ScDocument* pDoc, const ScRangeList &rMatchedRanges, bool bCellNotes )
81 {
82     ListWrapper aList(*mxList);
83     std::vector<OUString> aTabNames = pDoc->GetAllTableNames();
84     SCTAB nTabCount = aTabNames.size();
85 
86     // tdf#92160 - too many results blow the widget's mind
87     size_t nMatchMax = rMatchedRanges.size();
88     if (nMatchMax > ListWrapper::mnMaximum)
89         nMatchMax = ListWrapper::mnMaximum;
90 
91     if (bCellNotes)
92     {
93         for (size_t i = 0, n = nMatchMax; i < n; ++i)
94         {
95             /* TODO: a CellNotes iterator would come handy and might speed
96              * things up a little, though we only loop through the
97              * search/replace result positions here. */
98             ScRange const & rRange( rMatchedRanges[i] );
99             // Bear in mind that mostly the range is one address position
100             // or a column or a row joined.
101             ScAddress aPos( rRange.aStart );
102             for ( ; aPos.Tab() <= rRange.aEnd.Tab(); aPos.IncTab())
103             {
104                 if (aPos.Tab() >= nTabCount)
105                     break;  // can this even happen? we just searched on existing sheets ...
106                 for (aPos.SetCol( rRange.aStart.Col()); aPos.Col() <= rRange.aEnd.Col(); aPos.IncCol())
107                 {
108                     for (aPos.SetRow( rRange.aStart.Row()); aPos.Row() <= rRange.aEnd.Row(); aPos.IncRow())
109                     {
110                         const ScPostIt* pNote = pDoc->GetNote( aPos);
111                         if (pNote)
112                             aList.Insert(aTabNames[aPos.Tab()], aPos,
113                                          pDoc->GetAddressConvention(),
114                                          pNote->GetText());
115                     }
116                 }
117             }
118         }
119     }
120     else
121     {
122         for (size_t i = 0, n = nMatchMax; i < n; ++i)
123         {
124             ScCellIterator aIter(pDoc, rMatchedRanges[i]);
125             for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
126             {
127                 const ScAddress& aPos = aIter.GetPos();
128                 if (aPos.Tab() >= nTabCount)
129                     // Out-of-bound sheet index.
130                     continue;
131 
132                 aList.Insert(aTabNames[aPos.Tab()], aPos,
133                              pDoc->GetAddressConvention(),
134                              pDoc->GetString(aPos));
135             }
136         }
137     }
138 
139     OUString aTotal(ScResId(SCSTR_TOTAL, aList.mnCount));
140     OUString aSearchResults = aTotal.replaceFirst("%1", OUString::number(aList.mnCount));
141     if (aList.mnCount > ListWrapper::mnMaximum)
142         aSearchResults += " " + ScGlobal::ReplaceOrAppend( aSkipped, "%1", OUString::number( ListWrapper::mnMaximum ) );
143     mxSearchResults->set_label(aSearchResults);
144 
145     mpDoc = pDoc;
146 }
147 
Close()148 void SearchResultsDlg::Close()
149 {
150     if (mpBindings)
151     {
152         // Remove this dialog from the view frame after the dialog gets
153         // dismissed, else it would keep popping up endlessly!
154         SfxDispatcher* pDispacher = mpBindings ->GetDispatcher();
155         SfxBoolItem aItem(SID_SEARCH_RESULTS_DIALOG, false);
156         if (pDispacher)
157         {
158             pDispacher->ExecuteList(SID_SEARCH_RESULTS_DIALOG,
159                 SfxCallMode::SYNCHRON | SfxCallMode::RECORD, { &aItem });
160         }
161     }
162 
163     SfxDialogController::Close();
164 }
165 
IMPL_LINK(SearchResultsDlg,HeaderBarClick,int,nColumn,void)166 IMPL_LINK(SearchResultsDlg, HeaderBarClick, int, nColumn, void)
167 {
168     if (!mbSorted)
169     {
170         mxList->make_sorted();
171         mbSorted = true;
172     }
173 
174     bool bSortAtoZ = mxList->get_sort_order();
175 
176     //set new arrow positions in headerbar
177     if (nColumn == mxList->get_sort_column())
178     {
179         bSortAtoZ = !bSortAtoZ;
180         mxList->set_sort_order(bSortAtoZ);
181     }
182     else
183     {
184         int nOldSortColumn = mxList->get_sort_column();
185         if (nOldSortColumn != -1)
186             mxList->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
187         mxList->set_sort_column(nColumn);
188     }
189 
190     if (nColumn != -1)
191     {
192         //sort lists
193         mxList->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
194     }
195 }
196 
IMPL_LINK_NOARG(SearchResultsDlg,ListSelectHdl,weld::TreeView &,void)197 IMPL_LINK_NOARG( SearchResultsDlg, ListSelectHdl, weld::TreeView&, void )
198 {
199     if (!mpDoc)
200         return;
201 
202     int nEntry = mxList->get_selected_index();
203     OUString aTabStr = mxList->get_text(nEntry, 0);
204     OUString aPosStr = mxList->get_text(nEntry, 1);
205 
206     SCTAB nTab = -1;
207     if (!mpDoc->GetTable(aTabStr, nTab))
208         // No sheet with specified name.
209         return;
210 
211     ScAddress aPos;
212     ScRefFlags nRes = aPos.Parse(aPosStr, mpDoc, mpDoc->GetAddressConvention());
213     if (!(nRes & ScRefFlags::VALID))
214         // Invalid address string.
215         return;
216 
217     // Jump to the cell.
218     ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell();
219     pScViewShell->SetTabNo(nTab);
220     pScViewShell->SetCursor(aPos.Col(), aPos.Row());
221     pScViewShell->AlignToCursor(aPos.Col(), aPos.Row(), SC_FOLLOW_JUMP);
222 }
223 
IMPL_STATIC_LINK(SearchResultsDlg,OnShowToggled,weld::ToggleButton &,rButton,void)224 IMPL_STATIC_LINK( SearchResultsDlg, OnShowToggled, weld::ToggleButton&, rButton, void )
225 {
226     ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell();
227     ScViewOptions aViewOpt( pScViewShell->GetViewData().GetOptions() );
228     aViewOpt.SetOption( VOPT_SUMMARY, rButton.get_active() );
229     pScViewShell->GetViewData().SetOptions( aViewOpt );
230 }
231 
SearchResultsDlgWrapper(vcl::Window * _pParent,sal_uInt16 nId,SfxBindings * pBindings,SfxChildWinInfo *)232 SearchResultsDlgWrapper::SearchResultsDlgWrapper(
233     vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* pBindings, SfxChildWinInfo* /*pInfo*/)
234     : SfxChildWindow(_pParent, nId)
235     , m_xDialog(new SearchResultsDlg(pBindings, _pParent->GetFrameWeld()))
236 {
237     SetController(m_xDialog);
238 }
239 
~SearchResultsDlgWrapper()240 SearchResultsDlgWrapper::~SearchResultsDlgWrapper() {}
241 
GetInfo() const242 SfxChildWinInfo SearchResultsDlgWrapper::GetInfo() const
243 {
244     SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
245     aInfo.bVisible = false;
246     return aInfo;
247 }
248 
249 SFX_IMPL_CHILDWINDOW_WITHID(SearchResultsDlgWrapper, SID_SEARCH_RESULTS_DIALOG);
250 
251 }
252 
253 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
254