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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <core_resource.hxx>
21 #include <indexfieldscontrol.hxx>
22 #include <dbu_dlg.hxx>
23 #include <strings.hrc>
24 #include <osl/diagnose.h>
25 #include <helpids.h>
26 #include <toolkit/helper/vclunohelper.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/svapp.hxx>
29 
30 namespace dbaui
31 {
32 
33 static constexpr auto BROWSER_STANDARD_FLAGS = BrowserMode::COLUMNSELECTION | BrowserMode::HLINES | BrowserMode::VLINES |
34                                     BrowserMode::HIDECURSOR | BrowserMode::HIDESELECT | BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL;
35 
36 #define COLUMN_ID_FIELDNAME     1
37 #define COLUMN_ID_ORDER         2
38 
39     using namespace ::com::sun::star::uno;
40     using namespace ::svt;
41 
42     // DbaMouseDownListBoxController
43     class DbaMouseDownListBoxController : public ListBoxCellController
44     {
45     protected:
46         Link<DbaMouseDownListBoxController&,void>  m_aAdditionalModifyHdl;
47 
48     public:
DbaMouseDownListBoxController(ListBoxControl * _pParent)49         explicit DbaMouseDownListBoxController(ListBoxControl* _pParent)
50             :ListBoxCellController(_pParent)
51         {
52         }
53 
54         void SetAdditionalModifyHdl(const Link<DbaMouseDownListBoxController&,void>& _rHdl);
55 
56     protected:
WantMouseEvent() const57         virtual bool WantMouseEvent() const override { return true; }
58         virtual void callModifyHdl() override;
59     };
60 
SetAdditionalModifyHdl(const Link<DbaMouseDownListBoxController &,void> & _rHdl)61     void DbaMouseDownListBoxController::SetAdditionalModifyHdl(const Link<DbaMouseDownListBoxController&,void>& _rHdl)
62     {
63         m_aAdditionalModifyHdl = _rHdl;
64     }
65 
callModifyHdl()66     void DbaMouseDownListBoxController::callModifyHdl()
67     {
68         m_aAdditionalModifyHdl.Call(*this);
69         ListBoxCellController::callModifyHdl();
70     }
71 
72     // IndexFieldsControl
IndexFieldsControl(const css::uno::Reference<css::awt::XWindow> & rParent)73     IndexFieldsControl::IndexFieldsControl(const css::uno::Reference<css::awt::XWindow> &rParent)
74         : EditBrowseBox(VCLUnoHelper::GetWindow(rParent), EditBrowseBoxFlags::SMART_TAB_TRAVEL | EditBrowseBoxFlags::ACTIVATE_ON_BUTTONDOWN, WB_TABSTOP | WB_BORDER, BROWSER_STANDARD_FLAGS)
75         , m_aSeekRow(m_aFields.end())
76         , m_pSortingCell(nullptr)
77         , m_pFieldNameCell(nullptr)
78         , m_bAddIndexAppendix(false)
79     {
80     }
81 
~IndexFieldsControl()82     IndexFieldsControl::~IndexFieldsControl()
83     {
84         disposeOnce();
85     }
86 
dispose()87     void IndexFieldsControl::dispose()
88     {
89         m_pSortingCell.disposeAndClear();
90         m_pFieldNameCell.disposeAndClear();
91         ::svt::EditBrowseBox::dispose();
92     }
93 
SeekRow(long nRow)94     bool IndexFieldsControl::SeekRow(long nRow)
95     {
96         if (!EditBrowseBox::SeekRow(nRow))
97             return false;
98 
99         if (nRow < 0)
100         {
101             m_aSeekRow = m_aFields.end();
102         }
103         else
104         {
105             m_aSeekRow = m_aFields.begin() + nRow;
106             OSL_ENSURE(m_aSeekRow <= m_aFields.end(), "IndexFieldsControl::SeekRow: invalid row!");
107         }
108 
109         return true;
110     }
111 
PaintCell(OutputDevice & _rDev,const tools::Rectangle & _rRect,sal_uInt16 _nColumnId) const112     void IndexFieldsControl::PaintCell( OutputDevice& _rDev, const tools::Rectangle& _rRect, sal_uInt16 _nColumnId ) const
113     {
114         Point aPos(_rRect.TopLeft());
115         aPos.AdjustX(1 );
116 
117         OUString aText = GetRowCellText(m_aSeekRow,_nColumnId);
118         Size TxtSize(GetDataWindow().GetTextWidth(aText), GetDataWindow().GetTextHeight());
119 
120         // clipping
121         if (aPos.X() < _rRect.Right() || aPos.X() + TxtSize.Width() > _rRect.Right() ||
122             aPos.Y() < _rRect.Top() || aPos.Y() + TxtSize.Height() > _rRect.Bottom())
123             _rDev.SetClipRegion(vcl::Region(_rRect));
124 
125         // allow for a disabled control ...
126         bool bEnabled = IsEnabled();
127         Color aOriginalColor = _rDev.GetTextColor();
128         if (!bEnabled)
129             _rDev.SetTextColor(GetSettings().GetStyleSettings().GetDisableColor());
130 
131         // draw the text
132         _rDev.DrawText(aPos, aText);
133 
134         // reset the color (if necessary)
135         if (!bEnabled)
136             _rDev.SetTextColor(aOriginalColor);
137 
138         if (_rDev.IsClipRegion())
139             _rDev.SetClipRegion();
140     }
141 
initializeFrom(const IndexFields & _rFields)142     void IndexFieldsControl::initializeFrom(const IndexFields& _rFields)
143     {
144         // copy the field descriptions
145         m_aFields = _rFields;
146         m_aSeekRow = m_aFields.end();
147 
148         SetUpdateMode(false);
149         // remove all rows
150         RowRemoved(1, GetRowCount());
151         // insert rows for the fields
152         RowInserted(GetRowCount(), m_aFields.size(), false);
153         // insert an additional row for a new field for that index
154         RowInserted(GetRowCount(), 1, false);
155         SetUpdateMode(true);
156 
157         GoToRowColumnId(0, COLUMN_ID_FIELDNAME);
158     }
159 
commitTo(IndexFields & _rFields)160     void IndexFieldsControl::commitTo(IndexFields& _rFields)
161     {
162         // do not just copy the array, we may have empty field names (which should not be copied)
163         _rFields.resize(m_aFields.size());
164         IndexFields::iterator aDest = std::copy_if(m_aFields.begin(), m_aFields.end(), _rFields.begin(),
165             [](const OIndexField& source) { return !source.sFieldName.isEmpty(); });
166 
167         _rFields.resize(aDest - _rFields.begin());
168     }
169 
GetTotalCellWidth(long _nRow,sal_uInt16 _nColId)170     sal_uInt32 IndexFieldsControl::GetTotalCellWidth(long _nRow, sal_uInt16 _nColId)
171     {
172         if (COLUMN_ID_ORDER == _nColId)
173         {
174             sal_Int32 nWidthAsc = GetTextWidth(m_sAscendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
175             sal_Int32 nWidthDesc = GetTextWidth(m_sDescendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
176             // maximum plus some additional space
177             return std::max(nWidthAsc, nWidthDesc) + GetTextWidth(OUString('0')) * 2;
178         }
179         return EditBrowseBox::GetTotalCellWidth(_nRow, _nColId);
180     }
181 
Init(const Sequence<OUString> & _rAvailableFields,bool _bAddIndexAppendix)182     void IndexFieldsControl::Init(const Sequence< OUString >& _rAvailableFields, bool _bAddIndexAppendix)
183     {
184         m_bAddIndexAppendix = _bAddIndexAppendix;
185 
186         RemoveColumns();
187 
188         // for the width: both columns together should be somewhat smaller than the whole window (without the scrollbar)
189         sal_Int32 nFieldNameWidth = GetSizePixel().Width();
190 
191         if ( m_bAddIndexAppendix )
192         {
193             m_sAscendingText = DBA_RES(STR_ORDER_ASCENDING);
194             m_sDescendingText = DBA_RES(STR_ORDER_DESCENDING);
195 
196             // the "sort order" column
197             OUString sColumnName = DBA_RES(STR_TAB_INDEX_SORTORDER);
198             // the width of the order column is the maximum widths of the texts used
199             // (the title of the column)
200             sal_Int32 nSortOrderColumnWidth = GetTextWidth(sColumnName);
201             // ("ascending" + scrollbar width)
202             sal_Int32 nOther = GetTextWidth(m_sAscendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
203             nSortOrderColumnWidth = std::max(nSortOrderColumnWidth, nOther);
204             // ("descending" + scrollbar width)
205             nOther = GetTextWidth(m_sDescendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
206             nSortOrderColumnWidth = std::max(nSortOrderColumnWidth, nOther);
207             // (plus some additional space)
208             nSortOrderColumnWidth += GetTextWidth(OUString('0')) * 2;
209             InsertDataColumn(COLUMN_ID_ORDER, sColumnName, nSortOrderColumnWidth, HeaderBarItemBits::STDSTYLE, 1);
210 
211             m_pSortingCell = VclPtr<ListBoxControl>::Create(&GetDataWindow());
212             m_pSortingCell->InsertEntry(m_sAscendingText);
213             m_pSortingCell->InsertEntry(m_sDescendingText);
214             m_pSortingCell->SetHelpId( HID_DLGINDEX_INDEXDETAILS_SORTORDER );
215 
216             nFieldNameWidth -= nSortOrderColumnWidth;
217         }
218         StyleSettings aSystemStyle = Application::GetSettings().GetStyleSettings();
219         nFieldNameWidth -= aSystemStyle.GetScrollBarSize();
220         nFieldNameWidth -= 8;
221         // the "field name" column
222         OUString sColumnName = DBA_RES(STR_TAB_INDEX_FIELD);
223         InsertDataColumn(COLUMN_ID_FIELDNAME, sColumnName, nFieldNameWidth, HeaderBarItemBits::STDSTYLE, 0);
224 
225         // create the cell controllers
226         // for the field name cell
227         m_pFieldNameCell = VclPtr<ListBoxControl>::Create(&GetDataWindow());
228         m_pFieldNameCell->InsertEntry(OUString());
229         m_pFieldNameCell->SetHelpId( HID_DLGINDEX_INDEXDETAILS_FIELD );
230         const OUString* pFields = _rAvailableFields.getConstArray();
231         const OUString* pFieldsEnd = pFields + _rAvailableFields.getLength();
232         for (;pFields < pFieldsEnd; ++pFields)
233             m_pFieldNameCell->InsertEntry(*pFields);
234     }
235 
GetController(long _nRow,sal_uInt16 _nColumnId)236     CellController* IndexFieldsControl::GetController(long _nRow, sal_uInt16 _nColumnId)
237     {
238         if (!IsEnabled())
239             return nullptr;
240 
241         IndexFields::const_iterator aRow;
242         bool bNewField = !implGetFieldDesc(_nRow, aRow);
243 
244         DbaMouseDownListBoxController* pReturn = nullptr;
245         switch (_nColumnId)
246         {
247             case COLUMN_ID_ORDER:
248                 if (!bNewField && m_pSortingCell && !aRow->sFieldName.isEmpty())
249                     pReturn = new DbaMouseDownListBoxController(m_pSortingCell);
250                 break;
251 
252             case COLUMN_ID_FIELDNAME:
253                 pReturn = new DbaMouseDownListBoxController(m_pFieldNameCell);
254                 break;
255 
256             default:
257                 OSL_FAIL("IndexFieldsControl::GetController: invalid column id!");
258         }
259 
260         if (pReturn)
261             pReturn->SetAdditionalModifyHdl(LINK(this, IndexFieldsControl, OnListEntrySelected));
262 
263         return pReturn;
264     }
265 
implGetFieldDesc(long _nRow,IndexFields::const_iterator & _rPos)266     bool IndexFieldsControl::implGetFieldDesc(long _nRow, IndexFields::const_iterator& _rPos)
267     {
268         _rPos = m_aFields.end();
269         if ((_nRow < 0) || (_nRow >= static_cast<sal_Int32>(m_aFields.size())))
270             return false;
271         _rPos = m_aFields.begin() + _nRow;
272         return true;
273     }
274 
SaveModified()275     bool IndexFieldsControl::SaveModified()
276     {
277         if (!IsModified())
278             return true;
279 
280         switch (GetCurColumnId())
281         {
282             case COLUMN_ID_FIELDNAME:
283             {
284                 OUString sFieldSelected = m_pFieldNameCell->GetSelectedEntry();
285                 bool bEmptySelected = sFieldSelected.isEmpty();
286                 if (isNewField())
287                 {
288                     if (!bEmptySelected)
289                     {
290                         // add a new field to the collection
291                         OIndexField aNewField;
292                         aNewField.sFieldName = sFieldSelected;
293                         m_aFields.push_back(aNewField);
294                         RowInserted(GetRowCount());
295                     }
296                 }
297                 else
298                 {
299                     sal_Int32 nRow = GetCurRow();
300                     OSL_ENSURE(nRow < static_cast<sal_Int32>(m_aFields.size()), "IndexFieldsControl::SaveModified: invalid current row!");
301                     if (nRow >= 0)  // may be -1 in case the control was empty
302                     {
303                         // remove the field from the selection
304                         IndexFields::iterator aPos = m_aFields.begin() + nRow;
305 
306                         if (bEmptySelected)
307                         {
308                             aPos->sFieldName.clear();
309 
310                             // invalidate the row to force repaint
311                             Invalidate(GetRowRectPixel(nRow));
312                             return true;
313                         }
314 
315                         if (sFieldSelected == aPos->sFieldName)
316                             // nothing changed
317                             return true;
318 
319                         aPos->sFieldName = sFieldSelected;
320                     }
321                 }
322 
323                 Invalidate(GetRowRectPixel(GetCurRow()));
324             }
325             break;
326             case COLUMN_ID_ORDER:
327             {
328                 OSL_ENSURE(!isNewField(), "IndexFieldsControl::SaveModified: why the hell ...!!!");
329                 // selected entry
330                 sal_Int32 nPos = m_pSortingCell->GetSelectedEntryPos();
331                 OSL_ENSURE(LISTBOX_ENTRY_NOTFOUND != nPos, "IndexFieldsControl::SaveModified: how did you get this selection??");
332                 // adjust the sort flag in the index field description
333                 OIndexField& rCurrentField = m_aFields[GetCurRow()];
334                 rCurrentField.bSortAscending = (0 == nPos);
335 
336             }
337             break;
338             default:
339                 OSL_FAIL("IndexFieldsControl::SaveModified: invalid column id!");
340         }
341         return true;
342     }
343 
InitController(CellControllerRef &,long _nRow,sal_uInt16 _nColumnId)344     void IndexFieldsControl::InitController(CellControllerRef& /*_rController*/, long _nRow, sal_uInt16 _nColumnId)
345     {
346         IndexFields::const_iterator aFieldDescription;
347         bool bNewField = !implGetFieldDesc(_nRow, aFieldDescription);
348 
349         switch (_nColumnId)
350         {
351             case COLUMN_ID_FIELDNAME:
352                 m_pFieldNameCell->SelectEntry(bNewField ? OUString() : aFieldDescription->sFieldName);
353                 m_pFieldNameCell->SaveValue();
354                 break;
355 
356             case COLUMN_ID_ORDER:
357                 m_pSortingCell->SelectEntry(aFieldDescription->bSortAscending ? m_sAscendingText : m_sDescendingText);
358                 m_pSortingCell->SaveValue();
359                 break;
360 
361             default:
362                 OSL_FAIL("IndexFieldsControl::InitController: invalid column id!");
363         }
364     }
365 
IMPL_LINK(IndexFieldsControl,OnListEntrySelected,DbaMouseDownListBoxController &,rController,void)366     IMPL_LINK( IndexFieldsControl, OnListEntrySelected, DbaMouseDownListBoxController&, rController, void )
367     {
368         ListBoxControl& rListBox = rController.GetListBox();
369         if (!rListBox.IsTravelSelect())
370             m_aModifyHdl.Call(*this);
371 
372         if (&rListBox == m_pFieldNameCell.get())
373         {   // a field has been selected
374             if (GetCurRow() >= GetRowCount() - 2)
375             {   // and we're in one of the last two rows
376                 OUString sSelectedEntry = m_pFieldNameCell->GetSelectedEntry();
377                 sal_Int32 nCurrentRow = GetCurRow();
378                 sal_Int32 rowCount = GetRowCount();
379 
380                 OSL_ENSURE((static_cast<sal_Int32>(m_aFields.size() + 1)) == rowCount, "IndexFieldsControl::OnListEntrySelected: inconsistence!");
381 
382                 if (!sSelectedEntry.isEmpty() && (nCurrentRow == rowCount - 1) /*&& (!m_nMaxColumnsInIndex || rowCount < m_nMaxColumnsInIndex )*/ )
383                 {   // in the last row, a non-empty string has been selected
384                     // -> insert a new row
385                     m_aFields.emplace_back();
386                     RowInserted(GetRowCount());
387                     Invalidate(GetRowRectPixel(nCurrentRow));
388                 }
389                 else if (sSelectedEntry.isEmpty() && (nCurrentRow == rowCount - 2))
390                 {   // in the (last-1)th row, an empty entry has been selected
391                     // -> remove the last row
392                     m_aFields.pop_back();
393                     RowRemoved(GetRowCount() - 1);
394                     Invalidate(GetRowRectPixel(nCurrentRow));
395                 }
396             }
397 
398             SaveModified();
399         }
400     }
GetCellText(long _nRow,sal_uInt16 nColId) const401     OUString IndexFieldsControl::GetCellText(long _nRow,sal_uInt16 nColId) const
402     {
403         IndexFields::const_iterator aRow = m_aFields.end();
404         if ( _nRow >= 0 )
405         {
406             aRow = m_aFields.begin() + _nRow;
407             OSL_ENSURE(aRow <= m_aFields.end(), "IndexFieldsControl::SeekRow: invalid row!");
408         }
409         return GetRowCellText(aRow,nColId);
410     }
GetRowCellText(const IndexFields::const_iterator & _rRow,sal_uInt16 nColId) const411     OUString IndexFieldsControl::GetRowCellText(const IndexFields::const_iterator& _rRow,sal_uInt16 nColId) const
412     {
413         if (_rRow < m_aFields.end())
414         {
415             switch (nColId)
416             {
417                 case COLUMN_ID_FIELDNAME:
418                     return _rRow->sFieldName;
419                 case COLUMN_ID_ORDER:
420                     if (_rRow->sFieldName.isEmpty())
421                         return OUString();
422                     else
423                         return _rRow->bSortAscending ? m_sAscendingText : m_sDescendingText;
424                 default:
425                     OSL_FAIL("IndexFieldsControl::GetCurrentRowCellText: invalid column id!");
426             }
427         }
428         return OUString();
429     }
IsTabAllowed(bool) const430     bool IndexFieldsControl::IsTabAllowed(bool /*bForward*/) const
431     {
432         return false;
433     }
434 
435 }   // namespace dbaui
436 
437 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
438