Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / svx / source / fmcomp / gridctrl.cxx
bloba85f86cc1954083c0c0a3cdc6f03736dae2eb002
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/dbtoolsclient.hxx"
25 #include "svx/fmtools.hxx"
26 #include <svtools/stringtransfer.hxx>
28 #include "fmprop.hrc"
29 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
30 #include <com/sun/star/accessibility/XAccessible.hpp>
31 #include <com/sun/star/sdb/XResultSetAccess.hpp>
32 #include <com/sun/star/sdb/RowChangeAction.hpp>
33 #include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
34 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
35 #include <com/sun/star/sdbcx/Privilege.hpp>
36 #include <com/sun/star/container/XChild.hpp>
37 #include <com/sun/star/util/NumberFormatter.hpp>
38 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
39 #include <com/sun/star/util/XCloneable.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
42 #include <comphelper/processfactory.hxx>
43 #include <tools/resid.hxx>
44 #include <tools/diagnose_ex.h>
45 #include <vcl/menu.hxx>
46 #include <vcl/settings.hxx>
48 #include "svx/fmresids.hrc"
50 #include <svx/svxids.hrc>
51 #include <tools/shl.hxx>
52 #include <svx/dialmgr.hxx>
53 #include "fmservs.hxx"
54 #include "sdbdatacolumn.hxx"
56 #include <comphelper/property.hxx>
58 #include <algorithm>
59 #include <cstdlib>
60 #include <map>
62 using namespace ::svxform;
63 using namespace ::svt;
64 using namespace ::com::sun::star::beans;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::uno;
67 using namespace ::com::sun::star::sdbc;
68 using namespace ::com::sun::star::sdbcx;
69 using namespace ::com::sun::star::sdb;
70 using namespace ::com::sun::star::datatransfer;
71 using namespace ::com::sun::star::container;
72 using namespace com::sun::star::accessibility;
74 #define ROWSTATUS(row) (!row.Is() ? "NULL" : row->GetStatus() == GRS_CLEAN ? "CLEAN" : row->GetStatus() == GRS_MODIFIED ? "MODIFIED" : row->GetStatus() == GRS_DELETED ? "DELETED" : "INVALID")
76 #define DEFAULT_BROWSE_MODE \
77 BROWSER_COLUMNSELECTION \
78 | BROWSER_MULTISELECTION \
79 | BROWSER_KEEPSELECTION \
80 | BROWSER_TRACKING_TIPS \
81 | BROWSER_HLINESFULL \
82 | BROWSER_VLINESFULL \
83 | BROWSER_HEADERBAR_NEW \
85 class RowSetEventListener : public ::cppu::WeakImplHelper1<XRowsChangeListener>
87 DbGridControl* m_pControl;
88 public:
89 RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
93 private:
94 // XEventListener
95 virtual void SAL_CALL disposing(const ::com::sun::star::lang::EventObject& /*i_aEvt*/) throw ( RuntimeException, std::exception ) SAL_OVERRIDE
98 virtual void SAL_CALL rowsChanged(const ::com::sun::star::sdb::RowsChangeEvent& i_aEvt) throw ( RuntimeException, std::exception ) SAL_OVERRIDE
100 if ( i_aEvt.Action == RowChangeAction::UPDATE )
102 ::DbGridControl::GrantControlAccess aAccess;
103 CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
104 const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
105 const Any* pIter = i_aEvt.Bookmarks.getConstArray();
106 const Any* pEnd = pIter + i_aEvt.Bookmarks.getLength();
107 for(;pIter != pEnd;++pIter)
109 pSeek->moveToBookmark(*pIter);
110 // get the data
111 rSeekRow->SetState(pSeek, true);
112 sal_Int32 nSeekPos = pSeek->getRow() - 1;
113 m_pControl->SetSeekPos(nSeekPos,aAccess);
114 m_pControl->RowModified(nSeekPos);
120 class GridFieldValueListener;
121 typedef std::map<sal_uInt16, GridFieldValueListener*> ColumnFieldValueListeners;
123 class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
125 osl::Mutex m_aMutex;
126 DbGridControl& m_rParent;
127 ::comphelper::OPropertyChangeMultiplexer* m_pRealListener;
128 sal_uInt16 m_nId;
129 sal_Int16 m_nSuspended;
130 bool m_bDisposed : 1;
132 public:
133 GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
134 virtual ~GridFieldValueListener();
136 virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) SAL_OVERRIDE;
138 void suspend() { ++m_nSuspended; }
139 void resume() { --m_nSuspended; }
141 void dispose();
144 GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId)
145 :OPropertyChangeListener(m_aMutex)
146 ,m_rParent(_rParent)
147 ,m_pRealListener(NULL)
148 ,m_nId(_nId)
149 ,m_nSuspended(0)
150 ,m_bDisposed(false)
152 if (_rField.is())
154 m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
155 m_pRealListener->addProperty(FM_PROP_VALUE);
156 m_pRealListener->acquire();
160 GridFieldValueListener::~GridFieldValueListener()
162 dispose();
165 void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& _evt) throw( RuntimeException )
167 DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
168 if (m_nSuspended <= 0)
169 m_rParent.FieldValueChanged(m_nId, _evt);
172 void GridFieldValueListener::dispose()
174 if (m_bDisposed)
176 DBG_ASSERT(m_pRealListener == NULL, "GridFieldValueListener::dispose : inconsistent !");
177 return;
180 if (m_pRealListener)
182 m_pRealListener->dispose();
183 m_pRealListener->release();
184 m_pRealListener = NULL;
187 m_bDisposed = true;
188 m_rParent.FieldListenerDisposing(m_nId);
191 class DisposeListenerGridBridge : public FmXDisposeListener
193 osl::Mutex m_aMutex;
194 DbGridControl& m_rParent;
195 FmXDisposeMultiplexer* m_pRealListener;
197 public:
198 DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId = -1);
199 virtual ~DisposeListenerGridBridge();
201 virtual void disposing(const EventObject& _rEvent, sal_Int16 _nId) throw( RuntimeException ) SAL_OVERRIDE { m_rParent.disposing(_nId, _rEvent); }
204 DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId)
205 :FmXDisposeListener(m_aMutex)
206 ,m_rParent(_rParent)
207 ,m_pRealListener(NULL)
210 if (_rxObject.is())
212 m_pRealListener = new FmXDisposeMultiplexer(this, _rxObject, _rId);
213 m_pRealListener->acquire();
217 DisposeListenerGridBridge::~DisposeListenerGridBridge()
219 if (m_pRealListener)
221 m_pRealListener->dispose();
222 m_pRealListener->release();
223 m_pRealListener = NULL;
228 static const sal_uInt16 ControlMap[] =
230 DbGridControl::NavigationBar::RECORD_TEXT,
231 DbGridControl::NavigationBar::RECORD_ABSOLUTE,
232 DbGridControl::NavigationBar::RECORD_OF,
233 DbGridControl::NavigationBar::RECORD_COUNT,
234 DbGridControl::NavigationBar::RECORD_FIRST,
235 DbGridControl::NavigationBar::RECORD_NEXT,
236 DbGridControl::NavigationBar::RECORD_PREV,
237 DbGridControl::NavigationBar::RECORD_LAST,
238 DbGridControl::NavigationBar::RECORD_NEW,
242 bool CompareBookmark(const Any& aLeft, const Any& aRight)
244 return ::comphelper::compare(aLeft, aRight);
247 class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
249 DbGridControl* m_pParent;
251 // a DbGridControl has no mutex, so we use our own as the base class expects one
252 osl::Mutex m_aMutex;
253 sal_Int16 m_nSuspended;
255 public:
256 FmXGridSourcePropListener(DbGridControl* _pParent);
258 void suspend() { ++m_nSuspended; }
259 void resume() { --m_nSuspended; }
261 virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) SAL_OVERRIDE;
264 FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
265 :OPropertyChangeListener(m_aMutex)
266 ,m_pParent(_pParent)
267 ,m_nSuspended(0)
269 DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
272 void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
274 DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
275 if (m_nSuspended <= 0)
276 m_pParent->DataSourcePropertyChanged(evt);
279 DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(Window* pParent, WinBits nStyle)
280 :NumericField(pParent, nStyle)
282 SetMin(1);
283 SetFirst(1);
284 SetSpinSize(1);
286 SetDecimalDigits(0);
287 SetStrictFormat(true);
290 void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt)
292 if (rEvt.GetKeyCode() == KEY_RETURN && !GetText().isEmpty())
294 sal_Int64 nRecord = GetValue();
295 if (nRecord < GetMin() || nRecord > GetMax())
296 return;
297 else
298 ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
300 else if (rEvt.GetKeyCode() == KEY_TAB)
301 GetParent()->GetParent()->GrabFocus();
302 else
303 NumericField::KeyInput(rEvt);
306 void DbGridControl::NavigationBar::AbsolutePos::LoseFocus()
308 NumericField::LoseFocus();
309 sal_Int64 nRecord = GetValue();
310 if (nRecord < GetMin() || nRecord > GetMax())
311 return;
312 else
314 ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
315 ((NavigationBar*)GetParent())->InvalidateState(NavigationBar::RECORD_ABSOLUTE);
319 void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord)
321 if (m_bPositioning)
322 return;
323 // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition,
324 // so protect against this recursion
325 m_bPositioning = true;
326 ((DbGridControl*)GetParent())->MoveToPosition(nRecord - 1);
327 m_bPositioning = false;
330 DbGridControl::NavigationBar::NavigationBar(Window* pParent, WinBits nStyle)
331 :Control(pParent, nStyle)
332 ,m_aRecordText(this, WB_VCENTER)
333 ,m_aAbsolute(this, WB_CENTER | WB_VCENTER)
334 ,m_aRecordOf(this, WB_VCENTER)
335 ,m_aRecordCount(this, WB_VCENTER)
336 ,m_aFirstBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
337 ,m_aPrevBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS)
338 ,m_aNextBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS)
339 ,m_aLastBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
340 ,m_aNewBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS)
341 ,m_nDefaultWidth(0)
342 ,m_nCurrentPos(-1)
343 ,m_bPositioning(false)
345 m_aFirstBtn.SetSymbol(SYMBOL_FIRST);
346 m_aPrevBtn.SetSymbol(SYMBOL_PREV);
347 m_aNextBtn.SetSymbol(SYMBOL_NEXT);
348 m_aLastBtn.SetSymbol(SYMBOL_LAST);
349 m_aNewBtn.SetModeImage(((DbGridControl*)pParent)->GetImage(DbGridControl_Base::NEW));
351 m_aFirstBtn.SetHelpId(HID_GRID_TRAVEL_FIRST);
352 m_aPrevBtn.SetHelpId(HID_GRID_TRAVEL_PREV);
353 m_aNextBtn.SetHelpId(HID_GRID_TRAVEL_NEXT);
354 m_aLastBtn.SetHelpId(HID_GRID_TRAVEL_LAST);
355 m_aNewBtn.SetHelpId(HID_GRID_TRAVEL_NEW);
356 m_aAbsolute.SetHelpId(HID_GRID_TRAVEL_ABSOLUTE);
357 m_aRecordCount.SetHelpId(HID_GRID_NUMBEROFRECORDS);
359 // Handler fuer Buttons einrichten
360 m_aFirstBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
361 m_aPrevBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
362 m_aNextBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
363 m_aLastBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
364 m_aNewBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
366 m_aRecordText.SetText(SVX_RESSTR(RID_STR_REC_TEXT));
367 m_aRecordOf.SetText(SVX_RESSTR(RID_STR_REC_FROM_TEXT));
368 m_aRecordCount.SetText(OUString('?'));
370 m_nDefaultWidth = ArrangeControls();
372 m_aFirstBtn.Disable();
373 m_aPrevBtn.Disable();
374 m_aNextBtn.Disable();
375 m_aLastBtn.Disable();
376 m_aNewBtn.Disable();
377 m_aRecordText.Disable();
378 m_aRecordOf.Disable();
379 m_aRecordCount.Disable();
380 m_aAbsolute.Disable();
382 AllSettings aSettings = m_aNextBtn.GetSettings();
383 MouseSettings aMouseSettings = aSettings.GetMouseSettings();
384 aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
385 aSettings.SetMouseSettings(aMouseSettings);
386 m_aNextBtn.SetSettings(aSettings, true);
387 m_aPrevBtn.SetSettings(aSettings, true);
389 m_aFirstBtn.Show();
390 m_aPrevBtn.Show();
391 m_aNextBtn.Show();
392 m_aLastBtn.Show();
393 m_aNewBtn.Show();
394 m_aRecordText.Show();
395 m_aRecordOf.Show();
396 m_aRecordCount.Show();
397 m_aAbsolute.Show();
400 namespace
402 void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize)
404 _rButton.SetPosPixel( _rPos );
405 _rButton.SetSizePixel( _rSize );
406 _rPos.X() += (sal_uInt16)_rSize.Width();
410 sal_uInt16 DbGridControl::NavigationBar::ArrangeControls()
412 // positioning of the controls
413 // calculate base size
414 Rectangle aRect(((DbGridControl*)GetParent())->GetControlArea());
415 const long nH = aRect.GetSize().Height();
416 Size aBorder = LogicToPixel(Size(2, 2),MAP_APPFONT);
417 aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height()));
418 sal_uInt16 nX = 1;
419 sal_uInt16 nY = 0;
421 // Is the font of this edit larger than the field?
422 if (m_aAbsolute.GetTextHeight() > nH)
424 Font aApplFont (m_aAbsolute.GetFont());
425 const Size pointAbsoluteSize(m_aAbsolute.PixelToLogic( Size( 0, nH - 2 ), MapMode(MAP_POINT) ));
426 aApplFont.SetSize( pointAbsoluteSize );
427 m_aAbsolute.SetControlFont( aApplFont );
429 aApplFont.SetTransparent( true );
430 m_aRecordText.SetControlFont( aApplFont );
431 m_aRecordOf.SetControlFont( aApplFont );
432 m_aRecordCount.SetControlFont( aApplFont );
435 // set size and position of the control
436 OUString aText = m_aRecordText.GetText();
437 long nTextWidth = m_aRecordText.GetTextWidth(aText);
438 m_aRecordText.SetPosPixel(Point(nX,nY));
439 m_aRecordText.SetSizePixel(Size(nTextWidth,nH));
440 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
442 // count an extra hairspace (U+200A) left and right
443 const OUString sevenDigits(m_aAbsolute.CreateFieldText(6000000));
444 const OUString hairSpace(static_cast<sal_Unicode>(0x200A));
445 OUString textPattern(hairSpace);
446 textPattern += sevenDigits;
447 textPattern += hairSpace;
448 nTextWidth = m_aAbsolute.GetTextWidth( textPattern );
449 m_aAbsolute.SetPosPixel(Point(nX,nY));
450 m_aAbsolute.SetSizePixel(Size(nTextWidth, nH));
451 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
453 aText = m_aRecordOf.GetText();
454 nTextWidth = m_aRecordOf.GetTextWidth(aText);
455 m_aRecordOf.SetPosPixel(Point(nX,nY));
456 m_aRecordOf.SetSizePixel(Size(nTextWidth,nH));
457 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
459 textPattern = sevenDigits + " * (" + sevenDigits + ")";
460 nTextWidth = m_aRecordCount.GetTextWidth( textPattern );
461 m_aRecordCount.SetPosPixel(Point(nX,nY));
462 m_aRecordCount.SetSizePixel(Size(nTextWidth,nH));
463 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
465 Point aButtonPos(nX,nY);
466 const Size aButtonSize(nH,nH);
467 SetPosAndSize(m_aFirstBtn, aButtonPos, aButtonSize);
468 SetPosAndSize(m_aPrevBtn, aButtonPos, aButtonSize);
469 SetPosAndSize(m_aNextBtn, aButtonPos, aButtonSize);
470 SetPosAndSize(m_aLastBtn, aButtonPos, aButtonSize);
471 SetPosAndSize(m_aNewBtn, aButtonPos, aButtonSize);
473 nX = sal::static_int_cast< sal_uInt16 >(aButtonPos.X() + 1);
475 return nX;
478 IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton )
480 DbGridControl* pParent = (DbGridControl*)GetParent();
482 if (pParent->m_aMasterSlotExecutor.IsSet())
484 long lResult = 0;
485 if (pButton == &m_aFirstBtn)
486 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_FIRST);
487 else if( pButton == &m_aPrevBtn )
488 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_PREV);
489 else if( pButton == &m_aNextBtn )
490 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEXT);
491 else if( pButton == &m_aLastBtn )
492 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_LAST);
493 else if( pButton == &m_aNewBtn )
494 lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEW);
496 if (lResult)
497 // the link already handled it
498 return 0;
501 if (pButton == &m_aFirstBtn)
502 pParent->MoveToFirst();
503 else if( pButton == &m_aPrevBtn )
504 pParent->MoveToPrev();
505 else if( pButton == &m_aNextBtn )
506 pParent->MoveToNext();
507 else if( pButton == &m_aLastBtn )
508 pParent->MoveToLast();
509 else if( pButton == &m_aNewBtn )
510 pParent->AppendNew();
511 return 0;
514 void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll)
516 if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)
518 DbGridControl* pParent = (DbGridControl*)GetParent();
520 sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControl::OPT_INSERT) ? 2 : 1);
522 // check if everything needs to be invalidated
523 bAll = bAll || m_nCurrentPos <= 0;
524 bAll = bAll || nCurrentPos <= 0;
525 bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
526 bAll = bAll || nCurrentPos >= nAdjustedRowCount;
528 if ( bAll )
530 m_nCurrentPos = nCurrentPos;
531 int i = 0;
532 while (ControlMap[i])
533 SetState(ControlMap[i++]);
535 else // is in the center
537 m_nCurrentPos = nCurrentPos;
538 SetState(NavigationBar::RECORD_COUNT);
539 SetState(NavigationBar::RECORD_ABSOLUTE);
544 bool DbGridControl::NavigationBar::GetState(sal_uInt16 nWhich) const
546 DbGridControl* pParent = (DbGridControl*)GetParent();
548 if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
549 || pParent->IsFilterMode() )
550 return false;
551 else
553 // check if we have a master state provider
554 if (pParent->m_aMasterStateProvider.IsSet())
556 long nState = pParent->m_aMasterStateProvider.Call(reinterpret_cast< void* >( nWhich ) );
557 if (nState>=0)
558 return (nState>0);
561 bool bAvailable = true;
563 switch (nWhich)
565 case NavigationBar::RECORD_FIRST:
566 case NavigationBar::RECORD_PREV:
567 bAvailable = m_nCurrentPos > 0;
568 break;
569 case NavigationBar::RECORD_NEXT:
570 if(pParent->m_bRecordCountFinal)
572 bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
573 if (!bAvailable && pParent->GetOptions() & DbGridControl::OPT_INSERT)
574 bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
576 break;
577 case NavigationBar::RECORD_LAST:
578 if(pParent->m_bRecordCountFinal)
580 if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
581 bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
582 m_nCurrentPos != pParent->GetRowCount() - 2;
583 else
584 bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
586 break;
587 case NavigationBar::RECORD_NEW:
588 bAvailable = (pParent->GetOptions() & DbGridControl::OPT_INSERT) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
589 break;
590 case NavigationBar::RECORD_ABSOLUTE:
591 bAvailable = pParent->GetRowCount() > 0;
592 break;
594 return bAvailable;
598 void DbGridControl::NavigationBar::SetState(sal_uInt16 nWhich)
600 bool bAvailable = GetState(nWhich);
601 DbGridControl* pParent = (DbGridControl*)GetParent();
602 Window* pWnd = NULL;
603 switch (nWhich)
605 case NavigationBar::RECORD_FIRST:
606 pWnd = &m_aFirstBtn;
607 break;
608 case NavigationBar::RECORD_PREV:
609 pWnd = &m_aPrevBtn;
610 break;
611 case NavigationBar::RECORD_NEXT:
612 pWnd = &m_aNextBtn;
613 break;
614 case NavigationBar::RECORD_LAST:
615 pWnd = &m_aLastBtn;
616 break;
617 case NavigationBar::RECORD_NEW:
618 pWnd = &m_aNewBtn;
619 break;
620 case NavigationBar::RECORD_ABSOLUTE:
621 pWnd = &m_aAbsolute;
622 if (bAvailable)
624 if (pParent->m_nTotalCount >= 0)
626 if (pParent->IsCurrentAppending())
627 m_aAbsolute.SetMax(pParent->m_nTotalCount + 1);
628 else
629 m_aAbsolute.SetMax(pParent->m_nTotalCount);
631 else
632 m_aAbsolute.SetMax(LONG_MAX);
634 m_aAbsolute.SetValue(m_nCurrentPos + 1);
636 else
637 m_aAbsolute.SetText(OUString());
638 break;
639 case NavigationBar::RECORD_TEXT:
640 pWnd = &m_aRecordText;
641 break;
642 case NavigationBar::RECORD_OF:
643 pWnd = &m_aRecordOf;
644 break;
645 case NavigationBar::RECORD_COUNT:
647 pWnd = &m_aRecordCount;
648 OUString aText;
649 if (bAvailable)
651 if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
653 if (pParent->IsCurrentAppending() && !pParent->IsModified())
654 aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount());
655 else
656 aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount() - 1);
658 else
659 aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount());
660 if(!pParent->m_bRecordCountFinal)
661 aText += " *";
663 else
664 aText = "";
666 // add the number of selected rows, if applicable
667 if (pParent->GetSelectRowCount())
669 OUString aExtendedInfo(aText);
670 aExtendedInfo += " (";
671 aExtendedInfo += m_aAbsolute.CreateFieldText(pParent->GetSelectRowCount());
672 aExtendedInfo += ")";
673 pWnd->SetText(aExtendedInfo);
675 else
676 pWnd->SetText(aText);
678 pParent->SetRealRowCount(aText);
679 } break;
681 DBG_ASSERT(pWnd, "kein Fenster");
682 if (pWnd && (pWnd->IsEnabled() != bAvailable))
683 // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
684 // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
685 // do this check.
686 // For further explanation see Bug 69900.
687 pWnd->Enable(bAvailable);
690 void DbGridControl::NavigationBar::Resize()
692 Control::Resize();
693 ArrangeControls();
696 void DbGridControl::NavigationBar::Paint(const Rectangle& rRect)
698 Control::Paint(rRect);
699 Point aAbsolutePos = m_aAbsolute.GetPosPixel();
700 Size aAbsoluteSize = m_aAbsolute.GetSizePixel();
702 DrawLine(Point(aAbsolutePos.X() - 1, 0 ),
703 Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
705 DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ),
706 Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
709 void DbGridControl::NavigationBar::StateChanged( StateChangedType nType )
711 Control::StateChanged( nType );
713 Window* pWindows[] = { &m_aRecordText,
714 &m_aAbsolute,
715 &m_aRecordOf,
716 &m_aRecordCount,
717 &m_aFirstBtn,
718 &m_aPrevBtn,
719 &m_aNextBtn,
720 &m_aLastBtn,
721 &m_aNewBtn
724 switch ( nType )
726 case STATE_CHANGE_MIRRORING:
728 bool bIsRTLEnabled = IsRTLEnabled();
729 for ( size_t i=0; i < (sizeof (pWindows) / sizeof(pWindows[0])); ++i )
730 pWindows[i]->EnableRTL( bIsRTLEnabled );
732 break;
734 case STATE_CHANGE_ZOOM:
736 Fraction aZoom = GetZoom();
738 // not all of these controls need to know the new zoom, but to be sure ...
739 Font aFont( GetSettings().GetStyleSettings().GetFieldFont() );
740 if ( IsControlFont() )
741 aFont.Merge( GetControlFont() );
743 for (size_t i=0; i < sizeof(pWindows)/sizeof(pWindows[0]); ++i)
745 pWindows[i]->SetZoom(aZoom);
746 pWindows[i]->SetZoomedPointFont(aFont);
749 SetZoomedPointFont( aFont );
751 // rearrange the controls
752 m_nDefaultWidth = ArrangeControls();
754 break;
758 DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor)
759 :m_bIsNew(false)
762 if (pCur && pCur->Is())
764 Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY);
765 DataColumn* pColumn;
766 for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
768 Reference< XPropertySet > xColSet(
769 xColumns->getByIndex(i), css::uno::UNO_QUERY);
770 pColumn = new DataColumn(xColSet);
771 m_aVariants.push_back( pColumn );
774 if (pCur->rowDeleted())
775 m_eStatus = GRS_DELETED;
776 else
778 if (bPaintCursor)
779 m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GRS_INVALID : GRS_CLEAN;
780 else
782 Reference< XPropertySet > xSet = pCur->getPropertySet();
783 if (xSet.is())
785 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
786 if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
787 m_eStatus = GRS_INVALID;
788 else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
789 m_eStatus = GRS_MODIFIED;
790 else
791 m_eStatus = GRS_CLEAN;
793 else
794 m_eStatus = GRS_INVALID;
797 if (!m_bIsNew && IsValid())
798 m_aBookmark = pCur->getBookmark();
799 else
800 m_aBookmark = Any();
802 else
803 m_eStatus = GRS_INVALID;
806 DbGridRow::~DbGridRow()
808 for ( size_t i = 0, n = m_aVariants.size(); i < n; ++i )
809 delete m_aVariants[ i ];
810 m_aVariants.clear();
813 void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor)
815 if (pCur && pCur->Is())
817 if (pCur->rowDeleted())
819 m_eStatus = GRS_DELETED;
820 m_bIsNew = false;
822 else
824 m_eStatus = GRS_CLEAN;
825 if (!bPaintCursor)
827 Reference< XPropertySet > xSet = pCur->getPropertySet();
828 DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
830 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
831 m_eStatus = GRS_MODIFIED;
832 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
834 else
835 m_bIsNew = false;
840 if (!m_bIsNew && IsValid())
841 m_aBookmark = pCur->getBookmark();
842 else
843 m_aBookmark = Any();
845 catch(SQLException&)
847 DBG_UNHANDLED_EXCEPTION();
848 m_aBookmark = Any();
849 m_eStatus = GRS_INVALID;
850 m_bIsNew = false;
853 else
855 m_aBookmark = Any();
856 m_eStatus = GRS_INVALID;
857 m_bIsNew = false;
861 DbGridControl::DbGridControl(
862 Reference< XComponentContext > _rxContext,
863 Window* pParent,
864 WinBits nBits)
865 :DbGridControl_Base(pParent, EBBF_NONE, nBits, DEFAULT_BROWSE_MODE )
866 ,m_xContext(_rxContext)
867 ,m_aBar(this)
868 ,m_nAsynAdjustEvent(0)
869 ,m_pDataSourcePropMultiplexer(NULL)
870 ,m_pDataSourcePropListener(NULL)
871 ,m_pFieldListeners(NULL)
872 ,m_pCursorDisposeListener(NULL)
873 ,m_pGridListener(NULL)
874 ,m_pDataCursor(NULL)
875 ,m_pSeekCursor(NULL)
876 ,m_nSeekPos(-1)
877 ,m_nTotalCount(-1)
878 ,m_aNullDate(OTypeConversionClient().getStandardDate())
879 ,m_nMode(DEFAULT_BROWSE_MODE)
880 ,m_nCurrentPos(-1)
881 ,m_nDeleteEvent(0)
882 ,m_nOptions(OPT_READONLY)
883 ,m_nOptionMask(OPT_INSERT | OPT_UPDATE | OPT_DELETE)
884 ,m_nLastColId((sal_uInt16)-1)
885 ,m_nLastRowId(-1)
886 ,m_bDesignMode(false)
887 ,m_bRecordCountFinal(false)
888 ,m_bMultiSelection(true)
889 ,m_bNavigationBar(true)
890 ,m_bSynchDisplay(true)
891 ,m_bForceROController(false)
892 ,m_bHandle(true)
893 ,m_bFilterMode(false)
894 ,m_bWantDestruction(false)
895 ,m_bInAdjustDataSource(false)
896 ,m_bPendingAdjustRows(false)
897 ,m_bHideScrollbars( false )
898 ,m_bUpdating(false)
901 OUString sName(SVX_RESSTR(RID_STR_NAVIGATIONBAR));
902 m_aBar.SetAccessibleName(sName);
903 m_aBar.Show();
904 ImplInitWindow( InitAll );
907 void DbGridControl::InsertHandleColumn()
909 // BrowseBox has problems when painting without a handleColumn (hide it here)
910 if (HasHandle())
911 BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString()));
912 else
913 BrowseBox::InsertHandleColumn(0);
916 void DbGridControl::Init()
918 BrowserHeader* pNewHeader = CreateHeaderBar(this);
919 pHeader->SetMouseTransparent(false);
921 SetHeaderBar(pNewHeader);
922 SetMode(m_nMode);
923 SetCursorColor(Color(0xFF, 0, 0));
925 InsertHandleColumn();
928 DbGridControl::~DbGridControl()
930 RemoveColumns();
933 m_bWantDestruction = true;
934 osl::MutexGuard aGuard(m_aDestructionSafety);
935 if (m_pFieldListeners)
936 DisconnectFromFields();
937 if (m_pCursorDisposeListener)
939 delete m_pCursorDisposeListener;
940 m_pCursorDisposeListener = NULL;
944 if (m_nDeleteEvent)
945 Application::RemoveUserEvent(m_nDeleteEvent);
947 if (m_pDataSourcePropMultiplexer)
949 m_pDataSourcePropMultiplexer->dispose();
950 m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
951 delete m_pDataSourcePropListener;
952 m_pDataSourcePropMultiplexer = NULL;
953 m_pDataSourcePropListener = NULL;
955 m_xRowSetListener.clear();
957 delete m_pDataCursor;
958 delete m_pSeekCursor;
962 void DbGridControl::StateChanged( StateChangedType nType )
964 DbGridControl_Base::StateChanged( nType );
966 switch (nType)
968 case STATE_CHANGE_MIRRORING:
969 ImplInitWindow( InitWritingMode );
970 Invalidate();
971 break;
973 case STATE_CHANGE_ZOOM:
975 ImplInitWindow( InitFontFacet );
977 // and give it a chance to rearrange
978 Point aPoint = GetControlArea().TopLeft();
979 sal_uInt16 nX = (sal_uInt16)aPoint.X();
980 ArrangeControls(nX, (sal_uInt16)aPoint.Y());
981 ReserveControlArea((sal_uInt16)nX);
983 break;
984 case STATE_CHANGE_CONTROLFONT:
985 ImplInitWindow( InitFontFacet );
986 Invalidate();
987 break;
988 case STATE_CHANGE_CONTROLFOREGROUND:
989 ImplInitWindow( InitForeground );
990 Invalidate();
991 break;
992 case STATE_CHANGE_CONTROLBACKGROUND:
993 ImplInitWindow( InitBackground );
994 Invalidate();
995 break;
999 void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
1001 DbGridControl_Base::DataChanged( rDCEvt );
1002 if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS ) &&
1003 (rDCEvt.GetFlags() & SETTINGS_STYLE) )
1005 ImplInitWindow( InitAll );
1006 Invalidate();
1010 void DbGridControl::Select()
1012 DbGridControl_Base::Select();
1014 // as the selected rows may have changed, udate the according display in our navigation bar
1015 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1017 if (m_pGridListener)
1018 m_pGridListener->selectionChanged();
1021 void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
1023 for ( size_t i = 0; i < m_aColumns.size(); ++i )
1025 DbGridColumn* pCol = m_aColumns[ i ];
1026 if (pCol)
1027 pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
1030 if ( ( _eInitWhat & InitWritingMode ) != 0 )
1032 if ( m_bNavigationBar )
1034 m_aBar.EnableRTL( IsRTLEnabled() );
1038 if ( ( _eInitWhat & InitFontFacet ) != 0 )
1040 if ( m_bNavigationBar )
1042 Font aFont = m_aBar.GetSettings().GetStyleSettings().GetFieldFont();
1043 if ( IsControlFont() )
1044 m_aBar.SetControlFont( GetControlFont() );
1045 else
1046 m_aBar.SetControlFont();
1048 m_aBar.SetZoom( GetZoom() );
1052 if ( ( _eInitWhat & InitBackground ) != 0 )
1054 if (IsControlBackground())
1056 GetDataWindow().SetBackground(GetControlBackground());
1057 GetDataWindow().SetControlBackground(GetControlBackground());
1058 GetDataWindow().SetFillColor(GetControlBackground());
1060 else
1062 GetDataWindow().SetControlBackground();
1063 GetDataWindow().SetFillColor(GetFillColor());
1068 void DbGridControl::RemoveRows(bool bNewCursor)
1070 // Did the data cursor change?
1071 if (!bNewCursor)
1073 DELETEZ(m_pSeekCursor);
1074 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1075 m_nCurrentPos = m_nSeekPos = -1;
1076 m_nOptions = OPT_READONLY;
1078 RowRemoved(0, GetRowCount(), false);
1079 m_nTotalCount = -1;
1081 else
1083 RemoveRows();
1087 void DbGridControl::RemoveRows()
1089 // we're going to remove all columns and all row, so deactivate the current cell
1090 if (IsEditing())
1091 DeactivateCell();
1093 // de-initialize all columns
1094 // if there are columns, free all controllers
1095 for (size_t i = 0; i < m_aColumns.size(); i++)
1096 m_aColumns[ i ]->Clear();
1098 DELETEZ(m_pSeekCursor);
1099 DELETEZ(m_pDataCursor);
1101 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1102 m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1;
1103 m_nOptions = OPT_READONLY;
1105 // reset number of sentences to zero in the browser
1106 DbGridControl_Base::RemoveRows();
1107 m_aBar.InvalidateAll(m_nCurrentPos, true);
1110 void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
1112 // positioning of the controls
1113 if (m_bNavigationBar)
1115 nX = m_aBar.GetDefaultWidth();
1116 Rectangle aRect(GetControlArea());
1117 m_aBar.SetPosSizePixel(Point(0,nY + 1), Size(nX, aRect.GetSize().Height() - 1));
1121 void DbGridControl::EnableHandle(bool bEnable)
1123 if (m_bHandle == bEnable)
1124 return;
1126 // HandleColumn is only hidden because there are a lot of problems while painting otherwise
1127 RemoveColumn( HandleColumnId );
1128 m_bHandle = bEnable;
1129 InsertHandleColumn();
1132 namespace
1134 bool adjustModeForScrollbars( BrowserMode& _rMode, bool _bNavigationBar, bool _bHideScrollbars )
1136 BrowserMode nOldMode = _rMode;
1138 if ( !_bNavigationBar )
1140 _rMode &= ~BROWSER_AUTO_HSCROLL;
1143 if ( _bHideScrollbars )
1145 _rMode |= ( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1146 _rMode &= ~( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1148 else
1150 _rMode |= ( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1151 _rMode &= ~( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1154 // note: if we have a navigation bar, we always have a AUTO_HSCROLL. In particular,
1155 // _bHideScrollbars is ignored then
1156 if ( _bNavigationBar )
1158 _rMode |= BROWSER_AUTO_HSCROLL;
1159 _rMode &= ~BROWSER_NO_HSCROLL;
1162 return nOldMode != _rMode;
1166 void DbGridControl::EnableNavigationBar(bool bEnable)
1168 if (m_bNavigationBar == bEnable)
1169 return;
1171 m_bNavigationBar = bEnable;
1173 if (bEnable)
1175 m_aBar.Show();
1176 m_aBar.Enable();
1177 m_aBar.InvalidateAll(m_nCurrentPos, true);
1179 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1180 SetMode( m_nMode );
1182 // get size of the reserved ControlArea
1183 Point aPoint = GetControlArea().TopLeft();
1184 sal_uInt16 nX = (sal_uInt16)aPoint.X();
1186 ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1187 ReserveControlArea((sal_uInt16)nX);
1189 else
1191 m_aBar.Hide();
1192 m_aBar.Disable();
1194 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1195 SetMode( m_nMode );
1197 ReserveControlArea();
1201 sal_uInt16 DbGridControl::SetOptions(sal_uInt16 nOpt)
1203 DBG_ASSERT(!m_xCurrentRow || !m_xCurrentRow->IsModified(),
1204 "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
1206 // for the next setDataSource (which is triggered by a refresh, for instance)
1207 m_nOptionMask = nOpt;
1209 // normalize the new options
1210 Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
1211 if (xDataSourceSet.is())
1213 // check what kind of options are available
1214 sal_Int32 nPrivileges = 0;
1215 xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1216 if ((nPrivileges & Privilege::INSERT) == 0)
1217 nOpt &= ~OPT_INSERT;
1218 if ((nPrivileges & Privilege::UPDATE) == 0)
1219 nOpt &= ~OPT_UPDATE;
1220 if ((nPrivileges & Privilege::DELETE) == 0)
1221 nOpt &= ~OPT_DELETE;
1223 else
1224 nOpt = OPT_READONLY;
1226 // need to do something after that ?
1227 if (nOpt == m_nOptions)
1228 return m_nOptions;
1230 // the 'update' option only affects our BrowserMode (with or w/o focus rect)
1231 BrowserMode nNewMode = m_nMode;
1232 if ((m_nMode & BROWSER_CURSOR_WO_FOCUS) == 0)
1234 if (nOpt & OPT_UPDATE)
1235 nNewMode |= BROWSER_HIDECURSOR;
1236 else
1237 nNewMode &= ~BROWSER_HIDECURSOR;
1239 else
1240 nNewMode &= ~BROWSER_HIDECURSOR;
1241 // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
1243 if (nNewMode != m_nMode)
1245 SetMode(nNewMode);
1246 m_nMode = nNewMode;
1249 // _after_ setting the mode because this results in an ActivateCell
1250 DeactivateCell();
1252 bool bInsertChanged = (nOpt & OPT_INSERT) != (m_nOptions & OPT_INSERT);
1253 m_nOptions = nOpt;
1254 // we need to set this before the code below because it indirectly uses m_nOptions
1256 // the 'insert' option affects our empty row
1257 if (bInsertChanged)
1259 if (m_nOptions & OPT_INSERT)
1260 { // the insert option is to be set
1261 m_xEmptyRow = new DbGridRow();
1262 RowInserted(GetRowCount(), 1, true);
1264 else
1265 { // the insert option is to be reset
1266 m_xEmptyRow = NULL;
1267 if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
1268 GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
1269 RowRemoved(GetRowCount(), 1, true);
1273 // the 'delete' options has no immediate consequences
1275 ActivateCell();
1276 Invalidate();
1277 return m_nOptions;
1280 void DbGridControl::ForceHideScrollbars( bool _bForce )
1282 if ( m_bHideScrollbars == _bForce )
1283 return;
1285 m_bHideScrollbars = _bForce;
1287 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1288 SetMode( m_nMode );
1291 void DbGridControl::EnablePermanentCursor(bool bEnable)
1293 if (IsPermanentCursorEnabled() == bEnable)
1294 return;
1296 if (bEnable)
1298 m_nMode &= ~BROWSER_HIDECURSOR; // without this BROWSER_CURSOR_WO_FOCUS won't have any affect
1299 m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1301 else
1303 if (m_nOptions & OPT_UPDATE)
1304 m_nMode |= BROWSER_HIDECURSOR; // no cursor at all
1305 else
1306 m_nMode &= ~BROWSER_HIDECURSOR; // at least the "non-permanent" cursor
1308 m_nMode &= ~BROWSER_CURSOR_WO_FOCUS;
1310 SetMode(m_nMode);
1312 bool bWasEditing = IsEditing();
1313 DeactivateCell();
1314 if (bWasEditing)
1315 ActivateCell();
1318 bool DbGridControl::IsPermanentCursorEnabled() const
1320 return ((m_nMode & BROWSER_CURSOR_WO_FOCUS) != 0) && ((m_nMode & BROWSER_HIDECURSOR) == 0);
1323 void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
1325 if ((GetCurColumnId() == _nColId) && IsEditing())
1326 { // the controller which is currently active needs to be refreshed
1327 DeactivateCell();
1328 ActivateCell();
1332 void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, sal_uInt16 nOpts)
1334 if (!_xCursor.is() && !m_pDataCursor)
1335 return;
1337 if (m_pDataSourcePropMultiplexer)
1339 m_pDataSourcePropMultiplexer->dispose();
1340 m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
1341 delete m_pDataSourcePropListener;
1342 m_pDataSourcePropMultiplexer = NULL;
1343 m_pDataSourcePropListener = NULL;
1345 m_xRowSetListener.clear();
1347 // is the new cursor valid ?
1348 // the cursor is only valid if it contains some columns
1349 // if there is no cursor or the cursor is not valid we have to clean up an leave
1350 if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY)->getColumns()->hasElements())
1352 RemoveRows();
1353 return;
1356 // did the data cursor change?
1357 sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
1359 SetUpdateMode(false);
1360 RemoveRows();
1361 DisconnectFromFields();
1363 DELETEZ(m_pCursorDisposeListener);
1366 ::osl::MutexGuard aGuard(m_aAdjustSafety);
1367 if (m_nAsynAdjustEvent)
1369 // the adjust was thought to work with the old cursor which we don't have anymore
1370 RemoveUserEvent(m_nAsynAdjustEvent);
1371 m_nAsynAdjustEvent = 0;
1375 // get a new formatter and data cursor
1376 m_xFormatter = NULL;
1377 OStaticDataAccessTools aStaticTools;
1378 Reference< ::com::sun::star::util::XNumberFormatsSupplier > xSupplier = aStaticTools.getNumberFormats(aStaticTools.getRowSetConnection(_xCursor), true);
1379 if (xSupplier.is())
1381 m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter >(
1382 ::com::sun::star::util::NumberFormatter::create(m_xContext),
1383 UNO_QUERY);
1384 m_xFormatter->attachNumberFormatsSupplier(xSupplier);
1386 // retrieve the datebase of the Numberformatter
1389 xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
1391 catch(Exception&)
1396 m_pDataCursor = new CursorWrapper(_xCursor);
1398 // now create a cursor for painting rows
1399 // we need that cursor only if we are not in insert only mode
1400 Reference< XResultSet > xClone;
1401 Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
1404 xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
1406 catch(Exception&)
1409 if (xClone.is())
1410 m_pSeekCursor = new CursorWrapper(xClone);
1412 // property listening on the data source
1413 // (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
1414 // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
1415 // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
1416 // and forwards the property changes to a our special method "DataSourcePropertyChanged".)
1417 if (m_pDataCursor)
1419 m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
1420 m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
1421 m_pDataSourcePropMultiplexer->acquire();
1422 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
1423 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
1426 BrowserMode nOldMode = m_nMode;
1427 if (m_pSeekCursor)
1431 Reference< XPropertySet > xSet(_xCursor, UNO_QUERY);
1432 if (xSet.is())
1434 // check what kind of options are available
1435 sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
1436 xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
1438 if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
1440 sal_Int32 nPrivileges = 0;
1441 xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1443 // Insert Option should be set if insert only otherwise you won't see any rows
1444 // and no insertion is possible
1445 if ((m_nOptionMask & OPT_INSERT) && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & OPT_INSERT))
1446 m_nOptions |= OPT_INSERT;
1447 if ((m_nOptionMask & OPT_UPDATE) && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & OPT_UPDATE))
1448 m_nOptions |= OPT_UPDATE;
1449 if ((m_nOptionMask & OPT_DELETE) && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & OPT_DELETE))
1450 m_nOptions |= OPT_DELETE;
1454 catch( const Exception& )
1456 DBG_UNHANDLED_EXCEPTION();
1459 bool bPermanentCursor = IsPermanentCursorEnabled();
1460 m_nMode = DEFAULT_BROWSE_MODE;
1462 if ( bPermanentCursor )
1464 m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1465 m_nMode &= ~BROWSER_HIDECURSOR;
1467 else
1469 // updates are allowed -> no focus rectangle
1470 if ( m_nOptions & OPT_UPDATE )
1471 m_nMode |= BROWSER_HIDECURSOR;
1474 if ( m_bMultiSelection )
1475 m_nMode |= BROWSER_MULTISELECTION;
1476 else
1477 m_nMode &= ~BROWSER_MULTISELECTION;
1479 adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
1481 Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY);
1482 if (xSupplyColumns.is())
1483 InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
1485 ConnectToFields();
1488 sal_uInt32 nRecordCount(0);
1490 if (m_pSeekCursor)
1492 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1493 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1494 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1496 m_xRowSetListener = new RowSetEventListener(this);
1497 Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
1498 if ( xChangeBroad.is( ) )
1499 xChangeBroad->addRowsChangeListener(m_xRowSetListener);
1502 // insert the currently known rows
1503 // and one row if we are able to insert rows
1504 if (m_nOptions & OPT_INSERT)
1506 // insert the empty row for insertion
1507 m_xEmptyRow = new DbGridRow();
1508 ++nRecordCount;
1510 if (nRecordCount)
1512 m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor, true);
1513 m_xDataRow = new DbGridRow(m_pDataCursor, false);
1514 RowInserted(0, nRecordCount, false);
1516 if (m_xSeekRow->IsValid())
1519 m_nSeekPos = m_pSeekCursor->getRow() - 1;
1521 catch( const Exception& )
1523 DBG_UNHANDLED_EXCEPTION();
1524 m_nSeekPos = -1;
1527 else
1529 // no rows so we don't need a seekcursor
1530 DELETEZ(m_pSeekCursor);
1534 // go to the old column
1535 if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
1536 nCurPos = 0;
1538 // Column zero is a valid choice and guaranteed to exist,
1539 // but invisible to the user; if we have at least one
1540 // user-visible column, go to that one.
1541 if (nCurPos == 0 && ColCount() > 1)
1542 nCurPos = 1;
1544 // there are rows so go to the selected current column
1545 if (nRecordCount)
1546 GoToRowColumnId(0, GetColumnId(nCurPos));
1547 // else stop the editing if necessary
1548 else if (IsEditing())
1549 DeactivateCell();
1551 // now reset the mode
1552 if (m_nMode != nOldMode)
1553 SetMode(m_nMode);
1555 // RecalcRows was already called while resizing
1556 if (!IsResizing() && GetRowCount())
1557 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1559 m_aBar.InvalidateAll(m_nCurrentPos, true);
1560 SetUpdateMode(true);
1562 // start listening on the seek cursor
1563 if (m_pSeekCursor)
1564 m_pCursorDisposeListener = new DisposeListenerGridBridge(*this, Reference< XComponent > ((Reference< XInterface >)*m_pSeekCursor, UNO_QUERY), 0);
1567 void DbGridControl::RemoveColumns()
1569 if ( IsEditing() )
1570 DeactivateCell();
1572 for (size_t i = 0, n = m_aColumns.size(); i < n; i++)
1573 delete m_aColumns[ i ];
1574 m_aColumns.clear();
1576 DbGridControl_Base::RemoveColumns();
1579 DbGridColumn* DbGridControl::CreateColumn(sal_uInt16 nId) const
1581 return new DbGridColumn(nId, *(DbGridControl*)this);
1584 sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
1586 DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
1587 sal_uInt16 nRealPos = nModelPos;
1588 if (nModelPos != HEADERBAR_APPEND)
1590 // calc the view pos. we can't use our converting functions because the new column
1591 // has no VCL-representation, yet.
1592 sal_Int16 nViewPos = nModelPos;
1593 while (nModelPos--)
1595 if ( m_aColumns[ nModelPos ]->IsHidden() )
1596 --nViewPos;
1598 // restore nModelPos, we need it later
1599 nModelPos = nRealPos;
1600 // the position the base class gets is the view pos + 1 (because of the handle column)
1601 nRealPos = nViewPos + 1;
1604 // calculate the new id
1605 for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && (nId <= m_aColumns.size()); ++nId)
1607 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !");
1608 // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
1610 DbGridControl_Base::AppendColumn(rName, nWidth, nRealPos, nId);
1611 if (nModelPos == HEADERBAR_APPEND)
1612 m_aColumns.push_back( CreateColumn(nId) );
1613 else
1615 DbGridColumns::iterator it = m_aColumns.begin();
1616 ::std::advance( it, nModelPos );
1617 m_aColumns.insert( it, CreateColumn(nId) );
1620 return nId;
1623 void DbGridControl::RemoveColumn(sal_uInt16 nId)
1625 DbGridControl_Base::RemoveColumn(nId);
1627 const sal_uInt16 nIndex = GetModelColumnPos(nId);
1628 if(nIndex != GRID_COLUMN_NOT_FOUND)
1630 delete m_aColumns[nIndex];
1631 m_aColumns.erase( m_aColumns.begin()+nIndex );
1635 void DbGridControl::ColumnMoved(sal_uInt16 nId)
1637 DbGridControl_Base::ColumnMoved(nId);
1639 // remove the col from the model
1640 sal_uInt16 nOldModelPos = GetModelColumnPos(nId);
1641 #ifdef DBG_UTIL
1642 DbGridColumn* pCol = m_aColumns[ (sal_uInt32)nOldModelPos ];
1643 DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
1644 #endif
1646 // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
1647 // so the method won't work (in fact it would return the old model pos)
1649 // the new view pos is calculated easily
1650 sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
1652 // from that we can compute the new model pos
1653 sal_uInt16 nNewModelPos;
1654 for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
1656 if (!m_aColumns[ nNewModelPos ]->IsHidden())
1658 if (!nNewViewPos)
1659 break;
1660 else
1661 --nNewViewPos;
1664 DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
1666 // this will work. of course the model isn't fully consistent with our view right now, but let's
1667 // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
1668 // other case we can use analogue arguments).
1669 // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
1670 // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
1671 // within this range is constant, so we may calculate the view pos from the model pos in the above way.
1673 // for instance, let's look at a grid with six columns where the third one is hidden. this will
1674 // initially look like this :
1676 // +---+---+---+---+---+---+
1677 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1678 // +---+---+---+---+---+---+
1679 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1680 // +---+---+---+---+---+---+
1681 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1682 // +---+---+---+---+---+---+
1684 // if we move the column at (view) pos 1 to (view) pos 3 we have :
1686 // +---+---+---+---+---+---+
1687 // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet
1688 // +---+---+---+---+---+---+
1689 // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes
1690 // +---+---+---+---+---+---+
1691 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1692 // +---+---+---+---+---+---+
1694 // or, sorted by the out-of-date model positions :
1696 // +---+---+---+---+---+---+
1697 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1698 // +---+---+---+---+---+---+
1699 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1700 // +---+---+---+---+---+---+
1701 // view pos | 0 | 3 | - | 1 | 2 | 4 |
1702 // +---+---+---+---+---+---+
1704 // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
1705 // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
1706 // exactly the pos where we have to re-insert our column's model, so it looks ike this :
1708 // +---+---+---+---+---+---+
1709 // model pos | 0 |*1*| 2 | 3 | 4 | 5 |
1710 // +---+---+---+---+---+---+
1711 // ID | 1 | 3 | 4 | 5 | 2 | 6 |
1712 // +---+---+---+---+---+---+
1713 // view pos | 0 | - | 1 | 2 | 3 | 4 |
1714 // +---+---+---+---+---+---+
1716 // Now, all is consistent again.
1717 // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
1718 // the user expected the latter but there really is no good argument against our method ;) ...)
1720 // And no, this large explanation isn't just because I wanted to play a board game or something like
1721 // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
1722 // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
1724 DbGridColumn* temp = m_aColumns[ nOldModelPos ];
1726 DbGridColumns::iterator it = m_aColumns.begin();
1727 ::std::advance( it, nOldModelPos );
1728 m_aColumns.erase( it );
1730 it = m_aColumns.begin();
1731 ::std::advance( it, nNewModelPos );
1732 m_aColumns.insert( it, temp );
1735 bool DbGridControl::SeekRow(long nRow)
1737 // in filter mode or in insert only mode we don't have any cursor!
1738 if ( !SeekCursor( nRow ) )
1739 return false;
1741 if ( IsFilterMode() )
1743 DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
1744 m_xPaintRow = m_xEmptyRow;
1746 else
1748 // on the current position we have to take the current row for display as we want
1749 // to have the most recent values for display
1750 if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
1751 m_xPaintRow = m_xCurrentRow;
1752 // seek to the empty insert row
1753 else if ( IsInsertionRow( nRow ) )
1754 m_xPaintRow = m_xEmptyRow;
1755 else
1757 m_xSeekRow->SetState( m_pSeekCursor, true );
1758 m_xPaintRow = m_xSeekRow;
1762 DbGridControl_Base::SeekRow(nRow);
1764 return m_nSeekPos >= 0;
1767 // Is called whenever the visible amount of data changes
1768 void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen )
1770 RecalcRows(nNewTopRow, nLinesOnScreen, false);
1773 void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
1775 // Wenn kein Cursor -> keine Rows im Browser.
1776 if (!m_pSeekCursor)
1778 DBG_ASSERT(GetRowCount() == 0,"DbGridControl: ohne Cursor darf es keine Rows geben");
1779 return;
1782 // ignore any implicitly made updates
1783 bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
1784 if (bDisablePaint)
1785 EnablePaint(false);
1787 // adjust cache to the visible area
1788 Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
1789 sal_Int32 nCacheSize = 0;
1790 xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
1791 bool bCacheAligned = false;
1792 // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
1793 // positioned on the first sentence
1794 long nDelta = nNewTopRow - GetTopRow();
1795 // limit for relative positioning
1796 long nLimit = (nCacheSize) ? nCacheSize / 2 : 0;
1798 // more lines on screen than in cache
1799 if (nLimit < nLinesOnScreen)
1801 Any aCacheSize;
1802 aCacheSize <<= sal_Int32(nLinesOnScreen*2);
1803 xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
1804 // here we need to update the cursor for sure
1805 bUpdateCursor = true;
1806 bCacheAligned = true;
1807 nLimit = nLinesOnScreen;
1810 // In the following, all positionings are done as it is
1811 // ensured that there are enough lines in the data cache
1813 // window goes downwards with less than two windows difference or
1814 // the cache was updated and no rowcount yet
1815 if (nDelta < nLimit && (nDelta > 0
1816 || (bCacheAligned && m_nTotalCount < 0)) )
1817 SeekCursor(nNewTopRow + nLinesOnScreen - 1, false);
1818 else if (nDelta < 0 && std::abs(nDelta) < nLimit)
1819 SeekCursor(nNewTopRow, false);
1820 else if (nDelta != 0 || bUpdateCursor)
1821 SeekCursor(nNewTopRow, true);
1823 AdjustRows();
1825 // ignore any updates implicit made
1826 EnablePaint(true);
1829 void DbGridControl::RowInserted(long nRow, long nNumRows, bool bDoPaint, bool bKeepSelection)
1831 if (nNumRows)
1833 if (m_bRecordCountFinal && m_nTotalCount < 0)
1835 // if we have an insert row we have to reduce to count by 1
1836 // as the total count reflects only the existing rows in database
1837 m_nTotalCount = GetRowCount() + nNumRows;
1838 if (m_xEmptyRow.Is())
1839 --m_nTotalCount;
1841 else if (m_nTotalCount >= 0)
1842 m_nTotalCount += nNumRows;
1844 DbGridControl_Base::RowInserted(nRow, nNumRows, bDoPaint, bKeepSelection);
1845 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1849 void DbGridControl::RowRemoved(long nRow, long nNumRows, bool bDoPaint)
1851 if (nNumRows)
1853 if (m_bRecordCountFinal && m_nTotalCount < 0)
1855 m_nTotalCount = GetRowCount() - nNumRows;
1856 // if we have an insert row reduce by 1
1857 if (m_xEmptyRow.Is())
1858 --m_nTotalCount;
1860 else if (m_nTotalCount >= 0)
1861 m_nTotalCount -= nNumRows;
1863 DbGridControl_Base::RowRemoved(nRow, nNumRows, bDoPaint);
1864 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1868 void DbGridControl::AdjustRows()
1870 if (!m_pSeekCursor)
1871 return;
1873 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1875 // refresh RecordCount
1876 sal_Int32 nRecordCount = 0;
1877 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1878 if (!m_bRecordCountFinal)
1879 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1881 // Did the number of rows change?
1882 // Here we need to consider that there might be an additional row for adding new data sets
1884 // add additional AppendRow for insertion
1885 if (m_nOptions & OPT_INSERT)
1886 ++nRecordCount;
1888 // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
1889 if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
1890 m_xCurrentRow->IsNew())
1891 ++nRecordCount;
1892 // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this
1893 // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount
1894 // and a second time here (60787 - FS)
1896 if (nRecordCount != GetRowCount())
1898 long nDelta = GetRowCount() - (long)nRecordCount;
1899 if (nDelta > 0) // too many
1901 RowRemoved(GetRowCount() - nDelta, nDelta, false);
1902 // some rows are gone, thus, repaint starting at the current position
1903 Invalidate();
1905 sal_Int32 nNewPos = AlignSeekCursor();
1906 if (m_bSynchDisplay)
1907 DbGridControl_Base::GoToRow(nNewPos);
1909 SetCurrent(nNewPos);
1910 // there are rows so go to the selected current column
1911 if (nRecordCount)
1912 GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
1913 if (!IsResizing() && GetRowCount())
1914 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1915 m_aBar.InvalidateAll(m_nCurrentPos, true);
1917 else // too few
1918 RowInserted(GetRowCount(), -nDelta, true);
1921 if (m_bRecordCountFinal && m_nTotalCount < 0)
1923 if (m_nOptions & OPT_INSERT)
1924 m_nTotalCount = GetRowCount() - 1;
1925 else
1926 m_nTotalCount = GetRowCount();
1928 m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1931 DbGridControl_Base::RowStatus DbGridControl::GetRowStatus(long nRow) const
1933 if (IsFilterRow(nRow))
1934 return DbGridControl_Base::FILTER;
1935 else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
1937 // neue Zeile
1938 if (!IsValid(m_xCurrentRow))
1939 return DbGridControl_Base::DELETED;
1940 else if (IsModified())
1941 return DbGridControl_Base::MODIFIED;
1942 else if (m_xCurrentRow->IsNew())
1943 return DbGridControl_Base::CURRENTNEW;
1944 else
1945 return DbGridControl_Base::CURRENT;
1947 else if (IsInsertionRow(nRow))
1948 return DbGridControl_Base::NEW;
1949 else if (!IsValid(m_xSeekRow))
1950 return DbGridControl_Base::DELETED;
1951 else
1952 return DbGridControl_Base::CLEAN;
1955 void DbGridControl::PaintStatusCell(OutputDevice& rDev, const Rectangle& rRect) const
1957 DbGridControl_Base::PaintStatusCell(rDev, rRect);
1960 void DbGridControl::PaintCell(OutputDevice& rDev, const Rectangle& rRect, sal_uInt16 nColumnId) const
1962 if (!IsValid(m_xPaintRow))
1963 return;
1965 size_t Location = GetModelColumnPos(nColumnId);
1966 DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
1967 if (pColumn)
1969 Rectangle aArea(rRect);
1970 if ((GetMode() & BROWSER_CURSOR_WO_FOCUS) == BROWSER_CURSOR_WO_FOCUS)
1972 aArea.Top() += 1;
1973 aArea.Bottom() -= 1;
1975 pColumn->Paint(rDev, aArea, m_xPaintRow, getNumberFormatter());
1979 bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol)
1982 DeactivateCell( false );
1984 if ( m_pDataCursor
1985 && ( m_nCurrentPos != nNewRow )
1986 && !SetCurrent( nNewRow )
1989 ActivateCell();
1990 return false;
1993 if ( !DbGridControl_Base::CursorMoving( nNewRow, nNewCol ) )
1994 return false;
1996 return true;
1999 bool DbGridControl::SetCurrent(long nNewRow)
2001 // Each movement of the datacursor must start with BeginCursorAction and end with
2002 // EndCursorAction to block all notifications during the movement
2003 BeginCursorAction();
2007 // compare positions
2008 if (SeekCursor(nNewRow))
2010 if (IsFilterRow(nNewRow)) // special mode for filtering
2012 m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
2013 m_nCurrentPos = nNewRow;
2015 else
2017 bool bNewRowInserted = false;
2018 // Should we go to the insertrow ?
2019 if (IsInsertionRow(nNewRow))
2021 // to we need to move the cursor to the insert row?
2022 // we need to insert the if the current row isn't the insert row or if the
2023 // cursor triggered the move by itselt and we need a reinitialization of the row
2024 Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
2025 if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
2027 Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
2028 xUpdateCursor->moveToInsertRow();
2030 bNewRowInserted = true;
2032 else
2035 if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
2037 Any aBookmark = m_pSeekCursor->getBookmark();
2038 if (!m_xCurrentRow || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
2040 // adjust the cursor to the new desired row
2041 if (!m_pDataCursor->moveToBookmark(aBookmark))
2043 EndCursorAction();
2044 return false;
2049 m_xDataRow->SetState(m_pDataCursor, false);
2050 m_xCurrentRow = m_xDataRow;
2052 long nPaintPos = -1;
2053 // do we have to repaint the last regular row in case of setting defaults or autovalues
2054 if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
2055 nPaintPos = m_nCurrentPos;
2057 m_nCurrentPos = nNewRow;
2059 // repaint the new row to display all defaults
2060 if (bNewRowInserted)
2061 RowModified(m_nCurrentPos);
2062 if (nPaintPos >= 0)
2063 RowModified(nPaintPos);
2066 else
2068 OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
2069 EndCursorAction();
2070 return false;
2073 catch ( const Exception& )
2075 DBG_UNHANDLED_EXCEPTION();
2076 EndCursorAction();
2077 return false;
2080 EndCursorAction();
2081 return true;
2084 void DbGridControl::CursorMoved()
2087 // cursor movement due to deletion or insertion of rows
2088 if (m_pDataCursor && m_nCurrentPos != GetCurRow())
2090 DeactivateCell(true);
2091 SetCurrent(GetCurRow());
2094 DbGridControl_Base::CursorMoved();
2095 m_aBar.InvalidateAll(m_nCurrentPos);
2097 // select the new column when they moved
2098 if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
2100 SelectColumnId( GetCurColumnId() );
2103 if ( m_nLastColId != GetCurColumnId() )
2104 onColumnChange();
2105 m_nLastColId = GetCurColumnId();
2107 if ( m_nLastRowId != GetCurRow() )
2108 onRowChange();
2109 m_nLastRowId = GetCurRow();
2112 void DbGridControl::onRowChange()
2114 // not interested in
2117 void DbGridControl::onColumnChange()
2119 if ( m_pGridListener )
2120 m_pGridListener->columnChanged();
2123 void DbGridControl::setDisplaySynchron(bool bSync)
2125 if (bSync != m_bSynchDisplay)
2127 m_bSynchDisplay = bSync;
2128 if (m_bSynchDisplay)
2129 AdjustDataSource(false);
2133 void DbGridControl::AdjustDataSource(bool bFull)
2135 SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
2136 SolarMutexGuard aGuard;
2137 // If the current row is recalculated at the moment, do not adjust
2139 if (bFull)
2140 m_xCurrentRow = NULL;
2141 // if we are on the same row only repaint
2142 // but this is only possible for rows which are not inserted, in that case the comparison result
2143 // may not be correct
2144 else
2145 if ( m_xCurrentRow.Is()
2146 && !m_xCurrentRow->IsNew()
2147 && !m_pDataCursor->isBeforeFirst()
2148 && !m_pDataCursor->isAfterLast()
2149 && !m_pDataCursor->rowDeleted()
2152 bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
2154 bool bDataCursorIsOnNew = false;
2155 m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
2157 if ( bEqualBookmarks && !bDataCursorIsOnNew )
2159 // position of my data cursor is the same as the position our current row points tpo
2160 // sync the status, repaint, done
2161 DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Fehler in den Datenzeilen");
2162 SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
2163 RowModified(m_nCurrentPos);
2164 return;
2168 // away from the data cursor's row
2169 if (m_xPaintRow == m_xCurrentRow)
2170 m_xPaintRow = m_xSeekRow;
2172 // not up-to-date row, thus, adjust completely
2173 if (!m_xCurrentRow)
2174 AdjustRows();
2176 sal_Int32 nNewPos = AlignSeekCursor();
2177 if (nNewPos < 0)// could not find any position
2178 return;
2180 m_bInAdjustDataSource = true;
2181 if (nNewPos != m_nCurrentPos)
2183 if (m_bSynchDisplay)
2184 DbGridControl_Base::GoToRow(nNewPos);
2186 if (!m_xCurrentRow.Is())
2187 // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned
2188 // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what
2189 // CurrentRow is corrected to point two rows down, so that GoToRow will point into
2190 // emptiness (since we are - purportedly - at the correct position)
2191 SetCurrent(nNewPos);
2193 else
2195 SetCurrent(nNewPos);
2196 RowModified(nNewPos);
2198 m_bInAdjustDataSource = false;
2200 // if the data cursor was moved from outside, this section is voided
2201 SetNoSelection();
2202 m_aBar.InvalidateAll(m_nCurrentPos, m_xCurrentRow.Is());
2205 sal_Int32 DbGridControl::AlignSeekCursor()
2207 // position SeekCursor onto the data cursor, no data transmission
2209 if (!m_pSeekCursor)
2210 return -1;
2212 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
2214 // now align the seek cursor and the data cursor
2215 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
2216 m_nSeekPos = GetRowCount() - 1;
2217 else
2221 if ( m_pDataCursor->isBeforeFirst() )
2223 // this is somewhat strange, but can nevertheless happen
2224 DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
2225 m_pSeekCursor->first();
2226 m_pSeekCursor->previous();
2227 m_nSeekPos = -1;
2229 else if ( m_pDataCursor->isAfterLast() )
2231 DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
2232 m_pSeekCursor->last();
2233 m_pSeekCursor->next();
2234 m_nSeekPos = -1;
2236 else
2238 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2239 if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
2240 // unfortunately, moveToBookmark might lead to a re-positioning of the seek
2241 // cursor (if the complex moveToBookmark with all its events fires an update
2242 // somewhere) -> retry
2243 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2244 // Now there is still the chance of a failure but it is less likely.
2245 // The alternative would be an loop until everything is fine - no good solution...
2246 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2249 catch(Exception&)
2253 return m_nSeekPos;
2256 bool DbGridControl::SeekCursor(long nRow, bool bAbsolute)
2258 // position SeekCursor onto the data cursor, no data transmission
2260 // additions for the filtermode
2261 if (IsFilterRow(nRow))
2263 m_nSeekPos = 0;
2264 return true;
2267 if (!m_pSeekCursor)
2268 return false;
2270 // is this an insertion?
2271 if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
2272 nRow >= m_nCurrentPos)
2274 // if so, scrolling down must be prevented as this is already the last data set!
2275 if (nRow == m_nCurrentPos)
2277 // no adjustment necessary
2278 m_nSeekPos = nRow;
2280 else if (IsInsertionRow(nRow)) // blank row for data insertion
2281 m_nSeekPos = nRow;
2283 else if (IsInsertionRow(nRow)) // blank row for data insertion
2284 m_nSeekPos = nRow;
2285 else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & OPT_INSERT) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
2286 m_nSeekPos = nRow;
2287 else
2289 bool bSuccess = false;
2290 long nSteps = 0;
2293 if ( m_pSeekCursor->rowDeleted() )
2295 // somebody deleted the current row of the seek cursor. Move it away from this row.
2296 m_pSeekCursor->next();
2297 if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
2298 bAbsolute = true;
2301 if ( !bAbsolute )
2303 DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
2304 "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
2305 nSteps = nRow - (m_pSeekCursor->getRow() - 1);
2306 bAbsolute = bAbsolute || (std::abs(nSteps) > 100);
2309 if ( bAbsolute )
2311 bSuccess = m_pSeekCursor->absolute(nRow + 1);
2312 if (bSuccess)
2313 m_nSeekPos = nRow;
2315 else
2317 if (nSteps > 0) // position onto the last needed data set
2319 if (m_pSeekCursor->isAfterLast())
2320 bSuccess = false;
2321 else if (m_pSeekCursor->isBeforeFirst())
2322 bSuccess = m_pSeekCursor->absolute(nSteps);
2323 else
2324 bSuccess = m_pSeekCursor->relative(nSteps);
2326 else if (nSteps < 0)
2328 if (m_pSeekCursor->isBeforeFirst())
2329 bSuccess = false;
2330 else if (m_pSeekCursor->isAfterLast())
2331 bSuccess = m_pSeekCursor->absolute(nSteps);
2332 else
2333 bSuccess = m_pSeekCursor->relative(nSteps);
2335 else
2337 m_nSeekPos = nRow;
2338 return true;
2342 catch(Exception&)
2344 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2349 if (!bSuccess)
2351 if (bAbsolute || nSteps > 0)
2353 if (m_pSeekCursor->isLast())
2354 bSuccess = true;
2355 else
2356 bSuccess = m_pSeekCursor->last();
2358 else
2360 if (m_pSeekCursor->isFirst())
2361 bSuccess = true;
2362 else
2363 bSuccess = m_pSeekCursor->first();
2367 if (bSuccess)
2368 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2369 else
2370 m_nSeekPos = -1;
2372 catch(Exception&)
2374 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2375 DBG_UNHANDLED_EXCEPTION();
2376 m_nSeekPos = -1; // no further data set available
2379 return m_nSeekPos == nRow;
2382 void DbGridControl::MoveToFirst()
2384 if (m_pSeekCursor && (GetCurRow() != 0))
2385 MoveToPosition(0);
2388 void DbGridControl::MoveToLast()
2390 if (!m_pSeekCursor)
2391 return;
2393 if (m_nTotalCount < 0) // no RecordCount, yet
2397 bool bRes = m_pSeekCursor->last();
2399 if (bRes)
2401 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2402 AdjustRows();
2405 catch(Exception&)
2410 // position onto the last data set not on a blank row
2411 if (m_nOptions & OPT_INSERT)
2413 if ((GetRowCount() - 1) > 0)
2414 MoveToPosition(GetRowCount() - 2);
2416 else if (GetRowCount())
2417 MoveToPosition(GetRowCount() - 1);
2420 void DbGridControl::MoveToPrev()
2422 long nNewRow = std::max(GetCurRow() - 1L, 0L);
2423 if (GetCurRow() != nNewRow)
2424 MoveToPosition(nNewRow);
2427 void DbGridControl::MoveToNext()
2429 if (!m_pSeekCursor)
2430 return;
2432 if (m_nTotalCount > 0)
2434 // move the data cursor to the right position
2435 long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
2436 if (GetCurRow() != nNewRow)
2437 MoveToPosition(nNewRow);
2439 else
2441 bool bOk = false;
2444 // try to move to next row
2445 // when not possible our paint cursor is already on the last row
2446 // then we must be sure that the data cursor is on the position
2447 // we call ourself again
2448 bOk = m_pSeekCursor->next();
2449 if (bOk)
2451 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2452 MoveToPosition(GetCurRow() + 1);
2455 catch(SQLException &)
2457 DBG_UNHANDLED_EXCEPTION();
2460 if(!bOk)
2462 AdjustRows();
2463 if (m_nTotalCount > 0) // only to avoid infinte recursion
2464 MoveToNext();
2469 void DbGridControl::MoveToPosition(sal_uInt32 nPos)
2471 if (!m_pSeekCursor)
2472 return;
2474 if (m_nTotalCount < 0 && (long)nPos >= GetRowCount())
2478 if (!m_pSeekCursor->absolute(nPos + 1))
2480 AdjustRows();
2481 return;
2483 else
2485 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2486 AdjustRows();
2489 catch(Exception&)
2491 return;
2494 DbGridControl_Base::GoToRow(nPos);
2495 m_aBar.InvalidateAll(m_nCurrentPos);
2498 void DbGridControl::AppendNew()
2500 if (!m_pSeekCursor || !(m_nOptions & OPT_INSERT))
2501 return;
2503 if (m_nTotalCount < 0) // no RecordCount, yet
2507 bool bRes = m_pSeekCursor->last();
2509 if (bRes)
2511 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2512 AdjustRows();
2515 catch(Exception&)
2517 return;
2521 long nNewRow = m_nTotalCount + 1;
2522 if (nNewRow > 0 && GetCurRow() != nNewRow)
2523 MoveToPosition(nNewRow - 1);
2526 void DbGridControl::SetDesignMode(bool bMode)
2528 if ((bool) IsDesignMode() != bMode)
2530 // adjust Enable/Disable for design mode so that the headerbar remains configurable
2531 if (bMode)
2533 if (!IsEnabled())
2535 Enable();
2536 GetDataWindow().Disable();
2539 else
2541 // disable completely
2542 if (!GetDataWindow().IsEnabled())
2543 Disable();
2546 m_bDesignMode = bMode;
2547 GetDataWindow().SetMouseTransparent(bMode);
2548 SetMouseTransparent(bMode);
2550 m_aBar.InvalidateAll(m_nCurrentPos, true);
2554 void DbGridControl::SetFilterMode(bool bMode)
2556 if (IsFilterMode() != bMode)
2558 m_bFilterMode = bMode;
2560 if (bMode)
2562 SetUpdateMode(false);
2564 // there is no cursor anymore
2565 if (IsEditing())
2566 DeactivateCell();
2567 RemoveRows(false);
2569 m_xEmptyRow = new DbGridRow();
2571 // setting the new filter controls
2572 for ( size_t i = 0; i < m_aColumns.size(); ++i )
2574 DbGridColumn* pCurCol = m_aColumns[ i ];
2575 if (!pCurCol->IsHidden())
2576 pCurCol->UpdateControl();
2579 // one row for filtering
2580 RowInserted(0, 1, true);
2581 SetUpdateMode(true);
2583 else
2584 setDataSource(Reference< XRowSet > ());
2588 OUString DbGridControl::GetCellText(long _nRow, sal_uInt16 _nColId) const
2590 size_t Location = GetModelColumnPos( _nColId );
2591 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2592 OUString sRet;
2593 if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
2594 sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
2595 return sRet;
2598 OUString DbGridControl::GetCurrentRowCellText(DbGridColumn* pColumn,const DbGridRowRef& _rRow) const
2600 // text output for a single row
2601 OUString aText;
2602 if ( pColumn && IsValid(_rRow) )
2603 aText = pColumn->GetCellText(_rRow, m_xFormatter);
2604 return aText;
2607 sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId)
2609 if (SeekRow(nRow))
2611 size_t Location = GetModelColumnPos( nColId );
2612 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2613 return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
2615 else
2616 return 30; // FIXME magic number for defaul cell width
2619 void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu)
2621 bool bDelete = (m_nOptions & OPT_DELETE) && GetSelectRowCount() && !IsCurrentAppending();
2622 // if only a blank row is selected than do not delete
2623 bDelete = bDelete && !((m_nOptions & OPT_INSERT) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
2625 rMenu.EnableItem(SID_FM_DELETEROWS, bDelete);
2626 rMenu.EnableItem(SID_FM_RECORD_SAVE, IsModified());
2628 // the undo is more difficult
2629 bool bCanUndo = IsModified();
2630 long nState = -1;
2631 if (m_aMasterStateProvider.IsSet())
2632 nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
2633 bCanUndo &= ( 0 != nState );
2635 rMenu.EnableItem(SID_FM_RECORD_UNDO, bCanUndo);
2638 void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& /*rMenu*/, sal_uInt16 nExecutionResult)
2640 switch (nExecutionResult)
2642 case SID_FM_DELETEROWS:
2643 // delete asynchronously
2644 if (m_nDeleteEvent)
2645 Application::RemoveUserEvent(m_nDeleteEvent);
2646 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
2647 break;
2648 case SID_FM_RECORD_UNDO:
2649 Undo();
2650 break;
2651 case SID_FM_RECORD_SAVE:
2652 SaveRow();
2653 break;
2654 default:
2655 break;
2659 void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
2661 SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
2662 SolarMutexGuard aGuard;
2663 // prop "IsModified" changed ?
2664 // during update don't care about the modified state
2665 if (!IsUpdating() && evt.PropertyName == FM_PROP_ISMODIFIED )
2667 Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
2668 DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
2669 bool bIsNew = false;
2670 if (xSource.is())
2671 bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
2673 if (bIsNew && m_xCurrentRow.Is())
2675 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 !");
2676 sal_Int32 nRecordCount = 0;
2677 xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
2678 if (::comphelper::getBOOL(evt.NewValue))
2679 { // modified state changed from sal_False to sal_True and we're on a insert row
2680 // -> we've to add a new grid row
2681 if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
2683 RowInserted(GetRowCount(), 1, true);
2684 InvalidateStatusCell(m_nCurrentPos);
2685 m_aBar.InvalidateAll(m_nCurrentPos);
2688 else
2689 { // modified state changed from sal_True to sal_False and we're on a insert row
2690 // we have two "new row"s at the moment : the one we're editing currently (where the current
2691 // column is the only dirty element) and a "new new" row which is completely clean. As the first
2692 // one is about to be cleaned, too, the second one is obsolete now.
2693 if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
2695 RowRemoved(GetRowCount() - 1, 1, true);
2696 InvalidateStatusCell(m_nCurrentPos);
2697 m_aBar.InvalidateAll(m_nCurrentPos);
2701 if (m_xCurrentRow.Is())
2703 m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GRS_MODIFIED : GRS_CLEAN);
2704 m_xCurrentRow->SetNew( bIsNew );
2705 InvalidateStatusCell(m_nCurrentPos);
2706 SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
2711 void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
2713 if (!m_pSeekCursor || IsResizing())
2714 return;
2716 sal_uInt16 nColId = GetColumnAtXPosPixel(rPosPixel.X());
2717 long nRow = GetRowAtYPosPixel(rPosPixel.Y());
2718 if (nColId != HandleColumnId && nRow >= 0)
2720 if (GetDataWindow().IsMouseCaptured())
2721 GetDataWindow().ReleaseMouse();
2723 size_t Location = GetModelColumnPos( nColId );
2724 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2725 OStringTransferable* pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
2726 Reference< XTransferable > xEnsureDelete(pTransferable);
2727 pTransferable->StartDrag(this, DND_ACTION_COPY);
2731 bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2733 return (_nRow >= 0)
2734 && (_nRow < GetRowCount())
2735 && (_nColId != HandleColumnId)
2736 && (_nColId <= ColCount());
2739 void DbGridControl::copyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2741 DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
2742 DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ];
2743 SeekRow(_nRow);
2744 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
2747 void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos )
2749 PopupMenu aContextMenu( SVX_RES( RID_SVXMNU_ROWS ) );
2751 PreExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu );
2752 aContextMenu.RemoveDisabledEntries( true, true );
2753 PostExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu, aContextMenu.Execute( this, _rPreferredPos ) );
2755 // TODO: why this weird cast to sal_uInt16? What if we really have more than 65535 lines?
2756 // -> change this to sal_uInt32
2759 void DbGridControl::Command(const CommandEvent& rEvt)
2761 switch (rEvt.GetCommand())
2763 case COMMAND_CONTEXTMENU:
2765 if ( !m_pSeekCursor )
2767 DbGridControl_Base::Command(rEvt);
2768 return;
2771 if ( !rEvt.IsMouseEvent() )
2772 { // context menu requested by keyboard
2773 if ( GetSelectRowCount() )
2775 long nRow = FirstSelectedRow( );
2777 ::Rectangle aRowRect( GetRowRectPixel( nRow, true ) );
2778 executeRowContextMenu( nRow, aRowRect.LeftCenter() );
2780 // handled
2781 return;
2785 sal_uInt16 nColId = GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X());
2786 long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
2788 if (nColId == HandleColumnId)
2790 executeRowContextMenu( nRow, rEvt.GetMousePosPixel() );
2792 else if (canCopyCellText(nRow, nColId))
2794 PopupMenu aContextMenu(SVX_RES(RID_SVXMNU_CELL));
2795 aContextMenu.RemoveDisabledEntries(true, true);
2796 switch (aContextMenu.Execute(this, rEvt.GetMousePosPixel()))
2798 case SID_COPY:
2799 copyCellText(nRow, nColId);
2800 break;
2803 else
2805 DbGridControl_Base::Command(rEvt);
2806 return;
2809 default:
2810 DbGridControl_Base::Command(rEvt);
2814 IMPL_LINK(DbGridControl, OnDelete, void*, /*EMPTYTAG*/ )
2816 m_nDeleteEvent = 0;
2817 DeleteSelectedRows();
2818 return 0;
2821 void DbGridControl::DeleteSelectedRows()
2823 DBG_ASSERT(GetSelection(), "keine selection!!!");
2825 if (!m_pSeekCursor)
2826 return;
2829 CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId)
2831 if (!IsValid(m_xCurrentRow) || !IsEnabled())
2832 return NULL;
2834 size_t Location = GetModelColumnPos(nColumnId);
2835 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2836 if (!pColumn)
2837 return NULL;
2839 CellController* pReturn = NULL;
2840 if (IsFilterMode())
2841 pReturn = &pColumn->GetController();
2842 else
2844 if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
2846 if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
2847 return NULL;
2850 bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & OPT_INSERT));
2851 bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & OPT_UPDATE));
2853 if ((bInsert && !pColumn->IsAutoValue()) || bUpdate || m_bForceROController)
2855 pReturn = &pColumn->GetController();
2856 if (pReturn)
2858 // if it is an edit row, it is possible to give it a forced read-only property
2859 if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController))
2860 // controller could not be set to read-only in forceROController
2861 if (!bInsert && !bUpdate)
2862 // better use no controller than one without read-only
2863 pReturn = NULL;
2867 return pReturn;
2870 void DbGridControl::InitController(CellControllerRef& /*rController*/, long /*nRow*/, sal_uInt16 nColumnId)
2872 size_t Location = GetModelColumnPos(nColumnId);
2873 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2874 if (pColumn)
2875 pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
2878 void DbGridControl::CellModified()
2880 SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
2883 ::osl::MutexGuard aGuard(m_aAdjustSafety);
2884 if (m_nAsynAdjustEvent)
2886 SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
2887 RemoveUserEvent(m_nAsynAdjustEvent);
2888 m_nAsynAdjustEvent = 0;
2890 // force the call : this should be no problem as we're probably running in the solar thread here
2891 // (cell modified is triggered by user actions)
2892 if (m_bPendingAdjustRows)
2893 AdjustRows();
2894 else
2895 AdjustDataSource();
2899 if (!IsFilterMode() && IsValid(m_xCurrentRow) && !m_xCurrentRow->IsModified())
2901 // enable edit mode
2902 // a data set should be inserted
2903 if (m_xCurrentRow->IsNew())
2905 m_xCurrentRow->SetStatus(GRS_MODIFIED);
2906 SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
2907 // if no row was added yet, do it now
2908 if (m_nCurrentPos == GetRowCount() - 1)
2910 // increment RowCount
2911 RowInserted(GetRowCount(), 1, true);
2912 InvalidateStatusCell(m_nCurrentPos);
2913 m_aBar.InvalidateAll(m_nCurrentPos);
2916 else if (m_xCurrentRow->GetStatus() != GRS_MODIFIED)
2918 m_xCurrentRow->SetState(m_pDataCursor, false);
2919 SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
2920 m_xCurrentRow->SetStatus(GRS_MODIFIED);
2921 SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
2922 InvalidateStatusCell(m_nCurrentPos);
2927 void DbGridControl::Dispatch(sal_uInt16 nId)
2929 if (nId == BROWSER_CURSORENDOFFILE)
2931 if (m_nOptions & OPT_INSERT)
2932 AppendNew();
2933 else
2934 MoveToLast();
2936 else
2937 DbGridControl_Base::Dispatch(nId);
2940 void DbGridControl::Undo()
2942 if (!IsFilterMode() && IsValid(m_xCurrentRow) && IsModified())
2944 // check if we have somebody doin' the UNDO for us
2945 long nState = -1;
2946 if (m_aMasterStateProvider.IsSet())
2947 nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
2948 if (nState>0)
2949 { // yes, we have, and the slot is enabled
2950 DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
2951 long lResult = m_aMasterSlotExecutor.Call((void*)SID_FM_RECORD_UNDO);
2952 if (lResult)
2953 // handled
2954 return;
2956 else if (nState == 0)
2957 // yes, we have, and the slot is disabled
2958 return;
2960 BeginCursorAction();
2962 bool bAppending = m_xCurrentRow->IsNew();
2963 bool bDirty = m_xCurrentRow->IsModified();
2967 // cancel editing
2968 Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
2969 // no effects if we're not updating currently
2970 if (bAppending)
2971 // just refresh the row
2972 xUpdateCursor->moveToInsertRow();
2973 else
2974 xUpdateCursor->cancelRowUpdates();
2977 catch(Exception&)
2979 DBG_UNHANDLED_EXCEPTION();
2982 EndCursorAction();
2984 m_xDataRow->SetState(m_pDataCursor, false);
2985 if (&m_xPaintRow == &m_xCurrentRow)
2986 m_xPaintRow = m_xCurrentRow = m_xDataRow;
2987 else
2988 m_xCurrentRow = m_xDataRow;
2990 if (bAppending && (DbGridControl_Base::IsModified() || bDirty))
2991 // remove the row
2992 if (m_nCurrentPos == GetRowCount() - 2)
2993 { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
2994 // caused our data source form to be reset - which should be the usual case ....)
2995 RowRemoved(GetRowCount() - 1, 1, true);
2996 m_aBar.InvalidateAll(m_nCurrentPos);
2999 RowModified(m_nCurrentPos);
3003 void DbGridControl::resetCurrentRow()
3005 if (IsModified())
3007 // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
3008 // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
3009 // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
3010 // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
3011 // would never delete the obsolete "second insert row". Thus in this special case this method here
3012 // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
3013 // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
3014 Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
3015 if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
3017 // are we on a new row currently ?
3018 if (m_xCurrentRow->IsNew())
3020 if (m_nCurrentPos == GetRowCount() - 2)
3022 RowRemoved(GetRowCount() - 1, 1, true);
3023 m_aBar.InvalidateAll(m_nCurrentPos);
3028 // update the rows
3029 m_xDataRow->SetState(m_pDataCursor, false);
3030 if (&m_xPaintRow == &m_xCurrentRow)
3031 m_xPaintRow = m_xCurrentRow = m_xDataRow;
3032 else
3033 m_xCurrentRow = m_xDataRow;
3036 RowModified(GetCurRow()); // will update the current controller if affected
3039 void DbGridControl::RowModified( long nRow, sal_uInt16 /*nColId*/ )
3041 if (nRow == m_nCurrentPos && IsEditing())
3043 CellControllerRef aTmpRef = Controller();
3044 aTmpRef->ClearModified();
3045 InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
3047 DbGridControl_Base::RowModified(nRow);
3050 bool DbGridControl::IsModified() const
3052 return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || DbGridControl_Base::IsModified());
3055 bool DbGridControl::IsCurrentAppending() const
3057 return m_xCurrentRow.Is() && m_xCurrentRow->IsNew();
3060 bool DbGridControl::IsInsertionRow(long nRow) const
3062 return (m_nOptions & OPT_INSERT) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
3065 bool DbGridControl::SaveModified()
3067 SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
3068 DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
3069 if (!IsValid(m_xCurrentRow))
3070 return true;
3072 // accept input for this field
3073 // Where there changes at the current input field?
3074 if (!DbGridControl_Base::IsModified())
3075 return true;
3077 size_t Location = GetModelColumnPos( GetCurColumnId() );
3078 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3079 bool bOK = pColumn && pColumn->Commit();
3080 DBG_ASSERT( Controller().Is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
3081 if ( !Controller().Is() )
3082 // this might happen if the callbacks implicitly triggered by Commit
3083 // fiddled with the form or the control ...
3084 // (Note that this here is a workaround, at most. We need a general concept how
3085 // to treat this, you can imagine an arbitrary number of scenarios where a callback
3086 // triggers something which leaves us in an expected state.)
3087 // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
3088 return bOK;
3090 if (bOK)
3092 Controller()->ClearModified();
3094 if ( IsValid(m_xCurrentRow) )
3096 m_xCurrentRow->SetState(m_pDataCursor, false);
3097 SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3098 InvalidateStatusCell( m_nCurrentPos );
3100 else
3102 SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3105 else
3107 // reset the modified flag ....
3108 Controller()->SetModified();
3111 return bOK;
3114 bool DbGridControl::SaveRow()
3116 SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
3117 // valid row
3118 if (!IsValid(m_xCurrentRow) || !IsModified())
3119 return true;
3120 // value of the controller was not saved, yet
3121 else if (Controller().Is() && Controller()->IsModified())
3123 if (!SaveModified())
3124 return false;
3126 m_bUpdating = true;
3128 BeginCursorAction();
3129 bool bAppending = m_xCurrentRow->IsNew();
3130 bool bSuccess = false;
3133 Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
3134 if (bAppending)
3135 xUpdateCursor->insertRow();
3136 else
3137 xUpdateCursor->updateRow();
3138 bSuccess = true;
3140 catch(SQLException&)
3142 EndCursorAction();
3143 m_bUpdating = false;
3144 return false;
3149 if (bSuccess)
3151 // if we are appending we still sit on the insert row
3152 // we don't move just clear the flags not to move on the current row
3153 m_xCurrentRow->SetState(m_pDataCursor, false);
3154 SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
3155 m_xCurrentRow->SetNew(false);
3157 // adjust the seekcursor if it is on the same position as the datacursor
3158 if (m_nSeekPos == m_nCurrentPos || bAppending)
3160 // get the bookmark to refetch the data
3161 // in insert mode we take the new bookmark of the data cursor
3162 Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
3163 m_pSeekCursor->moveToBookmark(aBookmark);
3164 // get the data
3165 m_xSeekRow->SetState(m_pSeekCursor, true);
3166 m_nSeekPos = m_pSeekCursor->getRow() - 1;
3169 // and repaint the row
3170 RowModified(m_nCurrentPos);
3172 catch(Exception&)
3176 m_bUpdating = false;
3177 EndCursorAction();
3179 // The old code returned (nRecords != 0) here.
3180 // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
3181 // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
3182 // is zero, this simply means all fields had their original values.
3183 // FS - 06.12.99 - 70502
3184 return true;
3187 bool DbGridControl::PreNotify(NotifyEvent& rEvt)
3189 // do not handle events of the Navbar
3190 if (m_aBar.IsWindowOrChild(rEvt.GetWindow()))
3191 return BrowseBox::PreNotify(rEvt);
3193 switch (rEvt.GetType())
3195 case EVENT_KEYINPUT:
3197 const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
3199 sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
3200 bool bShift = pKeyEvent->GetKeyCode().IsShift();
3201 bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
3202 bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
3203 if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
3205 // Ctrl-Tab is used to step out of the control, without traveling to the
3206 // remaining cells first
3207 // -> build a new key event without the Ctrl-key, and let the very base class handle it
3208 KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
3209 KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
3211 // call the Control - our direct base class will interpret this in a way we do not want (and do
3212 // a cell traveling)
3213 Control::KeyInput( aNewEvent );
3214 return true;
3217 if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
3219 if (IsModified())
3221 Undo();
3222 return true;
3225 else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
3227 if ((m_nOptions & OPT_DELETE) && GetSelectRowCount())
3229 // delete asynchronously
3230 if (m_nDeleteEvent)
3231 Application::RemoveUserEvent(m_nDeleteEvent);
3232 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
3233 return true;
3236 } // no break!
3237 default:
3238 return DbGridControl_Base::PreNotify(rEvt);
3242 bool DbGridControl::IsTabAllowed(bool bRight) const
3244 if (bRight)
3245 // Tab only if not on the _last_ row
3246 return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
3247 GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
3248 else
3250 // Tab only if not on the _first_ row
3251 return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
3255 void DbGridControl::KeyInput( const KeyEvent& rEvt )
3257 if (rEvt.GetKeyCode().GetFunction() == KEYFUNC_COPY)
3259 long nRow = GetCurRow();
3260 sal_uInt16 nColId = GetCurColumnId();
3261 if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
3263 size_t Location = GetModelColumnPos( nColId );
3264 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3265 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
3266 return;
3269 DbGridControl_Base::KeyInput(rEvt);
3272 void DbGridControl::HideColumn(sal_uInt16 nId)
3274 DeactivateCell();
3276 // determine the col for the focus to set to after removal
3277 sal_uInt16 nPos = GetViewColumnPos(nId);
3278 sal_uInt16 nNewColId = nPos == (ColCount()-1)
3279 ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
3280 : GetColumnIdFromViewPos(nPos+1); // take the next
3282 long lCurrentWidth = GetColumnWidth(nId);
3283 DbGridControl_Base::RemoveColumn(nId);
3284 // don't use my own RemoveColumn, this would remove it from m_aColumns, too
3286 // update my model
3287 size_t Location = GetModelColumnPos( nId );
3288 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3289 DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
3290 if (pColumn)
3292 pColumn->m_bHidden = true;
3293 pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
3296 // and reset the focus
3297 if ( nId == GetCurColumnId() )
3298 GoToColumnId( nNewColId );
3301 void DbGridControl::ShowColumn(sal_uInt16 nId)
3303 sal_uInt16 nPos = GetModelColumnPos(nId);
3304 DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
3305 if (nPos == GRID_COLUMN_NOT_FOUND)
3306 return;
3308 DbGridColumn* pColumn = m_aColumns[ nPos ];
3309 if (!pColumn->IsHidden())
3311 DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3312 // if the column isn't marked as hidden, it should be visible, shouldn't it ?
3313 return;
3315 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3316 // the opposite situation ...
3318 // to determine the new view position we need an adjacent non-hidden column
3319 sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
3320 // first search the cols to the right
3321 for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
3323 DbGridColumn* pCurCol = m_aColumns[ i ];
3324 if (!pCurCol->IsHidden())
3326 nNextNonHidden = i;
3327 break;
3330 if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
3332 // then to the left
3333 for ( size_t i = nPos; i > 0; --i )
3335 DbGridColumn* pCurCol = m_aColumns[ i-1 ];
3336 if (!pCurCol->IsHidden())
3338 nNextNonHidden = i-1;
3339 break;
3343 sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
3344 ? 1 // there is no visible column -> insert behinde the handle col
3345 : GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1;
3346 // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
3347 // a position 1 for the first non-handle col -> +1
3348 DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3349 // we found a col marked as visible but got no view pos for it ...
3351 if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID))
3352 // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
3353 ++nNewViewPos;
3355 DeactivateCell();
3357 OUString aName;
3358 pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
3359 InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HIB_CENTER | HIB_VCENTER | HIB_CLICKABLE, nNewViewPos);
3360 pColumn->m_bHidden = false;
3362 ActivateCell();
3363 Invalidate();
3366 sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
3368 if (nPos >= m_aColumns.size())
3370 OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
3371 return GRID_COLUMN_NOT_FOUND;
3374 DbGridColumn* pCol = m_aColumns[ nPos ];
3375 #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
3376 // in der Debug-Version rechnen wir die ModelPos in eine ViewPos um und vergleichen das mit dem Wert,
3377 // den wir zurueckliefern werden (nId an der entsprechenden Col in m_aColumns)
3379 if (!pCol->IsHidden())
3380 { // macht nur Sinn, wenn die Spalte sichtbar ist
3381 sal_uInt16 nViewPos = nPos;
3382 for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i)
3383 if ( m_aColumns[ i ]->IsHidden())
3384 --nViewPos;
3386 DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
3387 "DbGridControl::GetColumnIdFromModelPos : this isn't consistent .... did I misunderstand something ?");
3389 #endif
3390 return pCol->GetId();
3393 sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
3395 for ( size_t i = 0; i < m_aColumns.size(); ++i )
3396 if ( m_aColumns[ i ]->GetId() == nId )
3397 return i;
3399 return GRID_COLUMN_NOT_FOUND;
3402 void DbGridControl::implAdjustInSolarThread(bool _bRows)
3404 SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
3405 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3406 if (::osl::Thread::getCurrentIdentifier() != Application::GetMainThreadIdentifier())
3408 m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ));
3409 m_bPendingAdjustRows = _bRows;
3410 if (_bRows)
3411 SAL_INFO("svx.fmcomp", "posting an AdjustRows");
3412 else
3413 SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
3415 else
3417 if (_bRows)
3418 SAL_INFO("svx.fmcomp", "doing an AdjustRows");
3419 else
3420 SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
3421 // always adjust the rows before adjusting the data source
3422 // If this is not necessary (because the row count did not change), nothing is done
3423 // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
3424 // to a position behind row count know 'til now, the cursorMoved notification may come before the
3425 // RowCountChanged notification
3426 // 94093 - 02.11.2001 - frank.schoenheit@sun.com
3427 AdjustRows();
3429 if ( !_bRows )
3430 AdjustDataSource();
3434 IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat)
3436 m_nAsynAdjustEvent = 0;
3438 AdjustRows();
3439 // see implAdjustInSolarThread for a comment why we do this every time
3441 if ( !pAdjustWhat )
3442 AdjustDataSource();
3444 return 0L;
3447 void DbGridControl::BeginCursorAction()
3449 if (m_pFieldListeners)
3451 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3452 ColumnFieldValueListeners::const_iterator aIter = pListeners->begin();
3453 while (aIter != pListeners->end())
3455 GridFieldValueListener* pCurrent = (*aIter).second;
3456 if (pCurrent)
3457 pCurrent->suspend();
3458 ++aIter;
3462 if (m_pDataSourcePropListener)
3463 m_pDataSourcePropListener->suspend();
3466 void DbGridControl::EndCursorAction()
3468 if (m_pFieldListeners)
3470 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3471 ColumnFieldValueListeners::const_iterator aIter = pListeners->begin();
3472 while (aIter != pListeners->end())
3474 GridFieldValueListener* pCurrent = (*aIter).second;
3475 if (pCurrent)
3476 pCurrent->resume();
3477 ++aIter;
3481 if (m_pDataSourcePropListener)
3482 m_pDataSourcePropListener->resume();
3485 void DbGridControl::ConnectToFields()
3487 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3488 DBG_ASSERT(!pListeners || pListeners->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
3490 if (!pListeners)
3492 pListeners = new ColumnFieldValueListeners;
3493 m_pFieldListeners = pListeners;
3496 for ( size_t i = 0; i < m_aColumns.size(); ++i )
3498 DbGridColumn* pCurrent = m_aColumns[ i ];
3499 sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
3500 if (GRID_COLUMN_NOT_FOUND == nViewPos)
3501 continue;
3503 Reference< XPropertySet > xField = pCurrent->GetField();
3504 if (!xField.is())
3505 continue;
3507 // column is visible and bound here
3508 GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
3509 DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
3510 rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
3514 void DbGridControl::DisconnectFromFields()
3516 if (!m_pFieldListeners)
3517 return;
3519 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3520 while (!pListeners->empty())
3522 #ifdef DBG_UTIL
3523 sal_Int32 nOldSize = pListeners->size();
3524 #endif
3525 pListeners->begin()->second->dispose();
3526 DBG_ASSERT(nOldSize > (sal_Int32)pListeners->size(), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
3529 delete pListeners;
3530 m_pFieldListeners = NULL;
3533 void DbGridControl::FieldValueChanged(sal_uInt16 _nId, const PropertyChangeEvent& /*_evt*/)
3535 osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
3536 // needed as this may run in a thread other than the main one
3537 if (GetRowStatus(GetCurRow()) != DbGridControl_Base::MODIFIED)
3538 // all other cases are handled elsewhere
3539 return;
3541 size_t Location = GetModelColumnPos( _nId );
3542 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3543 if (pColumn)
3545 bool bAcquiredPaintSafety = false;
3546 while (!m_bWantDestruction && !bAcquiredPaintSafety)
3547 bAcquiredPaintSafety = Application::GetSolarMutex().tryToAcquire();
3549 if (m_bWantDestruction)
3550 { // at this moment, within another thread, our destructor tries to destroy the listener which called this method
3551 // => don't do anything
3552 // 73365 - 23.02.00 - FS
3553 if (bAcquiredPaintSafety)
3554 // though the above while-loop suggests that (m_bWantDestruction && bAcquiredPaintSafety) is impossible,
3555 // it isnt't, as m_bWantDestruction isn't protected with any mutex
3556 Application::GetSolarMutex().release();
3557 return;
3559 // here we got the solar mutex, transfer it to a guard for safety reasons
3560 SolarMutexGuard aPaintSafety;
3561 Application::GetSolarMutex().release();
3563 // and finally do the update ...
3564 pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
3565 RowModified(GetCurRow(), _nId);
3569 void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
3571 ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3572 if (!pListeners)
3574 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
3575 return;
3578 ColumnFieldValueListeners::iterator aPos = pListeners->find(_nId);
3579 if (aPos == pListeners->end())
3581 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
3582 return;
3585 delete aPos->second;
3587 pListeners->erase(aPos);
3590 void DbGridControl::disposing(sal_uInt16 _nId, const EventObject& /*_rEvt*/)
3592 if (_nId == 0)
3593 { // the seek cursor is beeing disposed
3594 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3595 setDataSource(NULL,0); // our clone was disposed so we set our datasource to null to avoid later acces to it
3596 if (m_nAsynAdjustEvent)
3598 RemoveUserEvent(m_nAsynAdjustEvent);
3599 m_nAsynAdjustEvent = 0;
3604 sal_Int32 DbGridControl::GetAccessibleControlCount() const
3606 return DbGridControl_Base::GetAccessibleControlCount() + 1; // the navigation control
3609 Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
3611 Reference<XAccessible > xRet;
3612 if ( _nIndex == DbGridControl_Base::GetAccessibleControlCount() )
3614 xRet = m_aBar.GetAccessible();
3616 else
3617 xRet = DbGridControl_Base::CreateAccessibleControl( _nIndex );
3618 return xRet;
3621 Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
3623 sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
3624 size_t Location = GetModelColumnPos(nColumnId);
3625 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3626 if ( pColumn )
3628 Reference< ::com::sun::star::awt::XControl> xInt(pColumn->GetCell());
3629 Reference< ::com::sun::star::awt::XCheckBox> xBox(xInt,UNO_QUERY);
3630 if ( xBox.is() )
3632 TriState eValue = TRISTATE_FALSE;
3633 switch( xBox->getState() )
3635 case 0:
3636 eValue = TRISTATE_FALSE;
3637 break;
3638 case 1:
3639 eValue = TRISTATE_TRUE;
3640 break;
3641 case 2:
3642 eValue = TRISTATE_INDET;
3643 break;
3645 return DbGridControl_Base::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
3648 return DbGridControl_Base::CreateAccessibleCell( _nRow, _nColumnPos );
3651 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */