1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <core_resource.hxx>
21 #include <indexfieldscontrol.hxx>
22 #include <strings.hrc>
23 #include <o3tl/safeint.hxx>
24 #include <osl/diagnose.h>
26 #include <toolkit/helper/vclunohelper.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/svapp.hxx>
33 constexpr auto BROWSER_STANDARD_FLAGS
= BrowserMode::COLUMNSELECTION
| BrowserMode::HLINES
| BrowserMode::VLINES
|
34 BrowserMode::HIDECURSOR
| BrowserMode::HIDESELECT
| BrowserMode::AUTO_HSCROLL
| BrowserMode::AUTO_VSCROLL
;
36 #define COLUMN_ID_FIELDNAME 1
37 #define COLUMN_ID_ORDER 2
39 using namespace ::com::sun::star::uno
;
40 using namespace ::svt
;
42 // DbaMouseDownListBoxController
43 class DbaMouseDownListBoxController
: public ListBoxCellController
46 Link
<DbaMouseDownListBoxController
&,void> m_aAdditionalModifyHdl
;
49 explicit DbaMouseDownListBoxController(ListBoxControl
* _pParent
)
50 :ListBoxCellController(_pParent
)
54 void SetAdditionalModifyHdl(const Link
<DbaMouseDownListBoxController
&,void>& _rHdl
);
57 virtual void callModifyHdl() override
;
60 void DbaMouseDownListBoxController::SetAdditionalModifyHdl(const Link
<DbaMouseDownListBoxController
&,void>& _rHdl
)
62 m_aAdditionalModifyHdl
= _rHdl
;
65 void DbaMouseDownListBoxController::callModifyHdl()
67 m_aAdditionalModifyHdl
.Call(*this);
68 ListBoxCellController::callModifyHdl();
72 IndexFieldsControl::IndexFieldsControl(const css::uno::Reference
<css::awt::XWindow
> &rParent
)
73 : EditBrowseBox(VCLUnoHelper::GetWindow(rParent
), EditBrowseBoxFlags::SMART_TAB_TRAVEL
| EditBrowseBoxFlags::ACTIVATE_ON_BUTTONDOWN
, WB_TABSTOP
| WB_BORDER
, BROWSER_STANDARD_FLAGS
)
74 , m_aSeekRow(m_aFields
.end())
75 , m_pSortingCell(nullptr)
76 , m_pFieldNameCell(nullptr)
77 , m_bAddIndexAppendix(false)
81 IndexFieldsControl::~IndexFieldsControl()
86 void IndexFieldsControl::dispose()
88 m_pSortingCell
.disposeAndClear();
89 m_pFieldNameCell
.disposeAndClear();
90 ::svt::EditBrowseBox::dispose();
93 bool IndexFieldsControl::SeekRow(sal_Int32 nRow
)
95 if (!EditBrowseBox::SeekRow(nRow
))
100 m_aSeekRow
= m_aFields
.end();
104 m_aSeekRow
= m_aFields
.begin() + nRow
;
105 OSL_ENSURE(m_aSeekRow
<= m_aFields
.end(), "IndexFieldsControl::SeekRow: invalid row!");
111 void IndexFieldsControl::PaintCell( OutputDevice
& _rDev
, const tools::Rectangle
& _rRect
, sal_uInt16 _nColumnId
) const
113 Point
aPos(_rRect
.TopLeft());
116 OUString aText
= GetRowCellText(m_aSeekRow
,_nColumnId
);
117 Size
TxtSize(GetDataWindow().GetTextWidth(aText
), GetDataWindow().GetTextHeight());
120 if (aPos
.X() < _rRect
.Right() || aPos
.X() + TxtSize
.Width() > _rRect
.Right() ||
121 aPos
.Y() < _rRect
.Top() || aPos
.Y() + TxtSize
.Height() > _rRect
.Bottom())
122 _rDev
.SetClipRegion(vcl::Region(_rRect
));
124 // allow for a disabled control ...
125 bool bEnabled
= IsEnabled();
126 Color aOriginalColor
= _rDev
.GetTextColor();
128 _rDev
.SetTextColor(GetSettings().GetStyleSettings().GetDisableColor());
131 _rDev
.DrawText(aPos
, aText
);
133 // reset the color (if necessary)
135 _rDev
.SetTextColor(aOriginalColor
);
137 if (_rDev
.IsClipRegion())
138 _rDev
.SetClipRegion();
141 void IndexFieldsControl::initializeFrom(IndexFields
&& _rFields
)
143 // copy the field descriptions
144 m_aFields
= std::move(_rFields
);
145 m_aSeekRow
= m_aFields
.end();
147 SetUpdateMode(false);
149 RowRemoved(1, GetRowCount());
150 // insert rows for the fields
151 RowInserted(GetRowCount(), m_aFields
.size(), false);
152 // insert an additional row for a new field for that index
153 RowInserted(GetRowCount(), 1, false);
156 GoToRowColumnId(0, COLUMN_ID_FIELDNAME
);
159 void IndexFieldsControl::commitTo(IndexFields
& _rFields
)
161 // do not just copy the array, we may have empty field names (which should not be copied)
162 _rFields
.resize(m_aFields
.size());
163 IndexFields::iterator aDest
= std::copy_if(m_aFields
.begin(), m_aFields
.end(), _rFields
.begin(),
164 [](const OIndexField
& source
) { return !source
.sFieldName
.isEmpty(); });
166 _rFields
.resize(aDest
- _rFields
.begin());
169 sal_uInt32
IndexFieldsControl::GetTotalCellWidth(sal_Int32 _nRow
, sal_uInt16 _nColId
)
171 if (COLUMN_ID_ORDER
== _nColId
)
173 sal_Int32 nWidthAsc
= GetTextWidth(m_sAscendingText
) + GetSettings().GetStyleSettings().GetScrollBarSize();
174 sal_Int32 nWidthDesc
= GetTextWidth(m_sDescendingText
) + GetSettings().GetStyleSettings().GetScrollBarSize();
175 // maximum plus some additional space
176 return std::max(nWidthAsc
, nWidthDesc
) + GetTextWidth(OUString('0')) * 2;
178 return EditBrowseBox::GetTotalCellWidth(_nRow
, _nColId
);
181 void IndexFieldsControl::Init(const Sequence
< OUString
>& _rAvailableFields
, bool _bAddIndexAppendix
)
183 m_bAddIndexAppendix
= _bAddIndexAppendix
;
187 // for the width: both columns together should be somewhat smaller than the whole window (without the scrollbar)
188 sal_Int32 nFieldNameWidth
= GetSizePixel().Width();
190 if ( m_bAddIndexAppendix
)
192 m_sAscendingText
= DBA_RES(STR_ORDER_ASCENDING
);
193 m_sDescendingText
= DBA_RES(STR_ORDER_DESCENDING
);
195 // the "sort order" column
196 OUString sColumnName
= DBA_RES(STR_TAB_INDEX_SORTORDER
);
197 // the width of the order column is the maximum widths of the texts used
198 // (the title of the column)
199 sal_Int32 nSortOrderColumnWidth
= GetTextWidth(sColumnName
);
200 // ("ascending" + scrollbar width)
201 sal_Int32 nOther
= GetTextWidth(m_sAscendingText
) + GetSettings().GetStyleSettings().GetScrollBarSize();
202 nSortOrderColumnWidth
= std::max(nSortOrderColumnWidth
, nOther
);
203 // ("descending" + scrollbar width)
204 nOther
= GetTextWidth(m_sDescendingText
) + GetSettings().GetStyleSettings().GetScrollBarSize();
205 nSortOrderColumnWidth
= std::max(nSortOrderColumnWidth
, nOther
);
206 // (plus some additional space)
207 nSortOrderColumnWidth
+= GetTextWidth(OUString('0')) * 2;
208 InsertDataColumn(COLUMN_ID_ORDER
, sColumnName
, nSortOrderColumnWidth
, HeaderBarItemBits::STDSTYLE
, 1);
210 m_pSortingCell
= VclPtr
<ListBoxControl
>::Create(&GetDataWindow());
211 weld::ComboBox
& rSortingListBox
= m_pSortingCell
->get_widget();
212 rSortingListBox
.append_text(m_sAscendingText
);
213 rSortingListBox
.append_text(m_sDescendingText
);
214 rSortingListBox
.set_help_id(HID_DLGINDEX_INDEXDETAILS_SORTORDER
);
216 nFieldNameWidth
-= nSortOrderColumnWidth
;
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);
225 // create the cell controllers
226 // for the field name cell
227 m_pFieldNameCell
= VclPtr
<ListBoxControl
>::Create(&GetDataWindow());
228 weld::ComboBox
& rNameListBox
= m_pFieldNameCell
->get_widget();
229 rNameListBox
.append_text(OUString());
230 rNameListBox
.set_help_id(HID_DLGINDEX_INDEXDETAILS_FIELD
);
231 for (auto& text
: _rAvailableFields
)
232 rNameListBox
.append_text(text
);
235 CellController
* IndexFieldsControl::GetController(sal_Int32 _nRow
, sal_uInt16 _nColumnId
)
240 IndexFields::const_iterator aRow
;
241 bool bNewField
= !implGetFieldDesc(_nRow
, aRow
);
243 DbaMouseDownListBoxController
* pReturn
= nullptr;
246 case COLUMN_ID_ORDER
:
247 if (!bNewField
&& m_pSortingCell
&& !aRow
->sFieldName
.isEmpty())
248 pReturn
= new DbaMouseDownListBoxController(m_pSortingCell
);
251 case COLUMN_ID_FIELDNAME
:
252 pReturn
= new DbaMouseDownListBoxController(m_pFieldNameCell
);
256 OSL_FAIL("IndexFieldsControl::GetController: invalid column id!");
260 pReturn
->SetAdditionalModifyHdl(LINK(this, IndexFieldsControl
, OnListEntrySelected
));
265 bool IndexFieldsControl::implGetFieldDesc(sal_Int32 _nRow
, IndexFields::const_iterator
& _rPos
)
267 _rPos
= m_aFields
.end();
268 if ((_nRow
< 0) || (o3tl::make_unsigned(_nRow
) >= m_aFields
.size()))
270 _rPos
= m_aFields
.begin() + _nRow
;
274 bool IndexFieldsControl::SaveModified()
279 switch (GetCurColumnId())
281 case COLUMN_ID_FIELDNAME
:
283 weld::ComboBox
& rNameListBox
= m_pFieldNameCell
->get_widget();
284 OUString sFieldSelected
= rNameListBox
.get_active_text();
285 bool bEmptySelected
= sFieldSelected
.isEmpty();
290 // add a new field to the collection
291 OIndexField aNewField
;
292 aNewField
.sFieldName
= sFieldSelected
;
293 m_aFields
.push_back(aNewField
);
294 RowInserted(GetRowCount());
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
303 // remove the field from the selection
304 IndexFields::iterator aPos
= m_aFields
.begin() + nRow
;
308 aPos
->sFieldName
.clear();
310 // invalidate the row to force repaint
311 Invalidate(GetRowRectPixel(nRow
));
315 if (sFieldSelected
== aPos
->sFieldName
)
319 aPos
->sFieldName
= sFieldSelected
;
323 Invalidate(GetRowRectPixel(GetCurRow()));
326 case COLUMN_ID_ORDER
:
328 OSL_ENSURE(!isNewField(), "IndexFieldsControl::SaveModified: why the hell ...!!!");
330 weld::ComboBox
& rSortingListBox
= m_pSortingCell
->get_widget();
331 sal_Int32 nPos
= rSortingListBox
.get_active();
332 OSL_ENSURE(nPos
!= -1, "IndexFieldsControl::SaveModified: how did you get this selection??");
333 // adjust the sort flag in the index field description
334 OIndexField
& rCurrentField
= m_aFields
[GetCurRow()];
335 rCurrentField
.bSortAscending
= (0 == nPos
);
340 OSL_FAIL("IndexFieldsControl::SaveModified: invalid column id!");
345 void IndexFieldsControl::InitController(CellControllerRef
& /*_rController*/, sal_Int32 _nRow
, sal_uInt16 _nColumnId
)
347 IndexFields::const_iterator aFieldDescription
;
348 bool bNewField
= !implGetFieldDesc(_nRow
, aFieldDescription
);
352 case COLUMN_ID_FIELDNAME
:
354 weld::ComboBox
& rNameListBox
= m_pFieldNameCell
->get_widget();
355 rNameListBox
.set_active_text(bNewField
? OUString() : aFieldDescription
->sFieldName
);
356 rNameListBox
.save_value();
360 case COLUMN_ID_ORDER
:
362 weld::ComboBox
& rSortingListBox
= m_pSortingCell
->get_widget();
363 rSortingListBox
.set_active_text(aFieldDescription
->bSortAscending
? m_sAscendingText
: m_sDescendingText
);
364 rSortingListBox
.save_value();
369 OSL_FAIL("IndexFieldsControl::InitController: invalid column id!");
373 IMPL_LINK( IndexFieldsControl
, OnListEntrySelected
, DbaMouseDownListBoxController
&, rController
, void )
375 weld::ComboBox
& rListBox
= rController
.GetListBox();
376 if (!rListBox
.get_popup_shown())
377 m_aModifyHdl
.Call(*this);
379 if (&rListBox
!= &m_pFieldNameCell
->get_widget())
382 // a field has been selected
383 if (GetCurRow() >= GetRowCount() - 2)
384 { // and we're in one of the last two rows
385 OUString sSelectedEntry
= rListBox
.get_active_text();
386 sal_Int32 nCurrentRow
= GetCurRow();
387 sal_Int32 rowCount
= GetRowCount();
389 OSL_ENSURE((static_cast<sal_Int32
>(m_aFields
.size() + 1)) == rowCount
, "IndexFieldsControl::OnListEntrySelected: inconsistence!");
391 if (!sSelectedEntry
.isEmpty() && (nCurrentRow
== rowCount
- 1) /*&& (!m_nMaxColumnsInIndex || rowCount < m_nMaxColumnsInIndex )*/ )
392 { // in the last row, a non-empty string has been selected
393 // -> insert a new row
394 m_aFields
.emplace_back();
395 RowInserted(GetRowCount());
396 Invalidate(GetRowRectPixel(nCurrentRow
));
398 else if (sSelectedEntry
.isEmpty() && (nCurrentRow
== rowCount
- 2))
399 { // in the (last-1)th row, an empty entry has been selected
400 // -> remove the last row
401 m_aFields
.pop_back();
402 RowRemoved(GetRowCount() - 1);
403 Invalidate(GetRowRectPixel(nCurrentRow
));
409 OUString
IndexFieldsControl::GetCellText(sal_Int32 _nRow
,sal_uInt16 nColId
) const
411 IndexFields::const_iterator aRow
= m_aFields
.end();
414 aRow
= m_aFields
.begin() + _nRow
;
415 OSL_ENSURE(aRow
<= m_aFields
.end(), "IndexFieldsControl::SeekRow: invalid row!");
417 return GetRowCellText(aRow
,nColId
);
419 OUString
IndexFieldsControl::GetRowCellText(const IndexFields::const_iterator
& _rRow
,sal_uInt16 nColId
) const
421 if (_rRow
< m_aFields
.end())
425 case COLUMN_ID_FIELDNAME
:
426 return _rRow
->sFieldName
;
427 case COLUMN_ID_ORDER
:
428 if (_rRow
->sFieldName
.isEmpty())
431 return _rRow
->bSortAscending
? m_sAscendingText
: m_sDescendingText
;
433 OSL_FAIL("IndexFieldsControl::GetCurrentRowCellText: invalid column id!");
438 bool IndexFieldsControl::IsTabAllowed(bool /*bForward*/) const
445 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */