bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / fmcomp / gridctrl.cxx
blob76920e17650c73d26522a04173df2959fa9aa579
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 .
20 #include <sal/macros.h>
21 #include "fmhelp.hrc"
22 #include <svx/gridctrl.hxx>
23 #include "gridcell.hxx"
24 #include "svx/fmtools.hxx"
25 #include <svtools/stringtransfer.hxx>
26 #include <connectivity/dbtools.hxx>
27 #include <connectivity/dbconversion.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/processfactory.hxx>
44 #include <tools/resid.hxx>
45 #include <tools/diagnose_ex.h>
46 #include <tools/fract.hxx>
47 #include <vcl/menu.hxx>
48 #include <vcl/settings.hxx>
50 #include "svx/fmresids.hrc"
52 #include <svx/svxids.hrc>
53 #include <svx/dialmgr.hxx>
54 #include "fmservs.hxx"
55 #include "sdbdatacolumn.hxx"
57 #include <comphelper/property.hxx>
59 #include <algorithm>
60 #include <cstdlib>
61 #include <map>
63 using namespace ::dbtools;
64 using namespace ::dbtools::DBTypeConversion;
65 using namespace ::svxform;
66 using namespace ::svt;
67 using namespace ::com::sun::star::beans;
68 using namespace ::com::sun::star::lang;
69 using namespace ::com::sun::star::uno;
70 using namespace ::com::sun::star::sdbc;
71 using namespace ::com::sun::star::sdbcx;
72 using namespace ::com::sun::star::sdb;
73 using namespace ::com::sun::star::datatransfer;
74 using namespace ::com::sun::star::container;
75 using namespace com::sun::star::accessibility;
77 #define ROWSTATUS(row) (!row.Is() ? "NULL" : row->GetStatus() == GRS_CLEAN ? "CLEAN" : row->GetStatus() == GRS_MODIFIED ? "MODIFIED" : row->GetStatus() == GRS_DELETED ? "DELETED" : "INVALID")
79 #define DEFAULT_BROWSE_MODE \
80 BrowserMode::COLUMNSELECTION \
81 | BrowserMode::MULTISELECTION \
82 | BrowserMode::KEEPHIGHLIGHT \
83 | BrowserMode::TRACKING_TIPS \
84 | BrowserMode::HLINES \
85 | BrowserMode::VLINES \
86 | BrowserMode::HEADERBAR_NEW \
88 class RowSetEventListener : public ::cppu::WeakImplHelper1<XRowsChangeListener>
90 VclPtr<DbGridControl> m_pControl;
91 public:
92 RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
96 private:
97 // XEventListener
98 virtual void SAL_CALL disposing(const ::com::sun::star::lang::EventObject& /*i_aEvt*/) throw ( RuntimeException, std::exception ) SAL_OVERRIDE
101 virtual void SAL_CALL rowsChanged(const ::com::sun::star::sdb::RowsChangeEvent& i_aEvt) throw ( RuntimeException, std::exception ) SAL_OVERRIDE
103 if ( i_aEvt.Action == RowChangeAction::UPDATE )
105 ::DbGridControl::GrantControlAccess aAccess;
106 CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
107 const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
108 const Any* pIter = i_aEvt.Bookmarks.getConstArray();
109 const Any* pEnd = pIter + i_aEvt.Bookmarks.getLength();
110 for(;pIter != pEnd;++pIter)
112 pSeek->moveToBookmark(*pIter);
113 // get the data
114 rSeekRow->SetState(pSeek, true);
115 sal_Int32 nSeekPos = pSeek->getRow() - 1;
116 m_pControl->SetSeekPos(nSeekPos,aAccess);
117 m_pControl->RowModified(nSeekPos);
123 class GridFieldValueListener;
124 typedef std::map<sal_uInt16, GridFieldValueListener*> ColumnFieldValueListeners;
126 class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
128 osl::Mutex m_aMutex;
129 DbGridControl& m_rParent;
130 ::comphelper::OPropertyChangeMultiplexer* m_pRealListener;
131 sal_uInt16 m_nId;
132 sal_Int16 m_nSuspended;
133 bool m_bDisposed : 1;
135 public:
136 GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
137 virtual ~GridFieldValueListener();
139 virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) SAL_OVERRIDE;
141 void suspend() { ++m_nSuspended; }
142 void resume() { --m_nSuspended; }
144 void dispose();
147 GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId)
148 :OPropertyChangeListener(m_aMutex)
149 ,m_rParent(_rParent)
150 ,m_pRealListener(NULL)
151 ,m_nId(_nId)
152 ,m_nSuspended(0)
153 ,m_bDisposed(false)
155 if (_rField.is())
157 m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
158 m_pRealListener->addProperty(FM_PROP_VALUE);
159 m_pRealListener->acquire();
163 GridFieldValueListener::~GridFieldValueListener()
165 dispose();
168 void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& _evt) throw( RuntimeException )
170 DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
171 if (m_nSuspended <= 0)
172 m_rParent.FieldValueChanged(m_nId, _evt);
175 void GridFieldValueListener::dispose()
177 if (m_bDisposed)
179 DBG_ASSERT(m_pRealListener == NULL, "GridFieldValueListener::dispose : inconsistent !");
180 return;
183 if (m_pRealListener)
185 m_pRealListener->dispose();
186 m_pRealListener->release();
187 m_pRealListener = NULL;
190 m_bDisposed = true;
191 m_rParent.FieldListenerDisposing(m_nId);
194 class DisposeListenerGridBridge : public FmXDisposeListener
196 osl::Mutex m_aMutex;
197 DbGridControl& m_rParent;
198 FmXDisposeMultiplexer* m_pRealListener;
200 public:
201 DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId = -1);
202 virtual ~DisposeListenerGridBridge();
204 virtual void disposing(const EventObject& _rEvent, sal_Int16 _nId) throw( RuntimeException ) SAL_OVERRIDE { m_rParent.disposing(_nId, _rEvent); }
207 DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId)
208 :FmXDisposeListener(m_aMutex)
209 ,m_rParent(_rParent)
210 ,m_pRealListener(NULL)
213 if (_rxObject.is())
215 m_pRealListener = new FmXDisposeMultiplexer(this, _rxObject, _rId);
216 m_pRealListener->acquire();
220 DisposeListenerGridBridge::~DisposeListenerGridBridge()
222 if (m_pRealListener)
224 m_pRealListener->dispose();
225 m_pRealListener->release();
226 m_pRealListener = NULL;
231 static const sal_uInt16 ControlMap[] =
233 DbGridControl::NavigationBar::RECORD_TEXT,
234 DbGridControl::NavigationBar::RECORD_ABSOLUTE,
235 DbGridControl::NavigationBar::RECORD_OF,
236 DbGridControl::NavigationBar::RECORD_COUNT,
237 DbGridControl::NavigationBar::RECORD_FIRST,
238 DbGridControl::NavigationBar::RECORD_NEXT,
239 DbGridControl::NavigationBar::RECORD_PREV,
240 DbGridControl::NavigationBar::RECORD_LAST,
241 DbGridControl::NavigationBar::RECORD_NEW,
245 bool CompareBookmark(const Any& aLeft, const Any& aRight)
247 return ::comphelper::compare(aLeft, aRight);
250 class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
252 VclPtr<DbGridControl> m_pParent;
254 // a DbGridControl has no mutex, so we use our own as the base class expects one
255 osl::Mutex m_aMutex;
256 sal_Int16 m_nSuspended;
258 public:
259 FmXGridSourcePropListener(DbGridControl* _pParent);
261 void suspend() { ++m_nSuspended; }
262 void resume() { --m_nSuspended; }
264 virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) SAL_OVERRIDE;
267 FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
268 :OPropertyChangeListener(m_aMutex)
269 ,m_pParent(_pParent)
270 ,m_nSuspended(0)
272 DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
275 void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
277 DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
278 if (m_nSuspended <= 0)
279 m_pParent->DataSourcePropertyChanged(evt);
282 DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(vcl::Window* pParent, WinBits nStyle)
283 :NumericField(pParent, nStyle)
285 SetMin(1);
286 SetFirst(1);
287 SetSpinSize(1);
289 SetDecimalDigits(0);
290 SetStrictFormat(true);
293 void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt)
295 if (rEvt.GetKeyCode() == KEY_RETURN && !GetText().isEmpty())
297 sal_Int64 nRecord = GetValue();
298 if (nRecord < GetMin() || nRecord > GetMax())
299 return;
300 else
301 static_cast<NavigationBar*>(GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
303 else if (rEvt.GetKeyCode() == KEY_TAB)
304 GetParent()->GetParent()->GrabFocus();
305 else
306 NumericField::KeyInput(rEvt);
309 void DbGridControl::NavigationBar::AbsolutePos::LoseFocus()
311 NumericField::LoseFocus();
312 sal_Int64 nRecord = GetValue();
313 if (nRecord < GetMin() || nRecord > GetMax())
314 return;
315 else
317 static_cast<NavigationBar*>(GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
318 static_cast<NavigationBar*>(GetParent())->InvalidateState(NavigationBar::RECORD_ABSOLUTE);
322 void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord)
324 if (m_bPositioning)
325 return;
326 // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition,
327 // so protect against this recursion
328 m_bPositioning = true;
329 static_cast<DbGridControl*>(GetParent())->MoveToPosition(nRecord - 1);
330 m_bPositioning = false;
333 DbGridControl::NavigationBar::NavigationBar(vcl::Window* pParent, WinBits nStyle)
334 :Control(pParent, nStyle)
335 ,m_aRecordText(VclPtr<FixedText>::Create(this, WB_VCENTER))
336 ,m_aAbsolute(VclPtr<DbGridControl::NavigationBar::AbsolutePos>::Create(this, WB_CENTER | WB_VCENTER))
337 ,m_aRecordOf(VclPtr<FixedText>::Create(this, WB_VCENTER))
338 ,m_aRecordCount(VclPtr<FixedText>::Create(this, WB_VCENTER))
339 ,m_aFirstBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
340 ,m_aPrevBtn(VclPtr<ImageButton>::Create(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS))
341 ,m_aNextBtn(VclPtr<ImageButton>::Create(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS))
342 ,m_aLastBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
343 ,m_aNewBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
344 ,m_nDefaultWidth(0)
345 ,m_nCurrentPos(-1)
346 ,m_bPositioning(false)
348 m_aFirstBtn->SetSymbol(SymbolType::FIRST);
349 m_aPrevBtn->SetSymbol(SymbolType::PREV);
350 m_aNextBtn->SetSymbol(SymbolType::NEXT);
351 m_aLastBtn->SetSymbol(SymbolType::LAST);
352 m_aNewBtn->SetModeImage(static_cast<DbGridControl*>(pParent)->GetImage(DbGridControl_Base::NEW));
354 m_aFirstBtn->SetHelpId(HID_GRID_TRAVEL_FIRST);
355 m_aPrevBtn->SetHelpId(HID_GRID_TRAVEL_PREV);
356 m_aNextBtn->SetHelpId(HID_GRID_TRAVEL_NEXT);
357 m_aLastBtn->SetHelpId(HID_GRID_TRAVEL_LAST);
358 m_aNewBtn->SetHelpId(HID_GRID_TRAVEL_NEW);
359 m_aAbsolute->SetHelpId(HID_GRID_TRAVEL_ABSOLUTE);
360 m_aRecordCount->SetHelpId(HID_GRID_NUMBEROFRECORDS);
362 // Handler fuer Buttons einrichten
363 m_aFirstBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
364 m_aPrevBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
365 m_aNextBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
366 m_aLastBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
367 m_aNewBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
369 m_aRecordText->SetText(SVX_RESSTR(RID_STR_REC_TEXT));
370 m_aRecordOf->SetText(SVX_RESSTR(RID_STR_REC_FROM_TEXT));
371 m_aRecordCount->SetText(OUString('?'));
373 m_nDefaultWidth = ArrangeControls();
375 m_aFirstBtn->Disable();
376 m_aPrevBtn->Disable();
377 m_aNextBtn->Disable();
378 m_aLastBtn->Disable();
379 m_aNewBtn->Disable();
380 m_aRecordText->Disable();
381 m_aRecordOf->Disable();
382 m_aRecordCount->Disable();
383 m_aAbsolute->Disable();
385 AllSettings aSettings = m_aNextBtn->GetSettings();
386 MouseSettings aMouseSettings = aSettings.GetMouseSettings();
387 aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
388 aSettings.SetMouseSettings(aMouseSettings);
389 m_aNextBtn->SetSettings(aSettings, true);
390 m_aPrevBtn->SetSettings(aSettings, true);
392 m_aFirstBtn->Show();
393 m_aPrevBtn->Show();
394 m_aNextBtn->Show();
395 m_aLastBtn->Show();
396 m_aNewBtn->Show();
397 m_aRecordText->Show();
398 m_aRecordOf->Show();
399 m_aRecordCount->Show();
400 m_aAbsolute->Show();
404 DbGridControl::NavigationBar::~NavigationBar()
406 disposeOnce();
409 void DbGridControl::NavigationBar::dispose()
411 m_aRecordText.disposeAndClear();
412 m_aAbsolute.disposeAndClear();
413 m_aRecordOf.disposeAndClear();
414 m_aRecordCount.disposeAndClear();
415 m_aFirstBtn.disposeAndClear();
416 m_aPrevBtn.disposeAndClear();
417 m_aNextBtn.disposeAndClear();
418 m_aLastBtn.disposeAndClear();
419 m_aNewBtn.disposeAndClear();
420 Control::dispose();
423 namespace
425 void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize)
427 _rButton.SetPosPixel( _rPos );
428 _rButton.SetSizePixel( _rSize );
429 _rPos.X() += (sal_uInt16)_rSize.Width();
433 sal_uInt16 DbGridControl::NavigationBar::ArrangeControls()
435 // positioning of the controls
436 // calculate base size
437 Rectangle aRect(static_cast<DbGridControl*>(GetParent())->GetControlArea());
438 const long nH = aRect.GetSize().Height();
439 Size aBorder = LogicToPixel(Size(2, 2),MAP_APPFONT);
440 aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height()));
441 sal_uInt16 nX = 1;
442 sal_uInt16 nY = 0;
444 // Is the font of this edit larger than the field?
445 if (m_aAbsolute->GetTextHeight() > nH)
447 vcl::Font aApplFont (m_aAbsolute->GetFont());
448 const Size pointAbsoluteSize(m_aAbsolute->PixelToLogic( Size( 0, nH - 2 ), MapMode(MAP_POINT) ));
449 aApplFont.SetSize( pointAbsoluteSize );
450 m_aAbsolute->SetControlFont( aApplFont );
452 aApplFont.SetTransparent( true );
453 m_aRecordText->SetControlFont( aApplFont );
454 m_aRecordOf->SetControlFont( aApplFont );
455 m_aRecordCount->SetControlFont( aApplFont );
458 // set size and position of the control
459 OUString aText = m_aRecordText->GetText();
460 long nTextWidth = m_aRecordText->GetTextWidth(aText);
461 m_aRecordText->SetPosPixel(Point(nX,nY));
462 m_aRecordText->SetSizePixel(Size(nTextWidth,nH));
463 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
465 // count an extra hairspace (U+200A) left and right
466 const OUString sevenDigits(m_aAbsolute->CreateFieldText(6000000));
467 const OUString hairSpace(static_cast<sal_Unicode>(0x200A));
468 OUString textPattern(hairSpace);
469 textPattern += sevenDigits;
470 textPattern += hairSpace;
471 nTextWidth = m_aAbsolute->GetTextWidth( textPattern );
472 m_aAbsolute->SetPosPixel(Point(nX,nY));
473 m_aAbsolute->SetSizePixel(Size(nTextWidth, nH));
474 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
476 aText = m_aRecordOf->GetText();
477 nTextWidth = m_aRecordOf->GetTextWidth(aText);
478 m_aRecordOf->SetPosPixel(Point(nX,nY));
479 m_aRecordOf->SetSizePixel(Size(nTextWidth,nH));
480 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
482 textPattern = sevenDigits + " * (" + sevenDigits + ")";
483 nTextWidth = m_aRecordCount->GetTextWidth( textPattern );
484 m_aRecordCount->SetPosPixel(Point(nX,nY));
485 m_aRecordCount->SetSizePixel(Size(nTextWidth,nH));
486 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
488 Point aButtonPos(nX,nY);
489 const Size aButtonSize(nH,nH);
490 SetPosAndSize(*m_aFirstBtn.get(), aButtonPos, aButtonSize);
491 SetPosAndSize(*m_aPrevBtn.get(), aButtonPos, aButtonSize);
492 SetPosAndSize(*m_aNextBtn.get(), aButtonPos, aButtonSize);
493 SetPosAndSize(*m_aLastBtn.get(), aButtonPos, aButtonSize);
494 SetPosAndSize(*m_aNewBtn.get(), aButtonPos, aButtonSize);
496 nX = sal::static_int_cast< sal_uInt16 >(aButtonPos.X() + 1);
498 return nX;
501 IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton )
503 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
505 if (pParent->m_aMasterSlotExecutor.IsSet())
507 long lResult = 0;
508 if (pButton == m_aFirstBtn.get())
509 lResult = pParent->m_aMasterSlotExecutor.Call(reinterpret_cast<void*>(RECORD_FIRST));
510 else if( pButton == m_aPrevBtn.get() )
511 lResult = pParent->m_aMasterSlotExecutor.Call(reinterpret_cast<void*>(RECORD_PREV));
512 else if( pButton == m_aNextBtn.get() )
513 lResult = pParent->m_aMasterSlotExecutor.Call(reinterpret_cast<void*>(RECORD_NEXT));
514 else if( pButton == m_aLastBtn.get() )
515 lResult = pParent->m_aMasterSlotExecutor.Call(reinterpret_cast<void*>(RECORD_LAST));
516 else if( pButton == m_aNewBtn.get() )
517 lResult = pParent->m_aMasterSlotExecutor.Call(reinterpret_cast<void*>(RECORD_NEW));
519 if (lResult)
520 // the link already handled it
521 return 0;
524 if (pButton == m_aFirstBtn.get())
525 pParent->MoveToFirst();
526 else if( pButton == m_aPrevBtn.get() )
527 pParent->MoveToPrev();
528 else if( pButton == m_aNextBtn.get() )
529 pParent->MoveToNext();
530 else if( pButton == m_aLastBtn.get() )
531 pParent->MoveToLast();
532 else if( pButton == m_aNewBtn.get() )
533 pParent->AppendNew();
534 return 0;
537 void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll)
539 if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)
541 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
543 sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControl::OPT_INSERT) ? 2 : 1);
545 // check if everything needs to be invalidated
546 bAll = bAll || m_nCurrentPos <= 0;
547 bAll = bAll || nCurrentPos <= 0;
548 bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
549 bAll = bAll || nCurrentPos >= nAdjustedRowCount;
551 if ( bAll )
553 m_nCurrentPos = nCurrentPos;
554 int i = 0;
555 while (ControlMap[i])
556 SetState(ControlMap[i++]);
558 else // is in the center
560 m_nCurrentPos = nCurrentPos;
561 SetState(NavigationBar::RECORD_COUNT);
562 SetState(NavigationBar::RECORD_ABSOLUTE);
567 bool DbGridControl::NavigationBar::GetState(sal_uInt16 nWhich) const
569 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
571 if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
572 || pParent->IsFilterMode() )
573 return false;
574 else
576 // check if we have a master state provider
577 if (pParent->m_aMasterStateProvider.IsSet())
579 long nState = pParent->m_aMasterStateProvider.Call(reinterpret_cast< void* >( nWhich ) );
580 if (nState>=0)
581 return (nState>0);
584 bool bAvailable = true;
586 switch (nWhich)
588 case NavigationBar::RECORD_FIRST:
589 case NavigationBar::RECORD_PREV:
590 bAvailable = m_nCurrentPos > 0;
591 break;
592 case NavigationBar::RECORD_NEXT:
593 if(pParent->m_bRecordCountFinal)
595 bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
596 if (!bAvailable && pParent->GetOptions() & DbGridControl::OPT_INSERT)
597 bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
599 break;
600 case NavigationBar::RECORD_LAST:
601 if(pParent->m_bRecordCountFinal)
603 if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
604 bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
605 m_nCurrentPos != pParent->GetRowCount() - 2;
606 else
607 bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
609 break;
610 case NavigationBar::RECORD_NEW:
611 bAvailable = (pParent->GetOptions() & DbGridControl::OPT_INSERT) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
612 break;
613 case NavigationBar::RECORD_ABSOLUTE:
614 bAvailable = pParent->GetRowCount() > 0;
615 break;
617 return bAvailable;
621 void DbGridControl::NavigationBar::SetState(sal_uInt16 nWhich)
623 bool bAvailable = GetState(nWhich);
624 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
625 vcl::Window* pWnd = NULL;
626 switch (nWhich)
628 case NavigationBar::RECORD_FIRST:
629 pWnd = m_aFirstBtn.get();
630 break;
631 case NavigationBar::RECORD_PREV:
632 pWnd = m_aPrevBtn.get();
633 break;
634 case NavigationBar::RECORD_NEXT:
635 pWnd = m_aNextBtn.get();
636 break;
637 case NavigationBar::RECORD_LAST:
638 pWnd = m_aLastBtn.get();
639 break;
640 case NavigationBar::RECORD_NEW:
641 pWnd = m_aNewBtn.get();
642 break;
643 case NavigationBar::RECORD_ABSOLUTE:
644 pWnd = m_aAbsolute.get();
645 if (bAvailable)
647 if (pParent->m_nTotalCount >= 0)
649 if (pParent->IsCurrentAppending())
650 m_aAbsolute->SetMax(pParent->m_nTotalCount + 1);
651 else
652 m_aAbsolute->SetMax(pParent->m_nTotalCount);
654 else
655 m_aAbsolute->SetMax(LONG_MAX);
657 m_aAbsolute->SetValue(m_nCurrentPos + 1);
659 else
660 m_aAbsolute->SetText(OUString());
661 break;
662 case NavigationBar::RECORD_TEXT:
663 pWnd = m_aRecordText.get();
664 break;
665 case NavigationBar::RECORD_OF:
666 pWnd = m_aRecordOf.get();
667 break;
668 case NavigationBar::RECORD_COUNT:
670 pWnd = m_aRecordCount.get();
671 OUString aText;
672 if (bAvailable)
674 if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
676 if (pParent->IsCurrentAppending() && !pParent->IsModified())
677 aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount());
678 else
679 aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount() - 1);
681 else
682 aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount());
683 if(!pParent->m_bRecordCountFinal)
684 aText += " *";
686 else
687 aText.clear();
689 // add the number of selected rows, if applicable
690 if (pParent->GetSelectRowCount())
692 OUString aExtendedInfo(aText);
693 aExtendedInfo += " (";
694 aExtendedInfo += m_aAbsolute->CreateFieldText(pParent->GetSelectRowCount());
695 aExtendedInfo += ")";
696 pWnd->SetText(aExtendedInfo);
698 else
699 pWnd->SetText(aText);
701 pParent->SetRealRowCount(aText);
702 } break;
704 DBG_ASSERT(pWnd, "kein Fenster");
705 if (pWnd && (pWnd->IsEnabled() != bAvailable))
706 // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
707 // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
708 // do this check.
709 // For further explanation see Bug 69900.
710 pWnd->Enable(bAvailable);
713 void DbGridControl::NavigationBar::Resize()
715 Control::Resize();
716 ArrangeControls();
719 void DbGridControl::NavigationBar::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
721 Control::Paint(rRenderContext, rRect);
722 Point aAbsolutePos = m_aAbsolute->GetPosPixel();
723 Size aAbsoluteSize = m_aAbsolute->GetSizePixel();
725 rRenderContext.DrawLine(Point(aAbsolutePos.X() - 1, 0 ),
726 Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
728 rRenderContext.DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ),
729 Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
732 void DbGridControl::NavigationBar::StateChanged(StateChangedType nType)
734 Control::StateChanged(nType);
736 vcl::Window* pWindows[] =
738 m_aRecordText.get(),
739 m_aAbsolute.get(),
740 m_aRecordOf.get(),
741 m_aRecordCount.get(),
742 m_aFirstBtn.get(),
743 m_aPrevBtn.get(),
744 m_aNextBtn.get(),
745 m_aLastBtn.get(),
746 m_aNewBtn.get()
749 switch ( nType )
751 case StateChangedType::Mirroring:
753 bool bIsRTLEnabled = IsRTLEnabled();
754 for (size_t i=0; i < (sizeof (pWindows) / sizeof(pWindows[0])); ++i)
755 pWindows[i]->EnableRTL( bIsRTLEnabled );
757 break;
759 case StateChangedType::Zoom:
761 Fraction aZoom = GetZoom();
763 // not all of these controls need to know the new zoom, but to be sure ...
764 vcl::Font aFont(GetSettings().GetStyleSettings().GetFieldFont());
765 if (IsControlFont())
766 aFont.Merge(GetControlFont());
768 for (size_t i=0; i < sizeof(pWindows)/sizeof(pWindows[0]); ++i)
770 pWindows[i]->SetZoom(aZoom);
771 pWindows[i]->SetZoomedPointFont(*pWindows[i], aFont);
774 SetZoomedPointFont(*this, aFont);
776 // rearrange the controls
777 m_nDefaultWidth = ArrangeControls();
779 break;
780 default:;
784 DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor)
785 :m_bIsNew(false)
788 if (pCur && pCur->Is())
790 Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY);
791 for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
793 Reference< XPropertySet > xColSet(
794 xColumns->getByIndex(i), css::uno::UNO_QUERY);
795 DataColumn* pColumn = new DataColumn(xColSet);
796 m_aVariants.push_back( pColumn );
799 if (pCur->rowDeleted())
800 m_eStatus = GRS_DELETED;
801 else
803 if (bPaintCursor)
804 m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GRS_INVALID : GRS_CLEAN;
805 else
807 Reference< XPropertySet > xSet = pCur->getPropertySet();
808 if (xSet.is())
810 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
811 if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
812 m_eStatus = GRS_INVALID;
813 else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
814 m_eStatus = GRS_MODIFIED;
815 else
816 m_eStatus = GRS_CLEAN;
818 else
819 m_eStatus = GRS_INVALID;
822 if (!m_bIsNew && IsValid())
823 m_aBookmark = pCur->getBookmark();
824 else
825 m_aBookmark = Any();
827 else
828 m_eStatus = GRS_INVALID;
831 DbGridRow::~DbGridRow()
833 for ( size_t i = 0, n = m_aVariants.size(); i < n; ++i )
834 delete m_aVariants[ i ];
835 m_aVariants.clear();
838 void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor)
840 if (pCur && pCur->Is())
842 if (pCur->rowDeleted())
844 m_eStatus = GRS_DELETED;
845 m_bIsNew = false;
847 else
849 m_eStatus = GRS_CLEAN;
850 if (!bPaintCursor)
852 Reference< XPropertySet > xSet = pCur->getPropertySet();
853 DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
855 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
856 m_eStatus = GRS_MODIFIED;
857 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
859 else
860 m_bIsNew = false;
865 if (!m_bIsNew && IsValid())
866 m_aBookmark = pCur->getBookmark();
867 else
868 m_aBookmark = Any();
870 catch(SQLException&)
872 DBG_UNHANDLED_EXCEPTION();
873 m_aBookmark = Any();
874 m_eStatus = GRS_INVALID;
875 m_bIsNew = false;
878 else
880 m_aBookmark = Any();
881 m_eStatus = GRS_INVALID;
882 m_bIsNew = false;
886 DbGridControl::DbGridControl(
887 Reference< XComponentContext > _rxContext,
888 vcl::Window* pParent,
889 WinBits nBits)
890 :DbGridControl_Base(pParent, EditBrowseBoxFlags::NONE, nBits, DEFAULT_BROWSE_MODE )
891 ,m_xContext(_rxContext)
892 ,m_aBar(VclPtr<DbGridControl::NavigationBar>::Create(this))
893 ,m_nAsynAdjustEvent(0)
894 ,m_pDataSourcePropMultiplexer(NULL)
895 ,m_pDataSourcePropListener(NULL)
896 ,m_pFieldListeners(NULL)
897 ,m_pCursorDisposeListener(NULL)
898 ,m_pGridListener(NULL)
899 ,m_pDataCursor(NULL)
900 ,m_pSeekCursor(NULL)
901 ,m_nSeekPos(-1)
902 ,m_nTotalCount(-1)
903 ,m_aNullDate(::dbtools::DBTypeConversion::getStandardDate())
904 ,m_nMode(DEFAULT_BROWSE_MODE)
905 ,m_nCurrentPos(-1)
906 ,m_nDeleteEvent(0)
907 ,m_nOptions(OPT_READONLY)
908 ,m_nOptionMask(OPT_INSERT | OPT_UPDATE | OPT_DELETE)
909 ,m_nLastColId((sal_uInt16)-1)
910 ,m_nLastRowId(-1)
911 ,m_bDesignMode(false)
912 ,m_bRecordCountFinal(false)
913 ,m_bMultiSelection(true)
914 ,m_bNavigationBar(true)
915 ,m_bSynchDisplay(true)
916 ,m_bForceROController(false)
917 ,m_bHandle(true)
918 ,m_bFilterMode(false)
919 ,m_bWantDestruction(false)
920 ,m_bInAdjustDataSource(false)
921 ,m_bPendingAdjustRows(false)
922 ,m_bHideScrollbars( false )
923 ,m_bUpdating(false)
926 OUString sName(SVX_RESSTR(RID_STR_NAVIGATIONBAR));
927 m_aBar->SetAccessibleName(sName);
928 m_aBar->Show();
929 ImplInitWindow( InitAll );
932 void DbGridControl::InsertHandleColumn()
934 // BrowseBox has problems when painting without a handleColumn (hide it here)
935 if (HasHandle())
936 BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString()));
937 else
938 BrowseBox::InsertHandleColumn(0);
941 void DbGridControl::Init()
943 BrowserHeader* pNewHeader = CreateHeaderBar(this);
944 pHeader->SetMouseTransparent(false);
946 SetHeaderBar(pNewHeader);
947 SetMode(m_nMode);
948 SetCursorColor(Color(0xFF, 0, 0));
950 InsertHandleColumn();
953 DbGridControl::~DbGridControl()
955 disposeOnce();
958 void DbGridControl::dispose()
960 if (!IsDisposed())
962 RemoveColumns();
964 m_bWantDestruction = true;
965 osl::MutexGuard aGuard(m_aDestructionSafety);
966 if (m_pFieldListeners)
967 DisconnectFromFields();
968 if (m_pCursorDisposeListener)
970 delete m_pCursorDisposeListener;
971 m_pCursorDisposeListener = NULL;
975 if (m_nDeleteEvent)
976 Application::RemoveUserEvent(m_nDeleteEvent);
978 if (m_pDataSourcePropMultiplexer)
980 m_pDataSourcePropMultiplexer->dispose();
981 m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
982 delete m_pDataSourcePropListener;
983 m_pDataSourcePropMultiplexer = NULL;
984 m_pDataSourcePropListener = NULL;
986 m_xRowSetListener.clear();
988 delete m_pDataCursor;
989 m_pDataCursor = NULL;
990 delete m_pSeekCursor;
991 m_pSeekCursor = NULL;
993 m_aBar.disposeAndClear();
995 DbGridControl_Base::dispose();
998 void DbGridControl::StateChanged( StateChangedType nType )
1000 DbGridControl_Base::StateChanged( nType );
1002 switch (nType)
1004 case StateChangedType::Mirroring:
1005 ImplInitWindow( InitWritingMode );
1006 Invalidate();
1007 break;
1009 case StateChangedType::Zoom:
1011 ImplInitWindow( InitFontFacet );
1013 // and give it a chance to rearrange
1014 Point aPoint = GetControlArea().TopLeft();
1015 sal_uInt16 nX = (sal_uInt16)aPoint.X();
1016 ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1017 ReserveControlArea((sal_uInt16)nX);
1019 break;
1020 case StateChangedType::ControlFont:
1021 ImplInitWindow( InitFontFacet );
1022 Invalidate();
1023 break;
1024 case StateChangedType::ControlForeground:
1025 ImplInitWindow( InitForeground );
1026 Invalidate();
1027 break;
1028 case StateChangedType::ControlBackground:
1029 ImplInitWindow( InitBackground );
1030 Invalidate();
1031 break;
1032 default:;
1036 void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
1038 DbGridControl_Base::DataChanged( rDCEvt );
1039 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS ) &&
1040 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
1042 ImplInitWindow( InitAll );
1043 Invalidate();
1047 void DbGridControl::Select()
1049 DbGridControl_Base::Select();
1051 // as the selected rows may have changed, update the according display in our navigation bar
1052 m_aBar->InvalidateState(NavigationBar::RECORD_COUNT);
1054 if (m_pGridListener)
1055 m_pGridListener->selectionChanged();
1058 void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
1060 for ( size_t i = 0; i < m_aColumns.size(); ++i )
1062 DbGridColumn* pCol = m_aColumns[ i ];
1063 if (pCol)
1064 pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
1067 if ( ( _eInitWhat & InitWritingMode ) != 0 )
1069 if ( m_bNavigationBar )
1071 m_aBar->EnableRTL( IsRTLEnabled() );
1075 if ( ( _eInitWhat & InitFontFacet ) != 0 )
1077 if ( m_bNavigationBar )
1079 vcl::Font aFont = m_aBar->GetSettings().GetStyleSettings().GetFieldFont();
1080 if ( IsControlFont() )
1081 m_aBar->SetControlFont( GetControlFont() );
1082 else
1083 m_aBar->SetControlFont();
1085 m_aBar->SetZoom( GetZoom() );
1089 if ( ( _eInitWhat & InitBackground ) != 0 )
1091 if (IsControlBackground())
1093 GetDataWindow().SetBackground(GetControlBackground());
1094 GetDataWindow().SetControlBackground(GetControlBackground());
1095 GetDataWindow().SetFillColor(GetControlBackground());
1097 else
1099 GetDataWindow().SetControlBackground();
1100 GetDataWindow().SetFillColor(GetFillColor());
1105 void DbGridControl::RemoveRows(bool bNewCursor)
1107 // Did the data cursor change?
1108 if (!bNewCursor)
1110 DELETEZ(m_pSeekCursor);
1111 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1112 m_nCurrentPos = m_nSeekPos = -1;
1113 m_nOptions = OPT_READONLY;
1115 RowRemoved(0, GetRowCount(), false);
1116 m_nTotalCount = -1;
1118 else
1120 RemoveRows();
1124 void DbGridControl::RemoveRows()
1126 // we're going to remove all columns and all row, so deactivate the current cell
1127 if (IsEditing())
1128 DeactivateCell();
1130 // de-initialize all columns
1131 // if there are columns, free all controllers
1132 for (size_t i = 0; i < m_aColumns.size(); i++)
1133 m_aColumns[ i ]->Clear();
1135 DELETEZ(m_pSeekCursor);
1136 DELETEZ(m_pDataCursor);
1138 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1139 m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1;
1140 m_nOptions = OPT_READONLY;
1142 // reset number of sentences to zero in the browser
1143 DbGridControl_Base::RemoveRows();
1144 m_aBar->InvalidateAll(m_nCurrentPos, true);
1147 void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
1149 // positioning of the controls
1150 if (m_bNavigationBar)
1152 nX = m_aBar->GetDefaultWidth();
1153 Rectangle aRect(GetControlArea());
1154 m_aBar->SetPosSizePixel(Point(0,nY + 1), Size(nX, aRect.GetSize().Height() - 1));
1158 void DbGridControl::EnableHandle(bool bEnable)
1160 if (m_bHandle == bEnable)
1161 return;
1163 // HandleColumn is only hidden because there are a lot of problems while painting otherwise
1164 RemoveColumn( HandleColumnId );
1165 m_bHandle = bEnable;
1166 InsertHandleColumn();
1169 namespace
1171 bool adjustModeForScrollbars( BrowserMode& _rMode, bool _bNavigationBar, bool _bHideScrollbars )
1173 BrowserMode nOldMode = _rMode;
1175 if ( !_bNavigationBar )
1177 _rMode &= ~BrowserMode::AUTO_HSCROLL;
1180 if ( _bHideScrollbars )
1182 _rMode |= ( BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL );
1183 _rMode &= ~BrowserMode( BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL );
1185 else
1187 _rMode |= ( BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL );
1188 _rMode &= ~BrowserMode( BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL );
1191 // note: if we have a navigation bar, we always have a AUTO_HSCROLL. In particular,
1192 // _bHideScrollbars is ignored then
1193 if ( _bNavigationBar )
1195 _rMode |= BrowserMode::AUTO_HSCROLL;
1196 _rMode &= ~BrowserMode::NO_HSCROLL;
1199 return nOldMode != _rMode;
1203 void DbGridControl::EnableNavigationBar(bool bEnable)
1205 if (m_bNavigationBar == bEnable)
1206 return;
1208 m_bNavigationBar = bEnable;
1210 if (bEnable)
1212 m_aBar->Show();
1213 m_aBar->Enable();
1214 m_aBar->InvalidateAll(m_nCurrentPos, true);
1216 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1217 SetMode( m_nMode );
1219 // get size of the reserved ControlArea
1220 Point aPoint = GetControlArea().TopLeft();
1221 sal_uInt16 nX = (sal_uInt16)aPoint.X();
1223 ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1224 ReserveControlArea((sal_uInt16)nX);
1226 else
1228 m_aBar->Hide();
1229 m_aBar->Disable();
1231 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1232 SetMode( m_nMode );
1234 ReserveControlArea();
1238 sal_uInt16 DbGridControl::SetOptions(sal_uInt16 nOpt)
1240 DBG_ASSERT(!m_xCurrentRow || !m_xCurrentRow->IsModified(),
1241 "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
1243 // for the next setDataSource (which is triggered by a refresh, for instance)
1244 m_nOptionMask = nOpt;
1246 // normalize the new options
1247 Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
1248 if (xDataSourceSet.is())
1250 // check what kind of options are available
1251 sal_Int32 nPrivileges = 0;
1252 xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1253 if ((nPrivileges & Privilege::INSERT) == 0)
1254 nOpt &= ~OPT_INSERT;
1255 if ((nPrivileges & Privilege::UPDATE) == 0)
1256 nOpt &= ~OPT_UPDATE;
1257 if ((nPrivileges & Privilege::DELETE) == 0)
1258 nOpt &= ~OPT_DELETE;
1260 else
1261 nOpt = OPT_READONLY;
1263 // need to do something after that ?
1264 if (nOpt == m_nOptions)
1265 return m_nOptions;
1267 // the 'update' option only affects our BrowserMode (with or w/o focus rect)
1268 BrowserMode nNewMode = m_nMode;
1269 if (!(m_nMode & BrowserMode::CURSOR_WO_FOCUS))
1271 if (nOpt & OPT_UPDATE)
1272 nNewMode |= BrowserMode::HIDECURSOR;
1273 else
1274 nNewMode &= ~BrowserMode::HIDECURSOR;
1276 else
1277 nNewMode &= ~BrowserMode::HIDECURSOR;
1278 // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
1280 if (nNewMode != m_nMode)
1282 SetMode(nNewMode);
1283 m_nMode = nNewMode;
1286 // _after_ setting the mode because this results in an ActivateCell
1287 DeactivateCell();
1289 bool bInsertChanged = (nOpt & OPT_INSERT) != (m_nOptions & OPT_INSERT);
1290 m_nOptions = nOpt;
1291 // we need to set this before the code below because it indirectly uses m_nOptions
1293 // the 'insert' option affects our empty row
1294 if (bInsertChanged)
1296 if (m_nOptions & OPT_INSERT)
1297 { // the insert option is to be set
1298 m_xEmptyRow = new DbGridRow();
1299 RowInserted(GetRowCount(), 1, true);
1301 else
1302 { // the insert option is to be reset
1303 m_xEmptyRow = NULL;
1304 if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
1305 GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
1306 RowRemoved(GetRowCount(), 1, true);
1310 // the 'delete' options has no immediate consequences
1312 ActivateCell();
1313 Invalidate();
1314 return m_nOptions;
1317 void DbGridControl::ForceHideScrollbars( bool _bForce )
1319 if ( m_bHideScrollbars == _bForce )
1320 return;
1322 m_bHideScrollbars = _bForce;
1324 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1325 SetMode( m_nMode );
1328 void DbGridControl::EnablePermanentCursor(bool bEnable)
1330 if (IsPermanentCursorEnabled() == bEnable)
1331 return;
1333 if (bEnable)
1335 m_nMode &= ~BrowserMode::HIDECURSOR; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect
1336 m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
1338 else
1340 if (m_nOptions & OPT_UPDATE)
1341 m_nMode |= BrowserMode::HIDECURSOR; // no cursor at all
1342 else
1343 m_nMode &= ~BrowserMode::HIDECURSOR; // at least the "non-permanent" cursor
1345 m_nMode &= ~BrowserMode::CURSOR_WO_FOCUS;
1347 SetMode(m_nMode);
1349 bool bWasEditing = IsEditing();
1350 DeactivateCell();
1351 if (bWasEditing)
1352 ActivateCell();
1355 bool DbGridControl::IsPermanentCursorEnabled() const
1357 return (m_nMode & BrowserMode::CURSOR_WO_FOCUS) && !(m_nMode & BrowserMode::HIDECURSOR);
1360 void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
1362 if ((GetCurColumnId() == _nColId) && IsEditing())
1363 { // the controller which is currently active needs to be refreshed
1364 DeactivateCell();
1365 ActivateCell();
1369 void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, sal_uInt16 nOpts)
1371 if (!_xCursor.is() && !m_pDataCursor)
1372 return;
1374 if (m_pDataSourcePropMultiplexer)
1376 m_pDataSourcePropMultiplexer->dispose();
1377 m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
1378 delete m_pDataSourcePropListener;
1379 m_pDataSourcePropMultiplexer = NULL;
1380 m_pDataSourcePropListener = NULL;
1382 m_xRowSetListener.clear();
1384 // is the new cursor valid ?
1385 // the cursor is only valid if it contains some columns
1386 // if there is no cursor or the cursor is not valid we have to clean up an leave
1387 if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY)->getColumns()->hasElements())
1389 RemoveRows();
1390 return;
1393 // did the data cursor change?
1394 sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
1396 SetUpdateMode(false);
1397 RemoveRows();
1398 DisconnectFromFields();
1400 DELETEZ(m_pCursorDisposeListener);
1403 ::osl::MutexGuard aGuard(m_aAdjustSafety);
1404 if (m_nAsynAdjustEvent)
1406 // the adjust was thought to work with the old cursor which we don't have anymore
1407 RemoveUserEvent(m_nAsynAdjustEvent);
1408 m_nAsynAdjustEvent = 0;
1412 // get a new formatter and data cursor
1413 m_xFormatter = NULL;
1414 Reference< ::com::sun::star::util::XNumberFormatsSupplier > xSupplier = getNumberFormats(getConnection(_xCursor), true);
1415 if (xSupplier.is())
1417 m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter >(
1418 ::com::sun::star::util::NumberFormatter::create(m_xContext),
1419 UNO_QUERY);
1420 m_xFormatter->attachNumberFormatsSupplier(xSupplier);
1422 // retrieve the datebase of the Numberformatter
1425 xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
1427 catch(Exception&)
1432 m_pDataCursor = new CursorWrapper(_xCursor);
1434 // now create a cursor for painting rows
1435 // we need that cursor only if we are not in insert only mode
1436 Reference< XResultSet > xClone;
1437 Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
1440 xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
1442 catch(Exception&)
1445 if (xClone.is())
1446 m_pSeekCursor = new CursorWrapper(xClone);
1448 // property listening on the data source
1449 // (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
1450 // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
1451 // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
1452 // and forwards the property changes to a our special method "DataSourcePropertyChanged".)
1453 if (m_pDataCursor)
1455 m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
1456 m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
1457 m_pDataSourcePropMultiplexer->acquire();
1458 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
1459 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
1462 BrowserMode nOldMode = m_nMode;
1463 if (m_pSeekCursor)
1467 Reference< XPropertySet > xSet(_xCursor, UNO_QUERY);
1468 if (xSet.is())
1470 // check what kind of options are available
1471 sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
1472 xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
1474 if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
1476 sal_Int32 nPrivileges = 0;
1477 xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1479 // Insert Option should be set if insert only otherwise you won't see any rows
1480 // and no insertion is possible
1481 if ((m_nOptionMask & OPT_INSERT) && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & OPT_INSERT))
1482 m_nOptions |= OPT_INSERT;
1483 if ((m_nOptionMask & OPT_UPDATE) && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & OPT_UPDATE))
1484 m_nOptions |= OPT_UPDATE;
1485 if ((m_nOptionMask & OPT_DELETE) && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & OPT_DELETE))
1486 m_nOptions |= OPT_DELETE;
1490 catch( const Exception& )
1492 DBG_UNHANDLED_EXCEPTION();
1495 bool bPermanentCursor = IsPermanentCursorEnabled();
1496 m_nMode = DEFAULT_BROWSE_MODE;
1498 if ( bPermanentCursor )
1500 m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
1501 m_nMode &= ~BrowserMode::HIDECURSOR;
1503 else
1505 // updates are allowed -> no focus rectangle
1506 if ( m_nOptions & OPT_UPDATE )
1507 m_nMode |= BrowserMode::HIDECURSOR;
1510 if ( m_bMultiSelection )
1511 m_nMode |= BrowserMode::MULTISELECTION;
1512 else
1513 m_nMode &= ~BrowserMode::MULTISELECTION;
1515 adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
1517 Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY);
1518 if (xSupplyColumns.is())
1519 InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
1521 ConnectToFields();
1524 sal_uInt32 nRecordCount(0);
1526 if (m_pSeekCursor)
1528 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1529 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1530 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1532 m_xRowSetListener = new RowSetEventListener(this);
1533 Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
1534 if ( xChangeBroad.is( ) )
1535 xChangeBroad->addRowsChangeListener(m_xRowSetListener);
1538 // insert the currently known rows
1539 // and one row if we are able to insert rows
1540 if (m_nOptions & OPT_INSERT)
1542 // insert the empty row for insertion
1543 m_xEmptyRow = new DbGridRow();
1544 ++nRecordCount;
1546 if (nRecordCount)
1548 m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor, true);
1549 m_xDataRow = new DbGridRow(m_pDataCursor, false);
1550 RowInserted(0, nRecordCount, false);
1552 if (m_xSeekRow->IsValid())
1555 m_nSeekPos = m_pSeekCursor->getRow() - 1;
1557 catch( const Exception& )
1559 DBG_UNHANDLED_EXCEPTION();
1560 m_nSeekPos = -1;
1563 else
1565 // no rows so we don't need a seekcursor
1566 DELETEZ(m_pSeekCursor);
1570 // go to the old column
1571 if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
1572 nCurPos = 0;
1574 // Column zero is a valid choice and guaranteed to exist,
1575 // but invisible to the user; if we have at least one
1576 // user-visible column, go to that one.
1577 if (nCurPos == 0 && ColCount() > 1)
1578 nCurPos = 1;
1580 // there are rows so go to the selected current column
1581 if (nRecordCount)
1582 GoToRowColumnId(0, GetColumnId(nCurPos));
1583 // else stop the editing if necessary
1584 else if (IsEditing())
1585 DeactivateCell();
1587 // now reset the mode
1588 if (m_nMode != nOldMode)
1589 SetMode(m_nMode);
1591 // RecalcRows was already called while resizing
1592 if (!IsResizing() && GetRowCount())
1593 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1595 m_aBar->InvalidateAll(m_nCurrentPos, true);
1596 SetUpdateMode(true);
1598 // start listening on the seek cursor
1599 if (m_pSeekCursor)
1600 m_pCursorDisposeListener = new DisposeListenerGridBridge(*this, Reference< XComponent > (Reference< XInterface >(*m_pSeekCursor), UNO_QUERY), 0);
1603 void DbGridControl::RemoveColumns()
1605 if ( IsEditing() )
1606 DeactivateCell();
1608 for (size_t i = 0, n = m_aColumns.size(); i < n; i++)
1609 delete m_aColumns[ i ];
1610 m_aColumns.clear();
1612 DbGridControl_Base::RemoveColumns();
1615 DbGridColumn* DbGridControl::CreateColumn(sal_uInt16 nId) const
1617 return new DbGridColumn(nId, *const_cast<DbGridControl*>(this));
1620 sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
1622 DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
1623 sal_uInt16 nRealPos = nModelPos;
1624 if (nModelPos != HEADERBAR_APPEND)
1626 // calc the view pos. we can't use our converting functions because the new column
1627 // has no VCL-representation, yet.
1628 sal_Int16 nViewPos = nModelPos;
1629 while (nModelPos--)
1631 if ( m_aColumns[ nModelPos ]->IsHidden() )
1632 --nViewPos;
1634 // restore nModelPos, we need it later
1635 nModelPos = nRealPos;
1636 // the position the base class gets is the view pos + 1 (because of the handle column)
1637 nRealPos = nViewPos + 1;
1640 // calculate the new id
1641 for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && (nId <= m_aColumns.size()); ++nId)
1643 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !");
1644 // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
1646 DbGridControl_Base::AppendColumn(rName, nWidth, nRealPos, nId);
1647 if (nModelPos == HEADERBAR_APPEND)
1648 m_aColumns.push_back( CreateColumn(nId) );
1649 else
1651 DbGridColumns::iterator it = m_aColumns.begin();
1652 ::std::advance( it, nModelPos );
1653 m_aColumns.insert( it, CreateColumn(nId) );
1656 return nId;
1659 void DbGridControl::RemoveColumn(sal_uInt16 nId)
1661 DbGridControl_Base::RemoveColumn(nId);
1663 const sal_uInt16 nIndex = GetModelColumnPos(nId);
1664 if(nIndex != GRID_COLUMN_NOT_FOUND)
1666 delete m_aColumns[nIndex];
1667 m_aColumns.erase( m_aColumns.begin()+nIndex );
1671 void DbGridControl::ColumnMoved(sal_uInt16 nId)
1673 DbGridControl_Base::ColumnMoved(nId);
1675 // remove the col from the model
1676 sal_uInt16 nOldModelPos = GetModelColumnPos(nId);
1677 #ifdef DBG_UTIL
1678 DbGridColumn* pCol = m_aColumns[ (sal_uInt32)nOldModelPos ];
1679 DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
1680 #endif
1682 // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
1683 // so the method won't work (in fact it would return the old model pos)
1685 // the new view pos is calculated easily
1686 sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
1688 // from that we can compute the new model pos
1689 sal_uInt16 nNewModelPos;
1690 for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
1692 if (!m_aColumns[ nNewModelPos ]->IsHidden())
1694 if (!nNewViewPos)
1695 break;
1696 else
1697 --nNewViewPos;
1700 DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
1702 // this will work. of course the model isn't fully consistent with our view right now, but let's
1703 // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
1704 // other case we can use analogue arguments).
1705 // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
1706 // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
1707 // within this range is constant, so we may calculate the view pos from the model pos in the above way.
1709 // for instance, let's look at a grid with six columns where the third one is hidden. this will
1710 // initially look like this :
1712 // +---+---+---+---+---+---+
1713 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1714 // +---+---+---+---+---+---+
1715 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1716 // +---+---+---+---+---+---+
1717 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1718 // +---+---+---+---+---+---+
1720 // if we move the column at (view) pos 1 to (view) pos 3 we have :
1722 // +---+---+---+---+---+---+
1723 // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet
1724 // +---+---+---+---+---+---+
1725 // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes
1726 // +---+---+---+---+---+---+
1727 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1728 // +---+---+---+---+---+---+
1730 // or, sorted by the out-of-date model positions :
1732 // +---+---+---+---+---+---+
1733 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1734 // +---+---+---+---+---+---+
1735 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1736 // +---+---+---+---+---+---+
1737 // view pos | 0 | 3 | - | 1 | 2 | 4 |
1738 // +---+---+---+---+---+---+
1740 // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
1741 // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
1742 // exactly the pos where we have to re-insert our column's model, so it looks ike this :
1744 // +---+---+---+---+---+---+
1745 // model pos | 0 |*1*| 2 | 3 | 4 | 5 |
1746 // +---+---+---+---+---+---+
1747 // ID | 1 | 3 | 4 | 5 | 2 | 6 |
1748 // +---+---+---+---+---+---+
1749 // view pos | 0 | - | 1 | 2 | 3 | 4 |
1750 // +---+---+---+---+---+---+
1752 // Now, all is consistent again.
1753 // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
1754 // the user expected the latter but there really is no good argument against our method ;) ...)
1756 // And no, this large explanation isn't just because I wanted to play a board game or something like
1757 // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
1758 // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
1760 DbGridColumn* temp = m_aColumns[ nOldModelPos ];
1762 DbGridColumns::iterator it = m_aColumns.begin();
1763 ::std::advance( it, nOldModelPos );
1764 m_aColumns.erase( it );
1766 it = m_aColumns.begin();
1767 ::std::advance( it, nNewModelPos );
1768 m_aColumns.insert( it, temp );
1771 bool DbGridControl::SeekRow(long nRow)
1773 // in filter mode or in insert only mode we don't have any cursor!
1774 if ( !SeekCursor( nRow ) )
1775 return false;
1777 if ( IsFilterMode() )
1779 DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
1780 m_xPaintRow = m_xEmptyRow;
1782 else
1784 // on the current position we have to take the current row for display as we want
1785 // to have the most recent values for display
1786 if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
1787 m_xPaintRow = m_xCurrentRow;
1788 // seek to the empty insert row
1789 else if ( IsInsertionRow( nRow ) )
1790 m_xPaintRow = m_xEmptyRow;
1791 else
1793 m_xSeekRow->SetState( m_pSeekCursor, true );
1794 m_xPaintRow = m_xSeekRow;
1798 DbGridControl_Base::SeekRow(nRow);
1800 return m_nSeekPos >= 0;
1803 // Is called whenever the visible amount of data changes
1804 void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen )
1806 RecalcRows(nNewTopRow, nLinesOnScreen, false);
1809 void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
1811 // Wenn kein Cursor -> keine Rows im Browser.
1812 if (!m_pSeekCursor)
1814 DBG_ASSERT(GetRowCount() == 0,"DbGridControl: ohne Cursor darf es keine Rows geben");
1815 return;
1818 // ignore any implicitly made updates
1819 bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
1820 if (bDisablePaint)
1821 EnablePaint(false);
1823 // adjust cache to the visible area
1824 Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
1825 sal_Int32 nCacheSize = 0;
1826 xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
1827 bool bCacheAligned = false;
1828 // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
1829 // positioned on the first sentence
1830 long nDelta = nNewTopRow - GetTopRow();
1831 // limit for relative positioning
1832 long nLimit = (nCacheSize) ? nCacheSize / 2 : 0;
1834 // more lines on screen than in cache
1835 if (nLimit < nLinesOnScreen)
1837 Any aCacheSize;
1838 aCacheSize <<= sal_Int32(nLinesOnScreen*2);
1839 xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
1840 // here we need to update the cursor for sure
1841 bUpdateCursor = true;
1842 bCacheAligned = true;
1843 nLimit = nLinesOnScreen;
1846 // In the following, all positionings are done as it is
1847 // ensured that there are enough lines in the data cache
1849 // window goes downwards with less than two windows difference or
1850 // the cache was updated and no rowcount yet
1851 if (nDelta < nLimit && (nDelta > 0
1852 || (bCacheAligned && m_nTotalCount < 0)) )
1853 SeekCursor(nNewTopRow + nLinesOnScreen - 1, false);
1854 else if (nDelta < 0 && std::abs(nDelta) < nLimit)
1855 SeekCursor(nNewTopRow, false);
1856 else if (nDelta != 0 || bUpdateCursor)
1857 SeekCursor(nNewTopRow, true);
1859 AdjustRows();
1861 // ignore any updates implicit made
1862 EnablePaint(true);
1865 void DbGridControl::RowInserted(long nRow, long nNumRows, bool bDoPaint, bool bKeepSelection)
1867 if (nNumRows)
1869 if (m_bRecordCountFinal && m_nTotalCount < 0)
1871 // if we have an insert row we have to reduce to count by 1
1872 // as the total count reflects only the existing rows in database
1873 m_nTotalCount = GetRowCount() + nNumRows;
1874 if (m_xEmptyRow.Is())
1875 --m_nTotalCount;
1877 else if (m_nTotalCount >= 0)
1878 m_nTotalCount += nNumRows;
1880 DbGridControl_Base::RowInserted(nRow, nNumRows, bDoPaint, bKeepSelection);
1881 m_aBar->InvalidateState(NavigationBar::RECORD_COUNT);
1885 void DbGridControl::RowRemoved(long nRow, long nNumRows, bool bDoPaint)
1887 if (nNumRows)
1889 if (m_bRecordCountFinal && m_nTotalCount < 0)
1891 m_nTotalCount = GetRowCount() - nNumRows;
1892 // if we have an insert row reduce by 1
1893 if (m_xEmptyRow.Is())
1894 --m_nTotalCount;
1896 else if (m_nTotalCount >= 0)
1897 m_nTotalCount -= nNumRows;
1899 DbGridControl_Base::RowRemoved(nRow, nNumRows, bDoPaint);
1900 m_aBar->InvalidateState(NavigationBar::RECORD_COUNT);
1904 void DbGridControl::AdjustRows()
1906 if (!m_pSeekCursor)
1907 return;
1909 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1911 // refresh RecordCount
1912 sal_Int32 nRecordCount = 0;
1913 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1914 if (!m_bRecordCountFinal)
1915 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1917 // Did the number of rows change?
1918 // Here we need to consider that there might be an additional row for adding new data sets
1920 // add additional AppendRow for insertion
1921 if (m_nOptions & OPT_INSERT)
1922 ++nRecordCount;
1924 // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
1925 if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
1926 m_xCurrentRow->IsNew())
1927 ++nRecordCount;
1928 // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this
1929 // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount
1930 // and a second time here (60787 - FS)
1932 if (nRecordCount != GetRowCount())
1934 long nDelta = GetRowCount() - (long)nRecordCount;
1935 if (nDelta > 0) // too many
1937 RowRemoved(GetRowCount() - nDelta, nDelta, false);
1938 // some rows are gone, thus, repaint starting at the current position
1939 Invalidate();
1941 sal_Int32 nNewPos = AlignSeekCursor();
1942 if (m_bSynchDisplay)
1943 DbGridControl_Base::GoToRow(nNewPos);
1945 SetCurrent(nNewPos);
1946 // there are rows so go to the selected current column
1947 if (nRecordCount)
1948 GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
1949 if (!IsResizing() && GetRowCount())
1950 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1951 m_aBar->InvalidateAll(m_nCurrentPos, true);
1953 else // too few
1954 RowInserted(GetRowCount(), -nDelta, true);
1957 if (m_bRecordCountFinal && m_nTotalCount < 0)
1959 if (m_nOptions & OPT_INSERT)
1960 m_nTotalCount = GetRowCount() - 1;
1961 else
1962 m_nTotalCount = GetRowCount();
1964 m_aBar->InvalidateState(NavigationBar::RECORD_COUNT);
1967 DbGridControl_Base::RowStatus DbGridControl::GetRowStatus(long nRow) const
1969 if (IsFilterRow(nRow))
1970 return DbGridControl_Base::FILTER;
1971 else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
1973 // neue Zeile
1974 if (!IsValid(m_xCurrentRow))
1975 return DbGridControl_Base::DELETED;
1976 else if (IsModified())
1977 return DbGridControl_Base::MODIFIED;
1978 else if (m_xCurrentRow->IsNew())
1979 return DbGridControl_Base::CURRENTNEW;
1980 else
1981 return DbGridControl_Base::CURRENT;
1983 else if (IsInsertionRow(nRow))
1984 return DbGridControl_Base::NEW;
1985 else if (!IsValid(m_xSeekRow))
1986 return DbGridControl_Base::DELETED;
1987 else
1988 return DbGridControl_Base::CLEAN;
1991 void DbGridControl::PaintStatusCell(OutputDevice& rDev, const Rectangle& rRect) const
1993 DbGridControl_Base::PaintStatusCell(rDev, rRect);
1996 void DbGridControl::PaintCell(OutputDevice& rDev, const Rectangle& rRect, sal_uInt16 nColumnId) const
1998 if (!IsValid(m_xPaintRow))
1999 return;
2001 size_t Location = GetModelColumnPos(nColumnId);
2002 DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2003 if (pColumn)
2005 Rectangle aArea(rRect);
2006 if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS)
2008 aArea.Top() += 1;
2009 aArea.Bottom() -= 1;
2011 pColumn->Paint(rDev, aArea, m_xPaintRow, getNumberFormatter());
2015 bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol)
2018 DeactivateCell( false );
2020 if ( m_pDataCursor
2021 && ( m_nCurrentPos != nNewRow )
2022 && !SetCurrent( nNewRow )
2025 ActivateCell();
2026 return false;
2029 if ( !DbGridControl_Base::CursorMoving( nNewRow, nNewCol ) )
2030 return false;
2032 return true;
2035 bool DbGridControl::SetCurrent(long nNewRow)
2037 // Each movement of the datacursor must start with BeginCursorAction and end with
2038 // EndCursorAction to block all notifications during the movement
2039 BeginCursorAction();
2043 // compare positions
2044 if (SeekCursor(nNewRow))
2046 if (IsFilterRow(nNewRow)) // special mode for filtering
2048 m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
2049 m_nCurrentPos = nNewRow;
2051 else
2053 bool bNewRowInserted = false;
2054 // Should we go to the insertrow ?
2055 if (IsInsertionRow(nNewRow))
2057 // to we need to move the cursor to the insert row?
2058 // we need to insert the if the current row isn't the insert row or if the
2059 // cursor triggered the move by itselt and we need a reinitialization of the row
2060 Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
2061 if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
2063 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
2064 xUpdateCursor->moveToInsertRow();
2066 bNewRowInserted = true;
2068 else
2071 if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
2073 Any aBookmark = m_pSeekCursor->getBookmark();
2074 if (!m_xCurrentRow || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
2076 // adjust the cursor to the new desired row
2077 if (!m_pDataCursor->moveToBookmark(aBookmark))
2079 EndCursorAction();
2080 return false;
2085 m_xDataRow->SetState(m_pDataCursor, false);
2086 m_xCurrentRow = m_xDataRow;
2088 long nPaintPos = -1;
2089 // do we have to repaint the last regular row in case of setting defaults or autovalues
2090 if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
2091 nPaintPos = m_nCurrentPos;
2093 m_nCurrentPos = nNewRow;
2095 // repaint the new row to display all defaults
2096 if (bNewRowInserted)
2097 RowModified(m_nCurrentPos);
2098 if (nPaintPos >= 0)
2099 RowModified(nPaintPos);
2102 else
2104 OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
2105 EndCursorAction();
2106 return false;
2109 catch ( const Exception& )
2111 DBG_UNHANDLED_EXCEPTION();
2112 EndCursorAction();
2113 return false;
2116 EndCursorAction();
2117 return true;
2120 void DbGridControl::CursorMoved()
2123 // cursor movement due to deletion or insertion of rows
2124 if (m_pDataCursor && m_nCurrentPos != GetCurRow())
2126 DeactivateCell(true);
2127 SetCurrent(GetCurRow());
2130 DbGridControl_Base::CursorMoved();
2131 m_aBar->InvalidateAll(m_nCurrentPos);
2133 // select the new column when they moved
2134 if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
2136 SelectColumnId( GetCurColumnId() );
2139 if ( m_nLastColId != GetCurColumnId() )
2140 onColumnChange();
2141 m_nLastColId = GetCurColumnId();
2143 if ( m_nLastRowId != GetCurRow() )
2144 onRowChange();
2145 m_nLastRowId = GetCurRow();
2148 void DbGridControl::onRowChange()
2150 // not interested in
2153 void DbGridControl::onColumnChange()
2155 if ( m_pGridListener )
2156 m_pGridListener->columnChanged();
2159 void DbGridControl::setDisplaySynchron(bool bSync)
2161 if (bSync != m_bSynchDisplay)
2163 m_bSynchDisplay = bSync;
2164 if (m_bSynchDisplay)
2165 AdjustDataSource(false);
2169 void DbGridControl::AdjustDataSource(bool bFull)
2171 SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
2172 SolarMutexGuard aGuard;
2173 // If the current row is recalculated at the moment, do not adjust
2175 if (bFull)
2176 m_xCurrentRow = NULL;
2177 // if we are on the same row only repaint
2178 // but this is only possible for rows which are not inserted, in that case the comparison result
2179 // may not be correct
2180 else
2181 if ( m_xCurrentRow.Is()
2182 && !m_xCurrentRow->IsNew()
2183 && !m_pDataCursor->isBeforeFirst()
2184 && !m_pDataCursor->isAfterLast()
2185 && !m_pDataCursor->rowDeleted()
2188 bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
2190 bool bDataCursorIsOnNew = false;
2191 m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
2193 if ( bEqualBookmarks && !bDataCursorIsOnNew )
2195 // position of my data cursor is the same as the position our current row points tpo
2196 // sync the status, repaint, done
2197 DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Fehler in den Datenzeilen");
2198 SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
2199 RowModified(m_nCurrentPos);
2200 return;
2204 // away from the data cursor's row
2205 if (m_xPaintRow == m_xCurrentRow)
2206 m_xPaintRow = m_xSeekRow;
2208 // not up-to-date row, thus, adjust completely
2209 if (!m_xCurrentRow)
2210 AdjustRows();
2212 sal_Int32 nNewPos = AlignSeekCursor();
2213 if (nNewPos < 0)// could not find any position
2214 return;
2216 m_bInAdjustDataSource = true;
2217 if (nNewPos != m_nCurrentPos)
2219 if (m_bSynchDisplay)
2220 DbGridControl_Base::GoToRow(nNewPos);
2222 if (!m_xCurrentRow.Is())
2223 // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned
2224 // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what
2225 // CurrentRow is corrected to point two rows down, so that GoToRow will point into
2226 // emptiness (since we are - purportedly - at the correct position)
2227 SetCurrent(nNewPos);
2229 else
2231 SetCurrent(nNewPos);
2232 RowModified(nNewPos);
2234 m_bInAdjustDataSource = false;
2236 // if the data cursor was moved from outside, this section is voided
2237 SetNoSelection();
2238 m_aBar->InvalidateAll(m_nCurrentPos, m_xCurrentRow.Is());
2241 sal_Int32 DbGridControl::AlignSeekCursor()
2243 // position SeekCursor onto the data cursor, no data transmission
2245 if (!m_pSeekCursor)
2246 return -1;
2248 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
2250 // now align the seek cursor and the data cursor
2251 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
2252 m_nSeekPos = GetRowCount() - 1;
2253 else
2257 if ( m_pDataCursor->isBeforeFirst() )
2259 // this is somewhat strange, but can nevertheless happen
2260 DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
2261 m_pSeekCursor->first();
2262 m_pSeekCursor->previous();
2263 m_nSeekPos = -1;
2265 else if ( m_pDataCursor->isAfterLast() )
2267 DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
2268 m_pSeekCursor->last();
2269 m_pSeekCursor->next();
2270 m_nSeekPos = -1;
2272 else
2274 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2275 if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
2276 // unfortunately, moveToBookmark might lead to a re-positioning of the seek
2277 // cursor (if the complex moveToBookmark with all its events fires an update
2278 // somewhere) -> retry
2279 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2280 // Now there is still the chance of a failure but it is less likely.
2281 // The alternative would be an loop until everything is fine - no good solution...
2282 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2285 catch(Exception&)
2289 return m_nSeekPos;
2292 bool DbGridControl::SeekCursor(long nRow, bool bAbsolute)
2294 // position SeekCursor onto the data cursor, no data transmission
2296 // additions for the filtermode
2297 if (IsFilterRow(nRow))
2299 m_nSeekPos = 0;
2300 return true;
2303 if (!m_pSeekCursor)
2304 return false;
2306 // is this an insertion?
2307 if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
2308 nRow >= m_nCurrentPos)
2310 // if so, scrolling down must be prevented as this is already the last data set!
2311 if (nRow == m_nCurrentPos)
2313 // no adjustment necessary
2314 m_nSeekPos = nRow;
2316 else if (IsInsertionRow(nRow)) // blank row for data insertion
2317 m_nSeekPos = nRow;
2319 else if (IsInsertionRow(nRow)) // blank row for data insertion
2320 m_nSeekPos = nRow;
2321 else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & OPT_INSERT) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
2322 m_nSeekPos = nRow;
2323 else
2325 bool bSuccess = false;
2326 long nSteps = 0;
2329 if ( m_pSeekCursor->rowDeleted() )
2331 // somebody deleted the current row of the seek cursor. Move it away from this row.
2332 m_pSeekCursor->next();
2333 if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
2334 bAbsolute = true;
2337 if ( !bAbsolute )
2339 DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
2340 "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
2341 nSteps = nRow - (m_pSeekCursor->getRow() - 1);
2342 bAbsolute = bAbsolute || (std::abs(nSteps) > 100);
2345 if ( bAbsolute )
2347 bSuccess = m_pSeekCursor->absolute(nRow + 1);
2348 if (bSuccess)
2349 m_nSeekPos = nRow;
2351 else
2353 if (nSteps > 0) // position onto the last needed data set
2355 if (m_pSeekCursor->isAfterLast())
2356 bSuccess = false;
2357 else if (m_pSeekCursor->isBeforeFirst())
2358 bSuccess = m_pSeekCursor->absolute(nSteps);
2359 else
2360 bSuccess = m_pSeekCursor->relative(nSteps);
2362 else if (nSteps < 0)
2364 if (m_pSeekCursor->isBeforeFirst())
2365 bSuccess = false;
2366 else if (m_pSeekCursor->isAfterLast())
2367 bSuccess = m_pSeekCursor->absolute(nSteps);
2368 else
2369 bSuccess = m_pSeekCursor->relative(nSteps);
2371 else
2373 m_nSeekPos = nRow;
2374 return true;
2378 catch(Exception&)
2380 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2385 if (!bSuccess)
2387 if (bAbsolute || nSteps > 0)
2389 if (m_pSeekCursor->isLast())
2390 bSuccess = true;
2391 else
2392 bSuccess = m_pSeekCursor->last();
2394 else
2396 if (m_pSeekCursor->isFirst())
2397 bSuccess = true;
2398 else
2399 bSuccess = m_pSeekCursor->first();
2403 if (bSuccess)
2404 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2405 else
2406 m_nSeekPos = -1;
2408 catch(Exception&)
2410 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2411 DBG_UNHANDLED_EXCEPTION();
2412 m_nSeekPos = -1; // no further data set available
2415 return m_nSeekPos == nRow;
2418 void DbGridControl::MoveToFirst()
2420 if (m_pSeekCursor && (GetCurRow() != 0))
2421 MoveToPosition(0);
2424 void DbGridControl::MoveToLast()
2426 if (!m_pSeekCursor)
2427 return;
2429 if (m_nTotalCount < 0) // no RecordCount, yet
2433 bool bRes = m_pSeekCursor->last();
2435 if (bRes)
2437 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2438 AdjustRows();
2441 catch(Exception&)
2446 // position onto the last data set not on a blank row
2447 if (m_nOptions & OPT_INSERT)
2449 if ((GetRowCount() - 1) > 0)
2450 MoveToPosition(GetRowCount() - 2);
2452 else if (GetRowCount())
2453 MoveToPosition(GetRowCount() - 1);
2456 void DbGridControl::MoveToPrev()
2458 long nNewRow = std::max(GetCurRow() - 1L, 0L);
2459 if (GetCurRow() != nNewRow)
2460 MoveToPosition(nNewRow);
2463 void DbGridControl::MoveToNext()
2465 if (!m_pSeekCursor)
2466 return;
2468 if (m_nTotalCount > 0)
2470 // move the data cursor to the right position
2471 long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
2472 if (GetCurRow() != nNewRow)
2473 MoveToPosition(nNewRow);
2475 else
2477 bool bOk = false;
2480 // try to move to next row
2481 // when not possible our paint cursor is already on the last row
2482 // then we must be sure that the data cursor is on the position
2483 // we call ourself again
2484 bOk = m_pSeekCursor->next();
2485 if (bOk)
2487 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2488 MoveToPosition(GetCurRow() + 1);
2491 catch(SQLException &)
2493 DBG_UNHANDLED_EXCEPTION();
2496 if(!bOk)
2498 AdjustRows();
2499 if (m_nTotalCount > 0) // only to avoid infinte recursion
2500 MoveToNext();
2505 void DbGridControl::MoveToPosition(sal_uInt32 nPos)
2507 if (!m_pSeekCursor)
2508 return;
2510 if (m_nTotalCount < 0 && (long)nPos >= GetRowCount())
2514 if (!m_pSeekCursor->absolute(nPos + 1))
2516 AdjustRows();
2517 return;
2519 else
2521 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2522 AdjustRows();
2525 catch(Exception&)
2527 return;
2530 DbGridControl_Base::GoToRow(nPos);
2531 m_aBar->InvalidateAll(m_nCurrentPos);
2534 void DbGridControl::AppendNew()
2536 if (!m_pSeekCursor || !(m_nOptions & OPT_INSERT))
2537 return;
2539 if (m_nTotalCount < 0) // no RecordCount, yet
2543 bool bRes = m_pSeekCursor->last();
2545 if (bRes)
2547 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2548 AdjustRows();
2551 catch(Exception&)
2553 return;
2557 long nNewRow = m_nTotalCount + 1;
2558 if (nNewRow > 0 && GetCurRow() != nNewRow)
2559 MoveToPosition(nNewRow - 1);
2562 void DbGridControl::SetDesignMode(bool bMode)
2564 if ((bool) IsDesignMode() != bMode)
2566 // adjust Enable/Disable for design mode so that the headerbar remains configurable
2567 if (bMode)
2569 if (!IsEnabled())
2571 Enable();
2572 GetDataWindow().Disable();
2575 else
2577 // disable completely
2578 if (!GetDataWindow().IsEnabled())
2579 Disable();
2582 m_bDesignMode = bMode;
2583 GetDataWindow().SetMouseTransparent(bMode);
2584 SetMouseTransparent(bMode);
2586 m_aBar->InvalidateAll(m_nCurrentPos, true);
2590 void DbGridControl::SetFilterMode(bool bMode)
2592 if (IsFilterMode() != bMode)
2594 m_bFilterMode = bMode;
2596 if (bMode)
2598 SetUpdateMode(false);
2600 // there is no cursor anymore
2601 if (IsEditing())
2602 DeactivateCell();
2603 RemoveRows(false);
2605 m_xEmptyRow = new DbGridRow();
2607 // setting the new filter controls
2608 for ( size_t i = 0; i < m_aColumns.size(); ++i )
2610 DbGridColumn* pCurCol = m_aColumns[ i ];
2611 if (!pCurCol->IsHidden())
2612 pCurCol->UpdateControl();
2615 // one row for filtering
2616 RowInserted(0, 1, true);
2617 SetUpdateMode(true);
2619 else
2620 setDataSource(Reference< XRowSet > ());
2624 OUString DbGridControl::GetCellText(long _nRow, sal_uInt16 _nColId) const
2626 size_t Location = GetModelColumnPos( _nColId );
2627 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2628 OUString sRet;
2629 if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
2630 sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
2631 return sRet;
2634 OUString DbGridControl::GetCurrentRowCellText(DbGridColumn* pColumn,const DbGridRowRef& _rRow) const
2636 // text output for a single row
2637 OUString aText;
2638 if ( pColumn && IsValid(_rRow) )
2639 aText = pColumn->GetCellText(_rRow, m_xFormatter);
2640 return aText;
2643 sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId)
2645 if (SeekRow(nRow))
2647 size_t Location = GetModelColumnPos( nColId );
2648 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2649 return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
2651 else
2652 return 30; // FIXME magic number for defaul cell width
2655 void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu)
2657 bool bDelete = (m_nOptions & OPT_DELETE) && GetSelectRowCount() && !IsCurrentAppending();
2658 // if only a blank row is selected than do not delete
2659 bDelete = bDelete && !((m_nOptions & OPT_INSERT) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
2661 rMenu.EnableItem(SID_FM_DELETEROWS, bDelete);
2662 rMenu.EnableItem(SID_FM_RECORD_SAVE, IsModified());
2664 // the undo is more difficult
2665 bool bCanUndo = IsModified();
2666 long nState = -1;
2667 if (m_aMasterStateProvider.IsSet())
2668 nState = m_aMasterStateProvider.Call(reinterpret_cast<void*>(SID_FM_RECORD_UNDO));
2669 bCanUndo &= ( 0 != nState );
2671 rMenu.EnableItem(SID_FM_RECORD_UNDO, bCanUndo);
2674 void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& /*rMenu*/, sal_uInt16 nExecutionResult)
2676 switch (nExecutionResult)
2678 case SID_FM_DELETEROWS:
2679 // delete asynchronously
2680 if (m_nDeleteEvent)
2681 Application::RemoveUserEvent(m_nDeleteEvent);
2682 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), NULL, true);
2683 break;
2684 case SID_FM_RECORD_UNDO:
2685 Undo();
2686 break;
2687 case SID_FM_RECORD_SAVE:
2688 SaveRow();
2689 break;
2690 default:
2691 break;
2695 void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
2697 SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
2698 SolarMutexGuard aGuard;
2699 // prop "IsModified" changed ?
2700 // during update don't care about the modified state
2701 if (!IsUpdating() && evt.PropertyName == FM_PROP_ISMODIFIED )
2703 Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
2704 DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
2705 bool bIsNew = false;
2706 if (xSource.is())
2707 bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
2709 if (bIsNew && m_xCurrentRow.Is())
2711 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 !");
2712 sal_Int32 nRecordCount = 0;
2713 xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
2714 if (::comphelper::getBOOL(evt.NewValue))
2715 { // modified state changed from sal_False to sal_True and we're on a insert row
2716 // -> we've to add a new grid row
2717 if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
2719 RowInserted(GetRowCount(), 1, true);
2720 InvalidateStatusCell(m_nCurrentPos);
2721 m_aBar->InvalidateAll(m_nCurrentPos);
2724 else
2725 { // modified state changed from sal_True to sal_False and we're on a insert row
2726 // we have two "new row"s at the moment : the one we're editing currently (where the current
2727 // column is the only dirty element) and a "new new" row which is completely clean. As the first
2728 // one is about to be cleaned, too, the second one is obsolete now.
2729 if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
2731 RowRemoved(GetRowCount() - 1, 1, true);
2732 InvalidateStatusCell(m_nCurrentPos);
2733 m_aBar->InvalidateAll(m_nCurrentPos);
2737 if (m_xCurrentRow.Is())
2739 m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GRS_MODIFIED : GRS_CLEAN);
2740 m_xCurrentRow->SetNew( bIsNew );
2741 InvalidateStatusCell(m_nCurrentPos);
2742 SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
2747 void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
2749 if (!m_pSeekCursor || IsResizing())
2750 return;
2752 sal_uInt16 nColId = GetColumnAtXPosPixel(rPosPixel.X());
2753 long nRow = GetRowAtYPosPixel(rPosPixel.Y());
2754 if (nColId != HandleColumnId && nRow >= 0)
2756 if (GetDataWindow().IsMouseCaptured())
2757 GetDataWindow().ReleaseMouse();
2759 size_t Location = GetModelColumnPos( nColId );
2760 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2761 OStringTransferable* pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
2762 Reference< XTransferable > xEnsureDelete(pTransferable);
2763 pTransferable->StartDrag(this, DND_ACTION_COPY);
2767 bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2769 return (_nRow >= 0)
2770 && (_nRow < GetRowCount())
2771 && (_nColId != HandleColumnId)
2772 && (_nColId <= ColCount());
2775 void DbGridControl::copyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2777 DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
2778 DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ];
2779 SeekRow(_nRow);
2780 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
2783 void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos )
2785 PopupMenu aContextMenu( SVX_RES( RID_SVXMNU_ROWS ) );
2787 PreExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu );
2788 aContextMenu.RemoveDisabledEntries( true, true );
2789 PostExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu, aContextMenu.Execute( this, _rPreferredPos ) );
2791 // TODO: why this weird cast to sal_uInt16? What if we really have more than 65535 lines?
2792 // -> change this to sal_uInt32
2795 void DbGridControl::Command(const CommandEvent& rEvt)
2797 switch (rEvt.GetCommand())
2799 case CommandEventId::ContextMenu:
2801 if ( !m_pSeekCursor )
2803 DbGridControl_Base::Command(rEvt);
2804 return;
2807 if ( !rEvt.IsMouseEvent() )
2808 { // context menu requested by keyboard
2809 if ( GetSelectRowCount() )
2811 long nRow = FirstSelectedRow( );
2813 ::Rectangle aRowRect( GetRowRectPixel( nRow, true ) );
2814 executeRowContextMenu( nRow, aRowRect.LeftCenter() );
2816 // handled
2817 return;
2821 sal_uInt16 nColId = GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X());
2822 long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
2824 if (nColId == HandleColumnId)
2826 executeRowContextMenu( nRow, rEvt.GetMousePosPixel() );
2828 else if (canCopyCellText(nRow, nColId))
2830 PopupMenu aContextMenu(SVX_RES(RID_SVXMNU_CELL));
2831 aContextMenu.RemoveDisabledEntries(true, true);
2832 switch (aContextMenu.Execute(this, rEvt.GetMousePosPixel()))
2834 case SID_COPY:
2835 copyCellText(nRow, nColId);
2836 break;
2839 else
2841 DbGridControl_Base::Command(rEvt);
2842 return;
2845 //fall-through
2846 default:
2847 DbGridControl_Base::Command(rEvt);
2851 IMPL_LINK_NOARG(DbGridControl, OnDelete)
2853 m_nDeleteEvent = 0;
2854 DeleteSelectedRows();
2855 return 0;
2858 void DbGridControl::DeleteSelectedRows()
2860 DBG_ASSERT(GetSelection(), "keine selection!!!");
2862 if (!m_pSeekCursor)
2863 return;
2866 CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId)
2868 if (!IsValid(m_xCurrentRow) || !IsEnabled())
2869 return NULL;
2871 size_t Location = GetModelColumnPos(nColumnId);
2872 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2873 if (!pColumn)
2874 return NULL;
2876 CellController* pReturn = NULL;
2877 if (IsFilterMode())
2878 pReturn = &pColumn->GetController();
2879 else
2881 if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
2883 if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
2884 return NULL;
2887 bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & OPT_INSERT));
2888 bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & OPT_UPDATE));
2890 if ((bInsert && !pColumn->IsAutoValue()) || bUpdate || m_bForceROController)
2892 pReturn = &pColumn->GetController();
2893 if (pReturn)
2895 // if it is an edit row, it is possible to give it a forced read-only property
2896 if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController))
2897 // controller could not be set to read-only in forceROController
2898 if (!bInsert && !bUpdate)
2899 // better use no controller than one without read-only
2900 pReturn = NULL;
2904 return pReturn;
2907 void DbGridControl::CellModified()
2909 SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
2912 ::osl::MutexGuard aGuard(m_aAdjustSafety);
2913 if (m_nAsynAdjustEvent)
2915 SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
2916 RemoveUserEvent(m_nAsynAdjustEvent);
2917 m_nAsynAdjustEvent = 0;
2919 // force the call : this should be no problem as we're probably running in the solar thread here
2920 // (cell modified is triggered by user actions)
2921 if (m_bPendingAdjustRows)
2922 AdjustRows();
2923 else
2924 AdjustDataSource();
2928 if (!IsFilterMode() && IsValid(m_xCurrentRow) && !m_xCurrentRow->IsModified())
2930 // enable edit mode
2931 // a data set should be inserted
2932 if (m_xCurrentRow->IsNew())
2934 m_xCurrentRow->SetStatus(GRS_MODIFIED);
2935 SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
2936 // if no row was added yet, do it now
2937 if (m_nCurrentPos == GetRowCount() - 1)
2939 // increment RowCount
2940 RowInserted(GetRowCount(), 1, true);
2941 InvalidateStatusCell(m_nCurrentPos);
2942 m_aBar->InvalidateAll(m_nCurrentPos);
2945 else if (m_xCurrentRow->GetStatus() != GRS_MODIFIED)
2947 m_xCurrentRow->SetState(m_pDataCursor, false);
2948 SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
2949 m_xCurrentRow->SetStatus(GRS_MODIFIED);
2950 SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
2951 InvalidateStatusCell(m_nCurrentPos);
2956 void DbGridControl::Dispatch(sal_uInt16 nId)
2958 if (nId == BROWSER_CURSORENDOFFILE)
2960 if (m_nOptions & OPT_INSERT)
2961 AppendNew();
2962 else
2963 MoveToLast();
2965 else
2966 DbGridControl_Base::Dispatch(nId);
2969 void DbGridControl::Undo()
2971 if (!IsFilterMode() && IsValid(m_xCurrentRow) && IsModified())
2973 // check if we have somebody doin' the UNDO for us
2974 long nState = -1;
2975 if (m_aMasterStateProvider.IsSet())
2976 nState = m_aMasterStateProvider.Call(reinterpret_cast<void*>(SID_FM_RECORD_UNDO));
2977 if (nState>0)
2978 { // yes, we have, and the slot is enabled
2979 DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
2980 long lResult = m_aMasterSlotExecutor.Call(reinterpret_cast<void*>(SID_FM_RECORD_UNDO));
2981 if (lResult)
2982 // handled
2983 return;
2985 else if (nState == 0)
2986 // yes, we have, and the slot is disabled
2987 return;
2989 BeginCursorAction();
2991 bool bAppending = m_xCurrentRow->IsNew();
2992 bool bDirty = m_xCurrentRow->IsModified();
2996 // cancel editing
2997 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
2998 // no effects if we're not updating currently
2999 if (bAppending)
3000 // just refresh the row
3001 xUpdateCursor->moveToInsertRow();
3002 else
3003 xUpdateCursor->cancelRowUpdates();
3006 catch(Exception&)
3008 DBG_UNHANDLED_EXCEPTION();
3011 EndCursorAction();
3013 m_xDataRow->SetState(m_pDataCursor, false);
3014 if (&m_xPaintRow == &m_xCurrentRow)
3015 m_xPaintRow = m_xCurrentRow = m_xDataRow;
3016 else
3017 m_xCurrentRow = m_xDataRow;
3019 if (bAppending && (DbGridControl_Base::IsModified() || bDirty))
3020 // remove the row
3021 if (m_nCurrentPos == GetRowCount() - 2)
3022 { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
3023 // caused our data source form to be reset - which should be the usual case ....)
3024 RowRemoved(GetRowCount() - 1, 1, true);
3025 m_aBar->InvalidateAll(m_nCurrentPos);
3028 RowModified(m_nCurrentPos);
3032 void DbGridControl::resetCurrentRow()
3034 if (IsModified())
3036 // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
3037 // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
3038 // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
3039 // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
3040 // would never delete the obsolete "second insert row". Thus in this special case this method here
3041 // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
3042 // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
3043 Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
3044 if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
3046 // are we on a new row currently ?
3047 if (m_xCurrentRow->IsNew())
3049 if (m_nCurrentPos == GetRowCount() - 2)
3051 RowRemoved(GetRowCount() - 1, 1, true);
3052 m_aBar->InvalidateAll(m_nCurrentPos);
3057 // update the rows
3058 m_xDataRow->SetState(m_pDataCursor, false);
3059 if (&m_xPaintRow == &m_xCurrentRow)
3060 m_xPaintRow = m_xCurrentRow = m_xDataRow;
3061 else
3062 m_xCurrentRow = m_xDataRow;
3065 RowModified(GetCurRow()); // will update the current controller if affected
3068 void DbGridControl::RowModified( long nRow, sal_uInt16 /*nColId*/ )
3070 if (nRow == m_nCurrentPos && IsEditing())
3072 CellControllerRef aTmpRef = Controller();
3073 aTmpRef->ClearModified();
3074 InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
3076 DbGridControl_Base::RowModified(nRow);
3079 bool DbGridControl::IsModified() const
3081 return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || DbGridControl_Base::IsModified());
3084 bool DbGridControl::IsCurrentAppending() const
3086 return m_xCurrentRow.Is() && m_xCurrentRow->IsNew();
3089 bool DbGridControl::IsInsertionRow(long nRow) const
3091 return (m_nOptions & OPT_INSERT) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
3094 bool DbGridControl::SaveModified()
3096 SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
3097 DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
3098 if (!IsValid(m_xCurrentRow))
3099 return true;
3101 // accept input for this field
3102 // Where there changes at the current input field?
3103 if (!DbGridControl_Base::IsModified())
3104 return true;
3106 size_t Location = GetModelColumnPos( GetCurColumnId() );
3107 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3108 bool bOK = pColumn && pColumn->Commit();
3109 DBG_ASSERT( Controller().Is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
3110 if ( !Controller().Is() )
3111 // this might happen if the callbacks implicitly triggered by Commit
3112 // fiddled with the form or the control ...
3113 // (Note that this here is a workaround, at most. We need a general concept how
3114 // to treat this, you can imagine an arbitrary number of scenarios where a callback
3115 // triggers something which leaves us in an expected state.)
3116 // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
3117 return bOK;
3119 if (bOK)
3121 Controller()->ClearModified();
3123 if ( IsValid(m_xCurrentRow) )
3125 m_xCurrentRow->SetState(m_pDataCursor, false);
3126 SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3127 InvalidateStatusCell( m_nCurrentPos );
3129 else
3131 SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3134 else
3136 // reset the modified flag ....
3137 Controller()->SetModified();
3140 return bOK;
3143 bool DbGridControl::SaveRow()
3145 SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
3146 // valid row
3147 if (!IsValid(m_xCurrentRow) || !IsModified())
3148 return true;
3149 // value of the controller was not saved, yet
3150 else if (Controller().Is() && Controller()->IsModified())
3152 if (!SaveModified())
3153 return false;
3155 m_bUpdating = true;
3157 BeginCursorAction();
3158 bool bAppending = m_xCurrentRow->IsNew();
3159 bool bSuccess = false;
3162 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
3163 if (bAppending)
3164 xUpdateCursor->insertRow();
3165 else
3166 xUpdateCursor->updateRow();
3167 bSuccess = true;
3169 catch(SQLException&)
3171 EndCursorAction();
3172 m_bUpdating = false;
3173 return false;
3178 if (bSuccess)
3180 // if we are appending we still sit on the insert row
3181 // we don't move just clear the flags not to move on the current row
3182 m_xCurrentRow->SetState(m_pDataCursor, false);
3183 SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
3184 m_xCurrentRow->SetNew(false);
3186 // adjust the seekcursor if it is on the same position as the datacursor
3187 if (m_nSeekPos == m_nCurrentPos || bAppending)
3189 // get the bookmark to refetch the data
3190 // in insert mode we take the new bookmark of the data cursor
3191 Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
3192 m_pSeekCursor->moveToBookmark(aBookmark);
3193 // get the data
3194 m_xSeekRow->SetState(m_pSeekCursor, true);
3195 m_nSeekPos = m_pSeekCursor->getRow() - 1;
3198 // and repaint the row
3199 RowModified(m_nCurrentPos);
3201 catch(Exception&)
3205 m_bUpdating = false;
3206 EndCursorAction();
3208 // The old code returned (nRecords != 0) here.
3209 // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
3210 // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
3211 // is zero, this simply means all fields had their original values.
3212 // FS - 06.12.99 - 70502
3213 return true;
3216 bool DbGridControl::PreNotify(NotifyEvent& rEvt)
3218 // do not handle events of the Navbar
3219 if (m_aBar->IsWindowOrChild(rEvt.GetWindow()))
3220 return BrowseBox::PreNotify(rEvt);
3222 switch (rEvt.GetType())
3224 case MouseNotifyEvent::KEYINPUT:
3226 const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
3228 sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
3229 bool bShift = pKeyEvent->GetKeyCode().IsShift();
3230 bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
3231 bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
3232 if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
3234 // Ctrl-Tab is used to step out of the control, without traveling to the
3235 // remaining cells first
3236 // -> build a new key event without the Ctrl-key, and let the very base class handle it
3237 vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
3238 KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
3240 // call the Control - our direct base class will interpret this in a way we do not want (and do
3241 // a cell traveling)
3242 Control::KeyInput( aNewEvent );
3243 return true;
3246 if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
3248 if (IsModified())
3250 Undo();
3251 return true;
3254 else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
3256 if ((m_nOptions & OPT_DELETE) && GetSelectRowCount())
3258 // delete asynchronously
3259 if (m_nDeleteEvent)
3260 Application::RemoveUserEvent(m_nDeleteEvent);
3261 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), NULL, true);
3262 return true;
3265 } // no break!
3266 default:
3267 return DbGridControl_Base::PreNotify(rEvt);
3271 bool DbGridControl::IsTabAllowed(bool bRight) const
3273 if (bRight)
3274 // Tab only if not on the _last_ row
3275 return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
3276 GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
3277 else
3279 // Tab only if not on the _first_ row
3280 return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
3284 void DbGridControl::KeyInput( const KeyEvent& rEvt )
3286 if (rEvt.GetKeyCode().GetFunction() == KeyFuncType::COPY)
3288 long nRow = GetCurRow();
3289 sal_uInt16 nColId = GetCurColumnId();
3290 if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
3292 size_t Location = GetModelColumnPos( nColId );
3293 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3294 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
3295 return;
3298 DbGridControl_Base::KeyInput(rEvt);
3301 void DbGridControl::HideColumn(sal_uInt16 nId)
3303 DeactivateCell();
3305 // determine the col for the focus to set to after removal
3306 sal_uInt16 nPos = GetViewColumnPos(nId);
3307 sal_uInt16 nNewColId = nPos == (ColCount()-1)
3308 ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
3309 : GetColumnIdFromViewPos(nPos+1); // take the next
3311 long lCurrentWidth = GetColumnWidth(nId);
3312 DbGridControl_Base::RemoveColumn(nId);
3313 // don't use my own RemoveColumn, this would remove it from m_aColumns, too
3315 // update my model
3316 size_t Location = GetModelColumnPos( nId );
3317 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3318 DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
3319 if (pColumn)
3321 pColumn->m_bHidden = true;
3322 pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
3325 // and reset the focus
3326 if ( nId == GetCurColumnId() )
3327 GoToColumnId( nNewColId );
3330 void DbGridControl::ShowColumn(sal_uInt16 nId)
3332 sal_uInt16 nPos = GetModelColumnPos(nId);
3333 DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
3334 if (nPos == GRID_COLUMN_NOT_FOUND)
3335 return;
3337 DbGridColumn* pColumn = m_aColumns[ nPos ];
3338 if (!pColumn->IsHidden())
3340 DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3341 // if the column isn't marked as hidden, it should be visible, shouldn't it ?
3342 return;
3344 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3345 // the opposite situation ...
3347 // to determine the new view position we need an adjacent non-hidden column
3348 sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
3349 // first search the cols to the right
3350 for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
3352 DbGridColumn* pCurCol = m_aColumns[ i ];
3353 if (!pCurCol->IsHidden())
3355 nNextNonHidden = i;
3356 break;
3359 if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
3361 // then to the left
3362 for ( size_t i = nPos; i > 0; --i )
3364 DbGridColumn* pCurCol = m_aColumns[ i-1 ];
3365 if (!pCurCol->IsHidden())
3367 nNextNonHidden = i-1;
3368 break;
3372 sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
3373 ? 1 // there is no visible column -> insert behinde the handle col
3374 : GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1;
3375 // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
3376 // a position 1 for the first non-handle col -> +1
3377 DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3378 // we found a col marked as visible but got no view pos for it ...
3380 if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID))
3381 // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
3382 ++nNewViewPos;
3384 DeactivateCell();
3386 OUString aName;
3387 pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
3388 InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HeaderBarItemBits::CENTER | HeaderBarItemBits::VCENTER | HeaderBarItemBits::CLICKABLE, nNewViewPos);
3389 pColumn->m_bHidden = false;
3391 ActivateCell();
3392 Invalidate();
3395 sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
3397 if (nPos >= m_aColumns.size())
3399 OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
3400 return GRID_COLUMN_NOT_FOUND;
3403 DbGridColumn* pCol = m_aColumns[ nPos ];
3404 #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
3405 // in der Debug-Version rechnen wir die ModelPos in eine ViewPos um und vergleichen das mit dem Wert,
3406 // den wir zurueckliefern werden (nId an der entsprechenden Col in m_aColumns)
3408 if (!pCol->IsHidden())
3409 { // macht nur Sinn, wenn die Spalte sichtbar ist
3410 sal_uInt16 nViewPos = nPos;
3411 for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i)
3412 if ( m_aColumns[ i ]->IsHidden())
3413 --nViewPos;
3415 DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
3416 "DbGridControl::GetColumnIdFromModelPos : this isn't consistent .... did I misunderstand something ?");
3418 #endif
3419 return pCol->GetId();
3422 sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
3424 for ( size_t i = 0; i < m_aColumns.size(); ++i )
3425 if ( m_aColumns[ i ]->GetId() == nId )
3426 return i;
3428 return GRID_COLUMN_NOT_FOUND;
3431 void DbGridControl::implAdjustInSolarThread(bool _bRows)
3433 SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
3434 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3435 if (::osl::Thread::getCurrentIdentifier() != Application::GetMainThreadIdentifier())
3437 m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ), true);
3438 m_bPendingAdjustRows = _bRows;
3439 if (_bRows)
3440 SAL_INFO("svx.fmcomp", "posting an AdjustRows");
3441 else
3442 SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
3444 else
3446 if (_bRows)
3447 SAL_INFO("svx.fmcomp", "doing an AdjustRows");
3448 else
3449 SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
3450 // always adjust the rows before adjusting the data source
3451 // If this is not necessary (because the row count did not change), nothing is done
3452 // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
3453 // to a position behind row count know 'til now, the cursorMoved notification may come before the
3454 // RowCountChanged notification
3455 // 94093 - 02.11.2001 - frank.schoenheit@sun.com
3456 AdjustRows();
3458 if ( !_bRows )
3459 AdjustDataSource();
3463 IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat)
3465 m_nAsynAdjustEvent = 0;
3467 AdjustRows();
3468 // see implAdjustInSolarThread for a comment why we do this every time
3470 if ( !pAdjustWhat )
3471 AdjustDataSource();
3473 return 0L;
3476 void DbGridControl::BeginCursorAction()
3478 if (m_pFieldListeners)
3480 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3481 ColumnFieldValueListeners::const_iterator aIter = pListeners->begin();
3482 while (aIter != pListeners->end())
3484 GridFieldValueListener* pCurrent = (*aIter).second;
3485 if (pCurrent)
3486 pCurrent->suspend();
3487 ++aIter;
3491 if (m_pDataSourcePropListener)
3492 m_pDataSourcePropListener->suspend();
3495 void DbGridControl::EndCursorAction()
3497 if (m_pFieldListeners)
3499 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3500 ColumnFieldValueListeners::const_iterator aIter = pListeners->begin();
3501 while (aIter != pListeners->end())
3503 GridFieldValueListener* pCurrent = (*aIter).second;
3504 if (pCurrent)
3505 pCurrent->resume();
3506 ++aIter;
3510 if (m_pDataSourcePropListener)
3511 m_pDataSourcePropListener->resume();
3514 void DbGridControl::ConnectToFields()
3516 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3517 DBG_ASSERT(!pListeners || pListeners->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
3519 if (!pListeners)
3521 pListeners = new ColumnFieldValueListeners;
3522 m_pFieldListeners = pListeners;
3525 for ( size_t i = 0; i < m_aColumns.size(); ++i )
3527 DbGridColumn* pCurrent = m_aColumns[ i ];
3528 sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
3529 if (GRID_COLUMN_NOT_FOUND == nViewPos)
3530 continue;
3532 Reference< XPropertySet > xField = pCurrent->GetField();
3533 if (!xField.is())
3534 continue;
3536 // column is visible and bound here
3537 GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
3538 DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
3539 rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
3543 void DbGridControl::DisconnectFromFields()
3545 if (!m_pFieldListeners)
3546 return;
3548 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3549 while (!pListeners->empty())
3551 #ifdef DBG_UTIL
3552 sal_Int32 nOldSize = pListeners->size();
3553 #endif
3554 pListeners->begin()->second->dispose();
3555 DBG_ASSERT(nOldSize > (sal_Int32)pListeners->size(), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
3558 delete pListeners;
3559 m_pFieldListeners = NULL;
3562 void DbGridControl::FieldValueChanged(sal_uInt16 _nId, const PropertyChangeEvent& /*_evt*/)
3564 osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
3565 // needed as this may run in a thread other than the main one
3566 if (GetRowStatus(GetCurRow()) != DbGridControl_Base::MODIFIED)
3567 // all other cases are handled elsewhere
3568 return;
3570 size_t Location = GetModelColumnPos( _nId );
3571 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3572 if (pColumn)
3574 boost::scoped_ptr<vcl::SolarMutexTryAndBuyGuard> pGuard;
3575 while (!m_bWantDestruction && (!pGuard || !pGuard->isAcquired()))
3576 pGuard.reset(new vcl::SolarMutexTryAndBuyGuard);
3578 if (m_bWantDestruction)
3579 { // at this moment, within another thread, our destructor tries to destroy the listener which called this method
3580 // => don't do anything
3581 // 73365 - 23.02.00 - FS
3582 return;
3585 // and finally do the update ...
3586 pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
3587 RowModified(GetCurRow(), _nId);
3591 void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
3593 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3594 if (!pListeners)
3596 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
3597 return;
3600 ColumnFieldValueListeners::iterator aPos = pListeners->find(_nId);
3601 if (aPos == pListeners->end())
3603 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
3604 return;
3607 delete aPos->second;
3609 pListeners->erase(aPos);
3612 void DbGridControl::disposing(sal_uInt16 _nId, const EventObject& /*_rEvt*/)
3614 if (_nId == 0)
3615 { // the seek cursor is being disposed
3616 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3617 setDataSource(NULL,0); // our clone was disposed so we set our datasource to null to avoid later access to it
3618 if (m_nAsynAdjustEvent)
3620 RemoveUserEvent(m_nAsynAdjustEvent);
3621 m_nAsynAdjustEvent = 0;
3626 sal_Int32 DbGridControl::GetAccessibleControlCount() const
3628 return DbGridControl_Base::GetAccessibleControlCount() + 1; // the navigation control
3631 Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
3633 Reference<XAccessible > xRet;
3634 if ( _nIndex == DbGridControl_Base::GetAccessibleControlCount() )
3636 xRet = m_aBar->GetAccessible();
3638 else
3639 xRet = DbGridControl_Base::CreateAccessibleControl( _nIndex );
3640 return xRet;
3643 Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
3645 sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
3646 size_t Location = GetModelColumnPos(nColumnId);
3647 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3648 if ( pColumn )
3650 Reference< ::com::sun::star::awt::XControl> xInt(pColumn->GetCell());
3651 Reference< ::com::sun::star::awt::XCheckBox> xBox(xInt,UNO_QUERY);
3652 if ( xBox.is() )
3654 TriState eValue = TRISTATE_FALSE;
3655 switch( xBox->getState() )
3657 case 0:
3658 eValue = TRISTATE_FALSE;
3659 break;
3660 case 1:
3661 eValue = TRISTATE_TRUE;
3662 break;
3663 case 2:
3664 eValue = TRISTATE_INDET;
3665 break;
3667 return DbGridControl_Base::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
3670 return DbGridControl_Base::CreateAccessibleCell( _nRow, _nColumnPos );
3673 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */