/src/libreoffice/svx/source/fmcomp/gridctrl.cxx
Line | Count | Source (jump to first uncovered line) |
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/log.hxx> |
21 | | #include <helpids.h> |
22 | | #include <svx/gridctrl.hxx> |
23 | | #include <gridcell.hxx> |
24 | | #include <svx/fmtools.hxx> |
25 | | #include <svtools/stringtransfer.hxx> |
26 | | #include <connectivity/dbtools.hxx> |
27 | | #include <connectivity/dbconversion.hxx> |
28 | | |
29 | | #include <fmprop.hxx> |
30 | | #include <com/sun/star/sdbc/ResultSetConcurrency.hpp> |
31 | | #include <com/sun/star/accessibility/XAccessible.hpp> |
32 | | #include <com/sun/star/sdb/XResultSetAccess.hpp> |
33 | | #include <com/sun/star/sdb/RowChangeAction.hpp> |
34 | | #include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp> |
35 | | #include <com/sun/star/sdbc/SQLException.hpp> |
36 | | #include <com/sun/star/sdbc/XResultSetUpdate.hpp> |
37 | | #include <com/sun/star/sdbc/XRowSet.hpp> |
38 | | #include <com/sun/star/sdbcx/Privilege.hpp> |
39 | | #include <com/sun/star/util/NumberFormatter.hpp> |
40 | | #include <com/sun/star/util/XNumberFormatsSupplier.hpp> |
41 | | #include <com/sun/star/beans/XPropertySet.hpp> |
42 | | #include <com/sun/star/beans/PropertyChangeEvent.hpp> |
43 | | #include <com/sun/star/container/XIndexAccess.hpp> |
44 | | #include <comphelper/diagnose_ex.hxx> |
45 | | #include <tools/debug.hxx> |
46 | | #include <tools/fract.hxx> |
47 | | #include <vcl/settings.hxx> |
48 | | #include <vcl/commandevent.hxx> |
49 | | #include <vcl/svapp.hxx> |
50 | | #include <vcl/weld.hxx> |
51 | | #include <vcl/weldutils.hxx> |
52 | | |
53 | | #include <svx/strings.hrc> |
54 | | |
55 | | #include <svx/dialmgr.hxx> |
56 | | #include <sdbdatacolumn.hxx> |
57 | | |
58 | | #include <comphelper/property.hxx> |
59 | | #include <comphelper/types.hxx> |
60 | | #include <cppuhelper/implbase.hxx> |
61 | | |
62 | | #include <algorithm> |
63 | | #include <cstdlib> |
64 | | #include <memory> |
65 | | |
66 | | using namespace ::dbtools; |
67 | | using namespace ::dbtools::DBTypeConversion; |
68 | | using namespace ::svxform; |
69 | | using namespace ::svt; |
70 | | using namespace ::com::sun::star::beans; |
71 | | using namespace ::com::sun::star::lang; |
72 | | using namespace ::com::sun::star::uno; |
73 | | using namespace ::com::sun::star::sdbc; |
74 | | using namespace ::com::sun::star::sdbcx; |
75 | | using namespace ::com::sun::star::sdb; |
76 | | using namespace ::com::sun::star::datatransfer; |
77 | | using namespace ::com::sun::star::container; |
78 | | using namespace com::sun::star::accessibility; |
79 | | |
80 | | #define ROWSTATUS(row) (!row.is() ? "NULL" : row->GetStatus() == GridRowStatus::Clean ? "CLEAN" : row->GetStatus() == GridRowStatus::Modified ? "MODIFIED" : row->GetStatus() == GridRowStatus::Deleted ? "DELETED" : "INVALID") |
81 | | |
82 | | constexpr auto DEFAULT_BROWSE_MODE = |
83 | | BrowserMode::COLUMNSELECTION |
84 | | | BrowserMode::MULTISELECTION |
85 | | | BrowserMode::KEEPHIGHLIGHT |
86 | | | BrowserMode::TRACKING_TIPS |
87 | | | BrowserMode::HLINES |
88 | | | BrowserMode::VLINES |
89 | | | BrowserMode::HEADERBAR_NEW; |
90 | | |
91 | | class RowSetEventListener : public ::cppu::WeakImplHelper<XRowsChangeListener> |
92 | | { |
93 | | VclPtr<DbGridControl> m_pControl; |
94 | | public: |
95 | 0 | explicit RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl) |
96 | 0 | { |
97 | 0 | } |
98 | | |
99 | | private: |
100 | | // XEventListener |
101 | | virtual void SAL_CALL disposing(const css::lang::EventObject& /*i_aEvt*/) override |
102 | 0 | { |
103 | 0 | } |
104 | | virtual void SAL_CALL rowsChanged(const css::sdb::RowsChangeEvent& i_aEvt) override |
105 | 0 | { |
106 | 0 | if ( i_aEvt.Action != RowChangeAction::UPDATE ) |
107 | 0 | return; |
108 | | |
109 | 0 | ::DbGridControl::GrantControlAccess aAccess; |
110 | 0 | CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess); |
111 | 0 | const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess); |
112 | 0 | for(const Any& rBookmark : i_aEvt.Bookmarks) |
113 | 0 | { |
114 | 0 | pSeek->moveToBookmark(rBookmark); |
115 | | // get the data |
116 | 0 | rSeekRow->SetState(pSeek, true); |
117 | 0 | sal_Int32 nSeekPos = pSeek->getRow() - 1; |
118 | 0 | m_pControl->SetSeekPos(nSeekPos,aAccess); |
119 | 0 | m_pControl->RowModified(nSeekPos); |
120 | 0 | } |
121 | 0 | } |
122 | | }; |
123 | | |
124 | | class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener |
125 | | { |
126 | | DbGridControl& m_rParent; |
127 | | rtl::Reference<::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() override; |
135 | | |
136 | | virtual void _propertyChanged(const PropertyChangeEvent& evt) override; |
137 | | |
138 | 0 | void suspend() { ++m_nSuspended; } |
139 | 0 | void resume() { --m_nSuspended; } |
140 | | |
141 | | void dispose(); |
142 | | }; |
143 | | |
144 | | GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId) |
145 | 0 | :OPropertyChangeListener() |
146 | 0 | ,m_rParent(_rParent) |
147 | 0 | ,m_nId(_nId) |
148 | 0 | ,m_nSuspended(0) |
149 | 0 | ,m_bDisposed(false) |
150 | 0 | { |
151 | 0 | if (_rField.is()) |
152 | 0 | { |
153 | 0 | m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField); |
154 | 0 | m_pRealListener->addProperty(FM_PROP_VALUE); |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | | GridFieldValueListener::~GridFieldValueListener() |
159 | 0 | { |
160 | 0 | dispose(); |
161 | 0 | } |
162 | | |
163 | | void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& /*_evt*/) |
164 | 0 | { |
165 | 0 | DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !"); |
166 | 0 | if (m_nSuspended <= 0) |
167 | 0 | m_rParent.FieldValueChanged(m_nId); |
168 | 0 | } |
169 | | |
170 | | void GridFieldValueListener::dispose() |
171 | 0 | { |
172 | 0 | if (m_bDisposed) |
173 | 0 | { |
174 | 0 | DBG_ASSERT(!m_pRealListener, "GridFieldValueListener::dispose : inconsistent !"); |
175 | 0 | return; |
176 | 0 | } |
177 | | |
178 | 0 | if (m_pRealListener.is()) |
179 | 0 | { |
180 | 0 | m_pRealListener->dispose(); |
181 | 0 | m_pRealListener.clear(); |
182 | 0 | } |
183 | |
|
184 | 0 | m_bDisposed = true; |
185 | 0 | m_rParent.FieldListenerDisposing(m_nId); |
186 | 0 | } |
187 | | |
188 | | class DisposeListenerGridBridge : public FmXDisposeListener |
189 | | { |
190 | | DbGridControl& m_rParent; |
191 | | rtl::Reference<FmXDisposeMultiplexer> m_xRealListener; |
192 | | |
193 | | public: |
194 | | DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject); |
195 | | virtual ~DisposeListenerGridBridge() override; |
196 | | |
197 | 0 | virtual void disposing(sal_Int16 _nId) override { m_rParent.disposing(_nId); } |
198 | | }; |
199 | | |
200 | | DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject) |
201 | 0 | :FmXDisposeListener() |
202 | 0 | ,m_rParent(_rParent) |
203 | 0 | { |
204 | |
|
205 | 0 | if (_rxObject.is()) |
206 | 0 | { |
207 | 0 | m_xRealListener = new FmXDisposeMultiplexer(this, _rxObject); |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | DisposeListenerGridBridge::~DisposeListenerGridBridge() |
212 | 0 | { |
213 | 0 | if (m_xRealListener.is()) |
214 | 0 | { |
215 | 0 | m_xRealListener->dispose(); |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | | const DbGridControlNavigationBarState ControlMap[] = |
220 | | { |
221 | | DbGridControlNavigationBarState::Text, |
222 | | DbGridControlNavigationBarState::Absolute, |
223 | | DbGridControlNavigationBarState::Of, |
224 | | DbGridControlNavigationBarState::Count, |
225 | | DbGridControlNavigationBarState::First, |
226 | | DbGridControlNavigationBarState::Next, |
227 | | DbGridControlNavigationBarState::Prev, |
228 | | DbGridControlNavigationBarState::Last, |
229 | | DbGridControlNavigationBarState::New, |
230 | | DbGridControlNavigationBarState::NONE |
231 | | }; |
232 | | |
233 | | bool CompareBookmark(const Any& aLeft, const Any& aRight) |
234 | 0 | { |
235 | 0 | return aLeft == aRight; |
236 | 0 | } |
237 | | |
238 | | class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener |
239 | | { |
240 | | VclPtr<DbGridControl> m_pParent; |
241 | | |
242 | | sal_Int16 m_nSuspended; |
243 | | |
244 | | public: |
245 | | explicit FmXGridSourcePropListener(DbGridControl* _pParent); |
246 | | |
247 | 0 | void suspend() { ++m_nSuspended; } |
248 | 0 | void resume() { --m_nSuspended; } |
249 | | |
250 | | virtual void _propertyChanged(const PropertyChangeEvent& evt) override; |
251 | | }; |
252 | | |
253 | | FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent) |
254 | 0 | :OPropertyChangeListener() |
255 | 0 | ,m_pParent(_pParent) |
256 | 0 | ,m_nSuspended(0) |
257 | 0 | { |
258 | 0 | DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !"); |
259 | 0 | } |
260 | | |
261 | | void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) |
262 | 0 | { |
263 | 0 | DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !"); |
264 | 0 | if (m_nSuspended <= 0) |
265 | 0 | m_pParent->DataSourcePropertyChanged(evt); |
266 | 0 | } |
267 | | |
268 | | const int nReserveNumDigits = 7; |
269 | | |
270 | | NavigationBar::AbsolutePos::AbsolutePos(std::unique_ptr<weld::Entry> xEntry, NavigationBar* pBar) |
271 | 0 | : RecordItemWindowBase(std::move(xEntry)) |
272 | 0 | , m_xParent(pBar) |
273 | 0 | { |
274 | 0 | } |
275 | | |
276 | | bool NavigationBar::AbsolutePos::DoKeyInput(const KeyEvent& rEvt) |
277 | 0 | { |
278 | 0 | if (rEvt.GetKeyCode() == KEY_TAB) |
279 | 0 | { |
280 | 0 | m_xParent->GetParent()->GrabFocus(); |
281 | 0 | return true; |
282 | 0 | } |
283 | 0 | return RecordItemWindowBase::DoKeyInput(rEvt); |
284 | 0 | } |
285 | | |
286 | | void NavigationBar::AbsolutePos::PositionFired(sal_Int64 nRecord) |
287 | 0 | { |
288 | 0 | m_xParent->PositionDataSource(nRecord); |
289 | 0 | m_xParent->InvalidateState(DbGridControlNavigationBarState::Absolute); |
290 | 0 | } |
291 | | |
292 | | void NavigationBar::PositionDataSource(sal_Int32 nRecord) |
293 | 0 | { |
294 | 0 | if (m_bPositioning) |
295 | 0 | return; |
296 | | // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition, |
297 | | // so protect against this recursion |
298 | 0 | m_bPositioning = true; |
299 | 0 | static_cast<DbGridControl*>(GetParent())->MoveToPosition(nRecord - 1); |
300 | 0 | m_bPositioning = false; |
301 | 0 | } |
302 | | |
303 | | NavigationBar::NavigationBar(DbGridControl* pParent) |
304 | 0 | : InterimItemWindow(pParent, u"svx/ui/navigationbar.ui"_ustr, u"NavigationBar"_ustr) |
305 | 0 | , m_xRecordText(m_xBuilder->weld_label(u"recordtext"_ustr)) |
306 | 0 | , m_xAbsolute(new NavigationBar::AbsolutePos(m_xBuilder->weld_entry(u"entry-noframe"_ustr), this)) |
307 | 0 | , m_xRecordOf(m_xBuilder->weld_label(u"recordof"_ustr)) |
308 | 0 | , m_xRecordCount(m_xBuilder->weld_label(u"recordcount"_ustr)) |
309 | 0 | , m_xFirstBtn(m_xBuilder->weld_button(u"first"_ustr)) |
310 | 0 | , m_xPrevBtn(m_xBuilder->weld_button(u"prev"_ustr)) |
311 | 0 | , m_xNextBtn(m_xBuilder->weld_button(u"next"_ustr)) |
312 | 0 | , m_xLastBtn(m_xBuilder->weld_button(u"last"_ustr)) |
313 | 0 | , m_xNewBtn(m_xBuilder->weld_button(u"new"_ustr)) |
314 | 0 | , m_xPrevRepeater(std::make_shared<weld::ButtonPressRepeater>(*m_xPrevBtn, LINK(this,NavigationBar,OnClick))) |
315 | 0 | , m_xNextRepeater(std::make_shared<weld::ButtonPressRepeater>(*m_xNextBtn, LINK(this,NavigationBar,OnClick))) |
316 | 0 | , m_nCurrentPos(-1) |
317 | 0 | , m_bPositioning(false) |
318 | 0 | { |
319 | 0 | m_xFirstBtn->set_help_id(HID_GRID_TRAVEL_FIRST); |
320 | 0 | m_xPrevBtn->set_help_id(HID_GRID_TRAVEL_PREV); |
321 | 0 | m_xNextBtn->set_help_id(HID_GRID_TRAVEL_NEXT); |
322 | 0 | m_xLastBtn->set_help_id(HID_GRID_TRAVEL_LAST); |
323 | 0 | m_xNewBtn->set_help_id(HID_GRID_TRAVEL_NEW); |
324 | 0 | m_xAbsolute->set_help_id(HID_GRID_TRAVEL_ABSOLUTE); |
325 | 0 | m_xRecordCount->set_help_id(HID_GRID_NUMBEROFRECORDS); |
326 | | |
327 | | // set handlers for buttons |
328 | 0 | m_xFirstBtn->connect_clicked(LINK(this,NavigationBar,OnClick)); |
329 | 0 | m_xLastBtn->connect_clicked(LINK(this,NavigationBar,OnClick)); |
330 | 0 | m_xNewBtn->connect_clicked(LINK(this,NavigationBar,OnClick)); |
331 | |
|
332 | 0 | m_xRecordText->set_label(SvxResId(RID_STR_REC_TEXT)); |
333 | 0 | m_xRecordOf->set_label(SvxResId(RID_STR_REC_FROM_TEXT)); |
334 | 0 | m_xRecordCount->set_label(OUString('?')); |
335 | |
|
336 | 0 | vcl::Font aApplFont(Application::GetSettings().GetStyleSettings().GetToolFont()); |
337 | 0 | SetPointFontAndZoom(aApplFont, Fraction(1, 1)); |
338 | |
|
339 | 0 | m_xContainer->connect_size_allocate(LINK(this, NavigationBar, SizeAllocHdl)); |
340 | 0 | } Unexecuted instantiation: NavigationBar::NavigationBar(DbGridControl*) Unexecuted instantiation: NavigationBar::NavigationBar(DbGridControl*) |
341 | | |
342 | | IMPL_LINK(NavigationBar, SizeAllocHdl, const Size&, rSize, void) |
343 | 0 | { |
344 | 0 | if (rSize == m_aLastAllocSize) |
345 | 0 | return; |
346 | 0 | m_aLastAllocSize = rSize; |
347 | 0 | static_cast<DbGridControl*>(GetParent())->RearrangeAtIdle(); |
348 | 0 | } |
349 | | |
350 | | NavigationBar::~NavigationBar() |
351 | 0 | { |
352 | 0 | disposeOnce(); |
353 | 0 | } |
354 | | |
355 | | void NavigationBar::dispose() |
356 | 0 | { |
357 | 0 | m_xRecordText.reset(); |
358 | 0 | m_xAbsolute.reset(); |
359 | 0 | m_xRecordOf.reset(); |
360 | 0 | m_xRecordCount.reset(); |
361 | 0 | m_xFirstBtn.reset(); |
362 | 0 | m_xPrevBtn.reset(); |
363 | 0 | m_xNextBtn.reset(); |
364 | 0 | m_xLastBtn.reset(); |
365 | 0 | m_xNewBtn.reset(); |
366 | 0 | InterimItemWindow::dispose(); |
367 | 0 | } |
368 | | |
369 | | sal_uInt16 NavigationBar::GetPreferredWidth() const |
370 | 0 | { |
371 | 0 | return m_xContainer->get_preferred_size().Width(); |
372 | 0 | } |
373 | | |
374 | | IMPL_LINK(NavigationBar, OnClick, weld::Button&, rButton, void) |
375 | 0 | { |
376 | 0 | DbGridControl* pParent = static_cast<DbGridControl*>(GetParent()); |
377 | |
|
378 | 0 | if (pParent->m_aMasterSlotExecutor.IsSet()) |
379 | 0 | { |
380 | 0 | bool lResult = false; |
381 | 0 | if (&rButton == m_xFirstBtn.get()) |
382 | 0 | lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::First); |
383 | 0 | else if( &rButton == m_xPrevBtn.get() ) |
384 | 0 | lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Prev); |
385 | 0 | else if( &rButton == m_xNextBtn.get() ) |
386 | 0 | lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Next); |
387 | 0 | else if( &rButton == m_xLastBtn.get() ) |
388 | 0 | lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Last); |
389 | 0 | else if( &rButton == m_xNewBtn.get() ) |
390 | 0 | lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::New); |
391 | |
|
392 | 0 | if (lResult) |
393 | | // the link already handled it |
394 | 0 | return; |
395 | 0 | } |
396 | | |
397 | 0 | if (&rButton == m_xFirstBtn.get()) |
398 | 0 | pParent->MoveToFirst(); |
399 | 0 | else if( &rButton == m_xPrevBtn.get() ) |
400 | 0 | pParent->MoveToPrev(); |
401 | 0 | else if( &rButton == m_xNextBtn.get() ) |
402 | 0 | pParent->MoveToNext(); |
403 | 0 | else if( &rButton == m_xLastBtn.get() ) |
404 | 0 | pParent->MoveToLast(); |
405 | 0 | else if( &rButton == m_xNewBtn.get() ) |
406 | 0 | pParent->AppendNew(); |
407 | 0 | } |
408 | | |
409 | | void NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll) |
410 | 0 | { |
411 | 0 | if (!(m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)) |
412 | 0 | return; |
413 | | |
414 | 0 | DbGridControl* pParent = static_cast<DbGridControl*>(GetParent()); |
415 | |
|
416 | 0 | sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControlOptions::Insert) ? 2 : 1); |
417 | | |
418 | | // check if everything needs to be invalidated |
419 | 0 | bAll = bAll || m_nCurrentPos <= 0; |
420 | 0 | bAll = bAll || nCurrentPos <= 0; |
421 | 0 | bAll = bAll || m_nCurrentPos >= nAdjustedRowCount; |
422 | 0 | bAll = bAll || nCurrentPos >= nAdjustedRowCount; |
423 | |
|
424 | 0 | if ( bAll ) |
425 | 0 | { |
426 | 0 | m_nCurrentPos = nCurrentPos; |
427 | 0 | int i = 0; |
428 | 0 | while (ControlMap[i] != DbGridControlNavigationBarState::NONE) |
429 | 0 | SetState(ControlMap[i++]); |
430 | 0 | } |
431 | 0 | else // is in the center |
432 | 0 | { |
433 | 0 | m_nCurrentPos = nCurrentPos; |
434 | 0 | SetState(DbGridControlNavigationBarState::Count); |
435 | 0 | SetState(DbGridControlNavigationBarState::Absolute); |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | | bool NavigationBar::GetState(DbGridControlNavigationBarState nWhich) const |
440 | 0 | { |
441 | 0 | DbGridControl* pParent = static_cast<DbGridControl*>(GetParent()); |
442 | |
|
443 | 0 | if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled() |
444 | 0 | || pParent->IsFilterMode() ) |
445 | 0 | return false; |
446 | 0 | else |
447 | 0 | { |
448 | | // check if we have a master state provider |
449 | 0 | if (pParent->m_aMasterStateProvider.IsSet()) |
450 | 0 | { |
451 | 0 | tools::Long nState = pParent->m_aMasterStateProvider.Call( nWhich ); |
452 | 0 | if (nState>=0) |
453 | 0 | return (nState>0); |
454 | 0 | } |
455 | | |
456 | 0 | bool bAvailable = true; |
457 | |
|
458 | 0 | switch (nWhich) |
459 | 0 | { |
460 | 0 | case DbGridControlNavigationBarState::First: |
461 | 0 | case DbGridControlNavigationBarState::Prev: |
462 | 0 | bAvailable = m_nCurrentPos > 0; |
463 | 0 | break; |
464 | 0 | case DbGridControlNavigationBarState::Next: |
465 | 0 | if(pParent->m_bRecordCountFinal) |
466 | 0 | { |
467 | 0 | bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1; |
468 | 0 | if (!bAvailable && pParent->GetOptions() & DbGridControlOptions::Insert) |
469 | 0 | bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified(); |
470 | 0 | } |
471 | 0 | break; |
472 | 0 | case DbGridControlNavigationBarState::Last: |
473 | 0 | if(pParent->m_bRecordCountFinal) |
474 | 0 | { |
475 | 0 | if (pParent->GetOptions() & DbGridControlOptions::Insert) |
476 | 0 | bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 : |
477 | 0 | m_nCurrentPos != pParent->GetRowCount() - 2; |
478 | 0 | else |
479 | 0 | bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1; |
480 | 0 | } |
481 | 0 | break; |
482 | 0 | case DbGridControlNavigationBarState::New: |
483 | 0 | bAvailable = (pParent->GetOptions() & DbGridControlOptions::Insert) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1; |
484 | 0 | break; |
485 | 0 | case DbGridControlNavigationBarState::Absolute: |
486 | 0 | bAvailable = pParent->GetRowCount() > 0; |
487 | 0 | break; |
488 | 0 | default: break; |
489 | 0 | } |
490 | 0 | return bAvailable; |
491 | 0 | } |
492 | 0 | } |
493 | | |
494 | | void NavigationBar::SetState(DbGridControlNavigationBarState nWhich) |
495 | 0 | { |
496 | 0 | bool bAvailable = GetState(nWhich); |
497 | 0 | DbGridControl* pParent = static_cast<DbGridControl*>(GetParent()); |
498 | 0 | weld::Widget* pWnd = nullptr; |
499 | 0 | switch (nWhich) |
500 | 0 | { |
501 | 0 | case DbGridControlNavigationBarState::First: |
502 | 0 | pWnd = m_xFirstBtn.get(); |
503 | 0 | break; |
504 | 0 | case DbGridControlNavigationBarState::Prev: |
505 | 0 | pWnd = m_xPrevBtn.get(); |
506 | 0 | break; |
507 | 0 | case DbGridControlNavigationBarState::Next: |
508 | 0 | pWnd = m_xNextBtn.get(); |
509 | 0 | break; |
510 | 0 | case DbGridControlNavigationBarState::Last: |
511 | 0 | pWnd = m_xLastBtn.get(); |
512 | 0 | break; |
513 | 0 | case DbGridControlNavigationBarState::New: |
514 | 0 | pWnd = m_xNewBtn.get(); |
515 | 0 | break; |
516 | 0 | case DbGridControlNavigationBarState::Absolute: |
517 | 0 | pWnd = m_xAbsolute->GetWidget(); |
518 | 0 | if (bAvailable) |
519 | 0 | m_xAbsolute->set_text(OUString::number(m_nCurrentPos + 1)); |
520 | 0 | else |
521 | 0 | m_xAbsolute->set_text(OUString()); |
522 | 0 | break; |
523 | 0 | case DbGridControlNavigationBarState::Text: |
524 | 0 | pWnd = m_xRecordText.get(); |
525 | 0 | break; |
526 | 0 | case DbGridControlNavigationBarState::Of: |
527 | 0 | pWnd = m_xRecordOf.get(); |
528 | 0 | break; |
529 | 0 | case DbGridControlNavigationBarState::Count: |
530 | 0 | { |
531 | 0 | pWnd = m_xRecordCount.get(); |
532 | 0 | OUString aText; |
533 | 0 | if (bAvailable) |
534 | 0 | { |
535 | 0 | if (pParent->GetOptions() & DbGridControlOptions::Insert) |
536 | 0 | { |
537 | 0 | if (pParent->IsCurrentAppending() && !pParent->IsModified()) |
538 | 0 | aText = OUString::number(pParent->GetRowCount()); |
539 | 0 | else |
540 | 0 | aText = OUString::number(pParent->GetRowCount() - 1); |
541 | 0 | } |
542 | 0 | else |
543 | 0 | aText = OUString::number(pParent->GetRowCount()); |
544 | 0 | if(!pParent->m_bRecordCountFinal) |
545 | 0 | aText += " *"; |
546 | 0 | } |
547 | 0 | else |
548 | 0 | aText.clear(); |
549 | | |
550 | | // add the number of selected rows, if applicable |
551 | 0 | if (pParent->GetSelectRowCount()) |
552 | 0 | { |
553 | 0 | OUString aExtendedInfo = aText + " (" + |
554 | 0 | OUString::number(pParent->GetSelectRowCount()) + ")"; |
555 | 0 | m_xRecordCount->set_label(aExtendedInfo); |
556 | 0 | } |
557 | 0 | else |
558 | 0 | m_xRecordCount->set_label(aText); |
559 | |
|
560 | 0 | pParent->SetRealRowCount(aText); |
561 | 0 | } break; |
562 | 0 | default: break; |
563 | 0 | } |
564 | 0 | DBG_ASSERT(pWnd, "no window"); |
565 | 0 | if (!(pWnd && (pWnd->get_sensitive() != bAvailable))) |
566 | 0 | return; |
567 | | |
568 | | // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user |
569 | | // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we |
570 | | // do this check. |
571 | | // For further explanation see Bug 69900. |
572 | 0 | pWnd->set_sensitive(bAvailable); |
573 | 0 | if (!bAvailable) |
574 | 0 | { |
575 | 0 | if (pWnd == m_xNextBtn.get()) |
576 | 0 | m_xNextRepeater->Stop(); |
577 | 0 | else if (pWnd == m_xPrevBtn.get()) |
578 | 0 | m_xPrevRepeater->Stop(); |
579 | 0 | } |
580 | 0 | } |
581 | | |
582 | | static void ScaleButton(weld::Button& rBtn, const Fraction& rZoom) |
583 | 0 | { |
584 | 0 | rBtn.set_size_request(-1, -1); |
585 | 0 | Size aPrefSize = rBtn.get_preferred_size(); |
586 | 0 | aPrefSize.setWidth(std::round(double(aPrefSize.Width() * rZoom))); |
587 | 0 | aPrefSize.setHeight(std::round(double(aPrefSize.Height() * rZoom))); |
588 | 0 | rBtn.set_size_request(aPrefSize.Width(), aPrefSize.Height()); |
589 | 0 | } |
590 | | |
591 | | void NavigationBar::SetPointFontAndZoom(const vcl::Font& rFont, const Fraction& rZoom) |
592 | 0 | { |
593 | 0 | vcl::Font aFont(rFont); |
594 | 0 | if (rZoom.GetNumerator() != rZoom.GetDenominator()) |
595 | 0 | { |
596 | 0 | Size aSize = aFont.GetFontSize(); |
597 | 0 | aSize.setWidth(std::round(double(aSize.Width() * rZoom))); |
598 | 0 | aSize.setHeight(std::round(double(aSize.Height() * rZoom))); |
599 | 0 | aFont.SetFontSize(aSize); |
600 | 0 | } |
601 | |
|
602 | 0 | m_xRecordText->set_font(aFont); |
603 | 0 | m_xAbsolute->GetWidget()->set_font(aFont); |
604 | 0 | m_xRecordOf->set_font(aFont); |
605 | 0 | m_xRecordCount->set_font(aFont); |
606 | |
|
607 | 0 | auto nReserveWidth = m_xRecordCount->get_approximate_digit_width() * nReserveNumDigits; |
608 | 0 | m_xAbsolute->GetWidget()->set_size_request(nReserveWidth, -1); |
609 | 0 | m_xRecordCount->set_size_request(nReserveWidth, -1); |
610 | |
|
611 | 0 | ScaleButton(*m_xFirstBtn, rZoom); |
612 | 0 | ScaleButton(*m_xPrevBtn, rZoom); |
613 | 0 | ScaleButton(*m_xNextBtn, rZoom); |
614 | 0 | ScaleButton(*m_xLastBtn, rZoom); |
615 | 0 | ScaleButton(*m_xNewBtn, rZoom); |
616 | |
|
617 | 0 | SetZoom(rZoom); |
618 | |
|
619 | 0 | InvalidateChildSizeCache(); |
620 | 0 | } |
621 | | |
622 | 0 | DbGridRow::DbGridRow():m_eStatus(GridRowStatus::Clean), m_bIsNew(true) |
623 | 0 | {} |
624 | | |
625 | | DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor) |
626 | 0 | :m_bIsNew(false) |
627 | 0 | { |
628 | |
|
629 | 0 | if (pCur && pCur->Is()) |
630 | 0 | { |
631 | 0 | Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY); |
632 | 0 | for (sal_Int32 i = 0; i < xColumns->getCount(); ++i) |
633 | 0 | { |
634 | 0 | Reference< XPropertySet > xColSet( |
635 | 0 | xColumns->getByIndex(i), css::uno::UNO_QUERY); |
636 | 0 | m_aVariants.emplace_back( new DataColumn(xColSet) ); |
637 | 0 | } |
638 | |
|
639 | 0 | if (pCur->rowDeleted()) |
640 | 0 | m_eStatus = GridRowStatus::Deleted; |
641 | 0 | else |
642 | 0 | { |
643 | 0 | if (bPaintCursor) |
644 | 0 | m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GridRowStatus::Invalid : GridRowStatus::Clean; |
645 | 0 | else |
646 | 0 | { |
647 | 0 | const Reference< XPropertySet >& xSet = pCur->getPropertySet(); |
648 | 0 | if (xSet.is()) |
649 | 0 | { |
650 | 0 | m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)); |
651 | 0 | if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst())) |
652 | 0 | m_eStatus = GridRowStatus::Invalid; |
653 | 0 | else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED))) |
654 | 0 | m_eStatus = GridRowStatus::Modified; |
655 | 0 | else |
656 | 0 | m_eStatus = GridRowStatus::Clean; |
657 | 0 | } |
658 | 0 | else |
659 | 0 | m_eStatus = GridRowStatus::Invalid; |
660 | 0 | } |
661 | 0 | } |
662 | 0 | if (!m_bIsNew && IsValid()) |
663 | 0 | m_aBookmark = pCur->getBookmark(); |
664 | 0 | else |
665 | 0 | m_aBookmark = Any(); |
666 | 0 | } |
667 | 0 | else |
668 | 0 | m_eStatus = GridRowStatus::Invalid; |
669 | 0 | } |
670 | | |
671 | | DbGridRow::~DbGridRow() |
672 | 0 | { |
673 | 0 | } |
674 | | |
675 | | void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor) |
676 | 0 | { |
677 | 0 | if (pCur && pCur->Is()) |
678 | 0 | { |
679 | 0 | if (pCur->rowDeleted()) |
680 | 0 | { |
681 | 0 | m_eStatus = GridRowStatus::Deleted; |
682 | 0 | m_bIsNew = false; |
683 | 0 | } |
684 | 0 | else |
685 | 0 | { |
686 | 0 | m_eStatus = GridRowStatus::Clean; |
687 | 0 | if (!bPaintCursor) |
688 | 0 | { |
689 | 0 | const Reference< XPropertySet >& xSet = pCur->getPropertySet(); |
690 | 0 | DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !"); |
691 | |
|
692 | 0 | if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED))) |
693 | 0 | m_eStatus = GridRowStatus::Modified; |
694 | 0 | m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)); |
695 | 0 | } |
696 | 0 | else |
697 | 0 | m_bIsNew = false; |
698 | 0 | } |
699 | |
|
700 | 0 | try |
701 | 0 | { |
702 | 0 | if (!m_bIsNew && IsValid()) |
703 | 0 | m_aBookmark = pCur->getBookmark(); |
704 | 0 | else |
705 | 0 | m_aBookmark = Any(); |
706 | 0 | } |
707 | 0 | catch(SQLException&) |
708 | 0 | { |
709 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
710 | 0 | m_aBookmark = Any(); |
711 | 0 | m_eStatus = GridRowStatus::Invalid; |
712 | 0 | m_bIsNew = false; |
713 | 0 | } |
714 | 0 | } |
715 | 0 | else |
716 | 0 | { |
717 | 0 | m_aBookmark = Any(); |
718 | 0 | m_eStatus = GridRowStatus::Invalid; |
719 | 0 | m_bIsNew = false; |
720 | 0 | } |
721 | 0 | } |
722 | | |
723 | | DbGridControl::DbGridControl( |
724 | | Reference< XComponentContext > const & _rxContext, |
725 | | vcl::Window* pParent, |
726 | | WinBits nBits) |
727 | 0 | :EditBrowseBox(pParent, EditBrowseBoxFlags::NONE, nBits, DEFAULT_BROWSE_MODE ) |
728 | 0 | ,m_xContext(_rxContext) |
729 | 0 | ,m_aBar(VclPtr<NavigationBar>::Create(this)) |
730 | 0 | ,m_nAsynAdjustEvent(nullptr) |
731 | 0 | ,m_pDataSourcePropListener(nullptr) |
732 | 0 | ,m_pGridListener(nullptr) |
733 | 0 | ,m_nSeekPos(-1) |
734 | 0 | ,m_nTotalCount(-1) |
735 | 0 | ,m_aRearrangeIdle("DbGridControl Rearrange Idle") |
736 | 0 | ,m_aNullDate(::dbtools::DBTypeConversion::getStandardDate()) |
737 | 0 | ,m_nMode(DEFAULT_BROWSE_MODE) |
738 | 0 | ,m_nCurrentPos(-1) |
739 | 0 | ,m_nDeleteEvent(nullptr) |
740 | 0 | ,m_nOptions(DbGridControlOptions::Readonly) |
741 | 0 | ,m_nOptionMask(DbGridControlOptions::Insert | DbGridControlOptions::Update | DbGridControlOptions::Delete) |
742 | 0 | ,m_nLastColId(sal_uInt16(-1)) |
743 | 0 | ,m_nLastRowId(-1) |
744 | 0 | ,m_bDesignMode(false) |
745 | 0 | ,m_bRecordCountFinal(false) |
746 | 0 | ,m_bSynchDisplay(true) |
747 | 0 | ,m_bHandle(true) |
748 | 0 | ,m_bFilterMode(false) |
749 | 0 | ,m_bWantDestruction(false) |
750 | 0 | ,m_bPendingAdjustRows(false) |
751 | 0 | ,m_bHideScrollbars( false ) |
752 | 0 | ,m_bUpdating(false) |
753 | 0 | { |
754 | 0 | m_bNavigationBar = true; |
755 | |
|
756 | 0 | OUString sName(SvxResId(RID_STR_NAVIGATIONBAR)); |
757 | 0 | m_aBar->SetAccessibleName(sName); |
758 | 0 | m_aBar->Show(); |
759 | 0 | ImplInitWindow( InitWindowFacet::All ); |
760 | |
|
761 | 0 | m_aRearrangeIdle.SetInvokeHandler(LINK(this, DbGridControl, RearrangeHdl)); |
762 | 0 | } |
763 | | |
764 | | void DbGridControl::InsertHandleColumn() |
765 | 0 | { |
766 | | // BrowseBox has problems when painting without a handleColumn (hide it here) |
767 | 0 | if (HasHandle()) |
768 | 0 | BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString())); |
769 | 0 | else |
770 | 0 | BrowseBox::InsertHandleColumn(0); |
771 | 0 | } |
772 | | |
773 | | void DbGridControl::Init() |
774 | 0 | { |
775 | 0 | VclPtr<BrowserHeader> pNewHeader = CreateHeaderBar(this); |
776 | 0 | pHeader->SetMouseTransparent(false); |
777 | |
|
778 | 0 | SetHeaderBar(pNewHeader); |
779 | 0 | SetMode(m_nMode); |
780 | 0 | SetCursorColor(Color(0xFF, 0, 0)); |
781 | |
|
782 | 0 | InsertHandleColumn(); |
783 | 0 | } |
784 | | |
785 | | DbGridControl::~DbGridControl() |
786 | 0 | { |
787 | 0 | disposeOnce(); |
788 | 0 | } |
789 | | |
790 | | void DbGridControl::dispose() |
791 | 0 | { |
792 | 0 | RemoveColumns(); |
793 | |
|
794 | 0 | m_bWantDestruction = true; |
795 | 0 | osl::MutexGuard aGuard(m_aDestructionSafety); |
796 | 0 | if (!m_aFieldListeners.empty()) |
797 | 0 | DisconnectFromFields(); |
798 | 0 | m_pCursorDisposeListener.reset(); |
799 | |
|
800 | 0 | if (m_nDeleteEvent) |
801 | 0 | Application::RemoveUserEvent(m_nDeleteEvent); |
802 | |
|
803 | 0 | if (m_pDataSourcePropMultiplexer.is()) |
804 | 0 | { |
805 | 0 | m_pDataSourcePropMultiplexer->dispose(); |
806 | 0 | m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer |
807 | 0 | delete m_pDataSourcePropListener; |
808 | 0 | m_pDataSourcePropListener = nullptr; |
809 | 0 | } |
810 | 0 | m_xRowSetListener.clear(); |
811 | |
|
812 | 0 | m_pDataCursor.reset(); |
813 | 0 | m_pSeekCursor.reset(); |
814 | |
|
815 | 0 | m_aBar.disposeAndClear(); |
816 | |
|
817 | 0 | m_aRearrangeIdle.Stop(); |
818 | |
|
819 | 0 | EditBrowseBox::dispose(); |
820 | 0 | } |
821 | | |
822 | | void DbGridControl::RearrangeAtIdle() |
823 | 0 | { |
824 | 0 | if (isDisposed()) |
825 | 0 | return; |
826 | 0 | m_aRearrangeIdle.Start(); |
827 | 0 | } |
828 | | |
829 | | void DbGridControl::StateChanged( StateChangedType nType ) |
830 | 0 | { |
831 | 0 | EditBrowseBox::StateChanged( nType ); |
832 | |
|
833 | 0 | switch (nType) |
834 | 0 | { |
835 | 0 | case StateChangedType::Mirroring: |
836 | 0 | ImplInitWindow( InitWindowFacet::WritingMode ); |
837 | 0 | Invalidate(); |
838 | 0 | break; |
839 | | |
840 | 0 | case StateChangedType::Zoom: |
841 | 0 | { |
842 | 0 | ImplInitWindow( InitWindowFacet::Font ); |
843 | 0 | RearrangeAtIdle(); |
844 | 0 | } |
845 | 0 | break; |
846 | 0 | case StateChangedType::ControlFont: |
847 | 0 | ImplInitWindow( InitWindowFacet::Font ); |
848 | 0 | Invalidate(); |
849 | 0 | break; |
850 | 0 | case StateChangedType::ControlForeground: |
851 | 0 | ImplInitWindow( InitWindowFacet::Foreground ); |
852 | 0 | Invalidate(); |
853 | 0 | break; |
854 | 0 | case StateChangedType::ControlBackground: |
855 | 0 | ImplInitWindow( InitWindowFacet::Background ); |
856 | 0 | Invalidate(); |
857 | 0 | break; |
858 | 0 | default:; |
859 | 0 | } |
860 | 0 | } |
861 | | |
862 | | void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt ) |
863 | 0 | { |
864 | 0 | EditBrowseBox::DataChanged( rDCEvt ); |
865 | 0 | if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS ) && |
866 | 0 | (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) |
867 | 0 | { |
868 | 0 | ImplInitWindow( InitWindowFacet::All ); |
869 | 0 | Invalidate(); |
870 | 0 | } |
871 | 0 | } |
872 | | |
873 | | void DbGridControl::Select() |
874 | 0 | { |
875 | 0 | EditBrowseBox::Select(); |
876 | | |
877 | | // as the selected rows may have changed, update the according display in our navigation bar |
878 | 0 | m_aBar->InvalidateState(DbGridControlNavigationBarState::Count); |
879 | |
|
880 | 0 | if (m_pGridListener) |
881 | 0 | m_pGridListener->selectionChanged(); |
882 | 0 | } |
883 | | |
884 | | void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat ) |
885 | 0 | { |
886 | 0 | for (auto const & pCol : m_aColumns) |
887 | 0 | { |
888 | 0 | pCol->ImplInitWindow( GetDataWindow(), _eInitWhat ); |
889 | 0 | } |
890 | |
|
891 | 0 | if ( _eInitWhat & InitWindowFacet::WritingMode ) |
892 | 0 | { |
893 | 0 | if ( m_bNavigationBar ) |
894 | 0 | { |
895 | 0 | m_aBar->EnableRTL( IsRTLEnabled() ); |
896 | 0 | } |
897 | 0 | } |
898 | |
|
899 | 0 | if ( _eInitWhat & InitWindowFacet::Font ) |
900 | 0 | { |
901 | 0 | if ( m_bNavigationBar ) |
902 | 0 | { |
903 | 0 | const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); |
904 | 0 | vcl::Font aFont = rStyleSettings.GetToolFont(); |
905 | 0 | if (IsControlFont()) |
906 | 0 | aFont.Merge(GetControlFont()); |
907 | |
|
908 | 0 | m_aBar->SetPointFontAndZoom(aFont, GetZoom()); |
909 | 0 | } |
910 | 0 | } |
911 | |
|
912 | 0 | if ( !(_eInitWhat & InitWindowFacet::Background) ) |
913 | 0 | return; |
914 | | |
915 | 0 | if (IsControlBackground()) |
916 | 0 | { |
917 | 0 | GetDataWindow().SetBackground(GetControlBackground()); |
918 | 0 | GetDataWindow().SetControlBackground(GetControlBackground()); |
919 | 0 | GetDataWindow().GetOutDev()->SetFillColor(GetControlBackground()); |
920 | 0 | } |
921 | 0 | else |
922 | 0 | { |
923 | 0 | GetDataWindow().SetControlBackground(); |
924 | 0 | GetDataWindow().GetOutDev()->SetFillColor(GetOutDev()->GetFillColor()); |
925 | 0 | } |
926 | 0 | } |
927 | | |
928 | | void DbGridControl::RemoveRows(bool bNewCursor) |
929 | 0 | { |
930 | | // Did the data cursor change? |
931 | 0 | if (!bNewCursor) |
932 | 0 | { |
933 | 0 | m_pSeekCursor.reset(); |
934 | 0 | m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr; |
935 | 0 | m_nCurrentPos = m_nSeekPos = -1; |
936 | 0 | m_nOptions = DbGridControlOptions::Readonly; |
937 | |
|
938 | 0 | RowRemoved(0, GetRowCount(), false); |
939 | 0 | m_nTotalCount = -1; |
940 | 0 | } |
941 | 0 | else |
942 | 0 | { |
943 | 0 | RemoveRows(); |
944 | 0 | } |
945 | 0 | } |
946 | | |
947 | | void DbGridControl::RemoveRows() |
948 | 0 | { |
949 | | // we're going to remove all columns and all row, so deactivate the current cell |
950 | 0 | if (IsEditing()) |
951 | 0 | DeactivateCell(); |
952 | | |
953 | | // de-initialize all columns |
954 | | // if there are columns, free all controllers |
955 | 0 | for (auto const & pColumn : m_aColumns) |
956 | 0 | pColumn->Clear(); |
957 | |
|
958 | 0 | m_pSeekCursor.reset(); |
959 | 0 | m_pDataCursor.reset(); |
960 | |
|
961 | 0 | m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr; |
962 | 0 | m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1; |
963 | 0 | m_nOptions = DbGridControlOptions::Readonly; |
964 | | |
965 | | // reset number of sentences to zero in the browser |
966 | 0 | EditBrowseBox::RemoveRows(); |
967 | 0 | m_aBar->InvalidateAll(m_nCurrentPos, true); |
968 | 0 | } |
969 | | |
970 | | void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY) |
971 | 0 | { |
972 | | // positioning of the controls |
973 | 0 | if (m_bNavigationBar) |
974 | 0 | { |
975 | 0 | tools::Rectangle aRect(GetControlArea()); |
976 | 0 | nX = m_aBar->GetPreferredWidth(); |
977 | 0 | m_aBar->SetPosSizePixel(Point(0, nY + 1), Size(nX, aRect.GetSize().Height() - 1)); |
978 | 0 | } |
979 | 0 | } |
980 | | |
981 | | void DbGridControl::EnableHandle(bool bEnable) |
982 | 0 | { |
983 | 0 | if (m_bHandle == bEnable) |
984 | 0 | return; |
985 | | |
986 | | // HandleColumn is only hidden because there are a lot of problems while painting otherwise |
987 | 0 | RemoveColumn( HandleColumnId ); |
988 | 0 | m_bHandle = bEnable; |
989 | 0 | InsertHandleColumn(); |
990 | 0 | } |
991 | | |
992 | | namespace |
993 | | { |
994 | | bool adjustModeForScrollbars( BrowserMode& _rMode, bool _bNavigationBar, bool _bHideScrollbars ) |
995 | 0 | { |
996 | 0 | BrowserMode nOldMode = _rMode; |
997 | |
|
998 | 0 | if ( !_bNavigationBar ) |
999 | 0 | { |
1000 | 0 | _rMode &= ~BrowserMode::AUTO_HSCROLL; |
1001 | 0 | } |
1002 | |
|
1003 | 0 | if ( _bHideScrollbars ) |
1004 | 0 | { |
1005 | 0 | _rMode |= BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL; |
1006 | 0 | _rMode &= ~BrowserMode( BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL ); |
1007 | 0 | } |
1008 | 0 | else |
1009 | 0 | { |
1010 | 0 | _rMode |= BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL; |
1011 | 0 | _rMode &= ~BrowserMode( BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL ); |
1012 | 0 | } |
1013 | | |
1014 | | // note: if we have a navigation bar, we always have an AUTO_HSCROLL. In particular, |
1015 | | // _bHideScrollbars is ignored then |
1016 | 0 | if ( _bNavigationBar ) |
1017 | 0 | { |
1018 | 0 | _rMode |= BrowserMode::AUTO_HSCROLL; |
1019 | 0 | _rMode &= ~BrowserMode::NO_HSCROLL; |
1020 | 0 | } |
1021 | |
|
1022 | 0 | return nOldMode != _rMode; |
1023 | 0 | } |
1024 | | } |
1025 | | |
1026 | | void DbGridControl::EnableNavigationBar(bool bEnable) |
1027 | 0 | { |
1028 | 0 | if (m_bNavigationBar == bEnable) |
1029 | 0 | return; |
1030 | | |
1031 | 0 | m_bNavigationBar = bEnable; |
1032 | |
|
1033 | 0 | if (bEnable) |
1034 | 0 | { |
1035 | 0 | m_aBar->Show(); |
1036 | 0 | m_aBar->Enable(); |
1037 | 0 | m_aBar->InvalidateAll(m_nCurrentPos, true); |
1038 | |
|
1039 | 0 | if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) |
1040 | 0 | SetMode( m_nMode ); |
1041 | | |
1042 | | // get size of the reserved ControlArea |
1043 | 0 | Point aPoint = GetControlArea().TopLeft(); |
1044 | 0 | sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X()); |
1045 | |
|
1046 | 0 | ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y())); |
1047 | 0 | ReserveControlArea(nX); |
1048 | 0 | } |
1049 | 0 | else |
1050 | 0 | { |
1051 | 0 | m_aBar->Hide(); |
1052 | 0 | m_aBar->Disable(); |
1053 | |
|
1054 | 0 | if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) |
1055 | 0 | SetMode( m_nMode ); |
1056 | |
|
1057 | 0 | ReserveControlArea(); |
1058 | 0 | } |
1059 | 0 | } |
1060 | | |
1061 | | DbGridControlOptions DbGridControl::SetOptions(DbGridControlOptions nOpt) |
1062 | 0 | { |
1063 | 0 | DBG_ASSERT(!m_xCurrentRow.is() || !m_xCurrentRow->IsModified(), |
1064 | 0 | "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !"); |
1065 | | |
1066 | | // for the next setDataSource (which is triggered by a refresh, for instance) |
1067 | 0 | m_nOptionMask = nOpt; |
1068 | | |
1069 | | // normalize the new options |
1070 | 0 | Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet(); |
1071 | 0 | if (xDataSourceSet.is()) |
1072 | 0 | { |
1073 | | // check what kind of options are available |
1074 | 0 | sal_Int32 nPrivileges = 0; |
1075 | 0 | xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges; |
1076 | 0 | if ((nPrivileges & Privilege::INSERT) == 0) |
1077 | 0 | nOpt &= ~DbGridControlOptions::Insert; |
1078 | 0 | if ((nPrivileges & Privilege::UPDATE) == 0) |
1079 | 0 | nOpt &= ~DbGridControlOptions::Update; |
1080 | 0 | if ((nPrivileges & Privilege::DELETE) == 0) |
1081 | 0 | nOpt &= ~DbGridControlOptions::Delete; |
1082 | 0 | } |
1083 | 0 | else |
1084 | 0 | nOpt = DbGridControlOptions::Readonly; |
1085 | | |
1086 | | // need to do something after that ? |
1087 | 0 | if (nOpt == m_nOptions) |
1088 | 0 | return m_nOptions; |
1089 | | |
1090 | | // the 'update' option only affects our BrowserMode (with or w/o focus rect) |
1091 | 0 | BrowserMode nNewMode = m_nMode; |
1092 | 0 | if (!(m_nMode & BrowserMode::CURSOR_WO_FOCUS)) |
1093 | 0 | { |
1094 | 0 | if (nOpt & DbGridControlOptions::Update) |
1095 | 0 | nNewMode |= BrowserMode::HIDECURSOR; |
1096 | 0 | else |
1097 | 0 | nNewMode &= ~BrowserMode::HIDECURSOR; |
1098 | 0 | } |
1099 | 0 | else |
1100 | 0 | nNewMode &= ~BrowserMode::HIDECURSOR; |
1101 | | // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ... |
1102 | |
|
1103 | 0 | if (nNewMode != m_nMode) |
1104 | 0 | { |
1105 | 0 | SetMode(nNewMode); |
1106 | 0 | m_nMode = nNewMode; |
1107 | 0 | } |
1108 | | |
1109 | | // _after_ setting the mode because this results in an ActivateCell |
1110 | 0 | DeactivateCell(); |
1111 | |
|
1112 | 0 | bool bInsertChanged = (nOpt & DbGridControlOptions::Insert) != (m_nOptions & DbGridControlOptions::Insert); |
1113 | 0 | m_nOptions = nOpt; |
1114 | | // we need to set this before the code below because it indirectly uses m_nOptions |
1115 | | |
1116 | | // the 'insert' option affects our empty row |
1117 | 0 | if (bInsertChanged) |
1118 | 0 | { |
1119 | 0 | if (m_nOptions & DbGridControlOptions::Insert) |
1120 | 0 | { // the insert option is to be set |
1121 | 0 | m_xEmptyRow = new DbGridRow(); |
1122 | 0 | RowInserted(GetRowCount()); |
1123 | 0 | } |
1124 | 0 | else |
1125 | 0 | { // the insert option is to be reset |
1126 | 0 | m_xEmptyRow = nullptr; |
1127 | 0 | if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0)) |
1128 | 0 | GoToRowColumnId(GetCurRow() - 1, GetCurColumnId()); |
1129 | 0 | RowRemoved(GetRowCount()); |
1130 | 0 | } |
1131 | 0 | } |
1132 | | |
1133 | | // the 'delete' options has no immediate consequences |
1134 | |
|
1135 | 0 | ActivateCell(); |
1136 | 0 | Invalidate(); |
1137 | 0 | return m_nOptions; |
1138 | 0 | } |
1139 | | |
1140 | | void DbGridControl::ForceHideScrollbars() |
1141 | 0 | { |
1142 | 0 | if ( m_bHideScrollbars ) |
1143 | 0 | return; |
1144 | | |
1145 | 0 | m_bHideScrollbars = true; |
1146 | |
|
1147 | 0 | if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) |
1148 | 0 | SetMode( m_nMode ); |
1149 | 0 | } |
1150 | | |
1151 | | void DbGridControl::EnablePermanentCursor(bool bEnable) |
1152 | 0 | { |
1153 | 0 | if (IsPermanentCursorEnabled() == bEnable) |
1154 | 0 | return; |
1155 | | |
1156 | 0 | if (bEnable) |
1157 | 0 | { |
1158 | 0 | m_nMode &= ~BrowserMode::HIDECURSOR; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect |
1159 | 0 | m_nMode |= BrowserMode::CURSOR_WO_FOCUS; |
1160 | 0 | } |
1161 | 0 | else |
1162 | 0 | { |
1163 | 0 | if (m_nOptions & DbGridControlOptions::Update) |
1164 | 0 | m_nMode |= BrowserMode::HIDECURSOR; // no cursor at all |
1165 | 0 | else |
1166 | 0 | m_nMode &= ~BrowserMode::HIDECURSOR; // at least the "non-permanent" cursor |
1167 | |
|
1168 | 0 | m_nMode &= ~BrowserMode::CURSOR_WO_FOCUS; |
1169 | 0 | } |
1170 | 0 | SetMode(m_nMode); |
1171 | |
|
1172 | 0 | bool bWasEditing = IsEditing(); |
1173 | 0 | DeactivateCell(); |
1174 | 0 | if (bWasEditing) |
1175 | 0 | ActivateCell(); |
1176 | 0 | } |
1177 | | |
1178 | | bool DbGridControl::IsPermanentCursorEnabled() const |
1179 | 0 | { |
1180 | 0 | return (m_nMode & BrowserMode::CURSOR_WO_FOCUS) && !(m_nMode & BrowserMode::HIDECURSOR); |
1181 | 0 | } |
1182 | | |
1183 | | void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/) |
1184 | 0 | { |
1185 | 0 | if ((GetCurColumnId() == _nColId) && IsEditing()) |
1186 | 0 | { // the controller which is currently active needs to be refreshed |
1187 | 0 | DeactivateCell(); |
1188 | 0 | ActivateCell(); |
1189 | 0 | } |
1190 | 0 | } |
1191 | | |
1192 | | void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, DbGridControlOptions nOpts) |
1193 | 0 | { |
1194 | 0 | if (!_xCursor.is() && !m_pDataCursor) |
1195 | 0 | return; |
1196 | | |
1197 | 0 | if (m_pDataSourcePropMultiplexer.is()) |
1198 | 0 | { |
1199 | 0 | m_pDataSourcePropMultiplexer->dispose(); |
1200 | 0 | m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer |
1201 | 0 | delete m_pDataSourcePropListener; |
1202 | 0 | m_pDataSourcePropListener = nullptr; |
1203 | 0 | } |
1204 | 0 | m_xRowSetListener.clear(); |
1205 | | |
1206 | | // is the new cursor valid ? |
1207 | | // the cursor is only valid if it contains some columns |
1208 | | // if there is no cursor or the cursor is not valid we have to clean up and leave |
1209 | 0 | if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY_THROW)->getColumns()->hasElements()) |
1210 | 0 | { |
1211 | 0 | RemoveRows(); |
1212 | 0 | return; |
1213 | 0 | } |
1214 | | |
1215 | | // did the data cursor change? |
1216 | 0 | sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId()); |
1217 | |
|
1218 | 0 | SetUpdateMode(false); |
1219 | 0 | RemoveRows(); |
1220 | 0 | DisconnectFromFields(); |
1221 | |
|
1222 | 0 | m_pCursorDisposeListener.reset(); |
1223 | |
|
1224 | 0 | { |
1225 | 0 | ::osl::MutexGuard aGuard(m_aAdjustSafety); |
1226 | 0 | if (m_nAsynAdjustEvent) |
1227 | 0 | { |
1228 | | // the adjust was thought to work with the old cursor which we don't have anymore |
1229 | 0 | RemoveUserEvent(m_nAsynAdjustEvent); |
1230 | 0 | m_nAsynAdjustEvent = nullptr; |
1231 | 0 | } |
1232 | 0 | } |
1233 | | |
1234 | | // get a new formatter and data cursor |
1235 | 0 | m_xFormatter = nullptr; |
1236 | 0 | Reference< css::util::XNumberFormatsSupplier > xSupplier = getNumberFormats(getConnection(_xCursor), true); |
1237 | 0 | if (xSupplier.is()) |
1238 | 0 | { |
1239 | 0 | m_xFormatter = css::util::NumberFormatter::create(m_xContext); |
1240 | 0 | m_xFormatter->attachNumberFormatsSupplier(xSupplier); |
1241 | | |
1242 | | // retrieve the datebase of the Numberformatter |
1243 | 0 | try |
1244 | 0 | { |
1245 | 0 | xSupplier->getNumberFormatSettings()->getPropertyValue(u"NullDate"_ustr) >>= m_aNullDate; |
1246 | 0 | } |
1247 | 0 | catch(Exception&) |
1248 | 0 | { |
1249 | 0 | } |
1250 | 0 | } |
1251 | |
|
1252 | 0 | m_pDataCursor.reset(new CursorWrapper(_xCursor)); |
1253 | | |
1254 | | // now create a cursor for painting rows |
1255 | | // we need that cursor only if we are not in insert only mode |
1256 | 0 | Reference< XResultSet > xClone; |
1257 | 0 | Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY ); |
1258 | 0 | try |
1259 | 0 | { |
1260 | 0 | xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > (); |
1261 | 0 | } |
1262 | 0 | catch(Exception&) |
1263 | 0 | { |
1264 | 0 | } |
1265 | 0 | if (xClone.is()) |
1266 | 0 | m_pSeekCursor.reset(new CursorWrapper(xClone)); |
1267 | | |
1268 | | // property listening on the data source |
1269 | | // (Normally one class would be sufficient : the multiplexer which could forward the property change to us. |
1270 | | // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported. |
1271 | | // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class) |
1272 | | // and forwards the property changes to our special method "DataSourcePropertyChanged".) |
1273 | 0 | if (m_pDataCursor) |
1274 | 0 | { |
1275 | 0 | m_pDataSourcePropListener = new FmXGridSourcePropListener(this); |
1276 | 0 | m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() ); |
1277 | 0 | m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED); |
1278 | 0 | m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW); |
1279 | 0 | } |
1280 | |
|
1281 | 0 | BrowserMode nOldMode = m_nMode; |
1282 | 0 | if (m_pSeekCursor) |
1283 | 0 | { |
1284 | 0 | try |
1285 | 0 | { |
1286 | 0 | Reference< XPropertySet > xSet(_xCursor, UNO_QUERY); |
1287 | 0 | if (xSet.is()) |
1288 | 0 | { |
1289 | | // check what kind of options are available |
1290 | 0 | sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY; |
1291 | 0 | xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency; |
1292 | |
|
1293 | 0 | if ( ResultSetConcurrency::UPDATABLE == nConcurrency ) |
1294 | 0 | { |
1295 | 0 | sal_Int32 nPrivileges = 0; |
1296 | 0 | xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges; |
1297 | | |
1298 | | // Insert Option should be set if insert only otherwise you won't see any rows |
1299 | | // and no insertion is possible |
1300 | 0 | if ((m_nOptionMask & DbGridControlOptions::Insert) |
1301 | 0 | && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & DbGridControlOptions::Insert)) |
1302 | 0 | m_nOptions |= DbGridControlOptions::Insert; |
1303 | 0 | if ((m_nOptionMask & DbGridControlOptions::Update) |
1304 | 0 | && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & DbGridControlOptions::Update)) |
1305 | 0 | m_nOptions |= DbGridControlOptions::Update; |
1306 | 0 | if ((m_nOptionMask & DbGridControlOptions::Delete) |
1307 | 0 | && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & DbGridControlOptions::Delete)) |
1308 | 0 | m_nOptions |= DbGridControlOptions::Delete; |
1309 | 0 | } |
1310 | 0 | } |
1311 | 0 | } |
1312 | 0 | catch( const Exception& ) |
1313 | 0 | { |
1314 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1315 | 0 | } |
1316 | |
|
1317 | 0 | bool bPermanentCursor = IsPermanentCursorEnabled(); |
1318 | 0 | m_nMode = DEFAULT_BROWSE_MODE; |
1319 | |
|
1320 | 0 | if ( bPermanentCursor ) |
1321 | 0 | { |
1322 | 0 | m_nMode |= BrowserMode::CURSOR_WO_FOCUS; |
1323 | 0 | m_nMode &= ~BrowserMode::HIDECURSOR; |
1324 | 0 | } |
1325 | 0 | else |
1326 | 0 | { |
1327 | | // updates are allowed -> no focus rectangle |
1328 | 0 | if ( m_nOptions & DbGridControlOptions::Update ) |
1329 | 0 | m_nMode |= BrowserMode::HIDECURSOR; |
1330 | 0 | } |
1331 | |
|
1332 | 0 | m_nMode |= BrowserMode::MULTISELECTION; |
1333 | |
|
1334 | 0 | adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ); |
1335 | |
|
1336 | 0 | Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY); |
1337 | 0 | if (xSupplyColumns.is()) |
1338 | 0 | InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY)); |
1339 | |
|
1340 | 0 | ConnectToFields(); |
1341 | 0 | } |
1342 | |
|
1343 | 0 | sal_uInt32 nRecordCount(0); |
1344 | |
|
1345 | 0 | if (m_pSeekCursor) |
1346 | 0 | { |
1347 | 0 | Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet(); |
1348 | 0 | xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount; |
1349 | 0 | m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL)); |
1350 | |
|
1351 | 0 | m_xRowSetListener = new RowSetEventListener(this); |
1352 | 0 | Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY); |
1353 | 0 | if ( xChangeBroad.is( ) ) |
1354 | 0 | xChangeBroad->addRowsChangeListener(m_xRowSetListener); |
1355 | | |
1356 | | |
1357 | | // insert the currently known rows |
1358 | | // and one row if we are able to insert rows |
1359 | 0 | if (m_nOptions & DbGridControlOptions::Insert) |
1360 | 0 | { |
1361 | | // insert the empty row for insertion |
1362 | 0 | m_xEmptyRow = new DbGridRow(); |
1363 | 0 | ++nRecordCount; |
1364 | 0 | } |
1365 | 0 | if (nRecordCount) |
1366 | 0 | { |
1367 | 0 | m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor.get(), true); |
1368 | 0 | m_xDataRow = new DbGridRow(m_pDataCursor.get(), false); |
1369 | 0 | RowInserted(0, nRecordCount, false); |
1370 | |
|
1371 | 0 | if (m_xSeekRow->IsValid()) |
1372 | 0 | try |
1373 | 0 | { |
1374 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
1375 | 0 | } |
1376 | 0 | catch( const Exception& ) |
1377 | 0 | { |
1378 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1379 | 0 | m_nSeekPos = -1; |
1380 | 0 | } |
1381 | 0 | } |
1382 | 0 | else |
1383 | 0 | { |
1384 | | // no rows so we don't need a seekcursor |
1385 | 0 | m_pSeekCursor.reset(); |
1386 | 0 | } |
1387 | 0 | } |
1388 | | |
1389 | | // go to the old column |
1390 | 0 | if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount()) |
1391 | 0 | nCurPos = 0; |
1392 | | |
1393 | | // Column zero is a valid choice and guaranteed to exist, |
1394 | | // but invisible to the user; if we have at least one |
1395 | | // user-visible column, go to that one. |
1396 | 0 | if (nCurPos == 0 && ColCount() > 1) |
1397 | 0 | nCurPos = 1; |
1398 | | |
1399 | | // there are rows so go to the selected current column |
1400 | 0 | if (nRecordCount) |
1401 | 0 | GoToRowColumnId(0, GetColumnId(nCurPos)); |
1402 | | // else stop the editing if necessary |
1403 | 0 | else if (IsEditing()) |
1404 | 0 | DeactivateCell(); |
1405 | | |
1406 | | // now reset the mode |
1407 | 0 | if (m_nMode != nOldMode) |
1408 | 0 | SetMode(m_nMode); |
1409 | | |
1410 | | // RecalcRows was already called while resizing |
1411 | 0 | if (!IsResizing() && GetRowCount()) |
1412 | 0 | RecalcRows(GetTopRow(), GetVisibleRows(), true); |
1413 | |
|
1414 | 0 | m_aBar->InvalidateAll(m_nCurrentPos, true); |
1415 | 0 | SetUpdateMode(true); |
1416 | | |
1417 | | // start listening on the seek cursor |
1418 | 0 | if (m_pSeekCursor) |
1419 | 0 | m_pCursorDisposeListener.reset(new DisposeListenerGridBridge(*this, Reference< XComponent > (Reference< XInterface >(*m_pSeekCursor), UNO_QUERY))); |
1420 | 0 | } |
1421 | | |
1422 | | void DbGridControl::RemoveColumns() |
1423 | 0 | { |
1424 | 0 | if ( !isDisposed() && IsEditing() ) |
1425 | 0 | DeactivateCell(); |
1426 | |
|
1427 | 0 | m_aColumns.clear(); |
1428 | |
|
1429 | 0 | EditBrowseBox::RemoveColumns(); |
1430 | 0 | } |
1431 | | |
1432 | | std::unique_ptr<DbGridColumn> DbGridControl::CreateColumn(sal_uInt16 nId) |
1433 | 0 | { |
1434 | 0 | return std::unique_ptr<DbGridColumn>(new DbGridColumn(nId, *this)); |
1435 | 0 | } |
1436 | | |
1437 | | sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId) |
1438 | 0 | { |
1439 | 0 | DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ..."); |
1440 | 0 | sal_uInt16 nRealPos = nModelPos; |
1441 | 0 | if (nModelPos != HEADERBAR_APPEND) |
1442 | 0 | { |
1443 | | // calc the view pos. we can't use our converting functions because the new column |
1444 | | // has no VCL-representation, yet. |
1445 | 0 | sal_Int16 nViewPos = nModelPos; |
1446 | 0 | while (nModelPos--) |
1447 | 0 | { |
1448 | 0 | if ( m_aColumns[ nModelPos ]->IsHidden() ) |
1449 | 0 | --nViewPos; |
1450 | 0 | } |
1451 | | // restore nModelPos, we need it later |
1452 | 0 | nModelPos = nRealPos; |
1453 | | // the position the base class gets is the view pos + 1 (because of the handle column) |
1454 | 0 | nRealPos = nViewPos + 1; |
1455 | 0 | } |
1456 | | |
1457 | | // calculate the new id |
1458 | 0 | for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && size_t(nId) <= m_aColumns.size(); ++nId) |
1459 | 0 | ; |
1460 | 0 | DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !"); |
1461 | | // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..." |
1462 | |
|
1463 | 0 | EditBrowseBox::AppendColumn(rName, nWidth, nRealPos, nId); |
1464 | 0 | if (nModelPos == HEADERBAR_APPEND) |
1465 | 0 | m_aColumns.push_back( CreateColumn(nId) ); |
1466 | 0 | else |
1467 | 0 | m_aColumns.insert( m_aColumns.begin() + nModelPos, CreateColumn(nId) ); |
1468 | |
|
1469 | 0 | return nId; |
1470 | 0 | } |
1471 | | |
1472 | | void DbGridControl::RemoveColumn(sal_uInt16 nId) |
1473 | 0 | { |
1474 | 0 | EditBrowseBox::RemoveColumn(nId); |
1475 | |
|
1476 | 0 | const sal_uInt16 nIndex = GetModelColumnPos(nId); |
1477 | 0 | if(nIndex != GRID_COLUMN_NOT_FOUND) |
1478 | 0 | { |
1479 | 0 | m_aColumns.erase( m_aColumns.begin()+nIndex ); |
1480 | 0 | } |
1481 | 0 | } |
1482 | | |
1483 | | void DbGridControl::ColumnMoved(sal_uInt16 nId) |
1484 | 0 | { |
1485 | 0 | EditBrowseBox::ColumnMoved(nId); |
1486 | | |
1487 | | // remove the col from the model |
1488 | 0 | sal_uInt16 nOldModelPos = GetModelColumnPos(nId); |
1489 | | #ifdef DBG_UTIL |
1490 | | DbGridColumn* pCol = m_aColumns[ nOldModelPos ].get(); |
1491 | | DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?"); |
1492 | | #endif |
1493 | | |
1494 | | // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment |
1495 | | // so the method won't work (in fact it would return the old model pos) |
1496 | | |
1497 | | // the new view pos is calculated easily |
1498 | 0 | sal_uInt16 nNewViewPos = GetViewColumnPos(nId); |
1499 | | |
1500 | | // from that we can compute the new model pos |
1501 | 0 | size_t nNewModelPos; |
1502 | 0 | for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos) |
1503 | 0 | { |
1504 | 0 | if (!m_aColumns[ nNewModelPos ]->IsHidden()) |
1505 | 0 | { |
1506 | 0 | if (!nNewViewPos) |
1507 | 0 | break; |
1508 | 0 | else |
1509 | 0 | --nNewViewPos; |
1510 | 0 | } |
1511 | 0 | } |
1512 | 0 | DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !"); |
1513 | | |
1514 | | // this will work. of course the model isn't fully consistent with our view right now, but let's |
1515 | | // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the |
1516 | | // other case we can use analogue arguments). |
1517 | | // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n. |
1518 | | // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols |
1519 | | // within this range is constant, so we may calculate the view pos from the model pos in the above way. |
1520 | | |
1521 | | // for instance, let's look at a grid with six columns where the third one is hidden. this will |
1522 | | // initially look like this : |
1523 | | |
1524 | | // +---+---+---+---+---+---+ |
1525 | | // model pos | 0 | 1 |*2*| 3 | 4 | 5 | |
1526 | | // +---+---+---+---+---+---+ |
1527 | | // ID | 1 | 2 | 3 | 4 | 5 | 6 | |
1528 | | // +---+---+---+---+---+---+ |
1529 | | // view pos | 0 | 1 | - | 2 | 3 | 4 | |
1530 | | // +---+---+---+---+---+---+ |
1531 | | |
1532 | | // if we move the column at (view) pos 1 to (view) pos 3 we have : |
1533 | | |
1534 | | // +---+---+---+---+---+---+ |
1535 | | // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet |
1536 | | // +---+---+---+---+---+---+ |
1537 | | // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes |
1538 | | // +---+---+---+---+---+---+ |
1539 | | // view pos | 0 | 1 | - | 2 | 3 | 4 | |
1540 | | // +---+---+---+---+---+---+ |
1541 | | |
1542 | | // or, sorted by the out-of-date model positions : |
1543 | | |
1544 | | // +---+---+---+---+---+---+ |
1545 | | // model pos | 0 | 1 |*2*| 3 | 4 | 5 | |
1546 | | // +---+---+---+---+---+---+ |
1547 | | // ID | 1 | 2 | 3 | 4 | 5 | 6 | |
1548 | | // +---+---+---+---+---+---+ |
1549 | | // view pos | 0 | 3 | - | 1 | 2 | 4 | |
1550 | | // +---+---+---+---+---+---+ |
1551 | | |
1552 | | // We know the new view pos (3) of the moved column because our base class tells us. So we look at our |
1553 | | // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is |
1554 | | // exactly the pos where we have to re-insert our column's model, so it looks ike this : |
1555 | | |
1556 | | // +---+---+---+---+---+---+ |
1557 | | // model pos | 0 |*1*| 2 | 3 | 4 | 5 | |
1558 | | // +---+---+---+---+---+---+ |
1559 | | // ID | 1 | 3 | 4 | 5 | 2 | 6 | |
1560 | | // +---+---+---+---+---+---+ |
1561 | | // view pos | 0 | - | 1 | 2 | 3 | 4 | |
1562 | | // +---+---+---+---+---+---+ |
1563 | | |
1564 | | // Now, all is consistent again. |
1565 | | // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe |
1566 | | // the user expected the latter but there really is no good argument against our method ;) ...) |
1567 | | |
1568 | | // And no, this large explanation isn't just because I wanted to play a board game or something like |
1569 | | // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col |
1570 | | // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;) |
1571 | |
|
1572 | 0 | auto temp = std::move(m_aColumns[ nOldModelPos ]); |
1573 | 0 | m_aColumns.erase( m_aColumns.begin() + nOldModelPos ); |
1574 | 0 | m_aColumns.insert( m_aColumns.begin() + nNewModelPos, std::move(temp) ); |
1575 | 0 | } |
1576 | | |
1577 | | bool DbGridControl::SeekRow(sal_Int32 nRow) |
1578 | 0 | { |
1579 | | // in filter mode or in insert only mode we don't have any cursor! |
1580 | 0 | if ( !SeekCursor( nRow ) ) |
1581 | 0 | return false; |
1582 | | |
1583 | 0 | if ( IsFilterMode() ) |
1584 | 0 | { |
1585 | 0 | DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" ); |
1586 | 0 | m_xPaintRow = m_xEmptyRow; |
1587 | 0 | } |
1588 | 0 | else |
1589 | 0 | { |
1590 | | // on the current position we have to take the current row for display as we want |
1591 | | // to have the most recent values for display |
1592 | 0 | if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() ) |
1593 | 0 | m_xPaintRow = m_xCurrentRow; |
1594 | | // seek to the empty insert row |
1595 | 0 | else if ( IsInsertionRow( nRow ) ) |
1596 | 0 | m_xPaintRow = m_xEmptyRow; |
1597 | 0 | else |
1598 | 0 | { |
1599 | 0 | m_xSeekRow->SetState( m_pSeekCursor.get(), true ); |
1600 | 0 | m_xPaintRow = m_xSeekRow; |
1601 | 0 | } |
1602 | 0 | } |
1603 | |
|
1604 | 0 | EditBrowseBox::SeekRow(nRow); |
1605 | |
|
1606 | 0 | return m_nSeekPos >= 0; |
1607 | 0 | } |
1608 | | |
1609 | | // Is called whenever the visible amount of data changes |
1610 | | void DbGridControl::VisibleRowsChanged( sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen ) |
1611 | 0 | { |
1612 | 0 | RecalcRows(nNewTopRow, nLinesOnScreen, false); |
1613 | 0 | } |
1614 | | |
1615 | | void DbGridControl::RecalcRows(sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor) |
1616 | 0 | { |
1617 | | // If no cursor -> no rows in the browser. |
1618 | 0 | if (!m_pSeekCursor) |
1619 | 0 | { |
1620 | 0 | DBG_ASSERT(GetRowCount() == 0,"DbGridControl: without cursor no rows are allowed to be there"); |
1621 | 0 | return; |
1622 | 0 | } |
1623 | | |
1624 | | // ignore any implicitly made updates |
1625 | 0 | bool bDisablePaint = !bUpdateCursor && IsPaintEnabled(); |
1626 | 0 | if (bDisablePaint) |
1627 | 0 | EnablePaint(false); |
1628 | | |
1629 | | // adjust cache to the visible area |
1630 | 0 | Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet(); |
1631 | 0 | sal_Int32 nCacheSize = 0; |
1632 | 0 | xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize; |
1633 | 0 | bool bCacheAligned = false; |
1634 | | // no further cursor movements after initializing (m_nSeekPos < 0) because it is already |
1635 | | // positioned on the first sentence |
1636 | 0 | tools::Long nDelta = nNewTopRow - GetTopRow(); |
1637 | | // limit for relative positioning |
1638 | 0 | tools::Long nLimit = nCacheSize ? nCacheSize / 2 : 0; |
1639 | | |
1640 | | // more lines on screen than in cache |
1641 | 0 | if (nLimit < nLinesOnScreen) |
1642 | 0 | { |
1643 | 0 | Any aCacheSize; |
1644 | 0 | aCacheSize <<= sal_Int32(nLinesOnScreen*2); |
1645 | 0 | xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize); |
1646 | | // here we need to update the cursor for sure |
1647 | 0 | bUpdateCursor = true; |
1648 | 0 | bCacheAligned = true; |
1649 | 0 | nLimit = nLinesOnScreen; |
1650 | 0 | } |
1651 | | |
1652 | | // In the following, all positionings are done as it is |
1653 | | // ensured that there are enough lines in the data cache |
1654 | | |
1655 | | // window goes downwards with less than two windows difference or |
1656 | | // the cache was updated and no rowcount yet |
1657 | 0 | if (nDelta < nLimit && (nDelta > 0 |
1658 | 0 | || (bCacheAligned && m_nTotalCount < 0)) ) |
1659 | 0 | SeekCursor(nNewTopRow + nLinesOnScreen - 1); |
1660 | 0 | else if (nDelta < 0 && std::abs(nDelta) < nLimit) |
1661 | 0 | SeekCursor(nNewTopRow); |
1662 | 0 | else if (nDelta != 0 || bUpdateCursor) |
1663 | 0 | SeekCursor(nNewTopRow, true); |
1664 | |
|
1665 | 0 | AdjustRows(); |
1666 | | |
1667 | | // ignore any updates implicit made |
1668 | 0 | EnablePaint(true); |
1669 | 0 | } |
1670 | | |
1671 | | void DbGridControl::RowInserted(sal_Int32 nRow, sal_Int32 nNumRows, bool bDoPaint) |
1672 | 0 | { |
1673 | 0 | if (!nNumRows) |
1674 | 0 | return; |
1675 | | |
1676 | 0 | if (m_bRecordCountFinal && m_nTotalCount < 0) |
1677 | 0 | { |
1678 | | // if we have an insert row we have to reduce to count by 1 |
1679 | | // as the total count reflects only the existing rows in database |
1680 | 0 | m_nTotalCount = GetRowCount() + nNumRows; |
1681 | 0 | if (m_xEmptyRow.is()) |
1682 | 0 | --m_nTotalCount; |
1683 | 0 | } |
1684 | 0 | else if (m_nTotalCount >= 0) |
1685 | 0 | m_nTotalCount += nNumRows; |
1686 | |
|
1687 | 0 | EditBrowseBox::RowInserted(nRow, nNumRows, bDoPaint); |
1688 | 0 | m_aBar->InvalidateState(DbGridControlNavigationBarState::Count); |
1689 | 0 | } |
1690 | | |
1691 | | void DbGridControl::RowRemoved(sal_Int32 nRow, sal_Int32 nNumRows, bool bDoPaint) |
1692 | 0 | { |
1693 | 0 | if (!nNumRows) |
1694 | 0 | return; |
1695 | | |
1696 | 0 | if (m_bRecordCountFinal && m_nTotalCount < 0) |
1697 | 0 | { |
1698 | 0 | m_nTotalCount = GetRowCount() - nNumRows; |
1699 | | // if we have an insert row reduce by 1 |
1700 | 0 | if (m_xEmptyRow.is()) |
1701 | 0 | --m_nTotalCount; |
1702 | 0 | } |
1703 | 0 | else if (m_nTotalCount >= 0) |
1704 | 0 | m_nTotalCount -= nNumRows; |
1705 | |
|
1706 | 0 | EditBrowseBox::RowRemoved(nRow, nNumRows, bDoPaint); |
1707 | 0 | m_aBar->InvalidateState(DbGridControlNavigationBarState::Count); |
1708 | 0 | } |
1709 | | |
1710 | | void DbGridControl::AdjustRows() |
1711 | 0 | { |
1712 | 0 | if (!m_pSeekCursor) |
1713 | 0 | return; |
1714 | | |
1715 | 0 | Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet(); |
1716 | | |
1717 | | // refresh RecordCount |
1718 | 0 | sal_Int32 nRecordCount = 0; |
1719 | 0 | xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount; |
1720 | 0 | if (!m_bRecordCountFinal) |
1721 | 0 | m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL)); |
1722 | | |
1723 | | // Did the number of rows change? |
1724 | | // Here we need to consider that there might be an additional row for adding new data sets |
1725 | | |
1726 | | // add additional AppendRow for insertion |
1727 | 0 | if (m_nOptions & DbGridControlOptions::Insert) |
1728 | 0 | ++nRecordCount; |
1729 | | |
1730 | | // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow |
1731 | 0 | if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow && |
1732 | 0 | m_xCurrentRow->IsNew()) |
1733 | 0 | ++nRecordCount; |
1734 | | // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this |
1735 | | // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount |
1736 | | // and a second time here (60787 - FS) |
1737 | |
|
1738 | 0 | if (nRecordCount != GetRowCount()) |
1739 | 0 | { |
1740 | 0 | tools::Long nDelta = GetRowCount() - static_cast<tools::Long>(nRecordCount); |
1741 | 0 | if (nDelta > 0) // too many |
1742 | 0 | { |
1743 | 0 | RowRemoved(GetRowCount() - nDelta, nDelta, false); |
1744 | | // some rows are gone, thus, repaint starting at the current position |
1745 | 0 | Invalidate(); |
1746 | |
|
1747 | 0 | sal_Int32 nNewPos = AlignSeekCursor(); |
1748 | 0 | if (m_bSynchDisplay) |
1749 | 0 | EditBrowseBox::GoToRow(nNewPos); |
1750 | |
|
1751 | 0 | SetCurrent(nNewPos); |
1752 | | // there are rows so go to the selected current column |
1753 | 0 | if (nRecordCount) |
1754 | 0 | GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId())); |
1755 | 0 | if (!IsResizing() && GetRowCount()) |
1756 | 0 | RecalcRows(GetTopRow(), GetVisibleRows(), true); |
1757 | 0 | m_aBar->InvalidateAll(m_nCurrentPos, true); |
1758 | 0 | } |
1759 | 0 | else // too few |
1760 | 0 | RowInserted(GetRowCount(), -nDelta); |
1761 | 0 | } |
1762 | |
|
1763 | 0 | if (m_bRecordCountFinal && m_nTotalCount < 0) |
1764 | 0 | { |
1765 | 0 | if (m_nOptions & DbGridControlOptions::Insert) |
1766 | 0 | m_nTotalCount = GetRowCount() - 1; |
1767 | 0 | else |
1768 | 0 | m_nTotalCount = GetRowCount(); |
1769 | 0 | } |
1770 | 0 | m_aBar->InvalidateState(DbGridControlNavigationBarState::Count); |
1771 | 0 | } |
1772 | | |
1773 | | svt::EditBrowseBox::RowStatus DbGridControl::GetRowStatus(sal_Int32 nRow) const |
1774 | 0 | { |
1775 | 0 | if (IsFilterRow(nRow)) |
1776 | 0 | return EditBrowseBox::FILTER; |
1777 | 0 | else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos) |
1778 | 0 | { |
1779 | | // new row |
1780 | 0 | if (!IsValid(m_xCurrentRow)) |
1781 | 0 | return EditBrowseBox::DELETED; |
1782 | 0 | else if (IsModified()) |
1783 | 0 | return EditBrowseBox::MODIFIED; |
1784 | 0 | else if (m_xCurrentRow->IsNew()) |
1785 | 0 | return EditBrowseBox::CURRENTNEW; |
1786 | 0 | else |
1787 | 0 | return EditBrowseBox::CURRENT; |
1788 | 0 | } |
1789 | 0 | else if (IsInsertionRow(nRow)) |
1790 | 0 | return EditBrowseBox::NEW; |
1791 | 0 | else if (!IsValid(m_xSeekRow)) |
1792 | 0 | return EditBrowseBox::DELETED; |
1793 | 0 | else |
1794 | 0 | return EditBrowseBox::CLEAN; |
1795 | 0 | } |
1796 | | |
1797 | | void DbGridControl::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId) const |
1798 | 0 | { |
1799 | 0 | if (!IsValid(m_xPaintRow)) |
1800 | 0 | return; |
1801 | | |
1802 | 0 | size_t Location = GetModelColumnPos(nColumnId); |
1803 | 0 | DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
1804 | 0 | if (pColumn) |
1805 | 0 | { |
1806 | 0 | tools::Rectangle aArea(rRect); |
1807 | 0 | if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS) |
1808 | 0 | { |
1809 | 0 | aArea.AdjustTop(1 ); |
1810 | 0 | aArea.AdjustBottom( -1 ); |
1811 | 0 | } |
1812 | 0 | pColumn->Paint(rDev, aArea, m_xPaintRow.get(), getNumberFormatter()); |
1813 | 0 | } |
1814 | 0 | } |
1815 | | |
1816 | | bool DbGridControl::CursorMoving(sal_Int32 nNewRow, sal_uInt16 nNewCol) |
1817 | 0 | { |
1818 | |
|
1819 | 0 | DeactivateCell( false ); |
1820 | |
|
1821 | 0 | if ( m_pDataCursor |
1822 | 0 | && ( m_nCurrentPos != nNewRow ) |
1823 | 0 | && !SetCurrent( nNewRow ) |
1824 | 0 | ) |
1825 | 0 | { |
1826 | 0 | ActivateCell(); |
1827 | 0 | return false; |
1828 | 0 | } |
1829 | | |
1830 | 0 | return EditBrowseBox::CursorMoving( nNewRow, nNewCol ); |
1831 | 0 | } |
1832 | | |
1833 | | bool DbGridControl::SetCurrent(sal_Int32 nNewRow) |
1834 | 0 | { |
1835 | | // Each movement of the datacursor must start with BeginCursorAction and end with |
1836 | | // EndCursorAction to block all notifications during the movement |
1837 | 0 | BeginCursorAction(); |
1838 | |
|
1839 | 0 | try |
1840 | 0 | { |
1841 | | // compare positions |
1842 | 0 | if (SeekCursor(nNewRow)) |
1843 | 0 | { |
1844 | 0 | if (IsFilterRow(nNewRow)) // special mode for filtering |
1845 | 0 | { |
1846 | 0 | m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow; |
1847 | 0 | m_nCurrentPos = nNewRow; |
1848 | 0 | } |
1849 | 0 | else |
1850 | 0 | { |
1851 | 0 | bool bNewRowInserted = false; |
1852 | | // Should we go to the insertrow ? |
1853 | 0 | if (IsInsertionRow(nNewRow)) |
1854 | 0 | { |
1855 | | // to we need to move the cursor to the insert row? |
1856 | | // we need to insert the if the current row isn't the insert row or if the |
1857 | | // cursor triggered the move by itself and we need a reinitialization of the row |
1858 | 0 | Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet(); |
1859 | 0 | if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) ) |
1860 | 0 | { |
1861 | 0 | Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY); |
1862 | 0 | xUpdateCursor->moveToInsertRow(); |
1863 | 0 | } |
1864 | 0 | bNewRowInserted = true; |
1865 | 0 | } |
1866 | 0 | else |
1867 | 0 | { |
1868 | |
|
1869 | 0 | if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() ) |
1870 | 0 | { |
1871 | 0 | Any aBookmark = m_pSeekCursor->getBookmark(); |
1872 | 0 | if (!m_xCurrentRow.is() || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark())) |
1873 | 0 | { |
1874 | | // adjust the cursor to the new desired row |
1875 | 0 | if (!m_pDataCursor->moveToBookmark(aBookmark)) |
1876 | 0 | { |
1877 | 0 | EndCursorAction(); |
1878 | 0 | return false; |
1879 | 0 | } |
1880 | 0 | } |
1881 | 0 | } |
1882 | 0 | } |
1883 | 0 | m_xDataRow->SetState(m_pDataCursor.get(), false); |
1884 | 0 | m_xCurrentRow = m_xDataRow; |
1885 | |
|
1886 | 0 | tools::Long nPaintPos = -1; |
1887 | | // do we have to repaint the last regular row in case of setting defaults or autovalues |
1888 | 0 | if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2)) |
1889 | 0 | nPaintPos = m_nCurrentPos; |
1890 | |
|
1891 | 0 | m_nCurrentPos = nNewRow; |
1892 | | |
1893 | | // repaint the new row to display all defaults |
1894 | 0 | if (bNewRowInserted) |
1895 | 0 | RowModified(m_nCurrentPos); |
1896 | 0 | if (nPaintPos >= 0) |
1897 | 0 | RowModified(nPaintPos); |
1898 | 0 | } |
1899 | 0 | } |
1900 | 0 | else |
1901 | 0 | { |
1902 | 0 | OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !"); |
1903 | 0 | EndCursorAction(); |
1904 | 0 | return false; |
1905 | 0 | } |
1906 | 0 | } |
1907 | 0 | catch ( const Exception& ) |
1908 | 0 | { |
1909 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
1910 | 0 | EndCursorAction(); |
1911 | 0 | return false; |
1912 | 0 | } |
1913 | | |
1914 | 0 | EndCursorAction(); |
1915 | 0 | return true; |
1916 | 0 | } |
1917 | | |
1918 | | void DbGridControl::CursorMoved() |
1919 | 0 | { |
1920 | | |
1921 | | // cursor movement due to deletion or insertion of rows |
1922 | 0 | if (m_pDataCursor && m_nCurrentPos != GetCurRow()) |
1923 | 0 | { |
1924 | 0 | DeactivateCell(); |
1925 | 0 | SetCurrent(GetCurRow()); |
1926 | 0 | } |
1927 | |
|
1928 | 0 | EditBrowseBox::CursorMoved(); |
1929 | 0 | m_aBar->InvalidateAll(m_nCurrentPos); |
1930 | | |
1931 | | // select the new column when they moved |
1932 | 0 | if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() ) |
1933 | 0 | { |
1934 | 0 | SelectColumnId( GetCurColumnId() ); |
1935 | 0 | } |
1936 | |
|
1937 | 0 | if ( m_nLastColId != GetCurColumnId() ) |
1938 | 0 | onColumnChange(); |
1939 | 0 | m_nLastColId = GetCurColumnId(); |
1940 | |
|
1941 | 0 | if ( m_nLastRowId != GetCurRow() ) |
1942 | 0 | onRowChange(); |
1943 | 0 | m_nLastRowId = GetCurRow(); |
1944 | 0 | } |
1945 | | |
1946 | | void DbGridControl::onRowChange() |
1947 | 0 | { |
1948 | | // not interested in |
1949 | 0 | } |
1950 | | |
1951 | | void DbGridControl::onColumnChange() |
1952 | 0 | { |
1953 | 0 | if ( m_pGridListener ) |
1954 | 0 | m_pGridListener->columnChanged(); |
1955 | 0 | } |
1956 | | |
1957 | | void DbGridControl::setDisplaySynchron(bool bSync) |
1958 | 0 | { |
1959 | 0 | if (bSync != m_bSynchDisplay) |
1960 | 0 | { |
1961 | 0 | m_bSynchDisplay = bSync; |
1962 | 0 | if (m_bSynchDisplay) |
1963 | 0 | AdjustDataSource(); |
1964 | 0 | } |
1965 | 0 | } |
1966 | | |
1967 | | void DbGridControl::AdjustDataSource(bool bFull) |
1968 | 0 | { |
1969 | 0 | SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource"); |
1970 | 0 | SolarMutexGuard aGuard; |
1971 | | // If the current row is recalculated at the moment, do not adjust |
1972 | |
|
1973 | 0 | if (bFull) |
1974 | 0 | m_xCurrentRow = nullptr; |
1975 | | // if we are on the same row only repaint |
1976 | | // but this is only possible for rows which are not inserted, in that case the comparison result |
1977 | | // may not be correct |
1978 | 0 | else |
1979 | 0 | if ( m_xCurrentRow.is() |
1980 | 0 | && !m_xCurrentRow->IsNew() |
1981 | 0 | && !m_pDataCursor->isBeforeFirst() |
1982 | 0 | && !m_pDataCursor->isAfterLast() |
1983 | 0 | && !m_pDataCursor->rowDeleted() |
1984 | 0 | ) |
1985 | 0 | { |
1986 | 0 | bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() ); |
1987 | |
|
1988 | 0 | bool bDataCursorIsOnNew = false; |
1989 | 0 | m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew; |
1990 | |
|
1991 | 0 | if ( bEqualBookmarks && !bDataCursorIsOnNew ) |
1992 | 0 | { |
1993 | | // position of my data cursor is the same as the position our current row points tpo |
1994 | | // sync the status, repaint, done |
1995 | 0 | DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Errors in the data row"); |
1996 | 0 | SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow)); |
1997 | 0 | RowModified(m_nCurrentPos); |
1998 | 0 | return; |
1999 | 0 | } |
2000 | 0 | } |
2001 | | |
2002 | | // away from the data cursor's row |
2003 | 0 | if (m_xPaintRow == m_xCurrentRow) |
2004 | 0 | m_xPaintRow = m_xSeekRow; |
2005 | | |
2006 | | // not up-to-date row, thus, adjust completely |
2007 | 0 | if (!m_xCurrentRow.is()) |
2008 | 0 | AdjustRows(); |
2009 | |
|
2010 | 0 | sal_Int32 nNewPos = AlignSeekCursor(); |
2011 | 0 | if (nNewPos < 0)// could not find any position |
2012 | 0 | return; |
2013 | | |
2014 | 0 | if (nNewPos != m_nCurrentPos) |
2015 | 0 | { |
2016 | 0 | if (m_bSynchDisplay) |
2017 | 0 | EditBrowseBox::GoToRow(nNewPos); |
2018 | |
|
2019 | 0 | if (!m_xCurrentRow.is()) |
2020 | | // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned |
2021 | | // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what |
2022 | | // CurrentRow is corrected to point two rows down, so that GoToRow will point into |
2023 | | // emptiness (since we are - purportedly - at the correct position) |
2024 | 0 | SetCurrent(nNewPos); |
2025 | 0 | } |
2026 | 0 | else |
2027 | 0 | { |
2028 | 0 | SetCurrent(nNewPos); |
2029 | 0 | RowModified(nNewPos); |
2030 | 0 | } |
2031 | | |
2032 | | // if the data cursor was moved from outside, this section is voided |
2033 | 0 | SetNoSelection(); |
2034 | 0 | m_aBar->InvalidateAll(m_nCurrentPos, m_xCurrentRow.is()); |
2035 | 0 | } |
2036 | | |
2037 | | sal_Int32 DbGridControl::AlignSeekCursor() |
2038 | 0 | { |
2039 | | // position SeekCursor onto the data cursor, no data transmission |
2040 | |
|
2041 | 0 | if (!m_pSeekCursor) |
2042 | 0 | return -1; |
2043 | | |
2044 | 0 | Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet(); |
2045 | | |
2046 | | // now align the seek cursor and the data cursor |
2047 | 0 | if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW))) |
2048 | 0 | m_nSeekPos = GetRowCount() - 1; |
2049 | 0 | else |
2050 | 0 | { |
2051 | 0 | try |
2052 | 0 | { |
2053 | 0 | if ( m_pDataCursor->isBeforeFirst() ) |
2054 | 0 | { |
2055 | | // this is somewhat strange, but can nevertheless happen |
2056 | 0 | SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" ); |
2057 | 0 | m_pSeekCursor->first(); |
2058 | 0 | m_pSeekCursor->previous(); |
2059 | 0 | m_nSeekPos = -1; |
2060 | 0 | } |
2061 | 0 | else if ( m_pDataCursor->isAfterLast() ) |
2062 | 0 | { |
2063 | 0 | SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" ); |
2064 | 0 | m_pSeekCursor->last(); |
2065 | 0 | m_pSeekCursor->next(); |
2066 | 0 | m_nSeekPos = -1; |
2067 | 0 | } |
2068 | 0 | else |
2069 | 0 | { |
2070 | 0 | m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark()); |
2071 | 0 | if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark())) |
2072 | | // unfortunately, moveToBookmark might lead to a re-positioning of the seek |
2073 | | // cursor (if the complex moveToBookmark with all its events fires an update |
2074 | | // somewhere) -> retry |
2075 | 0 | m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark()); |
2076 | | // Now there is still the chance of a failure but it is less likely. |
2077 | | // The alternative would be a loop until everything is fine - no good solution... |
2078 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
2079 | 0 | } |
2080 | 0 | } |
2081 | 0 | catch(Exception&) |
2082 | 0 | { |
2083 | 0 | } |
2084 | 0 | } |
2085 | 0 | return m_nSeekPos; |
2086 | 0 | } |
2087 | | |
2088 | | bool DbGridControl::SeekCursor(sal_Int32 nRow, bool bAbsolute) |
2089 | 0 | { |
2090 | | // position SeekCursor onto the data cursor, no data transmission |
2091 | | |
2092 | | // additions for the filtermode |
2093 | 0 | if (IsFilterRow(nRow)) |
2094 | 0 | { |
2095 | 0 | m_nSeekPos = 0; |
2096 | 0 | return true; |
2097 | 0 | } |
2098 | | |
2099 | 0 | if (!m_pSeekCursor) |
2100 | 0 | return false; |
2101 | | |
2102 | | // is this an insertion? |
2103 | 0 | if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() && |
2104 | 0 | nRow >= m_nCurrentPos) |
2105 | 0 | { |
2106 | | // if so, scrolling down must be prevented as this is already the last data set! |
2107 | 0 | if (nRow == m_nCurrentPos) |
2108 | 0 | { |
2109 | | // no adjustment necessary |
2110 | 0 | m_nSeekPos = nRow; |
2111 | 0 | } |
2112 | 0 | else if (IsInsertionRow(nRow)) // blank row for data insertion |
2113 | 0 | m_nSeekPos = nRow; |
2114 | 0 | } |
2115 | 0 | else if (IsInsertionRow(nRow)) // blank row for data insertion |
2116 | 0 | m_nSeekPos = nRow; |
2117 | 0 | else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & DbGridControlOptions::Insert) ? 1 : 0)) && m_pSeekCursor->isAfterLast()) |
2118 | 0 | m_nSeekPos = nRow; |
2119 | 0 | else |
2120 | 0 | { |
2121 | 0 | bool bSuccess = false; |
2122 | 0 | tools::Long nSteps = 0; |
2123 | 0 | try |
2124 | 0 | { |
2125 | 0 | if ( m_pSeekCursor->rowDeleted() ) |
2126 | 0 | { |
2127 | | // somebody deleted the current row of the seek cursor. Move it away from this row. |
2128 | 0 | m_pSeekCursor->next(); |
2129 | 0 | if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() ) |
2130 | 0 | bAbsolute = true; |
2131 | 0 | } |
2132 | |
|
2133 | 0 | if ( !bAbsolute ) |
2134 | 0 | { |
2135 | 0 | DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(), |
2136 | 0 | "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" ); |
2137 | 0 | nSteps = nRow - (m_pSeekCursor->getRow() - 1); |
2138 | 0 | bAbsolute = std::abs(nSteps) > 100; |
2139 | 0 | } |
2140 | |
|
2141 | 0 | if ( bAbsolute ) |
2142 | 0 | { |
2143 | 0 | bSuccess = m_pSeekCursor->absolute(nRow + 1); |
2144 | 0 | if (bSuccess) |
2145 | 0 | m_nSeekPos = nRow; |
2146 | 0 | } |
2147 | 0 | else |
2148 | 0 | { |
2149 | 0 | if (nSteps > 0) // position onto the last needed data set |
2150 | 0 | { |
2151 | 0 | if (m_pSeekCursor->isAfterLast()) |
2152 | 0 | bSuccess = false; |
2153 | 0 | else if (m_pSeekCursor->isBeforeFirst()) |
2154 | 0 | bSuccess = m_pSeekCursor->absolute(nSteps); |
2155 | 0 | else |
2156 | 0 | bSuccess = m_pSeekCursor->relative(nSteps); |
2157 | 0 | } |
2158 | 0 | else if (nSteps < 0) |
2159 | 0 | { |
2160 | 0 | if (m_pSeekCursor->isBeforeFirst()) |
2161 | 0 | bSuccess = false; |
2162 | 0 | else if (m_pSeekCursor->isAfterLast()) |
2163 | 0 | bSuccess = m_pSeekCursor->absolute(nSteps); |
2164 | 0 | else |
2165 | 0 | bSuccess = m_pSeekCursor->relative(nSteps); |
2166 | 0 | } |
2167 | 0 | else |
2168 | 0 | { |
2169 | 0 | m_nSeekPos = nRow; |
2170 | 0 | return true; |
2171 | 0 | } |
2172 | 0 | } |
2173 | 0 | } |
2174 | 0 | catch(Exception&) |
2175 | 0 | { |
2176 | 0 | OSL_FAIL("DbGridControl::SeekCursor : failed ..."); |
2177 | 0 | } |
2178 | | |
2179 | 0 | try |
2180 | 0 | { |
2181 | 0 | if (!bSuccess) |
2182 | 0 | { |
2183 | 0 | if (bAbsolute || nSteps > 0) |
2184 | 0 | { |
2185 | 0 | if (m_pSeekCursor->isLast()) |
2186 | 0 | bSuccess = true; |
2187 | 0 | else |
2188 | 0 | bSuccess = m_pSeekCursor->last(); |
2189 | 0 | } |
2190 | 0 | else |
2191 | 0 | { |
2192 | 0 | if (m_pSeekCursor->isFirst()) |
2193 | 0 | bSuccess = true; |
2194 | 0 | else |
2195 | 0 | bSuccess = m_pSeekCursor->first(); |
2196 | 0 | } |
2197 | 0 | } |
2198 | |
|
2199 | 0 | if (bSuccess) |
2200 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
2201 | 0 | else |
2202 | 0 | m_nSeekPos = -1; |
2203 | 0 | } |
2204 | 0 | catch(Exception&) |
2205 | 0 | { |
2206 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
2207 | 0 | OSL_FAIL("DbGridControl::SeekCursor : failed ..."); |
2208 | 0 | m_nSeekPos = -1; // no further data set available |
2209 | 0 | } |
2210 | 0 | } |
2211 | 0 | return m_nSeekPos == nRow; |
2212 | 0 | } |
2213 | | |
2214 | | void DbGridControl::MoveToFirst() |
2215 | 0 | { |
2216 | 0 | if (m_pSeekCursor && (GetCurRow() != 0)) |
2217 | 0 | MoveToPosition(0); |
2218 | 0 | } |
2219 | | |
2220 | | void DbGridControl::MoveToLast() |
2221 | 0 | { |
2222 | 0 | if (!m_pSeekCursor) |
2223 | 0 | return; |
2224 | | |
2225 | 0 | if (m_nTotalCount < 0) // no RecordCount, yet |
2226 | 0 | { |
2227 | 0 | try |
2228 | 0 | { |
2229 | 0 | bool bRes = m_pSeekCursor->last(); |
2230 | |
|
2231 | 0 | if (bRes) |
2232 | 0 | { |
2233 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
2234 | 0 | AdjustRows(); |
2235 | 0 | } |
2236 | 0 | } |
2237 | 0 | catch(Exception&) |
2238 | 0 | { |
2239 | 0 | } |
2240 | 0 | } |
2241 | | |
2242 | | // position onto the last data set not on a blank row |
2243 | 0 | if (m_nOptions & DbGridControlOptions::Insert) |
2244 | 0 | { |
2245 | 0 | if ((GetRowCount() - 1) > 0) |
2246 | 0 | MoveToPosition(GetRowCount() - 2); |
2247 | 0 | } |
2248 | 0 | else if (GetRowCount()) |
2249 | 0 | MoveToPosition(GetRowCount() - 1); |
2250 | 0 | } |
2251 | | |
2252 | | void DbGridControl::MoveToPrev() |
2253 | 0 | { |
2254 | 0 | sal_Int32 nNewRow = std::max(GetCurRow() - 1, sal_Int32(0)); |
2255 | 0 | if (GetCurRow() != nNewRow) |
2256 | 0 | MoveToPosition(nNewRow); |
2257 | 0 | } |
2258 | | |
2259 | | void DbGridControl::MoveToNext() |
2260 | 0 | { |
2261 | 0 | if (!m_pSeekCursor) |
2262 | 0 | return; |
2263 | | |
2264 | 0 | if (m_nTotalCount > 0) |
2265 | 0 | { |
2266 | | // move the data cursor to the right position |
2267 | 0 | tools::Long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1); |
2268 | 0 | if (GetCurRow() != nNewRow) |
2269 | 0 | MoveToPosition(nNewRow); |
2270 | 0 | } |
2271 | 0 | else |
2272 | 0 | { |
2273 | 0 | bool bOk = false; |
2274 | 0 | try |
2275 | 0 | { |
2276 | | // try to move to next row |
2277 | | // when not possible our paint cursor is already on the last row |
2278 | | // then we must be sure that the data cursor is on the position |
2279 | | // we call ourself again |
2280 | 0 | bOk = m_pSeekCursor->next(); |
2281 | 0 | if (bOk) |
2282 | 0 | { |
2283 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
2284 | 0 | MoveToPosition(GetCurRow() + 1); |
2285 | 0 | } |
2286 | 0 | } |
2287 | 0 | catch(SQLException &) |
2288 | 0 | { |
2289 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
2290 | 0 | } |
2291 | |
|
2292 | 0 | if(!bOk) |
2293 | 0 | { |
2294 | 0 | AdjustRows(); |
2295 | 0 | if (m_nTotalCount > 0) // only to avoid infinite recursion |
2296 | 0 | MoveToNext(); |
2297 | 0 | } |
2298 | 0 | } |
2299 | 0 | } |
2300 | | |
2301 | | void DbGridControl::MoveToPosition(sal_uInt32 nPos) |
2302 | 0 | { |
2303 | 0 | if (!m_pSeekCursor) |
2304 | 0 | return; |
2305 | | |
2306 | 0 | if (m_nTotalCount < 0 && static_cast<tools::Long>(nPos) >= GetRowCount()) |
2307 | 0 | { |
2308 | 0 | try |
2309 | 0 | { |
2310 | 0 | if (!m_pSeekCursor->absolute(nPos + 1)) |
2311 | 0 | { |
2312 | 0 | AdjustRows(); |
2313 | 0 | return; |
2314 | 0 | } |
2315 | 0 | else |
2316 | 0 | { |
2317 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
2318 | 0 | AdjustRows(); |
2319 | 0 | } |
2320 | 0 | } |
2321 | 0 | catch(Exception&) |
2322 | 0 | { |
2323 | 0 | return; |
2324 | 0 | } |
2325 | 0 | } |
2326 | 0 | EditBrowseBox::GoToRow(nPos); |
2327 | 0 | m_aBar->InvalidateAll(m_nCurrentPos); |
2328 | 0 | } |
2329 | | |
2330 | | void DbGridControl::AppendNew() |
2331 | 0 | { |
2332 | 0 | if (!m_pSeekCursor || !(m_nOptions & DbGridControlOptions::Insert)) |
2333 | 0 | return; |
2334 | | |
2335 | 0 | if (m_nTotalCount < 0) // no RecordCount, yet |
2336 | 0 | { |
2337 | 0 | try |
2338 | 0 | { |
2339 | 0 | bool bRes = m_pSeekCursor->last(); |
2340 | |
|
2341 | 0 | if (bRes) |
2342 | 0 | { |
2343 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
2344 | 0 | AdjustRows(); |
2345 | 0 | } |
2346 | 0 | } |
2347 | 0 | catch(Exception&) |
2348 | 0 | { |
2349 | 0 | return; |
2350 | 0 | } |
2351 | 0 | } |
2352 | | |
2353 | 0 | tools::Long nNewRow = m_nTotalCount + 1; |
2354 | 0 | if (nNewRow > 0 && GetCurRow() != nNewRow) |
2355 | 0 | MoveToPosition(nNewRow - 1); |
2356 | 0 | } |
2357 | | |
2358 | | void DbGridControl::SetDesignMode(bool bMode) |
2359 | 0 | { |
2360 | 0 | if (IsDesignMode() == bMode) |
2361 | 0 | return; |
2362 | | |
2363 | | // adjust Enable/Disable for design mode so that the headerbar remains configurable |
2364 | 0 | if (bMode) |
2365 | 0 | { |
2366 | 0 | if (!IsEnabled()) |
2367 | 0 | { |
2368 | 0 | Enable(); |
2369 | 0 | GetDataWindow().Disable(); |
2370 | 0 | } |
2371 | 0 | } |
2372 | 0 | else |
2373 | 0 | { |
2374 | | // disable completely |
2375 | 0 | if (!GetDataWindow().IsEnabled()) |
2376 | 0 | Disable(); |
2377 | 0 | } |
2378 | |
|
2379 | 0 | m_bDesignMode = bMode; |
2380 | 0 | GetDataWindow().SetMouseTransparent(bMode); |
2381 | 0 | SetMouseTransparent(bMode); |
2382 | |
|
2383 | 0 | m_aBar->InvalidateAll(m_nCurrentPos, true); |
2384 | 0 | } |
2385 | | |
2386 | | void DbGridControl::SetFilterMode(bool bMode) |
2387 | 0 | { |
2388 | 0 | if (IsFilterMode() == bMode) |
2389 | 0 | return; |
2390 | | |
2391 | 0 | m_bFilterMode = bMode; |
2392 | |
|
2393 | 0 | if (bMode) |
2394 | 0 | { |
2395 | 0 | SetUpdateMode(false); |
2396 | | |
2397 | | // there is no cursor anymore |
2398 | 0 | if (IsEditing()) |
2399 | 0 | DeactivateCell(); |
2400 | 0 | RemoveRows(false); |
2401 | |
|
2402 | 0 | m_xEmptyRow = new DbGridRow(); |
2403 | | |
2404 | | // setting the new filter controls |
2405 | 0 | for (auto const & pCurCol : m_aColumns) |
2406 | 0 | { |
2407 | 0 | if (!pCurCol->IsHidden()) |
2408 | 0 | pCurCol->UpdateControl(); |
2409 | 0 | } |
2410 | | |
2411 | | // one row for filtering |
2412 | 0 | RowInserted(0); |
2413 | 0 | SetUpdateMode(true); |
2414 | 0 | } |
2415 | 0 | else |
2416 | 0 | setDataSource(Reference< XRowSet > ()); |
2417 | 0 | } |
2418 | | |
2419 | | OUString DbGridControl::GetCellText(sal_Int32 _nRow, sal_uInt16 _nColId) const |
2420 | 0 | { |
2421 | 0 | size_t Location = GetModelColumnPos( _nColId ); |
2422 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
2423 | 0 | OUString sRet; |
2424 | 0 | if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) ) |
2425 | 0 | sRet = GetCurrentRowCellText(pColumn, m_xPaintRow); |
2426 | 0 | return sRet; |
2427 | 0 | } |
2428 | | |
2429 | | OUString DbGridControl::GetCurrentRowCellText(DbGridColumn const * pColumn,const DbGridRowRef& _rRow) const |
2430 | 0 | { |
2431 | | // text output for a single row |
2432 | 0 | OUString aText; |
2433 | 0 | if ( pColumn && IsValid(_rRow) ) |
2434 | 0 | aText = pColumn->GetCellText(_rRow.get(), m_xFormatter); |
2435 | 0 | return aText; |
2436 | 0 | } |
2437 | | |
2438 | | sal_uInt32 DbGridControl::GetTotalCellWidth(sal_Int32 nRow, sal_uInt16 nColId) |
2439 | 0 | { |
2440 | 0 | if (SeekRow(nRow)) |
2441 | 0 | { |
2442 | 0 | size_t Location = GetModelColumnPos( nColId ); |
2443 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
2444 | 0 | return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow)); |
2445 | 0 | } |
2446 | 0 | else |
2447 | 0 | return 30; // FIXME magic number for default cell width |
2448 | 0 | } |
2449 | | |
2450 | | void DbGridControl::PreExecuteRowContextMenu(weld::Menu& rMenu) |
2451 | 0 | { |
2452 | 0 | bool bDelete = (m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount() && !IsCurrentAppending(); |
2453 | | // if only a blank row is selected then do not delete |
2454 | 0 | bDelete = bDelete && !((m_nOptions & DbGridControlOptions::Insert) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1)); |
2455 | |
|
2456 | 0 | rMenu.set_visible(u"delete"_ustr, bDelete); |
2457 | 0 | rMenu.set_visible(u"save"_ustr, IsModified()); |
2458 | | |
2459 | | // the undo is more difficult |
2460 | 0 | bool bCanUndo = IsModified(); |
2461 | 0 | int nState = -1; |
2462 | 0 | if (m_aMasterStateProvider.IsSet()) |
2463 | 0 | nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo); |
2464 | 0 | bCanUndo &= ( 0 != nState ); |
2465 | |
|
2466 | 0 | rMenu.set_visible(u"undo"_ustr, bCanUndo); |
2467 | 0 | } |
2468 | | |
2469 | | void DbGridControl::PostExecuteRowContextMenu(const OUString& rExecutionResult) |
2470 | 0 | { |
2471 | 0 | if (rExecutionResult == "delete") |
2472 | 0 | { |
2473 | | // delete asynchronously |
2474 | 0 | if (m_nDeleteEvent) |
2475 | 0 | Application::RemoveUserEvent(m_nDeleteEvent); |
2476 | 0 | m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true); |
2477 | 0 | } |
2478 | 0 | else if (rExecutionResult == "undo") |
2479 | 0 | Undo(); |
2480 | 0 | else if (rExecutionResult == "save") |
2481 | 0 | SaveRow(); |
2482 | 0 | } |
2483 | | |
2484 | | void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt) |
2485 | 0 | { |
2486 | 0 | SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged"); |
2487 | 0 | SolarMutexGuard aGuard; |
2488 | | // prop "IsModified" changed ? |
2489 | | // during update don't care about the modified state |
2490 | 0 | if (IsUpdating() || evt.PropertyName != FM_PROP_ISMODIFIED) |
2491 | 0 | return; |
2492 | | |
2493 | 0 | Reference< XPropertySet > xSource(evt.Source, UNO_QUERY); |
2494 | 0 | DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" ); |
2495 | 0 | bool bIsNew = false; |
2496 | 0 | if (xSource.is()) |
2497 | 0 | bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW)); |
2498 | |
|
2499 | 0 | if (bIsNew && m_xCurrentRow.is()) |
2500 | 0 | { |
2501 | 0 | 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 !"); |
2502 | 0 | sal_Int32 nRecordCount = 0; |
2503 | 0 | xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount; |
2504 | 0 | if (::comphelper::getBOOL(evt.NewValue)) |
2505 | 0 | { // modified state changed from sal_False to sal_True and we're on an insert row |
2506 | | // -> we've to add a new grid row |
2507 | 0 | if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew()) |
2508 | 0 | { |
2509 | 0 | RowInserted(GetRowCount()); |
2510 | 0 | InvalidateStatusCell(m_nCurrentPos); |
2511 | 0 | m_aBar->InvalidateAll(m_nCurrentPos); |
2512 | 0 | } |
2513 | 0 | } |
2514 | 0 | else |
2515 | 0 | { // modified state changed from sal_True to sal_False and we're on an insert row |
2516 | | // we have two "new row"s at the moment : the one we're editing currently (where the current |
2517 | | // column is the only dirty element) and a "new new" row which is completely clean. As the first |
2518 | | // one is about to be cleaned, too, the second one is obsolete now. |
2519 | 0 | if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2)) |
2520 | 0 | { |
2521 | 0 | RowRemoved(GetRowCount() - 1); |
2522 | 0 | InvalidateStatusCell(m_nCurrentPos); |
2523 | 0 | m_aBar->InvalidateAll(m_nCurrentPos); |
2524 | 0 | } |
2525 | 0 | } |
2526 | 0 | } |
2527 | 0 | if (m_xCurrentRow.is()) |
2528 | 0 | { |
2529 | 0 | m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean); |
2530 | 0 | m_xCurrentRow->SetNew( bIsNew ); |
2531 | 0 | InvalidateStatusCell(m_nCurrentPos); |
2532 | 0 | SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow)); |
2533 | 0 | } |
2534 | 0 | } |
2535 | | |
2536 | | void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel ) |
2537 | 0 | { |
2538 | 0 | if (!m_pSeekCursor || IsResizing()) |
2539 | 0 | return; |
2540 | | |
2541 | 0 | sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rPosPixel.X())); |
2542 | 0 | tools::Long nRow = GetRowAtYPosPixel(rPosPixel.Y()); |
2543 | 0 | if (nColId != HandleColumnId && nRow >= 0) |
2544 | 0 | { |
2545 | 0 | if (GetDataWindow().IsMouseCaptured()) |
2546 | 0 | GetDataWindow().ReleaseMouse(); |
2547 | |
|
2548 | 0 | size_t Location = GetModelColumnPos( nColId ); |
2549 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
2550 | 0 | rtl::Reference<OStringTransferable> pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow)); |
2551 | 0 | pTransferable->StartDrag(this, DND_ACTION_COPY); |
2552 | 0 | } |
2553 | 0 | } |
2554 | | |
2555 | | bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_uInt16 _nColId) |
2556 | 0 | { |
2557 | 0 | return (_nRow >= 0) |
2558 | 0 | && (_nRow < GetRowCount()) |
2559 | 0 | && (_nColId != HandleColumnId) |
2560 | 0 | && (GetModelColumnPos(_nColId) != GRID_COLUMN_NOT_FOUND); |
2561 | 0 | } |
2562 | | |
2563 | | void DbGridControl::copyCellText(sal_Int32 _nRow, sal_uInt16 _nColId) |
2564 | 0 | { |
2565 | 0 | DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!"); |
2566 | 0 | DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ].get(); |
2567 | 0 | SeekRow(_nRow); |
2568 | 0 | OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this ); |
2569 | 0 | } |
2570 | | |
2571 | | void DbGridControl::executeRowContextMenu(const Point& _rPreferredPos) |
2572 | 0 | { |
2573 | 0 | std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, u"svx/ui/rowsmenu.ui"_ustr)); |
2574 | 0 | std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr)); |
2575 | |
|
2576 | 0 | tools::Rectangle aRect(_rPreferredPos, Size(1,1)); |
2577 | 0 | weld::Window* pParent = weld::GetPopupParent(*this, aRect); |
2578 | |
|
2579 | 0 | PreExecuteRowContextMenu(*xContextMenu); |
2580 | 0 | PostExecuteRowContextMenu(xContextMenu->popup_at_rect(pParent, aRect)); |
2581 | 0 | } |
2582 | | |
2583 | | void DbGridControl::Command(const CommandEvent& rEvt) |
2584 | 0 | { |
2585 | 0 | switch (rEvt.GetCommand()) |
2586 | 0 | { |
2587 | 0 | case CommandEventId::ContextMenu: |
2588 | 0 | { |
2589 | 0 | if ( !m_pSeekCursor ) |
2590 | 0 | { |
2591 | 0 | EditBrowseBox::Command(rEvt); |
2592 | 0 | return; |
2593 | 0 | } |
2594 | | |
2595 | 0 | if ( !rEvt.IsMouseEvent() ) |
2596 | 0 | { // context menu requested by keyboard |
2597 | 0 | if ( GetSelectRowCount() ) |
2598 | 0 | { |
2599 | 0 | tools::Long nRow = FirstSelectedRow( ); |
2600 | |
|
2601 | 0 | ::tools::Rectangle aRowRect( GetRowRectPixel( nRow ) ); |
2602 | 0 | executeRowContextMenu(aRowRect.LeftCenter()); |
2603 | | |
2604 | | // handled |
2605 | 0 | return; |
2606 | 0 | } |
2607 | 0 | } |
2608 | | |
2609 | 0 | sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X())); |
2610 | 0 | tools::Long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y()); |
2611 | |
|
2612 | 0 | if (nColId == HandleColumnId) |
2613 | 0 | { |
2614 | 0 | executeRowContextMenu(rEvt.GetMousePosPixel()); |
2615 | 0 | } |
2616 | 0 | else if (canCopyCellText(nRow, nColId)) |
2617 | 0 | { |
2618 | 0 | ::tools::Rectangle aRect(rEvt.GetMousePosPixel(), Size(1, 1)); |
2619 | 0 | weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect); |
2620 | 0 | std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, u"svx/ui/cellmenu.ui"_ustr)); |
2621 | 0 | std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr)); |
2622 | 0 | if (!xContextMenu->popup_at_rect(pPopupParent, aRect).isEmpty()) |
2623 | 0 | copyCellText(nRow, nColId); |
2624 | 0 | } |
2625 | 0 | else |
2626 | 0 | { |
2627 | 0 | EditBrowseBox::Command(rEvt); |
2628 | 0 | return; |
2629 | 0 | } |
2630 | | |
2631 | 0 | [[fallthrough]]; |
2632 | 0 | } |
2633 | 0 | default: |
2634 | 0 | EditBrowseBox::Command(rEvt); |
2635 | 0 | } |
2636 | 0 | } |
2637 | | |
2638 | | IMPL_LINK_NOARG(DbGridControl, OnDelete, void*, void) |
2639 | 0 | { |
2640 | 0 | m_nDeleteEvent = nullptr; |
2641 | 0 | DeleteSelectedRows(); |
2642 | 0 | } |
2643 | | |
2644 | | IMPL_LINK_NOARG(DbGridControl, RearrangeHdl, Timer*, void) |
2645 | 0 | { |
2646 | 0 | if (isDisposed()) |
2647 | 0 | return; |
2648 | | |
2649 | | // and give it a chance to rearrange |
2650 | 0 | Point aPoint = GetControlArea().TopLeft(); |
2651 | 0 | sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X()); |
2652 | 0 | ArrangeControls(nX, aPoint.Y()); |
2653 | | // tdf#155364 like tdf#97731 if the reserved area changed size, give |
2654 | | // the controls a chance to adapt to the new size |
2655 | 0 | bool bChanged = ReserveControlArea(nX); |
2656 | 0 | if (bChanged) |
2657 | 0 | { |
2658 | 0 | ArrangeControls(nX, aPoint.Y()); |
2659 | 0 | Invalidate(); |
2660 | 0 | } |
2661 | 0 | } |
2662 | | |
2663 | | void DbGridControl::DeleteSelectedRows() |
2664 | 0 | { |
2665 | 0 | DBG_ASSERT(GetSelection(), "no selection!!!"); |
2666 | |
|
2667 | 0 | if (!m_pSeekCursor) |
2668 | 0 | return; |
2669 | 0 | } |
2670 | | |
2671 | | CellController* DbGridControl::GetController(sal_Int32 /*nRow*/, sal_uInt16 nColumnId) |
2672 | 0 | { |
2673 | 0 | if (!IsValid(m_xCurrentRow) || !IsEnabled()) |
2674 | 0 | return nullptr; |
2675 | | |
2676 | 0 | size_t Location = GetModelColumnPos(nColumnId); |
2677 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
2678 | 0 | if (!pColumn) |
2679 | 0 | return nullptr; |
2680 | | |
2681 | 0 | CellController* pReturn = nullptr; |
2682 | 0 | if (IsFilterMode()) |
2683 | 0 | pReturn = pColumn->GetController().get(); |
2684 | 0 | else |
2685 | 0 | { |
2686 | 0 | if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel())) |
2687 | 0 | { |
2688 | 0 | if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED))) |
2689 | 0 | return nullptr; |
2690 | 0 | } |
2691 | | |
2692 | 0 | bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Insert)); |
2693 | 0 | bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Update)); |
2694 | |
|
2695 | 0 | if ((bInsert && !pColumn->IsAutoValue()) || bUpdate) |
2696 | 0 | { |
2697 | 0 | pReturn = pColumn->GetController().get(); |
2698 | 0 | } |
2699 | 0 | } |
2700 | 0 | return pReturn; |
2701 | 0 | } |
2702 | | |
2703 | | void DbGridControl::CellModified() |
2704 | 0 | { |
2705 | 0 | SAL_INFO("svx.fmcomp", "DbGridControl::CellModified"); |
2706 | | |
2707 | 0 | { |
2708 | 0 | ::osl::MutexGuard aGuard(m_aAdjustSafety); |
2709 | 0 | if (m_nAsynAdjustEvent) |
2710 | 0 | { |
2711 | 0 | SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource")); |
2712 | 0 | RemoveUserEvent(m_nAsynAdjustEvent); |
2713 | 0 | m_nAsynAdjustEvent = nullptr; |
2714 | | |
2715 | | // force the call : this should be no problem as we're probably running in the solar thread here |
2716 | | // (cell modified is triggered by user actions) |
2717 | 0 | if (m_bPendingAdjustRows) |
2718 | 0 | AdjustRows(); |
2719 | 0 | else |
2720 | 0 | AdjustDataSource(); |
2721 | 0 | } |
2722 | 0 | } |
2723 | | |
2724 | 0 | if (IsFilterMode() || !IsValid(m_xCurrentRow) || m_xCurrentRow->IsModified()) |
2725 | 0 | return; |
2726 | | |
2727 | | // enable edit mode |
2728 | | // a data set should be inserted |
2729 | 0 | if (m_xCurrentRow->IsNew()) |
2730 | 0 | { |
2731 | 0 | m_xCurrentRow->SetStatus(GridRowStatus::Modified); |
2732 | 0 | SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED"); |
2733 | | // if no row was added yet, do it now |
2734 | 0 | if (m_nCurrentPos == GetRowCount() - 1) |
2735 | 0 | { |
2736 | | // increment RowCount |
2737 | 0 | RowInserted(GetRowCount()); |
2738 | 0 | InvalidateStatusCell(m_nCurrentPos); |
2739 | 0 | m_aBar->InvalidateAll(m_nCurrentPos); |
2740 | 0 | } |
2741 | 0 | } |
2742 | 0 | else if (m_xCurrentRow->GetStatus() != GridRowStatus::Modified) |
2743 | 0 | { |
2744 | 0 | m_xCurrentRow->SetState(m_pDataCursor.get(), false); |
2745 | 0 | SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow)); |
2746 | 0 | m_xCurrentRow->SetStatus(GridRowStatus::Modified); |
2747 | 0 | SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED"); |
2748 | 0 | InvalidateStatusCell(m_nCurrentPos); |
2749 | 0 | } |
2750 | 0 | } |
2751 | | |
2752 | | void DbGridControl::Dispatch(BrowserDispatchId eId) |
2753 | 0 | { |
2754 | 0 | if (eId == BrowserDispatchId::CURSORENDOFFILE) |
2755 | 0 | { |
2756 | 0 | if (m_nOptions & DbGridControlOptions::Insert) |
2757 | 0 | AppendNew(); |
2758 | 0 | else |
2759 | 0 | MoveToLast(); |
2760 | 0 | } |
2761 | 0 | else |
2762 | 0 | EditBrowseBox::Dispatch(eId); |
2763 | 0 | } |
2764 | | |
2765 | | void DbGridControl::Undo() |
2766 | 0 | { |
2767 | 0 | if (IsFilterMode() || !IsValid(m_xCurrentRow) || !IsModified()) |
2768 | 0 | return; |
2769 | | |
2770 | | // check if we have somebody doin' the UNDO for us |
2771 | 0 | int nState = -1; |
2772 | 0 | if (m_aMasterStateProvider.IsSet()) |
2773 | 0 | nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo); |
2774 | 0 | if (nState>0) |
2775 | 0 | { // yes, we have, and the slot is enabled |
2776 | 0 | DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?"); |
2777 | 0 | bool lResult = m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Undo); |
2778 | 0 | if (lResult) |
2779 | | // handled |
2780 | 0 | return; |
2781 | 0 | } |
2782 | 0 | else if (nState == 0) |
2783 | | // yes, we have, and the slot is disabled |
2784 | 0 | return; |
2785 | | |
2786 | 0 | BeginCursorAction(); |
2787 | |
|
2788 | 0 | bool bAppending = m_xCurrentRow->IsNew(); |
2789 | 0 | bool bDirty = m_xCurrentRow->IsModified(); |
2790 | |
|
2791 | 0 | try |
2792 | 0 | { |
2793 | | // cancel editing |
2794 | 0 | Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY); |
2795 | | // no effects if we're not updating currently |
2796 | 0 | if (bAppending) |
2797 | | // just refresh the row |
2798 | 0 | xUpdateCursor->moveToInsertRow(); |
2799 | 0 | else |
2800 | 0 | xUpdateCursor->cancelRowUpdates(); |
2801 | |
|
2802 | 0 | } |
2803 | 0 | catch(Exception&) |
2804 | 0 | { |
2805 | 0 | DBG_UNHANDLED_EXCEPTION("svx"); |
2806 | 0 | } |
2807 | |
|
2808 | 0 | EndCursorAction(); |
2809 | |
|
2810 | 0 | m_xDataRow->SetState(m_pDataCursor.get(), false); |
2811 | 0 | if (m_xPaintRow == m_xCurrentRow) |
2812 | 0 | m_xPaintRow = m_xCurrentRow = m_xDataRow; |
2813 | 0 | else |
2814 | 0 | m_xCurrentRow = m_xDataRow; |
2815 | |
|
2816 | 0 | if (bAppending && (EditBrowseBox::IsModified() || bDirty)) |
2817 | | // remove the row |
2818 | 0 | if (m_nCurrentPos == GetRowCount() - 2) |
2819 | 0 | { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow |
2820 | | // caused our data source form to be reset - which should be the usual case...) |
2821 | 0 | RowRemoved(GetRowCount() - 1); |
2822 | 0 | m_aBar->InvalidateAll(m_nCurrentPos); |
2823 | 0 | } |
2824 | |
|
2825 | 0 | RowModified(m_nCurrentPos); |
2826 | 0 | } |
2827 | | |
2828 | | void DbGridControl::resetCurrentRow() |
2829 | 0 | { |
2830 | 0 | if (IsModified()) |
2831 | 0 | { |
2832 | | // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which |
2833 | | // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of |
2834 | | // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the |
2835 | | // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we |
2836 | | // would never delete the obsolete "second insert row". Thus in this special case this method here |
2837 | | // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the |
2838 | | // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now) |
2839 | 0 | Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet(); |
2840 | 0 | if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED))) |
2841 | 0 | { |
2842 | | // are we on a new row currently ? |
2843 | 0 | if (m_xCurrentRow->IsNew()) |
2844 | 0 | { |
2845 | 0 | if (m_nCurrentPos == GetRowCount() - 2) |
2846 | 0 | { |
2847 | 0 | RowRemoved(GetRowCount() - 1); |
2848 | 0 | m_aBar->InvalidateAll(m_nCurrentPos); |
2849 | 0 | } |
2850 | 0 | } |
2851 | 0 | } |
2852 | | |
2853 | | // update the rows |
2854 | 0 | m_xDataRow->SetState(m_pDataCursor.get(), false); |
2855 | 0 | if (m_xPaintRow == m_xCurrentRow) |
2856 | 0 | m_xPaintRow = m_xCurrentRow = m_xDataRow; |
2857 | 0 | else |
2858 | 0 | m_xCurrentRow = m_xDataRow; |
2859 | 0 | } |
2860 | |
|
2861 | 0 | RowModified(GetCurRow()); // will update the current controller if affected |
2862 | 0 | } |
2863 | | |
2864 | | void DbGridControl::RowModified( sal_Int32 nRow ) |
2865 | 0 | { |
2866 | 0 | if (nRow == m_nCurrentPos && IsEditing()) |
2867 | 0 | { |
2868 | 0 | CellControllerRef aTmpRef = Controller(); |
2869 | 0 | aTmpRef->SaveValue(); |
2870 | 0 | InitController(aTmpRef, m_nCurrentPos, GetCurColumnId()); |
2871 | 0 | } |
2872 | 0 | EditBrowseBox::RowModified(nRow); |
2873 | 0 | } |
2874 | | |
2875 | | bool DbGridControl::IsModified() const |
2876 | 0 | { |
2877 | 0 | return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || EditBrowseBox::IsModified()); |
2878 | 0 | } |
2879 | | |
2880 | | bool DbGridControl::IsCurrentAppending() const |
2881 | 0 | { |
2882 | 0 | return m_xCurrentRow.is() && m_xCurrentRow->IsNew(); |
2883 | 0 | } |
2884 | | |
2885 | | bool DbGridControl::IsInsertionRow(sal_Int32 nRow) const |
2886 | 0 | { |
2887 | 0 | return (m_nOptions & DbGridControlOptions::Insert) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1); |
2888 | 0 | } |
2889 | | |
2890 | | bool DbGridControl::SaveModified() |
2891 | 0 | { |
2892 | 0 | SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified"); |
2893 | 0 | DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row"); |
2894 | 0 | if (!IsValid(m_xCurrentRow)) |
2895 | 0 | return true; |
2896 | | |
2897 | | // accept input for this field |
2898 | | // Where there changes at the current input field? |
2899 | 0 | if (!EditBrowseBox::IsModified()) |
2900 | 0 | return true; |
2901 | | |
2902 | 0 | size_t Location = GetModelColumnPos( GetCurColumnId() ); |
2903 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
2904 | 0 | bool bOK = pColumn && pColumn->Commit(); |
2905 | 0 | DBG_ASSERT( Controller().is(), "DbGridControl::SaveModified: was modified, by have no controller?!" ); |
2906 | 0 | if ( !Controller().is() ) |
2907 | | // this might happen if the callbacks implicitly triggered by Commit |
2908 | | // fiddled with the form or the control ... |
2909 | | // (Note that this here is a workaround, at most. We need a general concept how |
2910 | | // to treat this, you can imagine an arbitrary number of scenarios where a callback |
2911 | | // triggers something which leaves us in an expected state.) |
2912 | | // #i67147# / 2006-07-17 / frank.schoenheit@sun.com |
2913 | 0 | return bOK; |
2914 | | |
2915 | 0 | if (bOK) |
2916 | 0 | { |
2917 | 0 | Controller()->SaveValue(); |
2918 | |
|
2919 | 0 | if ( IsValid(m_xCurrentRow) ) |
2920 | 0 | { |
2921 | 0 | m_xCurrentRow->SetState(m_pDataCursor.get(), false); |
2922 | 0 | SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow)); |
2923 | 0 | InvalidateStatusCell( m_nCurrentPos ); |
2924 | 0 | } |
2925 | 0 | else |
2926 | 0 | { |
2927 | 0 | SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow)); |
2928 | 0 | } |
2929 | 0 | } |
2930 | | |
2931 | 0 | return bOK; |
2932 | 0 | } |
2933 | | |
2934 | | bool DbGridControl::SaveRow() |
2935 | 0 | { |
2936 | 0 | SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow"); |
2937 | | // valid row |
2938 | 0 | if (!IsValid(m_xCurrentRow) || !IsModified()) |
2939 | 0 | return true; |
2940 | | // value of the controller was not saved, yet |
2941 | 0 | else if (Controller().is() && Controller()->IsValueChangedFromSaved()) |
2942 | 0 | { |
2943 | 0 | if (!SaveModified()) |
2944 | 0 | return false; |
2945 | 0 | } |
2946 | 0 | m_bUpdating = true; |
2947 | |
|
2948 | 0 | BeginCursorAction(); |
2949 | 0 | bool bAppending = m_xCurrentRow->IsNew(); |
2950 | 0 | bool bSuccess = false; |
2951 | 0 | try |
2952 | 0 | { |
2953 | 0 | Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY); |
2954 | 0 | if (bAppending) |
2955 | 0 | xUpdateCursor->insertRow(); |
2956 | 0 | else |
2957 | 0 | xUpdateCursor->updateRow(); |
2958 | 0 | bSuccess = true; |
2959 | 0 | } |
2960 | 0 | catch(SQLException&) |
2961 | 0 | { |
2962 | 0 | EndCursorAction(); |
2963 | 0 | m_bUpdating = false; |
2964 | 0 | return false; |
2965 | 0 | } |
2966 | | |
2967 | 0 | try |
2968 | 0 | { |
2969 | 0 | if (bSuccess) |
2970 | 0 | { |
2971 | | // if we are appending we still sit on the insert row |
2972 | | // we don't move just clear the flags not to move on the current row |
2973 | 0 | m_xCurrentRow->SetState(m_pDataCursor.get(), false); |
2974 | 0 | SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow)); |
2975 | 0 | m_xCurrentRow->SetNew(false); |
2976 | | |
2977 | | // adjust the seekcursor if it is on the same position as the datacursor |
2978 | 0 | if (m_nSeekPos == m_nCurrentPos || bAppending) |
2979 | 0 | { |
2980 | | // get the bookmark to refetch the data |
2981 | | // in insert mode we take the new bookmark of the data cursor |
2982 | 0 | Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark(); |
2983 | 0 | m_pSeekCursor->moveToBookmark(aBookmark); |
2984 | | // get the data |
2985 | 0 | m_xSeekRow->SetState(m_pSeekCursor.get(), true); |
2986 | 0 | m_nSeekPos = m_pSeekCursor->getRow() - 1; |
2987 | 0 | } |
2988 | 0 | } |
2989 | | // and repaint the row |
2990 | 0 | RowModified(m_nCurrentPos); |
2991 | 0 | } |
2992 | 0 | catch(Exception&) |
2993 | 0 | { |
2994 | 0 | } |
2995 | | |
2996 | 0 | m_bUpdating = false; |
2997 | 0 | EndCursorAction(); |
2998 | | |
2999 | | // The old code returned (nRecords != 0) here. |
3000 | | // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown, |
3001 | | // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords |
3002 | | // is zero, this simply means all fields had their original values. |
3003 | | // FS - 06.12.99 - 70502 |
3004 | 0 | return true; |
3005 | 0 | } |
3006 | | |
3007 | | bool DbGridControl::PreNotify(NotifyEvent& rEvt) |
3008 | 0 | { |
3009 | | // do not handle events of the Navbar |
3010 | 0 | if (m_aBar->IsWindowOrChild(rEvt.GetWindow())) |
3011 | 0 | return BrowseBox::PreNotify(rEvt); |
3012 | | |
3013 | 0 | switch (rEvt.GetType()) |
3014 | 0 | { |
3015 | 0 | case NotifyEventType::KEYINPUT: |
3016 | 0 | { |
3017 | 0 | const KeyEvent* pKeyEvent = rEvt.GetKeyEvent(); |
3018 | |
|
3019 | 0 | sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode(); |
3020 | 0 | bool bShift = pKeyEvent->GetKeyCode().IsShift(); |
3021 | 0 | bool bCtrl = pKeyEvent->GetKeyCode().IsMod1(); |
3022 | 0 | bool bAlt = pKeyEvent->GetKeyCode().IsMod2(); |
3023 | 0 | if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt ) |
3024 | 0 | { |
3025 | | // Ctrl-Tab is used to step out of the control, without traveling to the |
3026 | | // remaining cells first |
3027 | | // -> build a new key event without the Ctrl-key, and let the very base class handle it |
3028 | 0 | vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false ); |
3029 | 0 | KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode ); |
3030 | | |
3031 | | // call the Control - our direct base class will interpret this in a way we do not want (and do |
3032 | | // a cell traveling) |
3033 | 0 | Control::KeyInput( aNewEvent ); |
3034 | 0 | return true; |
3035 | 0 | } |
3036 | | |
3037 | 0 | if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) ) |
3038 | 0 | { |
3039 | 0 | if (IsModified()) |
3040 | 0 | { |
3041 | 0 | Undo(); |
3042 | 0 | return true; |
3043 | 0 | } |
3044 | 0 | } |
3045 | 0 | else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows |
3046 | 0 | { |
3047 | 0 | if ((m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount()) |
3048 | 0 | { |
3049 | | // delete asynchronously |
3050 | 0 | if (m_nDeleteEvent) |
3051 | 0 | Application::RemoveUserEvent(m_nDeleteEvent); |
3052 | 0 | m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true); |
3053 | 0 | return true; |
3054 | 0 | } |
3055 | 0 | } |
3056 | | |
3057 | 0 | [[fallthrough]]; |
3058 | 0 | } |
3059 | 0 | default: |
3060 | 0 | return EditBrowseBox::PreNotify(rEvt); |
3061 | 0 | } |
3062 | 0 | } |
3063 | | |
3064 | | bool DbGridControl::IsTabAllowed(bool bRight) const |
3065 | 0 | { |
3066 | 0 | if (bRight) |
3067 | | // Tab only if not on the _last_ row |
3068 | 0 | return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal || |
3069 | 0 | GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1); |
3070 | 0 | else |
3071 | 0 | { |
3072 | | // Tab only if not on the _first_ row |
3073 | 0 | return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0); |
3074 | 0 | } |
3075 | 0 | } |
3076 | | |
3077 | | void DbGridControl::KeyInput( const KeyEvent& rEvt ) |
3078 | 0 | { |
3079 | 0 | if (rEvt.GetKeyCode().GetFunction() == KeyFuncType::COPY) |
3080 | 0 | { |
3081 | 0 | tools::Long nRow = GetCurRow(); |
3082 | 0 | sal_uInt16 nColId = GetCurColumnId(); |
3083 | 0 | if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount()) |
3084 | 0 | { |
3085 | 0 | size_t Location = GetModelColumnPos( nColId ); |
3086 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
3087 | 0 | OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this ); |
3088 | 0 | return; |
3089 | 0 | } |
3090 | 0 | } |
3091 | 0 | EditBrowseBox::KeyInput(rEvt); |
3092 | 0 | } |
3093 | | |
3094 | | void DbGridControl::HideColumn(sal_uInt16 nId) |
3095 | 0 | { |
3096 | 0 | DeactivateCell(); |
3097 | | |
3098 | | // determine the col for the focus to set to after removal |
3099 | 0 | sal_uInt16 nPos = GetViewColumnPos(nId); |
3100 | 0 | sal_uInt16 nNewColId = nPos == (ColCount()-1) |
3101 | 0 | ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous |
3102 | 0 | : GetColumnIdFromViewPos(nPos+1); // take the next |
3103 | |
|
3104 | 0 | tools::Long lCurrentWidth = GetColumnWidth(nId); |
3105 | 0 | EditBrowseBox::RemoveColumn(nId); |
3106 | | // don't use my own RemoveColumn, this would remove it from m_aColumns, too |
3107 | | |
3108 | | // update my model |
3109 | 0 | size_t Location = GetModelColumnPos( nId ); |
3110 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
3111 | 0 | DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !"); |
3112 | 0 | if (pColumn) |
3113 | 0 | { |
3114 | 0 | pColumn->m_bHidden = true; |
3115 | 0 | pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth); |
3116 | 0 | } |
3117 | | |
3118 | | // and reset the focus |
3119 | 0 | if ( nId == GetCurColumnId() ) |
3120 | 0 | GoToColumnId( nNewColId ); |
3121 | 0 | } |
3122 | | |
3123 | | void DbGridControl::ShowColumn(sal_uInt16 nId) |
3124 | 0 | { |
3125 | 0 | sal_uInt16 nPos = GetModelColumnPos(nId); |
3126 | 0 | DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !"); |
3127 | 0 | if (nPos == GRID_COLUMN_NOT_FOUND) |
3128 | 0 | return; |
3129 | | |
3130 | 0 | DbGridColumn* pColumn = m_aColumns[ nPos ].get(); |
3131 | 0 | if (!pColumn->IsHidden()) |
3132 | 0 | { |
3133 | 0 | DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !"); |
3134 | | // if the column isn't marked as hidden, it should be visible, shouldn't it ? |
3135 | 0 | return; |
3136 | 0 | } |
3137 | 0 | DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !"); |
3138 | | // the opposite situation ... |
3139 | | |
3140 | | // to determine the new view position we need an adjacent non-hidden column |
3141 | 0 | sal_uInt16 nNextNonHidden = BROWSER_INVALIDID; |
3142 | | // first search the cols to the right |
3143 | 0 | for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i ) |
3144 | 0 | { |
3145 | 0 | DbGridColumn* pCurCol = m_aColumns[ i ].get(); |
3146 | 0 | if (!pCurCol->IsHidden()) |
3147 | 0 | { |
3148 | 0 | nNextNonHidden = i; |
3149 | 0 | break; |
3150 | 0 | } |
3151 | 0 | } |
3152 | 0 | if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0)) |
3153 | 0 | { |
3154 | | // then to the left |
3155 | 0 | for ( size_t i = nPos; i > 0; --i ) |
3156 | 0 | { |
3157 | 0 | DbGridColumn* pCurCol = m_aColumns[ i-1 ].get(); |
3158 | 0 | if (!pCurCol->IsHidden()) |
3159 | 0 | { |
3160 | 0 | nNextNonHidden = i-1; |
3161 | 0 | break; |
3162 | 0 | } |
3163 | 0 | } |
3164 | 0 | } |
3165 | 0 | sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID) |
3166 | 0 | ? 1 // there is no visible column -> insert behind the handle col |
3167 | 0 | : GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1; |
3168 | | // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects |
3169 | | // a position 1 for the first non-handle col -> +1 |
3170 | 0 | DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !"); |
3171 | | // we found a col marked as visible but got no view pos for it ... |
3172 | |
|
3173 | 0 | if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID)) |
3174 | | // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos |
3175 | 0 | ++nNewViewPos; |
3176 | |
|
3177 | 0 | DeactivateCell(); |
3178 | |
|
3179 | 0 | OUString aName; |
3180 | 0 | pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName; |
3181 | 0 | InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HeaderBarItemBits::CENTER | HeaderBarItemBits::CLICKABLE, nNewViewPos); |
3182 | 0 | pColumn->m_bHidden = false; |
3183 | |
|
3184 | 0 | ActivateCell(); |
3185 | 0 | Invalidate(); |
3186 | 0 | } |
3187 | | |
3188 | | sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const |
3189 | 0 | { |
3190 | 0 | if (nPos >= m_aColumns.size()) |
3191 | 0 | { |
3192 | 0 | OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !"); |
3193 | 0 | return GRID_COLUMN_NOT_FOUND; |
3194 | 0 | } |
3195 | | |
3196 | 0 | DbGridColumn* pCol = m_aColumns[ nPos ].get(); |
3197 | | #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL |
3198 | | // in the debug version, we convert the ModelPos into a ViewPos and compare this with the |
3199 | | // value we will return (nId at the corresponding Col in m_aColumns) |
3200 | | |
3201 | | if (!pCol->IsHidden()) |
3202 | | { // makes sense only if the column is visible |
3203 | | sal_uInt16 nViewPos = nPos; |
3204 | | for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i) |
3205 | | if ( m_aColumns[ i ]->IsHidden()) |
3206 | | --nViewPos; |
3207 | | |
3208 | | DBG_ASSERT(GetColumnIdFromViewPos(nViewPos) == pCol->GetId(), |
3209 | | "DbGridControl::GetColumnIdFromModelPos : this isn't consistent... did I misunderstand something ?"); |
3210 | | } |
3211 | | #endif |
3212 | 0 | return pCol->GetId(); |
3213 | 0 | } |
3214 | | |
3215 | | sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const |
3216 | 0 | { |
3217 | 0 | for ( size_t i = 0; i < m_aColumns.size(); ++i ) |
3218 | 0 | if ( m_aColumns[ i ]->GetId() == nId ) |
3219 | 0 | return i; |
3220 | | |
3221 | 0 | return GRID_COLUMN_NOT_FOUND; |
3222 | 0 | } |
3223 | | |
3224 | | void DbGridControl::implAdjustInSolarThread(bool _bRows) |
3225 | 0 | { |
3226 | 0 | SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread"); |
3227 | 0 | ::osl::MutexGuard aGuard(m_aAdjustSafety); |
3228 | 0 | if (!Application::IsMainThread()) |
3229 | 0 | { |
3230 | 0 | m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ), true); |
3231 | 0 | m_bPendingAdjustRows = _bRows; |
3232 | 0 | if (_bRows) |
3233 | 0 | SAL_INFO("svx.fmcomp", "posting an AdjustRows"); |
3234 | 0 | else |
3235 | 0 | SAL_INFO("svx.fmcomp", "posting an AdjustDataSource"); |
3236 | 0 | } |
3237 | 0 | else |
3238 | 0 | { |
3239 | 0 | if (_bRows) |
3240 | 0 | SAL_INFO("svx.fmcomp", "doing an AdjustRows"); |
3241 | 0 | else |
3242 | 0 | SAL_INFO("svx.fmcomp", "doing an AdjustDataSource"); |
3243 | | // always adjust the rows before adjusting the data source |
3244 | | // If this is not necessary (because the row count did not change), nothing is done |
3245 | | // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved |
3246 | | // to a position behind row count know 'til now, the cursorMoved notification may come before the |
3247 | | // RowCountChanged notification |
3248 | | // 94093 - 02.11.2001 - frank.schoenheit@sun.com |
3249 | 0 | AdjustRows(); |
3250 | |
|
3251 | 0 | if ( !_bRows ) |
3252 | 0 | AdjustDataSource(); |
3253 | 0 | } |
3254 | 0 | } |
3255 | | |
3256 | | IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat, void) |
3257 | 0 | { |
3258 | 0 | m_nAsynAdjustEvent = nullptr; |
3259 | |
|
3260 | 0 | AdjustRows(); |
3261 | | // see implAdjustInSolarThread for a comment why we do this every time |
3262 | |
|
3263 | 0 | if ( !pAdjustWhat ) |
3264 | 0 | AdjustDataSource(); |
3265 | 0 | } |
3266 | | |
3267 | | void DbGridControl::BeginCursorAction() |
3268 | 0 | { |
3269 | 0 | for (const auto& rListener : m_aFieldListeners) |
3270 | 0 | { |
3271 | 0 | GridFieldValueListener* pCurrent = rListener.second; |
3272 | 0 | if (pCurrent) |
3273 | 0 | pCurrent->suspend(); |
3274 | 0 | } |
3275 | |
|
3276 | 0 | if (m_pDataSourcePropListener) |
3277 | 0 | m_pDataSourcePropListener->suspend(); |
3278 | 0 | } |
3279 | | |
3280 | | void DbGridControl::EndCursorAction() |
3281 | 0 | { |
3282 | 0 | for (const auto& rListener : m_aFieldListeners) |
3283 | 0 | { |
3284 | 0 | GridFieldValueListener* pCurrent = rListener.second; |
3285 | 0 | if (pCurrent) |
3286 | 0 | pCurrent->resume(); |
3287 | 0 | } |
3288 | |
|
3289 | 0 | if (m_pDataSourcePropListener) |
3290 | 0 | m_pDataSourcePropListener->resume(); |
3291 | 0 | } |
3292 | | |
3293 | | void DbGridControl::ConnectToFields() |
3294 | 0 | { |
3295 | 0 | DBG_ASSERT(m_aFieldListeners.empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !"); |
3296 | |
|
3297 | 0 | for (auto const & pCurrent : m_aColumns) |
3298 | 0 | { |
3299 | 0 | sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND; |
3300 | 0 | if (GRID_COLUMN_NOT_FOUND == nViewPos) |
3301 | 0 | continue; |
3302 | | |
3303 | 0 | Reference< XPropertySet > xField = pCurrent->GetField(); |
3304 | 0 | if (!xField.is()) |
3305 | 0 | continue; |
3306 | | |
3307 | | // column is visible and bound here |
3308 | 0 | GridFieldValueListener*& rpListener = m_aFieldListeners[pCurrent->GetId()]; |
3309 | 0 | DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!"); |
3310 | 0 | rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId()); |
3311 | 0 | } |
3312 | 0 | } |
3313 | | |
3314 | | void DbGridControl::DisconnectFromFields() |
3315 | 0 | { |
3316 | 0 | if (m_aFieldListeners.empty()) |
3317 | 0 | return; |
3318 | | |
3319 | 0 | while (!m_aFieldListeners.empty()) |
3320 | 0 | { |
3321 | 0 | sal_Int32 nOldSize = m_aFieldListeners.size(); |
3322 | 0 | m_aFieldListeners.begin()->second->dispose(); |
3323 | 0 | DBG_ASSERT(nOldSize > static_cast<sal_Int32>(m_aFieldListeners.size()), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !"); |
3324 | 0 | } |
3325 | 0 | } |
3326 | | |
3327 | | void DbGridControl::FieldValueChanged(sal_uInt16 _nId) |
3328 | 0 | { |
3329 | 0 | osl::MutexGuard aPreventDestruction(m_aDestructionSafety); |
3330 | | // needed as this may run in a thread other than the main one |
3331 | 0 | if (GetRowStatus(GetCurRow()) != EditBrowseBox::MODIFIED) |
3332 | | // all other cases are handled elsewhere |
3333 | 0 | return; |
3334 | | |
3335 | 0 | size_t Location = GetModelColumnPos( _nId ); |
3336 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
3337 | 0 | if (!pColumn) |
3338 | 0 | return; |
3339 | | |
3340 | 0 | std::unique_ptr<vcl::SolarMutexTryAndBuyGuard> pGuard; |
3341 | 0 | while (!m_bWantDestruction && (!pGuard || !pGuard->isAcquired())) |
3342 | 0 | pGuard.reset(new vcl::SolarMutexTryAndBuyGuard); |
3343 | |
|
3344 | 0 | if (m_bWantDestruction) |
3345 | 0 | { // at this moment, within another thread, our destructor tries to destroy the listener which called this method |
3346 | | // => don't do anything |
3347 | | // 73365 - 23.02.00 - FS |
3348 | 0 | return; |
3349 | 0 | } |
3350 | | |
3351 | | // and finally do the update ... |
3352 | 0 | pColumn->UpdateFromField(m_xCurrentRow.get(), m_xFormatter); |
3353 | 0 | RowModified(GetCurRow()); |
3354 | 0 | } |
3355 | | |
3356 | | void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId) |
3357 | 0 | { |
3358 | 0 | auto aPos = m_aFieldListeners.find(_nId); |
3359 | 0 | if (aPos == m_aFieldListeners.end()) |
3360 | 0 | { |
3361 | 0 | OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !"); |
3362 | 0 | return; |
3363 | 0 | } |
3364 | | |
3365 | 0 | delete aPos->second; |
3366 | |
|
3367 | 0 | m_aFieldListeners.erase(aPos); |
3368 | 0 | } |
3369 | | |
3370 | | void DbGridControl::disposing(sal_uInt16 _nId) |
3371 | 0 | { |
3372 | 0 | if (_nId == 0) |
3373 | 0 | { // the seek cursor is being disposed |
3374 | 0 | ::osl::MutexGuard aGuard(m_aAdjustSafety); |
3375 | 0 | setDataSource(nullptr, DbGridControlOptions::Readonly); // our clone was disposed so we set our datasource to null to avoid later access to it |
3376 | 0 | if (m_nAsynAdjustEvent) |
3377 | 0 | { |
3378 | 0 | RemoveUserEvent(m_nAsynAdjustEvent); |
3379 | 0 | m_nAsynAdjustEvent = nullptr; |
3380 | 0 | } |
3381 | 0 | } |
3382 | 0 | } |
3383 | | |
3384 | | sal_Int32 DbGridControl::GetAccessibleControlCount() const |
3385 | 0 | { |
3386 | 0 | return EditBrowseBox::GetAccessibleControlCount() + 1; // the navigation control |
3387 | 0 | } |
3388 | | |
3389 | | Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex ) |
3390 | 0 | { |
3391 | 0 | Reference<XAccessible > xRet; |
3392 | 0 | if ( _nIndex == EditBrowseBox::GetAccessibleControlCount() ) |
3393 | 0 | { |
3394 | 0 | xRet = m_aBar->GetAccessible(); |
3395 | 0 | } |
3396 | 0 | else |
3397 | 0 | xRet = EditBrowseBox::CreateAccessibleControl( _nIndex ); |
3398 | 0 | return xRet; |
3399 | 0 | } |
3400 | | |
3401 | | Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos ) |
3402 | 0 | { |
3403 | 0 | sal_uInt16 nColumnId = GetColumnId( _nColumnPos ); |
3404 | 0 | size_t Location = GetModelColumnPos(nColumnId); |
3405 | 0 | DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; |
3406 | 0 | if ( pColumn ) |
3407 | 0 | { |
3408 | 0 | Reference< css::awt::XControl> xInt(pColumn->GetCell()); |
3409 | 0 | Reference< css::awt::XCheckBox> xBox(xInt,UNO_QUERY); |
3410 | 0 | if ( xBox.is() ) |
3411 | 0 | { |
3412 | 0 | TriState eValue = TRISTATE_FALSE; |
3413 | 0 | switch( xBox->getState() ) |
3414 | 0 | { |
3415 | 0 | case 0: |
3416 | 0 | eValue = TRISTATE_FALSE; |
3417 | 0 | break; |
3418 | 0 | case 1: |
3419 | 0 | eValue = TRISTATE_TRUE; |
3420 | 0 | break; |
3421 | 0 | case 2: |
3422 | 0 | eValue = TRISTATE_INDET; |
3423 | 0 | break; |
3424 | 0 | } |
3425 | 0 | return EditBrowseBox::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue ); |
3426 | 0 | } |
3427 | 0 | } |
3428 | 0 | return EditBrowseBox::CreateAccessibleCell( _nRow, _nColumnPos ); |
3429 | 0 | } |
3430 | | |
3431 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |