xref: /core/sc/source/ui/dialogs/searchresults.cxx (revision 83d0b6bd)
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 <o3tl/safeint.hxx>
11 #include <searchresults.hxx>
12 #include <sfx2/bindings.hxx>
13 #include <sfx2/dispatch.hxx>
14 #include <sfx2/viewfrm.hxx>
15 #include <svx/srchdlg.hxx>
16 #include <dociter.hxx>
17 #include <document.hxx>
18 #include <tabvwsh.hxx>
19 #include <strings.hrc>
20 #include <sc.hrc>
21 #include <scresid.hxx>
22 
23 namespace sc {
24 
SearchResultsDlg(SfxBindings * _pBindings,weld::Window * pParent)25 SearchResultsDlg::SearchResultsDlg(SfxBindings* _pBindings, weld::Window* pParent)
26     : SfxDialogController(pParent, u"modules/scalc/ui/searchresults.ui"_ustr, u"SearchResultsDialog"_ustr)
27     , aSkipped(ScResId(SCSTR_SKIPPED))
28     , mpBindings(_pBindings)
29     , mpDoc(nullptr)
30     , mbSorted(false)
31     , mxList(m_xBuilder->weld_tree_view(u"results"_ustr))
32     , mxSearchResults(m_xBuilder->weld_label(u"lbSearchResults"_ustr))
33     , mxShowDialog(m_xBuilder->weld_check_button(u"cbShow"_ustr))
34 {
35     mxList->set_size_request(mxList->get_approximate_digit_width() * 50, mxList->get_height_rows(15));
36     mxShowDialog->connect_toggled(LINK(this, SearchResultsDlg, OnShowToggled));
37     std::vector<int> aWidths
38     {
39         o3tl::narrowing<int>(mxList->get_approximate_digit_width() * 10),
40         o3tl::narrowing<int>(mxList->get_approximate_digit_width() * 10)
41     };
42     mxList->set_column_fixed_widths(aWidths);
43     mxList->connect_changed(LINK(this, SearchResultsDlg, ListSelectHdl));
44     mxList->connect_column_clicked(LINK(this, SearchResultsDlg, HeaderBarClick));
45 }
46 
~SearchResultsDlg()47 SearchResultsDlg::~SearchResultsDlg()
48 {
49     // tdf#133807 if the search dialog is shown then re-present that dialog
50     // when this results dialog is dismissed
51     SfxViewFrame* pViewFrame = mpBindings->GetDispatcher()->GetFrame();
52     if (!pViewFrame)
53         return;
54     SfxChildWindow* pChildWindow = pViewFrame->GetChildWindow(
55             SvxSearchDialogWrapper::GetChildWindowId());
56     if (!pChildWindow)
57         return;
58     SvxSearchDialog* pSearchDlg = static_cast<SvxSearchDialog*>(pChildWindow->GetController().get());
59     if (!pSearchDlg)
60         return;
61     pSearchDlg->Present();
62 }
63 
64 namespace
65 {
66     class ListWrapper {
67         weld::TreeView& mrList;
68     public:
69         size_t mnCount = 0;
70         static const size_t mnMaximum = 1000;
ListWrapper(weld::TreeView & rList)71         ListWrapper(weld::TreeView& rList)
72             : mrList(rList)
73         {
74             mrList.clear();
75             mrList.freeze();
76         }
~ListWrapper()77         ~ListWrapper()
78         {
79             mrList.thaw();
80         }
Insert(const OUString & rTabName,const ScAddress & rPos,formula::FormulaGrammar::AddressConvention eConvention,const OUString & rText)81         void Insert(const OUString &rTabName,
82                     const ScAddress &rPos,
83                     formula::FormulaGrammar::AddressConvention eConvention,
84                     const OUString &rText)
85         {
86             if (mnCount++ < mnMaximum)
87             {
88                 mrList.append_text(rTabName);
89                 int nPos = mrList.n_children() - 1;
90                 mrList.set_text(nPos, rPos.Format(ScRefFlags::ADDR_ABS,
91                                       nullptr, eConvention), 1);
92                 mrList.set_text(nPos, rText, 2);
93             }
94         }
95     };
96 }
97 
FillResults(ScDocument & rDoc,const ScRangeList & rMatchedRanges,bool bCellNotes,bool bEmptyCells,bool bMatchedRangesWereClamped)98 void SearchResultsDlg::FillResults( ScDocument& rDoc, const ScRangeList &rMatchedRanges, bool bCellNotes,
99         bool bEmptyCells, bool bMatchedRangesWereClamped )
100 {
101     ListWrapper aList(*mxList);
102     std::vector<OUString> aTabNames = rDoc.GetAllTableNames();
103     SCTAB nTabCount = aTabNames.size();
104 
105     // tdf#92160 - too many results blow the widget's mind
106     size_t nMatchMax = rMatchedRanges.size();
107     if (nMatchMax > ListWrapper::mnMaximum)
108         nMatchMax = ListWrapper::mnMaximum;
109 
110     if (bCellNotes || bEmptyCells)
111     {
112         for (size_t i = 0, n = nMatchMax; i < n; ++i)
113         {
114             ScRange const & rRange( rMatchedRanges[i] );
115             // Bear in mind that mostly the range is one address position
116             // or a column or a row joined.
117             ScAddress aPos( rRange.aStart );
118             for ( ; aPos.Tab() <= rRange.aEnd.Tab(); aPos.IncTab())
119             {
120                 if (aPos.Tab() >= nTabCount)
121                     break;  // can this even happen? we just searched on existing sheets ...
122                 for (aPos.SetCol( rRange.aStart.Col()); aPos.Col() <= rRange.aEnd.Col(); aPos.IncCol())
123                 {
124                     for (aPos.SetRow( rRange.aStart.Row()); aPos.Row() <= rRange.aEnd.Row(); aPos.IncRow())
125                     {
126                         if (bCellNotes)
127                         {
128                             const ScPostIt* pNote = rDoc.GetNote( aPos);
129                             if (pNote)
130                                 aList.Insert(aTabNames[aPos.Tab()], aPos,
131                                         rDoc.GetAddressConvention(),
132                                         pNote->GetText());
133                         }
134                         else  // bEmptyCells
135                         {
136                             aList.Insert(aTabNames[aPos.Tab()], aPos,
137                                     rDoc.GetAddressConvention(),
138                                     rDoc.GetString(aPos));
139                         }
140                     }
141                 }
142             }
143         }
144     }
145     else
146     {
147         for (size_t i = 0, n = nMatchMax; i < n; ++i)
148         {
149             ScCellIterator aIter(rDoc, rMatchedRanges[i]);
150             for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
151             {
152                 const ScAddress& aPos = aIter.GetPos();
153                 if (aPos.Tab() >= nTabCount)
154                     // Out-of-bound sheet index.
155                     continue;
156 
157                 aList.Insert(aTabNames[aPos.Tab()], aPos,
158                              rDoc.GetAddressConvention(),
159                              rDoc.GetString(aPos));
160             }
161         }
162     }
163 
164     OUString aSearchResultsMsg;
165     if (bMatchedRangesWereClamped)
166     {
167         aSearchResultsMsg = ScResId(SCSTR_RESULTS_CLAMPED);
168         aSearchResultsMsg = aSearchResultsMsg.replaceFirst("%1", OUString::number(1000));
169     }
170     else
171     {
172         OUString aTotal(ScResId(SCSTR_TOTAL, aList.mnCount));
173         aSearchResultsMsg = aTotal.replaceFirst("%1", OUString::number(aList.mnCount));
174         if (aList.mnCount > ListWrapper::mnMaximum)
175             aSearchResultsMsg += " " + ScGlobal::ReplaceOrAppend( aSkipped, u"%1", OUString::number( ListWrapper::mnMaximum ) );
176     }
177     mxSearchResults->set_label(aSearchResultsMsg);
178 
179     mpDoc = &rDoc;
180 }
181 
Close()182 void SearchResultsDlg::Close()
183 {
184     if (mpBindings)
185     {
186         // Remove this dialog from the view frame after the dialog gets
187         // dismissed, else it would keep popping up endlessly!
188         SfxDispatcher* pDispacher = mpBindings ->GetDispatcher();
189         SfxBoolItem aItem(SID_SEARCH_RESULTS_DIALOG, false);
190         if (pDispacher)
191         {
192             pDispacher->ExecuteList(SID_SEARCH_RESULTS_DIALOG,
193                 SfxCallMode::SYNCHRON | SfxCallMode::RECORD, { &aItem });
194         }
195     }
196 
197     SfxDialogController::Close();
198 }
199 
IMPL_LINK(SearchResultsDlg,HeaderBarClick,int,nColumn,void)200 IMPL_LINK(SearchResultsDlg, HeaderBarClick, int, nColumn, void)
201 {
202     if (!mbSorted)
203     {
204         mxList->make_sorted();
205         mbSorted = true;
206     }
207 
208     bool bSortAtoZ = mxList->get_sort_order();
209 
210     //set new arrow positions in headerbar
211     if (nColumn == mxList->get_sort_column())
212     {
213         bSortAtoZ = !bSortAtoZ;
214         mxList->set_sort_order(bSortAtoZ);
215     }
216     else
217     {
218         int nOldSortColumn = mxList->get_sort_column();
219         if (nOldSortColumn != -1)
220             mxList->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
221         mxList->set_sort_column(nColumn);
222     }
223 
224     if (nColumn != -1)
225     {
226         //sort lists
227         mxList->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
228     }
229 }
230 
IMPL_LINK_NOARG(SearchResultsDlg,ListSelectHdl,weld::TreeView &,void)231 IMPL_LINK_NOARG( SearchResultsDlg, ListSelectHdl, weld::TreeView&, void )
232 {
233     if (!mpDoc)
234         return;
235 
236     int nEntry = mxList->get_selected_index();
237     OUString aTabStr = mxList->get_text(nEntry, 0);
238     OUString aPosStr = mxList->get_text(nEntry, 1);
239 
240     SCTAB nTab = -1;
241     if (!mpDoc->GetTable(aTabStr, nTab))
242         // No sheet with specified name.
243         return;
244 
245     ScAddress aPos;
246     ScRefFlags nRes = aPos.Parse(aPosStr, *mpDoc, mpDoc->GetAddressConvention());
247     if (!(nRes & ScRefFlags::VALID))
248         // Invalid address string.
249         return;
250 
251     // Jump to the cell.
252     if (ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell())
253     {
254         pScViewShell->SetTabNo(nTab);
255         pScViewShell->SetCursor(aPos.Col(), aPos.Row());
256         pScViewShell->AlignToCursor(aPos.Col(), aPos.Row(), SC_FOLLOW_JUMP);
257     }
258 }
259 
IMPL_STATIC_LINK(SearchResultsDlg,OnShowToggled,weld::Toggleable &,rButton,void)260 IMPL_STATIC_LINK( SearchResultsDlg, OnShowToggled, weld::Toggleable&, rButton, void )
261 {
262     if (ScTabViewShell* pScViewShell = ScTabViewShell::GetActiveViewShell())
263     {
264         ScViewOptions aViewOpt( pScViewShell->GetViewData().GetOptions() );
265         aViewOpt.SetOption( VOPT_SUMMARY, rButton.get_active() );
266         pScViewShell->GetViewData().SetOptions( aViewOpt );
267     }
268 }
269 
SearchResultsDlgWrapper(vcl::Window * _pParent,sal_uInt16 nId,SfxBindings * pBindings,SfxChildWinInfo *)270 SearchResultsDlgWrapper::SearchResultsDlgWrapper(
271     vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* pBindings, SfxChildWinInfo* /*pInfo*/)
272     : SfxChildWindow(_pParent, nId)
273     , m_xDialog(std::make_shared<SearchResultsDlg>(pBindings, _pParent->GetFrameWeld()))
274 {
275     SetController(m_xDialog);
276 }
277 
~SearchResultsDlgWrapper()278 SearchResultsDlgWrapper::~SearchResultsDlgWrapper() {}
279 
GetInfo() const280 SfxChildWinInfo SearchResultsDlgWrapper::GetInfo() const
281 {
282     SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
283     aInfo.bVisible = false;
284     return aInfo;
285 }
286 
287 SFX_IMPL_CHILDWINDOW_WITHID(SearchResultsDlgWrapper, SID_SEARCH_RESULTS_DIALOG);
288 
289 }
290 
291 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
292