Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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>
27 :
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>
47 :
48 : #include "svx/fmresids.hrc"
49 :
50 : #include <svx/svxids.hrc>
51 : #include <tools/shl.hxx>
52 : #include <svx/dialmgr.hxx>
53 : #include "fmservs.hxx"
54 : #include "sdbdatacolumn.hxx"
55 :
56 : #include <comphelper/property.hxx>
57 :
58 : #include <algorithm>
59 : #include <cstdlib>
60 : #include <map>
61 :
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;
73 :
74 : #define ROWSTATUS(row) (!row.Is() ? "NULL" : row->GetStatus() == GRS_CLEAN ? "CLEAN" : row->GetStatus() == GRS_MODIFIED ? "MODIFIED" : row->GetStatus() == GRS_DELETED ? "DELETED" : "INVALID")
75 :
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 \
84 :
85 0 : class RowSetEventListener : public ::cppu::WeakImplHelper1<XRowsChangeListener>
86 : {
87 : DbGridControl* m_pControl;
88 : public:
89 0 : RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
90 : {
91 0 : }
92 :
93 : private:
94 : // XEventListener
95 0 : virtual void SAL_CALL disposing(const ::com::sun::star::lang::EventObject& /*i_aEvt*/) throw ( RuntimeException, std::exception ) SAL_OVERRIDE
96 : {
97 0 : }
98 0 : virtual void SAL_CALL rowsChanged(const ::com::sun::star::sdb::RowsChangeEvent& i_aEvt) throw ( RuntimeException, std::exception ) SAL_OVERRIDE
99 : {
100 0 : if ( i_aEvt.Action == RowChangeAction::UPDATE )
101 : {
102 0 : ::DbGridControl::GrantControlAccess aAccess;
103 0 : CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
104 0 : const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
105 0 : const Any* pIter = i_aEvt.Bookmarks.getConstArray();
106 0 : const Any* pEnd = pIter + i_aEvt.Bookmarks.getLength();
107 0 : for(;pIter != pEnd;++pIter)
108 : {
109 0 : pSeek->moveToBookmark(*pIter);
110 : // get the data
111 0 : rSeekRow->SetState(pSeek, true);
112 0 : sal_Int32 nSeekPos = pSeek->getRow() - 1;
113 0 : m_pControl->SetSeekPos(nSeekPos,aAccess);
114 0 : m_pControl->RowModified(nSeekPos);
115 : }
116 : }
117 0 : }
118 : };
119 :
120 : class GridFieldValueListener;
121 : typedef std::map<sal_uInt16, GridFieldValueListener*> ColumnFieldValueListeners;
122 :
123 : class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
124 : {
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;
131 :
132 : public:
133 : GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
134 : virtual ~GridFieldValueListener();
135 :
136 : virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) SAL_OVERRIDE;
137 :
138 0 : void suspend() { ++m_nSuspended; }
139 0 : void resume() { --m_nSuspended; }
140 :
141 : void dispose();
142 : };
143 :
144 0 : 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 0 : ,m_bDisposed(false)
151 : {
152 0 : if (_rField.is())
153 : {
154 0 : m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
155 0 : m_pRealListener->addProperty(FM_PROP_VALUE);
156 0 : m_pRealListener->acquire();
157 : }
158 0 : }
159 :
160 0 : GridFieldValueListener::~GridFieldValueListener()
161 : {
162 0 : dispose();
163 0 : }
164 :
165 0 : void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& _evt) throw( RuntimeException )
166 : {
167 : DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
168 0 : if (m_nSuspended <= 0)
169 0 : m_rParent.FieldValueChanged(m_nId, _evt);
170 0 : }
171 :
172 0 : void GridFieldValueListener::dispose()
173 : {
174 0 : if (m_bDisposed)
175 : {
176 : DBG_ASSERT(m_pRealListener == NULL, "GridFieldValueListener::dispose : inconsistent !");
177 0 : return;
178 : }
179 :
180 0 : if (m_pRealListener)
181 : {
182 0 : m_pRealListener->dispose();
183 0 : m_pRealListener->release();
184 0 : m_pRealListener = NULL;
185 : }
186 :
187 0 : m_bDisposed = true;
188 0 : m_rParent.FieldListenerDisposing(m_nId);
189 : }
190 :
191 : class DisposeListenerGridBridge : public FmXDisposeListener
192 : {
193 : osl::Mutex m_aMutex;
194 : DbGridControl& m_rParent;
195 : FmXDisposeMultiplexer* m_pRealListener;
196 :
197 : public:
198 : DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId = -1);
199 : virtual ~DisposeListenerGridBridge();
200 :
201 0 : virtual void disposing(const EventObject& _rEvent, sal_Int16 _nId) throw( RuntimeException ) SAL_OVERRIDE { m_rParent.disposing(_nId, _rEvent); }
202 : };
203 :
204 0 : DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId)
205 : :FmXDisposeListener(m_aMutex)
206 : ,m_rParent(_rParent)
207 0 : ,m_pRealListener(NULL)
208 : {
209 :
210 0 : if (_rxObject.is())
211 : {
212 0 : m_pRealListener = new FmXDisposeMultiplexer(this, _rxObject, _rId);
213 0 : m_pRealListener->acquire();
214 : }
215 0 : }
216 :
217 0 : DisposeListenerGridBridge::~DisposeListenerGridBridge()
218 : {
219 0 : if (m_pRealListener)
220 : {
221 0 : m_pRealListener->dispose();
222 0 : m_pRealListener->release();
223 0 : m_pRealListener = NULL;
224 : }
225 :
226 0 : }
227 :
228 : static const sal_uInt16 ControlMap[] =
229 : {
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,
239 : 0
240 : };
241 :
242 0 : bool CompareBookmark(const Any& aLeft, const Any& aRight)
243 : {
244 0 : return ::comphelper::compare(aLeft, aRight);
245 : }
246 :
247 0 : class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
248 : {
249 : DbGridControl* m_pParent;
250 :
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;
254 :
255 : public:
256 : FmXGridSourcePropListener(DbGridControl* _pParent);
257 :
258 0 : void suspend() { ++m_nSuspended; }
259 0 : void resume() { --m_nSuspended; }
260 :
261 : virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) SAL_OVERRIDE;
262 : };
263 :
264 0 : FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
265 : :OPropertyChangeListener(m_aMutex)
266 : ,m_pParent(_pParent)
267 0 : ,m_nSuspended(0)
268 : {
269 : DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
270 0 : }
271 :
272 0 : void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
273 : {
274 : DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
275 0 : if (m_nSuspended <= 0)
276 0 : m_pParent->DataSourcePropertyChanged(evt);
277 0 : }
278 :
279 0 : DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(Window* pParent, WinBits nStyle)
280 0 : :NumericField(pParent, nStyle)
281 : {
282 0 : SetMin(1);
283 0 : SetFirst(1);
284 0 : SetSpinSize(1);
285 :
286 0 : SetDecimalDigits(0);
287 0 : SetStrictFormat(true);
288 0 : }
289 :
290 0 : void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt)
291 : {
292 0 : if (rEvt.GetKeyCode() == KEY_RETURN && !GetText().isEmpty())
293 : {
294 0 : sal_Int64 nRecord = GetValue();
295 0 : if (nRecord < GetMin() || nRecord > GetMax())
296 0 : return;
297 : else
298 0 : ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
299 : }
300 0 : else if (rEvt.GetKeyCode() == KEY_TAB)
301 0 : GetParent()->GetParent()->GrabFocus();
302 : else
303 0 : NumericField::KeyInput(rEvt);
304 : }
305 :
306 0 : void DbGridControl::NavigationBar::AbsolutePos::LoseFocus()
307 : {
308 0 : NumericField::LoseFocus();
309 0 : sal_Int64 nRecord = GetValue();
310 0 : if (nRecord < GetMin() || nRecord > GetMax())
311 0 : return;
312 : else
313 : {
314 0 : ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
315 0 : ((NavigationBar*)GetParent())->InvalidateState(NavigationBar::RECORD_ABSOLUTE);
316 : }
317 : }
318 :
319 0 : void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord)
320 : {
321 0 : if (m_bPositioning)
322 0 : return;
323 : // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition,
324 : // so protect against this recursion
325 0 : m_bPositioning = true;
326 0 : ((DbGridControl*)GetParent())->MoveToPosition(nRecord - 1);
327 0 : m_bPositioning = false;
328 : }
329 :
330 0 : 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 0 : ,m_bPositioning(false)
344 : {
345 0 : m_aFirstBtn.SetSymbol(SYMBOL_FIRST);
346 0 : m_aPrevBtn.SetSymbol(SYMBOL_PREV);
347 0 : m_aNextBtn.SetSymbol(SYMBOL_NEXT);
348 0 : m_aLastBtn.SetSymbol(SYMBOL_LAST);
349 0 : m_aNewBtn.SetModeImage(((DbGridControl*)pParent)->GetImage(DbGridControl_Base::NEW));
350 :
351 0 : m_aFirstBtn.SetHelpId(HID_GRID_TRAVEL_FIRST);
352 0 : m_aPrevBtn.SetHelpId(HID_GRID_TRAVEL_PREV);
353 0 : m_aNextBtn.SetHelpId(HID_GRID_TRAVEL_NEXT);
354 0 : m_aLastBtn.SetHelpId(HID_GRID_TRAVEL_LAST);
355 0 : m_aNewBtn.SetHelpId(HID_GRID_TRAVEL_NEW);
356 0 : m_aAbsolute.SetHelpId(HID_GRID_TRAVEL_ABSOLUTE);
357 0 : m_aRecordCount.SetHelpId(HID_GRID_NUMBEROFRECORDS);
358 :
359 : // Handler fuer Buttons einrichten
360 0 : m_aFirstBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
361 0 : m_aPrevBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
362 0 : m_aNextBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
363 0 : m_aLastBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
364 0 : m_aNewBtn.SetClickHdl(LINK(this,NavigationBar,OnClick));
365 :
366 0 : m_aRecordText.SetText(SVX_RESSTR(RID_STR_REC_TEXT));
367 0 : m_aRecordOf.SetText(SVX_RESSTR(RID_STR_REC_FROM_TEXT));
368 0 : m_aRecordCount.SetText(OUString('?'));
369 :
370 0 : m_nDefaultWidth = ArrangeControls();
371 :
372 0 : m_aFirstBtn.Disable();
373 0 : m_aPrevBtn.Disable();
374 0 : m_aNextBtn.Disable();
375 0 : m_aLastBtn.Disable();
376 0 : m_aNewBtn.Disable();
377 0 : m_aRecordText.Disable();
378 0 : m_aRecordOf.Disable();
379 0 : m_aRecordCount.Disable();
380 0 : m_aAbsolute.Disable();
381 :
382 0 : AllSettings aSettings = m_aNextBtn.GetSettings();
383 0 : MouseSettings aMouseSettings = aSettings.GetMouseSettings();
384 0 : aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
385 0 : aSettings.SetMouseSettings(aMouseSettings);
386 0 : m_aNextBtn.SetSettings(aSettings, true);
387 0 : m_aPrevBtn.SetSettings(aSettings, true);
388 :
389 0 : m_aFirstBtn.Show();
390 0 : m_aPrevBtn.Show();
391 0 : m_aNextBtn.Show();
392 0 : m_aLastBtn.Show();
393 0 : m_aNewBtn.Show();
394 0 : m_aRecordText.Show();
395 0 : m_aRecordOf.Show();
396 0 : m_aRecordCount.Show();
397 0 : m_aAbsolute.Show();
398 0 : }
399 :
400 : namespace
401 : {
402 0 : void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize)
403 : {
404 0 : _rButton.SetPosPixel( _rPos );
405 0 : _rButton.SetSizePixel( _rSize );
406 0 : _rPos.X() += (sal_uInt16)_rSize.Width();
407 0 : }
408 : }
409 :
410 0 : sal_uInt16 DbGridControl::NavigationBar::ArrangeControls()
411 : {
412 : // positioning of the controls
413 : // calculate base size
414 0 : Rectangle aRect(((DbGridControl*)GetParent())->GetControlArea());
415 0 : const long nH = aRect.GetSize().Height();
416 0 : Size aBorder = LogicToPixel(Size(2, 2),MAP_APPFONT);
417 0 : aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height()));
418 0 : sal_uInt16 nX = 1;
419 0 : sal_uInt16 nY = 0;
420 :
421 : // Is the font of this edit larger than the field?
422 0 : if (m_aAbsolute.GetTextHeight() > nH)
423 : {
424 0 : Font aApplFont (m_aAbsolute.GetFont());
425 0 : const Size pointAbsoluteSize(m_aAbsolute.PixelToLogic( Size( 0, nH - 2 ), MapMode(MAP_POINT) ));
426 0 : aApplFont.SetSize( pointAbsoluteSize );
427 0 : m_aAbsolute.SetControlFont( aApplFont );
428 :
429 0 : aApplFont.SetTransparent( true );
430 0 : m_aRecordText.SetControlFont( aApplFont );
431 0 : m_aRecordOf.SetControlFont( aApplFont );
432 0 : m_aRecordCount.SetControlFont( aApplFont );
433 : }
434 :
435 : // set size and position of the control
436 0 : OUString aText = m_aRecordText.GetText();
437 0 : long nTextWidth = m_aRecordText.GetTextWidth(aText);
438 0 : m_aRecordText.SetPosPixel(Point(nX,nY));
439 0 : m_aRecordText.SetSizePixel(Size(nTextWidth,nH));
440 0 : nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
441 :
442 : // count an extra hairspace (U+200A) left and right
443 0 : const OUString sevenDigits(m_aAbsolute.CreateFieldText(6000000));
444 0 : const OUString hairSpace(static_cast<sal_Unicode>(0x200A));
445 0 : OUString textPattern(hairSpace);
446 0 : textPattern += sevenDigits;
447 0 : textPattern += hairSpace;
448 0 : nTextWidth = m_aAbsolute.GetTextWidth( textPattern );
449 0 : m_aAbsolute.SetPosPixel(Point(nX,nY));
450 0 : m_aAbsolute.SetSizePixel(Size(nTextWidth, nH));
451 0 : nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
452 :
453 0 : aText = m_aRecordOf.GetText();
454 0 : nTextWidth = m_aRecordOf.GetTextWidth(aText);
455 0 : m_aRecordOf.SetPosPixel(Point(nX,nY));
456 0 : m_aRecordOf.SetSizePixel(Size(nTextWidth,nH));
457 0 : nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
458 :
459 0 : textPattern = sevenDigits + " * (" + sevenDigits + ")";
460 0 : nTextWidth = m_aRecordCount.GetTextWidth( textPattern );
461 0 : m_aRecordCount.SetPosPixel(Point(nX,nY));
462 0 : m_aRecordCount.SetSizePixel(Size(nTextWidth,nH));
463 0 : nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
464 :
465 0 : Point aButtonPos(nX,nY);
466 0 : const Size aButtonSize(nH,nH);
467 0 : SetPosAndSize(m_aFirstBtn, aButtonPos, aButtonSize);
468 0 : SetPosAndSize(m_aPrevBtn, aButtonPos, aButtonSize);
469 0 : SetPosAndSize(m_aNextBtn, aButtonPos, aButtonSize);
470 0 : SetPosAndSize(m_aLastBtn, aButtonPos, aButtonSize);
471 0 : SetPosAndSize(m_aNewBtn, aButtonPos, aButtonSize);
472 :
473 0 : nX = sal::static_int_cast< sal_uInt16 >(aButtonPos.X() + 1);
474 :
475 0 : return nX;
476 : }
477 :
478 0 : IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton )
479 : {
480 0 : DbGridControl* pParent = (DbGridControl*)GetParent();
481 :
482 0 : if (pParent->m_aMasterSlotExecutor.IsSet())
483 : {
484 0 : long lResult = 0;
485 0 : if (pButton == &m_aFirstBtn)
486 0 : lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_FIRST);
487 0 : else if( pButton == &m_aPrevBtn )
488 0 : lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_PREV);
489 0 : else if( pButton == &m_aNextBtn )
490 0 : lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEXT);
491 0 : else if( pButton == &m_aLastBtn )
492 0 : lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_LAST);
493 0 : else if( pButton == &m_aNewBtn )
494 0 : lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEW);
495 :
496 0 : if (lResult)
497 : // the link already handled it
498 0 : return 0;
499 : }
500 :
501 0 : if (pButton == &m_aFirstBtn)
502 0 : pParent->MoveToFirst();
503 0 : else if( pButton == &m_aPrevBtn )
504 0 : pParent->MoveToPrev();
505 0 : else if( pButton == &m_aNextBtn )
506 0 : pParent->MoveToNext();
507 0 : else if( pButton == &m_aLastBtn )
508 0 : pParent->MoveToLast();
509 0 : else if( pButton == &m_aNewBtn )
510 0 : pParent->AppendNew();
511 0 : return 0;
512 : }
513 :
514 0 : void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll)
515 : {
516 0 : if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)
517 : {
518 0 : DbGridControl* pParent = (DbGridControl*)GetParent();
519 :
520 0 : sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControl::OPT_INSERT) ? 2 : 1);
521 :
522 : // check if everything needs to be invalidated
523 0 : bAll = bAll || m_nCurrentPos <= 0;
524 0 : bAll = bAll || nCurrentPos <= 0;
525 0 : bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
526 0 : bAll = bAll || nCurrentPos >= nAdjustedRowCount;
527 :
528 0 : if ( bAll )
529 : {
530 0 : m_nCurrentPos = nCurrentPos;
531 0 : int i = 0;
532 0 : while (ControlMap[i])
533 0 : SetState(ControlMap[i++]);
534 : }
535 : else // is in the center
536 : {
537 0 : m_nCurrentPos = nCurrentPos;
538 0 : SetState(NavigationBar::RECORD_COUNT);
539 0 : SetState(NavigationBar::RECORD_ABSOLUTE);
540 : }
541 : }
542 0 : }
543 :
544 0 : bool DbGridControl::NavigationBar::GetState(sal_uInt16 nWhich) const
545 : {
546 0 : DbGridControl* pParent = (DbGridControl*)GetParent();
547 :
548 0 : if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
549 0 : || pParent->IsFilterMode() )
550 0 : return false;
551 : else
552 : {
553 : // check if we have a master state provider
554 0 : if (pParent->m_aMasterStateProvider.IsSet())
555 : {
556 0 : long nState = pParent->m_aMasterStateProvider.Call(reinterpret_cast< void* >( nWhich ) );
557 0 : if (nState>=0)
558 0 : return (nState>0);
559 : }
560 :
561 0 : bool bAvailable = true;
562 :
563 0 : switch (nWhich)
564 : {
565 : case NavigationBar::RECORD_FIRST:
566 : case NavigationBar::RECORD_PREV:
567 0 : bAvailable = m_nCurrentPos > 0;
568 0 : break;
569 : case NavigationBar::RECORD_NEXT:
570 0 : if(pParent->m_bRecordCountFinal)
571 : {
572 0 : bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
573 0 : if (!bAvailable && pParent->GetOptions() & DbGridControl::OPT_INSERT)
574 0 : bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
575 : }
576 0 : break;
577 : case NavigationBar::RECORD_LAST:
578 0 : if(pParent->m_bRecordCountFinal)
579 : {
580 0 : if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
581 0 : bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
582 0 : m_nCurrentPos != pParent->GetRowCount() - 2;
583 : else
584 0 : bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
585 : }
586 0 : break;
587 : case NavigationBar::RECORD_NEW:
588 0 : bAvailable = (pParent->GetOptions() & DbGridControl::OPT_INSERT) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
589 0 : break;
590 : case NavigationBar::RECORD_ABSOLUTE:
591 0 : bAvailable = pParent->GetRowCount() > 0;
592 0 : break;
593 : }
594 0 : return bAvailable;
595 : }
596 : }
597 :
598 0 : void DbGridControl::NavigationBar::SetState(sal_uInt16 nWhich)
599 : {
600 0 : bool bAvailable = GetState(nWhich);
601 0 : DbGridControl* pParent = (DbGridControl*)GetParent();
602 0 : Window* pWnd = NULL;
603 0 : switch (nWhich)
604 : {
605 : case NavigationBar::RECORD_FIRST:
606 0 : pWnd = &m_aFirstBtn;
607 0 : break;
608 : case NavigationBar::RECORD_PREV:
609 0 : pWnd = &m_aPrevBtn;
610 0 : break;
611 : case NavigationBar::RECORD_NEXT:
612 0 : pWnd = &m_aNextBtn;
613 0 : break;
614 : case NavigationBar::RECORD_LAST:
615 0 : pWnd = &m_aLastBtn;
616 0 : break;
617 : case NavigationBar::RECORD_NEW:
618 0 : pWnd = &m_aNewBtn;
619 0 : break;
620 : case NavigationBar::RECORD_ABSOLUTE:
621 0 : pWnd = &m_aAbsolute;
622 0 : if (bAvailable)
623 : {
624 0 : if (pParent->m_nTotalCount >= 0)
625 : {
626 0 : if (pParent->IsCurrentAppending())
627 0 : m_aAbsolute.SetMax(pParent->m_nTotalCount + 1);
628 : else
629 0 : m_aAbsolute.SetMax(pParent->m_nTotalCount);
630 : }
631 : else
632 0 : m_aAbsolute.SetMax(LONG_MAX);
633 :
634 0 : m_aAbsolute.SetValue(m_nCurrentPos + 1);
635 : }
636 : else
637 0 : m_aAbsolute.SetText(OUString());
638 0 : break;
639 : case NavigationBar::RECORD_TEXT:
640 0 : pWnd = &m_aRecordText;
641 0 : break;
642 : case NavigationBar::RECORD_OF:
643 0 : pWnd = &m_aRecordOf;
644 0 : break;
645 : case NavigationBar::RECORD_COUNT:
646 : {
647 0 : pWnd = &m_aRecordCount;
648 0 : OUString aText;
649 0 : if (bAvailable)
650 : {
651 0 : if (pParent->GetOptions() & DbGridControl::OPT_INSERT)
652 : {
653 0 : if (pParent->IsCurrentAppending() && !pParent->IsModified())
654 0 : aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount());
655 : else
656 0 : aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount() - 1);
657 : }
658 : else
659 0 : aText = m_aAbsolute.CreateFieldText(pParent->GetRowCount());
660 0 : if(!pParent->m_bRecordCountFinal)
661 0 : aText += " *";
662 : }
663 : else
664 0 : aText = "";
665 :
666 : // add the number of selected rows, if applicable
667 0 : if (pParent->GetSelectRowCount())
668 : {
669 0 : OUString aExtendedInfo(aText);
670 0 : aExtendedInfo += " (";
671 0 : aExtendedInfo += m_aAbsolute.CreateFieldText(pParent->GetSelectRowCount());
672 0 : aExtendedInfo += ")";
673 0 : pWnd->SetText(aExtendedInfo);
674 : }
675 : else
676 0 : pWnd->SetText(aText);
677 :
678 0 : pParent->SetRealRowCount(aText);
679 0 : } break;
680 : }
681 : DBG_ASSERT(pWnd, "kein Fenster");
682 0 : 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 0 : pWnd->Enable(bAvailable);
688 0 : }
689 :
690 0 : void DbGridControl::NavigationBar::Resize()
691 : {
692 0 : Control::Resize();
693 0 : ArrangeControls();
694 0 : }
695 :
696 0 : void DbGridControl::NavigationBar::Paint(const Rectangle& rRect)
697 : {
698 0 : Control::Paint(rRect);
699 0 : Point aAbsolutePos = m_aAbsolute.GetPosPixel();
700 0 : Size aAbsoluteSize = m_aAbsolute.GetSizePixel();
701 :
702 0 : DrawLine(Point(aAbsolutePos.X() - 1, 0 ),
703 0 : Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
704 :
705 0 : DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ),
706 0 : Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
707 0 : }
708 :
709 0 : void DbGridControl::NavigationBar::StateChanged( StateChangedType nType )
710 : {
711 0 : Control::StateChanged( nType );
712 :
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
722 0 : };
723 :
724 0 : switch ( nType )
725 : {
726 : case STATE_CHANGE_MIRRORING:
727 : {
728 0 : sal_Bool bIsRTLEnabled = IsRTLEnabled();
729 0 : for ( size_t i=0; i < (sizeof (pWindows) / sizeof(pWindows[0])); ++i )
730 0 : pWindows[i]->EnableRTL( bIsRTLEnabled );
731 : }
732 0 : break;
733 :
734 : case STATE_CHANGE_ZOOM:
735 : {
736 0 : Fraction aZoom = GetZoom();
737 :
738 : // not all of these controls need to know the new zoom, but to be sure ...
739 0 : Font aFont( GetSettings().GetStyleSettings().GetFieldFont() );
740 0 : if ( IsControlFont() )
741 0 : aFont.Merge( GetControlFont() );
742 :
743 0 : for (size_t i=0; i < sizeof(pWindows)/sizeof(pWindows[0]); ++i)
744 : {
745 0 : pWindows[i]->SetZoom(aZoom);
746 0 : pWindows[i]->SetZoomedPointFont(aFont);
747 : }
748 :
749 0 : SetZoomedPointFont( aFont );
750 :
751 : // rearrange the controls
752 0 : m_nDefaultWidth = ArrangeControls();
753 : }
754 0 : break;
755 : }
756 0 : }
757 :
758 0 : DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor)
759 0 : :m_bIsNew(false)
760 : {
761 :
762 0 : if (pCur && pCur->Is())
763 : {
764 0 : Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY);
765 : DataColumn* pColumn;
766 0 : for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
767 : {
768 : Reference< XPropertySet > xColSet(
769 0 : xColumns->getByIndex(i), css::uno::UNO_QUERY);
770 0 : pColumn = new DataColumn(xColSet);
771 0 : m_aVariants.push_back( pColumn );
772 0 : }
773 :
774 0 : if (pCur->rowDeleted())
775 0 : m_eStatus = GRS_DELETED;
776 : else
777 : {
778 0 : if (bPaintCursor)
779 0 : m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GRS_INVALID : GRS_CLEAN;
780 : else
781 : {
782 0 : Reference< XPropertySet > xSet = pCur->getPropertySet();
783 0 : if (xSet.is())
784 : {
785 0 : m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
786 0 : if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
787 0 : m_eStatus = GRS_INVALID;
788 0 : else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
789 0 : m_eStatus = GRS_MODIFIED;
790 : else
791 0 : m_eStatus = GRS_CLEAN;
792 : }
793 : else
794 0 : m_eStatus = GRS_INVALID;
795 : }
796 : }
797 0 : if (!m_bIsNew && IsValid())
798 0 : m_aBookmark = pCur->getBookmark();
799 : else
800 0 : m_aBookmark = Any();
801 : }
802 : else
803 0 : m_eStatus = GRS_INVALID;
804 0 : }
805 :
806 0 : DbGridRow::~DbGridRow()
807 : {
808 0 : for ( size_t i = 0, n = m_aVariants.size(); i < n; ++i )
809 0 : delete m_aVariants[ i ];
810 0 : m_aVariants.clear();
811 0 : }
812 :
813 0 : void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor)
814 : {
815 0 : if (pCur && pCur->Is())
816 : {
817 0 : if (pCur->rowDeleted())
818 : {
819 0 : m_eStatus = GRS_DELETED;
820 0 : m_bIsNew = false;
821 : }
822 : else
823 : {
824 0 : m_eStatus = GRS_CLEAN;
825 0 : if (!bPaintCursor)
826 : {
827 0 : Reference< XPropertySet > xSet = pCur->getPropertySet();
828 : DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
829 :
830 0 : if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
831 0 : m_eStatus = GRS_MODIFIED;
832 0 : m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
833 : }
834 : else
835 0 : m_bIsNew = false;
836 : }
837 :
838 : try
839 : {
840 0 : if (!m_bIsNew && IsValid())
841 0 : m_aBookmark = pCur->getBookmark();
842 : else
843 0 : m_aBookmark = Any();
844 : }
845 0 : catch(SQLException&)
846 : {
847 : DBG_UNHANDLED_EXCEPTION();
848 0 : m_aBookmark = Any();
849 0 : m_eStatus = GRS_INVALID;
850 0 : m_bIsNew = false;
851 : }
852 : }
853 : else
854 : {
855 0 : m_aBookmark = Any();
856 0 : m_eStatus = GRS_INVALID;
857 0 : m_bIsNew = false;
858 : }
859 0 : }
860 :
861 0 : 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 0 : ,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 0 : ,m_bUpdating(false)
899 : {
900 :
901 0 : OUString sName(SVX_RESSTR(RID_STR_NAVIGATIONBAR));
902 0 : m_aBar.SetAccessibleName(sName);
903 0 : m_aBar.Show();
904 0 : ImplInitWindow( InitAll );
905 0 : }
906 :
907 0 : void DbGridControl::InsertHandleColumn()
908 : {
909 : // BrowseBox has problems when painting without a handleColumn (hide it here)
910 0 : if (HasHandle())
911 0 : BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString()));
912 : else
913 0 : BrowseBox::InsertHandleColumn(0);
914 0 : }
915 :
916 0 : void DbGridControl::Init()
917 : {
918 0 : BrowserHeader* pNewHeader = CreateHeaderBar(this);
919 0 : pHeader->SetMouseTransparent(false);
920 :
921 0 : SetHeaderBar(pNewHeader);
922 0 : SetMode(m_nMode);
923 0 : SetCursorColor(Color(0xFF, 0, 0));
924 :
925 0 : InsertHandleColumn();
926 0 : }
927 :
928 0 : DbGridControl::~DbGridControl()
929 : {
930 0 : RemoveColumns();
931 :
932 : {
933 0 : m_bWantDestruction = true;
934 0 : osl::MutexGuard aGuard(m_aDestructionSafety);
935 0 : if (m_pFieldListeners)
936 0 : DisconnectFromFields();
937 0 : if (m_pCursorDisposeListener)
938 : {
939 0 : delete m_pCursorDisposeListener;
940 0 : m_pCursorDisposeListener = NULL;
941 0 : }
942 : }
943 :
944 0 : if (m_nDeleteEvent)
945 0 : Application::RemoveUserEvent(m_nDeleteEvent);
946 :
947 0 : if (m_pDataSourcePropMultiplexer)
948 : {
949 0 : m_pDataSourcePropMultiplexer->dispose();
950 0 : m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
951 0 : delete m_pDataSourcePropListener;
952 0 : m_pDataSourcePropMultiplexer = NULL;
953 0 : m_pDataSourcePropListener = NULL;
954 : }
955 0 : m_xRowSetListener.clear();
956 :
957 0 : delete m_pDataCursor;
958 0 : delete m_pSeekCursor;
959 :
960 0 : }
961 :
962 0 : void DbGridControl::StateChanged( StateChangedType nType )
963 : {
964 0 : DbGridControl_Base::StateChanged( nType );
965 :
966 0 : switch (nType)
967 : {
968 : case STATE_CHANGE_MIRRORING:
969 0 : ImplInitWindow( InitWritingMode );
970 0 : Invalidate();
971 0 : break;
972 :
973 : case STATE_CHANGE_ZOOM:
974 : {
975 0 : ImplInitWindow( InitFont );
976 :
977 : // and give it a chance to rearrange
978 0 : Point aPoint = GetControlArea().TopLeft();
979 0 : sal_uInt16 nX = (sal_uInt16)aPoint.X();
980 0 : ArrangeControls(nX, (sal_uInt16)aPoint.Y());
981 0 : ReserveControlArea((sal_uInt16)nX);
982 : }
983 0 : break;
984 : case STATE_CHANGE_CONTROLFONT:
985 0 : ImplInitWindow( InitFont );
986 0 : Invalidate();
987 0 : break;
988 : case STATE_CHANGE_CONTROLFOREGROUND:
989 0 : ImplInitWindow( InitForeground );
990 0 : Invalidate();
991 0 : break;
992 : case STATE_CHANGE_CONTROLBACKGROUND:
993 0 : ImplInitWindow( InitBackground );
994 0 : Invalidate();
995 0 : break;
996 : }
997 0 : }
998 :
999 0 : void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
1000 : {
1001 0 : DbGridControl_Base::DataChanged( rDCEvt );
1002 0 : if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS ) &&
1003 0 : (rDCEvt.GetFlags() & SETTINGS_STYLE) )
1004 : {
1005 0 : ImplInitWindow( InitAll );
1006 0 : Invalidate();
1007 : }
1008 0 : }
1009 :
1010 0 : void DbGridControl::Select()
1011 : {
1012 0 : DbGridControl_Base::Select();
1013 :
1014 : // as the selected rows may have changed, udate the according display in our navigation bar
1015 0 : m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1016 :
1017 0 : if (m_pGridListener)
1018 0 : m_pGridListener->selectionChanged();
1019 0 : }
1020 :
1021 0 : void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
1022 : {
1023 0 : for ( size_t i = 0; i < m_aColumns.size(); ++i )
1024 : {
1025 0 : DbGridColumn* pCol = m_aColumns[ i ];
1026 0 : if (pCol)
1027 0 : pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
1028 : }
1029 :
1030 0 : if ( ( _eInitWhat & InitWritingMode ) != 0 )
1031 : {
1032 0 : if ( m_bNavigationBar )
1033 : {
1034 0 : m_aBar.EnableRTL( IsRTLEnabled() );
1035 : }
1036 : }
1037 :
1038 0 : if ( ( _eInitWhat & InitFont ) != 0 )
1039 : {
1040 0 : if ( m_bNavigationBar )
1041 : {
1042 0 : Font aFont = m_aBar.GetSettings().GetStyleSettings().GetFieldFont();
1043 0 : if ( IsControlFont() )
1044 0 : m_aBar.SetControlFont( GetControlFont() );
1045 : else
1046 0 : m_aBar.SetControlFont();
1047 :
1048 0 : m_aBar.SetZoom( GetZoom() );
1049 : }
1050 : }
1051 :
1052 0 : if ( ( _eInitWhat & InitBackground ) != 0 )
1053 : {
1054 0 : if (IsControlBackground())
1055 : {
1056 0 : GetDataWindow().SetBackground(GetControlBackground());
1057 0 : GetDataWindow().SetControlBackground(GetControlBackground());
1058 0 : GetDataWindow().SetFillColor(GetControlBackground());
1059 : }
1060 : else
1061 : {
1062 0 : GetDataWindow().SetControlBackground();
1063 0 : GetDataWindow().SetFillColor(GetFillColor());
1064 : }
1065 : }
1066 0 : }
1067 :
1068 0 : void DbGridControl::RemoveRows(bool bNewCursor)
1069 : {
1070 : // Did the data cursor change?
1071 0 : if (!bNewCursor)
1072 : {
1073 0 : DELETEZ(m_pSeekCursor);
1074 0 : m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1075 0 : m_nCurrentPos = m_nSeekPos = -1;
1076 0 : m_nOptions = OPT_READONLY;
1077 :
1078 0 : RowRemoved(0, GetRowCount(), false);
1079 0 : m_nTotalCount = -1;
1080 : }
1081 : else
1082 : {
1083 0 : RemoveRows();
1084 : }
1085 0 : }
1086 :
1087 0 : void DbGridControl::RemoveRows()
1088 : {
1089 : // we're going to remove all columns and all row, so deactivate the current cell
1090 0 : if (IsEditing())
1091 0 : DeactivateCell();
1092 :
1093 : // de-initialize all columns
1094 : // if there are columns, free all controllers
1095 0 : for (size_t i = 0; i < m_aColumns.size(); i++)
1096 0 : m_aColumns[ i ]->Clear();
1097 :
1098 0 : DELETEZ(m_pSeekCursor);
1099 0 : DELETEZ(m_pDataCursor);
1100 :
1101 0 : m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL;
1102 0 : m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1;
1103 0 : m_nOptions = OPT_READONLY;
1104 :
1105 : // reset number of sentences to zero in the browser
1106 0 : DbGridControl_Base::RemoveRows();
1107 0 : m_aBar.InvalidateAll(m_nCurrentPos, true);
1108 0 : }
1109 :
1110 0 : void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
1111 : {
1112 : // positioning of the controls
1113 0 : if (m_bNavigationBar)
1114 : {
1115 0 : nX = m_aBar.GetDefaultWidth();
1116 0 : Rectangle aRect(GetControlArea());
1117 0 : m_aBar.SetPosSizePixel(Point(0,nY + 1), Size(nX, aRect.GetSize().Height() - 1));
1118 : }
1119 0 : }
1120 :
1121 0 : void DbGridControl::EnableHandle(bool bEnable)
1122 : {
1123 0 : if (m_bHandle == bEnable)
1124 0 : return;
1125 :
1126 : // HandleColumn is only hidden because there are a lot of problems while painting otherwise
1127 0 : RemoveColumn( HandleColumnId );
1128 0 : m_bHandle = bEnable;
1129 0 : InsertHandleColumn();
1130 : }
1131 :
1132 : namespace
1133 : {
1134 0 : bool adjustModeForScrollbars( BrowserMode& _rMode, sal_Bool _bNavigationBar, sal_Bool _bHideScrollbars )
1135 : {
1136 0 : BrowserMode nOldMode = _rMode;
1137 :
1138 0 : if ( !_bNavigationBar )
1139 : {
1140 0 : _rMode &= ~BROWSER_AUTO_HSCROLL;
1141 : }
1142 :
1143 0 : if ( _bHideScrollbars )
1144 : {
1145 0 : _rMode |= ( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1146 0 : _rMode &= ~( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1147 : }
1148 : else
1149 : {
1150 0 : _rMode |= ( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL );
1151 0 : _rMode &= ~( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL );
1152 : }
1153 :
1154 : // note: if we have a navigation bar, we always have a AUTO_HSCROLL. In particular,
1155 : // _bHideScrollbars is ignored then
1156 0 : if ( _bNavigationBar )
1157 : {
1158 0 : _rMode |= BROWSER_AUTO_HSCROLL;
1159 0 : _rMode &= ~BROWSER_NO_HSCROLL;
1160 : }
1161 :
1162 0 : return nOldMode != _rMode;
1163 : }
1164 : }
1165 :
1166 0 : void DbGridControl::EnableNavigationBar(bool bEnable)
1167 : {
1168 0 : if (m_bNavigationBar == bEnable)
1169 0 : return;
1170 :
1171 0 : m_bNavigationBar = bEnable;
1172 :
1173 0 : if (bEnable)
1174 : {
1175 0 : m_aBar.Show();
1176 0 : m_aBar.Enable();
1177 0 : m_aBar.InvalidateAll(m_nCurrentPos, true);
1178 :
1179 0 : if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1180 0 : SetMode( m_nMode );
1181 :
1182 : // get size of the reserved ControlArea
1183 0 : Point aPoint = GetControlArea().TopLeft();
1184 0 : sal_uInt16 nX = (sal_uInt16)aPoint.X();
1185 :
1186 0 : ArrangeControls(nX, (sal_uInt16)aPoint.Y());
1187 0 : ReserveControlArea((sal_uInt16)nX);
1188 : }
1189 : else
1190 : {
1191 0 : m_aBar.Hide();
1192 0 : m_aBar.Disable();
1193 :
1194 0 : if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1195 0 : SetMode( m_nMode );
1196 :
1197 0 : ReserveControlArea();
1198 : }
1199 : }
1200 :
1201 0 : sal_uInt16 DbGridControl::SetOptions(sal_uInt16 nOpt)
1202 : {
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 ;) !");
1205 :
1206 : // for the next setDataSource (which is triggered by a refresh, for instance)
1207 0 : m_nOptionMask = nOpt;
1208 :
1209 : // normalize the new options
1210 0 : Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
1211 0 : if (xDataSourceSet.is())
1212 : {
1213 : // check what kind of options are available
1214 0 : sal_Int32 nPrivileges = 0;
1215 0 : xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1216 0 : if ((nPrivileges & Privilege::INSERT) == 0)
1217 0 : nOpt &= ~OPT_INSERT;
1218 0 : if ((nPrivileges & Privilege::UPDATE) == 0)
1219 0 : nOpt &= ~OPT_UPDATE;
1220 0 : if ((nPrivileges & Privilege::DELETE) == 0)
1221 0 : nOpt &= ~OPT_DELETE;
1222 : }
1223 : else
1224 0 : nOpt = OPT_READONLY;
1225 :
1226 : // need to do something after that ?
1227 0 : if (nOpt == m_nOptions)
1228 0 : return m_nOptions;
1229 :
1230 : // the 'update' option only affects our BrowserMode (with or w/o focus rect)
1231 0 : BrowserMode nNewMode = m_nMode;
1232 0 : if ((m_nMode & BROWSER_CURSOR_WO_FOCUS) == 0)
1233 : {
1234 0 : if (nOpt & OPT_UPDATE)
1235 0 : nNewMode |= BROWSER_HIDECURSOR;
1236 : else
1237 0 : nNewMode &= ~BROWSER_HIDECURSOR;
1238 : }
1239 : else
1240 0 : nNewMode &= ~BROWSER_HIDECURSOR;
1241 : // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
1242 :
1243 0 : if (nNewMode != m_nMode)
1244 : {
1245 0 : SetMode(nNewMode);
1246 0 : m_nMode = nNewMode;
1247 : }
1248 :
1249 : // _after_ setting the mode because this results in an ActivateCell
1250 0 : DeactivateCell();
1251 :
1252 0 : bool bInsertChanged = (nOpt & OPT_INSERT) != (m_nOptions & OPT_INSERT);
1253 0 : m_nOptions = nOpt;
1254 : // we need to set this before the code below because it indirectly uses m_nOptions
1255 :
1256 : // the 'insert' option affects our empty row
1257 0 : if (bInsertChanged)
1258 : {
1259 0 : if (m_nOptions & OPT_INSERT)
1260 : { // the insert option is to be set
1261 0 : m_xEmptyRow = new DbGridRow();
1262 0 : RowInserted(GetRowCount(), 1, true);
1263 : }
1264 : else
1265 : { // the insert option is to be reset
1266 0 : m_xEmptyRow = NULL;
1267 0 : if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
1268 0 : GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
1269 0 : RowRemoved(GetRowCount(), 1, true);
1270 : }
1271 : }
1272 :
1273 : // the 'delete' options has no immediate consequences
1274 :
1275 0 : ActivateCell();
1276 0 : Invalidate();
1277 0 : return m_nOptions;
1278 : }
1279 :
1280 0 : void DbGridControl::ForceHideScrollbars( bool _bForce )
1281 : {
1282 0 : if ( m_bHideScrollbars == _bForce )
1283 0 : return;
1284 :
1285 0 : m_bHideScrollbars = _bForce;
1286 :
1287 0 : if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1288 0 : SetMode( m_nMode );
1289 : }
1290 :
1291 0 : void DbGridControl::EnablePermanentCursor(bool bEnable)
1292 : {
1293 0 : if (IsPermanentCursorEnabled() == bEnable)
1294 0 : return;
1295 :
1296 0 : if (bEnable)
1297 : {
1298 0 : m_nMode &= ~BROWSER_HIDECURSOR; // without this BROWSER_CURSOR_WO_FOCUS won't have any affect
1299 0 : m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1300 : }
1301 : else
1302 : {
1303 0 : if (m_nOptions & OPT_UPDATE)
1304 0 : m_nMode |= BROWSER_HIDECURSOR; // no cursor at all
1305 : else
1306 0 : m_nMode &= ~BROWSER_HIDECURSOR; // at least the "non-permanent" cursor
1307 :
1308 0 : m_nMode &= ~BROWSER_CURSOR_WO_FOCUS;
1309 : }
1310 0 : SetMode(m_nMode);
1311 :
1312 0 : sal_Bool bWasEditing = IsEditing();
1313 0 : DeactivateCell();
1314 0 : if (bWasEditing)
1315 0 : ActivateCell();
1316 : }
1317 :
1318 0 : bool DbGridControl::IsPermanentCursorEnabled() const
1319 : {
1320 0 : return ((m_nMode & BROWSER_CURSOR_WO_FOCUS) != 0) && ((m_nMode & BROWSER_HIDECURSOR) == 0);
1321 : }
1322 :
1323 0 : void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
1324 : {
1325 0 : if ((GetCurColumnId() == _nColId) && IsEditing())
1326 : { // the controller which is currently active needs to be refreshed
1327 0 : DeactivateCell();
1328 0 : ActivateCell();
1329 : }
1330 0 : }
1331 :
1332 0 : void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, sal_uInt16 nOpts)
1333 : {
1334 0 : if (!_xCursor.is() && !m_pDataCursor)
1335 0 : return;
1336 :
1337 0 : if (m_pDataSourcePropMultiplexer)
1338 : {
1339 0 : m_pDataSourcePropMultiplexer->dispose();
1340 0 : m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer
1341 0 : delete m_pDataSourcePropListener;
1342 0 : m_pDataSourcePropMultiplexer = NULL;
1343 0 : m_pDataSourcePropListener = NULL;
1344 : }
1345 0 : m_xRowSetListener.clear();
1346 :
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 0 : if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY)->getColumns()->hasElements())
1351 : {
1352 0 : RemoveRows();
1353 0 : return;
1354 : }
1355 :
1356 : // did the data cursor change?
1357 0 : sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
1358 :
1359 0 : SetUpdateMode(false);
1360 0 : RemoveRows();
1361 0 : DisconnectFromFields();
1362 :
1363 0 : DELETEZ(m_pCursorDisposeListener);
1364 :
1365 : {
1366 0 : ::osl::MutexGuard aGuard(m_aAdjustSafety);
1367 0 : if (m_nAsynAdjustEvent)
1368 : {
1369 : // the adjust was thought to work with the old cursor which we don't have anymore
1370 0 : RemoveUserEvent(m_nAsynAdjustEvent);
1371 0 : m_nAsynAdjustEvent = 0;
1372 0 : }
1373 : }
1374 :
1375 : // get a new formatter and data cursor
1376 0 : m_xFormatter = NULL;
1377 0 : OStaticDataAccessTools aStaticTools;
1378 0 : Reference< ::com::sun::star::util::XNumberFormatsSupplier > xSupplier = aStaticTools.getNumberFormats(aStaticTools.getRowSetConnection(_xCursor), sal_True);
1379 0 : if (xSupplier.is())
1380 : {
1381 0 : m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter >(
1382 : ::com::sun::star::util::NumberFormatter::create(m_xContext),
1383 0 : UNO_QUERY);
1384 0 : m_xFormatter->attachNumberFormatsSupplier(xSupplier);
1385 :
1386 : // retrieve the datebase of the Numberformatter
1387 : try
1388 : {
1389 0 : xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
1390 : }
1391 0 : catch(Exception&)
1392 : {
1393 : }
1394 : }
1395 :
1396 0 : m_pDataCursor = new CursorWrapper(_xCursor);
1397 :
1398 : // now create a cursor for painting rows
1399 : // we need that cursor only if we are not in insert only mode
1400 0 : Reference< XResultSet > xClone;
1401 0 : Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
1402 : try
1403 : {
1404 0 : xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
1405 : }
1406 0 : catch(Exception&)
1407 : {
1408 : }
1409 0 : if (xClone.is())
1410 0 : m_pSeekCursor = new CursorWrapper(xClone);
1411 :
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 0 : if (m_pDataCursor)
1418 : {
1419 0 : m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
1420 0 : m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
1421 0 : m_pDataSourcePropMultiplexer->acquire();
1422 0 : m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
1423 0 : m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
1424 : }
1425 :
1426 0 : BrowserMode nOldMode = m_nMode;
1427 0 : if (m_pSeekCursor)
1428 : {
1429 : try
1430 : {
1431 0 : Reference< XPropertySet > xSet(_xCursor, UNO_QUERY);
1432 0 : if (xSet.is())
1433 : {
1434 : // check what kind of options are available
1435 0 : sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
1436 0 : xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
1437 :
1438 0 : if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
1439 : {
1440 0 : sal_Int32 nPrivileges = 0;
1441 0 : xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1442 :
1443 : // Insert Option should be set if insert only otherwise you won't see any rows
1444 : // and no insertion is possible
1445 0 : if ((m_nOptionMask & OPT_INSERT) && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & OPT_INSERT))
1446 0 : m_nOptions |= OPT_INSERT;
1447 0 : if ((m_nOptionMask & OPT_UPDATE) && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & OPT_UPDATE))
1448 0 : m_nOptions |= OPT_UPDATE;
1449 0 : if ((m_nOptionMask & OPT_DELETE) && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & OPT_DELETE))
1450 0 : m_nOptions |= OPT_DELETE;
1451 : }
1452 0 : }
1453 : }
1454 0 : catch( const Exception& )
1455 : {
1456 : DBG_UNHANDLED_EXCEPTION();
1457 : }
1458 :
1459 0 : sal_Bool bPermanentCursor = IsPermanentCursorEnabled();
1460 0 : m_nMode = DEFAULT_BROWSE_MODE;
1461 :
1462 0 : if ( bPermanentCursor )
1463 : {
1464 0 : m_nMode |= BROWSER_CURSOR_WO_FOCUS;
1465 0 : m_nMode &= ~BROWSER_HIDECURSOR;
1466 : }
1467 : else
1468 : {
1469 : // updates are allowed -> no focus rectangle
1470 0 : if ( m_nOptions & OPT_UPDATE )
1471 0 : m_nMode |= BROWSER_HIDECURSOR;
1472 : }
1473 :
1474 0 : if ( m_bMultiSelection )
1475 0 : m_nMode |= BROWSER_MULTISELECTION;
1476 : else
1477 0 : m_nMode &= ~BROWSER_MULTISELECTION;
1478 :
1479 0 : adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
1480 :
1481 0 : Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY);
1482 0 : if (xSupplyColumns.is())
1483 0 : InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
1484 :
1485 0 : ConnectToFields();
1486 : }
1487 :
1488 0 : sal_uInt32 nRecordCount(0);
1489 :
1490 0 : if (m_pSeekCursor)
1491 : {
1492 0 : Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1493 0 : xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1494 0 : m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1495 :
1496 0 : m_xRowSetListener = new RowSetEventListener(this);
1497 0 : Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
1498 0 : if ( xChangeBroad.is( ) )
1499 0 : xChangeBroad->addRowsChangeListener(m_xRowSetListener);
1500 :
1501 :
1502 : // insert the currently known rows
1503 : // and one row if we are able to insert rows
1504 0 : if (m_nOptions & OPT_INSERT)
1505 : {
1506 : // insert the empty row for insertion
1507 0 : m_xEmptyRow = new DbGridRow();
1508 0 : ++nRecordCount;
1509 : }
1510 0 : if (nRecordCount)
1511 : {
1512 0 : m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor, true);
1513 0 : m_xDataRow = new DbGridRow(m_pDataCursor, false);
1514 0 : RowInserted(0, nRecordCount, false);
1515 :
1516 0 : if (m_xSeekRow->IsValid())
1517 : try
1518 : {
1519 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
1520 : }
1521 0 : catch( const Exception& )
1522 : {
1523 : DBG_UNHANDLED_EXCEPTION();
1524 0 : m_nSeekPos = -1;
1525 : }
1526 : }
1527 : else
1528 : {
1529 : // no rows so we don't need a seekcursor
1530 0 : DELETEZ(m_pSeekCursor);
1531 0 : }
1532 : }
1533 :
1534 : // go to the old column
1535 0 : if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
1536 0 : nCurPos = 0;
1537 :
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 0 : if (nCurPos == 0 && ColCount() > 1)
1542 0 : nCurPos = 1;
1543 :
1544 : // there are rows so go to the selected current column
1545 0 : if (nRecordCount)
1546 0 : GoToRowColumnId(0, GetColumnId(nCurPos));
1547 : // else stop the editing if necessary
1548 0 : else if (IsEditing())
1549 0 : DeactivateCell();
1550 :
1551 : // now reset the mode
1552 0 : if (m_nMode != nOldMode)
1553 0 : SetMode(m_nMode);
1554 :
1555 : // RecalcRows was already called while resizing
1556 0 : if (!IsResizing() && GetRowCount())
1557 0 : RecalcRows(GetTopRow(), GetVisibleRows(), true);
1558 :
1559 0 : m_aBar.InvalidateAll(m_nCurrentPos, true);
1560 0 : SetUpdateMode(true);
1561 :
1562 : // start listening on the seek cursor
1563 0 : if (m_pSeekCursor)
1564 0 : m_pCursorDisposeListener = new DisposeListenerGridBridge(*this, Reference< XComponent > ((Reference< XInterface >)*m_pSeekCursor, UNO_QUERY), 0);
1565 : }
1566 :
1567 0 : void DbGridControl::RemoveColumns()
1568 : {
1569 0 : if ( IsEditing() )
1570 0 : DeactivateCell();
1571 :
1572 0 : for (size_t i = 0, n = m_aColumns.size(); i < n; i++)
1573 0 : delete m_aColumns[ i ];
1574 0 : m_aColumns.clear();
1575 :
1576 0 : DbGridControl_Base::RemoveColumns();
1577 0 : }
1578 :
1579 0 : DbGridColumn* DbGridControl::CreateColumn(sal_uInt16 nId) const
1580 : {
1581 0 : return new DbGridColumn(nId, *(DbGridControl*)this);
1582 : }
1583 :
1584 0 : sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
1585 : {
1586 : DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
1587 0 : sal_uInt16 nRealPos = nModelPos;
1588 0 : if (nModelPos != HEADERBAR_APPEND)
1589 : {
1590 : // calc the view pos. we can't use our converting functions because the new column
1591 : // has no VCL-representation, yet.
1592 0 : sal_Int16 nViewPos = nModelPos;
1593 0 : while (nModelPos--)
1594 : {
1595 0 : if ( m_aColumns[ nModelPos ]->IsHidden() )
1596 0 : --nViewPos;
1597 : }
1598 : // restore nModelPos, we need it later
1599 0 : nModelPos = nRealPos;
1600 : // the position the base class gets is the view pos + 1 (because of the handle column)
1601 0 : nRealPos = nViewPos + 1;
1602 : }
1603 :
1604 : // calculate the new id
1605 0 : for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && (nId <= m_aColumns.size()); ++nId)
1606 : ;
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 ..."
1609 :
1610 0 : DbGridControl_Base::AppendColumn(rName, nWidth, nRealPos, nId);
1611 0 : if (nModelPos == HEADERBAR_APPEND)
1612 0 : m_aColumns.push_back( CreateColumn(nId) );
1613 : else
1614 : {
1615 0 : DbGridColumns::iterator it = m_aColumns.begin();
1616 0 : ::std::advance( it, nModelPos );
1617 0 : m_aColumns.insert( it, CreateColumn(nId) );
1618 : }
1619 :
1620 0 : return nId;
1621 : }
1622 :
1623 0 : void DbGridControl::RemoveColumn(sal_uInt16 nId)
1624 : {
1625 0 : DbGridControl_Base::RemoveColumn(nId);
1626 :
1627 0 : const sal_uInt16 nIndex = GetModelColumnPos(nId);
1628 0 : if(nIndex != GRID_COLUMN_NOT_FOUND)
1629 : {
1630 0 : delete m_aColumns[nIndex];
1631 0 : m_aColumns.erase( m_aColumns.begin()+nIndex );
1632 : }
1633 0 : }
1634 :
1635 0 : void DbGridControl::ColumnMoved(sal_uInt16 nId)
1636 : {
1637 0 : DbGridControl_Base::ColumnMoved(nId);
1638 :
1639 : // remove the col from the model
1640 0 : 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
1645 :
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)
1648 :
1649 : // the new view pos is calculated easily
1650 0 : sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
1651 :
1652 : // from that we can compute the new model pos
1653 : sal_uInt16 nNewModelPos;
1654 0 : for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
1655 : {
1656 0 : if (!m_aColumns[ nNewModelPos ]->IsHidden())
1657 : {
1658 0 : if (!nNewViewPos)
1659 0 : break;
1660 : else
1661 0 : --nNewViewPos;
1662 : }
1663 : }
1664 : DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
1665 :
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.
1672 :
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 :
1675 :
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 : // +---+---+---+---+---+---+
1683 :
1684 : // if we move the column at (view) pos 1 to (view) pos 3 we have :
1685 :
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 : // +---+---+---+---+---+---+
1693 :
1694 : // or, sorted by the out-of-date model positions :
1695 :
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 : // +---+---+---+---+---+---+
1703 :
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 :
1707 :
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 : // +---+---+---+---+---+---+
1715 :
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 ;) ...)
1719 :
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 ;)
1723 :
1724 0 : DbGridColumn* temp = m_aColumns[ nOldModelPos ];
1725 :
1726 0 : DbGridColumns::iterator it = m_aColumns.begin();
1727 0 : ::std::advance( it, nOldModelPos );
1728 0 : m_aColumns.erase( it );
1729 :
1730 0 : it = m_aColumns.begin();
1731 0 : ::std::advance( it, nNewModelPos );
1732 0 : m_aColumns.insert( it, temp );
1733 0 : }
1734 :
1735 0 : bool DbGridControl::SeekRow(long nRow)
1736 : {
1737 : // in filter mode or in insert only mode we don't have any cursor!
1738 0 : if ( !SeekCursor( nRow ) )
1739 0 : return false;
1740 :
1741 0 : if ( IsFilterMode() )
1742 : {
1743 : DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
1744 0 : m_xPaintRow = m_xEmptyRow;
1745 : }
1746 : else
1747 : {
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 0 : if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
1751 0 : m_xPaintRow = m_xCurrentRow;
1752 : // seek to the empty insert row
1753 0 : else if ( IsInsertionRow( nRow ) )
1754 0 : m_xPaintRow = m_xEmptyRow;
1755 : else
1756 : {
1757 0 : m_xSeekRow->SetState( m_pSeekCursor, true );
1758 0 : m_xPaintRow = m_xSeekRow;
1759 : }
1760 : }
1761 :
1762 0 : DbGridControl_Base::SeekRow(nRow);
1763 :
1764 0 : return m_nSeekPos >= 0;
1765 : }
1766 :
1767 : // Is called whenever the visible amount of data changes
1768 0 : void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen )
1769 : {
1770 0 : RecalcRows(nNewTopRow, nLinesOnScreen, false);
1771 0 : }
1772 :
1773 0 : void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
1774 : {
1775 : // Wenn kein Cursor -> keine Rows im Browser.
1776 0 : if (!m_pSeekCursor)
1777 : {
1778 : DBG_ASSERT(GetRowCount() == 0,"DbGridControl: ohne Cursor darf es keine Rows geben");
1779 0 : return;
1780 : }
1781 :
1782 : // ignore any implicitly made updates
1783 0 : bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
1784 0 : if (bDisablePaint)
1785 0 : EnablePaint(false);
1786 :
1787 : // adjust cache to the visible area
1788 0 : Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
1789 0 : sal_Int32 nCacheSize = 0;
1790 0 : xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
1791 0 : bool bCacheAligned = false;
1792 : // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
1793 : // positioned on the first sentence
1794 0 : long nDelta = nNewTopRow - GetTopRow();
1795 : // limit for relative positioning
1796 0 : long nLimit = (nCacheSize) ? nCacheSize / 2 : 0;
1797 :
1798 : // more lines on screen than in cache
1799 0 : if (nLimit < nLinesOnScreen)
1800 : {
1801 0 : Any aCacheSize;
1802 0 : aCacheSize <<= sal_Int32(nLinesOnScreen*2);
1803 0 : xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
1804 : // here we need to update the cursor for sure
1805 0 : bUpdateCursor = true;
1806 0 : bCacheAligned = true;
1807 0 : nLimit = nLinesOnScreen;
1808 : }
1809 :
1810 : // In the following, all positionings are done as it is
1811 : // ensured that there are enough lines in the data cache
1812 :
1813 : // window goes downwards with less than two windows difference or
1814 : // the cache was updated and no rowcount yet
1815 0 : if (nDelta < nLimit && (nDelta > 0
1816 0 : || (bCacheAligned && m_nTotalCount < 0)) )
1817 0 : SeekCursor(nNewTopRow + nLinesOnScreen - 1, false);
1818 0 : else if (nDelta < 0 && std::abs(nDelta) < nLimit)
1819 0 : SeekCursor(nNewTopRow, false);
1820 0 : else if (nDelta != 0 || bUpdateCursor)
1821 0 : SeekCursor(nNewTopRow, true);
1822 :
1823 0 : AdjustRows();
1824 :
1825 : // ignore any updates implicit made
1826 0 : EnablePaint(true);
1827 : }
1828 :
1829 0 : void DbGridControl::RowInserted(long nRow, long nNumRows, bool bDoPaint, bool bKeepSelection)
1830 : {
1831 0 : if (nNumRows)
1832 : {
1833 0 : if (m_bRecordCountFinal && m_nTotalCount < 0)
1834 : {
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 0 : m_nTotalCount = GetRowCount() + nNumRows;
1838 0 : if (m_xEmptyRow.Is())
1839 0 : --m_nTotalCount;
1840 : }
1841 0 : else if (m_nTotalCount >= 0)
1842 0 : m_nTotalCount += nNumRows;
1843 :
1844 0 : DbGridControl_Base::RowInserted(nRow, nNumRows, bDoPaint, bKeepSelection);
1845 0 : m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1846 : }
1847 0 : }
1848 :
1849 0 : void DbGridControl::RowRemoved(long nRow, long nNumRows, bool bDoPaint)
1850 : {
1851 0 : if (nNumRows)
1852 : {
1853 0 : if (m_bRecordCountFinal && m_nTotalCount < 0)
1854 : {
1855 0 : m_nTotalCount = GetRowCount() - nNumRows;
1856 : // if we have an insert row reduce by 1
1857 0 : if (m_xEmptyRow.Is())
1858 0 : --m_nTotalCount;
1859 : }
1860 0 : else if (m_nTotalCount >= 0)
1861 0 : m_nTotalCount -= nNumRows;
1862 :
1863 0 : DbGridControl_Base::RowRemoved(nRow, nNumRows, bDoPaint);
1864 0 : m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1865 : }
1866 0 : }
1867 :
1868 0 : void DbGridControl::AdjustRows()
1869 : {
1870 0 : if (!m_pSeekCursor)
1871 0 : return;
1872 :
1873 0 : Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1874 :
1875 : // refresh RecordCount
1876 0 : sal_Int32 nRecordCount = 0;
1877 0 : xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1878 0 : if (!m_bRecordCountFinal)
1879 0 : m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1880 :
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
1883 :
1884 : // add additional AppendRow for insertion
1885 0 : if (m_nOptions & OPT_INSERT)
1886 0 : ++nRecordCount;
1887 :
1888 : // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
1889 0 : if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
1890 0 : m_xCurrentRow->IsNew())
1891 0 : ++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)
1895 :
1896 0 : if (nRecordCount != GetRowCount())
1897 : {
1898 0 : long nDelta = GetRowCount() - (long)nRecordCount;
1899 0 : if (nDelta > 0) // too many
1900 : {
1901 0 : RowRemoved(GetRowCount() - nDelta, nDelta, false);
1902 : // some rows are gone, thus, repaint starting at the current position
1903 0 : Invalidate();
1904 :
1905 0 : sal_Int32 nNewPos = AlignSeekCursor();
1906 0 : if (m_bSynchDisplay)
1907 0 : DbGridControl_Base::GoToRow(nNewPos);
1908 :
1909 0 : SetCurrent(nNewPos);
1910 : // there are rows so go to the selected current column
1911 0 : if (nRecordCount)
1912 0 : GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
1913 0 : if (!IsResizing() && GetRowCount())
1914 0 : RecalcRows(GetTopRow(), GetVisibleRows(), true);
1915 0 : m_aBar.InvalidateAll(m_nCurrentPos, true);
1916 : }
1917 : else // too few
1918 0 : RowInserted(GetRowCount(), -nDelta, true);
1919 : }
1920 :
1921 0 : if (m_bRecordCountFinal && m_nTotalCount < 0)
1922 : {
1923 0 : if (m_nOptions & OPT_INSERT)
1924 0 : m_nTotalCount = GetRowCount() - 1;
1925 : else
1926 0 : m_nTotalCount = GetRowCount();
1927 : }
1928 0 : m_aBar.InvalidateState(NavigationBar::RECORD_COUNT);
1929 : }
1930 :
1931 0 : DbGridControl_Base::RowStatus DbGridControl::GetRowStatus(long nRow) const
1932 : {
1933 0 : if (IsFilterRow(nRow))
1934 0 : return DbGridControl_Base::FILTER;
1935 0 : else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
1936 : {
1937 : // neue Zeile
1938 0 : if (!IsValid(m_xCurrentRow))
1939 0 : return DbGridControl_Base::DELETED;
1940 0 : else if (IsModified())
1941 0 : return DbGridControl_Base::MODIFIED;
1942 0 : else if (m_xCurrentRow->IsNew())
1943 0 : return DbGridControl_Base::CURRENTNEW;
1944 : else
1945 0 : return DbGridControl_Base::CURRENT;
1946 : }
1947 0 : else if (IsInsertionRow(nRow))
1948 0 : return DbGridControl_Base::NEW;
1949 0 : else if (!IsValid(m_xSeekRow))
1950 0 : return DbGridControl_Base::DELETED;
1951 : else
1952 0 : return DbGridControl_Base::CLEAN;
1953 : }
1954 :
1955 0 : void DbGridControl::PaintStatusCell(OutputDevice& rDev, const Rectangle& rRect) const
1956 : {
1957 0 : DbGridControl_Base::PaintStatusCell(rDev, rRect);
1958 0 : }
1959 :
1960 0 : void DbGridControl::PaintCell(OutputDevice& rDev, const Rectangle& rRect, sal_uInt16 nColumnId) const
1961 : {
1962 0 : if (!IsValid(m_xPaintRow))
1963 0 : return;
1964 :
1965 0 : size_t Location = GetModelColumnPos(nColumnId);
1966 0 : DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
1967 0 : if (pColumn)
1968 : {
1969 0 : Rectangle aArea(rRect);
1970 0 : if ((GetMode() & BROWSER_CURSOR_WO_FOCUS) == BROWSER_CURSOR_WO_FOCUS)
1971 : {
1972 0 : aArea.Top() += 1;
1973 0 : aArea.Bottom() -= 1;
1974 : }
1975 0 : pColumn->Paint(rDev, aArea, m_xPaintRow, getNumberFormatter());
1976 : }
1977 : }
1978 :
1979 0 : bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol)
1980 : {
1981 :
1982 0 : DeactivateCell( false );
1983 :
1984 0 : if ( m_pDataCursor
1985 0 : && ( m_nCurrentPos != nNewRow )
1986 0 : && !SetCurrent( nNewRow )
1987 : )
1988 : {
1989 0 : ActivateCell();
1990 0 : return false;
1991 : }
1992 :
1993 0 : if ( !DbGridControl_Base::CursorMoving( nNewRow, nNewCol ) )
1994 0 : return false;
1995 :
1996 0 : return true;
1997 : }
1998 :
1999 0 : bool DbGridControl::SetCurrent(long nNewRow)
2000 : {
2001 : // Each movement of the datacursor must start with BeginCursorAction and end with
2002 : // EndCursorAction to block all notifications during the movement
2003 0 : BeginCursorAction();
2004 :
2005 : try
2006 : {
2007 : // compare positions
2008 0 : if (SeekCursor(nNewRow))
2009 : {
2010 0 : if (IsFilterRow(nNewRow)) // special mode for filtering
2011 : {
2012 0 : m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
2013 0 : m_nCurrentPos = nNewRow;
2014 : }
2015 : else
2016 : {
2017 0 : bool bNewRowInserted = false;
2018 : // Should we go to the insertrow ?
2019 0 : if (IsInsertionRow(nNewRow))
2020 : {
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 0 : Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
2025 0 : if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
2026 : {
2027 0 : Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
2028 0 : xUpdateCursor->moveToInsertRow();
2029 : }
2030 0 : bNewRowInserted = true;
2031 : }
2032 : else
2033 : {
2034 :
2035 0 : if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
2036 : {
2037 0 : Any aBookmark = m_pSeekCursor->getBookmark();
2038 0 : if (!m_xCurrentRow || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
2039 : {
2040 : // adjust the cursor to the new desired row
2041 0 : if (!m_pDataCursor->moveToBookmark(aBookmark))
2042 : {
2043 0 : EndCursorAction();
2044 0 : return false;
2045 : }
2046 0 : }
2047 : }
2048 : }
2049 0 : m_xDataRow->SetState(m_pDataCursor, false);
2050 0 : m_xCurrentRow = m_xDataRow;
2051 :
2052 0 : long nPaintPos = -1;
2053 : // do we have to repaint the last regular row in case of setting defaults or autovalues
2054 0 : if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
2055 0 : nPaintPos = m_nCurrentPos;
2056 :
2057 0 : m_nCurrentPos = nNewRow;
2058 :
2059 : // repaint the new row to display all defaults
2060 0 : if (bNewRowInserted)
2061 0 : RowModified(m_nCurrentPos);
2062 0 : if (nPaintPos >= 0)
2063 0 : RowModified(nPaintPos);
2064 : }
2065 : }
2066 : else
2067 : {
2068 : OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
2069 0 : EndCursorAction();
2070 0 : return false;
2071 : }
2072 : }
2073 0 : catch ( const Exception& )
2074 : {
2075 : DBG_UNHANDLED_EXCEPTION();
2076 0 : EndCursorAction();
2077 0 : return false;
2078 : }
2079 :
2080 0 : EndCursorAction();
2081 0 : return true;
2082 : }
2083 :
2084 0 : void DbGridControl::CursorMoved()
2085 : {
2086 :
2087 : // cursor movement due to deletion or insertion of rows
2088 0 : if (m_pDataCursor && m_nCurrentPos != GetCurRow())
2089 : {
2090 0 : DeactivateCell(true);
2091 0 : SetCurrent(GetCurRow());
2092 : }
2093 :
2094 0 : DbGridControl_Base::CursorMoved();
2095 0 : m_aBar.InvalidateAll(m_nCurrentPos);
2096 :
2097 : // select the new column when they moved
2098 0 : if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
2099 : {
2100 0 : SelectColumnId( GetCurColumnId() );
2101 : }
2102 :
2103 0 : if ( m_nLastColId != GetCurColumnId() )
2104 0 : onColumnChange();
2105 0 : m_nLastColId = GetCurColumnId();
2106 :
2107 0 : if ( m_nLastRowId != GetCurRow() )
2108 0 : onRowChange();
2109 0 : m_nLastRowId = GetCurRow();
2110 0 : }
2111 :
2112 0 : void DbGridControl::onRowChange()
2113 : {
2114 : // not interested in
2115 0 : }
2116 :
2117 0 : void DbGridControl::onColumnChange()
2118 : {
2119 0 : if ( m_pGridListener )
2120 0 : m_pGridListener->columnChanged();
2121 0 : }
2122 :
2123 0 : void DbGridControl::setDisplaySynchron(bool bSync)
2124 : {
2125 0 : if (bSync != m_bSynchDisplay)
2126 : {
2127 0 : m_bSynchDisplay = bSync;
2128 0 : if (m_bSynchDisplay)
2129 0 : AdjustDataSource(false);
2130 : }
2131 0 : }
2132 :
2133 0 : void DbGridControl::AdjustDataSource(bool bFull)
2134 : {
2135 : SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
2136 0 : SolarMutexGuard aGuard;
2137 : // If the current row is recalculated at the moment, do not adjust
2138 :
2139 0 : if (bFull)
2140 0 : 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 comparision result
2143 : // may not be correct
2144 : else
2145 0 : if ( m_xCurrentRow.Is()
2146 0 : && !m_xCurrentRow->IsNew()
2147 0 : && !m_pDataCursor->isBeforeFirst()
2148 0 : && !m_pDataCursor->isAfterLast()
2149 0 : && !m_pDataCursor->rowDeleted()
2150 : )
2151 : {
2152 0 : sal_Bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
2153 :
2154 0 : sal_Bool bDataCursorIsOnNew = sal_False;
2155 0 : m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
2156 :
2157 0 : if ( bEqualBookmarks && !bDataCursorIsOnNew )
2158 : {
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 0 : RowModified(m_nCurrentPos);
2164 0 : return;
2165 : }
2166 : }
2167 :
2168 : // away from the data cursor's row
2169 0 : if (m_xPaintRow == m_xCurrentRow)
2170 0 : m_xPaintRow = m_xSeekRow;
2171 :
2172 : // not up-to-date row, thus, adjust completely
2173 0 : if (!m_xCurrentRow)
2174 0 : AdjustRows();
2175 :
2176 0 : sal_Int32 nNewPos = AlignSeekCursor();
2177 0 : if (nNewPos < 0)// could not find any position
2178 0 : return;
2179 :
2180 0 : m_bInAdjustDataSource = true;
2181 0 : if (nNewPos != m_nCurrentPos)
2182 : {
2183 0 : if (m_bSynchDisplay)
2184 0 : DbGridControl_Base::GoToRow(nNewPos);
2185 :
2186 0 : 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 0 : SetCurrent(nNewPos);
2192 : }
2193 : else
2194 : {
2195 0 : SetCurrent(nNewPos);
2196 0 : RowModified(nNewPos);
2197 : }
2198 0 : m_bInAdjustDataSource = false;
2199 :
2200 : // if the data cursor was moved from outside, this section is voided
2201 0 : SetNoSelection();
2202 0 : m_aBar.InvalidateAll(m_nCurrentPos, m_xCurrentRow.Is());
2203 : }
2204 :
2205 0 : sal_Int32 DbGridControl::AlignSeekCursor()
2206 : {
2207 : // position SeekCursor onto the data cursor, no data transmission
2208 :
2209 0 : if (!m_pSeekCursor)
2210 0 : return -1;
2211 :
2212 0 : Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
2213 :
2214 : // now align the seek cursor and the data cursor
2215 0 : if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
2216 0 : m_nSeekPos = GetRowCount() - 1;
2217 : else
2218 : {
2219 : try
2220 : {
2221 0 : if ( m_pDataCursor->isBeforeFirst() )
2222 : {
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 0 : m_pSeekCursor->first();
2226 0 : m_pSeekCursor->previous();
2227 0 : m_nSeekPos = -1;
2228 : }
2229 0 : else if ( m_pDataCursor->isAfterLast() )
2230 : {
2231 : DBG_WARNING( "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
2232 0 : m_pSeekCursor->last();
2233 0 : m_pSeekCursor->next();
2234 0 : m_nSeekPos = -1;
2235 : }
2236 : else
2237 : {
2238 0 : m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2239 0 : 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 0 : 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 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
2247 : }
2248 : }
2249 0 : catch(Exception&)
2250 : {
2251 : }
2252 : }
2253 0 : return m_nSeekPos;
2254 : }
2255 :
2256 0 : bool DbGridControl::SeekCursor(long nRow, bool bAbsolute)
2257 : {
2258 : // position SeekCursor onto the data cursor, no data transmission
2259 :
2260 : // additions for the filtermode
2261 0 : if (IsFilterRow(nRow))
2262 : {
2263 0 : m_nSeekPos = 0;
2264 0 : return true;
2265 : }
2266 :
2267 0 : if (!m_pSeekCursor)
2268 0 : return false;
2269 :
2270 : // is this an insertion?
2271 0 : if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
2272 0 : nRow >= m_nCurrentPos)
2273 : {
2274 : // if so, scrolling down must be prevented as this is already the last data set!
2275 0 : if (nRow == m_nCurrentPos)
2276 : {
2277 : // no adjustment necessary
2278 0 : m_nSeekPos = nRow;
2279 : }
2280 0 : else if (IsInsertionRow(nRow)) // blank row for data insertion
2281 0 : m_nSeekPos = nRow;
2282 : }
2283 0 : else if (IsInsertionRow(nRow)) // blank row for data insertion
2284 0 : m_nSeekPos = nRow;
2285 0 : else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & OPT_INSERT) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
2286 0 : m_nSeekPos = nRow;
2287 : else
2288 : {
2289 0 : bool bSuccess = false;
2290 0 : long nSteps = 0;
2291 : try
2292 : {
2293 0 : if ( m_pSeekCursor->rowDeleted() )
2294 : {
2295 : // somebody deleted the current row of the seek cursor. Move it away from this row.
2296 0 : m_pSeekCursor->next();
2297 0 : if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
2298 0 : bAbsolute = true;
2299 : }
2300 :
2301 0 : if ( !bAbsolute )
2302 : {
2303 : DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
2304 : "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
2305 0 : nSteps = nRow - (m_pSeekCursor->getRow() - 1);
2306 0 : bAbsolute = bAbsolute || (std::abs(nSteps) > 100);
2307 : }
2308 :
2309 0 : if ( bAbsolute )
2310 : {
2311 0 : bSuccess = m_pSeekCursor->absolute(nRow + 1);
2312 0 : if (bSuccess)
2313 0 : m_nSeekPos = nRow;
2314 : }
2315 : else
2316 : {
2317 0 : if (nSteps > 0) // position onto the last needed data set
2318 : {
2319 0 : if (m_pSeekCursor->isAfterLast())
2320 0 : bSuccess = false;
2321 0 : else if (m_pSeekCursor->isBeforeFirst())
2322 0 : bSuccess = m_pSeekCursor->absolute(nSteps);
2323 : else
2324 0 : bSuccess = m_pSeekCursor->relative(nSteps);
2325 : }
2326 0 : else if (nSteps < 0)
2327 : {
2328 0 : if (m_pSeekCursor->isBeforeFirst())
2329 0 : bSuccess = false;
2330 0 : else if (m_pSeekCursor->isAfterLast())
2331 0 : bSuccess = m_pSeekCursor->absolute(nSteps);
2332 : else
2333 0 : bSuccess = m_pSeekCursor->relative(nSteps);
2334 : }
2335 : else
2336 : {
2337 0 : m_nSeekPos = nRow;
2338 0 : return true;
2339 : }
2340 : }
2341 : }
2342 0 : catch(Exception&)
2343 : {
2344 : OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2345 : }
2346 :
2347 : try
2348 : {
2349 0 : if (!bSuccess)
2350 : {
2351 0 : if (bAbsolute || nSteps > 0)
2352 : {
2353 0 : if (m_pSeekCursor->isLast())
2354 0 : bSuccess = true;
2355 : else
2356 0 : bSuccess = m_pSeekCursor->last();
2357 : }
2358 : else
2359 : {
2360 0 : if (m_pSeekCursor->isFirst())
2361 0 : bSuccess = true;
2362 : else
2363 0 : bSuccess = m_pSeekCursor->first();
2364 : }
2365 : }
2366 :
2367 0 : if (bSuccess)
2368 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
2369 : else
2370 0 : m_nSeekPos = -1;
2371 : }
2372 0 : catch(Exception&)
2373 : {
2374 : OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2375 : DBG_UNHANDLED_EXCEPTION();
2376 0 : m_nSeekPos = -1; // no further data set available
2377 : }
2378 : }
2379 0 : return m_nSeekPos == nRow;
2380 : }
2381 :
2382 0 : void DbGridControl::MoveToFirst()
2383 : {
2384 0 : if (m_pSeekCursor && (GetCurRow() != 0))
2385 0 : MoveToPosition(0);
2386 0 : }
2387 :
2388 0 : void DbGridControl::MoveToLast()
2389 : {
2390 0 : if (!m_pSeekCursor)
2391 0 : return;
2392 :
2393 0 : if (m_nTotalCount < 0) // no RecordCount, yet
2394 : {
2395 : try
2396 : {
2397 0 : sal_Bool bRes = m_pSeekCursor->last();
2398 :
2399 0 : if (bRes)
2400 : {
2401 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
2402 0 : AdjustRows();
2403 : }
2404 : }
2405 0 : catch(Exception&)
2406 : {
2407 : }
2408 : }
2409 :
2410 : // position onto the last data set not on a blank row
2411 0 : if (m_nOptions & OPT_INSERT)
2412 : {
2413 0 : if ((GetRowCount() - 1) > 0)
2414 0 : MoveToPosition(GetRowCount() - 2);
2415 : }
2416 0 : else if (GetRowCount())
2417 0 : MoveToPosition(GetRowCount() - 1);
2418 : }
2419 :
2420 0 : void DbGridControl::MoveToPrev()
2421 : {
2422 0 : long nNewRow = std::max(GetCurRow() - 1L, 0L);
2423 0 : if (GetCurRow() != nNewRow)
2424 0 : MoveToPosition(nNewRow);
2425 0 : }
2426 :
2427 0 : void DbGridControl::MoveToNext()
2428 : {
2429 0 : if (!m_pSeekCursor)
2430 0 : return;
2431 :
2432 0 : if (m_nTotalCount > 0)
2433 : {
2434 : // move the data cursor to the right position
2435 0 : long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
2436 0 : if (GetCurRow() != nNewRow)
2437 0 : MoveToPosition(nNewRow);
2438 : }
2439 : else
2440 : {
2441 0 : sal_Bool bOk = sal_False;
2442 : try
2443 : {
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 0 : bOk = m_pSeekCursor->next();
2449 0 : if (bOk)
2450 : {
2451 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
2452 0 : MoveToPosition(GetCurRow() + 1);
2453 : }
2454 : }
2455 0 : catch(SQLException &)
2456 : {
2457 : DBG_UNHANDLED_EXCEPTION();
2458 : }
2459 :
2460 0 : if(!bOk)
2461 : {
2462 0 : AdjustRows();
2463 0 : if (m_nTotalCount > 0) // only to avoid infinte recursion
2464 0 : MoveToNext();
2465 : }
2466 : }
2467 : }
2468 :
2469 0 : void DbGridControl::MoveToPosition(sal_uInt32 nPos)
2470 : {
2471 0 : if (!m_pSeekCursor)
2472 0 : return;
2473 :
2474 0 : if (m_nTotalCount < 0 && (long)nPos >= GetRowCount())
2475 : {
2476 : try
2477 : {
2478 0 : if (!m_pSeekCursor->absolute(nPos + 1))
2479 : {
2480 0 : AdjustRows();
2481 0 : return;
2482 : }
2483 : else
2484 : {
2485 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
2486 0 : AdjustRows();
2487 : }
2488 : }
2489 0 : catch(Exception&)
2490 : {
2491 0 : return;
2492 : }
2493 : }
2494 0 : DbGridControl_Base::GoToRow(nPos);
2495 0 : m_aBar.InvalidateAll(m_nCurrentPos);
2496 : }
2497 :
2498 0 : void DbGridControl::AppendNew()
2499 : {
2500 0 : if (!m_pSeekCursor || !(m_nOptions & OPT_INSERT))
2501 0 : return;
2502 :
2503 0 : if (m_nTotalCount < 0) // no RecordCount, yet
2504 : {
2505 : try
2506 : {
2507 0 : sal_Bool bRes = m_pSeekCursor->last();
2508 :
2509 0 : if (bRes)
2510 : {
2511 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
2512 0 : AdjustRows();
2513 : }
2514 : }
2515 0 : catch(Exception&)
2516 : {
2517 0 : return;
2518 : }
2519 : }
2520 :
2521 0 : long nNewRow = m_nTotalCount + 1;
2522 0 : if (nNewRow > 0 && GetCurRow() != nNewRow)
2523 0 : MoveToPosition(nNewRow - 1);
2524 : }
2525 :
2526 0 : void DbGridControl::SetDesignMode(bool bMode)
2527 : {
2528 0 : if ((bool) IsDesignMode() != bMode)
2529 : {
2530 : // adjust Enable/Disable for design mode so that the headerbar remains configurable
2531 0 : if (bMode)
2532 : {
2533 0 : if (!IsEnabled())
2534 : {
2535 0 : Enable();
2536 0 : GetDataWindow().Disable();
2537 : }
2538 : }
2539 : else
2540 : {
2541 : // disable completely
2542 0 : if (!GetDataWindow().IsEnabled())
2543 0 : Disable();
2544 : }
2545 :
2546 0 : m_bDesignMode = bMode;
2547 0 : GetDataWindow().SetMouseTransparent(bMode);
2548 0 : SetMouseTransparent(bMode);
2549 :
2550 0 : m_aBar.InvalidateAll(m_nCurrentPos, true);
2551 : }
2552 0 : }
2553 :
2554 0 : void DbGridControl::SetFilterMode(bool bMode)
2555 : {
2556 0 : if (IsFilterMode() != bMode)
2557 : {
2558 0 : m_bFilterMode = bMode;
2559 :
2560 0 : if (bMode)
2561 : {
2562 0 : SetUpdateMode(false);
2563 :
2564 : // there is no cursor anymore
2565 0 : if (IsEditing())
2566 0 : DeactivateCell();
2567 0 : RemoveRows(false);
2568 :
2569 0 : m_xEmptyRow = new DbGridRow();
2570 :
2571 : // setting the new filter controls
2572 0 : for ( size_t i = 0; i < m_aColumns.size(); ++i )
2573 : {
2574 0 : DbGridColumn* pCurCol = m_aColumns[ i ];
2575 0 : if (!pCurCol->IsHidden())
2576 0 : pCurCol->UpdateControl();
2577 : }
2578 :
2579 : // one row for filtering
2580 0 : RowInserted(0, 1, true);
2581 0 : SetUpdateMode(true);
2582 : }
2583 : else
2584 0 : setDataSource(Reference< XRowSet > ());
2585 : }
2586 0 : }
2587 :
2588 0 : OUString DbGridControl::GetCellText(long _nRow, sal_uInt16 _nColId) const
2589 : {
2590 0 : size_t Location = GetModelColumnPos( _nColId );
2591 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2592 0 : OUString sRet;
2593 0 : if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
2594 0 : sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
2595 0 : return sRet;
2596 : }
2597 :
2598 0 : OUString DbGridControl::GetCurrentRowCellText(DbGridColumn* pColumn,const DbGridRowRef& _rRow) const
2599 : {
2600 : // text output for a single row
2601 0 : OUString aText;
2602 0 : if ( pColumn && IsValid(_rRow) )
2603 0 : aText = pColumn->GetCellText(_rRow, m_xFormatter);
2604 0 : return aText;
2605 : }
2606 :
2607 0 : sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId)
2608 : {
2609 0 : if (SeekRow(nRow))
2610 : {
2611 0 : size_t Location = GetModelColumnPos( nColId );
2612 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2613 0 : return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
2614 : }
2615 : else
2616 0 : return 30; // FIXME magic number for defaul cell width
2617 : }
2618 :
2619 0 : void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu)
2620 : {
2621 0 : sal_Bool bDelete = (m_nOptions & OPT_DELETE) && GetSelectRowCount() && !IsCurrentAppending();
2622 : // if only a blank row is selected than do not delete
2623 0 : bDelete = bDelete && !((m_nOptions & OPT_INSERT) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
2624 :
2625 0 : rMenu.EnableItem(SID_FM_DELETEROWS, bDelete);
2626 0 : rMenu.EnableItem(SID_FM_RECORD_SAVE, IsModified());
2627 :
2628 : // the undo is more difficult
2629 0 : bool bCanUndo = IsModified();
2630 0 : long nState = -1;
2631 0 : if (m_aMasterStateProvider.IsSet())
2632 0 : nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
2633 0 : bCanUndo &= ( 0 != nState );
2634 :
2635 0 : rMenu.EnableItem(SID_FM_RECORD_UNDO, bCanUndo);
2636 0 : }
2637 :
2638 0 : void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& /*rMenu*/, sal_uInt16 nExecutionResult)
2639 : {
2640 0 : switch (nExecutionResult)
2641 : {
2642 : case SID_FM_DELETEROWS:
2643 : // delete asynchronously
2644 0 : if (m_nDeleteEvent)
2645 0 : Application::RemoveUserEvent(m_nDeleteEvent);
2646 0 : m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
2647 0 : break;
2648 : case SID_FM_RECORD_UNDO:
2649 0 : Undo();
2650 0 : break;
2651 : case SID_FM_RECORD_SAVE:
2652 0 : SaveRow();
2653 0 : break;
2654 : default:
2655 0 : break;
2656 : }
2657 0 : }
2658 :
2659 0 : void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException )
2660 : {
2661 : SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
2662 0 : SolarMutexGuard aGuard;
2663 : // prop "IsModified" changed ?
2664 : // during update don't care about the modified state
2665 0 : if (!IsUpdating() && evt.PropertyName == FM_PROP_ISMODIFIED )
2666 : {
2667 0 : Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
2668 : DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
2669 0 : sal_Bool bIsNew = sal_False;
2670 0 : if (xSource.is())
2671 0 : bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
2672 :
2673 0 : if (bIsNew && m_xCurrentRow.Is())
2674 : {
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 0 : sal_Int32 nRecordCount = 0;
2677 0 : xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
2678 0 : 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 0 : if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
2682 : {
2683 0 : RowInserted(GetRowCount(), 1, true);
2684 0 : InvalidateStatusCell(m_nCurrentPos);
2685 0 : m_aBar.InvalidateAll(m_nCurrentPos);
2686 : }
2687 : }
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 obsolet now.
2693 0 : if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
2694 : {
2695 0 : RowRemoved(GetRowCount() - 1, 1, true);
2696 0 : InvalidateStatusCell(m_nCurrentPos);
2697 0 : m_aBar.InvalidateAll(m_nCurrentPos);
2698 : }
2699 : }
2700 : }
2701 0 : if (m_xCurrentRow.Is())
2702 : {
2703 0 : m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GRS_MODIFIED : GRS_CLEAN);
2704 0 : m_xCurrentRow->SetNew( bIsNew );
2705 0 : InvalidateStatusCell(m_nCurrentPos);
2706 : SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
2707 0 : }
2708 0 : }
2709 0 : }
2710 :
2711 0 : void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
2712 : {
2713 0 : if (!m_pSeekCursor || IsResizing())
2714 0 : return;
2715 :
2716 0 : sal_uInt16 nColId = GetColumnAtXPosPixel(rPosPixel.X());
2717 0 : long nRow = GetRowAtYPosPixel(rPosPixel.Y());
2718 0 : if (nColId != HandleColumnId && nRow >= 0)
2719 : {
2720 0 : if (GetDataWindow().IsMouseCaptured())
2721 0 : GetDataWindow().ReleaseMouse();
2722 :
2723 0 : size_t Location = GetModelColumnPos( nColId );
2724 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2725 0 : OStringTransferable* pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
2726 0 : Reference< XTransferable > xEnsureDelete(pTransferable);
2727 0 : pTransferable->StartDrag(this, DND_ACTION_COPY);
2728 : }
2729 : }
2730 :
2731 0 : bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2732 : {
2733 : return (_nRow >= 0)
2734 0 : && (_nRow < GetRowCount())
2735 0 : && (_nColId != HandleColumnId)
2736 0 : && (_nColId <= ColCount());
2737 : }
2738 :
2739 0 : void DbGridControl::copyCellText(sal_Int32 _nRow, sal_Int16 _nColId)
2740 : {
2741 : DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
2742 0 : DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ];
2743 0 : SeekRow(_nRow);
2744 0 : OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
2745 0 : }
2746 :
2747 0 : void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos )
2748 : {
2749 0 : PopupMenu aContextMenu( SVX_RES( RID_SVXMNU_ROWS ) );
2750 :
2751 0 : PreExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu );
2752 0 : aContextMenu.RemoveDisabledEntries( true, true );
2753 0 : PostExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu, aContextMenu.Execute( this, _rPreferredPos ) );
2754 :
2755 : // TODO: why this weird cast to sal_uInt16? What if we really have more than 65535 lines?
2756 : // -> change this to sal_uInt32
2757 0 : }
2758 :
2759 0 : void DbGridControl::Command(const CommandEvent& rEvt)
2760 : {
2761 0 : switch (rEvt.GetCommand())
2762 : {
2763 : case COMMAND_CONTEXTMENU:
2764 : {
2765 0 : if ( !m_pSeekCursor )
2766 : {
2767 0 : DbGridControl_Base::Command(rEvt);
2768 0 : return;
2769 : }
2770 :
2771 0 : if ( !rEvt.IsMouseEvent() )
2772 : { // context menu requested by keyboard
2773 0 : if ( GetSelectRowCount() )
2774 : {
2775 0 : long nRow = FirstSelectedRow( );
2776 :
2777 0 : ::Rectangle aRowRect( GetRowRectPixel( nRow, true ) );
2778 0 : executeRowContextMenu( nRow, aRowRect.LeftCenter() );
2779 :
2780 : // handled
2781 0 : return;
2782 : }
2783 : }
2784 :
2785 0 : sal_uInt16 nColId = GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X());
2786 0 : long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
2787 :
2788 0 : if (nColId == HandleColumnId)
2789 : {
2790 0 : executeRowContextMenu( nRow, rEvt.GetMousePosPixel() );
2791 : }
2792 0 : else if (canCopyCellText(nRow, nColId))
2793 : {
2794 0 : PopupMenu aContextMenu(SVX_RES(RID_SVXMNU_CELL));
2795 0 : aContextMenu.RemoveDisabledEntries(true, true);
2796 0 : switch (aContextMenu.Execute(this, rEvt.GetMousePosPixel()))
2797 : {
2798 : case SID_COPY:
2799 0 : copyCellText(nRow, nColId);
2800 0 : break;
2801 0 : }
2802 : }
2803 : else
2804 : {
2805 0 : DbGridControl_Base::Command(rEvt);
2806 0 : return;
2807 : }
2808 : }
2809 : default:
2810 0 : DbGridControl_Base::Command(rEvt);
2811 : }
2812 : }
2813 :
2814 0 : IMPL_LINK(DbGridControl, OnDelete, void*, /*EMPTYTAG*/ )
2815 : {
2816 0 : m_nDeleteEvent = 0;
2817 0 : DeleteSelectedRows();
2818 0 : return 0;
2819 : }
2820 :
2821 0 : void DbGridControl::DeleteSelectedRows()
2822 : {
2823 : DBG_ASSERT(GetSelection(), "keine selection!!!");
2824 :
2825 0 : if (!m_pSeekCursor)
2826 0 : return;
2827 : }
2828 :
2829 0 : CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId)
2830 : {
2831 0 : if (!IsValid(m_xCurrentRow) || !IsEnabled())
2832 0 : return NULL;
2833 :
2834 0 : size_t Location = GetModelColumnPos(nColumnId);
2835 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2836 0 : if (!pColumn)
2837 0 : return NULL;
2838 :
2839 0 : CellController* pReturn = NULL;
2840 0 : if (IsFilterMode())
2841 0 : pReturn = &pColumn->GetController();
2842 : else
2843 : {
2844 0 : if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
2845 : {
2846 0 : if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
2847 0 : return NULL;
2848 : }
2849 :
2850 0 : sal_Bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & OPT_INSERT));
2851 0 : sal_Bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & OPT_UPDATE));
2852 :
2853 0 : if ((bInsert && !pColumn->IsAutoValue()) || bUpdate || m_bForceROController)
2854 : {
2855 0 : pReturn = &pColumn->GetController();
2856 0 : if (pReturn)
2857 : {
2858 : // if it is an edit row, it is possible to give it a forced read-only property
2859 0 : if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController))
2860 : // controller could not be set to read-only in forceROController
2861 0 : if (!bInsert && !bUpdate)
2862 : // better use no controller than one without read-only
2863 0 : pReturn = NULL;
2864 : }
2865 : }
2866 : }
2867 0 : return pReturn;
2868 : }
2869 :
2870 0 : void DbGridControl::InitController(CellControllerRef& /*rController*/, long /*nRow*/, sal_uInt16 nColumnId)
2871 : {
2872 0 : size_t Location = GetModelColumnPos(nColumnId);
2873 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
2874 0 : if (pColumn)
2875 0 : pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
2876 0 : }
2877 :
2878 0 : void DbGridControl::CellModified()
2879 : {
2880 : SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
2881 :
2882 : {
2883 0 : ::osl::MutexGuard aGuard(m_aAdjustSafety);
2884 0 : if (m_nAsynAdjustEvent)
2885 : {
2886 : SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
2887 0 : RemoveUserEvent(m_nAsynAdjustEvent);
2888 0 : m_nAsynAdjustEvent = 0;
2889 :
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 0 : if (m_bPendingAdjustRows)
2893 0 : AdjustRows();
2894 : else
2895 0 : AdjustDataSource();
2896 0 : }
2897 : }
2898 :
2899 0 : if (!IsFilterMode() && IsValid(m_xCurrentRow) && !m_xCurrentRow->IsModified())
2900 : {
2901 : // enable edit mode
2902 : // a data set should be inserted
2903 0 : if (m_xCurrentRow->IsNew())
2904 : {
2905 0 : 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 0 : if (m_nCurrentPos == GetRowCount() - 1)
2909 : {
2910 : // increment RowCount
2911 0 : RowInserted(GetRowCount(), 1, true);
2912 0 : InvalidateStatusCell(m_nCurrentPos);
2913 0 : m_aBar.InvalidateAll(m_nCurrentPos);
2914 : }
2915 : }
2916 0 : else if (m_xCurrentRow->GetStatus() != GRS_MODIFIED)
2917 : {
2918 0 : m_xCurrentRow->SetState(m_pDataCursor, false);
2919 : SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
2920 0 : m_xCurrentRow->SetStatus(GRS_MODIFIED);
2921 : SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
2922 0 : InvalidateStatusCell(m_nCurrentPos);
2923 : }
2924 : }
2925 0 : }
2926 :
2927 0 : void DbGridControl::Dispatch(sal_uInt16 nId)
2928 : {
2929 0 : if (nId == BROWSER_CURSORENDOFFILE)
2930 : {
2931 0 : if (m_nOptions & OPT_INSERT)
2932 0 : AppendNew();
2933 : else
2934 0 : MoveToLast();
2935 : }
2936 : else
2937 0 : DbGridControl_Base::Dispatch(nId);
2938 0 : }
2939 :
2940 0 : void DbGridControl::Undo()
2941 : {
2942 0 : if (!IsFilterMode() && IsValid(m_xCurrentRow) && IsModified())
2943 : {
2944 : // check if we have somebody doin' the UNDO for us
2945 0 : long nState = -1;
2946 0 : if (m_aMasterStateProvider.IsSet())
2947 0 : nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO);
2948 0 : 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 0 : long lResult = m_aMasterSlotExecutor.Call((void*)SID_FM_RECORD_UNDO);
2952 0 : if (lResult)
2953 : // handled
2954 0 : return;
2955 : }
2956 0 : else if (nState == 0)
2957 : // yes, we have, and the slot is disabled
2958 0 : return;
2959 :
2960 0 : BeginCursorAction();
2961 :
2962 0 : sal_Bool bAppending = m_xCurrentRow->IsNew();
2963 0 : sal_Bool bDirty = m_xCurrentRow->IsModified();
2964 :
2965 : try
2966 : {
2967 : // cancel editing
2968 0 : Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
2969 : // no effects if we're not updating currently
2970 0 : if (bAppending)
2971 : // just refresh the row
2972 0 : xUpdateCursor->moveToInsertRow();
2973 : else
2974 0 : xUpdateCursor->cancelRowUpdates();
2975 :
2976 : }
2977 0 : catch(Exception&)
2978 : {
2979 : DBG_UNHANDLED_EXCEPTION();
2980 : }
2981 :
2982 0 : EndCursorAction();
2983 :
2984 0 : m_xDataRow->SetState(m_pDataCursor, false);
2985 0 : if (&m_xPaintRow == &m_xCurrentRow)
2986 0 : m_xPaintRow = m_xCurrentRow = m_xDataRow;
2987 : else
2988 0 : m_xCurrentRow = m_xDataRow;
2989 :
2990 0 : if (bAppending && (DbGridControl_Base::IsModified() || bDirty))
2991 : // remove the row
2992 0 : 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 0 : RowRemoved(GetRowCount() - 1, 1, true);
2996 0 : m_aBar.InvalidateAll(m_nCurrentPos);
2997 : }
2998 :
2999 0 : RowModified(m_nCurrentPos);
3000 : }
3001 : }
3002 :
3003 0 : void DbGridControl::resetCurrentRow()
3004 : {
3005 0 : if (IsModified())
3006 : {
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 obsolet "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 0 : Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
3015 0 : if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
3016 : {
3017 : // are we on a new row currently ?
3018 0 : if (m_xCurrentRow->IsNew())
3019 : {
3020 0 : if (m_nCurrentPos == GetRowCount() - 2)
3021 : {
3022 0 : RowRemoved(GetRowCount() - 1, 1, true);
3023 0 : m_aBar.InvalidateAll(m_nCurrentPos);
3024 : }
3025 : }
3026 : }
3027 :
3028 : // update the rows
3029 0 : m_xDataRow->SetState(m_pDataCursor, false);
3030 0 : if (&m_xPaintRow == &m_xCurrentRow)
3031 0 : m_xPaintRow = m_xCurrentRow = m_xDataRow;
3032 : else
3033 0 : m_xCurrentRow = m_xDataRow;
3034 : }
3035 :
3036 0 : RowModified(GetCurRow()); // will update the current controller if affected
3037 0 : }
3038 :
3039 0 : void DbGridControl::RowModified( long nRow, sal_uInt16 /*nColId*/ )
3040 : {
3041 0 : if (nRow == m_nCurrentPos && IsEditing())
3042 : {
3043 0 : CellControllerRef aTmpRef = Controller();
3044 0 : aTmpRef->ClearModified();
3045 0 : InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
3046 : }
3047 0 : DbGridControl_Base::RowModified(nRow);
3048 0 : }
3049 :
3050 0 : bool DbGridControl::IsModified() const
3051 : {
3052 0 : return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || DbGridControl_Base::IsModified());
3053 : }
3054 :
3055 0 : bool DbGridControl::IsCurrentAppending() const
3056 : {
3057 0 : return m_xCurrentRow.Is() && m_xCurrentRow->IsNew();
3058 : }
3059 :
3060 0 : bool DbGridControl::IsInsertionRow(long nRow) const
3061 : {
3062 0 : return (m_nOptions & OPT_INSERT) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
3063 : }
3064 :
3065 0 : bool DbGridControl::SaveModified()
3066 : {
3067 : SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
3068 : DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
3069 0 : if (!IsValid(m_xCurrentRow))
3070 0 : return true;
3071 :
3072 : // accept input for this field
3073 : // Where there changes at the current input field?
3074 0 : if (!DbGridControl_Base::IsModified())
3075 0 : return true;
3076 :
3077 0 : size_t Location = GetModelColumnPos( GetCurColumnId() );
3078 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3079 0 : bool bOK = pColumn && pColumn->Commit();
3080 : DBG_ASSERT( Controller().Is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
3081 0 : 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 0 : return bOK;
3089 :
3090 0 : if (bOK)
3091 : {
3092 0 : Controller()->ClearModified();
3093 :
3094 0 : if ( IsValid(m_xCurrentRow) )
3095 : {
3096 0 : m_xCurrentRow->SetState(m_pDataCursor, false);
3097 : SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3098 0 : InvalidateStatusCell( m_nCurrentPos );
3099 : }
3100 : else
3101 : {
3102 : SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3103 : }
3104 : }
3105 : else
3106 : {
3107 : // reset the modified flag ....
3108 0 : Controller()->SetModified();
3109 : }
3110 :
3111 0 : return bOK;
3112 : }
3113 :
3114 0 : bool DbGridControl::SaveRow()
3115 : {
3116 : SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
3117 : // valid row
3118 0 : if (!IsValid(m_xCurrentRow) || !IsModified())
3119 0 : return true;
3120 : // value of the controller was not saved, yet
3121 0 : else if (Controller().Is() && Controller()->IsModified())
3122 : {
3123 0 : if (!SaveModified())
3124 0 : return false;
3125 : }
3126 0 : m_bUpdating = true;
3127 :
3128 0 : BeginCursorAction();
3129 0 : sal_Bool bAppending = m_xCurrentRow->IsNew();
3130 0 : bool bSuccess = false;
3131 : try
3132 : {
3133 0 : Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY);
3134 0 : if (bAppending)
3135 0 : xUpdateCursor->insertRow();
3136 : else
3137 0 : xUpdateCursor->updateRow();
3138 0 : bSuccess = true;
3139 : }
3140 0 : catch(SQLException&)
3141 : {
3142 0 : EndCursorAction();
3143 0 : m_bUpdating = false;
3144 0 : return false;
3145 : }
3146 :
3147 : try
3148 : {
3149 0 : if (bSuccess)
3150 : {
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 0 : m_xCurrentRow->SetState(m_pDataCursor, false);
3154 : SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
3155 0 : m_xCurrentRow->SetNew(false);
3156 :
3157 : // adjust the seekcursor if it is on the same position as the datacursor
3158 0 : if (m_nSeekPos == m_nCurrentPos || bAppending)
3159 : {
3160 : // get the bookmark to refetch the data
3161 : // in insert mode we take the new bookmark of the data cursor
3162 0 : Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
3163 0 : m_pSeekCursor->moveToBookmark(aBookmark);
3164 : // get the data
3165 0 : m_xSeekRow->SetState(m_pSeekCursor, true);
3166 0 : m_nSeekPos = m_pSeekCursor->getRow() - 1;
3167 : }
3168 : }
3169 : // and repaint the row
3170 0 : RowModified(m_nCurrentPos);
3171 : }
3172 0 : catch(Exception&)
3173 : {
3174 : }
3175 :
3176 0 : m_bUpdating = false;
3177 0 : EndCursorAction();
3178 :
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 0 : return true;
3185 : }
3186 :
3187 0 : bool DbGridControl::PreNotify(NotifyEvent& rEvt)
3188 : {
3189 : // do not handle events of the Navbar
3190 0 : if (m_aBar.IsWindowOrChild(rEvt.GetWindow()))
3191 0 : return BrowseBox::PreNotify(rEvt);
3192 :
3193 0 : switch (rEvt.GetType())
3194 : {
3195 : case EVENT_KEYINPUT:
3196 : {
3197 0 : const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
3198 :
3199 0 : sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
3200 0 : sal_Bool bShift = pKeyEvent->GetKeyCode().IsShift();
3201 0 : sal_Bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
3202 0 : sal_Bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
3203 0 : if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
3204 : {
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 0 : KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
3209 0 : KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
3210 :
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 0 : Control::KeyInput( aNewEvent );
3214 0 : return true;
3215 : }
3216 :
3217 0 : if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
3218 : {
3219 0 : if (IsModified())
3220 : {
3221 0 : Undo();
3222 0 : return true;
3223 : }
3224 : }
3225 0 : else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
3226 : {
3227 0 : if ((m_nOptions & OPT_DELETE) && GetSelectRowCount())
3228 : {
3229 : // delete asynchronously
3230 0 : if (m_nDeleteEvent)
3231 0 : Application::RemoveUserEvent(m_nDeleteEvent);
3232 0 : m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete));
3233 0 : return true;
3234 : }
3235 : }
3236 : } // no break!
3237 : default:
3238 0 : return DbGridControl_Base::PreNotify(rEvt);
3239 : }
3240 : }
3241 :
3242 0 : bool DbGridControl::IsTabAllowed(bool bRight) const
3243 : {
3244 0 : if (bRight)
3245 : // Tab only if not on the _last_ row
3246 0 : return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
3247 0 : GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
3248 : else
3249 : {
3250 : // Tab only if not on the _first_ row
3251 0 : return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
3252 : }
3253 : }
3254 :
3255 0 : void DbGridControl::KeyInput( const KeyEvent& rEvt )
3256 : {
3257 0 : if (rEvt.GetKeyCode().GetFunction() == KEYFUNC_COPY)
3258 : {
3259 0 : long nRow = GetCurRow();
3260 0 : sal_uInt16 nColId = GetCurColumnId();
3261 0 : if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
3262 : {
3263 0 : size_t Location = GetModelColumnPos( nColId );
3264 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3265 0 : OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
3266 0 : return;
3267 : }
3268 : }
3269 0 : DbGridControl_Base::KeyInput(rEvt);
3270 : }
3271 :
3272 0 : void DbGridControl::HideColumn(sal_uInt16 nId)
3273 : {
3274 0 : DeactivateCell();
3275 :
3276 : // determine the col for the focus to set to after removal
3277 0 : sal_uInt16 nPos = GetViewColumnPos(nId);
3278 0 : sal_uInt16 nNewColId = nPos == (ColCount()-1)
3279 0 : ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
3280 0 : : GetColumnIdFromViewPos(nPos+1); // take the next
3281 :
3282 0 : long lCurrentWidth = GetColumnWidth(nId);
3283 0 : DbGridControl_Base::RemoveColumn(nId);
3284 : // don't use my own RemoveColumn, this would remove it from m_aColumns, too
3285 :
3286 : // update my model
3287 0 : size_t Location = GetModelColumnPos( nId );
3288 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3289 : DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
3290 0 : if (pColumn)
3291 : {
3292 0 : pColumn->m_bHidden = sal_True;
3293 0 : pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
3294 : }
3295 :
3296 : // and reset the focus
3297 0 : if ( nId == GetCurColumnId() )
3298 0 : GoToColumnId( nNewColId );
3299 0 : }
3300 :
3301 0 : void DbGridControl::ShowColumn(sal_uInt16 nId)
3302 : {
3303 0 : sal_uInt16 nPos = GetModelColumnPos(nId);
3304 : DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
3305 0 : if (nPos == GRID_COLUMN_NOT_FOUND)
3306 0 : return;
3307 :
3308 0 : DbGridColumn* pColumn = m_aColumns[ nPos ];
3309 0 : if (!pColumn->IsHidden())
3310 : {
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 0 : return;
3314 : }
3315 : DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3316 : // the opposite situation ...
3317 :
3318 : // to determine the new view position we need an adjacent non-hidden column
3319 0 : sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
3320 : // first search the cols to the right
3321 0 : for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
3322 : {
3323 0 : DbGridColumn* pCurCol = m_aColumns[ i ];
3324 0 : if (!pCurCol->IsHidden())
3325 : {
3326 0 : nNextNonHidden = i;
3327 0 : break;
3328 : }
3329 : }
3330 0 : if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
3331 : {
3332 : // then to the left
3333 0 : for ( size_t i = nPos; i > 0; --i )
3334 : {
3335 0 : DbGridColumn* pCurCol = m_aColumns[ i-1 ];
3336 0 : if (!pCurCol->IsHidden())
3337 : {
3338 0 : nNextNonHidden = i-1;
3339 0 : break;
3340 : }
3341 : }
3342 : }
3343 : sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
3344 : ? 1 // there is no visible column -> insert behinde the handle col
3345 0 : : 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 ...
3350 :
3351 0 : 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 0 : ++nNewViewPos;
3354 :
3355 0 : DeactivateCell();
3356 :
3357 0 : OUString aName;
3358 0 : pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
3359 0 : InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HIB_CENTER | HIB_VCENTER | HIB_CLICKABLE, nNewViewPos);
3360 0 : pColumn->m_bHidden = sal_False;
3361 :
3362 0 : ActivateCell();
3363 0 : Invalidate();
3364 : }
3365 :
3366 0 : sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
3367 : {
3368 0 : if (nPos >= m_aColumns.size())
3369 : {
3370 : OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
3371 0 : return GRID_COLUMN_NOT_FOUND;
3372 : }
3373 :
3374 0 : 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)
3378 :
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;
3385 :
3386 : DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
3387 : "DbGridControl::GetColumnIdFromModelPos : this isn't consistent .... did I misunderstand something ?");
3388 : }
3389 : #endif
3390 0 : return pCol->GetId();
3391 : }
3392 :
3393 0 : sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
3394 : {
3395 0 : for ( size_t i = 0; i < m_aColumns.size(); ++i )
3396 0 : if ( m_aColumns[ i ]->GetId() == nId )
3397 0 : return i;
3398 :
3399 0 : return GRID_COLUMN_NOT_FOUND;
3400 : }
3401 :
3402 0 : void DbGridControl::implAdjustInSolarThread(bool _bRows)
3403 : {
3404 : SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
3405 0 : ::osl::MutexGuard aGuard(m_aAdjustSafety);
3406 0 : if (::osl::Thread::getCurrentIdentifier() != Application::GetMainThreadIdentifier())
3407 : {
3408 0 : m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ));
3409 0 : m_bPendingAdjustRows = _bRows;
3410 0 : if (_bRows)
3411 : SAL_INFO("svx.fmcomp", "posting an AdjustRows");
3412 : else
3413 : SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
3414 : }
3415 : else
3416 : {
3417 0 : 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 0 : AdjustRows();
3428 :
3429 0 : if ( !_bRows )
3430 0 : AdjustDataSource();
3431 0 : }
3432 0 : }
3433 :
3434 0 : IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat)
3435 : {
3436 0 : m_nAsynAdjustEvent = 0;
3437 :
3438 0 : AdjustRows();
3439 : // see implAdjustInSolarThread for a comment why we do this every time
3440 :
3441 0 : if ( !pAdjustWhat )
3442 0 : AdjustDataSource();
3443 :
3444 0 : return 0L;
3445 : }
3446 :
3447 0 : void DbGridControl::BeginCursorAction()
3448 : {
3449 0 : if (m_pFieldListeners)
3450 : {
3451 0 : ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3452 0 : ColumnFieldValueListeners::const_iterator aIter = pListeners->begin();
3453 0 : while (aIter != pListeners->end())
3454 : {
3455 0 : GridFieldValueListener* pCurrent = (*aIter).second;
3456 0 : if (pCurrent)
3457 0 : pCurrent->suspend();
3458 0 : ++aIter;
3459 : }
3460 : }
3461 :
3462 0 : if (m_pDataSourcePropListener)
3463 0 : m_pDataSourcePropListener->suspend();
3464 0 : }
3465 :
3466 0 : void DbGridControl::EndCursorAction()
3467 : {
3468 0 : if (m_pFieldListeners)
3469 : {
3470 0 : ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3471 0 : ColumnFieldValueListeners::const_iterator aIter = pListeners->begin();
3472 0 : while (aIter != pListeners->end())
3473 : {
3474 0 : GridFieldValueListener* pCurrent = (*aIter).second;
3475 0 : if (pCurrent)
3476 0 : pCurrent->resume();
3477 0 : ++aIter;
3478 : }
3479 : }
3480 :
3481 0 : if (m_pDataSourcePropListener)
3482 0 : m_pDataSourcePropListener->resume();
3483 0 : }
3484 :
3485 0 : void DbGridControl::ConnectToFields()
3486 : {
3487 0 : ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3488 : DBG_ASSERT(!pListeners || pListeners->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
3489 :
3490 0 : if (!pListeners)
3491 : {
3492 0 : pListeners = new ColumnFieldValueListeners;
3493 0 : m_pFieldListeners = pListeners;
3494 : }
3495 :
3496 0 : for ( size_t i = 0; i < m_aColumns.size(); ++i )
3497 : {
3498 0 : DbGridColumn* pCurrent = m_aColumns[ i ];
3499 0 : sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
3500 0 : if (GRID_COLUMN_NOT_FOUND == nViewPos)
3501 0 : continue;
3502 :
3503 0 : Reference< XPropertySet > xField = pCurrent->GetField();
3504 0 : if (!xField.is())
3505 0 : continue;
3506 :
3507 : // column is visible and bound here
3508 0 : GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
3509 : DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
3510 0 : rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
3511 0 : }
3512 0 : }
3513 :
3514 0 : void DbGridControl::DisconnectFromFields()
3515 : {
3516 0 : if (!m_pFieldListeners)
3517 0 : return;
3518 :
3519 0 : ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3520 0 : while (!pListeners->empty())
3521 : {
3522 : #ifdef DBG_UTIL
3523 : sal_Int32 nOldSize = pListeners->size();
3524 : #endif
3525 0 : 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 !");
3527 : }
3528 :
3529 0 : delete pListeners;
3530 0 : m_pFieldListeners = NULL;
3531 : }
3532 :
3533 0 : void DbGridControl::FieldValueChanged(sal_uInt16 _nId, const PropertyChangeEvent& /*_evt*/)
3534 : {
3535 0 : osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
3536 : // needed as this may run in a thread other than the main one
3537 0 : if (GetRowStatus(GetCurRow()) != DbGridControl_Base::MODIFIED)
3538 : // all other cases are handled elsewhere
3539 0 : return;
3540 :
3541 0 : size_t Location = GetModelColumnPos( _nId );
3542 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3543 0 : if (pColumn)
3544 : {
3545 0 : sal_Bool bAcquiredPaintSafety = sal_False;
3546 0 : while (!m_bWantDestruction && !bAcquiredPaintSafety)
3547 0 : bAcquiredPaintSafety = Application::GetSolarMutex().tryToAcquire();
3548 :
3549 0 : 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 0 : 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 0 : Application::GetSolarMutex().release();
3557 0 : return;
3558 : }
3559 : // here we got the solar mutex, transfer it to a guard for safety reasons
3560 0 : SolarMutexGuard aPaintSafety;
3561 0 : Application::GetSolarMutex().release();
3562 :
3563 : // and finally do the update ...
3564 0 : pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter);
3565 0 : RowModified(GetCurRow(), _nId);
3566 0 : }
3567 : }
3568 :
3569 0 : void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
3570 : {
3571 0 : ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners;
3572 0 : if (!pListeners)
3573 : {
3574 : OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
3575 0 : return;
3576 : }
3577 :
3578 0 : ColumnFieldValueListeners::iterator aPos = pListeners->find(_nId);
3579 0 : if (aPos == pListeners->end())
3580 : {
3581 : OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
3582 0 : return;
3583 : }
3584 :
3585 0 : delete aPos->second;
3586 :
3587 0 : pListeners->erase(aPos);
3588 : }
3589 :
3590 0 : void DbGridControl::disposing(sal_uInt16 _nId, const EventObject& /*_rEvt*/)
3591 : {
3592 0 : if (_nId == 0)
3593 : { // the seek cursor is beeing disposed
3594 0 : ::osl::MutexGuard aGuard(m_aAdjustSafety);
3595 0 : setDataSource(NULL,0); // our clone was disposed so we set our datasource to null to avoid later acces to it
3596 0 : if (m_nAsynAdjustEvent)
3597 : {
3598 0 : RemoveUserEvent(m_nAsynAdjustEvent);
3599 0 : m_nAsynAdjustEvent = 0;
3600 0 : }
3601 : }
3602 0 : }
3603 :
3604 0 : sal_Int32 DbGridControl::GetAccessibleControlCount() const
3605 : {
3606 0 : return DbGridControl_Base::GetAccessibleControlCount() + 1; // the navigation control
3607 : }
3608 :
3609 0 : Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
3610 : {
3611 0 : Reference<XAccessible > xRet;
3612 0 : if ( _nIndex == DbGridControl_Base::GetAccessibleControlCount() )
3613 : {
3614 0 : xRet = m_aBar.GetAccessible();
3615 : }
3616 : else
3617 0 : xRet = DbGridControl_Base::CreateAccessibleControl( _nIndex );
3618 0 : return xRet;
3619 : }
3620 :
3621 0 : Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
3622 : {
3623 0 : sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
3624 0 : size_t Location = GetModelColumnPos(nColumnId);
3625 0 : DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ] : NULL;
3626 0 : if ( pColumn )
3627 : {
3628 0 : Reference< ::com::sun::star::awt::XControl> xInt(pColumn->GetCell());
3629 0 : Reference< ::com::sun::star::awt::XCheckBox> xBox(xInt,UNO_QUERY);
3630 0 : if ( xBox.is() )
3631 : {
3632 0 : TriState eValue = TRISTATE_FALSE;
3633 0 : switch( xBox->getState() )
3634 : {
3635 : case 0:
3636 0 : eValue = TRISTATE_FALSE;
3637 0 : break;
3638 : case 1:
3639 0 : eValue = TRISTATE_TRUE;
3640 0 : break;
3641 : case 2:
3642 0 : eValue = TRISTATE_INDET;
3643 0 : break;
3644 : }
3645 0 : return DbGridControl_Base::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
3646 0 : }
3647 : }
3648 0 : return DbGridControl_Base::CreateAccessibleCell( _nRow, _nColumnPos );
3649 : }
3650 :
3651 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|