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 <tools/diagnose_ex.h>
23 
24 #include <svx/rubydialog.hxx>
25 #include <sfx2/dispatch.hxx>
26 #include <sfx2/sfxsids.hrc>
27 #include <sfx2/viewfrm.hxx>
28 #include <sfx2/viewsh.hxx>
29 #include <svl/eitem.hxx>
30 #include <com/sun/star/frame/XController.hpp>
31 #include <com/sun/star/style/XStyle.hpp>
32 #include <com/sun/star/text/XRubySelection.hpp>
33 #include <com/sun/star/beans/PropertyValues.hpp>
34 #include <com/sun/star/beans/XPropertySet.hpp>
35 #include <com/sun/star/beans/XPropertySetInfo.hpp>
36 #include <com/sun/star/container/XNameContainer.hpp>
37 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
38 #include <com/sun/star/text/RubyAdjust.hpp>
39 #include <com/sun/star/view/XSelectionChangeListener.hpp>
40 #include <com/sun/star/view/XSelectionSupplier.hpp>
41 #include <cppuhelper/implbase.hxx>
42 #include <svtools/colorcfg.hxx>
43 #include <vcl/event.hxx>
44 #include <vcl/settings.hxx>
45 #include <vcl/svapp.hxx>
46 
47 using namespace css::uno;
48 using namespace css::frame;
49 using namespace css::text;
50 using namespace css::beans;
51 using namespace css::style;
52 using namespace css::view;
53 using namespace css::lang;
54 using namespace css::container;
55 
56 SFX_IMPL_CHILDWINDOW(SvxRubyChildWindow, SID_RUBY_DIALOG);
57 
58 namespace
59 {
60 constexpr OUStringLiteral cRubyBaseText = u"RubyBaseText";
61 constexpr OUStringLiteral cRubyText = u"RubyText";
62 constexpr OUStringLiteral cRubyAdjust = u"RubyAdjust";
63 constexpr OUStringLiteral cRubyPosition = u"RubyPosition";
64 constexpr OUStringLiteral cRubyCharStyleName = u"RubyCharStyleName";
65 
66 } // end anonymous namespace
67 
SvxRubyChildWindow(vcl::Window * _pParent,sal_uInt16 nId,SfxBindings * pBindings,SfxChildWinInfo const * pInfo)68 SvxRubyChildWindow::SvxRubyChildWindow(vcl::Window* _pParent, sal_uInt16 nId,
69                                        SfxBindings* pBindings, SfxChildWinInfo const* pInfo)
70     : SfxChildWindow(_pParent, nId)
71 {
72     auto xDlg = std::make_shared<SvxRubyDialog>(pBindings, this, _pParent->GetFrameWeld());
73     SetController(xDlg);
74     xDlg->Initialize(pInfo);
75 }
76 
GetInfo() const77 SfxChildWinInfo SvxRubyChildWindow::GetInfo() const { return SfxChildWindow::GetInfo(); }
78 
79 class SvxRubyData_Impl : public cppu::WeakImplHelper<css::view::XSelectionChangeListener>
80 {
81     Reference<XModel> xModel;
82     Reference<XRubySelection> xSelection;
83     Sequence<PropertyValues> aRubyValues;
84     Reference<XController> xController;
85     bool bHasSelectionChanged;
86 
87 public:
88     SvxRubyData_Impl();
89     virtual ~SvxRubyData_Impl() override;
90 
91     void SetController(const Reference<XController>& xCtrl);
GetModel()92     Reference<XModel> const& GetModel()
93     {
94         if (!xController.is())
95             xModel = nullptr;
96         else
97             xModel = xController->getModel();
98         return xModel;
99     }
HasSelectionChanged() const100     bool HasSelectionChanged() const { return bHasSelectionChanged; }
GetRubySelection()101     Reference<XRubySelection> const& GetRubySelection()
102     {
103         xSelection.set(xController, UNO_QUERY);
104         return xSelection;
105     }
UpdateRubyValues()106     void UpdateRubyValues()
107     {
108         if (!xSelection.is())
109             aRubyValues.realloc(0);
110         else
111             aRubyValues = xSelection->getRubyList(false);
112         bHasSelectionChanged = false;
113     }
GetRubyValues()114     Sequence<PropertyValues>& GetRubyValues() { return aRubyValues; }
115     void AssertOneEntry();
116 
117     virtual void SAL_CALL selectionChanged(const css::lang::EventObject& aEvent) override;
118     virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
119 };
120 
SvxRubyData_Impl()121 SvxRubyData_Impl::SvxRubyData_Impl()
122     : bHasSelectionChanged(false)
123 {
124 }
125 
~SvxRubyData_Impl()126 SvxRubyData_Impl::~SvxRubyData_Impl() {}
127 
SetController(const Reference<XController> & xCtrl)128 void SvxRubyData_Impl::SetController(const Reference<XController>& xCtrl)
129 {
130     if (xCtrl.get() == xController.get())
131         return;
132 
133     try
134     {
135         Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
136         if (xSelSupp.is())
137             xSelSupp->removeSelectionChangeListener(this);
138 
139         bHasSelectionChanged = true;
140         xController = xCtrl;
141         xSelSupp.set(xController, UNO_QUERY);
142         if (xSelSupp.is())
143             xSelSupp->addSelectionChangeListener(this);
144     }
145     catch (const Exception&)
146     {
147     }
148 }
149 
selectionChanged(const EventObject &)150 void SvxRubyData_Impl::selectionChanged(const EventObject&) { bHasSelectionChanged = true; }
151 
disposing(const EventObject &)152 void SvxRubyData_Impl::disposing(const EventObject&)
153 {
154     try
155     {
156         Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
157         if (xSelSupp.is())
158             xSelSupp->removeSelectionChangeListener(this);
159     }
160     catch (const Exception&)
161     {
162     }
163     xController = nullptr;
164 }
165 
AssertOneEntry()166 void SvxRubyData_Impl::AssertOneEntry()
167 {
168     //create one entry
169     if (!aRubyValues.hasElements())
170     {
171         aRubyValues.realloc(1);
172         Sequence<PropertyValue>& rValues = aRubyValues.getArray()[0];
173         rValues.realloc(5);
174         PropertyValue* pValues = rValues.getArray();
175         pValues[0].Name = cRubyBaseText;
176         pValues[1].Name = cRubyText;
177         pValues[2].Name = cRubyAdjust;
178         pValues[3].Name = cRubyPosition;
179         pValues[4].Name = cRubyCharStyleName;
180     }
181 }
182 
SvxRubyDialog(SfxBindings * pBind,SfxChildWindow * pCW,weld::Window * pParent)183 SvxRubyDialog::SvxRubyDialog(SfxBindings* pBind, SfxChildWindow* pCW, weld::Window* pParent)
184     : SfxModelessDialogController(pBind, pCW, pParent, "svx/ui/asianphoneticguidedialog.ui",
185                                   "AsianPhoneticGuideDialog")
186     , nLastPos(0)
187     , nCurrentEdit(0)
188     , bModified(false)
189     , pBindings(pBind)
190     , m_pImpl(new SvxRubyData_Impl)
191     , m_xLeftFT(m_xBuilder->weld_label("basetextft"))
192     , m_xRightFT(m_xBuilder->weld_label("rubytextft"))
193     , m_xLeft1ED(m_xBuilder->weld_entry("Left1ED"))
194     , m_xRight1ED(m_xBuilder->weld_entry("Right1ED"))
195     , m_xLeft2ED(m_xBuilder->weld_entry("Left2ED"))
196     , m_xRight2ED(m_xBuilder->weld_entry("Right2ED"))
197     , m_xLeft3ED(m_xBuilder->weld_entry("Left3ED"))
198     , m_xRight3ED(m_xBuilder->weld_entry("Right3ED"))
199     , m_xLeft4ED(m_xBuilder->weld_entry("Left4ED"))
200     , m_xRight4ED(m_xBuilder->weld_entry("Right4ED"))
201     , m_xScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow", true))
202     , m_xAdjustLB(m_xBuilder->weld_combo_box("adjustlb"))
203     , m_xPositionLB(m_xBuilder->weld_combo_box("positionlb"))
204     , m_xCharStyleFT(m_xBuilder->weld_label("styleft"))
205     , m_xCharStyleLB(m_xBuilder->weld_combo_box("stylelb"))
206     , m_xStylistPB(m_xBuilder->weld_button("styles"))
207     , m_xApplyPB(m_xBuilder->weld_button("ok"))
208     , m_xClosePB(m_xBuilder->weld_button("close"))
209     , m_xContentArea(m_xDialog->weld_content_area())
210     , m_xGrid(m_xBuilder->weld_widget("grid"))
211     , m_xPreviewWin(new RubyPreview)
212     , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", *m_xPreviewWin))
213 {
214     m_xCharStyleLB->make_sorted();
215     m_xPreviewWin->setRubyDialog(this);
216     m_xScrolledWindow->set_size_request(-1, m_xGrid->get_preferred_size().Height());
217     m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
218 
219     aEditArr[0] = m_xLeft1ED.get();
220     aEditArr[1] = m_xRight1ED.get();
221     aEditArr[2] = m_xLeft2ED.get();
222     aEditArr[3] = m_xRight2ED.get();
223     aEditArr[4] = m_xLeft3ED.get();
224     aEditArr[5] = m_xRight3ED.get();
225     aEditArr[6] = m_xLeft4ED.get();
226     aEditArr[7] = m_xRight4ED.get();
227 
228     m_xApplyPB->connect_clicked(LINK(this, SvxRubyDialog, ApplyHdl_Impl));
229     m_xClosePB->connect_clicked(LINK(this, SvxRubyDialog, CloseHdl_Impl));
230     m_xStylistPB->connect_clicked(LINK(this, SvxRubyDialog, StylistHdl_Impl));
231     m_xAdjustLB->connect_changed(LINK(this, SvxRubyDialog, AdjustHdl_Impl));
232     m_xPositionLB->connect_changed(LINK(this, SvxRubyDialog, PositionHdl_Impl));
233     m_xCharStyleLB->connect_changed(LINK(this, SvxRubyDialog, CharStyleHdl_Impl));
234 
235     Link<weld::ScrolledWindow&, void> aScrLk(LINK(this, SvxRubyDialog, ScrollHdl_Impl));
236     m_xScrolledWindow->connect_vadjustment_changed(aScrLk);
237 
238     Link<weld::Entry&, void> aEditLk(LINK(this, SvxRubyDialog, EditModifyHdl_Impl));
239     Link<weld::Widget&, void> aFocusLk(LINK(this, SvxRubyDialog, EditFocusHdl_Impl));
240     Link<const KeyEvent&, bool> aKeyUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownHdl_Impl));
241     Link<const KeyEvent&, bool> aKeyTabUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownTabHdl_Impl));
242     for (sal_uInt16 i = 0; i < 8; i++)
243     {
244         aEditArr[i]->connect_changed(aEditLk);
245         aEditArr[i]->connect_focus_in(aFocusLk);
246         if (!i || 7 == i)
247             aEditArr[i]->connect_key_press(aKeyTabUpDownLk);
248         else
249             aEditArr[i]->connect_key_press(aKeyUpDownLk);
250     }
251 }
252 
~SvxRubyDialog()253 SvxRubyDialog::~SvxRubyDialog()
254 {
255     ClearCharStyleList();
256     EventObject aEvent;
257     m_pImpl->disposing(aEvent);
258 }
259 
ClearCharStyleList()260 void SvxRubyDialog::ClearCharStyleList() { m_xCharStyleLB->clear(); }
261 
Close()262 void SvxRubyDialog::Close()
263 {
264     if (IsClosing())
265         return;
266     SfxViewFrame* pViewFrame = SfxViewFrame::Current();
267     if (pViewFrame)
268         pViewFrame->ToggleChildWindow(SID_RUBY_DIALOG);
269 }
270 
Activate()271 void SvxRubyDialog::Activate()
272 {
273     SfxModelessDialogController::Activate();
274     if (!m_xContentArea)
275     {
276         // tdf#141967 if Activate is called during tear down bail early
277         return;
278     }
279     //get selection from current view frame
280     SfxViewFrame* pCurFrm = SfxViewFrame::Current();
281     Reference<XController> xCtrl = pCurFrm->GetFrame().GetController();
282     m_pImpl->SetController(xCtrl);
283     if (!m_pImpl->HasSelectionChanged())
284         return;
285 
286     Reference<XRubySelection> xRubySel = m_pImpl->GetRubySelection();
287     m_pImpl->UpdateRubyValues();
288     EnableControls(xRubySel.is());
289     if (xRubySel.is())
290     {
291         Reference<XModel> xModel = m_pImpl->GetModel();
292         const OUString sCharStyleSelect = m_xCharStyleLB->get_active_text();
293         ClearCharStyleList();
294         Reference<XStyleFamiliesSupplier> xSupplier(xModel, UNO_QUERY);
295         if (xSupplier.is())
296         {
297             try
298             {
299                 Reference<XNameAccess> xFam = xSupplier->getStyleFamilies();
300                 Any aChar = xFam->getByName("CharacterStyles");
301                 Reference<XNameContainer> xChar;
302                 aChar >>= xChar;
303                 Reference<XIndexAccess> xCharIdx(xChar, UNO_QUERY);
304                 if (xCharIdx.is())
305                 {
306                     OUString sUIName("DisplayName");
307                     for (sal_Int32 nStyle = 0; nStyle < xCharIdx->getCount(); nStyle++)
308                     {
309                         Any aStyle = xCharIdx->getByIndex(nStyle);
310                         Reference<XStyle> xStyle;
311                         aStyle >>= xStyle;
312                         Reference<XPropertySet> xPrSet(xStyle, UNO_QUERY);
313                         OUString sName, sCoreName;
314                         if (xPrSet.is())
315                         {
316                             Reference<XPropertySetInfo> xInfo = xPrSet->getPropertySetInfo();
317                             if (xInfo->hasPropertyByName(sUIName))
318                             {
319                                 Any aName = xPrSet->getPropertyValue(sUIName);
320                                 aName >>= sName;
321                             }
322                         }
323                         if (xStyle.is())
324                         {
325                             sCoreName = xStyle->getName();
326                             if (sName.isEmpty())
327                                 sName = sCoreName;
328                         }
329                         if (!sName.isEmpty())
330                         {
331                             m_xCharStyleLB->append(sCoreName, sName);
332                         }
333                     }
334                 }
335             }
336             catch (const Exception&)
337             {
338                 TOOLS_WARN_EXCEPTION("svx.dialog", "exception in style access");
339             }
340             if (!sCharStyleSelect.isEmpty())
341                 m_xCharStyleLB->set_active_text(sCharStyleSelect);
342         }
343         m_xCharStyleLB->set_sensitive(xSupplier.is());
344         m_xCharStyleFT->set_sensitive(xSupplier.is());
345     }
346     Update();
347     m_xPreviewWin->Invalidate();
348 }
349 
SetRubyText(sal_Int32 nPos,weld::Entry & rLeft,weld::Entry & rRight)350 void SvxRubyDialog::SetRubyText(sal_Int32 nPos, weld::Entry& rLeft, weld::Entry& rRight)
351 {
352     OUString sLeft, sRight;
353     const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
354     bool bEnable = aRubyValues.getLength() > nPos;
355     if (bEnable)
356     {
357         const Sequence<PropertyValue> aProps = aRubyValues.getConstArray()[nPos];
358         for (const PropertyValue& rProp : aProps)
359         {
360             if (rProp.Name == cRubyBaseText)
361                 rProp.Value >>= sLeft;
362             else if (rProp.Name == cRubyText)
363                 rProp.Value >>= sRight;
364         }
365     }
366     else if (!nPos)
367     {
368         bEnable = true;
369     }
370     rLeft.set_sensitive(bEnable);
371     rRight.set_sensitive(bEnable);
372     rLeft.set_text(sLeft);
373     rRight.set_text(sRight);
374     rLeft.save_value();
375     rRight.save_value();
376 }
377 
GetRubyText()378 void SvxRubyDialog::GetRubyText()
379 {
380     tools::Long nTempLastPos = GetLastPos();
381     for (int i = 0; i < 8; i += 2)
382     {
383         if (aEditArr[i]->get_sensitive()
384             && (aEditArr[i]->get_value_changed_from_saved()
385                 || aEditArr[i + 1]->get_value_changed_from_saved()))
386         {
387             Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
388             DBG_ASSERT(aRubyValues.getLength() > (i / 2 + nTempLastPos), "wrong index");
389             SetModified(true);
390             Sequence<PropertyValue>& rProps = aRubyValues.getArray()[i / 2 + nTempLastPos];
391             for (PropertyValue& propVal : rProps)
392             {
393                 if (propVal.Name == cRubyBaseText)
394                     propVal.Value <<= aEditArr[i]->get_text();
395                 else if (propVal.Name == cRubyText)
396                     propVal.Value <<= aEditArr[i + 1]->get_text();
397             }
398         }
399     }
400 }
401 
Update()402 void SvxRubyDialog::Update()
403 {
404     const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
405     sal_Int32 nLen = aRubyValues.getLength();
406     m_xScrolledWindow->vadjustment_configure(0, 0, !nLen ? 1 : nLen, 1, 4, 4);
407     if (nLen > 4)
408         m_xScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS);
409     else
410         m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
411     SetLastPos(0);
412     SetModified(false);
413 
414     sal_Int16 nAdjust = -1;
415     sal_Int16 nPosition = -1;
416     OUString sCharStyleName, sTmp;
417     bool bCharStyleEqual = true;
418     for (sal_Int32 nRuby = 0; nRuby < nLen; nRuby++)
419     {
420         const Sequence<PropertyValue>& rProps = aRubyValues.getConstArray()[nRuby];
421         for (const PropertyValue& rProp : rProps)
422         {
423             if (nAdjust > -2 && rProp.Name == cRubyAdjust)
424             {
425                 sal_Int16 nTmp = sal_Int16();
426                 rProp.Value >>= nTmp;
427                 if (!nRuby)
428                     nAdjust = nTmp;
429                 else if (nAdjust != nTmp)
430                     nAdjust = -2;
431             }
432             if (nPosition > -2 && rProp.Name == cRubyPosition)
433             {
434                 sal_Int16 nTmp = sal_Int16();
435                 rProp.Value >>= nTmp;
436                 if (!nRuby)
437                     nPosition = nTmp;
438                 else if (nPosition != nTmp)
439                     nPosition = -2;
440             }
441             if (bCharStyleEqual && rProp.Name == cRubyCharStyleName)
442             {
443                 rProp.Value >>= sTmp;
444                 if (!nRuby)
445                     sCharStyleName = sTmp;
446                 else if (sCharStyleName != sTmp)
447                     bCharStyleEqual = false;
448             }
449         }
450     }
451     if (!nLen)
452     {
453         //enable selection if the ruby list is empty
454         nAdjust = 0;
455         nPosition = 0;
456     }
457     if (nAdjust > -1)
458         m_xAdjustLB->set_active(nAdjust);
459     else
460         m_xAdjustLB->set_active(-1);
461     if (nPosition > -1)
462         m_xPositionLB->set_active(nPosition);
463     if (!nLen || (bCharStyleEqual && sCharStyleName.isEmpty()))
464         sCharStyleName = "Rubies";
465     if (!sCharStyleName.isEmpty())
466     {
467         for (int i = 0, nEntryCount = m_xCharStyleLB->get_count(); i < nEntryCount; i++)
468         {
469             OUString sCoreName = m_xCharStyleLB->get_id(i);
470             if (sCharStyleName == sCoreName)
471             {
472                 m_xCharStyleLB->set_active(i);
473                 break;
474             }
475         }
476     }
477     else
478         m_xCharStyleLB->set_active(-1);
479 
480     ScrollHdl_Impl(*m_xScrolledWindow);
481 }
482 
GetCurrentText(OUString & rBase,OUString & rRuby)483 void SvxRubyDialog::GetCurrentText(OUString& rBase, OUString& rRuby)
484 {
485     rBase = aEditArr[nCurrentEdit * 2]->get_text();
486     rRuby = aEditArr[nCurrentEdit * 2 + 1]->get_text();
487 }
488 
IMPL_LINK(SvxRubyDialog,ScrollHdl_Impl,weld::ScrolledWindow &,rScroll,void)489 IMPL_LINK(SvxRubyDialog, ScrollHdl_Impl, weld::ScrolledWindow&, rScroll, void)
490 {
491     int nPos = rScroll.vadjustment_get_value();
492     if (GetLastPos() != nPos)
493     {
494         GetRubyText();
495     }
496     SetRubyText(nPos++, *m_xLeft1ED, *m_xRight1ED);
497     SetRubyText(nPos++, *m_xLeft2ED, *m_xRight2ED);
498     SetRubyText(nPos++, *m_xLeft3ED, *m_xRight3ED);
499     SetRubyText(nPos, *m_xLeft4ED, *m_xRight4ED);
500     SetLastPos(nPos - 3);
501     m_xPreviewWin->Invalidate();
502 }
503 
IMPL_LINK_NOARG(SvxRubyDialog,ApplyHdl_Impl,weld::Button &,void)504 IMPL_LINK_NOARG(SvxRubyDialog, ApplyHdl_Impl, weld::Button&, void)
505 {
506     const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
507     if (!aRubyValues.hasElements())
508     {
509         AssertOneEntry();
510         PositionHdl_Impl(*m_xPositionLB);
511         AdjustHdl_Impl(*m_xAdjustLB);
512         CharStyleHdl_Impl(*m_xCharStyleLB);
513     }
514     GetRubyText();
515     //reset all edit fields - SaveValue is called
516     ScrollHdl_Impl(*m_xScrolledWindow);
517 
518     Reference<XRubySelection> xSelection = m_pImpl->GetRubySelection();
519     if (IsModified() && xSelection.is())
520     {
521         try
522         {
523             xSelection->setRubyList(aRubyValues, false);
524         }
525         catch (const Exception&)
526         {
527             TOOLS_WARN_EXCEPTION("svx.dialog", "");
528         }
529     }
530 }
531 
IMPL_LINK_NOARG(SvxRubyDialog,CloseHdl_Impl,weld::Button &,void)532 IMPL_LINK_NOARG(SvxRubyDialog, CloseHdl_Impl, weld::Button&, void) { Close(); }
533 
IMPL_LINK_NOARG(SvxRubyDialog,StylistHdl_Impl,weld::Button &,void)534 IMPL_LINK_NOARG(SvxRubyDialog, StylistHdl_Impl, weld::Button&, void)
535 {
536     std::unique_ptr<SfxPoolItem> pState;
537     SfxItemState eState = pBindings->QueryState(SID_STYLE_DESIGNER, pState);
538     if (eState <= SfxItemState::SET || !pState
539         || !static_cast<SfxBoolItem*>(pState.get())->GetValue())
540     {
541         pBindings->GetDispatcher()->Execute(SID_STYLE_DESIGNER,
542                                             SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
543     }
544 }
545 
IMPL_LINK(SvxRubyDialog,AdjustHdl_Impl,weld::ComboBox &,rBox,void)546 IMPL_LINK(SvxRubyDialog, AdjustHdl_Impl, weld::ComboBox&, rBox, void)
547 {
548     AssertOneEntry();
549     sal_Int16 nAdjust = rBox.get_active();
550     Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
551     for (PropertyValues& rProps : aRubyValues)
552     {
553         for (PropertyValue& propVal : rProps)
554         {
555             if (propVal.Name == cRubyAdjust)
556                 propVal.Value <<= nAdjust;
557         }
558         SetModified(true);
559     }
560     m_xPreviewWin->Invalidate();
561 }
562 
IMPL_LINK(SvxRubyDialog,PositionHdl_Impl,weld::ComboBox &,rBox,void)563 IMPL_LINK(SvxRubyDialog, PositionHdl_Impl, weld::ComboBox&, rBox, void)
564 {
565     AssertOneEntry();
566     sal_Int16 nPosition = rBox.get_active();
567     Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
568     for (PropertyValues& rProps : aRubyValues)
569     {
570         for (PropertyValue& propVal : rProps)
571         {
572             if (propVal.Name == cRubyPosition)
573                 propVal.Value <<= nPosition;
574         }
575         SetModified(true);
576     }
577     m_xPreviewWin->Invalidate();
578 }
579 
IMPL_LINK_NOARG(SvxRubyDialog,CharStyleHdl_Impl,weld::ComboBox &,void)580 IMPL_LINK_NOARG(SvxRubyDialog, CharStyleHdl_Impl, weld::ComboBox&, void)
581 {
582     AssertOneEntry();
583     OUString sStyleName;
584     if (m_xCharStyleLB->get_active() != -1)
585         sStyleName = m_xCharStyleLB->get_active_id();
586     Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
587     for (PropertyValues& rProps : aRubyValues)
588     {
589         for (PropertyValue& propVal : rProps)
590         {
591             if (propVal.Name == cRubyCharStyleName)
592             {
593                 propVal.Value <<= sStyleName;
594             }
595         }
596         SetModified(true);
597     }
598 }
599 
IMPL_LINK(SvxRubyDialog,EditFocusHdl_Impl,weld::Widget &,rEdit,void)600 IMPL_LINK(SvxRubyDialog, EditFocusHdl_Impl, weld::Widget&, rEdit, void)
601 {
602     for (sal_uInt16 i = 0; i < 8; i++)
603     {
604         if (&rEdit == aEditArr[i])
605         {
606             nCurrentEdit = i / 2;
607             break;
608         }
609     }
610     m_xPreviewWin->Invalidate();
611 }
612 
IMPL_LINK(SvxRubyDialog,EditModifyHdl_Impl,weld::Entry &,rEdit,void)613 IMPL_LINK(SvxRubyDialog, EditModifyHdl_Impl, weld::Entry&, rEdit, void)
614 {
615     EditFocusHdl_Impl(rEdit);
616 }
617 
EditScrollHdl_Impl(sal_Int32 nParam)618 bool SvxRubyDialog::EditScrollHdl_Impl(sal_Int32 nParam)
619 {
620     bool bRet = false;
621     //scroll forward
622     if (nParam > 0 && (aEditArr[7]->has_focus() || aEditArr[6]->has_focus()))
623     {
624         if (m_xScrolledWindow->vadjustment_get_upper()
625             > m_xScrolledWindow->vadjustment_get_value()
626                   + m_xScrolledWindow->vadjustment_get_page_size())
627         {
628             m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value()
629                                                      + 1);
630             aEditArr[6]->grab_focus();
631             bRet = true;
632         }
633     }
634     //scroll backward
635     else if (m_xScrolledWindow->vadjustment_get_value()
636              && (aEditArr[0]->has_focus() || aEditArr[1]->has_focus()))
637     {
638         m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value() - 1);
639         aEditArr[1]->grab_focus();
640         bRet = true;
641     }
642     if (bRet)
643         ScrollHdl_Impl(*m_xScrolledWindow);
644     return bRet;
645 }
646 
EditJumpHdl_Impl(sal_Int32 nParam)647 bool SvxRubyDialog::EditJumpHdl_Impl(sal_Int32 nParam)
648 {
649     bool bHandled = false;
650     sal_uInt16 nIndex = USHRT_MAX;
651     for (sal_uInt16 i = 0; i < 8; i++)
652     {
653         if (aEditArr[i]->has_focus())
654             nIndex = i;
655     }
656     if (nIndex < 8)
657     {
658         if (nParam > 0)
659         {
660             if (nIndex < 6)
661                 aEditArr[nIndex + 2]->grab_focus();
662             else if (EditScrollHdl_Impl(nParam))
663                 aEditArr[nIndex]->grab_focus();
664         }
665         else
666         {
667             if (nIndex > 1)
668                 aEditArr[nIndex - 2]->grab_focus();
669             else if (EditScrollHdl_Impl(nParam))
670                 aEditArr[nIndex]->grab_focus();
671         }
672         bHandled = true;
673     }
674     return bHandled;
675 }
676 
AssertOneEntry()677 void SvxRubyDialog::AssertOneEntry() { m_pImpl->AssertOneEntry(); }
678 
EnableControls(bool bEnable)679 void SvxRubyDialog::EnableControls(bool bEnable)
680 {
681     m_xContentArea->set_sensitive(bEnable);
682     m_xApplyPB->set_sensitive(bEnable);
683 }
684 
RubyPreview()685 RubyPreview::RubyPreview()
686     : m_pParentDlg(nullptr)
687 {
688 }
689 
~RubyPreview()690 RubyPreview::~RubyPreview() {}
691 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)692 void RubyPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
693 {
694     rRenderContext.Push(PushFlags::ALL);
695 
696     rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
697 
698     Size aWinSize = rRenderContext.GetOutputSize();
699 
700     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
701     svtools::ColorConfig aColorConfig;
702 
703     Color aNewTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
704     Color aNewFillColor(rStyleSettings.GetWindowColor());
705 
706     vcl::Font aFont = rRenderContext.GetFont();
707     aFont.SetFontHeight(aWinSize.Height() / 4);
708     aFont.SetFillColor(aNewFillColor);
709     aFont.SetColor(aNewTextColor);
710     rRenderContext.SetFont(aFont);
711 
712     tools::Rectangle aRect(Point(0, 0), aWinSize);
713     rRenderContext.SetLineColor();
714     rRenderContext.SetFillColor(aFont.GetFillColor());
715     rRenderContext.DrawRect(aRect);
716 
717     OUString sBaseText, sRubyText;
718     m_pParentDlg->GetCurrentText(sBaseText, sRubyText);
719 
720     tools::Long nTextHeight = rRenderContext.GetTextHeight();
721     tools::Long nBaseWidth = rRenderContext.GetTextWidth(sBaseText);
722 
723     vcl::Font aRubyFont(aFont);
724     aRubyFont.SetFontHeight(aRubyFont.GetFontHeight() * 70 / 100);
725     rRenderContext.SetFont(aRubyFont);
726     tools::Long nRubyWidth = rRenderContext.GetTextWidth(sRubyText);
727     rRenderContext.SetFont(aFont);
728 
729     RubyAdjust nAdjust = static_cast<RubyAdjust>(m_pParentDlg->m_xAdjustLB->get_active());
730     //use center if no adjustment is available
731     if (nAdjust > RubyAdjust_INDENT_BLOCK)
732         nAdjust = RubyAdjust_CENTER;
733 
734     //which part is stretched ?
735     bool bRubyStretch = nBaseWidth >= nRubyWidth;
736 
737     tools::Long nCenter = aWinSize.Width() / 2;
738     tools::Long nHalfWidth = std::max(nBaseWidth, nRubyWidth) / 2;
739     tools::Long nLeftStart = nCenter - nHalfWidth;
740     tools::Long nRightEnd = nCenter + nHalfWidth;
741 
742     // Default values for TOP or no selection
743     tools::Long nYRuby = aWinSize.Height() / 4 - nTextHeight / 2;
744     tools::Long nYBase = aWinSize.Height() * 3 / 4 - nTextHeight / 2;
745 
746     sal_Int16 nRubyPos = m_pParentDlg->m_xPositionLB->get_active();
747     if (nRubyPos == 1) // BOTTOM
748     {
749         tools::Long nTmp = nYRuby;
750         nYRuby = nYBase;
751         nYBase = nTmp;
752     }
753     else if (nRubyPos == 2) // RIGHT ( vertically )
754     {
755         // Align the ruby text and base text to the vertical center.
756         nYBase = (aWinSize.Height() - nTextHeight) / 2;
757         nYRuby = (aWinSize.Height() - nRubyWidth) / 2;
758 
759         // Align the ruby text at the right side of the base text
760         nAdjust = RubyAdjust_RIGHT;
761         nHalfWidth = nBaseWidth / 2;
762         nLeftStart = nCenter - nHalfWidth;
763         nRightEnd = nCenter + nHalfWidth + nRubyWidth + nTextHeight;
764         // Render base text first, then render ruby text on the right.
765         bRubyStretch = true;
766 
767         aRubyFont.SetVertical(true);
768         aRubyFont.SetOrientation(2700_deg10);
769     }
770 
771     tools::Long nYOutput;
772     tools::Long nOutTextWidth;
773     OUString sOutputText;
774 
775     if (bRubyStretch)
776     {
777         rRenderContext.DrawText(Point(nLeftStart, nYBase), sBaseText);
778         nYOutput = nYRuby;
779         sOutputText = sRubyText;
780         nOutTextWidth = nRubyWidth;
781         rRenderContext.SetFont(aRubyFont);
782     }
783     else
784     {
785         rRenderContext.SetFont(aRubyFont);
786         rRenderContext.DrawText(Point(nLeftStart, nYRuby), sRubyText);
787         nYOutput = nYBase;
788         sOutputText = sBaseText;
789         nOutTextWidth = nBaseWidth;
790         rRenderContext.SetFont(aFont);
791     }
792 
793     switch (nAdjust)
794     {
795         case RubyAdjust_LEFT:
796             rRenderContext.DrawText(Point(nLeftStart, nYOutput), sOutputText);
797             break;
798         case RubyAdjust_RIGHT:
799             rRenderContext.DrawText(Point(nRightEnd - nOutTextWidth, nYOutput), sOutputText);
800             break;
801         case RubyAdjust_INDENT_BLOCK:
802         {
803             tools::Long nCharWidth = rRenderContext.GetTextWidth("X");
804             if (nOutTextWidth < (nRightEnd - nLeftStart - nCharWidth))
805             {
806                 nCharWidth /= 2;
807                 nLeftStart += nCharWidth;
808                 nRightEnd -= nCharWidth;
809             }
810             [[fallthrough]];
811         }
812         case RubyAdjust_BLOCK:
813         {
814             if (sOutputText.getLength() > 1)
815             {
816                 sal_Int32 nCount = sOutputText.getLength();
817                 tools::Long nSpace
818                     = ((nRightEnd - nLeftStart) - rRenderContext.GetTextWidth(sOutputText))
819                       / (nCount - 1);
820                 for (sal_Int32 i = 0; i < nCount; i++)
821                 {
822                     OUString sChar(sOutputText[i]);
823                     rRenderContext.DrawText(Point(nLeftStart, nYOutput), sChar);
824                     tools::Long nCharWidth = rRenderContext.GetTextWidth(sChar);
825                     nLeftStart += nCharWidth + nSpace;
826                 }
827                 break;
828             }
829             [[fallthrough]];
830         }
831         case RubyAdjust_CENTER:
832             rRenderContext.DrawText(Point(nCenter - nOutTextWidth / 2, nYOutput), sOutputText);
833             break;
834         default:
835             break;
836     }
837     rRenderContext.Pop();
838 }
839 
SetDrawingArea(weld::DrawingArea * pDrawingArea)840 void RubyPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
841 {
842     pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 40,
843                                    pDrawingArea->get_text_height() * 7);
844     CustomWidgetController::SetDrawingArea(pDrawingArea);
845 }
846 
IMPL_LINK(SvxRubyDialog,KeyUpDownHdl_Impl,const KeyEvent &,rKEvt,bool)847 IMPL_LINK(SvxRubyDialog, KeyUpDownHdl_Impl, const KeyEvent&, rKEvt, bool)
848 {
849     bool bHandled = false;
850     const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
851     sal_uInt16 nCode = rKeyCode.GetCode();
852     if (KEY_UP == nCode || KEY_DOWN == nCode)
853     {
854         sal_Int32 nParam = KEY_UP == nCode ? -1 : 1;
855         bHandled = EditJumpHdl_Impl(nParam);
856     }
857     return bHandled;
858 }
859 
IMPL_LINK(SvxRubyDialog,KeyUpDownTabHdl_Impl,const KeyEvent &,rKEvt,bool)860 IMPL_LINK(SvxRubyDialog, KeyUpDownTabHdl_Impl, const KeyEvent&, rKEvt, bool)
861 {
862     bool bHandled = false;
863     const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
864     sal_uInt16 nMod = rKeyCode.GetModifier();
865     sal_uInt16 nCode = rKeyCode.GetCode();
866     if (nCode == KEY_TAB && (!nMod || KEY_SHIFT == nMod))
867     {
868         sal_Int32 nParam = KEY_SHIFT == nMod ? -1 : 1;
869         if (EditScrollHdl_Impl(nParam))
870             bHandled = true;
871     }
872     if (!bHandled)
873         bHandled = KeyUpDownHdl_Impl(rKEvt);
874     return bHandled;
875 }
876 
877 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
878