Coverage Report

Created: 2026-05-16 09:25

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