Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/svx/source/dialog/rubydialog.cxx
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * This file is part of the LibreOffice project.
3
 *
4
 * This Source Code Form is subject to the terms of the Mozilla Public
5
 * License, v. 2.0. If a copy of the MPL was not distributed with this
6
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
 *
8
 * This file incorporates work covered by the following license notice:
9
 *
10
 *   Licensed to the Apache Software Foundation (ASF) under one or more
11
 *   contributor license agreements. See the NOTICE file distributed
12
 *   with this work for additional information regarding copyright
13
 *   ownership. The ASF licenses this file to you under the Apache
14
 *   License, Version 2.0 (the "License"); you may not use this file
15
 *   except in compliance with the License. You may obtain a copy of
16
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
17
 */
18
19
#include <memory>
20
#include <sal/config.h>
21
#include <tools/debug.hxx>
22
#include <comphelper/diagnose_ex.hxx>
23
#include <comphelper/processfactory.hxx>
24
25
#include <svx/rubydialog.hxx>
26
#include <sfx2/dispatch.hxx>
27
#include <sfx2/sfxsids.hrc>
28
#include <sfx2/viewfrm.hxx>
29
#include <sfx2/viewsh.hxx>
30
#include <svl/eitem.hxx>
31
#include <com/sun/star/frame/XController.hpp>
32
#include <com/sun/star/style/XStyle.hpp>
33
#include <com/sun/star/text/XRubySelection.hpp>
34
#include <com/sun/star/beans/PropertyValues.hpp>
35
#include <com/sun/star/beans/XPropertySet.hpp>
36
#include <com/sun/star/beans/XPropertySetInfo.hpp>
37
#include <com/sun/star/container/XNameContainer.hpp>
38
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
39
#include <com/sun/star/text/RubyAdjust.hpp>
40
#include <com/sun/star/view/XSelectionChangeListener.hpp>
41
#include <com/sun/star/view/XSelectionSupplier.hpp>
42
#include <com/sun/star/i18n/BreakIterator.hpp>
43
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
44
#include <cppuhelper/implbase.hxx>
45
#include <svtools/colorcfg.hxx>
46
#include <vcl/event.hxx>
47
#include <vcl/settings.hxx>
48
#include <vcl/svapp.hxx>
49
#include <rtl/ustrbuf.hxx>
50
#include <svl/itemset.hxx>
51
52
using namespace css::uno;
53
using namespace css::frame;
54
using namespace css::text;
55
using namespace css::beans;
56
using namespace css::style;
57
using namespace css::view;
58
using namespace css::lang;
59
using namespace css::container;
60
61
SFX_IMPL_CHILDWINDOW(SvxRubyChildWindow, SID_RUBY_DIALOG);
62
63
namespace
64
{
65
constexpr OUString cRubyBaseText = u"RubyBaseText"_ustr;
66
constexpr OUString cRubyText = u"RubyText"_ustr;
67
constexpr OUString cRubyAdjust = u"RubyAdjust"_ustr;
68
constexpr OUString cRubyPosition = u"RubyPosition"_ustr;
69
constexpr OUString cRubyCharStyleName = u"RubyCharStyleName"_ustr;
70
71
} // end anonymous namespace
72
73
SvxRubyChildWindow::SvxRubyChildWindow(vcl::Window* _pParent, sal_uInt16 nId,
74
                                       SfxBindings* pBindings, SfxChildWinInfo const* pInfo)
75
0
    : SfxChildWindow(_pParent, nId)
76
0
{
77
0
    auto xDlg = std::make_shared<SvxRubyDialog>(pBindings, this, _pParent->GetFrameWeld());
78
0
    SetController(xDlg);
79
0
    xDlg->Initialize(pInfo);
80
0
}
81
82
0
SfxChildWinInfo SvxRubyChildWindow::GetInfo() const { return SfxChildWindow::GetInfo(); }
83
84
class SvxRubyData_Impl : public cppu::WeakImplHelper<css::view::XSelectionChangeListener>
85
{
86
    Reference<css::i18n::XBreakIterator> xBreak;
87
    Reference<XModel> xModel;
88
    Reference<XRubySelection> xSelection;
89
    Sequence<PropertyValues> aRubyValues;
90
    Reference<XController> xController;
91
    bool bHasSelectionChanged;
92
    bool bDisposing;
93
94
public:
95
    SvxRubyData_Impl();
96
    virtual ~SvxRubyData_Impl() override;
97
98
    void SetController(const Reference<XController>& xCtrl);
99
    Reference<XModel> const& GetModel()
100
0
    {
101
0
        if (!xController.is())
102
0
            xModel = nullptr;
103
0
        else
104
0
            xModel = xController->getModel();
105
0
        return xModel;
106
0
    }
107
0
    bool HasSelectionChanged() const { return bHasSelectionChanged; }
108
0
    bool IsDisposing() const { return bDisposing; }
109
    Reference<XRubySelection> const& GetRubySelection()
110
0
    {
111
0
        xSelection.set(xController, UNO_QUERY);
112
0
        return xSelection;
113
0
    }
114
    void UpdateRubyValues()
115
0
    {
116
0
        if (!xSelection.is())
117
0
            aRubyValues.realloc(0);
118
0
        else
119
0
            aRubyValues = xSelection->getRubyList(false);
120
0
        bHasSelectionChanged = false;
121
0
    }
122
0
    Sequence<PropertyValues>& GetRubyValues() { return aRubyValues; }
123
    void AssertOneEntry();
124
125
    virtual void SAL_CALL selectionChanged(const css::lang::EventObject& aEvent) override;
126
    virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
127
128
0
    bool IsSelectionGrouped() { return aRubyValues.getLength() < 2; }
129
130
    void MakeSelectionGrouped()
131
0
    {
132
0
        if (aRubyValues.getLength() < 2)
133
0
        {
134
0
            return;
135
0
        }
136
137
0
        OUString sBaseTmp;
138
0
        OUStringBuffer aBaseString;
139
0
        for (const PropertyValues& rVals : aRubyValues)
140
0
        {
141
0
            sBaseTmp.clear();
142
0
            for (const PropertyValue& rVal : rVals)
143
0
            {
144
0
                if (rVal.Name == cRubyBaseText)
145
0
                {
146
0
                    rVal.Value >>= sBaseTmp;
147
0
                }
148
0
            }
149
150
0
            aBaseString.append(sBaseTmp);
151
0
        }
152
153
0
        Sequence<PropertyValues> aNewRubyValues{ 1 };
154
0
        PropertyValues* pNewRubyValues = aNewRubyValues.getArray();
155
156
        // Copy some reasonable style values from the previous ruby array
157
0
        pNewRubyValues[0] = aRubyValues[0];
158
0
        for (const PropertyValues& rVals : aRubyValues)
159
0
        {
160
0
            for (const PropertyValue& rVal : rVals)
161
0
            {
162
0
                if (rVal.Name == cRubyText)
163
0
                {
164
0
                    rVal.Value >>= sBaseTmp;
165
0
                    if (!sBaseTmp.isEmpty())
166
0
                    {
167
0
                        pNewRubyValues[0] = rVals;
168
0
                        break;
169
0
                    }
170
0
                }
171
0
            }
172
0
        }
173
174
0
        PropertyValue* pNewValues = pNewRubyValues[0].getArray();
175
0
        for (sal_Int32 i = 0; i < pNewRubyValues[0].getLength(); ++i)
176
0
        {
177
0
            if (pNewValues[i].Name == cRubyBaseText)
178
0
            {
179
0
                sBaseTmp = aBaseString;
180
0
                pNewValues[i].Value <<= sBaseTmp;
181
0
            }
182
0
            else if (pNewValues[i].Name == cRubyText)
183
0
            {
184
0
                sBaseTmp.clear();
185
0
                pNewValues[i].Value <<= sBaseTmp;
186
0
            }
187
0
        }
188
189
0
        aRubyValues = std::move(aNewRubyValues);
190
0
    }
191
192
    bool IsSelectionMono()
193
0
    {
194
0
        if (!xBreak.is())
195
0
        {
196
            // Cannot continue if BreakIterator is not available
197
            // Disable the button
198
0
            return true;
199
0
        }
200
201
        // Locale does not matter in this case; default ICU BreakIterator is sufficient
202
0
        Locale aLocale;
203
204
0
        OUString sBaseTmp;
205
0
        return std::all_of(
206
0
            aRubyValues.begin(), aRubyValues.end(), [&](const PropertyValues& rVals) {
207
0
                return !std::any_of(rVals.begin(), rVals.end(), [&](const PropertyValue& rVal) {
208
0
                    if (rVal.Name == cRubyBaseText)
209
0
                    {
210
0
                        rVal.Value >>= sBaseTmp;
211
0
                        sal_Int32 nDone = 0;
212
0
                        auto nPos = xBreak->nextCharacters(
213
0
                            sBaseTmp, 0, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1,
214
0
                            nDone);
215
0
                        return nPos < sBaseTmp.getLength();
216
0
                    }
217
218
0
                    return false;
219
0
                });
220
0
            });
221
0
    }
222
223
    void MakeSelectionMono()
224
0
    {
225
0
        if (!xBreak.is())
226
0
        {
227
            // Cannot continue if BreakIterator is not available
228
0
            return;
229
0
        }
230
231
        // Locale does not matter in this case; default ICU BreakIterator is sufficient
232
0
        Locale aLocale;
233
234
0
        OUString sBaseTmp;
235
236
        // Count the grapheme clusters
237
0
        sal_Int32 nTotalGraphemeClusters = 0;
238
0
        for (const PropertyValues& rVals : aRubyValues)
239
0
        {
240
0
            for (const PropertyValue& rVal : rVals)
241
0
            {
242
0
                if (rVal.Name == cRubyBaseText)
243
0
                {
244
0
                    rVal.Value >>= sBaseTmp;
245
246
0
                    sal_Int32 nPos = 0;
247
0
                    while (nPos < sBaseTmp.getLength())
248
0
                    {
249
0
                        sal_Int32 nDone = 0;
250
0
                        nPos = xBreak->nextCharacters(sBaseTmp, nPos, aLocale,
251
0
                                                      css::i18n::CharacterIteratorMode::SKIPCELL, 1,
252
0
                                                      nDone);
253
0
                        ++nTotalGraphemeClusters;
254
0
                    }
255
0
                }
256
0
            }
257
0
        }
258
259
        // Put each grapheme cluster in its own entry
260
0
        Sequence<PropertyValues> aNewRubyValues{ nTotalGraphemeClusters };
261
0
        PropertyValues* pNewRubyValues = aNewRubyValues.getArray();
262
263
0
        sal_Int32 nCurrGraphemeCluster = 0;
264
0
        for (const PropertyValues& rVals : aRubyValues)
265
0
        {
266
0
            for (const PropertyValue& rVal : rVals)
267
0
            {
268
0
                if (rVal.Name == cRubyBaseText)
269
0
                {
270
0
                    rVal.Value >>= sBaseTmp;
271
272
0
                    sal_Int32 nPos = 0;
273
0
                    while (nPos < sBaseTmp.getLength())
274
0
                    {
275
0
                        sal_Int32 nDone = 0;
276
0
                        auto nNextPos = xBreak->nextCharacters(
277
0
                            sBaseTmp, nPos, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1,
278
0
                            nDone);
279
280
0
                        PropertyValues& rNewVals = pNewRubyValues[nCurrGraphemeCluster++];
281
282
                        // Initialize new property values with values from current run
283
0
                        rNewVals = rVals;
284
285
0
                        PropertyValue* aNewVals = rNewVals.getArray();
286
0
                        for (sal_Int32 i = 0; i < rNewVals.getLength(); ++i)
287
0
                        {
288
0
                            PropertyValue& rNewVal = aNewVals[i];
289
290
0
                            if (rNewVal.Name == cRubyText)
291
0
                            {
292
0
                                rNewVal.Value <<= OUString{};
293
0
                            }
294
0
                            else if (rNewVal.Name == cRubyBaseText)
295
0
                            {
296
0
                                rNewVal.Value <<= sBaseTmp.copy(nPos, nNextPos - nPos);
297
0
                            }
298
0
                        }
299
300
0
                        nPos = nNextPos;
301
0
                    }
302
0
                }
303
0
            }
304
0
        }
305
306
0
        aRubyValues = std::move(aNewRubyValues);
307
0
    }
308
};
309
310
SvxRubyData_Impl::SvxRubyData_Impl()
311
0
    : bHasSelectionChanged(false)
312
0
    , bDisposing(false)
313
0
{
314
0
    const Reference<XComponentContext>& xContext = ::comphelper::getProcessComponentContext();
315
0
    xBreak = css::i18n::BreakIterator::create(xContext);
316
0
}
317
318
0
SvxRubyData_Impl::~SvxRubyData_Impl() {}
319
320
void SvxRubyData_Impl::SetController(const Reference<XController>& xCtrl)
321
0
{
322
0
    if (xCtrl.get() == xController.get())
323
0
        return;
324
325
0
    try
326
0
    {
327
0
        Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
328
0
        if (xSelSupp.is())
329
0
            xSelSupp->removeSelectionChangeListener(this);
330
331
0
        bHasSelectionChanged = true;
332
0
        xController = xCtrl;
333
0
        xSelSupp.set(xController, UNO_QUERY);
334
0
        if (xSelSupp.is())
335
0
            xSelSupp->addSelectionChangeListener(this);
336
0
    }
337
0
    catch (const Exception&)
338
0
    {
339
0
    }
340
0
}
341
342
0
void SvxRubyData_Impl::selectionChanged(const EventObject&) { bHasSelectionChanged = true; }
343
344
void SvxRubyData_Impl::disposing(const EventObject&)
345
0
{
346
0
    try
347
0
    {
348
0
        Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
349
0
        if (xSelSupp.is())
350
0
            xSelSupp->removeSelectionChangeListener(this);
351
0
    }
352
0
    catch (const Exception&)
353
0
    {
354
0
    }
355
0
    xController = nullptr;
356
0
    bDisposing = true;
357
0
}
358
359
void SvxRubyData_Impl::AssertOneEntry()
360
0
{
361
    //create one entry
362
0
    if (!aRubyValues.hasElements())
363
0
    {
364
0
        aRubyValues.realloc(1);
365
0
        Sequence<PropertyValue>& rValues = aRubyValues.getArray()[0];
366
0
        rValues.realloc(5);
367
0
        PropertyValue* pValues = rValues.getArray();
368
0
        pValues[0].Name = cRubyBaseText;
369
0
        pValues[1].Name = cRubyText;
370
0
        pValues[2].Name = cRubyAdjust;
371
0
        pValues[3].Name = cRubyPosition;
372
0
        pValues[4].Name = cRubyCharStyleName;
373
0
    }
374
0
}
375
376
SvxRubyDialog::SvxRubyDialog(SfxBindings* pBind, SfxChildWindow* pCW, weld::Window* pParent)
377
0
    : SfxModelessDialogController(pBind, pCW, pParent, u"svx/ui/asianphoneticguidedialog.ui"_ustr,
378
0
                                  u"AsianPhoneticGuideDialog"_ustr)
379
0
    , nLastPos(0)
380
0
    , nCurrentEdit(0)
381
0
    , bModified(false)
382
0
    , pBindings(pBind)
383
0
    , m_pImpl(new SvxRubyData_Impl)
384
0
    , m_xLeft1ED(m_xBuilder->weld_entry(u"Left1ED"_ustr))
385
0
    , m_xRight1ED(m_xBuilder->weld_entry(u"Right1ED"_ustr))
386
0
    , m_xLeft2ED(m_xBuilder->weld_entry(u"Left2ED"_ustr))
387
0
    , m_xRight2ED(m_xBuilder->weld_entry(u"Right2ED"_ustr))
388
0
    , m_xLeft3ED(m_xBuilder->weld_entry(u"Left3ED"_ustr))
389
0
    , m_xRight3ED(m_xBuilder->weld_entry(u"Right3ED"_ustr))
390
0
    , m_xLeft4ED(m_xBuilder->weld_entry(u"Left4ED"_ustr))
391
0
    , m_xRight4ED(m_xBuilder->weld_entry(u"Right4ED"_ustr))
392
0
    , m_xScrolledWindow(m_xBuilder->weld_scrolled_window(u"scrolledwindow"_ustr, true))
393
0
    , m_xAdjustLB(m_xBuilder->weld_combo_box(u"adjustlb"_ustr))
394
0
    , m_xPositionLB(m_xBuilder->weld_combo_box(u"positionlb"_ustr))
395
0
    , m_xCharStyleFT(m_xBuilder->weld_label(u"styleft"_ustr))
396
0
    , m_xCharStyleLB(m_xBuilder->weld_combo_box(u"stylelb"_ustr))
397
0
    , m_xStylistPB(m_xBuilder->weld_button(u"styles"_ustr))
398
0
    , m_xSelectionGroupPB(m_xBuilder->weld_button(u"selection-group"_ustr))
399
0
    , m_xSelectionMonoPB(m_xBuilder->weld_button(u"selection-mono"_ustr))
400
0
    , m_xApplyPB(m_xBuilder->weld_button(u"ok"_ustr))
401
0
    , m_xClosePB(m_xBuilder->weld_button(u"close"_ustr))
402
0
    , m_xContentArea(m_xDialog->weld_content_area())
403
0
    , m_xGrid(m_xBuilder->weld_widget(u"grid"_ustr))
404
0
    , m_xPreviewWin(new RubyPreview)
405
0
    , m_xPreview(new weld::CustomWeld(*m_xBuilder, u"preview"_ustr, *m_xPreviewWin))
406
0
{
407
0
    m_xCharStyleLB->make_sorted();
408
0
    m_xPreviewWin->setRubyDialog(this);
409
0
    m_xScrolledWindow->set_size_request(-1, m_xGrid->get_preferred_size().Height());
410
0
    m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
411
412
0
    aEditArr[0] = m_xLeft1ED.get();
413
0
    aEditArr[1] = m_xRight1ED.get();
414
0
    aEditArr[2] = m_xLeft2ED.get();
415
0
    aEditArr[3] = m_xRight2ED.get();
416
0
    aEditArr[4] = m_xLeft3ED.get();
417
0
    aEditArr[5] = m_xRight3ED.get();
418
0
    aEditArr[6] = m_xLeft4ED.get();
419
0
    aEditArr[7] = m_xRight4ED.get();
420
421
0
    m_xSelectionGroupPB->connect_clicked(LINK(this, SvxRubyDialog, SelectionGroup_Impl));
422
0
    m_xSelectionMonoPB->connect_clicked(LINK(this, SvxRubyDialog, SelectionMono_Impl));
423
0
    m_xApplyPB->connect_clicked(LINK(this, SvxRubyDialog, ApplyHdl_Impl));
424
0
    m_xClosePB->connect_clicked(LINK(this, SvxRubyDialog, CloseHdl_Impl));
425
0
    m_xStylistPB->connect_clicked(LINK(this, SvxRubyDialog, StylistHdl_Impl));
426
0
    m_xAdjustLB->connect_changed(LINK(this, SvxRubyDialog, AdjustHdl_Impl));
427
0
    m_xPositionLB->connect_changed(LINK(this, SvxRubyDialog, PositionHdl_Impl));
428
0
    m_xCharStyleLB->connect_changed(LINK(this, SvxRubyDialog, CharStyleHdl_Impl));
429
430
0
    Link<weld::ScrolledWindow&, void> aScrLk(LINK(this, SvxRubyDialog, ScrollHdl_Impl));
431
0
    m_xScrolledWindow->connect_vadjustment_changed(aScrLk);
432
433
0
    Link<weld::Entry&, void> aEditLk(LINK(this, SvxRubyDialog, EditModifyHdl_Impl));
434
0
    Link<weld::Widget&, void> aFocusLk(LINK(this, SvxRubyDialog, EditFocusHdl_Impl));
435
0
    Link<const KeyEvent&, bool> aKeyUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownHdl_Impl));
436
0
    Link<const KeyEvent&, bool> aKeyTabUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownTabHdl_Impl));
437
0
    for (sal_uInt16 i = 0; i < 8; i++)
438
0
    {
439
0
        aEditArr[i]->connect_changed(aEditLk);
440
0
        aEditArr[i]->connect_focus_in(aFocusLk);
441
0
        if (!i || 7 == i)
442
0
            aEditArr[i]->connect_key_press(aKeyTabUpDownLk);
443
0
        else
444
0
            aEditArr[i]->connect_key_press(aKeyUpDownLk);
445
0
    }
446
0
}
447
448
SvxRubyDialog::~SvxRubyDialog()
449
0
{
450
0
    ClearCharStyleList();
451
0
    EventObject aEvent;
452
0
    m_pImpl->disposing(aEvent);
453
0
}
454
455
0
void SvxRubyDialog::ClearCharStyleList() { m_xCharStyleLB->clear(); }
456
457
void SvxRubyDialog::Close()
458
0
{
459
0
    if (IsClosing())
460
0
        return;
461
0
    SfxViewFrame* pViewFrame = SfxViewFrame::Current();
462
0
    if (pViewFrame)
463
0
        pViewFrame->ToggleChildWindow(SID_RUBY_DIALOG);
464
0
}
465
466
void SvxRubyDialog::Activate()
467
0
{
468
0
    SfxModelessDialogController::Activate();
469
0
    if (m_pImpl->IsDisposing())
470
0
    {
471
        // tdf#141967/tdf#152495 if Activate is called during tear down bail early
472
0
        return;
473
0
    }
474
475
    //get selection from current view frame
476
0
    SfxViewFrame* pCurFrm = SfxViewFrame::Current();
477
0
    Reference<XController> xCtrl(pCurFrm ? pCurFrm->GetFrame().GetController() : nullptr);
478
0
    m_pImpl->SetController(xCtrl);
479
0
    if (!m_pImpl->HasSelectionChanged())
480
0
        return;
481
482
0
    Reference<XRubySelection> xRubySel = m_pImpl->GetRubySelection();
483
0
    m_pImpl->UpdateRubyValues();
484
0
    EnableControls(xRubySel.is());
485
0
    if (xRubySel.is())
486
0
    {
487
0
        Reference<XModel> xModel = m_pImpl->GetModel();
488
0
        const OUString sCharStyleSelect = m_xCharStyleLB->get_active_text();
489
0
        ClearCharStyleList();
490
0
        Reference<XStyleFamiliesSupplier> xSupplier(xModel, UNO_QUERY);
491
0
        if (xSupplier.is())
492
0
        {
493
0
            try
494
0
            {
495
0
                Reference<XNameAccess> xFam = xSupplier->getStyleFamilies();
496
0
                Any aChar = xFam->getByName(u"CharacterStyles"_ustr);
497
0
                Reference<XNameContainer> xChar;
498
0
                aChar >>= xChar;
499
0
                Reference<XIndexAccess> xCharIdx(xChar, UNO_QUERY);
500
0
                if (xCharIdx.is())
501
0
                {
502
0
                    OUString sUIName(u"DisplayName"_ustr);
503
0
                    for (sal_Int32 nStyle = 0; nStyle < xCharIdx->getCount(); nStyle++)
504
0
                    {
505
0
                        Any aStyle = xCharIdx->getByIndex(nStyle);
506
0
                        Reference<XStyle> xStyle;
507
0
                        aStyle >>= xStyle;
508
0
                        Reference<XPropertySet> xPrSet(xStyle, UNO_QUERY);
509
0
                        OUString sName, sCoreName;
510
0
                        if (xPrSet.is())
511
0
                        {
512
0
                            Reference<XPropertySetInfo> xInfo = xPrSet->getPropertySetInfo();
513
0
                            if (xInfo->hasPropertyByName(sUIName))
514
0
                            {
515
0
                                Any aName = xPrSet->getPropertyValue(sUIName);
516
0
                                aName >>= sName;
517
0
                            }
518
0
                        }
519
0
                        if (xStyle.is())
520
0
                        {
521
0
                            sCoreName = xStyle->getName();
522
0
                            if (sName.isEmpty())
523
0
                                sName = sCoreName;
524
0
                        }
525
0
                        if (!sName.isEmpty())
526
0
                        {
527
0
                            m_xCharStyleLB->append(sCoreName, sName);
528
0
                        }
529
0
                    }
530
0
                }
531
0
            }
532
0
            catch (const Exception&)
533
0
            {
534
0
                TOOLS_WARN_EXCEPTION("svx.dialog", "exception in style access");
535
0
            }
536
0
            if (!sCharStyleSelect.isEmpty())
537
0
                m_xCharStyleLB->set_active_text(sCharStyleSelect);
538
0
        }
539
0
        m_xCharStyleLB->set_sensitive(xSupplier.is());
540
0
        m_xCharStyleFT->set_sensitive(xSupplier.is());
541
0
    }
542
0
    Update();
543
0
    m_xPreviewWin->Invalidate();
544
0
}
545
546
void SvxRubyDialog::SetRubyText(sal_Int32 nPos, weld::Entry& rLeft, weld::Entry& rRight)
547
0
{
548
0
    OUString sLeft, sRight;
549
0
    const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
550
0
    bool bEnable = aRubyValues.getLength() > nPos;
551
0
    if (bEnable)
552
0
    {
553
0
        const Sequence<PropertyValue> aProps = aRubyValues.getConstArray()[nPos];
554
0
        for (const PropertyValue& rProp : aProps)
555
0
        {
556
0
            if (rProp.Name == cRubyBaseText)
557
0
                rProp.Value >>= sLeft;
558
0
            else if (rProp.Name == cRubyText)
559
0
                rProp.Value >>= sRight;
560
0
        }
561
0
    }
562
0
    else if (!nPos)
563
0
    {
564
0
        bEnable = true;
565
0
    }
566
0
    rLeft.set_sensitive(bEnable);
567
0
    rRight.set_sensitive(bEnable);
568
0
    rLeft.set_text(sLeft);
569
0
    rRight.set_text(sRight);
570
0
    rLeft.save_value();
571
0
    rRight.save_value();
572
0
}
573
574
void SvxRubyDialog::GetRubyText()
575
0
{
576
0
    tools::Long nTempLastPos = GetLastPos();
577
0
    Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
578
0
    auto aRubyValuesRange = asNonConstRange(aRubyValues);
579
0
    for (int i = 0; i < 8; i += 2)
580
0
    {
581
0
        if (aEditArr[i]->get_sensitive()
582
0
            && (aEditArr[i]->get_value_changed_from_saved()
583
0
                || aEditArr[i + 1]->get_value_changed_from_saved()))
584
0
        {
585
0
            DBG_ASSERT(aRubyValues.getLength() > (i / 2 + nTempLastPos), "wrong index");
586
0
            SetModified(true);
587
0
            for (PropertyValue& propVal : asNonConstRange(aRubyValuesRange[i / 2 + nTempLastPos]))
588
0
            {
589
0
                if (propVal.Name == cRubyBaseText)
590
0
                    propVal.Value <<= aEditArr[i]->get_text();
591
0
                else if (propVal.Name == cRubyText)
592
0
                    propVal.Value <<= aEditArr[i + 1]->get_text();
593
0
            }
594
0
        }
595
0
    }
596
0
}
597
598
void SvxRubyDialog::Update()
599
0
{
600
    // Only enable selection grouping options when they can be applied
601
0
    m_xSelectionGroupPB->set_sensitive(!m_pImpl->IsSelectionGrouped());
602
0
    m_xSelectionMonoPB->set_sensitive(!m_pImpl->IsSelectionMono());
603
604
0
    const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
605
0
    sal_Int32 nLen = aRubyValues.getLength();
606
0
    m_xScrolledWindow->vadjustment_configure(0, 0, !nLen ? 1 : nLen, 1, 4, 4);
607
0
    if (nLen > 4)
608
0
        m_xScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS);
609
0
    else
610
0
        m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
611
0
    SetLastPos(0);
612
0
    SetModified(false);
613
614
0
    sal_Int16 nAdjust = -1;
615
0
    sal_Int16 nPosition = -1;
616
0
    OUString sCharStyleName, sTmp;
617
0
    bool bCharStyleEqual = true;
618
0
    for (sal_Int32 nRuby = 0; nRuby < nLen; nRuby++)
619
0
    {
620
0
        const Sequence<PropertyValue>& rProps = aRubyValues.getConstArray()[nRuby];
621
0
        for (const PropertyValue& rProp : rProps)
622
0
        {
623
0
            if (nAdjust > -2 && rProp.Name == cRubyAdjust)
624
0
            {
625
0
                sal_Int16 nTmp = sal_Int16();
626
0
                rProp.Value >>= nTmp;
627
0
                if (!nRuby)
628
0
                    nAdjust = nTmp;
629
0
                else if (nAdjust != nTmp)
630
0
                    nAdjust = -2;
631
0
            }
632
0
            if (nPosition > -2 && rProp.Name == cRubyPosition)
633
0
            {
634
0
                sal_Int16 nTmp = sal_Int16();
635
0
                rProp.Value >>= nTmp;
636
0
                if (!nRuby)
637
0
                    nPosition = nTmp;
638
0
                else if (nPosition != nTmp)
639
0
                    nPosition = -2;
640
0
            }
641
0
            if (bCharStyleEqual && rProp.Name == cRubyCharStyleName)
642
0
            {
643
0
                rProp.Value >>= sTmp;
644
0
                if (!nRuby)
645
0
                    sCharStyleName = sTmp;
646
0
                else if (sCharStyleName != sTmp)
647
0
                    bCharStyleEqual = false;
648
0
            }
649
0
        }
650
0
    }
651
0
    if (!nLen)
652
0
    {
653
        //enable selection if the ruby list is empty
654
0
        nAdjust = 0;
655
0
        nPosition = 0;
656
0
    }
657
0
    if (nAdjust > -1)
658
0
        m_xAdjustLB->set_active(nAdjust);
659
0
    else
660
0
        m_xAdjustLB->set_active(-1);
661
0
    if (nPosition > -1)
662
0
        m_xPositionLB->set_active(nPosition);
663
0
    if (!nLen || (bCharStyleEqual && sCharStyleName.isEmpty()))
664
0
        sCharStyleName = "Rubies";
665
0
    if (!sCharStyleName.isEmpty())
666
0
    {
667
0
        for (int i = 0, nEntryCount = m_xCharStyleLB->get_count(); i < nEntryCount; i++)
668
0
        {
669
0
            OUString sCoreName = m_xCharStyleLB->get_id(i);
670
0
            if (sCharStyleName == sCoreName)
671
0
            {
672
0
                m_xCharStyleLB->set_active(i);
673
0
                break;
674
0
            }
675
0
        }
676
0
    }
677
0
    else
678
0
        m_xCharStyleLB->set_active(-1);
679
680
0
    ScrollHdl_Impl(*m_xScrolledWindow);
681
0
}
682
683
void SvxRubyDialog::GetCurrentText(OUString& rBase, OUString& rRuby)
684
0
{
685
0
    rBase = aEditArr[nCurrentEdit * 2]->get_text();
686
0
    rRuby = aEditArr[nCurrentEdit * 2 + 1]->get_text();
687
0
}
688
689
IMPL_LINK(SvxRubyDialog, ScrollHdl_Impl, weld::ScrolledWindow&, rScroll, void)
690
0
{
691
0
    int nPos = rScroll.vadjustment_get_value();
692
0
    if (GetLastPos() != nPos)
693
0
    {
694
0
        GetRubyText();
695
0
    }
696
0
    SetRubyText(nPos++, *m_xLeft1ED, *m_xRight1ED);
697
0
    SetRubyText(nPos++, *m_xLeft2ED, *m_xRight2ED);
698
0
    SetRubyText(nPos++, *m_xLeft3ED, *m_xRight3ED);
699
0
    SetRubyText(nPos, *m_xLeft4ED, *m_xRight4ED);
700
0
    SetLastPos(nPos - 3);
701
0
    m_xPreviewWin->Invalidate();
702
0
}
703
704
IMPL_LINK_NOARG(SvxRubyDialog, SelectionGroup_Impl, weld::Button&, void)
705
0
{
706
0
    m_pImpl->MakeSelectionGrouped();
707
0
    Update();
708
0
}
709
710
IMPL_LINK_NOARG(SvxRubyDialog, SelectionMono_Impl, weld::Button&, void)
711
0
{
712
0
    m_pImpl->MakeSelectionMono();
713
0
    Update();
714
0
}
715
716
IMPL_LINK_NOARG(SvxRubyDialog, ApplyHdl_Impl, weld::Button&, void)
717
0
{
718
0
    const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
719
0
    if (!aRubyValues.hasElements())
720
0
    {
721
0
        AssertOneEntry();
722
0
        PositionHdl_Impl(*m_xPositionLB);
723
0
        AdjustHdl_Impl(*m_xAdjustLB);
724
0
        CharStyleHdl_Impl(*m_xCharStyleLB);
725
0
    }
726
0
    GetRubyText();
727
    //reset all edit fields - SaveValue is called
728
0
    ScrollHdl_Impl(*m_xScrolledWindow);
729
730
0
    Reference<XRubySelection> xSelection = m_pImpl->GetRubySelection();
731
0
    if (IsModified() && xSelection.is())
732
0
    {
733
0
        try
734
0
        {
735
0
            xSelection->setRubyList(aRubyValues, false);
736
0
        }
737
0
        catch (const Exception&)
738
0
        {
739
0
            TOOLS_WARN_EXCEPTION("svx.dialog", "");
740
0
        }
741
0
    }
742
0
}
743
744
0
IMPL_LINK_NOARG(SvxRubyDialog, CloseHdl_Impl, weld::Button&, void) { Close(); }
745
746
IMPL_LINK_NOARG(SvxRubyDialog, StylistHdl_Impl, weld::Button&, void)
747
0
{
748
0
    std::unique_ptr<SfxBoolItem> pState;
749
0
    SfxItemState eState = pBindings->QueryState(SID_STYLE_DESIGNER, pState);
750
0
    if (eState <= SfxItemState::SET || !pState || !pState->GetValue())
751
0
    {
752
0
        pBindings->GetDispatcher()->Execute(SID_STYLE_DESIGNER,
753
0
                                            SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
754
0
    }
755
0
}
756
757
IMPL_LINK(SvxRubyDialog, AdjustHdl_Impl, weld::ComboBox&, rBox, void)
758
0
{
759
0
    AssertOneEntry();
760
0
    sal_Int16 nAdjust = rBox.get_active();
761
0
    for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
762
0
    {
763
0
        for (PropertyValue& propVal : asNonConstRange(rProps))
764
0
        {
765
0
            if (propVal.Name == cRubyAdjust)
766
0
                propVal.Value <<= nAdjust;
767
0
        }
768
0
        SetModified(true);
769
0
    }
770
0
    m_xPreviewWin->Invalidate();
771
0
}
772
773
IMPL_LINK(SvxRubyDialog, PositionHdl_Impl, weld::ComboBox&, rBox, void)
774
0
{
775
0
    AssertOneEntry();
776
0
    sal_Int16 nPosition = rBox.get_active();
777
0
    for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
778
0
    {
779
0
        for (PropertyValue& propVal : asNonConstRange(rProps))
780
0
        {
781
0
            if (propVal.Name == cRubyPosition)
782
0
                propVal.Value <<= nPosition;
783
0
        }
784
0
        SetModified(true);
785
0
    }
786
0
    m_xPreviewWin->Invalidate();
787
0
}
788
789
IMPL_LINK_NOARG(SvxRubyDialog, CharStyleHdl_Impl, weld::ComboBox&, void)
790
0
{
791
0
    AssertOneEntry();
792
0
    OUString sStyleName;
793
0
    if (m_xCharStyleLB->get_active() != -1)
794
0
        sStyleName = m_xCharStyleLB->get_active_id();
795
0
    for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
796
0
    {
797
0
        for (PropertyValue& propVal : asNonConstRange(rProps))
798
0
        {
799
0
            if (propVal.Name == cRubyCharStyleName)
800
0
            {
801
0
                propVal.Value <<= sStyleName;
802
0
            }
803
0
        }
804
0
        SetModified(true);
805
0
    }
806
0
}
807
808
IMPL_LINK(SvxRubyDialog, EditFocusHdl_Impl, weld::Widget&, rEdit, void)
809
0
{
810
0
    for (sal_uInt16 i = 0; i < 8; i++)
811
0
    {
812
0
        if (&rEdit == aEditArr[i])
813
0
        {
814
0
            nCurrentEdit = i / 2;
815
0
            break;
816
0
        }
817
0
    }
818
0
    m_xPreviewWin->Invalidate();
819
0
}
820
821
IMPL_LINK(SvxRubyDialog, EditModifyHdl_Impl, weld::Entry&, rEdit, void)
822
0
{
823
0
    EditFocusHdl_Impl(rEdit);
824
0
}
825
826
bool SvxRubyDialog::EditScrollHdl_Impl(sal_Int32 nParam)
827
0
{
828
0
    bool bRet = false;
829
    //scroll forward
830
0
    if (nParam > 0 && (aEditArr[7]->has_focus() || aEditArr[6]->has_focus()))
831
0
    {
832
0
        if (m_xScrolledWindow->vadjustment_get_upper()
833
0
            > m_xScrolledWindow->vadjustment_get_value()
834
0
                  + m_xScrolledWindow->vadjustment_get_page_size())
835
0
        {
836
0
            m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value()
837
0
                                                     + 1);
838
0
            aEditArr[6]->grab_focus();
839
0
            bRet = true;
840
0
        }
841
0
    }
842
    //scroll backward
843
0
    else if (m_xScrolledWindow->vadjustment_get_value()
844
0
             && (aEditArr[0]->has_focus() || aEditArr[1]->has_focus()))
845
0
    {
846
0
        m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value() - 1);
847
0
        aEditArr[1]->grab_focus();
848
0
        bRet = true;
849
0
    }
850
0
    if (bRet)
851
0
        ScrollHdl_Impl(*m_xScrolledWindow);
852
0
    return bRet;
853
0
}
854
855
bool SvxRubyDialog::EditJumpHdl_Impl(sal_Int32 nParam)
856
0
{
857
0
    bool bHandled = false;
858
0
    sal_uInt16 nIndex = USHRT_MAX;
859
0
    for (sal_uInt16 i = 0; i < 8; i++)
860
0
    {
861
0
        if (aEditArr[i]->has_focus())
862
0
            nIndex = i;
863
0
    }
864
0
    if (nIndex < 8)
865
0
    {
866
0
        if (nParam > 0)
867
0
        {
868
0
            if (nIndex < 6)
869
0
                aEditArr[nIndex + 2]->grab_focus();
870
0
            else if (EditScrollHdl_Impl(nParam))
871
0
                aEditArr[nIndex]->grab_focus();
872
0
        }
873
0
        else
874
0
        {
875
0
            if (nIndex > 1)
876
0
                aEditArr[nIndex - 2]->grab_focus();
877
0
            else if (EditScrollHdl_Impl(nParam))
878
0
                aEditArr[nIndex]->grab_focus();
879
0
        }
880
0
        bHandled = true;
881
0
    }
882
0
    return bHandled;
883
0
}
884
885
0
void SvxRubyDialog::AssertOneEntry() { m_pImpl->AssertOneEntry(); }
886
887
void SvxRubyDialog::EnableControls(bool bEnable)
888
0
{
889
0
    m_xContentArea->set_sensitive(bEnable);
890
0
    m_xApplyPB->set_sensitive(bEnable);
891
0
}
892
893
RubyPreview::RubyPreview()
894
0
    : m_pParentDlg(nullptr)
895
0
{
896
0
}
897
898
0
RubyPreview::~RubyPreview() {}
899
900
void RubyPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
901
0
{
902
0
    rRenderContext.Push(vcl::PushFlags::ALL);
903
904
0
    rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
905
906
0
    Size aWinSize = rRenderContext.GetOutputSize();
907
908
0
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
909
0
    svtools::ColorConfig aColorConfig;
910
911
0
    Color aNewTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
912
0
    Color aNewFillColor(rStyleSettings.GetWindowColor());
913
914
0
    vcl::Font aFont = rRenderContext.GetFont();
915
0
    aFont.SetFontHeight(aWinSize.Height() / 4);
916
0
    aFont.SetFillColor(aNewFillColor);
917
0
    aFont.SetColor(aNewTextColor);
918
0
    rRenderContext.SetFont(aFont);
919
920
0
    tools::Rectangle aRect(Point(0, 0), aWinSize);
921
0
    rRenderContext.SetLineColor();
922
0
    rRenderContext.SetFillColor(aFont.GetFillColor());
923
0
    rRenderContext.DrawRect(aRect);
924
925
0
    OUString sBaseText, sRubyText;
926
0
    m_pParentDlg->GetCurrentText(sBaseText, sRubyText);
927
928
0
    tools::Long nTextHeight = rRenderContext.GetTextHeight();
929
0
    tools::Long nBaseWidth = rRenderContext.GetTextWidth(sBaseText);
930
931
0
    vcl::Font aRubyFont(aFont);
932
0
    aRubyFont.SetFontHeight(aRubyFont.GetFontHeight() * 70 / 100);
933
0
    rRenderContext.SetFont(aRubyFont);
934
0
    tools::Long nRubyWidth = rRenderContext.GetTextWidth(sRubyText);
935
0
    rRenderContext.SetFont(aFont);
936
937
0
    RubyAdjust nAdjust = static_cast<RubyAdjust>(m_pParentDlg->m_xAdjustLB->get_active());
938
    //use center if no adjustment is available
939
0
    if (nAdjust > RubyAdjust_INDENT_BLOCK)
940
0
        nAdjust = RubyAdjust_CENTER;
941
942
    //which part is stretched ?
943
0
    bool bRubyStretch = nBaseWidth >= nRubyWidth;
944
945
0
    tools::Long nCenter = aWinSize.Width() / 2;
946
0
    tools::Long nHalfWidth = std::max(nBaseWidth, nRubyWidth) / 2;
947
0
    tools::Long nLeftStart = nCenter - nHalfWidth;
948
0
    tools::Long nRightEnd = nCenter + nHalfWidth;
949
950
    // Default values for TOP or no selection
951
0
    tools::Long nYRuby = aWinSize.Height() / 4 - nTextHeight / 2;
952
0
    tools::Long nYBase = aWinSize.Height() * 3 / 4 - nTextHeight / 2;
953
954
0
    sal_Int16 nRubyPos = m_pParentDlg->m_xPositionLB->get_active();
955
0
    if (nRubyPos == 1) // BOTTOM
956
0
        std::swap(nYRuby, nYBase);
957
0
    else if (nRubyPos == 2) // RIGHT ( vertically )
958
0
    {
959
        // Align the ruby text and base text to the vertical center.
960
0
        nYBase = (aWinSize.Height() - nTextHeight) / 2;
961
0
        nYRuby = (aWinSize.Height() - nRubyWidth) / 2;
962
963
        // Align the ruby text at the right side of the base text
964
0
        nAdjust = RubyAdjust_RIGHT;
965
0
        nHalfWidth = nBaseWidth / 2;
966
0
        nLeftStart = nCenter - nHalfWidth;
967
0
        nRightEnd = nCenter + nHalfWidth + nRubyWidth + nTextHeight;
968
        // Render base text first, then render ruby text on the right.
969
0
        bRubyStretch = true;
970
971
0
        aRubyFont.SetVertical(true);
972
0
        aRubyFont.SetOrientation(2700_deg10);
973
0
    }
974
975
0
    tools::Long nYOutput;
976
0
    tools::Long nOutTextWidth;
977
0
    OUString sOutputText;
978
979
0
    if (bRubyStretch)
980
0
    {
981
0
        rRenderContext.DrawText(Point(nLeftStart, nYBase), sBaseText);
982
0
        nYOutput = nYRuby;
983
0
        sOutputText = sRubyText;
984
0
        nOutTextWidth = nRubyWidth;
985
0
        rRenderContext.SetFont(aRubyFont);
986
0
    }
987
0
    else
988
0
    {
989
0
        rRenderContext.SetFont(aRubyFont);
990
0
        rRenderContext.DrawText(Point(nLeftStart, nYRuby), sRubyText);
991
0
        nYOutput = nYBase;
992
0
        sOutputText = sBaseText;
993
0
        nOutTextWidth = nBaseWidth;
994
0
        rRenderContext.SetFont(aFont);
995
0
    }
996
997
0
    switch (nAdjust)
998
0
    {
999
0
        case RubyAdjust_LEFT:
1000
0
            rRenderContext.DrawText(Point(nLeftStart, nYOutput), sOutputText);
1001
0
            break;
1002
0
        case RubyAdjust_RIGHT:
1003
0
            rRenderContext.DrawText(Point(nRightEnd - nOutTextWidth, nYOutput), sOutputText);
1004
0
            break;
1005
0
        case RubyAdjust_INDENT_BLOCK:
1006
0
        {
1007
0
            tools::Long nCharWidth = rRenderContext.GetTextWidth(u"X"_ustr);
1008
0
            if (nOutTextWidth < (nRightEnd - nLeftStart - nCharWidth))
1009
0
            {
1010
0
                nCharWidth /= 2;
1011
0
                nLeftStart += nCharWidth;
1012
0
                nRightEnd -= nCharWidth;
1013
0
            }
1014
0
            [[fallthrough]];
1015
0
        }
1016
0
        case RubyAdjust_BLOCK:
1017
0
        {
1018
0
            if (sOutputText.getLength() > 1)
1019
0
            {
1020
0
                sal_Int32 nCount = sOutputText.getLength();
1021
0
                tools::Long nSpace
1022
0
                    = ((nRightEnd - nLeftStart) - rRenderContext.GetTextWidth(sOutputText))
1023
0
                      / (nCount - 1);
1024
0
                for (sal_Int32 i = 0; i < nCount; i++)
1025
0
                {
1026
0
                    OUString sChar(sOutputText[i]);
1027
0
                    rRenderContext.DrawText(Point(nLeftStart, nYOutput), sChar);
1028
0
                    tools::Long nCharWidth = rRenderContext.GetTextWidth(sChar);
1029
0
                    nLeftStart += nCharWidth + nSpace;
1030
0
                }
1031
0
                break;
1032
0
            }
1033
0
            [[fallthrough]];
1034
0
        }
1035
0
        case RubyAdjust_CENTER:
1036
0
            rRenderContext.DrawText(Point(nCenter - nOutTextWidth / 2, nYOutput), sOutputText);
1037
0
            break;
1038
0
        default:
1039
0
            break;
1040
0
    }
1041
0
    rRenderContext.Pop();
1042
0
}
1043
1044
void RubyPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
1045
0
{
1046
0
    pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 40,
1047
0
                                   pDrawingArea->get_text_height() * 7);
1048
0
    CustomWidgetController::SetDrawingArea(pDrawingArea);
1049
0
}
1050
1051
IMPL_LINK(SvxRubyDialog, KeyUpDownHdl_Impl, const KeyEvent&, rKEvt, bool)
1052
0
{
1053
0
    bool bHandled = false;
1054
0
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
1055
0
    sal_uInt16 nCode = rKeyCode.GetCode();
1056
0
    if (KEY_UP == nCode || KEY_DOWN == nCode)
1057
0
    {
1058
0
        sal_Int32 nParam = KEY_UP == nCode ? -1 : 1;
1059
0
        bHandled = EditJumpHdl_Impl(nParam);
1060
0
    }
1061
0
    return bHandled;
1062
0
}
1063
1064
IMPL_LINK(SvxRubyDialog, KeyUpDownTabHdl_Impl, const KeyEvent&, rKEvt, bool)
1065
0
{
1066
0
    bool bHandled = false;
1067
0
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
1068
0
    sal_uInt16 nMod = rKeyCode.GetModifier();
1069
0
    sal_uInt16 nCode = rKeyCode.GetCode();
1070
0
    if (nCode == KEY_TAB && (!nMod || KEY_SHIFT == nMod))
1071
0
    {
1072
0
        sal_Int32 nParam = KEY_SHIFT == nMod ? -1 : 1;
1073
0
        if (EditScrollHdl_Impl(nParam))
1074
0
            bHandled = true;
1075
0
    }
1076
0
    if (!bHandled)
1077
0
        bHandled = KeyUpDownHdl_Impl(rKEvt);
1078
0
    return bHandled;
1079
0
}
1080
1081
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */