1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_dbaccess.hxx"
26 
27 #ifndef _DBAUI_INDEXFIELDSCONTROL_HXX_
28 #include "indexfieldscontrol.hxx"
29 #endif
30 #ifndef _DBU_DLG_HRC_
31 #include "dbu_dlg.hrc"
32 #endif
33 #ifndef _OSL_DIAGNOSE_H_
34 #include <osl/diagnose.h>
35 #endif
36 #ifndef _DBA_DBACCESS_HELPID_HRC_
37 #include "dbaccess_helpid.hrc"
38 #endif
39 
40 //......................................................................
41 namespace dbaui
42 {
43 //......................................................................
44 
45 #define BROWSER_STANDARD_FLAGS		BROWSER_COLUMNSELECTION | BROWSER_HLINESFULL | BROWSER_VLINESFULL |	\
46 									BROWSER_HIDECURSOR | BROWSER_HIDESELECT | BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL
47 
48 #define COLUMN_ID_FIELDNAME		1
49 #define	COLUMN_ID_ORDER			2
50 
51 	using namespace ::com::sun::star::uno;
52 	using namespace ::svt;
53 
54 	//==================================================================
55 	//= DbaMouseDownListBoxController
56 	//==================================================================
57 	class DbaMouseDownListBoxController : public ListBoxCellController
58 	{
59 	protected:
60 		Link	m_aOriginalModifyHdl;
61 		Link	m_aAdditionalModifyHdl;
62 
63 	public:
64 		DbaMouseDownListBoxController(ListBoxControl* _pParent)
65 			:ListBoxCellController(_pParent)
66 		{
67 		}
68 
69 		void SetAdditionalModifyHdl(const Link& _rHdl);
70 
71 	protected:
72 		virtual sal_Bool WantMouseEvent() const { return sal_True; }
73 		virtual void SetModifyHdl(const Link& _rHdl);
74 
75 	private:
76 		void implCheckLinks();
77 		DECL_LINK( OnMultiplexModify, void* );
78 	};
79 
80 	//------------------------------------------------------------------
81 	void DbaMouseDownListBoxController::SetAdditionalModifyHdl(const Link& _rHdl)
82 	{
83 		m_aAdditionalModifyHdl = _rHdl;
84 		implCheckLinks();
85 	}
86 
87 	//------------------------------------------------------------------
88 	void DbaMouseDownListBoxController::SetModifyHdl(const Link& _rHdl)
89 	{
90 		m_aOriginalModifyHdl = _rHdl;
91 		implCheckLinks();
92 	}
93 
94 	//------------------------------------------------------------------
95 	IMPL_LINK( DbaMouseDownListBoxController, OnMultiplexModify, void*, _pArg )
96 	{
97 		if (m_aAdditionalModifyHdl.IsSet())
98 			m_aAdditionalModifyHdl.Call(_pArg);
99 		if (m_aOriginalModifyHdl.IsSet())
100 			m_aOriginalModifyHdl.Call(_pArg);
101 		return 0L;
102 	}
103 
104 	//------------------------------------------------------------------
105 	void DbaMouseDownListBoxController::implCheckLinks()
106 	{
107 		if (m_aAdditionalModifyHdl.IsSet() || m_aOriginalModifyHdl.IsSet())
108 			ListBoxCellController::SetModifyHdl(LINK(this, DbaMouseDownListBoxController, OnMultiplexModify));
109 		else
110 			ListBoxCellController::SetModifyHdl(Link());
111 	}
112 
113 	//==================================================================
114 	//= IndexFieldsControl
115 	//==================================================================
116 DBG_NAME(IndexFieldsControl)
117 //------------------------------------------------------------------
118 	IndexFieldsControl::IndexFieldsControl( Window* _pParent, const ResId& _rId ,sal_Int32 _nMaxColumnsInIndex,sal_Bool _bAddIndexAppendix)
119 		:EditBrowseBox(_pParent, _rId, EBBF_SMART_TAB_TRAVEL | EBBF_ACTIVATE_ON_BUTTONDOWN, BROWSER_STANDARD_FLAGS)
120 		,m_aSeekRow(m_aFields.end())
121 		,m_pSortingCell(NULL)
122 		,m_pFieldNameCell(NULL)
123 		,m_nMaxColumnsInIndex(_nMaxColumnsInIndex)
124         ,m_bAddIndexAppendix(_bAddIndexAppendix)
125 	{
126         DBG_CTOR(IndexFieldsControl,NULL);
127 
128 		SetUniqueId( UID_DLGINDEX_INDEXDETAILS_BACK );
129 		GetDataWindow().SetUniqueId( UID_DLGINDEX_INDEXDETAILS_MAIN );
130 	}
131 
132 	//------------------------------------------------------------------
133 	IndexFieldsControl::~IndexFieldsControl()
134 	{
135 		delete m_pSortingCell;
136 		delete m_pFieldNameCell;
137 
138         DBG_DTOR(IndexFieldsControl,NULL);
139     }
140 
141 	//------------------------------------------------------------------
142 	sal_Bool IndexFieldsControl::SeekRow(long nRow)
143 	{
144 		if (!EditBrowseBox::SeekRow(nRow))
145 			return sal_False;
146 
147 		if (nRow < 0)
148 		{
149 			m_aSeekRow = m_aFields.end();
150 		}
151 		else
152 		{
153 			m_aSeekRow = m_aFields.begin() + nRow;
154 			OSL_ENSURE(m_aSeekRow <= m_aFields.end(), "IndexFieldsControl::SeekRow: invalid row!");
155 		}
156 
157 		return sal_True;
158 	}
159 
160 	//------------------------------------------------------------------
161 	void IndexFieldsControl::PaintCell( OutputDevice& _rDev, const Rectangle& _rRect, sal_uInt16 _nColumnId ) const
162 	{
163 		Point aPos(_rRect.TopLeft());
164 		aPos.X() += 1;
165 
166 		String aText = GetRowCellText(m_aSeekRow,_nColumnId);
167 		Size TxtSize(GetDataWindow().GetTextWidth(aText), GetDataWindow().GetTextHeight());
168 
169 		// clipping
170 		if (aPos.X() < _rRect.Right() || aPos.X() + TxtSize.Width() > _rRect.Right() ||
171 			aPos.Y() < _rRect.Top() || aPos.Y() + TxtSize.Height() > _rRect.Bottom())
172 			_rDev.SetClipRegion( _rRect );
173 
174 		// allow for a disabled control ...
175 		sal_Bool bEnabled = IsEnabled();
176 		Color aOriginalColor = _rDev.GetTextColor();
177 		if (!bEnabled)
178 			_rDev.SetTextColor(GetSettings().GetStyleSettings().GetDisableColor());
179 
180 		// draw the text
181 		_rDev.DrawText(aPos, aText);
182 
183 		// reset the color (if necessary)
184 		if (!bEnabled)
185 			_rDev.SetTextColor(aOriginalColor);
186 
187 		if (_rDev.IsClipRegion())
188 			_rDev.SetClipRegion();
189 	}
190 
191 	//------------------------------------------------------------------
192 	void IndexFieldsControl::initializeFrom(const IndexFields& _rFields)
193 	{
194 		// copy the field descriptions
195 		m_aFields = _rFields;
196 		m_aSeekRow = m_aFields.end();
197 
198 		SetUpdateMode(sal_False);
199 		// remove all rows
200 		RowRemoved(1, GetRowCount());
201 		// insert rows for the the fields
202 		RowInserted(GetRowCount(), m_aFields.size(), sal_False);
203 		// insert an additional row for a new field for that index
204 //		if(!m_nMaxColumnsInIndex || GetRowCount() < m_nMaxColumnsInIndex )
205 		RowInserted(GetRowCount(), 1, sal_False);
206 		SetUpdateMode(sal_True);
207 
208 		GoToRowColumnId(0, COLUMN_ID_FIELDNAME);
209 	}
210 
211 	//------------------------------------------------------------------
212 	void IndexFieldsControl::commitTo(IndexFields& _rFields)
213 	{
214 		// do not just copy the array, we may have empty field names (which should not be copied)
215 		_rFields.resize(m_aFields.size());
216 		ConstIndexFieldsIterator aSource = m_aFields.begin();
217 		ConstIndexFieldsIterator aSourceEnd = m_aFields.end();
218 		IndexFieldsIterator aDest = _rFields.begin();
219 		for (; aSource < aSourceEnd; ++aSource)
220 			if (0 != aSource->sFieldName.Len())
221 			{
222 				*aDest = *aSource;
223 				++aDest;
224 			}
225 
226 		_rFields.resize(aDest - _rFields.begin());
227 	}
228 
229 	//------------------------------------------------------------------
230 	sal_uInt32 IndexFieldsControl::GetTotalCellWidth(long _nRow, sal_uInt16 _nColId)
231 	{
232 		if (COLUMN_ID_ORDER == _nColId)
233 		{
234 			sal_Int32 nWidthAsc = GetTextWidth(m_sAscendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
235 			sal_Int32 nWidthDesc = GetTextWidth(m_sDescendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
236 			// maximum plus some additional space
237 			return (nWidthAsc > nWidthDesc ? nWidthAsc : nWidthDesc) + GetTextWidth('0') * 2;
238 		}
239 		return EditBrowseBox::GetTotalCellWidth(_nRow, _nColId);
240 	}
241 
242 	//------------------------------------------------------------------
243 	void IndexFieldsControl::Init(const Sequence< ::rtl::OUString >& _rAvailableFields)
244 	{
245 		RemoveColumns();
246 
247         // for the width: both columns together should be somewhat smaller than the whole window (without the scrollbar)
248 		sal_Int32 nFieldNameWidth = GetSizePixel().Width();
249 
250         if ( m_bAddIndexAppendix )
251         {
252 		    m_sAscendingText = String(ModuleRes(STR_ORDER_ASCENDING));
253 		    m_sDescendingText = String(ModuleRes(STR_ORDER_DESCENDING));
254 
255 		    // the "sort order" column
256 		    String sColumnName = String(ModuleRes(STR_TAB_INDEX_SORTORDER));
257 		    // the width of the order column is the maximum widths of the texts used
258 		    // (the title of the column)
259 		    sal_Int32 nSortOrderColumnWidth = GetTextWidth(sColumnName);
260 		    // ("ascending" + scrollbar width)
261 		    sal_Int32 nOther = GetTextWidth(m_sAscendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
262 		    nSortOrderColumnWidth = nSortOrderColumnWidth > nOther ? nSortOrderColumnWidth : nOther;
263 		    // ("descending" + scrollbar width)
264 		    nOther = GetTextWidth(m_sDescendingText) + GetSettings().GetStyleSettings().GetScrollBarSize();
265 		    nSortOrderColumnWidth = nSortOrderColumnWidth > nOther ? nSortOrderColumnWidth : nOther;
266 		    // (plus some additional space)
267 		    nSortOrderColumnWidth += GetTextWidth('0') * 2;
268 		    InsertDataColumn(COLUMN_ID_ORDER, sColumnName, nSortOrderColumnWidth, HIB_STDSTYLE, 1);
269 
270             m_pSortingCell = new ListBoxControl(&GetDataWindow());
271 		    m_pSortingCell->InsertEntry(m_sAscendingText);
272 		    m_pSortingCell->InsertEntry(m_sDescendingText);
273             m_pSortingCell->SetHelpId( HID_DLGINDEX_INDEXDETAILS_SORTORDER );
274 
275             nFieldNameWidth -= nSortOrderColumnWidth;
276         }
277         StyleSettings aSystemStyle = Application::GetSettings().GetStyleSettings();
278 	    nFieldNameWidth -= aSystemStyle.GetScrollBarSize();
279 	    nFieldNameWidth -= 8;
280 		// the "field name" column
281 		String sColumnName = String(ModuleRes(STR_TAB_INDEX_FIELD));
282 		InsertDataColumn(COLUMN_ID_FIELDNAME, sColumnName, nFieldNameWidth, HIB_STDSTYLE, 0);
283 
284 		// create the cell controllers
285 		// for the field name cell
286 		m_pFieldNameCell = new ListBoxControl(&GetDataWindow());
287 		m_pFieldNameCell->InsertEntry(String());
288         m_pFieldNameCell->SetHelpId( HID_DLGINDEX_INDEXDETAILS_FIELD );
289 		const ::rtl::OUString* pFields = _rAvailableFields.getConstArray();
290 		const ::rtl::OUString* pFieldsEnd = pFields + _rAvailableFields.getLength();
291 		for (;pFields < pFieldsEnd; ++pFields)
292 			m_pFieldNameCell->InsertEntry(*pFields);
293 	}
294 
295 	//------------------------------------------------------------------
296 	CellController* IndexFieldsControl::GetController(long _nRow, sal_uInt16 _nColumnId)
297 	{
298 		if (!IsEnabled())
299 			return NULL;
300 
301 		ConstIndexFieldsIterator aRow;
302 		sal_Bool bNewField = !implGetFieldDesc(_nRow, aRow);
303 
304 		DbaMouseDownListBoxController* pReturn = NULL;
305 		switch (_nColumnId)
306 		{
307 			case COLUMN_ID_ORDER:
308 				if (!bNewField && m_pSortingCell && 0 != aRow->sFieldName.Len())
309 					pReturn = new DbaMouseDownListBoxController(m_pSortingCell);
310 				break;
311 
312 			case COLUMN_ID_FIELDNAME:
313 				pReturn = new DbaMouseDownListBoxController(m_pFieldNameCell);
314 				break;
315 
316 			default:
317 				OSL_ENSURE(sal_False, "IndexFieldsControl::GetController: invalid column id!");
318 		}
319 
320 		if (pReturn)
321 			pReturn->SetAdditionalModifyHdl(LINK(this, IndexFieldsControl, OnListEntrySelected));
322 
323 		return pReturn;
324 	}
325 
326 	//------------------------------------------------------------------
327 	sal_Bool IndexFieldsControl::implGetFieldDesc(long _nRow, ConstIndexFieldsIterator& _rPos)
328 	{
329 		_rPos = m_aFields.end();
330 		if ((_nRow < 0) || (_nRow >= (sal_Int32)m_aFields.size()))
331 			return sal_False;
332 		_rPos = m_aFields.begin() + _nRow;
333 		return sal_True;
334 	}
335 
336 	//------------------------------------------------------------------
337 	sal_Bool IndexFieldsControl::IsModified() const
338 	{
339 		return EditBrowseBox::IsModified();
340 	}
341 
342 	//------------------------------------------------------------------
343 	sal_Bool IndexFieldsControl::SaveModified()
344 	{
345 		if (!IsModified())
346 			return sal_True;
347 
348 		switch (GetCurColumnId())
349 		{
350 			case COLUMN_ID_FIELDNAME:
351 			{
352 				String sFieldSelected = m_pFieldNameCell->GetSelectEntry();
353 				sal_Bool bEmptySelected = 0 == sFieldSelected.Len();
354 				if (isNewField())
355 				{
356 					if (!bEmptySelected)
357 					{
358 						// add a new field to the collection
359 						OIndexField aNewField;
360 						aNewField.sFieldName = sFieldSelected;
361 						m_aFields.push_back(aNewField);
362 						RowInserted(GetRowCount(), 1, sal_True);
363 					}
364 				}
365 				else
366 				{
367 					sal_Int32 nRow = GetCurRow();
368 					OSL_ENSURE(nRow < (sal_Int32)m_aFields.size(), "IndexFieldsControl::SaveModified: invalid current row!");
369 					if (nRow >= 0)	// may be -1 in case the control was empty
370 					{
371 						// remove the field from the selection
372 						IndexFieldsIterator aPos = m_aFields.begin() + nRow;
373 
374 						if (bEmptySelected)
375 						{
376 							aPos->sFieldName = String();
377 
378 							// invalidate the row to force repaint
379 							Invalidate(GetRowRectPixel(nRow));
380 							return sal_True;
381 						}
382 
383 						if (sFieldSelected == aPos->sFieldName)
384 							// nothing changed
385 							return sal_True;
386 
387 						aPos->sFieldName = sFieldSelected;
388 					}
389 				}
390 
391 				Invalidate(GetRowRectPixel(GetCurRow()));
392 			}
393 			break;
394 			case COLUMN_ID_ORDER:
395 			{
396 				OSL_ENSURE(!isNewField(), "IndexFieldsControl::SaveModified: why the hell ...!!!");
397 				// selected entry
398 				sal_uInt16 nPos = m_pSortingCell->GetSelectEntryPos();
399 				OSL_ENSURE(LISTBOX_ENTRY_NOTFOUND != nPos, "IndexFieldsControl::SaveModified: how did you get this selection??");
400 				// adjust the sort flag in the index field description
401 				OIndexField& rCurrentField = m_aFields[GetCurRow()];
402 				rCurrentField.bSortAscending = (0 == nPos);
403 
404 			}
405 			break;
406 			default:
407 				OSL_ENSURE(sal_False, "IndexFieldsControl::SaveModified: invalid column id!");
408 		}
409 		return sal_True;
410 	}
411 
412 	//------------------------------------------------------------------
413 	void IndexFieldsControl::InitController(CellControllerRef& /*_rController*/, long _nRow, sal_uInt16 _nColumnId)
414 	{
415 		ConstIndexFieldsIterator aFieldDescription;
416 		sal_Bool bNewField = !implGetFieldDesc(_nRow, aFieldDescription);
417 
418 		switch (_nColumnId)
419 		{
420 			case COLUMN_ID_FIELDNAME:
421 				m_pFieldNameCell->SelectEntry(bNewField ? String() : aFieldDescription->sFieldName);
422 				m_pFieldNameCell->SaveValue();
423 				break;
424 
425 			case COLUMN_ID_ORDER:
426 				m_pSortingCell->SelectEntry(aFieldDescription->bSortAscending ? m_sAscendingText : m_sDescendingText);
427 				m_pSortingCell->SaveValue();
428 				break;
429 
430 			default:
431 				OSL_ENSURE(sal_False, "IndexFieldsControl::InitController: invalid column id!");
432 		}
433 	}
434 
435 	//------------------------------------------------------------------
436 	IMPL_LINK( IndexFieldsControl, OnListEntrySelected, ListBox*, _pBox )
437 	{
438 		if (!_pBox->IsTravelSelect() && m_aModifyHdl.IsSet())
439 			m_aModifyHdl.Call(this);
440 
441 		if (_pBox == m_pFieldNameCell)
442 		{	// a field has been selected
443 			if (GetCurRow() >= GetRowCount() - 2)
444 			{	// and we're in one of the last two rows
445 				String sSelectedEntry = m_pFieldNameCell->GetSelectEntry();
446 				sal_Int32 nCurrentRow = GetCurRow();
447 				sal_Int32 rowCount = GetRowCount();
448 
449 				OSL_ENSURE(((sal_Int32)(m_aFields.size() + 1)) == rowCount, "IndexFieldsControl::OnListEntrySelected: inconsistence!");
450 
451 				if (sSelectedEntry.Len() && (nCurrentRow == rowCount - 1) /*&& (!m_nMaxColumnsInIndex || rowCount < m_nMaxColumnsInIndex )*/ )
452 				{	// in the last row, an non-empty string has been selected
453 					// -> insert a new row
454 					m_aFields.push_back(OIndexField());
455 					RowInserted(GetRowCount(), 1);
456 					Invalidate(GetRowRectPixel(nCurrentRow));
457 				}
458 				else if (!sSelectedEntry.Len() && (nCurrentRow == rowCount - 2))
459 				{	// in the (last-1)th row, an empty entry has been selected
460 					// -> remove the last row
461 					m_aFields.erase(m_aFields.end() - 1);
462 					RowRemoved(GetRowCount() - 1, 1);
463 					Invalidate(GetRowRectPixel(nCurrentRow));
464 				}
465 			}
466 
467 			SaveModified();
468 		}
469 		return 0L;
470 	}
471 	//------------------------------------------------------------------
472 	String IndexFieldsControl::GetCellText(long _nRow,sal_uInt16 nColId) const
473 	{
474 		ConstIndexFieldsIterator aRow = m_aFields.end();
475 		if ( _nRow >= 0 )
476 		{
477 			aRow = m_aFields.begin() + _nRow;
478 			OSL_ENSURE(aRow <= m_aFields.end(), "IndexFieldsControl::SeekRow: invalid row!");
479 		}
480 		return GetRowCellText(aRow,nColId);
481 	}
482 	//------------------------------------------------------------------
483 	String IndexFieldsControl::GetRowCellText(const ConstIndexFieldsIterator& _rRow,sal_uInt16 nColId) const
484 	{
485 		if (_rRow < m_aFields.end())
486 		{
487 			switch (nColId)
488 			{
489 				case COLUMN_ID_FIELDNAME:
490 					return _rRow->sFieldName;
491 				case COLUMN_ID_ORDER:
492 					if (0 == _rRow->sFieldName.Len())
493 						return String();
494 					else
495 						return _rRow->bSortAscending ? m_sAscendingText : m_sDescendingText;
496 				default:
497 					OSL_ENSURE(sal_False, "IndexFieldsControl::GetCurrentRowCellText: invalid column id!");
498 			}
499 		}
500 		return String();
501 	}
502 	//------------------------------------------------------------------
503 	sal_Bool IndexFieldsControl::IsTabAllowed(sal_Bool /*bForward*/) const
504 	{
505 		return sal_False;
506 	}
507 	//------------------------------------------------------------------
508 
509 //......................................................................
510 }	// namespace dbaui
511 //......................................................................
512 
513