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