a11y: Simplify OCommonAccessibleComponent::getAccessibleIndexInParent
[LibreOffice.git] / svx / source / dialog / rubydialog.cxx
blob72147410ba8e1eea06af7523d4f554804a4e0e5b
1 /*
2 * This file is part of the LibreOffice project.
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/.
8 * This file incorporates work covered by the following license notice:
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 .
19 #include <memory>
20 #include <sal/config.h>
21 #include <tools/debug.hxx>
22 #include <comphelper/diagnose_ex.hxx>
23 #include <comphelper/processfactory.hxx>
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>
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;
61 SFX_IMPL_CHILDWINDOW(SvxRubyChildWindow, SID_RUBY_DIALOG);
63 namespace
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;
71 } // end anonymous namespace
73 SvxRubyChildWindow::SvxRubyChildWindow(vcl::Window* _pParent, sal_uInt16 nId,
74 SfxBindings* pBindings, SfxChildWinInfo const* pInfo)
75 : SfxChildWindow(_pParent, nId)
77 auto xDlg = std::make_shared<SvxRubyDialog>(pBindings, this, _pParent->GetFrameWeld());
78 SetController(xDlg);
79 xDlg->Initialize(pInfo);
82 SfxChildWinInfo SvxRubyChildWindow::GetInfo() const { return SfxChildWindow::GetInfo(); }
84 class SvxRubyData_Impl : public cppu::WeakImplHelper<css::view::XSelectionChangeListener>
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;
94 public:
95 SvxRubyData_Impl();
96 virtual ~SvxRubyData_Impl() override;
98 void SetController(const Reference<XController>& xCtrl);
99 Reference<XModel> const& GetModel()
101 if (!xController.is())
102 xModel = nullptr;
103 else
104 xModel = xController->getModel();
105 return xModel;
107 bool HasSelectionChanged() const { return bHasSelectionChanged; }
108 bool IsDisposing() const { return bDisposing; }
109 Reference<XRubySelection> const& GetRubySelection()
111 xSelection.set(xController, UNO_QUERY);
112 return xSelection;
114 void UpdateRubyValues()
116 if (!xSelection.is())
117 aRubyValues.realloc(0);
118 else
119 aRubyValues = xSelection->getRubyList(false);
120 bHasSelectionChanged = false;
122 Sequence<PropertyValues>& GetRubyValues() { return aRubyValues; }
123 void AssertOneEntry();
125 virtual void SAL_CALL selectionChanged(const css::lang::EventObject& aEvent) override;
126 virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
128 bool IsSelectionGrouped() { return aRubyValues.getLength() < 2; }
130 void MakeSelectionGrouped()
132 if (aRubyValues.getLength() < 2)
134 return;
137 OUString sBaseTmp;
138 OUStringBuffer aBaseString;
139 for (const PropertyValues& rVals : aRubyValues)
141 sBaseTmp.clear();
142 for (const PropertyValue& rVal : rVals)
144 if (rVal.Name == cRubyBaseText)
146 rVal.Value >>= sBaseTmp;
150 aBaseString.append(sBaseTmp);
153 Sequence<PropertyValues> aNewRubyValues{ 1 };
154 PropertyValues* pNewRubyValues = aNewRubyValues.getArray();
156 // Copy some reasonable style values from the previous ruby array
157 pNewRubyValues[0] = aRubyValues[0];
158 for (const PropertyValues& rVals : aRubyValues)
160 for (const PropertyValue& rVal : rVals)
162 if (rVal.Name == cRubyText)
164 rVal.Value >>= sBaseTmp;
165 if (!sBaseTmp.isEmpty())
167 pNewRubyValues[0] = rVals;
168 break;
174 PropertyValue* pNewValues = pNewRubyValues[0].getArray();
175 for (sal_Int32 i = 0; i < pNewRubyValues[0].getLength(); ++i)
177 if (pNewValues[i].Name == cRubyBaseText)
179 sBaseTmp = aBaseString;
180 pNewValues[i].Value <<= sBaseTmp;
182 else if (pNewValues[i].Name == cRubyText)
184 sBaseTmp.clear();
185 pNewValues[i].Value <<= sBaseTmp;
189 aRubyValues = std::move(aNewRubyValues);
192 bool IsSelectionMono()
194 if (!xBreak.is())
196 // Cannot continue if BreakIterator is not available
197 // Disable the button
198 return true;
201 // Locale does not matter in this case; default ICU BreakIterator is sufficient
202 Locale aLocale;
204 OUString sBaseTmp;
205 return std::all_of(
206 aRubyValues.begin(), aRubyValues.end(), [&](const PropertyValues& rVals) {
207 return !std::any_of(rVals.begin(), rVals.end(), [&](const PropertyValue& rVal) {
208 if (rVal.Name == cRubyBaseText)
210 rVal.Value >>= sBaseTmp;
211 sal_Int32 nDone = 0;
212 auto nPos = xBreak->nextCharacters(
213 sBaseTmp, 0, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1,
214 nDone);
215 return nPos < sBaseTmp.getLength();
218 return false;
223 void MakeSelectionMono()
225 if (!xBreak.is())
227 // Cannot continue if BreakIterator is not available
228 return;
231 // Locale does not matter in this case; default ICU BreakIterator is sufficient
232 Locale aLocale;
234 OUString sBaseTmp;
236 // Count the grapheme clusters
237 sal_Int32 nTotalGraphemeClusters = 0;
238 for (const PropertyValues& rVals : aRubyValues)
240 for (const PropertyValue& rVal : rVals)
242 if (rVal.Name == cRubyBaseText)
244 rVal.Value >>= sBaseTmp;
246 sal_Int32 nPos = 0;
247 while (nPos < sBaseTmp.getLength())
249 sal_Int32 nDone = 0;
250 nPos = xBreak->nextCharacters(sBaseTmp, nPos, aLocale,
251 css::i18n::CharacterIteratorMode::SKIPCELL, 1,
252 nDone);
253 ++nTotalGraphemeClusters;
259 // Put each grapheme cluster in its own entry
260 Sequence<PropertyValues> aNewRubyValues{ nTotalGraphemeClusters };
261 PropertyValues* pNewRubyValues = aNewRubyValues.getArray();
263 sal_Int32 nCurrGraphemeCluster = 0;
264 for (const PropertyValues& rVals : aRubyValues)
266 for (const PropertyValue& rVal : rVals)
268 if (rVal.Name == cRubyBaseText)
270 rVal.Value >>= sBaseTmp;
272 sal_Int32 nPos = 0;
273 while (nPos < sBaseTmp.getLength())
275 sal_Int32 nDone = 0;
276 auto nNextPos = xBreak->nextCharacters(
277 sBaseTmp, nPos, aLocale, css::i18n::CharacterIteratorMode::SKIPCELL, 1,
278 nDone);
280 PropertyValues& rNewVals = pNewRubyValues[nCurrGraphemeCluster++];
282 // Initialize new property values with values from current run
283 rNewVals = rVals;
285 PropertyValue* aNewVals = rNewVals.getArray();
286 for (sal_Int32 i = 0; i < rNewVals.getLength(); ++i)
288 PropertyValue& rNewVal = aNewVals[i];
290 if (rNewVal.Name == cRubyText)
292 rNewVal.Value <<= OUString{};
294 else if (rNewVal.Name == cRubyBaseText)
296 rNewVal.Value <<= sBaseTmp.copy(nPos, nNextPos - nPos);
300 nPos = nNextPos;
306 aRubyValues = std::move(aNewRubyValues);
310 SvxRubyData_Impl::SvxRubyData_Impl()
311 : bHasSelectionChanged(false)
312 , bDisposing(false)
314 const Reference<XComponentContext>& xContext = ::comphelper::getProcessComponentContext();
315 xBreak = css::i18n::BreakIterator::create(xContext);
318 SvxRubyData_Impl::~SvxRubyData_Impl() {}
320 void SvxRubyData_Impl::SetController(const Reference<XController>& xCtrl)
322 if (xCtrl.get() == xController.get())
323 return;
327 Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
328 if (xSelSupp.is())
329 xSelSupp->removeSelectionChangeListener(this);
331 bHasSelectionChanged = true;
332 xController = xCtrl;
333 xSelSupp.set(xController, UNO_QUERY);
334 if (xSelSupp.is())
335 xSelSupp->addSelectionChangeListener(this);
337 catch (const Exception&)
342 void SvxRubyData_Impl::selectionChanged(const EventObject&) { bHasSelectionChanged = true; }
344 void SvxRubyData_Impl::disposing(const EventObject&)
348 Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
349 if (xSelSupp.is())
350 xSelSupp->removeSelectionChangeListener(this);
352 catch (const Exception&)
355 xController = nullptr;
356 bDisposing = true;
359 void SvxRubyData_Impl::AssertOneEntry()
361 //create one entry
362 if (!aRubyValues.hasElements())
364 aRubyValues.realloc(1);
365 Sequence<PropertyValue>& rValues = aRubyValues.getArray()[0];
366 rValues.realloc(5);
367 PropertyValue* pValues = rValues.getArray();
368 pValues[0].Name = cRubyBaseText;
369 pValues[1].Name = cRubyText;
370 pValues[2].Name = cRubyAdjust;
371 pValues[3].Name = cRubyPosition;
372 pValues[4].Name = cRubyCharStyleName;
376 SvxRubyDialog::SvxRubyDialog(SfxBindings* pBind, SfxChildWindow* pCW, weld::Window* pParent)
377 : SfxModelessDialogController(pBind, pCW, pParent, u"svx/ui/asianphoneticguidedialog.ui"_ustr,
378 u"AsianPhoneticGuideDialog"_ustr)
379 , nLastPos(0)
380 , nCurrentEdit(0)
381 , bModified(false)
382 , pBindings(pBind)
383 , m_pImpl(new SvxRubyData_Impl)
384 , m_xLeft1ED(m_xBuilder->weld_entry(u"Left1ED"_ustr))
385 , m_xRight1ED(m_xBuilder->weld_entry(u"Right1ED"_ustr))
386 , m_xLeft2ED(m_xBuilder->weld_entry(u"Left2ED"_ustr))
387 , m_xRight2ED(m_xBuilder->weld_entry(u"Right2ED"_ustr))
388 , m_xLeft3ED(m_xBuilder->weld_entry(u"Left3ED"_ustr))
389 , m_xRight3ED(m_xBuilder->weld_entry(u"Right3ED"_ustr))
390 , m_xLeft4ED(m_xBuilder->weld_entry(u"Left4ED"_ustr))
391 , m_xRight4ED(m_xBuilder->weld_entry(u"Right4ED"_ustr))
392 , m_xScrolledWindow(m_xBuilder->weld_scrolled_window(u"scrolledwindow"_ustr, true))
393 , m_xAdjustLB(m_xBuilder->weld_combo_box(u"adjustlb"_ustr))
394 , m_xPositionLB(m_xBuilder->weld_combo_box(u"positionlb"_ustr))
395 , m_xCharStyleFT(m_xBuilder->weld_label(u"styleft"_ustr))
396 , m_xCharStyleLB(m_xBuilder->weld_combo_box(u"stylelb"_ustr))
397 , m_xStylistPB(m_xBuilder->weld_button(u"styles"_ustr))
398 , m_xSelectionGroupPB(m_xBuilder->weld_button(u"selection-group"_ustr))
399 , m_xSelectionMonoPB(m_xBuilder->weld_button(u"selection-mono"_ustr))
400 , m_xApplyPB(m_xBuilder->weld_button(u"ok"_ustr))
401 , m_xClosePB(m_xBuilder->weld_button(u"close"_ustr))
402 , m_xContentArea(m_xDialog->weld_content_area())
403 , m_xGrid(m_xBuilder->weld_widget(u"grid"_ustr))
404 , m_xPreviewWin(new RubyPreview)
405 , m_xPreview(new weld::CustomWeld(*m_xBuilder, u"preview"_ustr, *m_xPreviewWin))
407 m_xCharStyleLB->make_sorted();
408 m_xPreviewWin->setRubyDialog(this);
409 m_xScrolledWindow->set_size_request(-1, m_xGrid->get_preferred_size().Height());
410 m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
412 aEditArr[0] = m_xLeft1ED.get();
413 aEditArr[1] = m_xRight1ED.get();
414 aEditArr[2] = m_xLeft2ED.get();
415 aEditArr[3] = m_xRight2ED.get();
416 aEditArr[4] = m_xLeft3ED.get();
417 aEditArr[5] = m_xRight3ED.get();
418 aEditArr[6] = m_xLeft4ED.get();
419 aEditArr[7] = m_xRight4ED.get();
421 m_xSelectionGroupPB->connect_clicked(LINK(this, SvxRubyDialog, SelectionGroup_Impl));
422 m_xSelectionMonoPB->connect_clicked(LINK(this, SvxRubyDialog, SelectionMono_Impl));
423 m_xApplyPB->connect_clicked(LINK(this, SvxRubyDialog, ApplyHdl_Impl));
424 m_xClosePB->connect_clicked(LINK(this, SvxRubyDialog, CloseHdl_Impl));
425 m_xStylistPB->connect_clicked(LINK(this, SvxRubyDialog, StylistHdl_Impl));
426 m_xAdjustLB->connect_changed(LINK(this, SvxRubyDialog, AdjustHdl_Impl));
427 m_xPositionLB->connect_changed(LINK(this, SvxRubyDialog, PositionHdl_Impl));
428 m_xCharStyleLB->connect_changed(LINK(this, SvxRubyDialog, CharStyleHdl_Impl));
430 Link<weld::ScrolledWindow&, void> aScrLk(LINK(this, SvxRubyDialog, ScrollHdl_Impl));
431 m_xScrolledWindow->connect_vadjustment_changed(aScrLk);
433 Link<weld::Entry&, void> aEditLk(LINK(this, SvxRubyDialog, EditModifyHdl_Impl));
434 Link<weld::Widget&, void> aFocusLk(LINK(this, SvxRubyDialog, EditFocusHdl_Impl));
435 Link<const KeyEvent&, bool> aKeyUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownHdl_Impl));
436 Link<const KeyEvent&, bool> aKeyTabUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownTabHdl_Impl));
437 for (sal_uInt16 i = 0; i < 8; i++)
439 aEditArr[i]->connect_changed(aEditLk);
440 aEditArr[i]->connect_focus_in(aFocusLk);
441 if (!i || 7 == i)
442 aEditArr[i]->connect_key_press(aKeyTabUpDownLk);
443 else
444 aEditArr[i]->connect_key_press(aKeyUpDownLk);
448 SvxRubyDialog::~SvxRubyDialog()
450 ClearCharStyleList();
451 EventObject aEvent;
452 m_pImpl->disposing(aEvent);
455 void SvxRubyDialog::ClearCharStyleList() { m_xCharStyleLB->clear(); }
457 void SvxRubyDialog::Close()
459 if (IsClosing())
460 return;
461 SfxViewFrame* pViewFrame = SfxViewFrame::Current();
462 if (pViewFrame)
463 pViewFrame->ToggleChildWindow(SID_RUBY_DIALOG);
466 void SvxRubyDialog::Activate()
468 SfxModelessDialogController::Activate();
469 if (m_pImpl->IsDisposing())
471 // tdf#141967/tdf#152495 if Activate is called during tear down bail early
472 return;
475 //get selection from current view frame
476 SfxViewFrame* pCurFrm = SfxViewFrame::Current();
477 Reference<XController> xCtrl(pCurFrm ? pCurFrm->GetFrame().GetController() : nullptr);
478 m_pImpl->SetController(xCtrl);
479 if (!m_pImpl->HasSelectionChanged())
480 return;
482 Reference<XRubySelection> xRubySel = m_pImpl->GetRubySelection();
483 m_pImpl->UpdateRubyValues();
484 EnableControls(xRubySel.is());
485 if (xRubySel.is())
487 Reference<XModel> xModel = m_pImpl->GetModel();
488 const OUString sCharStyleSelect = m_xCharStyleLB->get_active_text();
489 ClearCharStyleList();
490 Reference<XStyleFamiliesSupplier> xSupplier(xModel, UNO_QUERY);
491 if (xSupplier.is())
495 Reference<XNameAccess> xFam = xSupplier->getStyleFamilies();
496 Any aChar = xFam->getByName(u"CharacterStyles"_ustr);
497 Reference<XNameContainer> xChar;
498 aChar >>= xChar;
499 Reference<XIndexAccess> xCharIdx(xChar, UNO_QUERY);
500 if (xCharIdx.is())
502 OUString sUIName(u"DisplayName"_ustr);
503 for (sal_Int32 nStyle = 0; nStyle < xCharIdx->getCount(); nStyle++)
505 Any aStyle = xCharIdx->getByIndex(nStyle);
506 Reference<XStyle> xStyle;
507 aStyle >>= xStyle;
508 Reference<XPropertySet> xPrSet(xStyle, UNO_QUERY);
509 OUString sName, sCoreName;
510 if (xPrSet.is())
512 Reference<XPropertySetInfo> xInfo = xPrSet->getPropertySetInfo();
513 if (xInfo->hasPropertyByName(sUIName))
515 Any aName = xPrSet->getPropertyValue(sUIName);
516 aName >>= sName;
519 if (xStyle.is())
521 sCoreName = xStyle->getName();
522 if (sName.isEmpty())
523 sName = sCoreName;
525 if (!sName.isEmpty())
527 m_xCharStyleLB->append(sCoreName, sName);
532 catch (const Exception&)
534 TOOLS_WARN_EXCEPTION("svx.dialog", "exception in style access");
536 if (!sCharStyleSelect.isEmpty())
537 m_xCharStyleLB->set_active_text(sCharStyleSelect);
539 m_xCharStyleLB->set_sensitive(xSupplier.is());
540 m_xCharStyleFT->set_sensitive(xSupplier.is());
542 Update();
543 m_xPreviewWin->Invalidate();
546 void SvxRubyDialog::SetRubyText(sal_Int32 nPos, weld::Entry& rLeft, weld::Entry& rRight)
548 OUString sLeft, sRight;
549 const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
550 bool bEnable = aRubyValues.getLength() > nPos;
551 if (bEnable)
553 const Sequence<PropertyValue> aProps = aRubyValues.getConstArray()[nPos];
554 for (const PropertyValue& rProp : aProps)
556 if (rProp.Name == cRubyBaseText)
557 rProp.Value >>= sLeft;
558 else if (rProp.Name == cRubyText)
559 rProp.Value >>= sRight;
562 else if (!nPos)
564 bEnable = true;
566 rLeft.set_sensitive(bEnable);
567 rRight.set_sensitive(bEnable);
568 rLeft.set_text(sLeft);
569 rRight.set_text(sRight);
570 rLeft.save_value();
571 rRight.save_value();
574 void SvxRubyDialog::GetRubyText()
576 tools::Long nTempLastPos = GetLastPos();
577 Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
578 auto aRubyValuesRange = asNonConstRange(aRubyValues);
579 for (int i = 0; i < 8; i += 2)
581 if (aEditArr[i]->get_sensitive()
582 && (aEditArr[i]->get_value_changed_from_saved()
583 || aEditArr[i + 1]->get_value_changed_from_saved()))
585 DBG_ASSERT(aRubyValues.getLength() > (i / 2 + nTempLastPos), "wrong index");
586 SetModified(true);
587 for (PropertyValue& propVal : asNonConstRange(aRubyValuesRange[i / 2 + nTempLastPos]))
589 if (propVal.Name == cRubyBaseText)
590 propVal.Value <<= aEditArr[i]->get_text();
591 else if (propVal.Name == cRubyText)
592 propVal.Value <<= aEditArr[i + 1]->get_text();
598 void SvxRubyDialog::Update()
600 // Only enable selection grouping options when they can be applied
601 m_xSelectionGroupPB->set_sensitive(!m_pImpl->IsSelectionGrouped());
602 m_xSelectionMonoPB->set_sensitive(!m_pImpl->IsSelectionMono());
604 const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
605 sal_Int32 nLen = aRubyValues.getLength();
606 m_xScrolledWindow->vadjustment_configure(0, 0, !nLen ? 1 : nLen, 1, 4, 4);
607 if (nLen > 4)
608 m_xScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS);
609 else
610 m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
611 SetLastPos(0);
612 SetModified(false);
614 sal_Int16 nAdjust = -1;
615 sal_Int16 nPosition = -1;
616 OUString sCharStyleName, sTmp;
617 bool bCharStyleEqual = true;
618 for (sal_Int32 nRuby = 0; nRuby < nLen; nRuby++)
620 const Sequence<PropertyValue>& rProps = aRubyValues.getConstArray()[nRuby];
621 for (const PropertyValue& rProp : rProps)
623 if (nAdjust > -2 && rProp.Name == cRubyAdjust)
625 sal_Int16 nTmp = sal_Int16();
626 rProp.Value >>= nTmp;
627 if (!nRuby)
628 nAdjust = nTmp;
629 else if (nAdjust != nTmp)
630 nAdjust = -2;
632 if (nPosition > -2 && rProp.Name == cRubyPosition)
634 sal_Int16 nTmp = sal_Int16();
635 rProp.Value >>= nTmp;
636 if (!nRuby)
637 nPosition = nTmp;
638 else if (nPosition != nTmp)
639 nPosition = -2;
641 if (bCharStyleEqual && rProp.Name == cRubyCharStyleName)
643 rProp.Value >>= sTmp;
644 if (!nRuby)
645 sCharStyleName = sTmp;
646 else if (sCharStyleName != sTmp)
647 bCharStyleEqual = false;
651 if (!nLen)
653 //enable selection if the ruby list is empty
654 nAdjust = 0;
655 nPosition = 0;
657 if (nAdjust > -1)
658 m_xAdjustLB->set_active(nAdjust);
659 else
660 m_xAdjustLB->set_active(-1);
661 if (nPosition > -1)
662 m_xPositionLB->set_active(nPosition);
663 if (!nLen || (bCharStyleEqual && sCharStyleName.isEmpty()))
664 sCharStyleName = "Rubies";
665 if (!sCharStyleName.isEmpty())
667 for (int i = 0, nEntryCount = m_xCharStyleLB->get_count(); i < nEntryCount; i++)
669 OUString sCoreName = m_xCharStyleLB->get_id(i);
670 if (sCharStyleName == sCoreName)
672 m_xCharStyleLB->set_active(i);
673 break;
677 else
678 m_xCharStyleLB->set_active(-1);
680 ScrollHdl_Impl(*m_xScrolledWindow);
683 void SvxRubyDialog::GetCurrentText(OUString& rBase, OUString& rRuby)
685 rBase = aEditArr[nCurrentEdit * 2]->get_text();
686 rRuby = aEditArr[nCurrentEdit * 2 + 1]->get_text();
689 IMPL_LINK(SvxRubyDialog, ScrollHdl_Impl, weld::ScrolledWindow&, rScroll, void)
691 int nPos = rScroll.vadjustment_get_value();
692 if (GetLastPos() != nPos)
694 GetRubyText();
696 SetRubyText(nPos++, *m_xLeft1ED, *m_xRight1ED);
697 SetRubyText(nPos++, *m_xLeft2ED, *m_xRight2ED);
698 SetRubyText(nPos++, *m_xLeft3ED, *m_xRight3ED);
699 SetRubyText(nPos, *m_xLeft4ED, *m_xRight4ED);
700 SetLastPos(nPos - 3);
701 m_xPreviewWin->Invalidate();
704 IMPL_LINK_NOARG(SvxRubyDialog, SelectionGroup_Impl, weld::Button&, void)
706 m_pImpl->MakeSelectionGrouped();
707 Update();
710 IMPL_LINK_NOARG(SvxRubyDialog, SelectionMono_Impl, weld::Button&, void)
712 m_pImpl->MakeSelectionMono();
713 Update();
716 IMPL_LINK_NOARG(SvxRubyDialog, ApplyHdl_Impl, weld::Button&, void)
718 const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
719 if (!aRubyValues.hasElements())
721 AssertOneEntry();
722 PositionHdl_Impl(*m_xPositionLB);
723 AdjustHdl_Impl(*m_xAdjustLB);
724 CharStyleHdl_Impl(*m_xCharStyleLB);
726 GetRubyText();
727 //reset all edit fields - SaveValue is called
728 ScrollHdl_Impl(*m_xScrolledWindow);
730 Reference<XRubySelection> xSelection = m_pImpl->GetRubySelection();
731 if (IsModified() && xSelection.is())
735 xSelection->setRubyList(aRubyValues, false);
737 catch (const Exception&)
739 TOOLS_WARN_EXCEPTION("svx.dialog", "");
744 IMPL_LINK_NOARG(SvxRubyDialog, CloseHdl_Impl, weld::Button&, void) { Close(); }
746 IMPL_LINK_NOARG(SvxRubyDialog, StylistHdl_Impl, weld::Button&, void)
748 std::unique_ptr<SfxBoolItem> pState;
749 SfxItemState eState = pBindings->QueryState(SID_STYLE_DESIGNER, pState);
750 if (eState <= SfxItemState::SET || !pState || !pState->GetValue())
752 pBindings->GetDispatcher()->Execute(SID_STYLE_DESIGNER,
753 SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
757 IMPL_LINK(SvxRubyDialog, AdjustHdl_Impl, weld::ComboBox&, rBox, void)
759 AssertOneEntry();
760 sal_Int16 nAdjust = rBox.get_active();
761 for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
763 for (PropertyValue& propVal : asNonConstRange(rProps))
765 if (propVal.Name == cRubyAdjust)
766 propVal.Value <<= nAdjust;
768 SetModified(true);
770 m_xPreviewWin->Invalidate();
773 IMPL_LINK(SvxRubyDialog, PositionHdl_Impl, weld::ComboBox&, rBox, void)
775 AssertOneEntry();
776 sal_Int16 nPosition = rBox.get_active();
777 for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
779 for (PropertyValue& propVal : asNonConstRange(rProps))
781 if (propVal.Name == cRubyPosition)
782 propVal.Value <<= nPosition;
784 SetModified(true);
786 m_xPreviewWin->Invalidate();
789 IMPL_LINK_NOARG(SvxRubyDialog, CharStyleHdl_Impl, weld::ComboBox&, void)
791 AssertOneEntry();
792 OUString sStyleName;
793 if (m_xCharStyleLB->get_active() != -1)
794 sStyleName = m_xCharStyleLB->get_active_id();
795 for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
797 for (PropertyValue& propVal : asNonConstRange(rProps))
799 if (propVal.Name == cRubyCharStyleName)
801 propVal.Value <<= sStyleName;
804 SetModified(true);
808 IMPL_LINK(SvxRubyDialog, EditFocusHdl_Impl, weld::Widget&, rEdit, void)
810 for (sal_uInt16 i = 0; i < 8; i++)
812 if (&rEdit == aEditArr[i])
814 nCurrentEdit = i / 2;
815 break;
818 m_xPreviewWin->Invalidate();
821 IMPL_LINK(SvxRubyDialog, EditModifyHdl_Impl, weld::Entry&, rEdit, void)
823 EditFocusHdl_Impl(rEdit);
826 bool SvxRubyDialog::EditScrollHdl_Impl(sal_Int32 nParam)
828 bool bRet = false;
829 //scroll forward
830 if (nParam > 0 && (aEditArr[7]->has_focus() || aEditArr[6]->has_focus()))
832 if (m_xScrolledWindow->vadjustment_get_upper()
833 > m_xScrolledWindow->vadjustment_get_value()
834 + m_xScrolledWindow->vadjustment_get_page_size())
836 m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value()
837 + 1);
838 aEditArr[6]->grab_focus();
839 bRet = true;
842 //scroll backward
843 else if (m_xScrolledWindow->vadjustment_get_value()
844 && (aEditArr[0]->has_focus() || aEditArr[1]->has_focus()))
846 m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value() - 1);
847 aEditArr[1]->grab_focus();
848 bRet = true;
850 if (bRet)
851 ScrollHdl_Impl(*m_xScrolledWindow);
852 return bRet;
855 bool SvxRubyDialog::EditJumpHdl_Impl(sal_Int32 nParam)
857 bool bHandled = false;
858 sal_uInt16 nIndex = USHRT_MAX;
859 for (sal_uInt16 i = 0; i < 8; i++)
861 if (aEditArr[i]->has_focus())
862 nIndex = i;
864 if (nIndex < 8)
866 if (nParam > 0)
868 if (nIndex < 6)
869 aEditArr[nIndex + 2]->grab_focus();
870 else if (EditScrollHdl_Impl(nParam))
871 aEditArr[nIndex]->grab_focus();
873 else
875 if (nIndex > 1)
876 aEditArr[nIndex - 2]->grab_focus();
877 else if (EditScrollHdl_Impl(nParam))
878 aEditArr[nIndex]->grab_focus();
880 bHandled = true;
882 return bHandled;
885 void SvxRubyDialog::AssertOneEntry() { m_pImpl->AssertOneEntry(); }
887 void SvxRubyDialog::EnableControls(bool bEnable)
889 m_xContentArea->set_sensitive(bEnable);
890 m_xApplyPB->set_sensitive(bEnable);
893 RubyPreview::RubyPreview()
894 : m_pParentDlg(nullptr)
898 RubyPreview::~RubyPreview() {}
900 void RubyPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
902 rRenderContext.Push(vcl::PushFlags::ALL);
904 rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
906 Size aWinSize = rRenderContext.GetOutputSize();
908 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
909 svtools::ColorConfig aColorConfig;
911 Color aNewTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
912 Color aNewFillColor(rStyleSettings.GetWindowColor());
914 vcl::Font aFont = rRenderContext.GetFont();
915 aFont.SetFontHeight(aWinSize.Height() / 4);
916 aFont.SetFillColor(aNewFillColor);
917 aFont.SetColor(aNewTextColor);
918 rRenderContext.SetFont(aFont);
920 tools::Rectangle aRect(Point(0, 0), aWinSize);
921 rRenderContext.SetLineColor();
922 rRenderContext.SetFillColor(aFont.GetFillColor());
923 rRenderContext.DrawRect(aRect);
925 OUString sBaseText, sRubyText;
926 m_pParentDlg->GetCurrentText(sBaseText, sRubyText);
928 tools::Long nTextHeight = rRenderContext.GetTextHeight();
929 tools::Long nBaseWidth = rRenderContext.GetTextWidth(sBaseText);
931 vcl::Font aRubyFont(aFont);
932 aRubyFont.SetFontHeight(aRubyFont.GetFontHeight() * 70 / 100);
933 rRenderContext.SetFont(aRubyFont);
934 tools::Long nRubyWidth = rRenderContext.GetTextWidth(sRubyText);
935 rRenderContext.SetFont(aFont);
937 RubyAdjust nAdjust = static_cast<RubyAdjust>(m_pParentDlg->m_xAdjustLB->get_active());
938 //use center if no adjustment is available
939 if (nAdjust > RubyAdjust_INDENT_BLOCK)
940 nAdjust = RubyAdjust_CENTER;
942 //which part is stretched ?
943 bool bRubyStretch = nBaseWidth >= nRubyWidth;
945 tools::Long nCenter = aWinSize.Width() / 2;
946 tools::Long nHalfWidth = std::max(nBaseWidth, nRubyWidth) / 2;
947 tools::Long nLeftStart = nCenter - nHalfWidth;
948 tools::Long nRightEnd = nCenter + nHalfWidth;
950 // Default values for TOP or no selection
951 tools::Long nYRuby = aWinSize.Height() / 4 - nTextHeight / 2;
952 tools::Long nYBase = aWinSize.Height() * 3 / 4 - nTextHeight / 2;
954 sal_Int16 nRubyPos = m_pParentDlg->m_xPositionLB->get_active();
955 if (nRubyPos == 1) // BOTTOM
956 std::swap(nYRuby, nYBase);
957 else if (nRubyPos == 2) // RIGHT ( vertically )
959 // Align the ruby text and base text to the vertical center.
960 nYBase = (aWinSize.Height() - nTextHeight) / 2;
961 nYRuby = (aWinSize.Height() - nRubyWidth) / 2;
963 // Align the ruby text at the right side of the base text
964 nAdjust = RubyAdjust_RIGHT;
965 nHalfWidth = nBaseWidth / 2;
966 nLeftStart = nCenter - nHalfWidth;
967 nRightEnd = nCenter + nHalfWidth + nRubyWidth + nTextHeight;
968 // Render base text first, then render ruby text on the right.
969 bRubyStretch = true;
971 aRubyFont.SetVertical(true);
972 aRubyFont.SetOrientation(2700_deg10);
975 tools::Long nYOutput;
976 tools::Long nOutTextWidth;
977 OUString sOutputText;
979 if (bRubyStretch)
981 rRenderContext.DrawText(Point(nLeftStart, nYBase), sBaseText);
982 nYOutput = nYRuby;
983 sOutputText = sRubyText;
984 nOutTextWidth = nRubyWidth;
985 rRenderContext.SetFont(aRubyFont);
987 else
989 rRenderContext.SetFont(aRubyFont);
990 rRenderContext.DrawText(Point(nLeftStart, nYRuby), sRubyText);
991 nYOutput = nYBase;
992 sOutputText = sBaseText;
993 nOutTextWidth = nBaseWidth;
994 rRenderContext.SetFont(aFont);
997 switch (nAdjust)
999 case RubyAdjust_LEFT:
1000 rRenderContext.DrawText(Point(nLeftStart, nYOutput), sOutputText);
1001 break;
1002 case RubyAdjust_RIGHT:
1003 rRenderContext.DrawText(Point(nRightEnd - nOutTextWidth, nYOutput), sOutputText);
1004 break;
1005 case RubyAdjust_INDENT_BLOCK:
1007 tools::Long nCharWidth = rRenderContext.GetTextWidth(u"X"_ustr);
1008 if (nOutTextWidth < (nRightEnd - nLeftStart - nCharWidth))
1010 nCharWidth /= 2;
1011 nLeftStart += nCharWidth;
1012 nRightEnd -= nCharWidth;
1014 [[fallthrough]];
1016 case RubyAdjust_BLOCK:
1018 if (sOutputText.getLength() > 1)
1020 sal_Int32 nCount = sOutputText.getLength();
1021 tools::Long nSpace
1022 = ((nRightEnd - nLeftStart) - rRenderContext.GetTextWidth(sOutputText))
1023 / (nCount - 1);
1024 for (sal_Int32 i = 0; i < nCount; i++)
1026 OUString sChar(sOutputText[i]);
1027 rRenderContext.DrawText(Point(nLeftStart, nYOutput), sChar);
1028 tools::Long nCharWidth = rRenderContext.GetTextWidth(sChar);
1029 nLeftStart += nCharWidth + nSpace;
1031 break;
1033 [[fallthrough]];
1035 case RubyAdjust_CENTER:
1036 rRenderContext.DrawText(Point(nCenter - nOutTextWidth / 2, nYOutput), sOutputText);
1037 break;
1038 default:
1039 break;
1041 rRenderContext.Pop();
1044 void RubyPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
1046 pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 40,
1047 pDrawingArea->get_text_height() * 7);
1048 CustomWidgetController::SetDrawingArea(pDrawingArea);
1051 IMPL_LINK(SvxRubyDialog, KeyUpDownHdl_Impl, const KeyEvent&, rKEvt, bool)
1053 bool bHandled = false;
1054 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
1055 sal_uInt16 nCode = rKeyCode.GetCode();
1056 if (KEY_UP == nCode || KEY_DOWN == nCode)
1058 sal_Int32 nParam = KEY_UP == nCode ? -1 : 1;
1059 bHandled = EditJumpHdl_Impl(nParam);
1061 return bHandled;
1064 IMPL_LINK(SvxRubyDialog, KeyUpDownTabHdl_Impl, const KeyEvent&, rKEvt, bool)
1066 bool bHandled = false;
1067 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
1068 sal_uInt16 nMod = rKeyCode.GetModifier();
1069 sal_uInt16 nCode = rKeyCode.GetCode();
1070 if (nCode == KEY_TAB && (!nMod || KEY_SHIFT == nMod))
1072 sal_Int32 nParam = KEY_SHIFT == nMod ? -1 : 1;
1073 if (EditScrollHdl_Impl(nParam))
1074 bHandled = true;
1076 if (!bHandled)
1077 bHandled = KeyUpDownHdl_Impl(rKEvt);
1078 return bHandled;
1081 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */