Coverage Report

Created: 2025-07-07 10:01

/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: */