bump product version to 4.1.6.2
[LibreOffice.git] / svx / source / fmcomp / gridctrl.cxx
blobf3d875c7361b9daf171295edcd156e9cd96f7dc8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <sal/macros.h>
22 #include "fmhelp.hrc"
23 #include <svx/gridctrl.hxx>
24 #include "gridcell.hxx"
25 #include "svx/dbtoolsclient.hxx"
26 #include "svx/fmtools.hxx"
27 #include <svtools/stringtransfer.hxx>
29 #include "fmprop.hrc"
30 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
31 #include <com/sun/star/accessibility/XAccessible.hpp>
32 #include <com/sun/star/sdb/XResultSetAccess.hpp>
33 #include <com/sun/star/sdb/RowChangeAction.hpp>
34 #include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
35 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
36 #include <com/sun/star/sdbcx/Privilege.hpp>
37 #include <com/sun/star/container/XChild.hpp>
38 #include <com/sun/star/util/NumberFormatter.hpp>
39 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
40 #include <com/sun/star/util/XCloneable.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
43 #include <comphelper/extract.hxx>
44 #include <comphelper/processfactory.hxx>
45 #include <tools/resid.hxx>
46 #include <tools/diagnose_ex.h>
47 #include <vcl/menu.hxx>
49 #include "svx/fmresids.hrc"
51 #include <svx/svxids.hrc>
52 #include <tools/shl.hxx>
53 #include <svx/dialmgr.hxx>
54 #include "fmservs.hxx"
55 #include "sdbdatacolumn.hxx"
57 #include <comphelper/stl_types.hxx>
58 #include <comphelper/property.hxx>
60 #include <algorithm>
62 using namespace ::svxform;
63 using namespace ::svt;
64 using namespace ::com::sun::star::beans;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::uno;
67 using namespace ::com::sun::star::sdbc;
68 using namespace ::com::sun::star::sdbcx;
69 using namespace ::com::sun::star::sdb;
70 using namespace ::com::sun::star::datatransfer;
71 using namespace ::com::sun::star::container;
72 using namespace com::sun::star::accessibility;
74 #define ROWSTATUS(row) (!row.Is() ? "NULL" : row->GetStatus() == GRS_CLEAN ? "CLEAN" : row->GetStatus() == GRS_MODIFIED ? "MODIFIED" : row->GetStatus() == GRS_DELETED ? "DELETED" : "INVALID")
77 #define DEFAULT_BROWSE_MODE \
78 BROWSER_COLUMNSELECTION \
79 | BROWSER_MULTISELECTION \
80 | BROWSER_KEEPSELECTION \
81 | BROWSER_TRACKING_TIPS \
82 | BROWSER_HLINESFULL \
83 | BROWSER_VLINESFULL \
84 | BROWSER_HEADERBAR_NEW \
86 class RowSetEventListener : public ::cppu::WeakImplHelper1<XRowsChangeListener>
88 DbGridControl* m_pControl;
89 public:
90 RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
93 private:
94 // XEventListener
95 virtual void SAL_CALL disposing(const ::com::sun::star::lang::EventObject& /*i_aEvt*/) throw ( RuntimeException )
98 virtual void SAL_CALL rowsChanged(const ::com::sun::star::sdb::RowsChangeEvent& i_aEvt) throw ( RuntimeException )
100 if ( i_aEvt.Action == RowChangeAction::UPDATE )
102 ::DbGridControl::GrantControlAccess aAccess;
103 CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
104 const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
105 const Any* pIter = i_aEvt.Bookmarks.getConstArray();
106 const Any* pEnd = pIter + i_aEvt.Bookmarks.getLength();
107 for(;pIter != pEnd;++pIter)
109 pSeek->moveToBookmark(*pIter);
110 // get the data
111 rSeekRow->SetState(pSeek, sal_True);
112 sal_Int32 nSeekPos = pSeek->getRow() - 1;
113 m_pControl->SetSeekPos(nSeekPos,aAccess);
114 m_pControl->RowModified(nSeekPos);
119 //==============================================================================
121 class GridFieldValueListener;
122 DECLARE_STL_MAP(sal_uInt16, GridFieldValueListener*, ::std::less<sal_uInt16>, ColumnFieldValueListeners);
124 //==============================================================================
126 DBG_NAME(GridFieldValueListener)
127 class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
129 osl::Mutex m_aMutex;
130 DbGridControl& m_rParent;
131 ::comphelper::OPropertyChangeMultiplexer* m_pRealListener;
132 sal_uInt16 m_nId;
133 sal_Int16 m_nSuspended;
134 sal_Bool m_bDisposed : 1;
136 public:
137 GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
138 virtual ~GridFieldValueListener();
140 virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException );
142 void suspend() { ++m_nSuspended; }
143 void resume() { --m_nSuspended; }
145 void dispose();
147 //------------------------------------------------------------------------------
148 GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId)
149 :OPropertyChangeListener(m_aMutex)
150 ,m_rParent(_rParent)
151 ,m_pRealListener(NULL)
152 ,m_nId(_nId)
153 ,m_nSuspended(0)
154 ,m_bDisposed(sal_False)
156 DBG_CTOR(GridFieldValueListener, NULL);
157 if (_rField.is())
159 m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
160 m_pRealListener->addProperty(FM_PROP_VALUE);
161 m_pRealListener->acquire();
165 //------------------------------------------------------------------------------
166 GridFieldValueListener::~GridFieldValueListener()
168 DBG_DTOR(GridFieldValueListener, NULL);
169 dispose();
172 //------------------------------------------------------------------------------
173 void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& _evt) throw( RuntimeException )
175 DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
176 if (m_nSuspended <= 0)
177 m_rParent.FieldValueChanged(m_nId, _evt);
180 //------------------------------------------------------------------------------
181 void GridFieldValueListener::dispose()
183 if (m_bDisposed)
185 DBG_ASSERT(m_pRealListener == NULL, "GridFieldValueListener::dispose : inconsistent !");
186 return;
189 if (m_pRealListener)
191 m_pRealListener->dispose();
192 m_pRealListener->release();
193 m_pRealListener = NULL;
196 m_bDisposed = sal_True;
197 m_rParent.FieldListenerDisposing(m_nId);
200 //==============================================================================
202 class DisposeListenerGridBridge : public FmXDisposeListener
204 osl::Mutex m_aMutex;
205 DbGridControl& m_rParent;
206 FmXDisposeMultiplexer* m_pRealListener;
208 public:
209 DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId = -1);
210 virtual ~DisposeListenerGridBridge();
212 virtual void disposing(const EventObject& _rEvent, sal_Int16 _nId) throw( RuntimeException ) { m_rParent.disposing(_nId, _rEvent); }
215 //==============================================================================
218 DBG_NAME(DisposeListenerGridBridge)
219 //------------------------------------------------------------------------------
220 DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId)
221 :FmXDisposeListener(m_aMutex)
222 ,m_rParent(_rParent)
223 ,m_pRealListener(NULL)
225 DBG_CTOR(DisposeListenerGridBridge,NULL);
227 if (_rxObject.is())
229 m_pRealListener = new FmXDisposeMultiplexer(this, _rxObject, _rId);
230 m_pRealListener->acquire();
234 //------------------------------------------------------------------------------
235 DisposeListenerGridBridge::~DisposeListenerGridBridge()
237 if (m_pRealListener)
239 m_pRealListener->dispose();
240 m_pRealListener->release();
241 m_pRealListener = NULL;
244 DBG_DTOR(DisposeListenerGridBridge,NULL);
247 //==============================================================================
249 static sal_uInt16 ControlMap[] =
251 DbGridControl::NavigationBar::RECORD_TEXT,
252 DbGridControl::NavigationBar::RECORD_ABSOLUTE,
253 DbGridControl::NavigationBar::RECORD_OF,
254 DbGridControl::NavigationBar::RECORD_COUNT,
255 DbGridControl::NavigationBar::RECORD_FIRST,
256 DbGridControl::NavigationBar::RECORD_NEXT,
257 DbGridControl::NavigationBar::RECORD_PREV,
258 DbGridControl::NavigationBar::RECORD_LAST,
259 DbGridControl::NavigationBar::RECORD_NEW,
263 //------------------------------------------------------------------------------
264 sal_Bool CompareBookmark(const Any& aLeft, const Any& aRight)
266 return ::comphelper::compare(aLeft, aRight);
269 //==============================================================================
270 class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
272 DbGridControl* m_pParent;
274 // a DbGridControl has no mutex, so we use our own as the base class expects one
275 osl::Mutex m_aMutex;
276 sal_Int16 m_nSuspended;
278 public:
279 FmXGridSourcePropListener(DbGridControl* _pParent);
281 void suspend() { ++m_nSuspended; }
282 void resume() { --m_nSuspended; }
284 virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException );
287 //------------------------------------------------------------------------------
288 FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
289 :OPropertyChangeListener(m_aMutex)
290 ,m_pParent(_pParent)
291 ,m_nSuspended(0)
293 DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
296 //------------------------------------------------------------------------------
297 void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
299 DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
300 if (m_nSuspended <= 0)
301 m_pParent->DataSourcePropertyChanged(evt);
304 //==============================================================================
305 //------------------------------------------------------------------------------
306 DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(Window* pParent, WinBits nStyle)
307 :NumericField(pParent, nStyle)
309 SetMin(1);
310 SetFirst(1);
311 SetSpinSize(1);
313 SetDecimalDigits(0);
314 SetStrictFormat(sal_True);
317 //------------------------------------------------------------------------------
318 void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt)
320 if (rEvt.GetKeyCode() == KEY_RETURN && !GetText().isEmpty())
322 sal_Int64 nRecord = GetValue();
323 if (nRecord < GetMin() || nRecord > GetMax())
324 return;
325 else
326 ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
328 else if (rEvt.GetKeyCode() == KEY_TAB)
329 GetParent()->GetParent()->GrabFocus();
330 else
331 NumericField::KeyInput(rEvt);
334 //------------------------------------------------------------------------------
335 void DbGridControl::NavigationBar::AbsolutePos::LoseFocus()
337 NumericField::LoseFocus();
338 sal_Int64 nRecord = GetValue();
339 if (nRecord < GetMin() || nRecord > GetMax())
340 return;
341 else
343 ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
344 ((NavigationBar*)GetParent())->InvalidateState(NavigationBar::RECORD_ABSOLUTE);
348 //------------------------------------------------------------------------------
349 void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord)
351 if (m_bPositioning)
352 return;
353 // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition, so protect agains this
354 // recursion
355 // 68167 - 13.08.99 - FS
356 m_bPositioning = sal_True;
357 ((DbGridControl*)GetParent())->MoveToPosition(nRecord - 1);
358 m_bPositioning = sal_False;
361 //------------------------------------------------------------------------------
362 DbGridControl::NavigationBar::NavigationBar(Window* pParent, WinBits nStyle)
363 :Control(pParent, nStyle)
364 ,m_aRecordText(this, WB_VCENTER)
365 ,m_aAbsolute(this, WB_CENTER | WB_VCENTER)
366 ,m_aRecordOf(this, WB_VCENTER)
367 ,m_aRecordCount(this, WB_VCENTER)
368 ,m_aFirstBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
369 ,m_aPrevBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS)
370 ,m_aNextBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS)
371 ,m_aLastBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
372 ,m_aNewBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
373 ,m_nDefaultWidth(0)
374 ,m_nCurrentPos(-1)
375 ,m_bPositioning(sal_False)
377 m_aFirstBtn.SetSymbol(SYMBOL_FIRST);
378 m_aPrevBtn.SetSymbol(SYMBOL_PREV);
379 m_aNextBtn.SetSymbol(SYMBOL_NEXT);
380 m_aLastBtn.SetSymbol(SYMBOL_LAST);
381 m_aNewBtn.SetModeImage(((DbGridControl*)pParent)->GetImage(DbGridControl_Base::NEW));
383 m_aFirstBtn.SetHelpId(HID_GRID_TRAVEL_FIRST);
384 m_aPrevBtn.SetHelpId(HID_GRID_TRAVEL_PREV);
385 m_aNextBtn.SetHelpId(HID_GRID_TRAVEL_NEXT);
386 m_aLastBtn.SetHelpId(HID_GRID_TRAVEL_LAST);
387 m_aNewBtn.SetHelpId(HID_GRID_TRAVEL_NEW);
388 m_aAbsolute.SetHelpId(HID_GRID_TRAVEL_ABSOLUTE);
389 m_aRecordCount.SetHelpId(HID_GRID_NUMBEROFRECORDS);
391 // Handler fuer Buttons einrichten
392 m_aFirstBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
393 m_aPrevBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
394 m_aNextBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
395 m_aLastBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
396 m_aNewBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
398 m_aRecordText.SetText(XubString(SVX_RES(RID_STR_REC_TEXT)));
399 m_aRecordOf.SetText(XubString(SVX_RES(RID_STR_REC_FROM_TEXT)));
400 m_aRecordCount.SetText(OUString('?'));
402 m_nDefaultWidth = ArrangeControls();
404 m_aFirstBtn.Disable();
405 m_aPrevBtn.Disable();
406 m_aNextBtn.Disable();
407 m_aLastBtn.Disable();
408 m_aNewBtn.Disable();
409 m_aRecordText.Disable();
410 m_aRecordOf.Disable();
411 m_aRecordCount.Disable();
412 m_aAbsolute.Disable();
414 AllSettings aSettings = m_aNextBtn.GetSettings();
415 MouseSettings aMouseSettings = aSettings.GetMouseSettings();
416 aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
417 aSettings.SetMouseSettings(aMouseSettings);
418 m_aNextBtn.SetSettings(aSettings, sal_True);
419 m_aPrevBtn.SetSettings(aSettings, sal_True);
421 m_aFirstBtn.Show();
422 m_aPrevBtn.Show();
423 m_aNextBtn.Show();
424 m_aLastBtn.Show();
425 m_aNewBtn.Show();
426 m_aRecordText.Show();
427 m_aRecordOf.Show();
428 m_aRecordCount.Show();
429 m_aAbsolute.Show();
432 namespace
434 void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize)
436 _rButton.SetPosPixel( _rPos );
437 _rButton.SetSizePixel( _rSize );
438 _rPos.X() += (sal_uInt16)_rSize.Width();
441 //------------------------------------------------------------------------------
442 sal_uInt16 DbGridControl::NavigationBar::ArrangeControls()
444 // Positionierung der Controls
445 // Basisgroessen ermitteln
446 Rectangle aRect(((DbGridControl*)GetParent())->GetControlArea());
447 const long nH = aRect.GetSize().Height();
448 Size aBorder = LogicToPixel(Size(2, 2),MAP_APPFONT);
449 aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height()));
450 sal_uInt16 nX = 1;
451 sal_uInt16 nY = 0;
453 // Ist der Font des Edits groesser als das Feld?
454 if (m_aAbsolute.GetTextHeight() > nH)
456 Font aApplFont (m_aAbsolute.GetFont());
457 const Size pointAbsoluteSize(m_aAbsolute.PixelToLogic( Size( 0, nH - 2 ), MapMode(MAP_POINT) ));
458 aApplFont.SetSize( pointAbsoluteSize );
459 m_aAbsolute.SetControlFont( aApplFont );
461 aApplFont.SetTransparent( sal_True );
462 m_aRecordText.SetControlFont( aApplFont );
463 m_aRecordOf.SetControlFont( aApplFont );
464 m_aRecordCount.SetControlFont( aApplFont );
467 // Controls Groessen und Positionen setzen
469 XubString aText = m_aRecordText.GetText();
470 long nTextWidth = m_aRecordText.GetTextWidth(aText);
471 m_aRecordText.SetPosPixel(Point(nX,nY));
472 m_aRecordText.SetSizePixel(Size(nTextWidth,nH));
473 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
475 // count an extra hairspace (U+200A) left and right
476 const OUString sevenDigits(m_aAbsolute.CreateFieldText(6000000));
477 const OUString hairSpace(static_cast<sal_Unicode>(0x200A));
478 OUString textPattern(hairSpace);
479 textPattern += sevenDigits;
480 textPattern += hairSpace;
481 nTextWidth = m_aAbsolute.GetTextWidth( textPattern );
482 m_aAbsolute.SetPosPixel(Point(nX,nY));
483 m_aAbsolute.SetSizePixel(Size(nTextWidth, nH));
484 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
486 aText = m_aRecordOf.GetText();
487 nTextWidth = m_aRecordOf.GetTextWidth(aText);
488 m_aRecordOf.SetPosPixel(Point(nX,nY));
489 m_aRecordOf.SetSizePixel(Size(nTextWidth,nH));
490 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
492 textPattern = sevenDigits + " * (" + sevenDigits + ")";
493 nTextWidth = m_aRecordCount.GetTextWidth( textPattern );
494 m_aRecordCount.SetPosPixel(Point(nX,nY));
495 m_aRecordCount.SetSizePixel(Size(nTextWidth,nH));
496 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
498 Point aButtonPos(nX,nY);
499 const Size aButtonSize(nH,nH);
500 SetPosAndSize(m_aFirstBtn, aButtonPos, aButtonSize);
501 SetPosAndSize(m_aPrevBtn, aButtonPos, aButtonSize);
502 SetPosAndSize(m_aNextBtn, aButtonPos, aButtonSize);
503 SetPosAndSize(m_aLastBtn, aButtonPos, aButtonSize);
504 SetPosAndSize(m_aNewBtn, aButtonPos, aButtonSize);
506 nX = sal::static_int_cast< sal_uInt16 >(aButtonPos.X() + 1);
508 return nX;
511 //------------------------------------------------------------------------------
512 IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton )
514 DbGridControl* pParent = (DbGridControl*)GetParent();
516 if (pParent->m_aMasterSlotExecutor.IsSet())
518 long lResult = 0;
519 if (pButton == &m_aFirstBtn)
520 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_FIRST);
521 else if( pButton == &m_aPrevBtn )
522 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_PREV);
523 else if( pButton == &m_aNextBtn )
524 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEXT);
525 else if( pButton == &m_aLastBtn )
526 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_LAST);
527 else if( pButton == &m_aNewBtn )
528 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEW);
530 if (lResult)
531 // the link already handled it
532 return 0;
535 if (pButton == &m_aFirstBtn)
536 pParent->MoveToFirst();
537 else if( pButton == &m_aPrevBtn )
538 pParent->MoveToPrev();
539 else if( pButton == &m_aNextBtn )
540 pParent->MoveToNext();
541 else if( pButton == &m_aLastBtn )
542 pParent->MoveToLast();
543 else if( pButton == &m_aNewBtn )
544 pParent->AppendNew();
545 return 0;
548 //------------------------------------------------------------------------------
549 void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, sal_Bool bAll)
551 if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)
553 DbGridControl* pParent = (DbGridControl*)GetParent();
555 sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControl::OPT_INSERT) ? 2 : 1);
557 // Wann muß alles invalidiert werden
558 bAll = bAll || m_nCurrentPos <= 0;
559 bAll = bAll || nCurrentPos <= 0;
560 bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
561 bAll = bAll || nCurrentPos >= nAdjustedRowCount;
563 if ( bAll )
565 m_nCurrentPos = nCurrentPos;
566 int i = 0;
567 while (ControlMap[i])
568 SetState(ControlMap[i++]);
570 else // befindet sich in der Mitte
572 m_nCurrentPos = nCurrentPos;
573 SetState(NavigationBar::RECORD_COUNT);
574 SetState(NavigationBar::RECORD_ABSOLUTE);
579 //------------------------------------------------------------------------------
580 sal_Bool DbGridControl::NavigationBar::GetState(sal_uInt16 nWhich) const
582 DbGridControl* pParent = (DbGridControl*)GetParent();
584 if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
585 || pParent->IsFilterMode() )
586 return sal_False;
587 else
589 // check if we have a master state provider
590 if (pParent->m_aMasterStateProvider.IsSet())
592 long nState = pParent->m_aMasterStateProvider.Call(reinterpret_cast< void* >( nWhich ) );
593 if (nState>=0)
594 return (nState>0);
597 sal_Bool bAvailable = sal_True;
599 switch (nWhich)
601 case NavigationBar::RECORD_FIRST:
602 case NavigationBar::RECORD_PREV:
603 bAvailable = m_nCurrentPos > 0;
604 break;
605 case NavigationBar::RECORD_NEXT:
606 if(pParent->m_bRecordCountFinal)
608 bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
609 if (!bAvailable && pParent->GetOptions() & DbGridControl::OPT_INSERT)
610 bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
612 break;
613 case NavigationBar::RECORD_LAST:
614 if(pParent->m_bRecordCountFinal)
616 if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
617 bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
618 m_nCurrentPos != pParent->GetRowCount() - 2;
619 else
620 bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
622 break;
623 case NavigationBar::RECORD_NEW:
624 bAvailable = (pParent->GetOptions() & DbGridControl::OPT_INSERT) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
625 break;
626 case NavigationBar::RECORD_ABSOLUTE:
627 bAvailable = pParent->GetRowCount() > 0;
628 break;
630 return bAvailable;
634 //------------------------------------------------------------------------------
635 void DbGridControl::NavigationBar::SetState(sal_uInt16 nWhich)
637 sal_Bool bAvailable = GetState(nWhich);
638 DbGridControl* pParent = (DbGridControl*)GetParent();
639 Window* pWnd = NULL;
640 switch (nWhich)
642 case NavigationBar::RECORD_FIRST:
643 pWnd = &m_aFirstBtn;
644 break;
645 case NavigationBar::RECORD_PREV:
646 pWnd = &m_aPrevBtn;
647 break;
648 case NavigationBar::RECORD_NEXT:
649 pWnd = &m_aNextBtn;
650 break;
651 case NavigationBar::RECORD_LAST:
652 pWnd = &m_aLastBtn;
653 break;
654 case NavigationBar::RECORD_NEW:
655 pWnd = &m_aNewBtn;
656 break;
657 case NavigationBar::RECORD_ABSOLUTE:
658 pWnd = &m_aAbsolute;
659 if (bAvailable)
661 if (pParent->m_nTotalCount >= 0)
663 if (pParent->IsCurrentAppending())
664 m_aAbsolute.SetMax(pParent->m_nTotalCount + 1);
665 else
666 m_aAbsolute.SetMax(pParent->m_nTotalCount);
668 else
669 m_aAbsolute.SetMax(LONG_MAX);
671 m_aAbsolute.SetValue(m_nCurrentPos + 1);
673 else
674 m_aAbsolute.SetText(String());
675 break;
676 case NavigationBar::RECORD_TEXT:
677 pWnd = &m_aRecordText;
678 break;
679 case NavigationBar::RECORD_OF:
680 pWnd = &m_aRecordOf;
681 break;
682 case NavigationBar::RECORD_COUNT:
684 pWnd = &m_aRecordCount;
685 String aText;
686 if (bAvailable)
688 if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
690 if (pParent->IsCurrentAppending() && !pParent->IsModified())
691 aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount());
692 else
693 aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount() - 1);
695 else
696 aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount());
697 if(!pParent->m_bRecordCountFinal)
698 aText += OUString(" *");
700 else
701 aText = String();
703 // add the number of selected rows, if applicable
704 if (pParent->GetSelectRowCount())
706 String aExtendedInfo(aText);
707 aExtendedInfo.AppendAscii(" (");
708 aExtendedInfo += m_aAbsolute.CreateFieldText(pParent->GetSelectRowCount());
709 aExtendedInfo += ')';
710 pWnd->SetText(aExtendedInfo);
712 else
713 pWnd->SetText(aText);
715 pParent->SetRealRowCount(aText);
716 } break;
718 DBG_ASSERT(pWnd, "kein Fenster");
719 if (pWnd && (pWnd->IsEnabled() != bAvailable))
720 // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
721 // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
722 // do this check.
723 // For further explanation see Bug 69900.
724 pWnd->Enable(bAvailable);
727 //------------------------------------------------------------------------------
728 void DbGridControl::NavigationBar::Resize()
730 Control::Resize();
731 ArrangeControls();
734 //------------------------------------------------------------------------------
735 void DbGridControl::NavigationBar::Paint(const Rectangle& rRect)
737 Control::Paint(rRect);
738 Point aAbsolutePos = m_aAbsolute.GetPosPixel();
739 Size aAbsoluteSize = m_aAbsolute.GetSizePixel();
741 DrawLine(Point(aAbsolutePos.X() - 1, 0 ),
742 Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
744 DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ),
745 Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
748 //------------------------------------------------------------------------------
749 void DbGridControl::NavigationBar::StateChanged( StateChangedType nType )
751 Control::StateChanged( nType );
753 Window* pWindows[] = { &m_aRecordText,
754 &m_aAbsolute,
755 &m_aRecordOf,
756 &m_aRecordCount,
757 &m_aFirstBtn,
758 &m_aPrevBtn,
759 &m_aNextBtn,
760 &m_aLastBtn,
761 &m_aNewBtn
764 switch ( nType )
766 case STATE_CHANGE_MIRRORING:
768 sal_Bool bIsRTLEnabled = IsRTLEnabled();
769 for ( size_t i=0; i < (sizeof (pWindows) / sizeof(pWindows[0])); ++i )
770 pWindows[i]->EnableRTL( bIsRTLEnabled );
772 break;
774 case STATE_CHANGE_ZOOM:
776 Fraction aZoom = GetZoom();
778 // not all of these controls need to know the new zoom, but to be sure ...
779 Font aFont( GetSettings().GetStyleSettings().GetFieldFont() );
780 if ( IsControlFont() )
781 aFont.Merge( GetControlFont() );
783 for (size_t i=0; i < sizeof(pWindows)/sizeof(pWindows[0]); ++i)
785 pWindows[i]->SetZoom(aZoom);
786 pWindows[i]->SetZoomedPointFont(aFont);
789 SetZoomedPointFont( aFont );
791 // rearrange the controls
792 m_nDefaultWidth = ArrangeControls();
794 break;
798 //------------------------------------------------------------------------------
799 DbGridRow::DbGridRow(CursorWrapper* pCur, sal_Bool bPaintCursor)
800 :m_bIsNew(sal_False)
803 if (pCur && pCur->Is())
805 Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY);
806 DataColumn* pColumn;
807 for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
809 Reference< XPropertySet > xColSet;
810 ::cppu::extractInterface(xColSet, xColumns->getByIndex(i));
811 pColumn = new DataColumn(xColSet);
812 m_aVariants.push_back( pColumn );
815 if (pCur->rowDeleted())
816 m_eStatus = GRS_DELETED;
817 else
819 if (bPaintCursor)
820 m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GRS_INVALID : GRS_CLEAN;
821 else
823 Reference< XPropertySet > xSet = pCur->getPropertySet();
824 if (xSet.is())
826 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
827 if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
828 m_eStatus = GRS_INVALID;
829 else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
830 m_eStatus = GRS_MODIFIED;
831 else
832 m_eStatus = GRS_CLEAN;
834 else
835 m_eStatus = GRS_INVALID;
838 if (!m_bIsNew && IsValid())
839 m_aBookmark = pCur->getBookmark();
840 else
841 m_aBookmark = Any();
843 else
844 m_eStatus = GRS_INVALID;
847 //------------------------------------------------------------------------------
848 DbGridRow::~DbGridRow()
850 for ( size_t i = 0, n = m_aVariants.size(); i < n; ++i )
851 delete m_aVariants[ i ];
852 m_aVariants.clear();
855 //------------------------------------------------------------------------------
856 void DbGridRow::SetState(CursorWrapper* pCur, sal_Bool bPaintCursor)
858 if (pCur && pCur->Is())
860 if (pCur->rowDeleted())
862 m_eStatus = GRS_DELETED;
863 m_bIsNew = sal_False;
865 else
867 m_eStatus = GRS_CLEAN;
868 if (!bPaintCursor)
870 Reference< XPropertySet > xSet = pCur->getPropertySet();
871 DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
873 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
874 m_eStatus = GRS_MODIFIED;
875 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
877 else
878 m_bIsNew = sal_False;
883 if (!m_bIsNew && IsValid())
884 m_aBookmark = pCur->getBookmark();
885 else
886 m_aBookmark = Any();
888 catch(SQLException&)
890 DBG_UNHANDLED_EXCEPTION();
891 m_aBookmark = Any();
892 m_eStatus = GRS_INVALID;
893 m_bIsNew = sal_False;
896 else
898 m_aBookmark = Any();
899 m_eStatus = GRS_INVALID;
900 m_bIsNew = sal_False;
904 DBG_NAME(DbGridControl);
905 //------------------------------------------------------------------------------
906 DbGridControl::DbGridControl(
907 Reference< XComponentContext > _rxContext,
908 Window* pParent,
909 WinBits nBits)
910 :DbGridControl_Base(pParent, EBBF_NONE, nBits, DEFAULT_BROWSE_MODE )
911 ,m_xContext(_rxContext)
912 ,m_aBar(this)
913 ,m_nAsynAdjustEvent(0)
914 ,m_pDataSourcePropMultiplexer(NULL)
915 ,m_pDataSourcePropListener(NULL)
916 ,m_pFieldListeners(NULL)
917 ,m_pCursorDisposeListener(NULL)
918 ,m_pGridListener(NULL)
919 ,m_pDataCursor(NULL)
920 ,m_pSeekCursor(NULL)
921 ,m_nSeekPos(-1)
922 ,m_nTotalCount(-1)
923 ,m_aNullDate(OTypeConversionClient().getStandardDate())
924 ,m_nMode(DEFAULT_BROWSE_MODE)
925 ,m_nCurrentPos(-1)
926 ,m_nDeleteEvent(0)
927 ,m_nOptions(OPT_READONLY)
928 ,m_nOptionMask(OPT_INSERT | OPT_UPDATE | OPT_DELETE)
929 ,m_nLastColId((sal_uInt16)-1)
930 ,m_nLastRowId(-1)
931 ,m_bDesignMode(sal_False)
932 ,m_bRecordCountFinal(sal_False)
933 ,m_bMultiSelection(sal_True)
934 ,m_bNavigationBar(sal_True)
935 ,m_bSynchDisplay(sal_True)
936 ,m_bForceROController(sal_False)
937 ,m_bHandle(sal_True)
938 ,m_bFilterMode(sal_False)
939 ,m_bWantDestruction(sal_False)
940 ,m_bInAdjustDataSource(sal_False)
941 ,m_bPendingAdjustRows(sal_False)
942 ,m_bHideScrollbars( sal_False )
943 ,m_bUpdating(sal_False)
945 DBG_CTOR(DbGridControl,NULL);
947 String sName(SVX_RES(RID_STR_NAVIGATIONBAR));
948 m_aBar.SetAccessibleName(sName);
949 m_aBar.Show();
950 ImplInitWindow( InitAll );
953 //------------------------------------------------------------------------------
954 void DbGridControl::InsertHandleColumn()
956 // Handle Column einfuegen
957 // Da die BrowseBox ohne handleColums Paintprobleme hat
958 // wird diese versteckt
959 if (HasHandle())
960 BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(String()));
961 else
962 BrowseBox::InsertHandleColumn(0);
965 //------------------------------------------------------------------------------
966 void DbGridControl::Init()
968 BrowserHeader* pNewHeader = CreateHeaderBar(this);
969 pHeader->SetMouseTransparent(sal_False);
971 SetHeaderBar(pNewHeader);
972 SetMode(m_nMode);
973 SetCursorColor(Color(0xFF, 0, 0));
975 InsertHandleColumn();
978 //------------------------------------------------------------------------------
979 DbGridControl::~DbGridControl()
981 RemoveColumns();
984 m_bWantDestruction = sal_True;
985 osl::MutexGuard aGuard(m_aDestructionSafety);
986 if (m_pFieldListeners)
987 DisconnectFromFields();
988 if (m_pCursorDisposeListener)
990 delete m_pCursorDisposeListener;
991 m_pCursorDisposeListener = NULL;
995 if (m_nDeleteEvent)
996 Application::RemoveUserEvent(m_nDeleteEvent);
998 if (m_pDataSourcePropMultiplexer)
1000 m_pDataSourcePropMultiplexer->dispose();
1001 m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
1002 delete m_pDataSourcePropListener;
1003 m_pDataSourcePropMultiplexer = NULL;
1004 m_pDataSourcePropListener = NULL;
1006 m_xRowSetListener.clear();
1008 delete m_pDataCursor;
1009 delete m_pSeekCursor;
1011 DBG_DTOR(DbGridControl,NULL);
1014 //------------------------------------------------------------------------------
1015 void DbGridControl::StateChanged( StateChangedType nType )
1017 DbGridControl_Base::StateChanged( nType );
1019 switch (nType)
1021 case STATE_CHANGE_MIRRORING:
1022 ImplInitWindow( InitWritingMode );
1023 Invalidate();
1024 break;
1026 case STATE_CHANGE_ZOOM:
1028 ImplInitWindow( InitFont );
1030 // and give it a chance to rearrange
1031 Point aPoint = GetControlArea().TopLeft();
1032 sal_uInt16 nX = (sal_uInt16)aPoint.X();
1033 ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1034 ReserveControlArea((sal_uInt16)nX);
1036 break;
1037 case STATE_CHANGE_CONTROLFONT:
1038 ImplInitWindow( InitFont );
1039 Invalidate();
1040 break;
1041 case STATE_CHANGE_CONTROLFOREGROUND:
1042 ImplInitWindow( InitForeground );
1043 Invalidate();
1044 break;
1045 case STATE_CHANGE_CONTROLBACKGROUND:
1046 ImplInitWindow( InitBackground );
1047 Invalidate();
1048 break;
1052 //------------------------------------------------------------------------------
1053 void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
1055 DbGridControl_Base::DataChanged( rDCEvt );
1056 if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS ) &&
1057 (rDCEvt.GetFlags() & SETTINGS_STYLE) )
1059 ImplInitWindow( InitAll );
1060 Invalidate();
1064 //------------------------------------------------------------------------------
1065 void DbGridControl::Select()
1067 DbGridControl_Base::Select();
1069 // as the selected rows may have changed, udate the according display in our navigation bar
1070 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1072 if (m_pGridListener)
1073 m_pGridListener->selectionChanged();
1076 //------------------------------------------------------------------------------
1077 void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
1079 for ( size_t i = 0; i < m_aColumns.size(); ++i )
1081 DbGridColumn* pCol = m_aColumns[ i ];
1082 if (pCol)
1083 pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
1086 if ( ( _eInitWhat & InitWritingMode ) != 0 )
1088 if ( m_bNavigationBar )
1090 m_aBar.EnableRTL( IsRTLEnabled() );
1094 if ( ( _eInitWhat & InitFont ) != 0 )
1096 if ( m_bNavigationBar )
1098 Font aFont = m_aBar.GetSettings().GetStyleSettings().GetFieldFont();
1099 if ( IsControlFont() )
1100 m_aBar.SetControlFont( GetControlFont() );
1101 else
1102 m_aBar.SetControlFont();
1104 m_aBar.SetZoom( GetZoom() );
1108 if ( ( _eInitWhat & InitBackground ) != 0 )
1110 if (IsControlBackground())
1112 GetDataWindow().SetBackground(GetControlBackground());
1113 GetDataWindow().SetControlBackground(GetControlBackground());
1114 GetDataWindow().SetFillColor(GetControlBackground());
1116 else
1118 GetDataWindow().SetControlBackground();
1119 GetDataWindow().SetFillColor(GetFillColor());
1124 //------------------------------------------------------------------------------
1125 void DbGridControl::RemoveRows(sal_Bool bNewCursor)
1127 // Hat sich der DatenCursor verandert ?
1128 if (!bNewCursor)
1130 DELETEZ(m_pSeekCursor);
1131 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1132 m_nCurrentPos = m_nSeekPos = -1;
1133 m_nOptions = OPT_READONLY;
1135 RowRemoved(0, GetRowCount(), sal_False);
1136 m_nTotalCount = -1;
1138 else
1140 RemoveRows();
1144 //------------------------------------------------------------------------------
1145 void DbGridControl::RemoveRows()
1147 // we're going to remove all columns and all row, so deactivate the current cell
1148 if (IsEditing())
1149 DeactivateCell();
1151 // alle Columns deinitialisieren
1152 // existieren Spalten, dann alle Controller freigeben
1153 for (size_t i = 0; i < m_aColumns.size(); i++)
1154 m_aColumns[ i ]->Clear();
1156 DELETEZ(m_pSeekCursor);
1157 DELETEZ(m_pDataCursor);
1159 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1160 m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1;
1161 m_nOptions = OPT_READONLY;
1163 // Anzahl Saetze im Browser auf 0 zuruecksetzen
1164 DbGridControl_Base::RemoveRows();
1165 m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
1168 //------------------------------------------------------------------------------
1169 void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
1171 // Positionierung der Controls
1172 if (m_bNavigationBar)
1174 nX = m_aBar.GetDefaultWidth();
1175 Rectangle aRect(GetControlArea());
1176 m_aBar.SetPosSizePixel(Point(0,nY + 1), Size(nX, aRect.GetSize().Height() - 1));
1180 //------------------------------------------------------------------------------
1181 void DbGridControl::EnableHandle(sal_Bool bEnable)
1183 if (m_bHandle == bEnable)
1184 return;
1186 // HandleColumn wird nur ausgeblendet,
1187 // da es sonst etliche Probleme mit dem Zeichnen gibt
1188 RemoveColumn( HandleColumnId );
1189 m_bHandle = bEnable;
1190 InsertHandleColumn();
1193 //------------------------------------------------------------------------------
1194 namespace
1196 bool adjustModeForScrollbars( BrowserMode& _rMode, sal_Bool _bNavigationBar, sal_Bool _bHideScrollbars )
1198 BrowserMode nOldMode = _rMode;
1200 if ( !_bNavigationBar )
1202 _rMode &= ~BROWSER_AUTO_HSCROLL;
1205 if ( _bHideScrollbars )
1207 _rMode |= ( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1208 _rMode &= ~( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1210 else
1212 _rMode |= ( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1213 _rMode &= ~( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1216 // note: if we have a navigation bar, we always have a AUTO_HSCROLL. In particular,
1217 // _bHideScrollbars is ignored then
1218 if ( _bNavigationBar )
1220 _rMode |= BROWSER_AUTO_HSCROLL;
1221 _rMode &= ~BROWSER_NO_HSCROLL;
1224 return nOldMode != _rMode;
1228 //------------------------------------------------------------------------------
1229 void DbGridControl::EnableNavigationBar(sal_Bool bEnable)
1231 if (m_bNavigationBar == bEnable)
1232 return;
1234 m_bNavigationBar = bEnable;
1236 if (bEnable)
1238 m_aBar.Show();
1239 m_aBar.Enable();
1240 m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
1242 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1243 SetMode( m_nMode );
1245 // liefert die Groe�e der Reserved ControlArea
1246 Point aPoint = GetControlArea().TopLeft();
1247 sal_uInt16 nX = (sal_uInt16)aPoint.X();
1249 ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1250 ReserveControlArea((sal_uInt16)nX);
1252 else
1254 m_aBar.Hide();
1255 m_aBar.Disable();
1257 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1258 SetMode( m_nMode );
1260 ReserveControlArea();
1264 //------------------------------------------------------------------------------
1265 sal_uInt16 DbGridControl::SetOptions(sal_uInt16 nOpt)
1267 DBG_ASSERT(!m_xCurrentRow || !m_xCurrentRow->IsModified(),
1268 "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
1270 // for the next setDataSource (which is triggered by a refresh, for instance)
1271 m_nOptionMask = nOpt;
1273 // normalize the new options
1274 Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
1275 if (xDataSourceSet.is())
1277 // feststellen welche Updatem�glichkeiten bestehen
1278 sal_Int32 nPrivileges = 0;
1279 xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1280 if ((nPrivileges & Privilege::INSERT) == 0)
1281 nOpt &= ~OPT_INSERT;
1282 if ((nPrivileges & Privilege::UPDATE) == 0)
1283 nOpt &= ~OPT_UPDATE;
1284 if ((nPrivileges & Privilege::DELETE) == 0)
1285 nOpt &= ~OPT_DELETE;
1287 else
1288 nOpt = OPT_READONLY;
1290 // need to do something after that ?
1291 if (nOpt == m_nOptions)
1292 return m_nOptions;
1294 // the 'update' option only affects our BrowserMode (with or w/o focus rect)
1295 BrowserMode nNewMode = m_nMode;
1296 if ((m_nMode & BROWSER_CURSOR_WO_FOCUS) == 0)
1298 if (nOpt & OPT_UPDATE)
1299 nNewMode |= BROWSER_HIDECURSOR;
1300 else
1301 nNewMode &= ~BROWSER_HIDECURSOR;
1303 else
1304 nNewMode &= ~BROWSER_HIDECURSOR;
1305 // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
1307 if (nNewMode != m_nMode)
1309 SetMode(nNewMode);
1310 m_nMode = nNewMode;
1313 // _after_ setting the mode because this results in an ActivateCell
1314 DeactivateCell();
1316 sal_Bool bInsertChanged = (nOpt & OPT_INSERT) != (m_nOptions & OPT_INSERT);
1317 m_nOptions = nOpt;
1318 // we need to set this before the code below because it indirectly uses m_nOptions
1320 // the 'insert' option affects our empty row
1321 if (bInsertChanged)
1323 if (m_nOptions & OPT_INSERT)
1324 { // the insert option is to be set
1325 m_xEmptyRow = new DbGridRow();
1326 RowInserted(GetRowCount(), 1, sal_True);
1328 else
1329 { // the insert option is to be reset
1330 m_xEmptyRow = NULL;
1331 if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
1332 GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
1333 RowRemoved(GetRowCount(), 1, sal_True);
1337 // the 'delete' options has no immediate consequences
1339 ActivateCell();
1340 Invalidate();
1341 return m_nOptions;
1344 //------------------------------------------------------------------------------
1345 void DbGridControl::ForceHideScrollbars( sal_Bool _bForce )
1347 if ( m_bHideScrollbars == _bForce )
1348 return;
1350 m_bHideScrollbars = _bForce;
1352 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1353 SetMode( m_nMode );
1356 //------------------------------------------------------------------------------
1357 void DbGridControl::EnablePermanentCursor(sal_Bool bEnable)
1359 if (IsPermanentCursorEnabled() == bEnable)
1360 return;
1362 if (bEnable)
1364 m_nMode &= ~BROWSER_HIDECURSOR; // without this BROWSER_CURSOR_WO_FOCUS won't have any affect
1365 m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1367 else
1369 if (m_nOptions & OPT_UPDATE)
1370 m_nMode |= BROWSER_HIDECURSOR; // no cursor at all
1371 else
1372 m_nMode &= ~BROWSER_HIDECURSOR; // at least the "non-permanent" cursor
1374 m_nMode &= ~BROWSER_CURSOR_WO_FOCUS;
1376 SetMode(m_nMode);
1378 sal_Bool bWasEditing = IsEditing();
1379 DeactivateCell();
1380 if (bWasEditing)
1381 ActivateCell();
1384 //------------------------------------------------------------------------------
1385 sal_Bool DbGridControl::IsPermanentCursorEnabled() const
1387 return ((m_nMode & BROWSER_CURSOR_WO_FOCUS) != 0) && ((m_nMode & BROWSER_HIDECURSOR) == 0);
1390 //------------------------------------------------------------------------------
1391 void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
1393 if ((GetCurColumnId() == _nColId) && IsEditing())
1394 { // the controller which is currently active needs to be refreshed
1395 DeactivateCell();
1396 ActivateCell();
1400 //------------------------------------------------------------------------------
1401 void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, sal_uInt16 nOpts)
1403 if (!_xCursor.is() && !m_pDataCursor)
1404 return;
1406 if (m_pDataSourcePropMultiplexer)
1408 m_pDataSourcePropMultiplexer->dispose();
1409 m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
1410 delete m_pDataSourcePropListener;
1411 m_pDataSourcePropMultiplexer = NULL;
1412 m_pDataSourcePropListener = NULL;
1414 m_xRowSetListener.clear();
1416 // is the new cursor valid ?
1417 // the cursor is only valid if it contains some columns
1418 // if there is no cursor or the cursor is not valid we have to clean up an leave
1419 if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY)->getColumns()->hasElements())
1421 RemoveRows();
1422 return;
1425 // Hat sich der DatenCursor verandert ?
1426 sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
1428 SetUpdateMode(sal_False);
1429 RemoveRows();
1430 DisconnectFromFields();
1432 DELETEZ(m_pCursorDisposeListener);
1435 ::osl::MutexGuard aGuard(m_aAdjustSafety);
1436 if (m_nAsynAdjustEvent)
1438 // the adjust was thought to work with the old cursor which we don't have anymore
1439 RemoveUserEvent(m_nAsynAdjustEvent);
1440 m_nAsynAdjustEvent = 0;
1444 // get a new formatter and data cursor
1445 m_xFormatter = NULL;
1446 OStaticDataAccessTools aStaticTools;
1447 Reference< ::com::sun::star::util::XNumberFormatsSupplier > xSupplier = aStaticTools.getNumberFormats(aStaticTools.getRowSetConnection(_xCursor), sal_True);
1448 if (xSupplier.is())
1450 m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter >(
1451 ::com::sun::star::util::NumberFormatter::create(m_xContext),
1452 UNO_QUERY);
1453 m_xFormatter->attachNumberFormatsSupplier(xSupplier);
1455 // retrieve the datebase of the Numberformatter
1458 xSupplier->getNumberFormatSettings()->getPropertyValue(OUString("NullDate")) >>= m_aNullDate;
1460 catch(Exception&)
1465 m_pDataCursor = new CursorWrapper(_xCursor);
1467 // now create a cursor for painting rows
1468 // we need that cursor only if we are not in insert only mode
1469 Reference< XResultSet > xClone;
1470 Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
1473 xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
1475 catch(Exception&)
1478 if (xClone.is())
1479 m_pSeekCursor = new CursorWrapper(xClone);
1481 // property listening on the data source
1482 // (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
1483 // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
1484 // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
1485 // and forwards the property changes to a our special method "DataSourcePropertyChanged".)
1486 if (m_pDataCursor)
1488 m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
1489 m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
1490 m_pDataSourcePropMultiplexer->acquire();
1491 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
1492 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
1495 BrowserMode nOldMode = m_nMode;
1496 if (m_pSeekCursor)
1500 Reference< XPropertySet > xSet(_xCursor, UNO_QUERY);
1501 if (xSet.is())
1503 // feststellen welche Updatemoeglichkeiten bestehen
1504 sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
1505 xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
1507 if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
1509 sal_Int32 nPrivileges = 0;
1510 xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1512 // Insert Option should be set if insert only otherwise you won't see any rows
1513 // and no insertion is possible
1514 if ((m_nOptionMask & OPT_INSERT) && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & OPT_INSERT))
1515 m_nOptions |= OPT_INSERT;
1516 if ((m_nOptionMask & OPT_UPDATE) && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & OPT_UPDATE))
1517 m_nOptions |= OPT_UPDATE;
1518 if ((m_nOptionMask & OPT_DELETE) && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & OPT_DELETE))
1519 m_nOptions |= OPT_DELETE;
1523 catch( const Exception& )
1525 DBG_UNHANDLED_EXCEPTION();
1528 sal_Bool bPermanentCursor = IsPermanentCursorEnabled();
1529 m_nMode = DEFAULT_BROWSE_MODE;
1531 if ( bPermanentCursor )
1533 m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1534 m_nMode &= ~BROWSER_HIDECURSOR;
1536 else
1538 // Duerfen Updates gemacht werden, kein Focus-RechtEck
1539 if ( m_nOptions & OPT_UPDATE )
1540 m_nMode |= BROWSER_HIDECURSOR;
1543 if ( m_bMultiSelection )
1544 m_nMode |= BROWSER_MULTISELECTION;
1545 else
1546 m_nMode &= ~BROWSER_MULTISELECTION;
1548 adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
1550 Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY);
1551 if (xSupplyColumns.is())
1552 InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
1554 ConnectToFields();
1557 sal_uInt32 nRecordCount(0);
1559 if (m_pSeekCursor)
1561 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1562 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1563 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1565 m_xRowSetListener = new RowSetEventListener(this);
1566 Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
1567 if ( xChangeBroad.is( ) )
1568 xChangeBroad->addRowsChangeListener(m_xRowSetListener);
1571 // insert the currently known rows
1572 // and one row if we are able to insert rows
1573 if (m_nOptions & OPT_INSERT)
1575 // insert the empty row for insertion
1576 m_xEmptyRow = new DbGridRow();
1577 ++nRecordCount;
1579 if (nRecordCount)
1581 m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor, sal_True);
1582 m_xDataRow = new DbGridRow(m_pDataCursor, sal_False);
1583 RowInserted(0, nRecordCount, sal_False);
1585 if (m_xSeekRow->IsValid())
1588 m_nSeekPos = m_pSeekCursor->getRow() - 1;
1590 catch( const Exception& )
1592 DBG_UNHANDLED_EXCEPTION();
1593 m_nSeekPos = -1;
1596 else
1598 // no rows so we don't need a seekcursor
1599 DELETEZ(m_pSeekCursor);
1603 // Zur alten Spalte gehen
1604 if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
1605 nCurPos = 0;
1607 // Column zero is a valid choice and guaranteed to exist,
1608 // but invisible to the user; if we have at least one
1609 // user-visible column, go to that one.
1610 if (nCurPos == 0 && ColCount() > 1)
1611 nCurPos = 1;
1613 // there are rows so go to the selected current column
1614 if (nRecordCount)
1615 GoToRowColumnId(0, GetColumnId(nCurPos));
1616 // else stop the editing if necessary
1617 else if (IsEditing())
1618 DeactivateCell();
1620 // now reset the mode
1621 if (m_nMode != nOldMode)
1622 SetMode(m_nMode);
1624 // beim Resizen wird RecalcRows gerufen
1625 if (!IsResizing() && GetRowCount())
1626 RecalcRows(GetTopRow(), GetVisibleRows(), sal_True);
1628 m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
1629 SetUpdateMode(sal_True);
1631 // start listening on the seek cursor
1632 if (m_pSeekCursor)
1633 m_pCursorDisposeListener = new DisposeListenerGridBridge(*this, Reference< XComponent > ((Reference< XInterface >)*m_pSeekCursor, UNO_QUERY), 0);
1636 //------------------------------------------------------------------------------
1637 void DbGridControl::RemoveColumns()
1639 if ( IsEditing() )
1640 DeactivateCell();
1642 for (size_t i = 0, n = m_aColumns.size(); i < n; i++)
1643 delete m_aColumns[ i ];
1644 m_aColumns.clear();
1646 DbGridControl_Base::RemoveColumns();
1649 //------------------------------------------------------------------------------
1650 DbGridColumn* DbGridControl::CreateColumn(sal_uInt16 nId) const
1652 return new DbGridColumn(nId, *(DbGridControl*)this);
1655 //------------------------------------------------------------------------------
1656 sal_uInt16 DbGridControl::AppendColumn(const XubString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
1658 DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
1659 sal_uInt16 nRealPos = nModelPos;
1660 if (nModelPos != HEADERBAR_APPEND)
1662 // calc the view pos. we can't use our converting functions because the new column
1663 // has no VCL-representation, yet.
1664 sal_Int16 nViewPos = nModelPos;
1665 while (nModelPos--)
1667 if ( m_aColumns[ nModelPos ]->IsHidden() )
1668 --nViewPos;
1670 // restore nModelPos, we need it later
1671 nModelPos = nRealPos;
1672 // the position the base class gets is the view pos + 1 (because of the handle column)
1673 nRealPos = nViewPos + 1;
1676 // calculate the new id
1677 for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && (nId <= m_aColumns.size()); ++nId)
1679 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !");
1680 // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
1682 DbGridControl_Base::AppendColumn(rName, nWidth, nRealPos, nId);
1683 if (nModelPos == HEADERBAR_APPEND)
1684 m_aColumns.push_back( CreateColumn(nId) );
1685 else
1687 DbGridColumns::iterator it = m_aColumns.begin();
1688 ::std::advance( it, nModelPos );
1689 m_aColumns.insert( it, CreateColumn(nId) );
1692 return nId;
1695 //------------------------------------------------------------------------------
1696 void DbGridControl::RemoveColumn(sal_uInt16 nId)
1698 DbGridControl_Base::RemoveColumn(nId);
1700 const sal_uInt16 nIndex = GetModelColumnPos(nId);
1701 if(nIndex != GRID_COLUMN_NOT_FOUND)
1703 delete m_aColumns[nIndex];
1704 m_aColumns.erase( m_aColumns.begin()+nIndex );
1708 //------------------------------------------------------------------------------
1709 void DbGridControl::ColumnMoved(sal_uInt16 nId)
1711 DbGridControl_Base::ColumnMoved(nId);
1713 // remove the col from the model
1714 sal_uInt16 nOldModelPos = GetModelColumnPos(nId);
1715 #ifdef DBG_UTIL
1716 DbGridColumn* pCol = m_aColumns[ (sal_uInt32)nOldModelPos ];
1717 DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
1718 #endif
1720 // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
1721 // so the method won't work (in fact it would return the old model pos)
1723 // the new view pos is calculated easily
1724 sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
1726 // from that we can compute the new model pos
1727 sal_uInt16 nNewModelPos;
1728 for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
1730 if (!m_aColumns[ nNewModelPos ]->IsHidden())
1732 if (!nNewViewPos)
1733 break;
1734 else
1735 --nNewViewPos;
1738 DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
1740 // this will work. of course the model isn't fully consistent with our view right now, but let's
1741 // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
1742 // other case we can use analogue arguments).
1743 // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
1744 // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
1745 // within this range is constant, so we may calculate the view pos from the model pos in the above way.
1747 // for instance, let's look at a grid with six columns where the third one is hidden. this will
1748 // initially look like this :
1750 // +---+---+---+---+---+---+
1751 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1752 // +---+---+---+---+---+---+
1753 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1754 // +---+---+---+---+---+---+
1755 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1756 // +---+---+---+---+---+---+
1758 // if we move the column at (view) pos 1 to (view) pos 3 we have :
1760 // +---+---+---+---+---+---+
1761 // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet
1762 // +---+---+---+---+---+---+
1763 // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes
1764 // +---+---+---+---+---+---+
1765 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1766 // +---+---+---+---+---+---+
1768 // or, sorted by the out-of-date model positions :
1770 // +---+---+---+---+---+---+
1771 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1772 // +---+---+---+---+---+---+
1773 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1774 // +---+---+---+---+---+---+
1775 // view pos | 0 | 3 | - | 1 | 2 | 4 |
1776 // +---+---+---+---+---+---+
1778 // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
1779 // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
1780 // exactly the pos where we have to re-insert our column's model, so it looks ike this :
1782 // +---+---+---+---+---+---+
1783 // model pos | 0 |*1*| 2 | 3 | 4 | 5 |
1784 // +---+---+---+---+---+---+
1785 // ID | 1 | 3 | 4 | 5 | 2 | 6 |
1786 // +---+---+---+---+---+---+
1787 // view pos | 0 | - | 1 | 2 | 3 | 4 |
1788 // +---+---+---+---+---+---+
1790 // Now, all is consistent again.
1791 // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
1792 // the user expected the latter but there really is no good argument against our method ;) ...)
1794 // And no, this large explanation isn't just because I wanted to play a board game or something like
1795 // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
1796 // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
1799 DbGridColumn* temp = m_aColumns[ nOldModelPos ];
1801 DbGridColumns::iterator it = m_aColumns.begin();
1802 ::std::advance( it, nOldModelPos );
1803 m_aColumns.erase( it );
1805 it = m_aColumns.begin();
1806 ::std::advance( it, nNewModelPos );
1807 m_aColumns.insert( it, temp );
1810 //------------------------------------------------------------------------------
1811 sal_Bool DbGridControl::SeekRow(long nRow)
1813 // in filter mode or in insert only mode we don't have any cursor!
1814 if ( !SeekCursor( nRow ) )
1815 return sal_False;
1817 if ( IsFilterMode() )
1819 DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
1820 m_xPaintRow = m_xEmptyRow;
1822 else
1824 // on the current position we have to take the current row for display as we want
1825 // to have the most recent values for display
1826 if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
1827 m_xPaintRow = m_xCurrentRow;
1828 // seek to the empty insert row
1829 else if ( IsInsertionRow( nRow ) )
1830 m_xPaintRow = m_xEmptyRow;
1831 else
1833 m_xSeekRow->SetState( m_pSeekCursor, sal_True );
1834 m_xPaintRow = m_xSeekRow;
1838 DbGridControl_Base::SeekRow(nRow);
1840 return m_nSeekPos >= 0;
1842 //------------------------------------------------------------------------------
1843 // Wird aufgerufen, wenn die dargestellte Datenmenge sich aendert
1844 //------------------------------------------------------------------------------
1845 void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen )
1847 RecalcRows(nNewTopRow, nLinesOnScreen , sal_False);
1850 //------------------------------------------------------------------------------
1851 void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, sal_Bool bUpdateCursor)
1853 DBG_CHKTHIS( DbGridControl, NULL );
1854 // Wenn kein Cursor -> keine Rows im Browser.
1855 if (!m_pSeekCursor)
1857 DBG_ASSERT(GetRowCount() == 0,"DbGridControl: ohne Cursor darf es keine Rows geben");
1858 return;
1861 // ignore any updates implicit made
1862 sal_Bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
1863 if (bDisablePaint)
1864 EnablePaint(sal_False);
1866 // Cache an den sichtbaren Bereich anpassen
1867 Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
1868 sal_Int32 nCacheSize = 0;
1869 xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
1870 sal_Bool bCacheAligned = sal_False;
1871 // Nach der Initialisierung (m_nSeekPos < 0) keine Cursorbewegung, da bereits auf den ersten
1872 // Satz positioniert
1873 long nDelta = nNewTopRow - GetTopRow();
1874 // Limit fuer relative Positionierung
1875 long nLimit = (nCacheSize) ? nCacheSize / 2 : 0;
1877 // mehr Zeilen auf dem Bildschirm als im Cache
1878 if (nLimit < nLinesOnScreen)
1880 Any aCacheSize;
1881 aCacheSize <<= sal_Int32(nLinesOnScreen*2);
1882 xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
1883 // jetzt auf alle Faelle den Cursor anpassen
1884 bUpdateCursor = sal_True;
1885 bCacheAligned = sal_True;
1886 nLimit = nLinesOnScreen;
1889 // Im folgenden werden die Positionierungen so vorgenommen, da� sichergestellt ist
1890 // da� ausreichend Zeilen im DatenCache vorhanden sind
1892 // Fenster geht nach unten, weniger als zwei Fenster Differenz
1893 // oder Cache angepasst und noch kein Rowcount
1894 if (nDelta < nLimit && (nDelta > 0
1895 || (bCacheAligned && m_nTotalCount < 0)) )
1896 SeekCursor(nNewTopRow + nLinesOnScreen - 1, sal_False);
1897 else if (nDelta < 0 && std::abs(nDelta) < nLimit)
1898 SeekCursor(nNewTopRow, sal_False);
1899 else if (nDelta != 0 || bUpdateCursor)
1900 SeekCursor(nNewTopRow, sal_True);
1902 AdjustRows();
1904 // ignore any updates implicit made
1905 EnablePaint(sal_True);
1908 //------------------------------------------------------------------------------
1909 void DbGridControl::RowInserted(long nRow, long nNumRows, sal_Bool bDoPaint, sal_Bool bKeepSelection)
1911 if (nNumRows)
1913 if (m_bRecordCountFinal && m_nTotalCount < 0)
1915 // if we have an insert row we have to reduce to count by 1
1916 // as the total count reflects only the existing rows in database
1917 m_nTotalCount = GetRowCount() + nNumRows;
1918 if (m_xEmptyRow.Is())
1919 --m_nTotalCount;
1921 else if (m_nTotalCount >= 0)
1922 m_nTotalCount += nNumRows;
1924 DbGridControl_Base::RowInserted(nRow, nNumRows, bDoPaint, bKeepSelection);
1925 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1929 //------------------------------------------------------------------------------
1930 void DbGridControl::RowRemoved(long nRow, long nNumRows, sal_Bool bDoPaint)
1932 if (nNumRows)
1934 if (m_bRecordCountFinal && m_nTotalCount < 0)
1936 m_nTotalCount = GetRowCount() - nNumRows;
1937 // if we have an insert row reduce by 1
1938 if (m_xEmptyRow.Is())
1939 --m_nTotalCount;
1941 else if (m_nTotalCount >= 0)
1942 m_nTotalCount -= nNumRows;
1944 DbGridControl_Base::RowRemoved(nRow, nNumRows, bDoPaint);
1945 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1949 //------------------------------------------------------------------------------
1950 void DbGridControl::AdjustRows()
1952 if (!m_pSeekCursor)
1953 return;
1955 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1957 // Aktualisieren des RecordCounts
1958 sal_Int32 nRecordCount = 0;
1959 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1960 if (!m_bRecordCountFinal)
1961 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1963 // hat sich die aktuelle Anzahl Rows veraendert
1964 // hierbei muss auch beruecksichtigt werden,
1965 // das eine zusaetzliche Zeile zum einfuegen von Datensaetzen vorhanden sein kann
1967 // zusaetzliche AppendRow fuers einfuegen
1968 if (m_nOptions & OPT_INSERT)
1969 ++nRecordCount;
1971 // wird gerade eingefuegt, dann gehoert die gerade hinzuzufuegende
1972 // Zeile nicht zum RecordCount und die Appendrow ebenfalls nicht
1973 if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
1974 m_xCurrentRow->IsNew())
1975 ++nRecordCount;
1976 // das ist mit !m_bUpdating abgesichert : innerhalb von SaveRow (m_bUpdating == sal_True) wuerde sonst der Datensatz, den ich editiere
1977 // (und den SaveRow gerade angefuegt hat, wodurch diese Methode hier getriggert wurde), doppelt zaehlen : einmal ist er schon
1978 // in dem normalen RecordCount drin, zum zweiten wuerde er hier gezaehlt werden (60787 - FS)
1980 if (nRecordCount != GetRowCount())
1982 long nDelta = GetRowCount() - (long)nRecordCount;
1983 if (nDelta > 0) // zuviele
1985 RowRemoved(GetRowCount() - nDelta, nDelta, sal_False);
1986 // es sind Zeilen weggefallen, dann ab der aktuellen Position neu zeichen
1987 Invalidate();
1989 sal_Int32 nNewPos = AlignSeekCursor();
1990 if (m_bSynchDisplay)
1991 DbGridControl_Base::GoToRow(nNewPos);
1993 SetCurrent(nNewPos);
1994 // there are rows so go to the selected current column
1995 if (nRecordCount)
1996 GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
1997 if (!IsResizing() && GetRowCount())
1998 RecalcRows(GetTopRow(), GetVisibleRows(), sal_True);
1999 m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
2001 else // zuwenig
2002 RowInserted(GetRowCount(), -nDelta, sal_True);
2005 if (m_bRecordCountFinal && m_nTotalCount < 0)
2007 if (m_nOptions & OPT_INSERT)
2008 m_nTotalCount = GetRowCount() - 1;
2009 else
2010 m_nTotalCount = GetRowCount();
2012 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
2015 //------------------------------------------------------------------------------
2016 DbGridControl_Base::RowStatus DbGridControl::GetRowStatus(long nRow) const
2018 if (IsFilterRow(nRow))
2019 return DbGridControl_Base::FILTER;
2020 else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
2022 // neue Zeile
2023 if (!IsValid(m_xCurrentRow))
2024 return DbGridControl_Base::DELETED;
2025 else if (IsModified())
2026 return DbGridControl_Base::MODIFIED;
2027 else if (m_xCurrentRow->IsNew())
2028 return DbGridControl_Base::CURRENTNEW;
2029 else
2030 return DbGridControl_Base::CURRENT;
2032 else if (IsInsertionRow(nRow))
2033 return DbGridControl_Base::NEW;
2034 else if (!IsValid(m_xSeekRow))
2035 return DbGridControl_Base::DELETED;
2036 else
2037 return DbGridControl_Base::CLEAN;
2040 //------------------------------------------------------------------------------
2041 void DbGridControl::PaintStatusCell(OutputDevice& rDev, const Rectangle& rRect) const
2043 DbGridControl_Base::PaintStatusCell(rDev, rRect);
2046 //------------------------------------------------------------------------------
2047 void DbGridControl::PaintCell(OutputDevice& rDev, const Rectangle& rRect, sal_uInt16 nColumnId) const
2049 if (!IsValid(m_xPaintRow))
2050 return;
2052 size_t Location = GetModelColumnPos(nColumnId);
2053 DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2054 if (pColumn)
2056 Rectangle aArea(rRect);
2057 if ((GetMode() & BROWSER_CURSOR_WO_FOCUS) == BROWSER_CURSOR_WO_FOCUS)
2059 aArea.Top() += 1;
2060 aArea.Bottom() -= 1;
2062 pColumn->Paint(rDev, aArea, m_xPaintRow, getNumberFormatter());
2066 //------------------------------------------------------------------------------
2067 sal_Bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol)
2069 DBG_CHKTHIS( DbGridControl, NULL );
2071 DeactivateCell( sal_False );
2073 if ( m_pDataCursor
2074 && ( m_nCurrentPos != nNewRow )
2075 && !SetCurrent( nNewRow )
2078 ActivateCell();
2079 return sal_False;
2082 if ( !DbGridControl_Base::CursorMoving( nNewRow, nNewCol ) )
2083 return sal_False;
2085 return sal_True;
2088 //------------------------------------------------------------------------------
2089 sal_Bool DbGridControl::SetCurrent(long nNewRow)
2091 // Each movement of the datacursor must start with BeginCursorAction and end with
2092 // EndCursorAction to block all notifications during the movement
2093 BeginCursorAction();
2097 // Abgleichen der Positionen
2098 if (SeekCursor(nNewRow))
2100 if (IsFilterRow(nNewRow)) // special mode for filtering
2102 m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
2103 m_nCurrentPos = nNewRow;
2105 else
2107 sal_Bool bNewRowInserted = sal_False;
2108 // Should we go to the insertrow ?
2109 if (IsInsertionRow(nNewRow))
2111 // to we need to move the cursor to the insert row?
2112 // we need to insert the if the current row isn't the insert row or if the
2113 // cursor triggered the move by itselt and we need a reinitialization of the row
2114 Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
2115 if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
2117 Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
2118 xUpdateCursor->moveToInsertRow();
2120 bNewRowInserted = sal_True;
2122 else
2125 if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
2127 Any aBookmark = m_pSeekCursor->getBookmark();
2128 if (!m_xCurrentRow || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
2130 // adjust the cursor to the new desired row
2131 if (!m_pDataCursor->moveToBookmark(aBookmark))
2133 EndCursorAction();
2134 return sal_False;
2139 m_xDataRow->SetState(m_pDataCursor, sal_False);
2140 m_xCurrentRow = m_xDataRow;
2142 long nPaintPos = -1;
2143 // do we have to repaint the last regular row in case of setting defaults or autovalues
2144 if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
2145 nPaintPos = m_nCurrentPos;
2147 m_nCurrentPos = nNewRow;
2149 // repaint the new row to display all defaults
2150 if (bNewRowInserted)
2151 RowModified(m_nCurrentPos);
2152 if (nPaintPos >= 0)
2153 RowModified(nPaintPos);
2156 else
2158 OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
2159 EndCursorAction();
2160 return sal_False;
2163 catch ( const Exception& )
2165 DBG_UNHANDLED_EXCEPTION();
2166 EndCursorAction();
2167 return sal_False;
2170 EndCursorAction();
2171 return sal_True;
2174 //------------------------------------------------------------------------------
2175 void DbGridControl::CursorMoved()
2177 DBG_CHKTHIS( DbGridControl, NULL );
2179 // CursorBewegung durch loeschen oder einfuegen von Zeilen
2180 if (m_pDataCursor && m_nCurrentPos != GetCurRow())
2182 DeactivateCell(sal_True);
2183 SetCurrent(GetCurRow());
2186 DbGridControl_Base::CursorMoved();
2187 m_aBar.InvalidateAll(m_nCurrentPos);
2189 // select the new column when they moved
2190 if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
2192 SelectColumnId( GetCurColumnId() );
2195 if ( m_nLastColId != GetCurColumnId() )
2196 onColumnChange();
2197 m_nLastColId = GetCurColumnId();
2199 if ( m_nLastRowId != GetCurRow() )
2200 onRowChange();
2201 m_nLastRowId = GetCurRow();
2204 //------------------------------------------------------------------------------
2205 void DbGridControl::onRowChange()
2207 // not interested in
2210 //------------------------------------------------------------------------------
2211 void DbGridControl::onColumnChange()
2213 if ( m_pGridListener )
2214 m_pGridListener->columnChanged();
2217 //------------------------------------------------------------------------------
2218 void DbGridControl::setDisplaySynchron(sal_Bool bSync)
2220 if (bSync != m_bSynchDisplay)
2222 m_bSynchDisplay = bSync;
2223 if (m_bSynchDisplay)
2224 AdjustDataSource(sal_False);
2228 //------------------------------------------------------------------------------
2229 void DbGridControl::AdjustDataSource(sal_Bool bFull)
2231 SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
2232 SolarMutexGuard aGuard;
2233 // wird die aktuelle Zeile gerade neu bestimmt,
2234 // wird kein abgleich vorgenommen
2236 if (bFull)
2237 m_xCurrentRow = NULL;
2238 // if we are on the same row only repaint
2239 // but this is only possible for rows which are not inserted, in that case the comparision result
2240 // may not be correct
2241 else
2242 if ( m_xCurrentRow.Is()
2243 && !m_xCurrentRow->IsNew()
2244 && !m_pDataCursor->isBeforeFirst()
2245 && !m_pDataCursor->isAfterLast()
2246 && !m_pDataCursor->rowDeleted()
2249 sal_Bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
2251 sal_Bool bDataCursorIsOnNew = sal_False;
2252 m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
2254 if ( bEqualBookmarks && !bDataCursorIsOnNew )
2256 // position of my data cursor is the same as the position our current row points tpo
2257 // sync the status, repaint, done
2258 DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Fehler in den Datenzeilen");
2259 SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
2260 RowModified(m_nCurrentPos);
2261 return;
2265 // weg von der Row des DatenCursors
2266 if (m_xPaintRow == m_xCurrentRow)
2267 m_xPaintRow = m_xSeekRow;
2269 // keine aktuelle Zeile dann komplett anpassen
2270 if (!m_xCurrentRow)
2271 AdjustRows();
2273 sal_Int32 nNewPos = AlignSeekCursor();
2274 if (nNewPos < 0) // keine Position gefunden
2275 return;
2277 m_bInAdjustDataSource = sal_True;
2278 if (nNewPos != m_nCurrentPos)
2280 if (m_bSynchDisplay)
2281 DbGridControl_Base::GoToRow(nNewPos);
2283 if (!m_xCurrentRow.Is())
2284 // das tritt zum Beispiel auf, wenn man die n (n>1) letzten Datensaetze geloescht hat, waehrend der Cursor auf dem letzten
2285 // steht : AdjustRows entfernt dann zwei Zeilen aus der BrowseBox, wodurch diese ihre CurrentRow um zwei nach unten
2286 // korrigiert, so dass dann das GoToRow in's Leere laeuft (da wir uns ja angeblich schon an der richtigen Position
2287 // befinden)
2288 SetCurrent(nNewPos);
2290 else
2292 SetCurrent(nNewPos);
2293 RowModified(nNewPos);
2295 m_bInAdjustDataSource = sal_False;
2297 // Wird der DatenCursor von aussen bewegt, wird die selektion aufgehoben
2298 SetNoSelection();
2299 m_aBar.InvalidateAll(m_nCurrentPos, m_xCurrentRow.Is());
2302 //------------------------------------------------------------------------------
2303 sal_Int32 DbGridControl::AlignSeekCursor()
2305 DBG_CHKTHIS( DbGridControl, NULL );
2306 // Positioniert den SeekCursor auf den DatenCursor, Daten werden nicht uebertragen
2308 if (!m_pSeekCursor)
2309 return -1;
2311 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
2313 // jetzt den seekcursor an den DatenCursor angleichen
2314 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
2315 m_nSeekPos = GetRowCount() - 1;
2316 else
2320 if ( m_pDataCursor->isBeforeFirst() )
2322 // this is somewhat strange, but can nevertheless happen
2323 DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
2324 m_pSeekCursor->first();
2325 m_pSeekCursor->previous();
2326 m_nSeekPos = -1;
2328 else if ( m_pDataCursor->isAfterLast() )
2330 DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
2331 m_pSeekCursor->last();
2332 m_pSeekCursor->next();
2333 m_nSeekPos = -1;
2335 else
2337 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2338 if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
2339 // dummerweise kann das moveToBookmark indirekt dazu fuehren, dass der Seek-Cursor wieder neu positoniert wird (wenn
2340 // naemlich das mit all seinen zu feuernden Events relativ komplexe moveToBookmark irgendwo ein Update ausloest),
2341 // also muss ich es nochmal versuchen
2342 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2343 // Nicht dass das jetzt nicht auch schief gegangen sein koennte, aber es ist zumindest unwahrscheinlicher geworden.
2344 // Und die Alternative waere eine Schleife so lange bis es stimmt, und das kann auch nicht die Loesung sein
2345 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2348 catch(Exception&)
2352 return m_nSeekPos;
2354 //------------------------------------------------------------------------------
2355 sal_Bool DbGridControl::SeekCursor(long nRow, sal_Bool bAbsolute)
2357 DBG_CHKTHIS( DbGridControl, NULL );
2358 // Positioniert den SeekCursor, Daten werden nicht uebertragen
2360 // additions for the filtermode
2361 if (IsFilterRow(nRow))
2363 m_nSeekPos = 0;
2364 return sal_True;
2367 if (!m_pSeekCursor)
2368 return sal_False;
2370 // Befinden wir uns gerade beim Einfuegen
2371 if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
2372 nRow >= m_nCurrentPos)
2374 // dann darf auf alle Faelle nicht weiter nach unten gescrollt werden
2375 // da der letzte Datensatz bereits erreicht wurde!
2376 if (nRow == m_nCurrentPos)
2378 // auf die aktuelle Zeile bewegt, dann muß kein abgleich gemacht werden, wenn
2379 // gerade ein Datensatz eingefuegt wird
2380 m_nSeekPos = nRow;
2382 else if (IsInsertionRow(nRow)) // Leerzeile zum Einfuegen von Datensaetzen
2383 m_nSeekPos = nRow;
2385 else if (IsInsertionRow(nRow)) // Leerzeile zum Einfuegen von Datensaetzen
2386 m_nSeekPos = nRow;
2387 else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & OPT_INSERT) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
2388 m_nSeekPos = nRow;
2389 else
2392 sal_Bool bSuccess=sal_False;
2393 long nSteps = 0;
2396 if ( m_pSeekCursor->rowDeleted() )
2398 // somebody deleted the current row of the seek cursor. Move it away from this row.
2399 m_pSeekCursor->next();
2400 if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
2401 bAbsolute = sal_True;
2404 if ( !bAbsolute )
2406 DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
2407 "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
2408 nSteps = nRow - (m_pSeekCursor->getRow() - 1);
2409 bAbsolute = bAbsolute || (abs(nSteps) > 100);
2412 if ( bAbsolute )
2414 bSuccess = m_pSeekCursor->absolute(nRow + 1);
2415 if (bSuccess)
2416 m_nSeekPos = nRow;
2418 else
2420 if (nSteps > 0) // auf den letzten benoetigten Datensatz positionieren
2422 if (m_pSeekCursor->isAfterLast())
2423 bSuccess = sal_False;
2424 else if (m_pSeekCursor->isBeforeFirst())
2425 bSuccess = m_pSeekCursor->absolute(nSteps);
2426 else
2427 bSuccess = m_pSeekCursor->relative(nSteps);
2429 else if (nSteps < 0)
2431 if (m_pSeekCursor->isBeforeFirst())
2432 bSuccess = sal_False;
2433 else if (m_pSeekCursor->isAfterLast())
2434 bSuccess = m_pSeekCursor->absolute(nSteps);
2435 else
2436 bSuccess = m_pSeekCursor->relative(nSteps);
2438 else
2440 m_nSeekPos = nRow;
2441 return sal_True;
2445 catch(Exception&)
2447 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2452 if (!bSuccess)
2454 if (bAbsolute || nSteps > 0)
2456 if (m_pSeekCursor->isLast())
2457 bSuccess=sal_True;
2458 else
2459 bSuccess = m_pSeekCursor->last();
2461 else
2463 if (m_pSeekCursor->isFirst())
2464 bSuccess = sal_True;
2465 else
2466 bSuccess = m_pSeekCursor->first();
2470 if (bSuccess)
2471 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2472 else
2473 m_nSeekPos = -1;
2475 catch(Exception&)
2477 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2478 DBG_UNHANDLED_EXCEPTION();
2479 m_nSeekPos = -1; // kein Datensatz mehr vorhanden
2482 return m_nSeekPos == nRow;
2484 //------------------------------------------------------------------------------
2485 void DbGridControl::MoveToFirst()
2487 if (m_pSeekCursor && (GetCurRow() != 0))
2488 MoveToPosition(0);
2491 //------------------------------------------------------------------------------
2492 void DbGridControl::MoveToLast()
2494 if (!m_pSeekCursor)
2495 return;
2497 if (m_nTotalCount < 0) // RecordCount steht noch nicht fest
2501 sal_Bool bRes = m_pSeekCursor->last();
2503 if (bRes)
2505 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2506 AdjustRows();
2509 catch(Exception&)
2514 // auf den letzen Datensatz positionieren, nicht auf die Leerzeile
2515 if (m_nOptions & OPT_INSERT)
2517 if ((GetRowCount() - 1) > 0)
2518 MoveToPosition(GetRowCount() - 2);
2520 else if (GetRowCount())
2521 MoveToPosition(GetRowCount() - 1);
2524 //------------------------------------------------------------------------------
2525 void DbGridControl::MoveToPrev()
2527 long nNewRow = std::max(GetCurRow() - 1L, 0L);
2528 if (GetCurRow() != nNewRow)
2529 MoveToPosition(nNewRow);
2532 //------------------------------------------------------------------------------
2533 void DbGridControl::MoveToNext()
2535 if (!m_pSeekCursor)
2536 return;
2538 if (m_nTotalCount > 0)
2540 // move the data cursor to the right position
2541 long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
2542 if (GetCurRow() != nNewRow)
2543 MoveToPosition(nNewRow);
2545 else
2547 sal_Bool bOk = sal_False;
2550 // try to move to next row
2551 // when not possible our paint cursor is already on the last row
2552 // then we must be sure that the data cursor is on the position
2553 // we call ourself again
2554 bOk = m_pSeekCursor->next();
2555 if (bOk)
2557 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2558 MoveToPosition(GetCurRow() + 1);
2561 catch(SQLException &)
2563 DBG_UNHANDLED_EXCEPTION();
2566 if(!bOk)
2568 AdjustRows();
2569 if (m_nTotalCount > 0) // only to avoid infinte recursion
2570 MoveToNext();
2575 //------------------------------------------------------------------------------
2576 void DbGridControl::MoveToPosition(sal_uInt32 nPos)
2578 if (!m_pSeekCursor)
2579 return;
2581 if (m_nTotalCount < 0 && (long)nPos >= GetRowCount())
2585 if (!m_pSeekCursor->absolute(nPos + 1))
2587 AdjustRows();
2588 return;
2590 else
2592 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2593 AdjustRows();
2596 catch(Exception&)
2598 return;
2601 DbGridControl_Base::GoToRow(nPos);
2602 m_aBar.InvalidateAll(m_nCurrentPos);
2605 //------------------------------------------------------------------------------
2606 void DbGridControl::AppendNew()
2608 if (!m_pSeekCursor || !(m_nOptions & OPT_INSERT))
2609 return;
2611 if (m_nTotalCount < 0) // RecordCount steht noch nicht fest
2615 sal_Bool bRes = m_pSeekCursor->last();
2617 if (bRes)
2619 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2620 AdjustRows();
2623 catch(Exception&)
2625 return;
2629 long nNewRow = m_nTotalCount + 1;
2630 if (nNewRow > 0 && GetCurRow() != nNewRow)
2631 MoveToPosition(nNewRow - 1);
2634 //------------------------------------------------------------------------------
2635 void DbGridControl::SetDesignMode(sal_Bool bMode)
2637 if (IsDesignMode() != bMode)
2639 // Enable/Disable f�r den Designmode anpassen damit die Headerbar konfigurierbar bleibt
2640 if (bMode)
2642 if (!IsEnabled())
2644 Enable();
2645 GetDataWindow().Disable();
2648 else
2650 // komplett disablen
2651 if (!GetDataWindow().IsEnabled())
2652 Disable();
2655 m_bDesignMode = bMode;
2656 GetDataWindow().SetMouseTransparent(bMode);
2657 SetMouseTransparent(bMode);
2659 m_aBar.InvalidateAll(m_nCurrentPos, sal_True);
2663 //------------------------------------------------------------------------------
2664 void DbGridControl::SetFilterMode(sal_Bool bMode)
2666 if (IsFilterMode() != bMode)
2668 m_bFilterMode = bMode;
2670 if (bMode)
2672 SetUpdateMode(sal_False);
2674 // es gibt kein Cursor mehr
2675 if (IsEditing())
2676 DeactivateCell();
2677 RemoveRows(sal_False);
2679 m_xEmptyRow = new DbGridRow();
2681 // setting the new filter controls
2682 for ( size_t i = 0; i < m_aColumns.size(); ++i )
2684 DbGridColumn* pCurCol = m_aColumns[ i ];
2685 if (!pCurCol->IsHidden())
2686 pCurCol->UpdateControl();
2689 // one row for filtering
2690 RowInserted(0, 1, sal_True);
2691 SetUpdateMode(sal_True);
2693 else
2694 setDataSource(Reference< XRowSet > ());
2697 // -----------------------------------------------------------------------------
2698 String DbGridControl::GetCellText(long _nRow, sal_uInt16 _nColId) const
2700 size_t Location = GetModelColumnPos( _nColId );
2701 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2702 String sRet;
2703 if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
2704 sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
2705 return sRet;
2707 //------------------------------------------------------------------------------
2708 XubString DbGridControl::GetCurrentRowCellText(DbGridColumn* pColumn,const DbGridRowRef& _rRow) const
2710 // Ausgabe des Textes fuer eine Zelle
2711 XubString aText;
2712 if ( pColumn && IsValid(_rRow) )
2713 aText = pColumn->GetCellText(_rRow, m_xFormatter);
2714 return aText;
2717 //------------------------------------------------------------------------------
2718 sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId)
2720 if (SeekRow(nRow))
2722 size_t Location = GetModelColumnPos( nColId );
2723 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2724 return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
2726 else
2727 return 30; //xxxx
2730 //------------------------------------------------------------------------------
2731 void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu)
2733 sal_Bool bDelete = (m_nOptions & OPT_DELETE) && GetSelectRowCount() && !IsCurrentAppending();
2734 // ist nur die Leerzeile selektiert, dann nicht loeschen
2735 bDelete = bDelete && !((m_nOptions & OPT_INSERT) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
2737 rMenu.EnableItem(SID_FM_DELETEROWS, bDelete);
2738 rMenu.EnableItem(SID_FM_RECORD_SAVE, IsModified());
2740 // the undo is more difficult
2741 sal_Bool bCanUndo = IsModified();
2742 long nState = -1;
2743 if (m_aMasterStateProvider.IsSet())
2744 nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
2745 bCanUndo &= ( 0 != nState );
2747 rMenu.EnableItem(SID_FM_RECORD_UNDO, bCanUndo);
2750 //------------------------------------------------------------------------------
2751 void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& /*rMenu*/, sal_uInt16 nExecutionResult)
2753 switch (nExecutionResult)
2755 case SID_FM_DELETEROWS:
2756 // delete asynchron
2757 if (m_nDeleteEvent)
2758 Application::RemoveUserEvent(m_nDeleteEvent);
2759 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
2760 break;
2761 case SID_FM_RECORD_UNDO:
2762 Undo();
2763 break;
2764 case SID_FM_RECORD_SAVE:
2765 SaveRow();
2766 break;
2767 default:
2768 break;
2772 //------------------------------------------------------------------------------
2773 void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
2775 SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
2776 SolarMutexGuard aGuard;
2777 // prop "IsModified" changed ?
2778 // during update don't care about the modified state
2779 if (!IsUpdating() && evt.PropertyName == FM_PROP_ISMODIFIED )
2781 Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
2782 DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
2783 sal_Bool bIsNew = sal_False;
2784 if (xSource.is())
2785 bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
2787 if (bIsNew && m_xCurrentRow.Is())
2789 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 !");
2790 sal_Int32 nRecordCount = 0;
2791 xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
2792 if (::comphelper::getBOOL(evt.NewValue))
2793 { // modified state changed from sal_False to sal_True and we're on a insert row
2794 // -> we've to add a new grid row
2795 if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
2797 RowInserted(GetRowCount(), 1, sal_True);
2798 InvalidateStatusCell(m_nCurrentPos);
2799 m_aBar.InvalidateAll(m_nCurrentPos);
2802 else
2803 { // modified state changed from sal_True to sal_False and we're on a insert row
2804 // we have two "new row"s at the moment : the one we're editing currently (where the current
2805 // column is the only dirty element) and a "new new" row which is completely clean. As the first
2806 // one is about to be cleaned, too, the second one is obsolet now.
2807 if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
2809 RowRemoved(GetRowCount() - 1, 1, sal_True);
2810 InvalidateStatusCell(m_nCurrentPos);
2811 m_aBar.InvalidateAll(m_nCurrentPos);
2815 if (m_xCurrentRow.Is())
2817 m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GRS_MODIFIED : GRS_CLEAN);
2818 m_xCurrentRow->SetNew( bIsNew );
2819 InvalidateStatusCell(m_nCurrentPos);
2820 SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
2825 //------------------------------------------------------------------------------
2826 void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
2828 if (!m_pSeekCursor || IsResizing())
2829 return;
2831 sal_uInt16 nColId = GetColumnAtXPosPixel(rPosPixel.X());
2832 long nRow = GetRowAtYPosPixel(rPosPixel.Y());
2833 if (nColId != HandleColumnId && nRow >= 0)
2835 if (GetDataWindow().IsMouseCaptured())
2836 GetDataWindow().ReleaseMouse();
2838 size_t Location = GetModelColumnPos( nColId );
2839 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2840 OStringTransferable* pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
2841 Reference< XTransferable > xEnsureDelete(pTransferable);
2842 pTransferable->StartDrag(this, DND_ACTION_COPY);
2846 //------------------------------------------------------------------------------
2847 sal_Bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2849 return (_nRow >= 0)
2850 && (_nRow < GetRowCount())
2851 && (_nColId != HandleColumnId)
2852 && (_nColId <= ColCount());
2855 //------------------------------------------------------------------------------
2856 void DbGridControl::copyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2858 DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
2859 DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ];
2860 SeekRow(_nRow);
2861 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
2864 //------------------------------------------------------------------------------
2865 void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos )
2867 PopupMenu aContextMenu( SVX_RES( RID_SVXMNU_ROWS ) );
2869 PreExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu );
2870 aContextMenu.RemoveDisabledEntries( sal_True, sal_True );
2871 PostExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu, aContextMenu.Execute( this, _rPreferredPos ) );
2873 // TODO: why this weird cast to sal_uInt16? What if we really have more than 65535 lines?
2874 // -> change this to sal_uInt32
2877 //------------------------------------------------------------------------------
2878 void DbGridControl::Command(const CommandEvent& rEvt)
2880 switch (rEvt.GetCommand())
2882 case COMMAND_CONTEXTMENU:
2884 if ( !m_pSeekCursor )
2886 DbGridControl_Base::Command(rEvt);
2887 return;
2890 if ( !rEvt.IsMouseEvent() )
2891 { // context menu requested by keyboard
2892 if ( GetSelectRowCount() )
2894 long nRow = FirstSelectedRow( );
2896 ::Rectangle aRowRect( GetRowRectPixel( nRow, sal_True ) );
2897 executeRowContextMenu( nRow, aRowRect.LeftCenter() );
2899 // handled
2900 return;
2904 sal_uInt16 nColId = GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X());
2905 long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
2907 if (nColId == HandleColumnId)
2909 executeRowContextMenu( nRow, rEvt.GetMousePosPixel() );
2911 else if (canCopyCellText(nRow, nColId))
2913 PopupMenu aContextMenu(SVX_RES(RID_SVXMNU_CELL));
2914 aContextMenu.RemoveDisabledEntries(sal_True, sal_True);
2915 switch (aContextMenu.Execute(this, rEvt.GetMousePosPixel()))
2917 case SID_COPY:
2918 copyCellText(nRow, nColId);
2919 break;
2922 else
2924 DbGridControl_Base::Command(rEvt);
2925 return;
2928 default:
2929 DbGridControl_Base::Command(rEvt);
2933 //------------------------------------------------------------------------------
2934 IMPL_LINK(DbGridControl, OnDelete, void*, /*EMPTYTAG*/ )
2936 DBG_CHKTHIS(DbGridControl, NULL );
2937 m_nDeleteEvent = 0;
2938 DeleteSelectedRows();
2939 return 0;
2942 //------------------------------------------------------------------------------
2943 void DbGridControl::DeleteSelectedRows()
2945 DBG_ASSERT(GetSelection(), "keine selection!!!");
2947 if (!m_pSeekCursor)
2948 return;
2951 //------------------------------------------------------------------------------
2952 CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId)
2954 if (!IsValid(m_xCurrentRow) || !IsEnabled())
2955 return NULL;
2957 size_t Location = GetModelColumnPos(nColumnId);
2958 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2959 if (!pColumn)
2960 return NULL;
2962 CellController* pReturn = NULL;
2963 if (IsFilterMode())
2964 pReturn = &pColumn->GetController();
2965 else
2967 if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
2969 if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
2970 return NULL;
2973 sal_Bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & OPT_INSERT));
2974 sal_Bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & OPT_UPDATE));
2976 if ((bInsert && !pColumn->IsAutoValue()) || bUpdate || m_bForceROController)
2978 pReturn = &pColumn->GetController();
2979 if (pReturn)
2981 // wenn es eine Edit-Zeile ist, kann ich ihr das forced read-only mitgeben
2982 if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController))
2983 // ich konnte den Controller in forceROController nicht auf ReadOnly setzen
2984 if (!bInsert && !bUpdate)
2985 // ich bin nur hier, da m_bForceROController gesetzt war
2986 // -> lieber kein Controller als einer ohne RO
2987 pReturn = NULL;
2991 return pReturn;
2994 //------------------------------------------------------------------------------
2995 void DbGridControl::InitController(CellControllerRef& /*rController*/, long /*nRow*/, sal_uInt16 nColumnId)
2997 size_t Location = GetModelColumnPos(nColumnId);
2998 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2999 if (pColumn)
3000 pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
3003 //------------------------------------------------------------------------------
3004 void DbGridControl::CellModified()
3006 SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
3009 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3010 if (m_nAsynAdjustEvent)
3012 SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
3013 RemoveUserEvent(m_nAsynAdjustEvent);
3014 m_nAsynAdjustEvent = 0;
3016 // force the call : this should be no problem as we're probably running in the solar thread here
3017 // (cell modified is triggered by user actions)
3018 if (m_bPendingAdjustRows)
3019 AdjustRows();
3020 else
3021 AdjustDataSource();
3025 if (!IsFilterMode() && IsValid(m_xCurrentRow) && !m_xCurrentRow->IsModified())
3027 // Einschalten des Editiermodus
3028 // Datensatz soll eingefuegt werden
3029 if (m_xCurrentRow->IsNew())
3031 m_xCurrentRow->SetStatus(GRS_MODIFIED);
3032 SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
3033 // wenn noch keine Zeile hinzugefuegt wurde, dann neue hinzunehmen
3034 if (m_nCurrentPos == GetRowCount() - 1)
3036 // RowCount um einen erhoehen
3037 RowInserted(GetRowCount(), 1, sal_True);
3038 InvalidateStatusCell(m_nCurrentPos);
3039 m_aBar.InvalidateAll(m_nCurrentPos);
3042 else if (m_xCurrentRow->GetStatus() != GRS_MODIFIED)
3044 m_xCurrentRow->SetState(m_pDataCursor, sal_False);
3045 SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3046 m_xCurrentRow->SetStatus(GRS_MODIFIED);
3047 SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
3048 InvalidateStatusCell(m_nCurrentPos);
3053 //------------------------------------------------------------------------------
3054 void DbGridControl::Dispatch(sal_uInt16 nId)
3056 if (nId == BROWSER_CURSORENDOFFILE)
3058 if (m_nOptions & OPT_INSERT)
3059 AppendNew();
3060 else
3061 MoveToLast();
3063 else
3064 DbGridControl_Base::Dispatch(nId);
3067 //------------------------------------------------------------------------------
3068 void DbGridControl::Undo()
3070 if (!IsFilterMode() && IsValid(m_xCurrentRow) && IsModified())
3072 // check if we have somebody doin' the UNDO for us
3073 long nState = -1;
3074 if (m_aMasterStateProvider.IsSet())
3075 nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
3076 if (nState>0)
3077 { // yes, we have, and the slot is enabled
3078 DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
3079 long lResult = m_aMasterSlotExecutor.Call((void*)SID_FM_RECORD_UNDO);
3080 if (lResult)
3081 // handled
3082 return;
3084 else if (nState == 0)
3085 // yes, we have, and the slot is disabled
3086 return;
3088 BeginCursorAction();
3090 sal_Bool bAppending = m_xCurrentRow->IsNew();
3091 sal_Bool bDirty = m_xCurrentRow->IsModified();
3095 // Editieren abbrechen
3096 Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
3097 // no effects if we're not updating currently
3098 if (bAppending)
3099 // just refresh the row
3100 xUpdateCursor->moveToInsertRow();
3101 else
3102 xUpdateCursor->cancelRowUpdates();
3105 catch(Exception&)
3107 DBG_UNHANDLED_EXCEPTION();
3110 EndCursorAction();
3112 m_xDataRow->SetState(m_pDataCursor, sal_False);
3113 if (&m_xPaintRow == &m_xCurrentRow)
3114 m_xPaintRow = m_xCurrentRow = m_xDataRow;
3115 else
3116 m_xCurrentRow = m_xDataRow;
3118 if (bAppending && (DbGridControl_Base::IsModified() || bDirty))
3119 // remove the row
3120 if (m_nCurrentPos == GetRowCount() - 2)
3121 { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
3122 // caused our data source form to be reset - which should be the usual case ....)
3123 RowRemoved(GetRowCount() - 1, 1, sal_True);
3124 m_aBar.InvalidateAll(m_nCurrentPos);
3127 RowModified(m_nCurrentPos);
3131 //------------------------------------------------------------------------------
3132 void DbGridControl::resetCurrentRow()
3134 if (IsModified())
3136 // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
3137 // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
3138 // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
3139 // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
3140 // would never delete the obsolet "second insert row". Thus in this special case this method here
3141 // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
3142 // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
3143 Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
3144 if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
3146 // are we on a new row currently ?
3147 if (m_xCurrentRow->IsNew())
3149 if (m_nCurrentPos == GetRowCount() - 2)
3151 RowRemoved(GetRowCount() - 1, 1, sal_True);
3152 m_aBar.InvalidateAll(m_nCurrentPos);
3157 // update the rows
3158 m_xDataRow->SetState(m_pDataCursor, sal_False);
3159 if (&m_xPaintRow == &m_xCurrentRow)
3160 m_xPaintRow = m_xCurrentRow = m_xDataRow;
3161 else
3162 m_xCurrentRow = m_xDataRow;
3165 RowModified(GetCurRow()); // will update the current controller if affected
3168 //------------------------------------------------------------------------------
3169 void DbGridControl::RowModified( long nRow, sal_uInt16 /*nColId*/ )
3171 if (nRow == m_nCurrentPos && IsEditing())
3173 CellControllerRef aTmpRef = Controller();
3174 aTmpRef->ClearModified();
3175 InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
3177 DbGridControl_Base::RowModified(nRow);
3180 //------------------------------------------------------------------------------
3181 sal_Bool DbGridControl::IsModified() const
3183 return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || DbGridControl_Base::IsModified());
3186 //------------------------------------------------------------------------------
3187 sal_Bool DbGridControl::IsCurrentAppending() const
3189 return m_xCurrentRow.Is() && m_xCurrentRow->IsNew();
3192 //------------------------------------------------------------------------------
3193 sal_Bool DbGridControl::IsInsertionRow(long nRow) const
3195 return (m_nOptions & OPT_INSERT) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
3198 //------------------------------------------------------------------------------
3199 sal_Bool DbGridControl::SaveModified()
3201 SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
3202 DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
3203 if (!IsValid(m_xCurrentRow))
3204 return sal_True;
3206 // Uebernimmt die Dateneingabe fuer das Feld
3207 // Hat es aenderungen im aktuellen Eingabefeld gegeben ?
3208 if (!DbGridControl_Base::IsModified())
3209 return sal_True;
3211 size_t Location = GetModelColumnPos( GetCurColumnId() );
3212 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3213 sal_Bool bOK = pColumn->Commit();
3214 DBG_ASSERT( Controller().Is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
3215 if ( !Controller().Is() )
3216 // this might happen if the callbacks implicitly triggered by Commit
3217 // fiddled with the form or the control ...
3218 // (Note that this here is a workaround, at most. We need a general concept how
3219 // to treat this, you can imagine an arbitrary number of scenarios where a callback
3220 // triggers something which leaves us in an expected state.)
3221 // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
3222 return bOK;
3224 if (bOK)
3226 Controller()->ClearModified();
3228 if ( IsValid(m_xCurrentRow) )
3230 m_xCurrentRow->SetState(m_pDataCursor, sal_False);
3231 SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3232 InvalidateStatusCell( m_nCurrentPos );
3234 else
3236 SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3239 else
3241 // reset the modified flag ....
3242 Controller()->SetModified();
3245 return bOK;
3248 //------------------------------------------------------------------------------
3249 sal_Bool DbGridControl::SaveRow()
3251 SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
3252 // gueltige Zeile
3253 if (!IsValid(m_xCurrentRow) || !IsModified())
3254 return sal_True;
3255 // Wert des Controllers noch nicht gespeichert
3256 else if (Controller().Is() && Controller()->IsModified())
3258 if (!SaveModified())
3259 return sal_False;
3261 m_bUpdating = sal_True;
3263 BeginCursorAction();
3264 sal_Bool bAppending = m_xCurrentRow->IsNew();
3265 sal_Bool bSuccess = sal_False;
3268 Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
3269 if (bAppending)
3270 xUpdateCursor->insertRow();
3271 else
3272 xUpdateCursor->updateRow();
3273 bSuccess = sal_True;
3275 catch(SQLException&)
3277 EndCursorAction();
3278 m_bUpdating = sal_False;
3279 return sal_False;
3284 if (bSuccess)
3286 // if we are appending we still sit on the insert row
3287 // we don't move just clear the flags not to move on the current row
3288 m_xCurrentRow->SetState(m_pDataCursor, sal_False);
3289 SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
3290 m_xCurrentRow->SetNew(sal_False);
3292 // adjust the seekcursor if it is on the same position as the datacursor
3293 if (m_nSeekPos == m_nCurrentPos || bAppending)
3295 // get the bookmark to refetch the data
3296 // in insert mode we take the new bookmark of the data cursor
3297 Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
3298 m_pSeekCursor->moveToBookmark(aBookmark);
3299 // get the data
3300 m_xSeekRow->SetState(m_pSeekCursor, sal_True);
3301 m_nSeekPos = m_pSeekCursor->getRow() - 1;
3304 // and repaint the row
3305 RowModified(m_nCurrentPos);
3307 catch(Exception&)
3311 m_bUpdating = sal_False;
3312 EndCursorAction();
3314 // The old code returned (nRecords != 0) here.
3315 // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
3316 // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
3317 // is zero, this simply means all fields had their original values.
3318 // FS - 06.12.99 - 70502
3319 return sal_True;
3322 //------------------------------------------------------------------------------
3323 long DbGridControl::PreNotify(NotifyEvent& rEvt)
3325 // keine Events der Navbar behandeln
3326 if (m_aBar.IsWindowOrChild(rEvt.GetWindow()))
3327 return BrowseBox::PreNotify(rEvt);
3329 switch (rEvt.GetType())
3331 case EVENT_KEYINPUT:
3333 const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
3335 sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
3336 sal_Bool bShift = pKeyEvent->GetKeyCode().IsShift();
3337 sal_Bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
3338 sal_Bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
3339 if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
3341 // Ctrl-Tab is used to step out of the control, without traveling to the
3342 // remaining cells first
3343 // -> build a new key event without the Ctrl-key, and let the very base class handle it
3344 KeyCode aNewCode( KEY_TAB, bShift, sal_False, sal_False, sal_False );
3345 KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
3347 // call the Control - our direct base class will interpret this in a way we do not want (and do
3348 // a cell traveling)
3349 Control::KeyInput( aNewEvent );
3350 return 1;
3353 if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
3355 if (IsModified())
3357 Undo();
3358 return 1;
3361 else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
3363 if ((m_nOptions & OPT_DELETE) && GetSelectRowCount())
3365 // delete asynchron
3366 if (m_nDeleteEvent)
3367 Application::RemoveUserEvent(m_nDeleteEvent);
3368 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
3369 return 1;
3372 } // kein break!
3373 default:
3374 return DbGridControl_Base::PreNotify(rEvt);
3378 //------------------------------------------------------------------------------
3379 sal_Bool DbGridControl::IsTabAllowed(sal_Bool bRight) const
3381 if (bRight)
3382 // Tab nur wenn nicht auf der letzten Zelle
3383 return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
3384 GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
3385 else
3387 // Tab nur wenn nicht auf der ersten Zelle
3388 return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
3392 //------------------------------------------------------------------------------
3393 void DbGridControl::KeyInput( const KeyEvent& rEvt )
3395 if (rEvt.GetKeyCode().GetFunction() == KEYFUNC_COPY)
3397 long nRow = GetCurRow();
3398 sal_uInt16 nColId = GetCurColumnId();
3399 if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
3401 size_t Location = GetModelColumnPos( nColId );
3402 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3403 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
3404 return;
3407 DbGridControl_Base::KeyInput(rEvt);
3410 //------------------------------------------------------------------------------
3411 void DbGridControl::HideColumn(sal_uInt16 nId)
3413 DeactivateCell();
3415 // determine the col for the focus to set to after removal
3416 sal_uInt16 nPos = GetViewColumnPos(nId);
3417 sal_uInt16 nNewColId = nPos == (ColCount()-1)
3418 ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
3419 : GetColumnIdFromViewPos(nPos+1); // take the next
3421 long lCurrentWidth = GetColumnWidth(nId);
3422 DbGridControl_Base::RemoveColumn(nId);
3423 // don't use my own RemoveColumn, this would remove it from m_aColumns, too
3425 // update my model
3426 size_t Location = GetModelColumnPos( nId );
3427 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3428 DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
3429 if (pColumn)
3431 pColumn->m_bHidden = sal_True;
3432 pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
3435 // and reset the focus
3436 if ( nId == GetCurColumnId() )
3437 GoToColumnId( nNewColId );
3440 //------------------------------------------------------------------------------
3441 void DbGridControl::ShowColumn(sal_uInt16 nId)
3443 sal_uInt16 nPos = GetModelColumnPos(nId);
3444 DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
3445 if (nPos == GRID_COLUMN_NOT_FOUND)
3446 return;
3448 DbGridColumn* pColumn = m_aColumns[ nPos ];
3449 if (!pColumn->IsHidden())
3451 DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3452 // if the column isn't marked as hidden, it should be visible, shouldn't it ?
3453 return;
3455 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3456 // the opposite situation ...
3458 // to determine the new view position we need an adjacent non-hidden column
3459 sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
3460 // first search the cols to the right
3461 for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
3463 DbGridColumn* pCurCol = m_aColumns[ i ];
3464 if (!pCurCol->IsHidden())
3466 nNextNonHidden = i;
3467 break;
3470 if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
3472 // then to the left
3473 for ( size_t i = nPos; i > 0; --i )
3475 DbGridColumn* pCurCol = m_aColumns[ i-1 ];
3476 if (!pCurCol->IsHidden())
3478 nNextNonHidden = i-1;
3479 break;
3483 sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
3484 ? 1 // there is no visible column -> insert behinde the handle col
3485 : GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1;
3486 // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
3487 // a position 1 for the first non-handle col -> +1
3488 DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3489 // we found a col marked as visible but got no view pos for it ...
3491 if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID))
3492 // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
3493 ++nNewViewPos;
3495 DeactivateCell();
3497 OUString aName;
3498 pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
3499 InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HIB_CENTER | HIB_VCENTER | HIB_CLICKABLE, nNewViewPos);
3500 pColumn->m_bHidden = sal_False;
3502 ActivateCell();
3503 Invalidate();
3506 //------------------------------------------------------------------------------
3507 sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
3509 if (nPos >= m_aColumns.size())
3511 OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
3512 return GRID_COLUMN_NOT_FOUND;
3515 DbGridColumn* pCol = m_aColumns[ nPos ];
3516 #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
3517 // in der Debug-Version rechnen wir die ModelPos in eine ViewPos um und vergleichen das mit dem Wert,
3518 // den wir zurueckliefern werden (nId an der entsprechenden Col in m_aColumns)
3520 if (!pCol->IsHidden())
3521 { // macht nur Sinn, wenn die Spalte sichtbar ist
3522 sal_uInt16 nViewPos = nPos;
3523 for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i)
3524 if ( m_aColumns[ i ]->IsHidden())
3525 --nViewPos;
3527 DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
3528 "DbGridControl::GetColumnIdFromModelPos : this isn't consistent .... did I misunderstand something ?");
3530 #endif
3531 return pCol->GetId();
3534 //------------------------------------------------------------------------------
3535 sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
3537 for ( size_t i = 0; i < m_aColumns.size(); ++i )
3538 if ( m_aColumns[ i ]->GetId() == nId )
3539 return i;
3541 return GRID_COLUMN_NOT_FOUND;
3544 //------------------------------------------------------------------------------
3545 void DbGridControl::implAdjustInSolarThread(sal_Bool _bRows)
3547 SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
3548 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3549 if (::osl::Thread::getCurrentIdentifier() != Application::GetMainThreadIdentifier())
3551 m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ));
3552 m_bPendingAdjustRows = _bRows;
3553 if (_bRows)
3554 SAL_INFO("svx.fmcomp", "posting an AdjustRows");
3555 else
3556 SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
3558 else
3560 if (_bRows)
3561 SAL_INFO("svx.fmcomp", "doing an AdjustRows");
3562 else
3563 SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
3564 // always adjust the rows before adjusting the data source
3565 // If this is not necessary (because the row count did not change), nothing is done
3566 // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
3567 // to a position behind row count know 'til now, the cursorMoved notification may come before the
3568 // RowCountChanged notification
3569 // 94093 - 02.11.2001 - frank.schoenheit@sun.com
3570 AdjustRows();
3572 if ( !_bRows )
3573 AdjustDataSource();
3577 //------------------------------------------------------------------------------
3578 IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat)
3580 m_nAsynAdjustEvent = 0;
3582 AdjustRows();
3583 // see implAdjustInSolarThread for a comment why we do this every time
3585 if ( !pAdjustWhat )
3586 AdjustDataSource();
3588 return 0L;
3591 //------------------------------------------------------------------------------
3592 void DbGridControl::BeginCursorAction()
3594 if (m_pFieldListeners)
3596 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3597 ConstColumnFieldValueListenersIterator aIter = pListeners->begin();
3598 while (aIter != pListeners->end())
3600 GridFieldValueListener* pCurrent = (*aIter).second;
3601 if (pCurrent)
3602 pCurrent->suspend();
3603 ++aIter;
3607 if (m_pDataSourcePropListener)
3608 m_pDataSourcePropListener->suspend();
3611 //------------------------------------------------------------------------------
3612 void DbGridControl::EndCursorAction()
3614 if (m_pFieldListeners)
3616 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3617 ConstColumnFieldValueListenersIterator aIter = pListeners->begin();
3618 while (aIter != pListeners->end())
3620 GridFieldValueListener* pCurrent = (*aIter).second;
3621 if (pCurrent)
3622 pCurrent->resume();
3623 ++aIter;
3627 if (m_pDataSourcePropListener)
3628 m_pDataSourcePropListener->resume();
3631 //------------------------------------------------------------------------------
3632 void DbGridControl::ConnectToFields()
3634 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3635 DBG_ASSERT(!pListeners || pListeners->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
3637 if (!pListeners)
3639 pListeners = new ColumnFieldValueListeners;
3640 m_pFieldListeners = pListeners;
3643 for ( size_t i = 0; i < m_aColumns.size(); ++i )
3645 DbGridColumn* pCurrent = m_aColumns[ i ];
3646 sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
3647 if (GRID_COLUMN_NOT_FOUND == nViewPos)
3648 continue;
3650 Reference< XPropertySet > xField = pCurrent->GetField();
3651 if (!xField.is())
3652 continue;
3654 // column is visible and bound here
3655 GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
3656 DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
3657 rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
3661 //------------------------------------------------------------------------------
3662 void DbGridControl::DisconnectFromFields()
3664 if (!m_pFieldListeners)
3665 return;
3667 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3668 while (pListeners->size())
3670 #ifdef DBG_UTIL
3671 sal_Int32 nOldSize = pListeners->size();
3672 #endif
3673 pListeners->begin()->second->dispose();
3674 DBG_ASSERT(nOldSize > (sal_Int32)pListeners->size(), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
3677 delete pListeners;
3678 m_pFieldListeners = NULL;
3681 //------------------------------------------------------------------------------
3682 void DbGridControl::FieldValueChanged(sal_uInt16 _nId, const PropertyChangeEvent& /*_evt*/)
3684 osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
3685 // needed as this may run in a thread other than the main one
3686 if (GetRowStatus(GetCurRow()) != DbGridControl_Base::MODIFIED)
3687 // all other cases are handled elsewhere
3688 return;
3690 size_t Location = GetModelColumnPos( _nId );
3691 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3692 if (pColumn)
3694 sal_Bool bAcquiredPaintSafety = sal_False;
3695 while (!m_bWantDestruction && !bAcquiredPaintSafety)
3696 bAcquiredPaintSafety = Application::GetSolarMutex().tryToAcquire();
3698 if (m_bWantDestruction)
3699 { // at this moment, within another thread, our destructor tries to destroy the listener which called this method
3700 // => don't do anything
3701 // 73365 - 23.02.00 - FS
3702 if (bAcquiredPaintSafety)
3703 // though the above while-loop suggests that (m_bWantDestruction && bAcquiredPaintSafety) is impossible,
3704 // it isnt't, as m_bWantDestruction isn't protected with any mutex
3705 Application::GetSolarMutex().release();
3706 return;
3708 // here we got the solar mutex, transfer it to a guard for safety reasons
3709 SolarMutexGuard aPaintSafety;
3710 Application::GetSolarMutex().release();
3712 // and finally do the update ...
3713 pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
3714 RowModified(GetCurRow(), _nId);
3718 //------------------------------------------------------------------------------
3719 void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
3721 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3722 if (!pListeners)
3724 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
3725 return;
3728 ColumnFieldValueListenersIterator aPos = pListeners->find(_nId);
3729 if (aPos == pListeners->end())
3731 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
3732 return;
3735 delete aPos->second;
3737 pListeners->erase(aPos);
3740 //------------------------------------------------------------------------------
3741 void DbGridControl::disposing(sal_uInt16 _nId, const EventObject& /*_rEvt*/)
3743 if (_nId == 0)
3744 { // the seek cursor is beeing disposed
3745 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3746 setDataSource(NULL,0); // our clone was disposed so we set our datasource to null to avoid later acces to it
3747 if (m_nAsynAdjustEvent)
3749 RemoveUserEvent(m_nAsynAdjustEvent);
3750 m_nAsynAdjustEvent = 0;
3754 // -----------------------------------------------------------------------------
3755 sal_Int32 DbGridControl::GetAccessibleControlCount() const
3757 return DbGridControl_Base::GetAccessibleControlCount() + 1; // the navigation control
3759 // -----------------------------------------------------------------------------
3760 Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
3762 Reference<XAccessible > xRet;
3763 if ( _nIndex == DbGridControl_Base::GetAccessibleControlCount() )
3765 xRet = m_aBar.GetAccessible();
3767 else
3768 xRet = DbGridControl_Base::CreateAccessibleControl( _nIndex );
3769 return xRet;
3771 // -----------------------------------------------------------------------------
3772 Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
3774 sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
3775 size_t Location = GetModelColumnPos(nColumnId);
3776 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3777 if ( pColumn )
3779 Reference< ::com::sun::star::awt::XControl> xInt(pColumn->GetCell());
3780 Reference< ::com::sun::star::awt::XCheckBox> xBox(xInt,UNO_QUERY);
3781 if ( xBox.is() )
3783 TriState eValue = STATE_NOCHECK;
3784 switch( xBox->getState() )
3786 case 0:
3787 eValue = STATE_NOCHECK;
3788 break;
3789 case 1:
3790 eValue = STATE_CHECK;
3791 break;
3792 case 2:
3793 eValue = STATE_DONTKNOW;
3794 break;
3796 return DbGridControl_Base::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
3799 return DbGridControl_Base::CreateAccessibleCell( _nRow, _nColumnPos );
3801 // -----------------------------------------------------------------------------
3804 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */