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