xref: /aoo42x/main/svx/source/fmcomp/gridctrl.cxx (revision 86e1cf34)
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_svx.hxx"
26 
27 #ifndef _SVX_FMHELP_HRC
28 #include "fmhelp.hrc"
29 #endif
30 #include <svx/gridctrl.hxx>
31 #include "gridcell.hxx"
32 #include "svx/dbtoolsclient.hxx"
33 #include "svx/fmtools.hxx"
34 #include <svtools/stringtransfer.hxx>
35 
36 #ifndef _SVX_FMPROP_HRC
37 #include "fmprop.hrc"
38 #endif
39 #include <svtools/stringtransfer.hxx>
40 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
41 #include <com/sun/star/accessibility/XAccessible.hpp>
42 #include <com/sun/star/sdb/XResultSetAccess.hpp>
43 #include <com/sun/star/sdb/RowChangeAction.hpp>
44 #include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
45 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
46 #include <com/sun/star/sdbcx/Privilege.hpp>
47 #include <com/sun/star/container/XChild.hpp>
48 #include <com/sun/star/util/XNumberFormatter.hpp>
49 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
50 #include <com/sun/star/util/XCloneable.hpp>
51 #include <com/sun/star/beans/XPropertySet.hpp>
52 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
53 #include <comphelper/extract.hxx>
54 #include <tools/resid.hxx>
55 #include <tools/diagnose_ex.h>
56 #include <vcl/sound.hxx>
57 #include <vcl/menu.hxx>
58 
59 #ifndef _SVX_FMRESIDS_HRC
60 #include "svx/fmresids.hrc"
61 #endif
62 
63 #ifndef _SVX_SVXIDS_HRC
64 #include <svx/svxids.hrc>
65 #endif
66 #include <tools/shl.hxx>
67 #include <svx/dialmgr.hxx>
68 #include "fmservs.hxx"
69 #include "sdbdatacolumn.hxx"
70 
71 #define HANDLE_ID	0
72 
73 #include <comphelper/stl_types.hxx>
74 #include <comphelper/property.hxx>
75 #include "trace.hxx"
76 
77 #include <algorithm>
78 
79 using namespace ::svxform;
80 using namespace ::svt;
81 using namespace ::com::sun::star::beans;
82 using namespace ::com::sun::star::lang;
83 using namespace ::com::sun::star::uno;
84 using namespace ::com::sun::star::sdbc;
85 using namespace ::com::sun::star::sdbcx;
86 using namespace ::com::sun::star::sdb;
87 using namespace ::com::sun::star::datatransfer;
88 using namespace ::com::sun::star::container;
89 using namespace com::sun::star::accessibility;
90 
91 #define ROWSTATUS(row)	!row.Is() ? "NULL" : row->GetStatus() == GRS_CLEAN ? "CLEAN" : row->GetStatus() == GRS_MODIFIED ? "MODIFIED" : row->GetStatus() == GRS_DELETED ? "DELETED" : "INVALID"
92 
93 
94 #define DEFAULT_BROWSE_MODE             \
95 			  BROWSER_COLUMNSELECTION   \
96 			| BROWSER_MULTISELECTION    \
97             | BROWSER_KEEPSELECTION     \
98 			| BROWSER_TRACKING_TIPS     \
99 			| BROWSER_HLINESFULL        \
100 			| BROWSER_VLINESFULL        \
101 			| BROWSER_HEADERBAR_NEW     \
102 
103 class RowSetEventListener : public ::cppu::WeakImplHelper1<XRowsChangeListener>
104 {
105     DbGridControl* m_pControl;
106 public:
107     RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
108     {
109     }
110 private:
111     // XEventListener
112     virtual void SAL_CALL disposing(const ::com::sun::star::lang::EventObject& /*i_aEvt*/) throw ( RuntimeException )
113     {
114     }
115     virtual void SAL_CALL rowsChanged(const ::com::sun::star::sdb::RowsChangeEvent& i_aEvt) throw ( RuntimeException )
116     {
117         if ( i_aEvt.Action == RowChangeAction::UPDATE )
118         {
119             ::DbGridControl::GrantControlAccess aAccess;
120             CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
121             const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
122             const Any* pIter = i_aEvt.Bookmarks.getConstArray();
123             const Any* pEnd  = pIter + i_aEvt.Bookmarks.getLength();
124             for(;pIter != pEnd;++pIter)
125             {
126                 pSeek->moveToBookmark(*pIter);
127 			    // get the data
128 			    rSeekRow->SetState(pSeek, sal_True);
129                 sal_Int32 nSeekPos = pSeek->getRow() - 1;
130 			    m_pControl->SetSeekPos(nSeekPos,aAccess);
131                 m_pControl->RowModified(nSeekPos);
132             }
133         }
134     }
135 };
136 //==============================================================================
137 
138 class GridFieldValueListener;
139 DECLARE_STL_MAP(sal_uInt16, GridFieldValueListener*, ::std::less<sal_uInt16>, ColumnFieldValueListeners);
140 
141 //==============================================================================
142 
143 DBG_NAME(GridFieldValueListener)
144 class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
145 {
146 	osl::Mutex							m_aMutex;
147 	DbGridControl&						m_rParent;
148 	::comphelper::OPropertyChangeMultiplexer*	m_pRealListener;
149 	sal_uInt16							m_nId;
150 	sal_Int16							m_nSuspended;
151 	sal_Bool							m_bDisposed : 1;
152 
153 public:
154 	GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
155 	virtual ~GridFieldValueListener();
156 
157 	virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException );
158 
159 	void suspend() { ++m_nSuspended; }
160 	void resume() { --m_nSuspended; }
161 
162 	void dispose();
163 };
164 //------------------------------------------------------------------------------
165 GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId)
166 	:OPropertyChangeListener(m_aMutex)
167 	,m_rParent(_rParent)
168     ,m_pRealListener(NULL)
169 	,m_nId(_nId)
170     ,m_nSuspended(0)
171 	,m_bDisposed(sal_False)
172 {
173 	DBG_CTOR(GridFieldValueListener, NULL);
174 	if (_rField.is())
175 	{
176 		m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
177 		m_pRealListener->addProperty(FM_PROP_VALUE);
178 		m_pRealListener->acquire();
179 	}
180 }
181 
182 //------------------------------------------------------------------------------
183 GridFieldValueListener::~GridFieldValueListener()
184 {
185 	DBG_DTOR(GridFieldValueListener, NULL);
186 	dispose();
187 }
188 
189 //------------------------------------------------------------------------------
190 void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& _evt) throw( RuntimeException )
191 {
192 	DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
193 	if (m_nSuspended <= 0)
194 		m_rParent.FieldValueChanged(m_nId, _evt);
195 }
196 
197 //------------------------------------------------------------------------------
198 void GridFieldValueListener::dispose()
199 {
200 	if (m_bDisposed)
201 	{
202 		DBG_ASSERT(m_pRealListener == NULL, "GridFieldValueListener::dispose : inconsistent !");
203 		return;
204 	}
205 
206 	if (m_pRealListener)
207 	{
208 		m_pRealListener->dispose();
209 		m_pRealListener->release();
210 		m_pRealListener = NULL;
211 	}
212 
213 	m_bDisposed = sal_True;
214 	m_rParent.FieldListenerDisposing(m_nId);
215 }
216 
217 //==============================================================================
218 
219 class DisposeListenerGridBridge : public FmXDisposeListener
220 {
221 	osl::Mutex				m_aMutex;
222 	DbGridControl&			m_rParent;
223 	FmXDisposeMultiplexer*	m_pRealListener;
224 
225 public:
226 	DisposeListenerGridBridge(	DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId = -1);
227 	virtual ~DisposeListenerGridBridge();
228 
229 	virtual void disposing(const EventObject& _rEvent, sal_Int16 _nId) throw( RuntimeException ) { m_rParent.disposing(_nId, _rEvent); }
230 };
231 
232 //==============================================================================
233 
234 
235 DBG_NAME(DisposeListenerGridBridge)
236 //------------------------------------------------------------------------------
237 DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId)
238 	:FmXDisposeListener(m_aMutex)
239 	,m_rParent(_rParent)
240 	,m_pRealListener(NULL)
241 {
242 	DBG_CTOR(DisposeListenerGridBridge,NULL);
243 
244 	if (_rxObject.is())
245 	{
246 		m_pRealListener = new FmXDisposeMultiplexer(this, _rxObject, _rId);
247 		m_pRealListener->acquire();
248 	}
249 }
250 
251 //------------------------------------------------------------------------------
252 DisposeListenerGridBridge::~DisposeListenerGridBridge()
253 {
254 	if (m_pRealListener)
255 	{
256 		m_pRealListener->dispose();
257 		m_pRealListener->release();
258 		m_pRealListener = NULL;
259 	}
260 
261 	DBG_DTOR(DisposeListenerGridBridge,NULL);
262 }
263 
264 //==============================================================================
265 
266 static sal_uInt16 ControlMap[] =
267 	{
268 		DbGridControl::NavigationBar::RECORD_TEXT,
269 		DbGridControl::NavigationBar::RECORD_ABSOLUTE,
270 		DbGridControl::NavigationBar::RECORD_OF,
271 		DbGridControl::NavigationBar::RECORD_COUNT,
272 		DbGridControl::NavigationBar::RECORD_FIRST,
273 		DbGridControl::NavigationBar::RECORD_NEXT,
274 		DbGridControl::NavigationBar::RECORD_PREV,
275 		DbGridControl::NavigationBar::RECORD_LAST,
276 		DbGridControl::NavigationBar::RECORD_NEW,
277 		0
278 	};
279 
280 //------------------------------------------------------------------------------
281 sal_Bool CompareBookmark(const Any& aLeft, const Any& aRight)
282 {
283 	return ::comphelper::compare(aLeft, aRight);
284 }
285 
286 //==============================================================================
287 class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
288 {
289 	DbGridControl* m_pParent;
290 
291 	// a DbGridControl has no mutex, so we use our own as the base class expects one
292 	osl::Mutex		m_aMutex;
293 	sal_Int16			m_nSuspended;
294 
295 public:
296 	FmXGridSourcePropListener(DbGridControl* _pParent);
297 
298 	void suspend() { ++m_nSuspended; }
299 	void resume() { --m_nSuspended; }
300 
301 	virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException );
302 };
303 
304 //------------------------------------------------------------------------------
305 FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
306 	:OPropertyChangeListener(m_aMutex)
307 	,m_pParent(_pParent)
308 	,m_nSuspended(0)
309 {
310 	DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
311 }
312 
313 //------------------------------------------------------------------------------
314 void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
315 {
316 	DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
317 	if (m_nSuspended <= 0)
318 		m_pParent->DataSourcePropertyChanged(evt);
319 }
320 
321 //==============================================================================
322 //------------------------------------------------------------------------------
323 DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(Window* pParent, WinBits nStyle)
324 				   :NumericField(pParent, nStyle)
325 {
326 	SetMin(1);
327 	SetFirst(1);
328 	SetSpinSize(1);
329 
330 	SetDecimalDigits(0);
331 	SetStrictFormat(sal_True);
332 }
333 
334 //------------------------------------------------------------------------------
335 void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt)
336 {
337 	if (rEvt.GetKeyCode() == KEY_RETURN && GetText().Len())
338 	{
339 		sal_Int64 nRecord = GetValue();
340 		if (nRecord < GetMin() || nRecord > GetMax())
341 			return;
342 		else
343 			((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
344 	}
345 	else if (rEvt.GetKeyCode() == KEY_TAB)
346 		GetParent()->GetParent()->GrabFocus();
347 	else
348 		NumericField::KeyInput(rEvt);
349 }
350 
351 //------------------------------------------------------------------------------
352 void DbGridControl::NavigationBar::AbsolutePos::LoseFocus()
353 {
354 	NumericField::LoseFocus();
355 	sal_Int64 nRecord = GetValue();
356 	if (nRecord < GetMin() || nRecord > GetMax())
357 		return;
358 	else
359 	{
360 		((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
361 		((NavigationBar*)GetParent())->InvalidateState(NavigationBar::RECORD_ABSOLUTE);
362 	}
363 }
364 
365 //------------------------------------------------------------------------------
366 void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord)
367 {
368 	if (m_bPositioning)
369 		return;
370 	// the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition, so protect against this
371 	// recursion
372 	// 68167 - 13.08.99 - FS
373 	m_bPositioning = sal_True;
374 	((DbGridControl*)GetParent())->MoveToPosition(nRecord - 1);
375 	m_bPositioning = sal_False;
376 }
377 
378 //------------------------------------------------------------------------------
379 DbGridControl::NavigationBar::NavigationBar(Window* pParent, WinBits nStyle)
380 		  :Control(pParent, nStyle)
381 		  ,m_aRecordText(this, WB_VCENTER)
382 		  ,m_aAbsolute(this, WB_VCENTER)
383 		  ,m_aRecordOf(this, WB_VCENTER)
384 		  ,m_aRecordCount(this, WB_CENTER | WB_VCENTER)
385 		  ,m_aFirstBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
386 		  ,m_aPrevBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS)
387 		  ,m_aNextBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS)
388 		  ,m_aLastBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
389 		  ,m_aNewBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
390 		  ,m_nDefaultWidth(0)
391 		  ,m_nCurrentPos(-1)
392 		  ,m_bPositioning(sal_False)
393 {
394 	m_aFirstBtn.SetSymbol(SYMBOL_FIRST);
395 	m_aPrevBtn.SetSymbol(SYMBOL_PREV);
396 	m_aNextBtn.SetSymbol(SYMBOL_NEXT);
397 	m_aLastBtn.SetSymbol(SYMBOL_LAST);
398 	m_aNewBtn.SetModeImage(((DbGridControl*)pParent)->GetImage(DbGridControl_Base::NEW));
399 
400 	m_aFirstBtn.SetHelpId(HID_GRID_TRAVEL_FIRST);
401 	m_aPrevBtn.SetHelpId(HID_GRID_TRAVEL_PREV);
402 	m_aNextBtn.SetHelpId(HID_GRID_TRAVEL_NEXT);
403 	m_aLastBtn.SetHelpId(HID_GRID_TRAVEL_LAST);
404 	m_aNewBtn.SetHelpId(HID_GRID_TRAVEL_NEW);
405 	m_aAbsolute.SetHelpId(HID_GRID_TRAVEL_ABSOLUTE);
406 	m_aRecordCount.SetHelpId(HID_GRID_NUMBEROFRECORDS);
407 
408 	// Handler fuer Buttons einrichten
409 	m_aFirstBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
410 	m_aPrevBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
411 	m_aNextBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
412 	m_aLastBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
413 	m_aNewBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
414 
415 	m_aRecordText.SetText(XubString(SVX_RES(RID_STR_REC_TEXT)));
416 	m_aRecordOf.SetText(XubString(SVX_RES(RID_STR_REC_FROM_TEXT)));
417 	m_aRecordCount.SetText('?');
418 
419 	m_nDefaultWidth = ArrangeControls();
420 
421 	m_aFirstBtn.Disable();
422 	m_aPrevBtn.Disable();
423 	m_aNextBtn.Disable();
424 	m_aLastBtn.Disable();
425 	m_aNewBtn.Disable();
426 	m_aRecordText.Disable();
427 	m_aRecordOf.Disable();
428 	m_aRecordCount.Disable();
429 	m_aAbsolute.Disable();
430 
431 	AllSettings aSettings = m_aNextBtn.GetSettings();
432 	MouseSettings aMouseSettings = aSettings.GetMouseSettings();
433 	aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
434 	aSettings.SetMouseSettings(aMouseSettings);
435 	m_aNextBtn.SetSettings(aSettings, sal_True);
436 	m_aPrevBtn.SetSettings(aSettings, sal_True);
437 
438 	m_aFirstBtn.Show();
439 	m_aPrevBtn.Show();
440 	m_aNextBtn.Show();
441 	m_aLastBtn.Show();
442 	m_aNewBtn.Show();
443 	m_aRecordText.Show();
444 	m_aRecordOf.Show();
445 	m_aRecordCount.Show();
446 	m_aAbsolute.Show();
447 }
448 
449 namespace
450 {
451 	void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize)
452 	{
453 		_rButton.SetPosPixel( _rPos );
454 		_rButton.SetSizePixel( _rSize );
455 		_rPos.X() += (sal_uInt16)_rSize.Width();
456 	}
457 }
458 //------------------------------------------------------------------------------
459 sal_uInt16 DbGridControl::NavigationBar::ArrangeControls()
460 {
461 	// Positionierung der Controls
462 	// Basisgroessen ermitteln
463 	sal_uInt16		nX = 0;
464 	sal_uInt16		nY = 0;
465 	Rectangle	aRect(((DbGridControl*)GetParent())->GetControlArea());
466 	const long	nH		= aRect.GetSize().Height();
467 	Size		aBorder = LogicToPixel(Size(3, 3),MAP_APPFONT);
468 				aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height()));
469 
470 	// Controls Groessen und Positionen setzen
471 	//
472 	XubString aText    = m_aRecordText.GetText();
473 	long nTextWidth = m_aRecordText.GetTextWidth(aText);
474 	m_aRecordText.SetPosPixel(Point(nX,nY) );
475 	m_aRecordText.SetSizePixel(Size(nTextWidth,nH));
476 	nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
477 
478 	m_aAbsolute.SetPosPixel( Point(nX,nY));
479 	m_aAbsolute.SetSizePixel( Size(3*nH,aRect.GetSize().Height()) ); // Heuristik XXXXXXX
480 	nX = sal::static_int_cast< sal_uInt16 >(nX + (3*nH) + aBorder.Width());
481 
482 	aText	   = m_aRecordOf.GetText();
483 	nTextWidth = m_aRecordOf.GetTextWidth(aText);
484 	m_aRecordOf.SetPosPixel(Point(nX,nY) );
485 	m_aRecordOf.SetSizePixel(Size(nTextWidth,nH));
486 	nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
487 
488 	nTextWidth = m_aRecordCount.GetTextWidth( String::CreateFromAscii("0000000 (00000) *") );
489 	m_aRecordCount.SetPosPixel(Point(nX,nY) );
490 	m_aRecordCount.SetSizePixel(Size(nTextWidth,nH));
491 	nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
492 
493 	Point aButtonPos(nX,nY);
494 	Size  aButtonSize(nH,nH);
495 	SetPosAndSize(m_aFirstBtn, aButtonPos, aButtonSize);
496 	SetPosAndSize(m_aPrevBtn, aButtonPos, aButtonSize);
497 	SetPosAndSize(m_aNextBtn, aButtonPos, aButtonSize);
498 	SetPosAndSize(m_aLastBtn, aButtonPos, aButtonSize);
499 	SetPosAndSize(m_aNewBtn, aButtonPos, aButtonSize);
500 
501 	nX = sal::static_int_cast< sal_uInt16 >(
502         aButtonPos.X() + (sal_uInt16)(nH + aBorder.Width()));
503 
504 	// Ist der Font des Edits groesser als das Feld?
505 	Font aOutputFont = m_aAbsolute.GetFont();
506 	if (aOutputFont.GetSize().Height() > nH)
507 	{
508 		Font aApplFont = OutputDevice::GetDefaultFont(
509 			DEFAULTFONT_SANS_UNICODE,
510 			Application::GetSettings().GetUILanguage(),
511 			DEFAULTFONT_FLAGS_ONLYONE,
512 			this
513 		);
514 		aApplFont.SetSize( Size( 0, nH - 2 ) );
515 		m_aAbsolute.SetControlFont( aApplFont );
516 
517 		aApplFont.SetTransparent( sal_True );
518 		m_aRecordText.SetControlFont( aApplFont );
519 		m_aRecordOf.SetControlFont( aApplFont );
520 		m_aRecordCount.SetControlFont( aApplFont );
521 	}
522 	return nX;
523 }
524 
525 //------------------------------------------------------------------------------
526 IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton )
527 {
528 	DbGridControl* pParent = (DbGridControl*)GetParent();
529 
530 	if (pParent->m_aMasterSlotExecutor.IsSet())
531 	{
532 		long lResult = 0;
533 		if (pButton == &m_aFirstBtn)
534 			lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_FIRST);
535 		else if( pButton == &m_aPrevBtn )
536 			lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_PREV);
537 		else if( pButton == &m_aNextBtn )
538 			lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEXT);
539 		else if( pButton == &m_aLastBtn )
540 			lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_LAST);
541 		else if( pButton == &m_aNewBtn )
542 			lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEW);
543 
544 		if (lResult)
545 			// the link already handled it
546 			return 0;
547 	}
548 
549 	if (pButton == &m_aFirstBtn)
550 		pParent->MoveToFirst();
551 	else if( pButton == &m_aPrevBtn )
552 		pParent->MoveToPrev();
553 	else if( pButton == &m_aNextBtn )
554 		pParent->MoveToNext();
555 	else if( pButton == &m_aLastBtn )
556 		pParent->MoveToLast();
557 	else if( pButton == &m_aNewBtn )
558 		pParent->AppendNew();
559 	return 0;
560 }
561 
562 //------------------------------------------------------------------------------
563 void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, sal_Bool bAll)
564 {
565 	if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)
566 	{
567 		DbGridControl* pParent = (DbGridControl*)GetParent();
568 
569         sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControl::OPT_INSERT) ? 2 : 1);
570 
571 		// Wann mu� alles invalidiert werden
572         bAll = bAll || m_nCurrentPos <= 0;
573         bAll = bAll || nCurrentPos <= 0;
574         bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
575         bAll = bAll || nCurrentPos >= nAdjustedRowCount;
576 
577         if ( bAll )
578 		{
579 			m_nCurrentPos = nCurrentPos;
580 			int i = 0;
581 			while (ControlMap[i])
582 				SetState(ControlMap[i++]);
583 		}
584 		else	// befindet sich in der Mitte
585 		{
586 			m_nCurrentPos = nCurrentPos;
587 			SetState(NavigationBar::RECORD_COUNT);
588 			SetState(NavigationBar::RECORD_ABSOLUTE);
589 		}
590 	}
591 }
592 
593 //------------------------------------------------------------------------------
594 sal_Bool DbGridControl::NavigationBar::GetState(sal_uInt16 nWhich) const
595 {
596 	DbGridControl* pParent = (DbGridControl*)GetParent();
597 
598 	if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
599 		|| pParent->IsFilterMode() )
600 		return sal_False;
601 	else
602 	{
603 		// check if we have a master state provider
604 		if (pParent->m_aMasterStateProvider.IsSet())
605 		{
606 			long nState = pParent->m_aMasterStateProvider.Call(reinterpret_cast< void* >( nWhich ) );
607 			if (nState>=0)
608 				return (nState>0);
609 		}
610 
611 		sal_Bool bAvailable = sal_True;
612 
613 		switch (nWhich)
614 		{
615 			case NavigationBar::RECORD_FIRST:
616 			case NavigationBar::RECORD_PREV:
617 				bAvailable = m_nCurrentPos > 0;
618 				break;
619 			case NavigationBar::RECORD_NEXT:
620 				if(pParent->m_bRecordCountFinal)
621 				{
622 					bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
623 					if (!bAvailable && pParent->GetOptions() & DbGridControl::OPT_INSERT)
624 						bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
625 				}
626 				break;
627 			case NavigationBar::RECORD_LAST:
628 				if(pParent->m_bRecordCountFinal)
629 				{
630 					if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
631 						bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
632 									 m_nCurrentPos != pParent->GetRowCount() - 2;
633 					else
634 						bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
635 				}
636 				break;
637 			case NavigationBar::RECORD_NEW:
638 				bAvailable = (pParent->GetOptions() & DbGridControl::OPT_INSERT) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
639 				break;
640 			case NavigationBar::RECORD_ABSOLUTE:
641 				bAvailable = pParent->GetRowCount() > 0;
642 				break;
643 		}
644 		return bAvailable;
645 	}
646 }
647 
648 //------------------------------------------------------------------------------
649 void DbGridControl::NavigationBar::SetState(sal_uInt16 nWhich)
650 {
651 	sal_Bool bAvailable = GetState(nWhich);
652 	DbGridControl* pParent = (DbGridControl*)GetParent();
653 	Window* pWnd = NULL;
654 	switch (nWhich)
655 	{
656 		case NavigationBar::RECORD_FIRST:
657 			pWnd = &m_aFirstBtn;
658 			break;
659 		case NavigationBar::RECORD_PREV:
660 			pWnd = &m_aPrevBtn;
661 			break;
662 		case NavigationBar::RECORD_NEXT:
663 			pWnd = &m_aNextBtn;
664 			break;
665 		case NavigationBar::RECORD_LAST:
666 			pWnd = &m_aLastBtn;
667 			break;
668 		case NavigationBar::RECORD_NEW:
669 			pWnd = &m_aNewBtn;
670 			break;
671 		case NavigationBar::RECORD_ABSOLUTE:
672 			pWnd = &m_aAbsolute;
673 			if (bAvailable)
674 			{
675 				if (pParent->m_nTotalCount >= 0)
676 				{
677 					if (pParent->IsCurrentAppending())
678 						m_aAbsolute.SetMax(pParent->m_nTotalCount + 1);
679 					else
680 						m_aAbsolute.SetMax(pParent->m_nTotalCount);
681 				}
682 				else
683 					m_aAbsolute.SetMax(LONG_MAX);
684 
685 				m_aAbsolute.SetValue(m_nCurrentPos + 1);
686 			}
687 			else
688 				m_aAbsolute.SetText(String());
689 			break;
690 		case NavigationBar::RECORD_TEXT:
691 			pWnd = &m_aRecordText;
692 			break;
693 		case NavigationBar::RECORD_OF:
694 			pWnd = &m_aRecordOf;
695 			break;
696 		case NavigationBar::RECORD_COUNT:
697 		{
698 			pWnd = &m_aRecordCount;
699 			String aText;
700 			if (bAvailable)
701 			{
702 				if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
703 				{
704 					if (pParent->IsCurrentAppending() && !pParent->IsModified())
705 						aText = String::CreateFromInt32(pParent->GetRowCount());
706 					else
707 						aText = String::CreateFromInt32(pParent->GetRowCount() - 1);
708 				}
709 				else
710 					aText = String::CreateFromInt32(pParent->GetRowCount());
711 				if(!pParent->m_bRecordCountFinal)
712 					aText += String::CreateFromAscii(" *");
713 			}
714 			else
715 				aText = String();
716 
717 			// add the number of selected rows, if applicable
718 			if (pParent->GetSelectRowCount())
719 			{
720 				String aExtendedInfo(aText);
721 				aExtendedInfo.AppendAscii(" (");
722 				aExtendedInfo += String::CreateFromInt32(pParent->GetSelectRowCount());
723 				aExtendedInfo += ')';
724 				pWnd->SetText(aExtendedInfo);
725 			}
726 			else
727 				pWnd->SetText(aText);
728 
729 			pParent->SetRealRowCount(aText);
730 		}	break;
731 	}
732 	DBG_ASSERT(pWnd, "kein Fenster");
733 	if (pWnd && (pWnd->IsEnabled() != bAvailable))
734 		// this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
735 		// event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
736 		// do this check.
737 		// For further explanation see Bug 69900.
738 		// FS - 18.11.99
739 		pWnd->Enable(bAvailable);
740 }
741 
742 //------------------------------------------------------------------------------
743 void DbGridControl::NavigationBar::Resize()
744 {
745 	Control::Resize();
746 	ArrangeControls();
747 }
748 
749 //------------------------------------------------------------------------------
750 void DbGridControl::NavigationBar::Paint(const Rectangle& rRect)
751 {
752 	Control::Paint(rRect);
753 	Point aAbsolutePos = m_aAbsolute.GetPosPixel();
754 	Size  aAbsoluteSize = m_aAbsolute.GetSizePixel();
755 
756 	DrawLine(Point(aAbsolutePos.X() - 1, 0 ),
757 			 Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
758 
759 	DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ),
760 			 Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
761 }
762 
763 //------------------------------------------------------------------------------
764 void DbGridControl::NavigationBar::StateChanged( StateChangedType nType )
765 {
766 	Control::StateChanged( nType );
767 
768     Window* pWindows[] = {  &m_aRecordText,
769                             &m_aAbsolute,
770                             &m_aRecordOf,
771                             &m_aRecordCount,
772                             &m_aFirstBtn,
773                             &m_aPrevBtn,
774                             &m_aNextBtn,
775                             &m_aLastBtn,
776                             &m_aNewBtn
777                         };
778 
779     switch ( nType )
780     {
781         case STATE_CHANGE_MIRRORING:
782         {
783             sal_Bool bIsRTLEnabled = IsRTLEnabled();
784             for ( size_t i=0; i < sizeof( pWindows ) / sizeof( pWindows[0] ); ++i )
785                 pWindows[i]->EnableRTL( bIsRTLEnabled );
786         }
787         break;
788 
789         case STATE_CHANGE_ZOOM:
790         {
791             Fraction aZoom = GetZoom();
792 
793             // not all of these controls need to know the new zoom, but to be sure ...
794             Font aFont( GetSettings().GetStyleSettings().GetFieldFont() );
795             if ( IsControlFont() )
796                 aFont.Merge( GetControlFont() );
797 
798             for (size_t i=0; i < sizeof(pWindows)/sizeof(pWindows[0]); ++i)
799             {
800                 pWindows[i]->SetZoom(aZoom);
801                 pWindows[i]->SetZoomedPointFont(aFont);
802             }
803 
804             SetZoomedPointFont( aFont );
805 
806             // rearrange the controls
807             m_nDefaultWidth = ArrangeControls();
808         }
809         break;
810     }
811 }
812 
813 //------------------------------------------------------------------------------
814 DbGridRow::DbGridRow(CursorWrapper* pCur, sal_Bool bPaintCursor)
815 		  :m_bIsNew(sal_False)
816 {
817 
818 	if (pCur && pCur->Is())
819 	{
820 		Reference< XIndexAccess >  xColumns(pCur->getColumns(), UNO_QUERY);
821 		DataColumn* pColumn;
822 		for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
823 		{
824 			Reference< XPropertySet > xColSet;
825 			::cppu::extractInterface(xColSet, xColumns->getByIndex(i));
826 			pColumn = new DataColumn(xColSet);
827 			m_aVariants.Insert(pColumn, LIST_APPEND);
828 		}
829 
830 		if (pCur->rowDeleted())
831 			m_eStatus = GRS_DELETED;
832 		else
833 		{
834 			if (bPaintCursor)
835 				m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GRS_INVALID : GRS_CLEAN;
836 			else
837 			{
838 				Reference< XPropertySet > xSet = pCur->getPropertySet();
839 				if (xSet.is())
840 				{
841 					m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
842 					if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
843 						m_eStatus = GRS_INVALID;
844 					else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
845 						m_eStatus = GRS_MODIFIED;
846 					else
847 						m_eStatus = GRS_CLEAN;
848 				}
849 				else
850 					m_eStatus = GRS_INVALID;
851 			}
852 		}
853 		if (!m_bIsNew && IsValid())
854 			m_aBookmark = pCur->getBookmark();
855 		else
856 			m_aBookmark = Any();
857 	}
858 	else
859 		m_eStatus = GRS_INVALID;
860 }
861 
862 //------------------------------------------------------------------------------
863 DbGridRow::~DbGridRow()
864 {
865 	sal_uInt32 nCount = m_aVariants.Count();
866 	for (sal_uInt32 i = 0; i < nCount; i++)
867 		delete m_aVariants.GetObject(i);
868 }
869 
870 //------------------------------------------------------------------------------
871 void DbGridRow::SetState(CursorWrapper* pCur, sal_Bool bPaintCursor)
872 {
873 	if (pCur && pCur->Is())
874 	{
875 		if (pCur->rowDeleted())
876 		{
877 			m_eStatus = GRS_DELETED;
878 			m_bIsNew = sal_False;
879 		}
880 		else
881 		{
882 			m_eStatus = GRS_CLEAN;
883 			if (!bPaintCursor)
884 			{
885 				Reference< XPropertySet > xSet = pCur->getPropertySet();
886 				DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
887 
888 				if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
889 					m_eStatus = GRS_MODIFIED;
890 				m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
891 			}
892 			else
893 				m_bIsNew = sal_False;
894 		}
895 
896         try
897         {
898 		    if (!m_bIsNew && IsValid())
899 			    m_aBookmark = pCur->getBookmark();
900 		    else
901 			    m_aBookmark = Any();
902         }
903         catch(SQLException&)
904         {
905             DBG_UNHANDLED_EXCEPTION();
906             m_aBookmark = Any();
907             m_eStatus = GRS_INVALID;
908 		    m_bIsNew = sal_False;
909         }
910 	}
911 	else
912 	{
913 		m_aBookmark = Any();
914 		m_eStatus = GRS_INVALID;
915 		m_bIsNew = sal_False;
916 	}
917 }
918 
919 DBG_NAME(DbGridControl);
920 //------------------------------------------------------------------------------
921 DbGridControl::DbGridControl(
922 				Reference< XMultiServiceFactory > _rxFactory,
923 				Window* pParent,
924 				WinBits nBits)
925 			:DbGridControl_Base(pParent, EBBF_NONE, nBits, DEFAULT_BROWSE_MODE )
926             ,m_xServiceFactory(_rxFactory)
927             ,m_aBar(this)
928             ,m_nAsynAdjustEvent(0)
929             ,m_pDataSourcePropMultiplexer(NULL)
930             ,m_pDataSourcePropListener(NULL)
931             ,m_pFieldListeners(NULL)
932             ,m_pCursorDisposeListener(NULL)
933             ,m_pGridListener(NULL)
934             ,m_pDataCursor(NULL)
935             ,m_pSeekCursor(NULL)
936             ,m_nSeekPos(-1)
937             ,m_nTotalCount(-1)
938             ,m_aNullDate(OTypeConversionClient().getStandardDate())
939             ,m_nMode(DEFAULT_BROWSE_MODE)
940             ,m_nCurrentPos(-1)
941             ,m_nDeleteEvent(0)
942             ,m_nOptions(OPT_READONLY)
943             ,m_nOptionMask(OPT_INSERT | OPT_UPDATE | OPT_DELETE)
944 	        ,m_nLastColId((sal_uInt16)-1)
945             ,m_nLastRowId(-1)
946             ,m_bDesignMode(sal_False)
947             ,m_bRecordCountFinal(sal_False)
948             ,m_bMultiSelection(sal_True)
949             ,m_bNavigationBar(sal_True)
950             ,m_bSynchDisplay(sal_True)
951             ,m_bForceROController(sal_False)
952             ,m_bHandle(sal_True)
953             ,m_bFilterMode(sal_False)
954             ,m_bWantDestruction(sal_False)
955             ,m_bInAdjustDataSource(sal_False)
956             ,m_bPendingAdjustRows(sal_False)
957             ,m_bHideScrollbars( sal_False )
958             ,m_bUpdating(sal_False)
959 {
960 	DBG_CTOR(DbGridControl,NULL);
961 
962     String sName(SVX_RES(RID_STR_NAVIGATIONBAR));
963     m_aBar.SetAccessibleName(sName);
964     m_aBar.Show();
965     ImplInitWindow( InitAll );
966 }
967 
968 //------------------------------------------------------------------------------
969 void DbGridControl::InsertHandleColumn()
970 {
971 	// Handle Column einfuegen
972 	// Da die BrowseBox ohne handleColums Paintprobleme hat
973 	// wird diese versteckt
974 	if (HasHandle())
975 		BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(String()));
976 	else
977 		BrowseBox::InsertHandleColumn(0);
978 }
979 
980 //------------------------------------------------------------------------------
981 void DbGridControl::Init()
982 {
983 	BrowserHeader* pNewHeader = CreateHeaderBar(this);
984 	pHeader->SetMouseTransparent(sal_False);
985 
986     SetHeaderBar(pNewHeader);
987 	SetMode(m_nMode);
988 	SetCursorColor(Color(0xFF, 0, 0));
989 
990 	InsertHandleColumn();
991 }
992 
993 //------------------------------------------------------------------------------
994 DbGridControl::~DbGridControl()
995 {
996 	RemoveColumns();
997 
998 	{
999 		m_bWantDestruction = sal_True;
1000 		osl::MutexGuard aGuard(m_aDestructionSafety);
1001 		if (m_pFieldListeners)
1002 			DisconnectFromFields();
1003 		if (m_pCursorDisposeListener)
1004 		{
1005 			delete m_pCursorDisposeListener;
1006 			m_pCursorDisposeListener = NULL;
1007 		}
1008 	}
1009 
1010 	if (m_nDeleteEvent)
1011 		Application::RemoveUserEvent(m_nDeleteEvent);
1012 
1013 	if (m_pDataSourcePropMultiplexer)
1014 	{
1015 		m_pDataSourcePropMultiplexer->dispose();
1016 		m_pDataSourcePropMultiplexer->release();	// this should delete the multiplexer
1017 		delete m_pDataSourcePropListener;
1018 		m_pDataSourcePropMultiplexer = NULL;
1019 		m_pDataSourcePropListener = NULL;
1020 	}
1021     m_xRowSetListener.clear();
1022 
1023 	delete m_pDataCursor;
1024 	delete m_pSeekCursor;
1025 
1026 	DBG_DTOR(DbGridControl,NULL);
1027 }
1028 
1029 //------------------------------------------------------------------------------
1030 void DbGridControl::StateChanged( StateChangedType nType )
1031 {
1032 	DbGridControl_Base::StateChanged( nType );
1033 
1034     switch (nType)
1035 	{
1036         case STATE_CHANGE_MIRRORING:
1037 			ImplInitWindow( InitWritingMode );
1038             Invalidate();
1039             break;
1040 
1041 		case STATE_CHANGE_ZOOM:
1042 		{
1043 			ImplInitWindow( InitFont );
1044 
1045 			// and give it a chance to rearrange
1046 			Point aPoint = GetControlArea().TopLeft();
1047 			sal_uInt16 nX = (sal_uInt16)aPoint.X();
1048 			ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1049 			ReserveControlArea((sal_uInt16)nX);
1050 		}
1051 		break;
1052 		case STATE_CHANGE_CONTROLFONT:
1053 			ImplInitWindow( InitFont );
1054 			Invalidate();
1055 			break;
1056 		case STATE_CHANGE_CONTROLFOREGROUND:
1057 			ImplInitWindow( InitForeground );
1058 			Invalidate();
1059 			break;
1060 		case STATE_CHANGE_CONTROLBACKGROUND:
1061 			ImplInitWindow( InitBackground );
1062 			Invalidate();
1063 			break;
1064 	}
1065 }
1066 
1067 //------------------------------------------------------------------------------
1068 void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
1069 {
1070 	DbGridControl_Base::DataChanged( rDCEvt );
1071 	if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS ) &&
1072 		 (rDCEvt.GetFlags() & SETTINGS_STYLE) )
1073 	{
1074 		ImplInitWindow( InitAll );
1075 		Invalidate();
1076 	}
1077 }
1078 
1079 //------------------------------------------------------------------------------
1080 void DbGridControl::Select()
1081 {
1082 	DbGridControl_Base::Select();
1083 
1084 	// as the selected rows may have changed, udate the according display in our navigation bar
1085 	m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1086 
1087 	if (m_pGridListener)
1088 		m_pGridListener->selectionChanged();
1089 }
1090 
1091 //------------------------------------------------------------------------------
1092 void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
1093 {
1094 	for ( sal_uInt32 i = 0; i < m_aColumns.Count(); ++i )
1095 	{
1096 		DbGridColumn* pCol = m_aColumns.GetObject(i);
1097 		if (pCol)
1098 			pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
1099 	}
1100 
1101     if ( ( _eInitWhat & InitWritingMode ) != 0 )
1102     {
1103 		if ( m_bNavigationBar )
1104         {
1105 			m_aBar.EnableRTL( IsRTLEnabled() );
1106         }
1107     }
1108 
1109     if ( ( _eInitWhat & InitFont ) != 0 )
1110     {
1111 		if ( m_bNavigationBar )
1112         {
1113 	        Font aFont = m_aBar.GetSettings().GetStyleSettings().GetFieldFont();
1114             if ( IsControlFont() )
1115                 m_aBar.SetControlFont( GetControlFont() );
1116             else
1117                 m_aBar.SetControlFont();
1118 
1119 			m_aBar.SetZoom( GetZoom() );
1120         }
1121     }
1122 
1123     if ( ( _eInitWhat & InitBackground ) != 0 )
1124 	{
1125 		if (IsControlBackground())
1126 		{
1127 			GetDataWindow().SetBackground(GetControlBackground());
1128 			GetDataWindow().SetControlBackground(GetControlBackground());
1129 			GetDataWindow().SetFillColor(GetControlBackground());
1130 		}
1131 		else
1132 		{
1133 			GetDataWindow().SetControlBackground();
1134 			GetDataWindow().SetFillColor(GetFillColor());
1135 		}
1136 	}
1137 }
1138 
1139 //------------------------------------------------------------------------------
1140 void DbGridControl::RemoveRows(sal_Bool bNewCursor)
1141 {
1142 	// Hat sich der DatenCursor verandert ?
1143 	if (!bNewCursor)
1144 	{
1145 		DELETEZ(m_pSeekCursor);
1146 		m_xPaintRow = m_xDataRow = m_xEmptyRow	= m_xCurrentRow = m_xSeekRow = NULL;
1147 		m_nCurrentPos = m_nSeekPos = -1;
1148 		m_nOptions	= OPT_READONLY;
1149 
1150 		RowRemoved(0, GetRowCount(), sal_False);
1151 		m_nTotalCount = -1;
1152 	}
1153 	else
1154 	{
1155 		RemoveRows();
1156 	}
1157 }
1158 
1159 //------------------------------------------------------------------------------
1160 void DbGridControl::RemoveRows()
1161 {
1162 	// we're going to remove all columns and all row, so deactivate the current cell
1163 	if (IsEditing())
1164 		DeactivateCell();
1165 
1166 	// alle Columns deinitialisieren
1167 	// existieren Spalten, dann alle Controller freigeben
1168 	for (sal_uInt32 i = 0; i < m_aColumns.Count(); i++)
1169 		m_aColumns.GetObject(i)->Clear();
1170 
1171 	DELETEZ(m_pSeekCursor);
1172 	DELETEZ(m_pDataCursor);
1173 
1174 	m_xPaintRow = m_xDataRow = m_xEmptyRow	= m_xCurrentRow = m_xSeekRow = NULL;
1175 	m_nCurrentPos = m_nSeekPos = m_nTotalCount	= -1;
1176 	m_nOptions	= OPT_READONLY;
1177 
1178 	// Anzahl Saetze im Browser auf 0 zuruecksetzen
1179 	DbGridControl_Base::RemoveRows();
1180 	m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
1181 }
1182 
1183 //------------------------------------------------------------------------------
1184 void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
1185 {
1186 	// Positionierung der Controls
1187 	if (m_bNavigationBar)
1188 	{
1189 		nX = m_aBar.GetDefaultWidth();
1190 		Rectangle	aRect(GetControlArea());
1191 		m_aBar.SetPosSizePixel(Point(0,nY + 1), Size(nX, aRect.GetSize().Height() - 1));
1192 	}
1193 }
1194 
1195 //------------------------------------------------------------------------------
1196 void DbGridControl::EnableHandle(sal_Bool bEnable)
1197 {
1198 	if (m_bHandle == bEnable)
1199 		return;
1200 
1201 	// HandleColumn wird nur ausgeblendet,
1202 	// da es sonst etliche Probleme mit dem Zeichnen gibt
1203 	RemoveColumn(0);
1204 	m_bHandle = bEnable;
1205 	InsertHandleColumn();
1206 }
1207 
1208 //------------------------------------------------------------------------------
1209 namespace
1210 {
1211     bool adjustModeForScrollbars( BrowserMode& _rMode, sal_Bool _bNavigationBar, sal_Bool _bHideScrollbars )
1212     {
1213         BrowserMode nOldMode = _rMode;
1214 
1215 		if ( !_bNavigationBar )
1216         {
1217             _rMode &= ~BROWSER_AUTO_HSCROLL;
1218         }
1219 
1220         if ( _bHideScrollbars )
1221         {
1222             _rMode |= ( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1223             _rMode &= ~( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1224         }
1225         else
1226         {
1227             _rMode |= ( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1228             _rMode &= ~( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1229         }
1230 
1231         // note: if we have a navigation bar, we always have a AUTO_HSCROLL. In particular,
1232         // _bHideScrollbars is ignored then
1233 		if ( _bNavigationBar )
1234         {
1235 			_rMode |= BROWSER_AUTO_HSCROLL;
1236             _rMode &= ~BROWSER_NO_HSCROLL;
1237         }
1238 
1239         return nOldMode != _rMode;
1240     }
1241 }
1242 
1243 //------------------------------------------------------------------------------
1244 void DbGridControl::EnableNavigationBar(sal_Bool bEnable)
1245 {
1246 	if (m_bNavigationBar == bEnable)
1247 		return;
1248 
1249 	m_bNavigationBar = bEnable;
1250 
1251     if (bEnable)
1252 	{
1253 		m_aBar.Show();
1254 		m_aBar.Enable();
1255 		m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
1256 
1257         if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1258             SetMode( m_nMode );
1259 
1260 		// liefert die Groe�e der Reserved ControlArea
1261 		Point aPoint = GetControlArea().TopLeft();
1262 		sal_uInt16 nX = (sal_uInt16)aPoint.X();
1263 
1264 		ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1265 		ReserveControlArea((sal_uInt16)nX);
1266 	}
1267 	else
1268 	{
1269 		m_aBar.Hide();
1270 		m_aBar.Disable();
1271 
1272         if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1273             SetMode( m_nMode );
1274 
1275 		ReserveControlArea();
1276 	}
1277 }
1278 
1279 //------------------------------------------------------------------------------
1280 sal_uInt16 DbGridControl::SetOptions(sal_uInt16 nOpt)
1281 {
1282 	DBG_ASSERT(!m_xCurrentRow || !m_xCurrentRow->IsModified(),
1283 		"DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
1284 
1285 	// for the next setDataSource (which is triggered by a refresh, for instance)
1286 	m_nOptionMask = nOpt;
1287 
1288 	// normalize the new options
1289 	Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
1290 	if (xDataSourceSet.is())
1291 	{
1292 		// feststellen welche Updatem�glichkeiten bestehen
1293 		sal_Int32 nPrivileges = 0;
1294 		xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1295 		if ((nPrivileges & Privilege::INSERT) == 0)
1296 			nOpt &= ~OPT_INSERT;
1297 		if ((nPrivileges & Privilege::UPDATE) == 0)
1298 			nOpt &= ~OPT_UPDATE;
1299 		if ((nPrivileges & Privilege::DELETE) == 0)
1300 			nOpt &= ~OPT_DELETE;
1301 	}
1302 	else
1303 		nOpt = OPT_READONLY;
1304 
1305 	// need to do something after that ?
1306 	if (nOpt == m_nOptions)
1307 		return m_nOptions;
1308 
1309 	// the 'update' option only affects our BrowserMode (with or w/o focus rect)
1310 	BrowserMode nNewMode = m_nMode;
1311 	if ((m_nMode & BROWSER_CURSOR_WO_FOCUS) == 0)
1312 	{
1313 		if (nOpt & OPT_UPDATE)
1314 			nNewMode |= BROWSER_HIDECURSOR;
1315 		else
1316 			nNewMode &= ~BROWSER_HIDECURSOR;
1317 	}
1318 	else
1319 		nNewMode &= ~BROWSER_HIDECURSOR;
1320 		// should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
1321 
1322 	if (nNewMode != m_nMode)
1323 	{
1324 		SetMode(nNewMode);
1325 		m_nMode = nNewMode;
1326 	}
1327 
1328 	// _after_ setting the mode because this results in an ActivateCell
1329 	DeactivateCell();
1330 
1331 	sal_Bool bInsertChanged = (nOpt & OPT_INSERT) != (m_nOptions & OPT_INSERT);
1332 	m_nOptions = nOpt;
1333 		// we need to set this before the code below because it indirectly uses m_nOptions
1334 
1335 	// the 'insert' option affects our empty row
1336 	if (bInsertChanged)
1337 	{
1338 		if (m_nOptions & OPT_INSERT)
1339 		{	// the insert option is to be set
1340 			m_xEmptyRow = new DbGridRow();
1341 			RowInserted(GetRowCount(), 1, sal_True);
1342 		}
1343 		else
1344 		{	// the insert option is to be reset
1345 			m_xEmptyRow = NULL;
1346 			if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
1347 				GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
1348 			RowRemoved(GetRowCount(), 1, sal_True);
1349 		}
1350 	}
1351 
1352 	// the 'delete' options has no immediate consequences
1353 
1354 	ActivateCell();
1355 	Invalidate();
1356 	return m_nOptions;
1357 }
1358 
1359 //------------------------------------------------------------------------------
1360 void DbGridControl::ForceHideScrollbars( sal_Bool _bForce )
1361 {
1362     if ( m_bHideScrollbars == _bForce )
1363         return;
1364 
1365     m_bHideScrollbars = _bForce;
1366 
1367     if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1368         SetMode( m_nMode );
1369 }
1370 
1371 //------------------------------------------------------------------------------
1372 sal_Bool DbGridControl::IsForceHideScrollbars() const
1373 {
1374     return m_bHideScrollbars;
1375 }
1376 
1377 //------------------------------------------------------------------------------
1378 void DbGridControl::EnablePermanentCursor(sal_Bool bEnable)
1379 {
1380 	if (IsPermanentCursorEnabled() == bEnable)
1381 		return;
1382 
1383 	if (bEnable)
1384 	{
1385 		m_nMode &= ~BROWSER_HIDECURSOR; 	// without this BROWSER_CURSOR_WO_FOCUS won't have any affect
1386 		m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1387 	}
1388 	else
1389 	{
1390 		if (m_nOptions & OPT_UPDATE)
1391 			m_nMode |= BROWSER_HIDECURSOR;		// no cursor at all
1392 		else
1393 			m_nMode &= ~BROWSER_HIDECURSOR; 	// at least the "non-permanent" cursor
1394 
1395 		m_nMode &= ~BROWSER_CURSOR_WO_FOCUS;
1396 	}
1397 	SetMode(m_nMode);
1398 
1399 	sal_Bool bWasEditing = IsEditing();
1400 	DeactivateCell();
1401 	if (bWasEditing)
1402 		ActivateCell();
1403 }
1404 
1405 //------------------------------------------------------------------------------
1406 sal_Bool DbGridControl::IsPermanentCursorEnabled() const
1407 {
1408 	return ((m_nMode & BROWSER_CURSOR_WO_FOCUS) != 0) && ((m_nMode & BROWSER_HIDECURSOR) == 0);
1409 }
1410 
1411 //------------------------------------------------------------------------------
1412 void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
1413 {
1414 	if ((GetCurColumnId() == _nColId) && IsEditing())
1415 	{	// the controller which is currently active needs to be refreshed
1416 		DeactivateCell();
1417 		ActivateCell();
1418 	}
1419 }
1420 
1421 //------------------------------------------------------------------------------
1422 void DbGridControl::SetMultiSelection(sal_Bool bMulti)
1423 {
1424 	m_bMultiSelection = bMulti;
1425 	if (m_bMultiSelection)
1426 		m_nMode |= BROWSER_MULTISELECTION;
1427 	else
1428 		m_nMode &= ~BROWSER_MULTISELECTION;
1429 
1430 	SetMode(m_nMode);
1431 }
1432 
1433 //------------------------------------------------------------------------------
1434 void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, sal_uInt16 nOpts)
1435 {
1436 	if (!_xCursor.is() && !m_pDataCursor)
1437 		return;
1438 
1439 	if (m_pDataSourcePropMultiplexer)
1440 	{
1441 		m_pDataSourcePropMultiplexer->dispose();
1442 		m_pDataSourcePropMultiplexer->release();	// this should delete the multiplexer
1443 		delete m_pDataSourcePropListener;
1444 		m_pDataSourcePropMultiplexer = NULL;
1445 		m_pDataSourcePropListener = NULL;
1446 	}
1447     m_xRowSetListener.clear();
1448 
1449 	// is the new cursor valid ?
1450 	// the cursor is only valid if it contains some columns
1451 	// if there is no cursor or the cursor is not valid we have to clean up an leave
1452 	if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY)->getColumns()->hasElements())
1453 	{
1454 		RemoveRows();
1455 		return;
1456 	}
1457 
1458 	// Hat sich der DatenCursor verandert ?
1459 	sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
1460 
1461 	SetUpdateMode(sal_False);
1462 	RemoveRows();
1463 	DisconnectFromFields();
1464 
1465 	DELETEZ(m_pCursorDisposeListener);
1466 
1467 	{
1468 		::osl::MutexGuard aGuard(m_aAdjustSafety);
1469 		if (m_nAsynAdjustEvent)
1470 		{
1471 			// the adjust was thought to work with the old cursor which we don't have anymore
1472 			RemoveUserEvent(m_nAsynAdjustEvent);
1473 			m_nAsynAdjustEvent = 0;
1474 		}
1475 	}
1476 
1477 	// get a new formatter and data cursor
1478 	m_xFormatter = NULL;
1479 	OStaticDataAccessTools aStaticTools;
1480 	Reference< ::com::sun::star::util::XNumberFormatsSupplier >  xSupplier = aStaticTools.getNumberFormats(aStaticTools.getRowSetConnection(_xCursor), sal_True);
1481 	if (xSupplier.is() && m_xServiceFactory.is())
1482 	{
1483 		m_xFormatter =	Reference< ::com::sun::star::util::XNumberFormatter >(
1484 			m_xServiceFactory->createInstance(FM_NUMBER_FORMATTER),
1485 			UNO_QUERY);
1486 		if (m_xFormatter.is())
1487 		{
1488 			m_xFormatter->attachNumberFormatsSupplier(xSupplier);
1489 
1490 			// retrieve the datebase of the Numberformatter
1491 			try
1492 			{
1493 				xSupplier->getNumberFormatSettings()->getPropertyValue(rtl::OUString::createFromAscii("NullDate")) >>= m_aNullDate;
1494 			}
1495 			catch(Exception&)
1496 			{
1497 			}
1498 		}
1499 	}
1500 
1501 	m_pDataCursor = new CursorWrapper(_xCursor);
1502 
1503 	// now create a cursor for painting rows
1504 	// we need that cursor only if we are not in insert only mode
1505 	Reference< XResultSet > xClone;
1506 	Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
1507 	try
1508 	{
1509 		xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
1510 	}
1511 	catch(Exception&)
1512 	{
1513 	}
1514 	if (xClone.is())
1515 		m_pSeekCursor = new CursorWrapper(xClone);
1516 
1517 	// property listening on the data source
1518 	// (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
1519 	// But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
1520 	// So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
1521 	// and forwards the property changes to a our special method "DataSourcePropertyChanged".)
1522 	if (m_pDataCursor)
1523 	{
1524 		m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
1525 		m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
1526 		m_pDataSourcePropMultiplexer->acquire();
1527 		m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
1528 		m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
1529 	}
1530 
1531 	BrowserMode nOldMode = m_nMode;
1532 	if (m_pSeekCursor)
1533 	{
1534 		try
1535 		{
1536 			Reference< XPropertySet >  xSet(_xCursor, UNO_QUERY);
1537 			if (xSet.is())
1538 			{
1539 				// feststellen welche Updatemoeglichkeiten bestehen
1540 				sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
1541 				xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
1542 
1543 				if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
1544 				{
1545 					sal_Int32 nPrivileges = 0;
1546 					xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1547 
1548 					// Insert Option should be set if insert only otherwise you won't see any rows
1549 					// and no insertion is possible
1550 					if ((m_nOptionMask & OPT_INSERT) && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & OPT_INSERT))
1551 						m_nOptions |= OPT_INSERT;
1552 					if ((m_nOptionMask & OPT_UPDATE) && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & OPT_UPDATE))
1553 						m_nOptions |= OPT_UPDATE;
1554 					if ((m_nOptionMask & OPT_DELETE) && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & OPT_DELETE))
1555 						m_nOptions |= OPT_DELETE;
1556 				}
1557 			}
1558 		}
1559 		catch( const Exception& )
1560 		{
1561             DBG_UNHANDLED_EXCEPTION();
1562 		}
1563 
1564 		sal_Bool bPermanentCursor = IsPermanentCursorEnabled();
1565 		m_nMode = DEFAULT_BROWSE_MODE;
1566 
1567         if ( bPermanentCursor )
1568 		{
1569 			m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1570 			m_nMode &= ~BROWSER_HIDECURSOR;
1571 		}
1572 		else
1573         {
1574 			// Duerfen Updates gemacht werden, kein Focus-RechtEck
1575 			if ( m_nOptions & OPT_UPDATE )
1576 				m_nMode |= BROWSER_HIDECURSOR;
1577         }
1578 
1579 		if ( m_bMultiSelection )
1580 			m_nMode |= BROWSER_MULTISELECTION;
1581         else
1582             m_nMode &= ~BROWSER_MULTISELECTION;
1583 
1584         adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
1585 
1586 		Reference< XColumnsSupplier >  xSupplyColumns(_xCursor, UNO_QUERY);
1587 		if (xSupplyColumns.is())
1588 			InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
1589 
1590 		ConnectToFields();
1591 	}
1592 
1593 	sal_uInt32 nRecordCount(0);
1594 
1595 	if (m_pSeekCursor)
1596 	{
1597 		Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1598 		xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1599 		m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1600 
1601         m_xRowSetListener = new RowSetEventListener(this);
1602         Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
1603         if ( xChangeBroad.is( ) )
1604             xChangeBroad->addRowsChangeListener(m_xRowSetListener);
1605 
1606 
1607 		// insert the currently known rows
1608 		// and one row if we are able to insert rows
1609 		if (m_nOptions & OPT_INSERT)
1610 		{
1611 			// insert the empty row for insertion
1612 			m_xEmptyRow = new DbGridRow();
1613 			++nRecordCount;
1614 		}
1615 		if (nRecordCount)
1616 		{
1617 			m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor, sal_True);
1618 			m_xDataRow	= new DbGridRow(m_pDataCursor, sal_False);
1619 			RowInserted(0, nRecordCount, sal_False);
1620 
1621 			if (m_xSeekRow->IsValid())
1622                 try
1623                 {
1624 				    m_nSeekPos = m_pSeekCursor->getRow() - 1;
1625                 }
1626                 catch( const Exception& )
1627                 {
1628                 	DBG_UNHANDLED_EXCEPTION();
1629                     m_nSeekPos = -1;
1630                 }
1631 		}
1632 		else
1633 		{
1634 			// no rows so we don't need a seekcursor
1635 			DELETEZ(m_pSeekCursor);
1636 		}
1637 	}
1638 
1639 	// Zur alten Spalte gehen
1640 	if (!nCurPos || nCurPos >= ColCount())
1641 		nCurPos = 1;
1642 
1643 	// there are rows so go to the selected current column
1644 	if (nRecordCount)
1645 		GoToRowColumnId(0, GetColumnId(nCurPos));
1646 	// else stop the editing if necessary
1647 	else if (IsEditing())
1648 		DeactivateCell();
1649 
1650 	// now reset the mode
1651 	if (m_nMode != nOldMode)
1652 		SetMode(m_nMode);
1653 
1654 	// beim Resizen wird RecalcRows gerufen
1655 	if (!IsResizing() && GetRowCount())
1656 		RecalcRows(GetTopRow(), GetVisibleRows(), sal_True);
1657 
1658 	m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
1659 	SetUpdateMode(sal_True);
1660 
1661 	// start listening on the seek cursor
1662 	if (m_pSeekCursor)
1663 		m_pCursorDisposeListener = new DisposeListenerGridBridge(*this, Reference< XComponent > ((Reference< XInterface >)*m_pSeekCursor, UNO_QUERY), 0);
1664 }
1665 
1666 //------------------------------------------------------------------------------
1667 void DbGridControl::RemoveColumns()
1668 {
1669 	if ( IsEditing() )
1670 		DeactivateCell();
1671 
1672 	for (sal_uInt32 i = 0; i < m_aColumns.Count(); i++)
1673 		delete m_aColumns.GetObject(i);
1674 	m_aColumns.Clear();
1675 
1676 	DbGridControl_Base::RemoveColumns();
1677 }
1678 
1679 //------------------------------------------------------------------------------
1680 DbGridColumn* DbGridControl::CreateColumn(sal_uInt16 nId) const
1681 {
1682 	return new DbGridColumn(nId, *(DbGridControl*)this);
1683 }
1684 
1685 //------------------------------------------------------------------------------
1686 sal_uInt16 DbGridControl::AppendColumn(const XubString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
1687 {
1688 	DBG_ASSERT(nId == (sal_uInt16)-1, "DbGridControl::AppendColumn : I want to set the ID myself ...");
1689 	sal_uInt16 nRealPos = nModelPos;
1690 	if (nModelPos != HEADERBAR_APPEND)
1691 	{
1692 		// calc the view pos. we can't use our converting functions because the new column
1693 		// has no VCL-representation, yet.
1694 		sal_Int16 nViewPos = nModelPos;
1695 		while (nModelPos--)
1696 		{
1697 			if (m_aColumns.GetObject(nModelPos)->IsHidden())
1698 				--nViewPos;
1699 		}
1700 		// restore nModelPos, we need it later
1701 		nModelPos = nRealPos;
1702 		// the position the base class gets is the view pos + 1 (because of the handle column)
1703 		nRealPos = nViewPos + 1;
1704 	}
1705 
1706 	// calculate the new id
1707     for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && (nId<=m_aColumns.Count()); ++nId)
1708 		;
1709 	DBG_ASSERT(GetViewColumnPos(nId) == (sal_uInt16)-1, "DbGridControl::AppendColumn : inconsistent internal state !");
1710 		// my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
1711 
1712 	DbGridControl_Base::AppendColumn(rName, nWidth, nRealPos, nId);
1713 	if (nModelPos == HEADERBAR_APPEND)
1714 		m_aColumns.Insert(CreateColumn(nId), LIST_APPEND);
1715 	else
1716 		m_aColumns.Insert(CreateColumn(nId), nModelPos);
1717 
1718 	return nId;
1719 }
1720 
1721 //------------------------------------------------------------------------------
1722 void DbGridControl::RemoveColumn(sal_uInt16 nId)
1723 {
1724 	sal_Int16 nIndex = GetModelColumnPos(nId);
1725 	DbGridControl_Base::RemoveColumn(nId);
1726 	delete m_aColumns.Remove(nIndex);
1727 }
1728 
1729 //------------------------------------------------------------------------------
1730 void DbGridControl::ColumnMoved(sal_uInt16 nId)
1731 {
1732 	DbGridControl_Base::ColumnMoved(nId);
1733 
1734 	// remove the col from the model
1735 	sal_Int16 nOldModelPos = GetModelColumnPos(nId);
1736 #ifdef DBG_UTIL
1737 	DbGridColumn* pCol = m_aColumns.GetObject((sal_uInt32)nOldModelPos);
1738 	DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
1739 #endif
1740 
1741 	// for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
1742 	// so the method won't work (in fact it would return the old model pos)
1743 
1744 	// the new view pos is calculated easily
1745 	sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
1746 
1747 	// from that we can compute the new model pos
1748 	sal_uInt16 nNewModelPos;
1749 	for (nNewModelPos = 0; nNewModelPos < m_aColumns.Count(); ++nNewModelPos)
1750 	{
1751 		if (!m_aColumns.GetObject(nNewModelPos)->IsHidden())
1752 		{
1753 			if (!nNewViewPos)
1754 				break;
1755 			else
1756 				--nNewViewPos;
1757 		}
1758 	}
1759 	DBG_ASSERT(nNewModelPos<m_aColumns.Count(), "DbGridControl::ColumnMoved : could not find the new model position !");
1760 
1761 	// this will work. of course the model isn't fully consistent with our view right now, but let's
1762 	// look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
1763 	// other case we can use analogue arguments).
1764 	// All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
1765 	// In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
1766 	// within this range is constant, so we may calculate the view pos from the model pos in the above way.
1767 	//
1768 	// for instance, let's look at a grid with six columns where the third one is hidden. this will
1769 	// initially look like this :
1770 	//
1771 	//				+---+---+---+---+---+---+
1772 	// model pos	| 0 | 1 |*2*| 3 | 4 | 5 |
1773 	//				+---+---+---+---+---+---+
1774 	// ID			| 1 | 2 | 3 | 4 | 5 | 6 |
1775 	//				+---+---+---+---+---+---+
1776 	// view pos 	| 0 | 1 | - | 2 | 3 | 4 |
1777 	//				+---+---+---+---+---+---+
1778 	//
1779 	// if we move the column at (view) pos 1 to (view) pos 3 we have :
1780 	//
1781 	//				+---+---+---+---+---+---+
1782 	// model pos	| 0 | 3 |*2*| 4 | 1 | 5 |	// not reflecting the changes, yet
1783 	//				+---+---+---+---+---+---+
1784 	// ID			| 1 | 4 | 3 | 5 | 2 | 6 |	// already reflecting the changes
1785 	//				+---+---+---+---+---+---+
1786 	// view pos 	| 0 | 1 | - | 2 | 3 | 4 |
1787 	//				+---+---+---+---+---+---+
1788 	//
1789 	// or, sorted by the out-of-date model positions :
1790 	//
1791 	//				+---+---+---+---+---+---+
1792 	// model pos	| 0 | 1 |*2*| 3 | 4 | 5 |
1793 	//				+---+---+---+---+---+---+
1794 	// ID			| 1 | 2 | 3 | 4 | 5 | 6 |
1795 	//				+---+---+---+---+---+---+
1796 	// view pos 	| 0 | 3 | - | 1 | 2 | 4 |
1797 	//				+---+---+---+---+---+---+
1798 	//
1799 	// We know the new view pos (3) of the moved column because our base class tells us. So we look at our
1800 	// model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
1801 	// exactly the pos where we have to re-insert our column's model, so it looks ike this :
1802 	//
1803 	//				+---+---+---+---+---+---+
1804 	// model pos	| 0 |*1*| 2 | 3 | 4 | 5 |
1805 	//				+---+---+---+---+---+---+
1806 	// ID			| 1 | 3 | 4 | 5 | 2 | 6 |
1807 	//				+---+---+---+---+---+---+
1808 	// view pos 	| 0 | - | 1 | 2 | 3 | 4 |
1809 	//				+---+---+---+---+---+---+
1810 	//
1811 	// Now, all is consistent again.
1812 	// (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
1813 	// the user expected the latter but there really is no good argument against our method ;) ...)
1814 	//
1815 	// And no, this large explanation isn't just because I wanted to play a board game or something like
1816 	// that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
1817 	// positions, view col positions)  is really painful (at least for me) so the above pictures helped me a lot ;)
1818 
1819 	m_aColumns.Insert(m_aColumns.Remove((sal_uInt32)nOldModelPos), nNewModelPos);
1820 }
1821 
1822 //------------------------------------------------------------------------------
1823 sal_Bool DbGridControl::SeekRow(long nRow)
1824 {
1825 	// in filter mode or in insert only mode we don't have any cursor!
1826 	if ( !SeekCursor( nRow ) )
1827         return sal_False;
1828 
1829 	if ( IsFilterMode() )
1830 	{
1831 		DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
1832 		m_xPaintRow = m_xEmptyRow;
1833 	}
1834     else
1835 	{
1836 		// on the current position we have to take the current row for display as we want
1837 		// to have the most recent values for display
1838 		if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
1839 			m_xPaintRow = m_xCurrentRow;
1840 		// seek to the empty insert row
1841 		else if ( IsInsertionRow( nRow ) )
1842 			m_xPaintRow = m_xEmptyRow;
1843 		else
1844 		{
1845 			m_xSeekRow->SetState( m_pSeekCursor, sal_True );
1846 			m_xPaintRow = m_xSeekRow;
1847 		}
1848 	}
1849 
1850     DbGridControl_Base::SeekRow(nRow);
1851 
1852     return m_nSeekPos >= 0;
1853 }
1854 //------------------------------------------------------------------------------
1855 // Wird aufgerufen, wenn die dargestellte Datenmenge sich aendert
1856 //------------------------------------------------------------------------------
1857 void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen )
1858 {
1859 	RecalcRows(nNewTopRow, nLinesOnScreen , sal_False);
1860 }
1861 
1862 //------------------------------------------------------------------------------
1863 void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, sal_Bool bUpdateCursor)
1864 {
1865 	DBG_CHKTHIS( DbGridControl, NULL );
1866 	// Wenn kein Cursor -> keine Rows im Browser.
1867 	if (!m_pSeekCursor)
1868 	{
1869 		DBG_ASSERT(GetRowCount() == 0,"DbGridControl: ohne Cursor darf es keine Rows geben");
1870 		return;
1871 	}
1872 
1873 	// ignore any updates implicit made
1874 	sal_Bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
1875 	if (bDisablePaint)
1876 		EnablePaint(sal_False);
1877 
1878 	// Cache an den sichtbaren Bereich anpassen
1879 	Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
1880 	sal_Int32 nCacheSize = 0;
1881 	xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
1882 	sal_Bool bCacheAligned	 = sal_False;
1883 	// Nach der Initialisierung (m_nSeekPos < 0) keine Cursorbewegung, da bereits auf den ersten
1884 	// Satz positioniert
1885 	long nDelta = nNewTopRow - GetTopRow();
1886 	// Limit fuer relative Positionierung
1887 	long nLimit = (nCacheSize) ? nCacheSize / 2 : 0;
1888 
1889 	// mehr Zeilen auf dem Bildschirm als im Cache
1890 	if (nLimit < nLinesOnScreen)
1891 	{
1892 		Any aCacheSize;
1893 		aCacheSize <<= sal_Int32(nLinesOnScreen*2);
1894 		xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
1895 		// jetzt auf alle Faelle den Cursor anpassen
1896 		bUpdateCursor = sal_True;
1897 		bCacheAligned = sal_True;
1898 		nLimit = nLinesOnScreen;
1899 	}
1900 
1901 	// Im folgenden werden die Positionierungen so vorgenommen, da� sichergestellt ist
1902 	// da� ausreichend Zeilen im DatenCache vorhanden sind
1903 
1904 	// Fenster geht nach unten, weniger als zwei Fenster Differenz
1905 	// oder Cache angepasst und noch kein Rowcount
1906 	if (nDelta < nLimit && (nDelta > 0
1907 		|| (bCacheAligned && m_nTotalCount < 0)) )
1908 		SeekCursor(nNewTopRow + nLinesOnScreen - 1, sal_False);
1909 	else if (nDelta < 0 && Abs(nDelta) < nLimit)
1910 		SeekCursor(nNewTopRow, sal_False);
1911 	else if (nDelta != 0 || bUpdateCursor)
1912 		SeekCursor(nNewTopRow, sal_True);
1913 
1914 	AdjustRows();
1915 
1916 	// ignore any updates implicit made
1917 	EnablePaint(sal_True);
1918 }
1919 
1920 //------------------------------------------------------------------------------
1921 void DbGridControl::RowInserted(long nRow, long nNumRows, sal_Bool bDoPaint, sal_Bool bKeepSelection)
1922 {
1923 	if (nNumRows)
1924 	{
1925 		if (m_bRecordCountFinal && m_nTotalCount < 0)
1926 		{
1927 			// if we have an insert row we have to reduce to count by 1
1928 			// as the total count reflects only the existing rows in database
1929 			m_nTotalCount = GetRowCount() + nNumRows;
1930 			if (m_xEmptyRow.Is())
1931 				--m_nTotalCount;
1932 		}
1933 		else if (m_nTotalCount >= 0)
1934 			m_nTotalCount += nNumRows;
1935 
1936 		DbGridControl_Base::RowInserted(nRow, nNumRows, bDoPaint, bKeepSelection);
1937 		m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1938 	}
1939 }
1940 
1941 //------------------------------------------------------------------------------
1942 void DbGridControl::RowRemoved(long nRow, long nNumRows, sal_Bool bDoPaint)
1943 {
1944 	if (nNumRows)
1945 	{
1946 		if (m_bRecordCountFinal && m_nTotalCount < 0)
1947 		{
1948 			m_nTotalCount = GetRowCount() - nNumRows;
1949 			// if we have an insert row reduce by 1
1950 			if (m_xEmptyRow.Is())
1951 				--m_nTotalCount;
1952 		}
1953 		else if (m_nTotalCount >= 0)
1954 			m_nTotalCount -= nNumRows;
1955 
1956 		DbGridControl_Base::RowRemoved(nRow, nNumRows, bDoPaint);
1957 		m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1958 	}
1959 }
1960 
1961 //------------------------------------------------------------------------------
1962 void DbGridControl::AdjustRows()
1963 {
1964 	if (!m_pSeekCursor)
1965 		return;
1966 
1967 	Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1968 
1969 	// Aktualisieren des RecordCounts
1970 	sal_Int32 nRecordCount = 0;
1971 	xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1972 	if (!m_bRecordCountFinal)
1973 		m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1974 
1975 	// hat sich die aktuelle Anzahl Rows veraendert
1976 	// hierbei muss auch beruecksichtigt werden,
1977 	// das eine zusaetzliche Zeile zum einfuegen von Datensaetzen vorhanden sein kann
1978 
1979 	// zusaetzliche AppendRow fuers einfuegen
1980 	if (m_nOptions & OPT_INSERT)
1981 		++nRecordCount;
1982 
1983 	// wird gerade eingefuegt, dann gehoert die gerade hinzuzufuegende
1984 	// Zeile nicht zum RecordCount und die Appendrow ebenfalls nicht
1985 	if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
1986 		m_xCurrentRow->IsNew())
1987 		++nRecordCount;
1988 	// das ist mit !m_bUpdating abgesichert : innerhalb von SaveRow (m_bUpdating == sal_True) wuerde sonst der Datensatz, den ich editiere
1989 	// (und den SaveRow gerade angefuegt hat, wodurch diese Methode hier getriggert wurde), doppelt zaehlen : einmal ist er schon
1990 	// in dem normalen RecordCount drin, zum zweiten wuerde er hier gezaehlt werden (60787 - FS)
1991 
1992 	if (nRecordCount != GetRowCount())
1993 	{
1994 		long nDelta = GetRowCount() - (long)nRecordCount;
1995 		if (nDelta > 0) 										// zuviele
1996 		{
1997 			RowRemoved(GetRowCount() - nDelta, nDelta, sal_False);
1998 			// es sind Zeilen weggefallen, dann ab der aktuellen Position neu zeichen
1999 			Invalidate();
2000 
2001             sal_Int32 nNewPos = AlignSeekCursor();
2002             if (m_bSynchDisplay)
2003 			    DbGridControl_Base::GoToRow(nNewPos);
2004 
2005             SetCurrent(nNewPos);
2006             // there are rows so go to the selected current column
2007 	        if (nRecordCount)
2008 		        GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
2009 	        if (!IsResizing() && GetRowCount())
2010 		        RecalcRows(GetTopRow(), GetVisibleRows(), sal_True);
2011             m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
2012 		}
2013 		else													// zuwenig
2014 			RowInserted(GetRowCount(), -nDelta, sal_True);
2015 	}
2016 
2017 	if (m_bRecordCountFinal && m_nTotalCount < 0)
2018 	{
2019 		if (m_nOptions & OPT_INSERT)
2020 			m_nTotalCount = GetRowCount() - 1;
2021 		else
2022 			m_nTotalCount = GetRowCount();
2023 	}
2024 	m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
2025 }
2026 
2027 //------------------------------------------------------------------------------
2028 DbGridControl_Base::RowStatus DbGridControl::GetRowStatus(long nRow) const
2029 {
2030 	if (IsFilterRow(nRow))
2031 		return DbGridControl_Base::FILTER;
2032 	else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
2033 	{
2034 		// neue Zeile
2035 		if (!IsValid(m_xCurrentRow))
2036 			return DbGridControl_Base::DELETED;
2037 		else if (IsModified())
2038 			return DbGridControl_Base::MODIFIED;
2039 		else if (m_xCurrentRow->IsNew())
2040 			return DbGridControl_Base::CURRENTNEW;
2041 		else
2042 			return DbGridControl_Base::CURRENT;
2043 	}
2044 	else if (IsInsertionRow(nRow))
2045 		return DbGridControl_Base::NEW;
2046 	else if (!IsValid(m_xSeekRow))
2047 		return DbGridControl_Base::DELETED;
2048 	else
2049 		return DbGridControl_Base::CLEAN;
2050 }
2051 
2052 //------------------------------------------------------------------------------
2053 void DbGridControl::PaintStatusCell(OutputDevice& rDev, const Rectangle& rRect) const
2054 {
2055 	DbGridControl_Base::PaintStatusCell(rDev, rRect);
2056 }
2057 
2058 //------------------------------------------------------------------------------
2059 void DbGridControl::PaintCell(OutputDevice& rDev, const Rectangle& rRect, sal_uInt16 nColumnId) const
2060 {
2061 	if (!IsValid(m_xPaintRow))
2062 		return;
2063 
2064 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId));
2065 	if (pColumn)
2066 	{
2067 		Rectangle aArea(rRect);
2068 		if ((GetMode() & BROWSER_CURSOR_WO_FOCUS) == BROWSER_CURSOR_WO_FOCUS)
2069 		{
2070 			aArea.Top() += 1;
2071 			aArea.Bottom() -= 1;
2072 		}
2073 		pColumn->Paint(rDev, aArea, m_xPaintRow, getNumberFormatter());
2074 	}
2075 }
2076 
2077 //------------------------------------------------------------------------------
2078 sal_Bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol)
2079 {
2080 	DBG_CHKTHIS( DbGridControl, NULL );
2081 
2082     DeactivateCell( sal_False );
2083 
2084 	if  (   m_pDataCursor
2085         &&  ( m_nCurrentPos != nNewRow )
2086         && !SetCurrent( nNewRow )
2087         )
2088     {
2089         ActivateCell();
2090 		return sal_False;
2091     }
2092 
2093     if ( !DbGridControl_Base::CursorMoving( nNewRow, nNewCol ) )
2094         return sal_False;
2095 
2096 	return sal_True;
2097 }
2098 
2099 //------------------------------------------------------------------------------
2100 sal_Bool DbGridControl::SetCurrent(long nNewRow)
2101 {
2102 	// Each movement of the datacursor must start with BeginCursorAction and end with
2103 	// EndCursorAction to block all notifications during the movement
2104 	BeginCursorAction();
2105 
2106 	try
2107 	{
2108 		// Abgleichen der Positionen
2109 		if (SeekCursor(nNewRow))
2110 		{
2111 			if (IsFilterRow(nNewRow))	// special mode for filtering
2112 			{
2113 				m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
2114 				m_nCurrentPos = nNewRow;
2115 			}
2116 			else
2117 			{
2118 				sal_Bool bNewRowInserted = sal_False;
2119 				// Should we go to the insertrow ?
2120 				if (IsInsertionRow(nNewRow))
2121 				{
2122 					// to we need to move the cursor to the insert row?
2123 					// we need to insert the if the current row isn't the insert row or if the
2124 					// cursor triggered the move by itselt and we need a reinitialization of the row
2125 					Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
2126 					if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
2127 					{
2128 						Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
2129 						xUpdateCursor->moveToInsertRow();
2130 					}
2131 					bNewRowInserted = sal_True;
2132 				}
2133 				else
2134 				{
2135 
2136 					if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
2137 					{
2138 						Any aBookmark = m_pSeekCursor->getBookmark();
2139 						if (!m_xCurrentRow || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
2140 						{
2141 							// adjust the cursor to the new desired row
2142 							if (!m_pDataCursor->moveToBookmark(aBookmark))
2143 							{
2144 								EndCursorAction();
2145 								return sal_False;
2146 							}
2147 						}
2148 					}
2149 				}
2150 				m_xDataRow->SetState(m_pDataCursor, sal_False);
2151 				m_xCurrentRow = m_xDataRow;
2152 
2153 				long nPaintPos = -1;
2154 				// do we have to repaint the last regular row in case of setting defaults or autovalues
2155 				if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
2156 					nPaintPos = m_nCurrentPos;
2157 
2158 				m_nCurrentPos = nNewRow;
2159 
2160 				// repaint the new row to display all defaults
2161 				if (bNewRowInserted)
2162 					RowModified(m_nCurrentPos);
2163 				if (nPaintPos >= 0)
2164 					RowModified(nPaintPos);
2165 			}
2166 		}
2167 		else
2168 		{
2169 			DBG_ERROR("DbGridControl::SetCurrent : SeekRow failed !");
2170 			EndCursorAction();
2171 			return sal_False;
2172 		}
2173 	}
2174 	catch ( const Exception& )
2175 	{
2176         DBG_UNHANDLED_EXCEPTION();
2177 		EndCursorAction();
2178 		return sal_False;
2179 	}
2180 
2181 	EndCursorAction();
2182 	return sal_True;
2183 }
2184 
2185 //------------------------------------------------------------------------------
2186 void DbGridControl::CursorMoved()
2187 {
2188 	DBG_CHKTHIS( DbGridControl, NULL );
2189 
2190 	// CursorBewegung durch loeschen oder einfuegen von Zeilen
2191 	if (m_pDataCursor && m_nCurrentPos != GetCurRow())
2192 	{
2193 		DeactivateCell(sal_True);
2194 		SetCurrent(GetCurRow());
2195 	}
2196 
2197 	DbGridControl_Base::CursorMoved();
2198 	m_aBar.InvalidateAll(m_nCurrentPos);
2199 
2200 	// select the new column when they moved
2201 	if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
2202 	{
2203 		SelectColumnId( GetCurColumnId() );
2204 	}
2205 
2206     if ( m_nLastColId != GetCurColumnId() )
2207         onColumnChange();
2208 	m_nLastColId = GetCurColumnId();
2209 
2210     if ( m_nLastRowId != GetCurRow() )
2211         onRowChange();
2212 	m_nLastRowId = GetCurRow();
2213 }
2214 
2215 //------------------------------------------------------------------------------
2216 void DbGridControl::onRowChange()
2217 {
2218     // not interested in
2219 }
2220 
2221 //------------------------------------------------------------------------------
2222 void DbGridControl::onColumnChange()
2223 {
2224     if ( m_pGridListener )
2225 		m_pGridListener->columnChanged();
2226 }
2227 
2228 //------------------------------------------------------------------------------
2229 void DbGridControl::setDisplaySynchron(sal_Bool bSync)
2230 {
2231 	if (bSync != m_bSynchDisplay)
2232 	{
2233 		m_bSynchDisplay = bSync;
2234 		if (m_bSynchDisplay)
2235 			AdjustDataSource(sal_False);
2236 	}
2237 }
2238 
2239 //------------------------------------------------------------------------------
2240 void DbGridControl::forceSyncDisplay()
2241 {
2242 	sal_Bool bOld = getDisplaySynchron();
2243 	setDisplaySynchron(sal_True);
2244 	if (!bOld)
2245 		setDisplaySynchron(bOld);
2246 }
2247 
2248 //------------------------------------------------------------------------------
2249 void DbGridControl::forceROController(sal_Bool bForce)
2250 {
2251 	if (m_bForceROController == bForce)
2252 		return;
2253 
2254 	m_bForceROController = bForce;
2255 	// alle Columns durchgehen und denen Bescheid geben
2256 	for (sal_uInt16 i=0; i<m_aColumns.Count(); ++i)
2257 	{
2258 		DbGridColumn* pColumn = m_aColumns.GetObject(i);
2259 		if (!pColumn)
2260 			continue;
2261 
2262 		CellController* pReturn = &pColumn->GetController();
2263 		if (!pReturn)
2264 			continue;
2265 
2266 		// nur wenn es eine Edit-Zeile ist, kann ich ihr das forced read-only mitgeben
2267 		if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController))
2268 			continue;
2269 
2270 		Edit& rEdit = (Edit&)pReturn->GetWindow();
2271 		rEdit.SetReadOnly(m_bForceROController);
2272 		if (m_bForceROController)
2273 			rEdit.SetStyle(rEdit.GetStyle() | WB_NOHIDESELECTION);
2274 		else
2275 			rEdit.SetStyle(rEdit.GetStyle() & ~WB_NOHIDESELECTION);
2276 	}
2277 
2278 	// die aktive Zelle erneut aktivieren, da sich ihr Controller geaendert haben kann
2279 	if (IsEditing())
2280 		DeactivateCell();
2281 	ActivateCell();
2282 }
2283 
2284 
2285 //------------------------------------------------------------------------------
2286 void DbGridControl::AdjustDataSource(sal_Bool bFull)
2287 {
2288 	TRACE_RANGE("DbGridControl::AdjustDataSource");
2289 	::vos::OGuard aGuard(Application::GetSolarMutex());
2290 	// wird die aktuelle Zeile gerade neu bestimmt,
2291 	// wird kein abgleich vorgenommen
2292 
2293 	if (bFull)
2294 		m_xCurrentRow = NULL;
2295 	// if we are on the same row only repaint
2296 	// but this is only possible for rows which are not inserted, in that case the comparison result
2297 	// may not be correct
2298 	else
2299         if  (   m_xCurrentRow.Is()
2300             &&  !m_xCurrentRow->IsNew()
2301             &&  !m_pDataCursor->isBeforeFirst()
2302             &&  !m_pDataCursor->isAfterLast()
2303             &&  !m_pDataCursor->rowDeleted()
2304             )
2305 		{
2306 			sal_Bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
2307 
2308 			sal_Bool bDataCursorIsOnNew = sal_False;
2309 			m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
2310 
2311 			if ( bEqualBookmarks && !bDataCursorIsOnNew )
2312 			{
2313 				// position of my data cursor is the same as the position our current row points tpo
2314 				// sync the status, repaint, done
2315 				DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Fehler in den Datenzeilen");
2316 				TRACE_RANGE_MESSAGE1("same position, new state : %s", ROWSTATUS(m_xCurrentRow));
2317 				RowModified(m_nCurrentPos);
2318 				return;
2319 			}
2320 		}
2321 
2322 	// weg von der Row des DatenCursors
2323 	if (m_xPaintRow == m_xCurrentRow)
2324 		m_xPaintRow = m_xSeekRow;
2325 
2326 	// keine aktuelle Zeile dann komplett anpassen
2327 	if (!m_xCurrentRow)
2328 		AdjustRows();
2329 
2330 	sal_Int32 nNewPos = AlignSeekCursor();
2331 	if (nNewPos < 0)	// keine Position gefunden
2332 		return;
2333 
2334 	m_bInAdjustDataSource = sal_True;
2335 	if (nNewPos != m_nCurrentPos)
2336 	{
2337 		if (m_bSynchDisplay)
2338 			DbGridControl_Base::GoToRow(nNewPos);
2339 
2340 		if (!m_xCurrentRow.Is())
2341 			// das tritt zum Beispiel auf, wenn man die n (n>1) letzten Datensaetze geloescht hat, waehrend der Cursor auf dem letzten
2342 			// steht : AdjustRows entfernt dann zwei Zeilen aus der BrowseBox, wodurch diese ihre CurrentRow um zwei nach unten
2343 			// korrigiert, so dass dann das GoToRow in's Leere laeuft (da wir uns ja angeblich schon an der richtigen Position
2344 			// befinden)
2345 			SetCurrent(nNewPos);
2346 	}
2347 	else
2348 	{
2349 		SetCurrent(nNewPos);
2350 		RowModified(nNewPos);
2351 	}
2352 	m_bInAdjustDataSource = sal_False;
2353 
2354 	// Wird der DatenCursor von aussen bewegt, wird die selektion aufgehoben
2355 	SetNoSelection();
2356 	m_aBar.InvalidateAll(m_nCurrentPos, m_xCurrentRow.Is());
2357 }
2358 
2359 //------------------------------------------------------------------------------
2360 sal_Int32 DbGridControl::AlignSeekCursor()
2361 {
2362 	DBG_CHKTHIS( DbGridControl, NULL );
2363 	// Positioniert den SeekCursor auf den DatenCursor, Daten werden nicht uebertragen
2364 
2365 	if (!m_pSeekCursor)
2366 		return -1;
2367 
2368 	Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
2369 
2370 	// jetzt den seekcursor an den DatenCursor angleichen
2371 	if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
2372 		m_nSeekPos = GetRowCount() - 1;
2373 	else
2374 	{
2375 		try
2376 		{
2377 			if ( m_pDataCursor->isBeforeFirst() )
2378 			{
2379 				// this is somewhat strange, but can nevertheless happen
2380 				DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
2381 				m_pSeekCursor->first();
2382 				m_pSeekCursor->previous();
2383 				m_nSeekPos = -1;
2384 			}
2385 			else if ( m_pDataCursor->isAfterLast() )
2386 			{
2387 				DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
2388 				m_pSeekCursor->last();
2389 				m_pSeekCursor->next();
2390 				m_nSeekPos = -1;
2391 			}
2392 			else
2393 			{
2394 				m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2395 				if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
2396 					// dummerweise kann das moveToBookmark indirekt dazu fuehren, dass der Seek-Cursor wieder neu positoniert wird (wenn
2397 					// naemlich das mit all seinen zu feuernden Events relativ komplexe moveToBookmark irgendwo ein Update ausloest),
2398 					// also muss ich es nochmal versuchen
2399 					m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2400 					// Nicht dass das jetzt nicht auch schief gegangen sein koennte, aber es ist zumindest unwahrscheinlicher geworden.
2401 					// Und die Alternative waere eine Schleife so lange bis es stimmt, und das kann auch nicht die Loesung sein
2402 				m_nSeekPos = m_pSeekCursor->getRow() - 1;
2403 			}
2404 		}
2405 		catch(Exception&)
2406 		{
2407 		}
2408 	}
2409 	return m_nSeekPos;
2410 }
2411 //------------------------------------------------------------------------------
2412 sal_Bool DbGridControl::SeekCursor(long nRow, sal_Bool bAbsolute)
2413 {
2414 	DBG_CHKTHIS( DbGridControl, NULL );
2415 	// Positioniert den SeekCursor, Daten werden nicht uebertragen
2416 
2417 	// additions for the filtermode
2418 	if (IsFilterRow(nRow))
2419 	{
2420 		m_nSeekPos = 0;
2421 		return sal_True;
2422 	}
2423 
2424 	if (!m_pSeekCursor)
2425 		return sal_False;
2426 
2427 	// Befinden wir uns gerade beim Einfuegen
2428 	if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
2429 		nRow >= m_nCurrentPos)
2430 	{
2431 		// dann darf auf alle Faelle nicht weiter nach unten gescrollt werden
2432 		// da der letzte Datensatz bereits erreicht wurde!
2433 		if (nRow == m_nCurrentPos)
2434 		{
2435 			// auf die aktuelle Zeile bewegt, dann muß kein abgleich gemacht werden, wenn
2436 			// gerade ein Datensatz eingefuegt wird
2437 			m_nSeekPos = nRow;
2438 		}
2439 		else if (IsInsertionRow(nRow))	// Leerzeile zum Einfuegen von Datensaetzen
2440 			m_nSeekPos = nRow;
2441 	}
2442 	else if (IsInsertionRow(nRow))	// Leerzeile zum Einfuegen von Datensaetzen
2443 		m_nSeekPos = nRow;
2444 	else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & OPT_INSERT) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
2445 		m_nSeekPos = nRow;
2446 	else
2447 	{
2448 
2449 		sal_Bool bSuccess=sal_False;
2450 		long nSteps = 0;
2451 		try
2452 		{
2453             if ( m_pSeekCursor->rowDeleted() )
2454             {
2455                 // somebody deleted the current row of the seek cursor. Move it away from this row.
2456                 m_pSeekCursor->next();
2457                 if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
2458 			        bAbsolute = sal_True;
2459             }
2460 
2461     		if ( !bAbsolute )
2462             {
2463                 DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
2464                     "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
2465 			    nSteps = nRow - (m_pSeekCursor->getRow() - 1);
2466 			    bAbsolute = bAbsolute || (abs(nSteps) > 100);
2467             }
2468 
2469             if ( bAbsolute )
2470 			{
2471                 bSuccess = m_pSeekCursor->absolute(nRow + 1);
2472 				if (bSuccess)
2473 					m_nSeekPos = nRow;
2474 			}
2475             else
2476             {
2477                 if (nSteps > 0) 								// auf den letzten benoetigten Datensatz positionieren
2478 				{
2479 					if (m_pSeekCursor->isAfterLast())
2480 						bSuccess = sal_False;
2481 					else if (m_pSeekCursor->isBeforeFirst())
2482 						bSuccess = m_pSeekCursor->absolute(nSteps);
2483 					else
2484 						bSuccess = m_pSeekCursor->relative(nSteps);
2485 				}
2486 				else if (nSteps < 0)
2487 				{
2488 					if (m_pSeekCursor->isBeforeFirst())
2489 						bSuccess = sal_False;
2490 					else if (m_pSeekCursor->isAfterLast())
2491 						bSuccess = m_pSeekCursor->absolute(nSteps);
2492 					else
2493 						bSuccess = m_pSeekCursor->relative(nSteps);
2494 				}
2495 				else
2496 				{
2497 					m_nSeekPos = nRow;
2498 					return sal_True;
2499 				}
2500 			}
2501 		}
2502 		catch(Exception&)
2503 		{
2504 			DBG_ERROR("DbGridControl::SeekCursor : failed ...");
2505 		}
2506 
2507 		try
2508 		{
2509 			if (!bSuccess)
2510 			{
2511 				if (bAbsolute || nSteps > 0)
2512 					bSuccess = m_pSeekCursor->last();
2513 				else
2514 					bSuccess = m_pSeekCursor->first();
2515 			}
2516 
2517 			if (bSuccess)
2518 				m_nSeekPos = m_pSeekCursor->getRow() - 1;
2519 			else
2520 				m_nSeekPos = -1;
2521 		}
2522 		catch(Exception&)
2523 		{
2524 			DBG_ERROR("DbGridControl::SeekCursor : failed ...");
2525 			m_nSeekPos = -1;						// kein Datensatz mehr vorhanden
2526 		}
2527 	}
2528 	return m_nSeekPos == nRow;
2529 }
2530 //------------------------------------------------------------------------------
2531 void DbGridControl::MoveToFirst()
2532 {
2533 	if (m_pSeekCursor && (GetCurRow() != 0))
2534 		MoveToPosition(0);
2535 }
2536 
2537 //------------------------------------------------------------------------------
2538 void DbGridControl::MoveToLast()
2539 {
2540 	if (!m_pSeekCursor)
2541 		return;
2542 
2543 	if (m_nTotalCount < 0)			// RecordCount steht noch nicht fest
2544 	{
2545 		try
2546 		{
2547 			sal_Bool bRes = m_pSeekCursor->last();
2548 
2549 			if (bRes)
2550 			{
2551 				m_nSeekPos = m_pSeekCursor->getRow() - 1;
2552 				AdjustRows();
2553 			}
2554 		}
2555 		catch(Exception&)
2556 		{
2557 		}
2558 	}
2559 
2560 	// auf den letzen Datensatz positionieren, nicht auf die Leerzeile
2561 	if (m_nOptions & OPT_INSERT)
2562 	{
2563 		if ((GetRowCount() - 1) > 0)
2564 			MoveToPosition(GetRowCount() - 2);
2565 	}
2566 	else if (GetRowCount())
2567 		MoveToPosition(GetRowCount() - 1);
2568 }
2569 
2570 //------------------------------------------------------------------------------
2571 void DbGridControl::MoveToPrev()
2572 {
2573 	long nNewRow = std::max(GetCurRow() - 1L, 0L);
2574 	if (GetCurRow() != nNewRow)
2575 		MoveToPosition(nNewRow);
2576 }
2577 
2578 //------------------------------------------------------------------------------
2579 void DbGridControl::MoveToNext()
2580 {
2581 	if (!m_pSeekCursor)
2582 		return;
2583 
2584 	if (m_nTotalCount > 0)
2585 	{
2586 		// move the data cursor to the right position
2587 		long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
2588 		if (GetCurRow() != nNewRow)
2589 			MoveToPosition(nNewRow);
2590 	}
2591 	else
2592 	{
2593 		sal_Bool bOk = sal_False;
2594 		try
2595 		{
2596 			// try to move to next row
2597 			// when not possible our paint cursor is already on the last row
2598 			// then we must be sure that the data cursor is on the position
2599 			// we call ourself again
2600             bOk = m_pSeekCursor->next();
2601 			if (bOk)
2602 			{
2603 				m_nSeekPos = m_pSeekCursor->getRow() - 1;
2604 				MoveToPosition(GetCurRow() + 1);
2605 			}
2606 		}
2607 		catch(SQLException &)
2608 		{
2609             DBG_UNHANDLED_EXCEPTION();
2610 		}
2611 
2612 		if(!bOk)
2613 		{
2614 			AdjustRows();
2615 			if (m_nTotalCount > 0) // only to avoid infinte recursion
2616 				MoveToNext();
2617 		}
2618 	}
2619 }
2620 
2621 //------------------------------------------------------------------------------
2622 void DbGridControl::MoveToPosition(sal_uInt32 nPos)
2623 {
2624 	if (!m_pSeekCursor)
2625 		return;
2626 
2627     if (m_nTotalCount < 0 && (long)nPos >= GetRowCount())
2628     {
2629         try
2630         {
2631             if (!m_pSeekCursor->absolute(nPos + 1))
2632             {
2633                 AdjustRows();
2634                 Sound::Beep();
2635                 return;
2636             }
2637             else
2638             {
2639                 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2640                 AdjustRows();
2641             }
2642         }
2643         catch(Exception&)
2644         {
2645             return;
2646         }
2647     }
2648     DbGridControl_Base::GoToRow(nPos);
2649     m_aBar.InvalidateAll(m_nCurrentPos);
2650 }
2651 
2652 //------------------------------------------------------------------------------
2653 void DbGridControl::AppendNew()
2654 {
2655 	if (!m_pSeekCursor || !(m_nOptions & OPT_INSERT))
2656 		return;
2657 
2658 	if (m_nTotalCount < 0)			// RecordCount steht noch nicht fest
2659 	{
2660 		try
2661 		{
2662 			sal_Bool bRes = m_pSeekCursor->last();
2663 
2664 			if (bRes)
2665 			{
2666 				m_nSeekPos = m_pSeekCursor->getRow() - 1;
2667 				AdjustRows();
2668 			}
2669 		}
2670 		catch(Exception&)
2671 		{
2672 			return;
2673 		}
2674 	}
2675 
2676 	long nNewRow = m_nTotalCount + 1;
2677 	if (nNewRow > 0 && GetCurRow() != nNewRow)
2678 		MoveToPosition(nNewRow - 1);
2679 }
2680 
2681 //------------------------------------------------------------------------------
2682 void DbGridControl::SetDesignMode(sal_Bool bMode)
2683 {
2684 	if (IsDesignMode() != bMode)
2685 	{
2686 		// Enable/Disable f�r den Designmode anpassen damit die Headerbar konfigurierbar bleibt
2687 		if (bMode)
2688 		{
2689 			if (!IsEnabled())
2690 			{
2691 				Enable();
2692 				GetDataWindow().Disable();
2693 			}
2694 		}
2695 		else
2696 		{
2697 			// komplett disablen
2698 			if (!GetDataWindow().IsEnabled())
2699 				Disable();
2700 		}
2701 
2702 		m_bDesignMode = bMode;
2703 		GetDataWindow().SetMouseTransparent(bMode);
2704 		SetMouseTransparent(bMode);
2705 
2706 		m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
2707 	}
2708 }
2709 
2710 //------------------------------------------------------------------------------
2711 void DbGridControl::SetFilterMode(sal_Bool bMode)
2712 {
2713 	if (IsFilterMode() != bMode)
2714 	{
2715 		m_bFilterMode = bMode;
2716 
2717 		if (bMode)
2718 		{
2719 			SetUpdateMode(sal_False);
2720 
2721 			// es gibt kein Cursor mehr
2722 			if (IsEditing())
2723 				DeactivateCell();
2724 			RemoveRows(sal_False);
2725 
2726 			m_xEmptyRow = new DbGridRow();
2727 
2728 			// setting the new filter controls
2729 			for (sal_uInt16 i = 0; i<m_aColumns.Count(); ++i)
2730 			{
2731 				DbGridColumn* pCurCol = m_aColumns.GetObject(i);
2732 				if (!pCurCol->IsHidden())
2733 					pCurCol->UpdateControl();
2734 			}
2735 
2736 			// one row for filtering
2737 			RowInserted(0, 1, sal_True);
2738 			SetUpdateMode(sal_True);
2739 		}
2740 		else
2741 			setDataSource(Reference< XRowSet > ());
2742 	}
2743 }
2744 // -----------------------------------------------------------------------------
2745 String DbGridControl::GetCellText(long _nRow, sal_uInt16 _nColId) const
2746 {
2747 	DbGridColumn* pColumn = m_aColumns.GetObject( GetModelColumnPos( _nColId ) );
2748 	String sRet;
2749 	if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
2750 		sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
2751 	return sRet;
2752 }
2753 //------------------------------------------------------------------------------
2754 XubString DbGridControl::GetCurrentRowCellText(DbGridColumn* pColumn,const DbGridRowRef& _rRow) const
2755 {
2756 	// Ausgabe des Textes fuer eine Zelle
2757 	XubString aText;
2758 	if ( pColumn && IsValid(m_xPaintRow) )
2759 		aText = pColumn->GetCellText(_rRow, m_xFormatter);
2760 	return aText;
2761 }
2762 
2763 //------------------------------------------------------------------------------
2764 sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId)
2765 {
2766 	if (SeekRow(nRow))
2767 	{
2768 		DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColId));
2769 		return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
2770 	}
2771 	else
2772 		return 30;	//xxxx
2773 }
2774 
2775 //------------------------------------------------------------------------------
2776 void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu)
2777 {
2778 	sal_Bool bDelete = (m_nOptions & OPT_DELETE) && GetSelectRowCount() && !IsCurrentAppending();
2779 	// ist nur die Leerzeile selektiert, dann nicht loeschen
2780 	bDelete = bDelete && !((m_nOptions & OPT_INSERT) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
2781 
2782 	rMenu.EnableItem(SID_FM_DELETEROWS, bDelete);
2783 	rMenu.EnableItem(SID_FM_RECORD_SAVE, IsModified());
2784 
2785 	// the undo is more difficult
2786 	sal_Bool bCanUndo = IsModified();
2787 	long nState = -1;
2788 	if (m_aMasterStateProvider.IsSet())
2789 		nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
2790 	bCanUndo &= ( 0 != nState );
2791 
2792 	rMenu.EnableItem(SID_FM_RECORD_UNDO, bCanUndo);
2793 }
2794 
2795 //------------------------------------------------------------------------------
2796 void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& /*rMenu*/, sal_uInt16 nExecutionResult)
2797 {
2798 	switch (nExecutionResult)
2799 	{
2800 		case SID_FM_DELETEROWS:
2801 			// delete asynchron
2802 			if (m_nDeleteEvent)
2803 				Application::RemoveUserEvent(m_nDeleteEvent);
2804 			m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
2805 			break;
2806 		case SID_FM_RECORD_UNDO:
2807 			Undo();
2808 			break;
2809 		case SID_FM_RECORD_SAVE:
2810 			SaveRow();
2811 			break;
2812 		default:
2813 			break;
2814 	}
2815 }
2816 
2817 //------------------------------------------------------------------------------
2818 void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
2819 {
2820 	TRACE_RANGE("DbGridControl::DataSourcePropertyChanged");
2821 	::vos::OGuard aGuard( Application::GetSolarMutex() );
2822 	// prop "IsModified" changed ?
2823 	// during update don't care about the modified state
2824 	if (!IsUpdating() && evt.PropertyName.compareTo(FM_PROP_ISMODIFIED) == COMPARE_EQUAL)
2825 	{
2826 		Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
2827 		DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
2828 		sal_Bool bIsNew = sal_False;
2829 		if (xSource.is())
2830 			bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
2831 
2832 		if (bIsNew && m_xCurrentRow.Is())
2833 		{
2834 			DBG_ASSERT(::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ROWCOUNTFINAL)), "DbGridControl::DataSourcePropertyChanged : somebody moved the form to a new record before the row count was final !");
2835 			sal_Int32 nRecordCount = 0;
2836 			xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
2837 			if (::comphelper::getBOOL(evt.NewValue))
2838 			{	// modified state changed from sal_False to sal_True and we're on a insert row
2839 				// -> we've to add a new grid row
2840 				if ((nRecordCount == GetRowCount() - 1)  && m_xCurrentRow->IsNew())
2841 				{
2842 					RowInserted(GetRowCount(), 1, sal_True);
2843 					InvalidateStatusCell(m_nCurrentPos);
2844 					m_aBar.InvalidateAll(m_nCurrentPos);
2845 				}
2846 			}
2847 			else
2848 			{	// modified state changed from sal_True to sal_False and we're on a insert row
2849 				// we have two "new row"s at the moment : the one we're editing currently (where the current
2850 				// column is the only dirty element) and a "new new" row which is completely clean. As the first
2851 				// one is about to be cleaned, too, the second one is obsolet now.
2852 				if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
2853 				{
2854 					RowRemoved(GetRowCount() - 1, 1, sal_True);
2855 					InvalidateStatusCell(m_nCurrentPos);
2856 					m_aBar.InvalidateAll(m_nCurrentPos);
2857 				}
2858 			}
2859 		}
2860 		if (m_xCurrentRow.Is())
2861 		{
2862 			m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GRS_MODIFIED : GRS_CLEAN);
2863 			m_xCurrentRow->SetNew( bIsNew );
2864 			InvalidateStatusCell(m_nCurrentPos);
2865 			TRACE_RANGE_MESSAGE1("modified flag changed, new state : %s", ROWSTATUS(m_xCurrentRow));
2866 		}
2867 	}
2868 }
2869 
2870 //------------------------------------------------------------------------------
2871 void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
2872 {
2873 	if (!m_pSeekCursor || IsResizing())
2874 		return;
2875 
2876 	sal_uInt16 nColId = GetColumnAtXPosPixel(rPosPixel.X());
2877 	long   nRow = GetRowAtYPosPixel(rPosPixel.Y());
2878 	if (nColId != HANDLE_ID && nRow >= 0)
2879 	{
2880 		if (GetDataWindow().IsMouseCaptured())
2881 			GetDataWindow().ReleaseMouse();
2882 
2883 		DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColId));
2884 		OStringTransferable* pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
2885 		Reference< XTransferable > xEnsureDelete(pTransferable);
2886 		pTransferable->StartDrag(this, DND_ACTION_COPY);
2887 	}
2888 }
2889 
2890 //------------------------------------------------------------------------------
2891 sal_Bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2892 {
2893 	return	(_nRow >= 0)
2894 		&&	(_nRow < GetRowCount())
2895 		&&	(_nColId > HANDLE_ID)
2896 		&&	(_nColId <= ColCount());
2897 }
2898 
2899 //------------------------------------------------------------------------------
2900 void DbGridControl::copyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2901 {
2902 	DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
2903 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(_nColId));
2904 	SeekRow(_nRow);
2905 	OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
2906 }
2907 
2908 //------------------------------------------------------------------------------
2909 void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos )
2910 {
2911 	PopupMenu aContextMenu( SVX_RES( RID_SVXMNU_ROWS ) );
2912 
2913 	PreExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu );
2914 	aContextMenu.RemoveDisabledEntries( sal_True, sal_True );
2915 	PostExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu, aContextMenu.Execute( this, _rPreferredPos ) );
2916 
2917 	// TODO: why this weird cast to sal_uInt16? What if we really have more than 65535 lines?
2918 	// -> change this to sal_uInt32
2919 }
2920 
2921 //------------------------------------------------------------------------------
2922 void DbGridControl::Command(const CommandEvent& rEvt)
2923 {
2924 	switch (rEvt.GetCommand())
2925 	{
2926 		case COMMAND_CONTEXTMENU:
2927 		{
2928 			if ( !m_pSeekCursor )
2929 			{
2930 				DbGridControl_Base::Command(rEvt);
2931 				return;
2932 			}
2933 
2934 			if ( !rEvt.IsMouseEvent() )
2935 			{	// context menu requested by keyboard
2936 				if ( GetSelectRowCount() )
2937 				{
2938 					long nRow = FirstSelectedRow( );
2939 
2940 					::Rectangle aRowRect( GetRowRectPixel( nRow, sal_True ) );
2941 					executeRowContextMenu( nRow, aRowRect.LeftCenter() );
2942 
2943 					// handled
2944 					return;
2945 				}
2946 			}
2947 
2948 			sal_uInt16 nColId = GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X());
2949 			long   nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
2950 
2951 			if (nColId == HANDLE_ID)
2952 			{
2953 				executeRowContextMenu( nRow, rEvt.GetMousePosPixel() );
2954 			}
2955 			else if (canCopyCellText(nRow, nColId))
2956 			{
2957 				PopupMenu aContextMenu(SVX_RES(RID_SVXMNU_CELL));
2958 				aContextMenu.RemoveDisabledEntries(sal_True, sal_True);
2959 				switch (aContextMenu.Execute(this, rEvt.GetMousePosPixel()))
2960 				{
2961 					case SID_COPY:
2962 						copyCellText(nRow, nColId);
2963 						break;
2964 				}
2965 			}
2966 			else
2967 			{
2968 				DbGridControl_Base::Command(rEvt);
2969 				return;
2970 			}
2971 		}
2972 		default:
2973 			DbGridControl_Base::Command(rEvt);
2974 	}
2975 }
2976 
2977 //------------------------------------------------------------------------------
2978 IMPL_LINK(DbGridControl, OnDelete, void*, /*EMPTYTAG*/ )
2979 {
2980 	DBG_CHKTHIS(DbGridControl, NULL );
2981 	m_nDeleteEvent = 0;
2982 	DeleteSelectedRows();
2983 	return 0;
2984 }
2985 
2986 //------------------------------------------------------------------------------
2987 void DbGridControl::DeleteSelectedRows()
2988 {
2989 	DBG_ASSERT(GetSelection(), "keine selection!!!");
2990 
2991 	if (!m_pSeekCursor)
2992 		return;
2993 
2994 /*	Application::EnterWait();
2995 	Reference< XPropertySet >  xSet = (XPropertySet*)xSeekCursor->queryInterface(XPropertySet::getSmartUik());
2996 
2997 	// wenn mehr als 25 Datensaetze geloescht werden, wird der Cache abgeschaltet
2998 	// da das loeschen ansonsten zu langsam wird
2999 	sal_uInt16 nCacheSize = 0;
3000 	if (GetSelectRowCount() > 25)
3001 	{
3002 		// CacheSize merken und Cache zuruecksetzen
3003 		nCacheSize = xSet->getPropertyValue(L"CacheSize").getUINT16();
3004 		if (nCacheSize)
3005 			xSet->setPropertyValue(L"CacheSize", Any(sal_uInt16(0)));
3006 	} */
3007 
3008 
3009 	/*
3010 	// mu� der Cache wiederhergestellt werden?
3011 	if (nCacheSize)
3012 	{
3013 		// Cache wieder einschalten
3014 		xSet->setPropertyValue(L"CacheSize", Any(sal_uInt16(nCacheSize)));
3015 
3016 		// Browser neu einstellen
3017 		RecalcRows(GetTopRow(), GetVisibleRows(), sal_True);
3018 
3019 		// aktuelle Zeile aktualisieren
3020 		SeekCursor(GetCurRow());
3021 		if (IsAppendRow(m_nSeekPos))
3022 			xDataCursor->addRecord();
3023 		else
3024 		{
3025 			Any aBookmark = xSeekCursor->getBookmark();
3026 			xDataCursor->moveToBookmark(aBookmark);
3027 		}
3028 		m_xCurrentRow = new DbGridRow(xDataCursor);
3029 		m_nCurrentPos = m_nSeekPos;
3030 
3031 		// complett invalidieren
3032 		Invalidate();
3033 	}
3034 	else
3035 		// Browser neu einstellen
3036 		RecalcRows(GetTopRow(), GetVisibleRows(), sal_True);
3037 
3038 	// gibt es keine Selection mehr?
3039 	if (!GetSelectRowCount())
3040 		ActivateCell();
3041 
3042 	m_aBar.InvalidateAll();
3043 	Application::LeaveWait();
3044 
3045 	m_bUpdating = sal_False;
3046 */
3047 }
3048 
3049 //------------------------------------------------------------------------------
3050 CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId)
3051 {
3052 	if (!IsValid(m_xCurrentRow) || !IsEnabled())
3053 		return NULL;
3054 
3055 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId));
3056 	if (!pColumn)
3057 		return NULL;
3058 
3059 	CellController* pReturn = NULL;
3060 	if (IsFilterMode())
3061 		pReturn = &pColumn->GetController();
3062 	else
3063 	{
3064 		if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
3065 		{
3066 			if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
3067 				return NULL;
3068 		}
3069 
3070 		sal_Bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & OPT_INSERT));
3071 		sal_Bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & OPT_UPDATE));
3072 
3073 		if ((bInsert && !pColumn->IsAutoValue()) || bUpdate || m_bForceROController)
3074 		{
3075 			pReturn = &pColumn->GetController();
3076 			if (pReturn)
3077 			{
3078 				// wenn es eine Edit-Zeile ist, kann ich ihr das forced read-only mitgeben
3079 				if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController))
3080 					// ich konnte den Controller in forceROController nicht auf ReadOnly setzen
3081 					if (!bInsert && !bUpdate)
3082 						// ich bin nur hier, da m_bForceROController gesetzt war
3083 						// -> lieber kein Controller als einer ohne RO
3084 						pReturn = NULL;
3085 			}
3086 		}
3087 	}
3088 	return pReturn;
3089 }
3090 
3091 //------------------------------------------------------------------------------
3092 void DbGridControl::InitController(CellControllerRef& /*rController*/, long /*nRow*/, sal_uInt16 nColumnId)
3093 {
3094 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId));
3095 	if (pColumn)
3096 		pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
3097 }
3098 
3099 //------------------------------------------------------------------------------
3100 void DbGridControl::CellModified()
3101 {
3102 	TRACE_RANGE("DbGridControl::CellModified");
3103 
3104 	{
3105 		::osl::MutexGuard aGuard(m_aAdjustSafety);
3106 		if (m_nAsynAdjustEvent)
3107 		{
3108 			TRACE_RANGE_MESSAGE1("forcing a synchron call to ", m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource");
3109 			RemoveUserEvent(m_nAsynAdjustEvent);
3110 			m_nAsynAdjustEvent = 0;
3111 
3112 			// force the call : this should be no problem as we're probably running in the solar thread here
3113 			// (cell modified is triggered by user actions)
3114 			if (m_bPendingAdjustRows)
3115 				AdjustRows();
3116 			else
3117 				AdjustDataSource();
3118 		}
3119 	}
3120 
3121 	if (!IsFilterMode() && IsValid(m_xCurrentRow) && !m_xCurrentRow->IsModified())
3122 	{
3123 		// Einschalten des Editiermodus
3124 		// Datensatz soll eingefuegt werden
3125 		if (m_xCurrentRow->IsNew())
3126 		{
3127 			m_xCurrentRow->SetStatus(GRS_MODIFIED);
3128 			TRACE_RANGE_MESSAGE("current row is new, new state : MODIFIED");
3129 			// wenn noch keine Zeile hinzugefuegt wurde, dann neue hinzunehmen
3130 			if (m_nCurrentPos == GetRowCount() - 1)
3131 			{
3132 				// RowCount um einen erhoehen
3133 				RowInserted(GetRowCount(), 1, sal_True);
3134 				InvalidateStatusCell(m_nCurrentPos);
3135 				m_aBar.InvalidateAll(m_nCurrentPos);
3136 			}
3137 		}
3138 		else if (m_xCurrentRow->GetStatus() != GRS_MODIFIED)
3139 		{
3140 			m_xCurrentRow->SetState(m_pDataCursor, sal_False);
3141 			TRACE_RANGE_MESSAGE1("current row is not new, after SetState, new state : %s", ROWSTATUS(m_xCurrentRow));
3142 			m_xCurrentRow->SetStatus(GRS_MODIFIED);
3143 			TRACE_RANGE_MESSAGE("current row is not new, new state : MODIFIED");
3144 			InvalidateStatusCell(m_nCurrentPos);
3145 		}
3146 	}
3147 }
3148 
3149 //------------------------------------------------------------------------------
3150 void DbGridControl::Dispatch(sal_uInt16 nId)
3151 {
3152 	if (nId == BROWSER_CURSORENDOFFILE)
3153 	{
3154 		if (m_nOptions & OPT_INSERT)
3155 			AppendNew();
3156 		else
3157 			MoveToLast();
3158 	}
3159 	else
3160 		DbGridControl_Base::Dispatch(nId);
3161 }
3162 
3163 //------------------------------------------------------------------------------
3164 void DbGridControl::Undo()
3165 {
3166 	if (!IsFilterMode() && IsValid(m_xCurrentRow) && IsModified())
3167 	{
3168 		// check if we have somebody doin' the UNDO for us
3169 		long nState = -1;
3170 		if (m_aMasterStateProvider.IsSet())
3171 			nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
3172 		if (nState>0)
3173 		{	// yes, we have, and the slot is enabled
3174 			DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
3175 			long lResult = m_aMasterSlotExecutor.Call((void*)SID_FM_RECORD_UNDO);
3176 			if (lResult)
3177 				// handled
3178 				return;
3179 		}
3180 		else if (nState == 0)
3181 			// yes, we have, and the slot is disabled
3182 			return;
3183 
3184 		BeginCursorAction();
3185 
3186 		sal_Bool bAppending = m_xCurrentRow->IsNew();
3187 		sal_Bool bDirty 	= m_xCurrentRow->IsModified();
3188 
3189 		try
3190 		{
3191 			// Editieren abbrechen
3192 			Reference< XResultSetUpdate >  xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
3193 			// no effects if we're not updating currently
3194 			if (bAppending)
3195 				// just refresh the row
3196 				xUpdateCursor->moveToInsertRow();
3197 			else
3198 				xUpdateCursor->cancelRowUpdates();
3199 
3200 		}
3201 		catch(Exception&)
3202 		{
3203             DBG_UNHANDLED_EXCEPTION();
3204 		}
3205 
3206 		EndCursorAction();
3207 
3208 		m_xDataRow->SetState(m_pDataCursor, sal_False);
3209 		if (&m_xPaintRow == &m_xCurrentRow)
3210 			m_xPaintRow = m_xCurrentRow = m_xDataRow;
3211 		else
3212 			m_xCurrentRow = m_xDataRow;
3213 
3214 		if (bAppending && (DbGridControl_Base::IsModified() || bDirty))
3215 		// remove the row
3216 			if (m_nCurrentPos == GetRowCount() - 2)
3217 			{	// maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
3218 				// caused our data source form to be reset - which should be the usual case ....)
3219 				RowRemoved(GetRowCount() - 1, 1, sal_True);
3220 				m_aBar.InvalidateAll(m_nCurrentPos);
3221 			}
3222 
3223 		RowModified(m_nCurrentPos);
3224 	}
3225 }
3226 
3227 //------------------------------------------------------------------------------
3228 void DbGridControl::resetCurrentRow()
3229 {
3230 	if (IsModified())
3231 	{
3232 		// scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
3233 		// is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
3234 		// the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
3235 		// data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
3236 		// would never delete the obsolet "second insert row". Thus in this special case this method here
3237 		// is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
3238 		// "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
3239 		Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
3240 		if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
3241 		{
3242 			// are we on a new row currently ?
3243 			if (m_xCurrentRow->IsNew())
3244 			{
3245 				if (m_nCurrentPos == GetRowCount() - 2)
3246 				{
3247 					RowRemoved(GetRowCount() - 1, 1, sal_True);
3248 					m_aBar.InvalidateAll(m_nCurrentPos);
3249 				}
3250 			}
3251 		}
3252 
3253 		// update the rows
3254 		m_xDataRow->SetState(m_pDataCursor, sal_False);
3255 		if (&m_xPaintRow == &m_xCurrentRow)
3256 			m_xPaintRow = m_xCurrentRow = m_xDataRow;
3257 		else
3258 			m_xCurrentRow = m_xDataRow;
3259 	}
3260 
3261 	RowModified(GetCurRow());		// will update the current controller if affected
3262 }
3263 
3264 //------------------------------------------------------------------------------
3265 void DbGridControl::RowModified( long nRow, sal_uInt16 /*nColId*/ )
3266 {
3267 	if (nRow == m_nCurrentPos && IsEditing())
3268 	{
3269 		CellControllerRef aTmpRef = Controller();
3270 		aTmpRef->ClearModified();
3271 		InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
3272 	}
3273 	DbGridControl_Base::RowModified(nRow);
3274 }
3275 
3276 //------------------------------------------------------------------------------
3277 sal_Bool DbGridControl::IsModified() const
3278 {
3279 	return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || DbGridControl_Base::IsModified());
3280 }
3281 
3282 //------------------------------------------------------------------------------
3283 sal_Bool DbGridControl::IsCurrentAppending() const
3284 {
3285 	return m_xCurrentRow.Is() && m_xCurrentRow->IsNew();
3286 }
3287 
3288 //------------------------------------------------------------------------------
3289 sal_Bool DbGridControl::IsInsertionRow(long nRow) const
3290 {
3291 	return (m_nOptions & OPT_INSERT) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
3292 }
3293 
3294 //------------------------------------------------------------------------------
3295 sal_Bool DbGridControl::SaveModified()
3296 {
3297 	TRACE_RANGE("DbGridControl::SaveModified");
3298 	DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
3299 	if (!IsValid(m_xCurrentRow))
3300 		return sal_True;
3301 
3302 	// Uebernimmt die Dateneingabe fuer das Feld
3303 	// Hat es aenderungen im aktuellen Eingabefeld gegeben ?
3304 	if (!DbGridControl_Base::IsModified())
3305 		return sal_True;
3306 
3307 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(GetCurColumnId()));
3308 	sal_Bool bOK = pColumn->Commit();
3309 	DBG_ASSERT( Controller().Is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
3310     if ( !Controller().Is() )
3311         // this might happen if the callbacks implicitly triggered by Commit
3312         // fiddled with the form or the control ...
3313         // (Note that this here is a workaround, at most. We need a general concept how
3314         // to treat this, you can imagine an arbitrary number of scenarios where a callback
3315         // triggers something which leaves us in an expected state.)
3316         // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
3317         return bOK;
3318 
3319 	if (bOK)
3320 	{
3321 		Controller()->ClearModified();
3322 
3323 		if ( IsValid(m_xCurrentRow) )
3324 		{
3325 			m_xCurrentRow->SetState(m_pDataCursor, sal_False);
3326 			TRACE_RANGE_MESSAGE1("explicit SetState, new state : %s", ROWSTATUS(m_xCurrentRow));
3327 			InvalidateStatusCell( m_nCurrentPos );
3328 		}
3329 #ifdef DBG_UTIL
3330 		else
3331 		{
3332 			TRACE_RANGE_MESSAGE1("no SetState, new state : %s", ROWSTATUS(m_xCurrentRow));
3333 		}
3334 #endif
3335 	}
3336 	else
3337 	{
3338 		// reset the modified flag ....
3339 		Controller()->SetModified();
3340 	}
3341 
3342 	return bOK;
3343 }
3344 
3345 //------------------------------------------------------------------------------
3346 sal_Bool DbGridControl::SaveRow()
3347 {
3348 	TRACE_RANGE("DbGridControl::SaveRow");
3349 	// gueltige Zeile
3350 	if (!IsValid(m_xCurrentRow) || !IsModified())
3351 		return sal_True;
3352 	// Wert des Controllers noch nicht gespeichert
3353 	else if (Controller().Is() && Controller()->IsModified())
3354 	{
3355 		if (!SaveModified())
3356 			return sal_False;
3357 	}
3358 	m_bUpdating = sal_True;
3359 
3360 	BeginCursorAction();
3361 	sal_Bool bAppending = m_xCurrentRow->IsNew();
3362 	sal_Bool bSuccess = sal_False;
3363 	try
3364 	{
3365 		Reference< XResultSetUpdate >  xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
3366 		if (bAppending)
3367 			xUpdateCursor->insertRow();
3368 		else
3369 			xUpdateCursor->updateRow();
3370 		bSuccess = sal_True;
3371 	}
3372 	catch(SQLException& e)
3373 	{
3374 		(void)e; // make compiler happy
3375 		EndCursorAction();
3376 		m_bUpdating = sal_False;
3377 		return sal_False;
3378 	}
3379 
3380 	try
3381 	{
3382 		if (bSuccess)
3383 		{
3384 			// if we are appending we still sit on the insert row
3385 			// we don't move just clear the flags not to move on the current row
3386 			m_xCurrentRow->SetState(m_pDataCursor, sal_False);
3387 			TRACE_RANGE_MESSAGE1("explicit SetState after a successful update, new state : %s", ROWSTATUS(m_xCurrentRow));
3388 			m_xCurrentRow->SetNew(sal_False);
3389 
3390 			// adjust the seekcursor if it is on the same position as the datacursor
3391 			if (m_nSeekPos == m_nCurrentPos || bAppending)
3392 			{
3393 				// get the bookmark to refetch the data
3394 				// in insert mode we take the new bookmark of the data cursor
3395 				Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
3396 				m_pSeekCursor->moveToBookmark(aBookmark);
3397 				// get the data
3398 				m_xSeekRow->SetState(m_pSeekCursor, sal_True);
3399 				m_nSeekPos = m_pSeekCursor->getRow() - 1;
3400 			}
3401 		}
3402 		// and repaint the row
3403 		RowModified(m_nCurrentPos);
3404 	}
3405 	catch(Exception&)
3406 	{
3407 	}
3408 
3409 	m_bUpdating = sal_False;
3410 	EndCursorAction();
3411 
3412 	// The old code returned (nRecords != 0) here.
3413 	// Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
3414 	// which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
3415 	// is zero, this simply means all fields had their original values.
3416 	// FS - 06.12.99 - 70502
3417 	return sal_True;
3418 }
3419 
3420 //------------------------------------------------------------------------------
3421 long DbGridControl::PreNotify(NotifyEvent& rEvt)
3422 {
3423 	// keine Events der Navbar behandeln
3424 	if (m_aBar.IsWindowOrChild(rEvt.GetWindow()))
3425 		return BrowseBox::PreNotify(rEvt);
3426 
3427 	switch (rEvt.GetType())
3428 	{
3429 		case EVENT_KEYINPUT:
3430 		{
3431 			const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
3432 
3433 			sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
3434 			sal_Bool   bShift = pKeyEvent->GetKeyCode().IsShift();
3435 			sal_Bool   bCtrl = pKeyEvent->GetKeyCode().IsMod1();
3436 			sal_Bool   bAlt = pKeyEvent->GetKeyCode().IsMod2();
3437 			if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
3438 			{
3439 				// Ctrl-Tab is used to step out of the control, without traveling to the
3440 				// remaining cells first
3441 				// -> build a new key event without the Ctrl-key, and let the very base class handle it
3442 				KeyCode aNewCode( KEY_TAB, bShift, sal_False, sal_False, sal_False );
3443 				KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
3444 
3445 				// call the Control - our direct base class will interpret this in a way we do not want (and do
3446 				// a cell traveling)
3447 				Control::KeyInput( aNewEvent );
3448 				return 1;
3449 			}
3450 
3451 			if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
3452 			{
3453 				if (IsModified())
3454 				{
3455 					Undo();
3456 					return 1;
3457 				}
3458 			}
3459 			else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl )	// delete rows
3460 			{
3461 				if ((m_nOptions & OPT_DELETE) && GetSelectRowCount())
3462 				{
3463 					// delete asynchron
3464 					if (m_nDeleteEvent)
3465 						Application::RemoveUserEvent(m_nDeleteEvent);
3466 					m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
3467 					return 1;
3468 				}
3469 			}
3470 		}	// kein break!
3471 		default:
3472 			return DbGridControl_Base::PreNotify(rEvt);
3473 	}
3474 }
3475 
3476 //------------------------------------------------------------------------------
3477 sal_Bool DbGridControl::IsTabAllowed(sal_Bool bRight) const
3478 {
3479 	if (bRight)
3480 		// Tab nur wenn nicht auf der letzten Zelle
3481 		return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
3482 			   GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
3483 	else
3484 	{
3485 		// Tab nur wenn nicht auf der ersten Zelle
3486 		return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
3487 	}
3488 }
3489 
3490 //------------------------------------------------------------------------------
3491 void DbGridControl::KeyInput( const KeyEvent& rEvt )
3492 {
3493 	if (rEvt.GetKeyCode().GetFunction() == KEYFUNC_COPY)
3494 	{
3495 		long nRow = GetCurRow();
3496 		sal_uInt16 nColId = GetCurColumnId();
3497 		if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
3498 		{
3499 			DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColId));
3500 			OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
3501 			return;
3502 		}
3503 	}
3504 	DbGridControl_Base::KeyInput(rEvt);
3505 }
3506 
3507 //------------------------------------------------------------------------------
3508 void DbGridControl::HideColumn(sal_uInt16 nId)
3509 {
3510 	DeactivateCell();
3511 
3512 	// determine the col for the focus to set to after removal
3513 	sal_uInt16 nPos = GetViewColumnPos(nId);
3514 	sal_uInt16 nNewColId = nPos == (ColCount()-1)
3515 		? GetColumnIdFromViewPos(nPos-1)	// last col is to be removed -> take the previous
3516 		: GetColumnIdFromViewPos(nPos+1);	// take the next
3517 
3518 	long lCurrentWidth = GetColumnWidth(nId);
3519 	DbGridControl_Base::RemoveColumn(nId);
3520 		// don't use my own RemoveColumn, this would remove it from m_aColumns, too
3521 
3522 	// update my model
3523 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nId));
3524 	DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
3525 	if (pColumn)
3526 	{
3527 		pColumn->m_bHidden = sal_True;
3528 		pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
3529 	}
3530 
3531 	// and reset the focus
3532 	if ( nId == GetCurColumnId() )
3533 		GoToColumnId( nNewColId );
3534 }
3535 
3536 //------------------------------------------------------------------------------
3537 void DbGridControl::ShowColumn(sal_uInt16 nId)
3538 {
3539 	sal_uInt16 nPos = GetModelColumnPos(nId);
3540 	DBG_ASSERT(nPos != (sal_uInt16)-1, "DbGridControl::ShowColumn : invalid argument !");
3541 	if (nPos == (sal_uInt16)-1)
3542 		return;
3543 
3544 	DbGridColumn* pColumn = m_aColumns.GetObject(nPos);
3545 	if (!pColumn->IsHidden())
3546 	{
3547 		DBG_ASSERT(GetViewColumnPos(nId) != (sal_uInt16)-1, "DbGridControl::ShowColumn : inconsistent internal state !");
3548 			// if the column isn't marked as hidden, it should be visible, shouldn't it ?
3549 		return;
3550 	}
3551 	DBG_ASSERT(GetViewColumnPos(nId) == (sal_uInt16)-1, "DbGridControl::ShowColumn : inconsistent internal state !");
3552 		// the opposite situation ...
3553 
3554 	// to determine the new view position we need an adjacent non-hidden column
3555 	sal_uInt16 nNextNonHidden = (sal_uInt16)-1;
3556 	// first search the cols to the right
3557 	for (sal_uInt16 i = nPos + 1; i<m_aColumns.Count(); ++i)
3558 	{
3559 		DbGridColumn* pCurCol = m_aColumns.GetObject(i);
3560 		if (!pCurCol->IsHidden())
3561 		{
3562 			nNextNonHidden = i;
3563 			break;
3564 		}
3565 	}
3566 	if ((nNextNonHidden == (sal_uInt16)-1) && (nPos > 0))
3567 	{
3568 		// then to the left
3569 		for (sal_uInt16 i = nPos; i>0; --i)
3570 		{
3571 			DbGridColumn* pCurCol = m_aColumns.GetObject(i-1);
3572 			if (!pCurCol->IsHidden())
3573 			{
3574 				nNextNonHidden = i-1;
3575 				break;
3576 			}
3577 		}
3578 	}
3579 	sal_uInt16 nNewViewPos = (nNextNonHidden == (sal_uInt16)-1)
3580 		? 1 // there is no visible column -> insert behinde the handle col
3581 		: GetViewColumnPos(m_aColumns.GetObject(nNextNonHidden)->GetId()) + 1;
3582 			// the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
3583 			// a position 1 for the first non-handle col -> +1
3584 	DBG_ASSERT(nNewViewPos != (sal_uInt16)-1, "DbGridControl::ShowColumn : inconsistent internal state !");
3585 		// we found a col marked as visible but got no view pos for it ...
3586 
3587 	if ((nNextNonHidden<nPos) && (nNextNonHidden != (sal_uInt16)-1))
3588 		// nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
3589 		++nNewViewPos;
3590 
3591 	DeactivateCell();
3592 
3593 	::rtl::OUString aName;
3594 	pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
3595 	InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HIB_CENTER | HIB_VCENTER | HIB_CLICKABLE, nNewViewPos);
3596 	pColumn->m_bHidden = sal_False;
3597 
3598 	ActivateCell();
3599 	Invalidate();
3600 }
3601 
3602 //------------------------------------------------------------------------------
3603 sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
3604 {
3605 	if (nPos >= m_aColumns.Count())
3606 	{
3607 		DBG_ERROR("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
3608 		return (sal_uInt16)-1;
3609 	}
3610 
3611 	DbGridColumn* pCol = m_aColumns.GetObject(nPos);
3612 #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
3613 	// in der Debug-Version rechnen wir die ModelPos in eine ViewPos um und vergleichen das mit dem Wert,
3614 	// den wir zurueckliefern werden (nId an der entsprechenden Col in m_aColumns)
3615 
3616 	if (!pCol->IsHidden())
3617 	{	// macht nur Sinn, wenn die Spalte sichtbar ist
3618 		sal_uInt16 nViewPos = nPos;
3619 		for (sal_uInt16 i=0; i<m_aColumns.Count() && i<nPos; ++i)
3620 			if (m_aColumns.GetObject(i)->IsHidden())
3621 				--nViewPos;
3622 
3623 		DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
3624 			"DbGridControl::GetColumnIdFromModelPos : this isn't consistent .... did I misunderstand something ?");
3625 	}
3626 #endif
3627 	return pCol->GetId();
3628 }
3629 
3630 //------------------------------------------------------------------------------
3631 sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
3632 {
3633 	for (sal_uInt16 i=0; i<m_aColumns.Count(); ++i)
3634 		if (m_aColumns.GetObject(i)->GetId() == nId)
3635 			return i;
3636 
3637     return GRID_COLUMN_NOT_FOUND;
3638 }
3639 
3640 //------------------------------------------------------------------------------
3641 void DbGridControl::implAdjustInSolarThread(sal_Bool _bRows)
3642 {
3643 	TRACE_RANGE("DbGridControl::implAdjustInSolarThread");
3644 	::osl::MutexGuard aGuard(m_aAdjustSafety);
3645 	if (::vos::OThread::getCurrentIdentifier() != Application::GetMainThreadIdentifier())
3646 	{
3647 		m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ));
3648 		m_bPendingAdjustRows = _bRows;
3649 #ifdef DBG_UTIL
3650 		if (_bRows)
3651 			TRACE_RANGE_MESSAGE("posting an AdjustRows")
3652 		else
3653 			TRACE_RANGE_MESSAGE("posting an AdjustDataSource")
3654 #endif
3655 	}
3656 	else
3657 	{
3658 #ifdef DBG_UTIL
3659 		if (_bRows)
3660 			TRACE_RANGE_MESSAGE("doing an AdjustRows")
3661 		else
3662 			TRACE_RANGE_MESSAGE("doing an AdjustDataSource")
3663 #endif
3664 		// always adjust the rows before adjusting the data source
3665 		// If this is not necessary (because the row count did not change), nothing is done
3666 		// The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
3667 		// to a position behind row count know 'til now, the cursorMoved notification may come before the
3668 		// RowCountChanged notification
3669 		// 94093 - 02.11.2001 - frank.schoenheit@sun.com
3670 		AdjustRows();
3671 
3672 		if ( !_bRows )
3673 			AdjustDataSource();
3674 	}
3675 }
3676 
3677 //------------------------------------------------------------------------------
3678 IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat)
3679 {
3680 	m_nAsynAdjustEvent = 0;
3681 
3682 	AdjustRows();
3683 		// see implAdjustInSolarThread for a comment why we do this every time
3684 
3685 	if ( !pAdjustWhat )
3686 		AdjustDataSource();
3687 
3688 	return 0L;
3689 }
3690 
3691 //------------------------------------------------------------------------------
3692 void DbGridControl::BeginCursorAction()
3693 {
3694 	if (m_pFieldListeners)
3695 	{
3696 		ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3697 		ConstColumnFieldValueListenersIterator aIter = pListeners->begin();
3698 		while (aIter != pListeners->end())
3699 		{
3700 			GridFieldValueListener* pCurrent = (*aIter).second;
3701 			if (pCurrent)
3702 				pCurrent->suspend();
3703 			++aIter;
3704 		}
3705 	}
3706 
3707 	if (m_pDataSourcePropListener)
3708 		m_pDataSourcePropListener->suspend();
3709 }
3710 
3711 //------------------------------------------------------------------------------
3712 void DbGridControl::EndCursorAction()
3713 {
3714 	if (m_pFieldListeners)
3715 	{
3716 		ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3717 		ConstColumnFieldValueListenersIterator aIter = pListeners->begin();
3718 		while (aIter != pListeners->end())
3719 		{
3720 			GridFieldValueListener* pCurrent = (*aIter).second;
3721 			if (pCurrent)
3722 				pCurrent->resume();
3723 			++aIter;
3724 		}
3725 	}
3726 
3727 	if (m_pDataSourcePropListener)
3728 		m_pDataSourcePropListener->resume();
3729 }
3730 
3731 //------------------------------------------------------------------------------
3732 void DbGridControl::ConnectToFields()
3733 {
3734 	ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3735 	DBG_ASSERT(!pListeners || pListeners->size() == 0, "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
3736 
3737 	if (!pListeners)
3738 	{
3739 		pListeners = new ColumnFieldValueListeners;
3740 		m_pFieldListeners = pListeners;
3741 	}
3742 
3743 	for (sal_Int32 i=0; i<(sal_Int32)m_aColumns.Count(); ++i)
3744 	{
3745 		DbGridColumn* pCurrent = m_aColumns.GetObject(i);
3746 		sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : (sal_uInt16)-1;
3747 		if ((sal_uInt16)-1 == nViewPos)
3748 			continue;
3749 
3750 		Reference< XPropertySet >  xField = pCurrent->GetField();
3751 		if (!xField.is())
3752 			continue;
3753 
3754 		// column is visible and bound here
3755 		GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
3756 		DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
3757 		rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
3758 	}
3759 }
3760 
3761 //------------------------------------------------------------------------------
3762 void DbGridControl::DisconnectFromFields()
3763 {
3764 	if (!m_pFieldListeners)
3765 		return;
3766 
3767 	ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3768 	while (pListeners->size())
3769 	{
3770 #ifdef DBG_UTIL
3771 		sal_Int32 nOldSize = pListeners->size();
3772 #endif
3773 		pListeners->begin()->second->dispose();
3774 		DBG_ASSERT(nOldSize > (sal_Int32)pListeners->size(), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
3775 	}
3776 
3777 	delete pListeners;
3778 	m_pFieldListeners = NULL;
3779 }
3780 
3781 //------------------------------------------------------------------------------
3782 void DbGridControl::FieldValueChanged(sal_uInt16 _nId, const PropertyChangeEvent& /*_evt*/)
3783 {
3784 	osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
3785 	// needed as this may run in a thread other than the main one
3786 	if (GetRowStatus(GetCurRow()) != DbGridControl_Base::MODIFIED)
3787 		// all other cases are handled elsewhere
3788 		return;
3789 
3790 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(_nId));
3791 	if (pColumn)
3792 	{
3793 		sal_Bool bAcquiredPaintSafety = sal_False;
3794 		while (!m_bWantDestruction && !bAcquiredPaintSafety)
3795 			bAcquiredPaintSafety  = Application::GetSolarMutex().tryToAcquire();
3796 
3797 		if (m_bWantDestruction)
3798 		{	// at this moment, within another thread, our destructor tries to destroy the listener which called this method
3799 			// => don't do anything
3800 			// 73365 - 23.02.00 - FS
3801 			if (bAcquiredPaintSafety)
3802 				// though the above while-loop suggests that (m_bWantDestruction && bAcquiredPaintSafety) is impossible,
3803 				// it isnt't, as m_bWantDestruction isn't protected with any mutex
3804 				Application::GetSolarMutex().release();
3805 			return;
3806 		}
3807 		// here we got the solar mutex, transfer it to a guard for safety reasons
3808 		::vos::OGuard aPaintSafety(Application::GetSolarMutex());
3809 		Application::GetSolarMutex().release();
3810 
3811 		// and finally do the update ...
3812 		pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
3813 		RowModified(GetCurRow(), _nId);
3814 	}
3815 }
3816 
3817 //------------------------------------------------------------------------------
3818 void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
3819 {
3820 	ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3821 	if (!pListeners)
3822 	{
3823 		DBG_ERROR("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
3824 		return;
3825 	}
3826 
3827 	ColumnFieldValueListenersIterator aPos = pListeners->find(_nId);
3828 	if (aPos == pListeners->end())
3829 	{
3830 		DBG_ERROR("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
3831 		return;
3832 	}
3833 
3834 	delete aPos->second;
3835 
3836 	pListeners->erase(aPos);
3837 }
3838 
3839 //------------------------------------------------------------------------------
3840 void DbGridControl::disposing(sal_uInt16 _nId, const EventObject& /*_rEvt*/)
3841 {
3842 	if (_nId == 0)
3843 	{	// the seek cursor is beeing disposed
3844 		::osl::MutexGuard aGuard(m_aAdjustSafety);
3845 		setDataSource(NULL,0); // our clone was disposed so we set our datasource to null to avoid later acces to it
3846 		if (m_nAsynAdjustEvent)
3847 		{
3848 			RemoveUserEvent(m_nAsynAdjustEvent);
3849 			m_nAsynAdjustEvent = 0;
3850 		}
3851 	}
3852 }
3853 // -----------------------------------------------------------------------------
3854 sal_Int32 DbGridControl::GetAccessibleControlCount() const
3855 {
3856 	return DbGridControl_Base::GetAccessibleControlCount() + 1; // the navigation control
3857 }
3858 // -----------------------------------------------------------------------------
3859 Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
3860 {
3861 	Reference<XAccessible > xRet;
3862 	if ( _nIndex == DbGridControl_Base::GetAccessibleControlCount() )
3863 	{
3864 		xRet = m_aBar.GetAccessible();
3865 	}
3866 	else
3867 		xRet = DbGridControl_Base::CreateAccessibleControl( _nIndex );
3868 	return xRet;
3869 }
3870 // -----------------------------------------------------------------------------
3871 Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
3872 {
3873 	sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
3874 	DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId));
3875 	if ( pColumn )
3876 	{
3877 		Reference< ::com::sun::star::awt::XControl> xInt(pColumn->GetCell());
3878 		Reference< ::com::sun::star::awt::XCheckBox> xBox(xInt,UNO_QUERY);
3879 		if ( xBox.is() )
3880 		{
3881 			TriState eValue = STATE_NOCHECK;
3882 			switch( xBox->getState() )
3883 			{
3884 				case 0:
3885 					eValue = STATE_NOCHECK;
3886 					break;
3887 				case 1:
3888 					eValue = STATE_CHECK;
3889 					break;
3890 				case 2:
3891 					eValue = STATE_DONTKNOW;
3892 					break;
3893 			}
3894 			return DbGridControl_Base::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue,sal_True );
3895 		}
3896 	}
3897 	return DbGridControl_Base::CreateAccessibleCell( _nRow, _nColumnPos );
3898 }
3899 // -----------------------------------------------------------------------------
3900 
3901 
3902