1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
21 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
22 #include <com/sun/star/frame/Desktop.hpp>
23 #include <com/sun/star/frame/XFramesSupplier.hpp>
24 #include <com/sun/star/container/XChild.hpp>
25 
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/servicehelper.hxx>
28 #include <comphelper/storagehelper.hxx>
29 #include <comphelper/string.hxx>
30 #include <i18nutil/unicode.hxx>
31 #include <officecfg/Office/Common.hxx>
32 #include <sfx2/dispatch.hxx>
33 #include <sfx2/docfile.hxx>
34 #include <sfx2/docfilt.hxx>
35 #include <sfx2/docinsert.hxx>
36 #include <sfx2/filedlghelper.hxx>
37 #include <sfx2/infobar.hxx>
38 #include <sfx2/msg.hxx>
39 #include <sfx2/objface.hxx>
40 #include <sfx2/printer.hxx>
41 #include <sfx2/request.hxx>
42 #include <sfx2/viewfac.hxx>
43 #include <svl/eitem.hxx>
44 #include <svl/itemset.hxx>
45 #include <svl/poolitem.hxx>
46 #include <svl/stritem.hxx>
47 #include <vcl/transfer.hxx>
48 #include <svtools/colorcfg.hxx>
49 #include <svl/whiter.hxx>
50 #include <svx/zoomslideritem.hxx>
51 #include <editeng/editeng.hxx>
52 #include <editeng/editview.hxx>
53 #include <svx/svxdlg.hxx>
54 #include <sfx2/zoomitem.hxx>
55 #include <vcl/commandevent.hxx>
56 #include <vcl/event.hxx>
57 #include <vcl/settings.hxx>
58 #include <vcl/virdev.hxx>
59 #include <sal/log.hxx>
60 #include <tools/svborder.hxx>
61 
62 #include <unotools/streamwrap.hxx>
63 
64 #include <unomodel.hxx>
65 #include <view.hxx>
66 #include <cfgitem.hxx>
67 #include <dialog.hxx>
68 #include <document.hxx>
69 #include <starmath.hrc>
70 #include <strings.hrc>
71 #include <smmod.hxx>
72 #include <mathmlimport.hxx>
73 #include <cursor.hxx>
74 #include "accessibility.hxx"
75 #include <ElementsDockingWindow.hxx>
76 #include <helpids.h>
77 
78 #define MINZOOM sal_uInt16(25)
79 #define MAXZOOM sal_uInt16(800)
80 
81 // space around the edit window, in pixels
82 // fdo#69111: Increased border on the top so that the window is
83 // easier to tear off.
84 #define CMD_BOX_PADDING 3
85 #define CMD_BOX_PADDING_TOP 11
86 
87 #define ShellClass_SmViewShell
88 #include <smslots.hxx>
89 
90 using namespace css;
91 using namespace css::accessibility;
92 using namespace css::uno;
93 
SmGraphicWindow(SmViewShell & rShell)94 SmGraphicWindow::SmGraphicWindow(SmViewShell& rShell)
95     : InterimItemWindow(&rShell.GetViewFrame()->GetWindow(), "modules/smath/ui/mathwindow.ui", "MathWindow")
96     , nZoom(100)
97     // continue to use user-scrolling to make this work equivalent to how it 'always' worked
98     , mxScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow", true))
99     , mxGraphic(new SmGraphicWidget(rShell, *this))
100     , mxGraphicWin(new weld::CustomWeld(*m_xBuilder, "mathview", *mxGraphic))
101 {
102     InitControlBase(mxGraphic->GetDrawingArea());
103 
104     nColumnPixW = nLinePixH = GetSettings().GetStyleSettings().GetScrollBarSize();
105 
106     mxScrolledWindow->connect_hadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
107     mxScrolledWindow->connect_vadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
108 
109     // docking windows are usually hidden (often already done in the
110     // resource) and will be shown by the sfx framework.
111     Hide();
112 }
113 
dispose()114 void SmGraphicWindow::dispose()
115 {
116     InitControlBase(nullptr);
117     mxGraphicWin.reset();
118     mxGraphic.reset();
119     mxScrolledWindow.reset();
120     InterimItemWindow::dispose();
121 }
122 
~SmGraphicWindow()123 SmGraphicWindow::~SmGraphicWindow()
124 {
125     disposeOnce();
126 }
127 
Resize()128 void SmGraphicWindow::Resize()
129 {
130     InterimItemWindow::Resize();
131 
132     // get the new output-size in pixel
133     Size aOutPixSz = GetOutputSizePixel();
134 
135     // determine the size of the output-area and if we need scrollbars
136     const auto nScrSize = mxScrolledWindow->get_scroll_thickness();
137     bool bVVisible = false; // by default no vertical-ScrollBar
138     bool bHVisible = false; // by default no horizontal-ScrollBar
139     bool bChanged;          // determines if a visiblility was changed
140     do
141     {
142         bChanged = false;
143 
144         // does we need a vertical ScrollBar
145         if ( aOutPixSz.Width() < aTotPixSz.Width() && !bHVisible )
146         {
147             bHVisible = true;
148             aOutPixSz.AdjustHeight( -nScrSize );
149             bChanged = true;
150         }
151 
152         // does we need a horizontal ScrollBar
153         if ( aOutPixSz.Height() < aTotPixSz.Height() && !bVVisible )
154         {
155             bVVisible = true;
156             aOutPixSz.AdjustWidth( -nScrSize );
157             bChanged = true;
158         }
159 
160     }
161     while ( bChanged );   // until no visibility has changed
162 
163     // store the old offset and map-mode
164     MapMode aMap(GetGraphicMapMode());
165     Point aOldPixOffset(aPixOffset);
166 
167     // justify (right/bottom borders should never exceed the virtual window)
168     Size aPixDelta;
169     if ( aPixOffset.X() < 0 &&
170          aPixOffset.X() + aTotPixSz.Width() < aOutPixSz.Width() )
171         aPixDelta.setWidth(
172             aOutPixSz.Width() - ( aPixOffset.X() + aTotPixSz.Width() ) );
173     if ( aPixOffset.Y() < 0 &&
174          aPixOffset.Y() + aTotPixSz.Height() < aOutPixSz.Height() )
175         aPixDelta.setHeight(
176             aOutPixSz.Height() - ( aPixOffset.Y() + aTotPixSz.Height() ) );
177     if ( aPixDelta.Width() || aPixDelta.Height() )
178     {
179         aPixOffset.AdjustX(aPixDelta.Width() );
180         aPixOffset.AdjustY(aPixDelta.Height() );
181     }
182 
183     // for axis without scrollbar restore the origin
184     if ( !bVVisible || !bHVisible )
185     {
186         aPixOffset = Point(
187                      bHVisible
188                      ? aPixOffset.X()
189                      : (aOutPixSz.Width()-aTotPixSz.Width()) / 2,
190                      bVVisible
191                      ? aPixOffset.Y()
192                      : (aOutPixSz.Height()-aTotPixSz.Height()) / 2 );
193     }
194     if (bHVisible && mxScrolledWindow->get_hpolicy() == VclPolicyType::NEVER)
195         aPixOffset.setX( 0 );
196     if (bVVisible && mxScrolledWindow->get_vpolicy() == VclPolicyType::NEVER)
197         aPixOffset.setY( 0 );
198 
199     // select the shifted map-mode
200     if (aPixOffset != aOldPixOffset)
201         SetGraphicMapMode(aMap);
202 
203     // show or hide scrollbars
204     mxScrolledWindow->set_vpolicy(bVVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
205     mxScrolledWindow->set_hpolicy(bHVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
206 
207     // resize scrollbars and set their ranges
208     if ( bHVisible )
209     {
210         mxScrolledWindow->hadjustment_configure(-aPixOffset.X(), 0, aTotPixSz.Width(), nColumnPixW,
211                                                 aOutPixSz.Width(), aOutPixSz.Width());
212     }
213     if ( bVVisible )
214     {
215         mxScrolledWindow->vadjustment_configure(-aPixOffset.Y(), 0, aTotPixSz.Height(), nLinePixH,
216                                                 aOutPixSz.Height(), aOutPixSz.Height());
217     }
218 }
219 
IMPL_LINK_NOARG(SmGraphicWindow,ScrollHdl,weld::ScrolledWindow &,void)220 IMPL_LINK_NOARG(SmGraphicWindow, ScrollHdl, weld::ScrolledWindow&, void)
221 {
222     MapMode aMap(GetGraphicMapMode());
223     Point aNewPixOffset(aPixOffset);
224 
225     // scrolling horizontally?
226     if (mxScrolledWindow->get_hpolicy() == VclPolicyType::ALWAYS)
227         aNewPixOffset.setX(-mxScrolledWindow->hadjustment_get_value());
228 
229     // scrolling vertically?
230     if (mxScrolledWindow->get_vpolicy() == VclPolicyType::ALWAYS)
231         aNewPixOffset.setY(-mxScrolledWindow->vadjustment_get_value());
232 
233     // scrolling?
234     if (aPixOffset == aNewPixOffset)
235         return;
236 
237     // recompute the logical scroll units
238     aPixOffset = aNewPixOffset;
239 
240     SetGraphicMapMode(aMap);
241 }
242 
SetGraphicMapMode(const MapMode & rNewMapMode)243 void SmGraphicWindow::SetGraphicMapMode(const MapMode& rNewMapMode)
244 {
245     OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
246     MapMode aMap( rNewMapMode );
247     aMap.SetOrigin( aMap.GetOrigin() + rDevice.PixelToLogic( aPixOffset, aMap ) );
248     rDevice.SetMapMode( aMap );
249     mxGraphic->Invalidate();
250 }
251 
GetGraphicMapMode() const252 MapMode SmGraphicWindow::GetGraphicMapMode() const
253 {
254     OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
255     MapMode aMap(rDevice.GetMapMode());
256     aMap.SetOrigin( aMap.GetOrigin() - rDevice.PixelToLogic( aPixOffset ) );
257     return aMap;
258 }
259 
SetTotalSize(const Size & rNewSize)260 void SmGraphicWindow::SetTotalSize( const Size& rNewSize )
261 {
262     OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
263     aTotPixSz = rDevice.LogicToPixel(rNewSize);
264     Resize();
265 }
266 
GetTotalSize() const267 Size SmGraphicWindow::GetTotalSize() const
268 {
269     OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
270     return rDevice.PixelToLogic(aTotPixSz);
271 }
272 
ShowContextMenu(const CommandEvent & rCEvt)273 void SmGraphicWindow::ShowContextMenu(const CommandEvent& rCEvt)
274 {
275     GetParent()->ToTop();
276     Point aPos(5, 5);
277     if (rCEvt.IsMouseEvent())
278         aPos = rCEvt.GetMousePosPixel();
279 
280     // added for replaceability of context menus
281     SfxDispatcher::ExecutePopup( this, &aPos );
282 }
283 
SmGraphicWidget(SmViewShell & rShell,SmGraphicWindow & rGraphicWindow)284 SmGraphicWidget::SmGraphicWidget(SmViewShell& rShell, SmGraphicWindow& rGraphicWindow)
285     : mrGraphicWindow(rGraphicWindow)
286     , bIsCursorVisible(false)
287     , bIsLineVisible(false)
288     , mrViewShell(rShell)
289 {
290 }
291 
SetDrawingArea(weld::DrawingArea * pDrawingArea)292 void SmGraphicWidget::SetDrawingArea(weld::DrawingArea* pDrawingArea)
293 {
294     weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
295 
296     OutputDevice& rDevice = pDrawingArea->get_ref_device();
297 
298     rDevice.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
299 
300     const Fraction aFraction(1, 1);
301     rDevice.SetMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction));
302 
303     SetTotalSize();
304 
305     SetHelpId(HID_SMA_WIN_DOCUMENT);
306 
307     ShowLine(false);
308     CaretBlinkInit();
309 }
310 
~SmGraphicWidget()311 SmGraphicWidget::~SmGraphicWidget()
312 {
313     if (mxAccessible.is())
314         mxAccessible->ClearWin();    // make Accessible nonfunctional
315     mxAccessible.clear();
316     CaretBlinkStop();
317 }
318 
MouseButtonDown(const MouseEvent & rMEvt)319 bool SmGraphicWidget::MouseButtonDown(const MouseEvent& rMEvt)
320 {
321     GrabFocus();
322 
323     // set formula-cursor and selection of edit window according to the
324     // position clicked at
325 
326     SAL_WARN_IF( rMEvt.GetClicks() == 0, "starmath", "0 clicks" );
327     if ( !rMEvt.IsLeft() )
328         return true;
329 
330     OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
331     // get click position relative to formula
332     Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
333 
334     const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree();
335     if (!pTree)
336         return true;
337 
338     if (IsInlineEditEnabled()) {
339         mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, !rMEvt.IsShift());
340         return true;
341     }
342     const SmNode *pNode = nullptr;
343     // if it was clicked inside the formula then get the appropriate node
344     if (pTree->OrientedDist(aPos) <= 0)
345         pNode = pTree->FindRectClosestTo(aPos);
346 
347     if (!pNode)
348         return true;
349 
350     SmEditWindow* pEdit = mrViewShell.GetEditWindow();
351     if (!pEdit)
352         return true;
353     const SmToken  aToken (pNode->GetToken());
354 
355     // set selection to the beginning of the token
356     pEdit->SetSelection(pNode->GetSelection());
357     SetCursor(pNode);
358 
359     // allow for immediate editing and
360     //! implicitly synchronize the cursor position mark in this window
361     pEdit->GrabFocus();
362 
363     return true;
364 }
365 
MouseMove(const MouseEvent & rMEvt)366 bool SmGraphicWidget::MouseMove(const MouseEvent &rMEvt)
367 {
368     if (rMEvt.IsLeft() && IsInlineEditEnabled())
369     {
370         OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
371         Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
372         mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, false);
373 
374         CaretBlinkStop();
375         SetIsCursorVisible(true);
376         CaretBlinkStart();
377         RepaintViewShellDoc();
378     }
379     return true;
380 }
381 
IsInlineEditEnabled()382 bool SmGraphicWidget::IsInlineEditEnabled()
383 {
384     return SmViewShell::IsInlineEditEnabled();
385 }
386 
GetFocus()387 void SmGraphicWidget::GetFocus()
388 {
389     if (!IsInlineEditEnabled())
390         return;
391     if (mrViewShell.GetEditWindow())
392         mrViewShell.GetEditWindow()->Flush();
393     //Let view shell know what insertions should be done in visual editor
394     mrViewShell.SetInsertIntoEditWindow(false);
395     SetIsCursorVisible(true);
396     ShowLine(true);
397     CaretBlinkStart();
398     RepaintViewShellDoc();
399 }
400 
LoseFocus()401 void SmGraphicWidget::LoseFocus()
402 {
403     if (mxAccessible.is())
404     {
405         uno::Any aOldValue, aNewValue;
406         aOldValue <<= AccessibleStateType::FOCUSED;
407         // aNewValue remains empty
408         mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED,
409                 aOldValue, aNewValue );
410     }
411     if (!IsInlineEditEnabled())
412         return;
413     SetIsCursorVisible(false);
414     ShowLine(false);
415     CaretBlinkStop();
416     RepaintViewShellDoc();
417 }
418 
RepaintViewShellDoc()419 void SmGraphicWidget::RepaintViewShellDoc()
420 {
421     SmDocShell* pDoc = mrViewShell.GetDoc();
422     if (pDoc)
423         pDoc->Repaint();
424 }
425 
IMPL_LINK_NOARG(SmGraphicWidget,CaretBlinkTimerHdl,Timer *,void)426 IMPL_LINK_NOARG(SmGraphicWidget, CaretBlinkTimerHdl, Timer *, void)
427 {
428     if (IsCursorVisible())
429         SetIsCursorVisible(false);
430     else
431         SetIsCursorVisible(true);
432 
433     RepaintViewShellDoc();
434 }
435 
CaretBlinkInit()436 void SmGraphicWidget::CaretBlinkInit()
437 {
438     aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWidget, CaretBlinkTimerHdl));
439     aCaretBlinkTimer.SetTimeout(Application::GetSettings().GetStyleSettings().GetCursorBlinkTime());
440 }
441 
CaretBlinkStart()442 void SmGraphicWidget::CaretBlinkStart()
443 {
444     if (!IsInlineEditEnabled())
445         return;
446     if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME)
447         aCaretBlinkTimer.Start();
448 }
449 
CaretBlinkStop()450 void SmGraphicWidget::CaretBlinkStop()
451 {
452     if (!IsInlineEditEnabled())
453         return;
454     aCaretBlinkTimer.Stop();
455 }
456 
457 // shows or hides the formula-cursor depending on 'bShow' is true or not
ShowCursor(bool bShow)458 void SmGraphicWidget::ShowCursor(bool bShow)
459 {
460     if (IsInlineEditEnabled())
461         return;
462 
463     bool bInvert = bShow != IsCursorVisible();
464     if (bInvert)
465     {
466         OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
467         InvertFocusRect(rDevice, aCursorRect);
468     }
469 
470     SetIsCursorVisible(bShow);
471 }
472 
ShowLine(bool bShow)473 void SmGraphicWidget::ShowLine(bool bShow)
474 {
475     if (!IsInlineEditEnabled())
476         return;
477 
478     bIsLineVisible = bShow;
479 }
480 
SetCursor(const SmNode * pNode)481 void SmGraphicWidget::SetCursor(const SmNode *pNode)
482 {
483     if (IsInlineEditEnabled())
484         return;
485 
486     const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree();
487 
488     // get appropriate rectangle
489     Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
490           aTLPos  (GetFormulaDrawPos() + aOffset);
491     aTLPos.AdjustX( -(pNode->GetItalicLeftSpace()) );
492     Size  aSize   (pNode->GetItalicSize());
493 
494     SetCursor(tools::Rectangle(aTLPos, aSize));
495 }
496 
SetCursor(const tools::Rectangle & rRect)497 void SmGraphicWidget::SetCursor(const tools::Rectangle &rRect)
498     // sets cursor to new position (rectangle) 'rRect'.
499     // The old cursor will be removed, and the new one will be shown if
500     // that is activated in the ConfigItem
501 {
502     if (IsInlineEditEnabled())
503         return;
504 
505     SmModule *pp = SM_MOD();
506 
507     if (IsCursorVisible())
508         ShowCursor(false);      // clean up remainings of old cursor
509     aCursorRect = rRect;
510     if (pp->GetConfig()->IsShowFormulaCursor())
511         ShowCursor(true);       // draw new cursor
512 }
513 
SetCursorPos(sal_uInt16 nRow,sal_uInt16 nCol)514 const SmNode * SmGraphicWidget::SetCursorPos(sal_uInt16 nRow, sal_uInt16 nCol)
515     // looks for a VISIBLE node in the formula tree with its token at
516     // (or around) the position 'nRow', 'nCol' in the edit window
517     // (row and column numbering starts with 1 there!).
518     // If there is such a node the formula-cursor is set to cover that nodes
519     // rectangle. If not the formula-cursor will be hidden.
520     // In any case the search result is being returned.
521 {
522     if (IsInlineEditEnabled())
523         return nullptr;
524 
525     // find visible node with token at nRow, nCol
526     const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree(),
527                  *pNode = nullptr;
528     if (pTree)
529         pNode = pTree->FindTokenAt(nRow, nCol);
530 
531     if (pNode)
532         SetCursor(pNode);
533     else
534         ShowCursor(false);
535 
536     return pNode;
537 }
538 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)539 void SmGraphicWidget::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
540 {
541     SmDocShell& rDoc = *mrViewShell.GetDoc();
542     Point aPoint;
543 
544     rDoc.DrawFormula(rRenderContext, aPoint, true);  //! modifies aPoint to be the topleft
545                                                      //! corner of the formula
546     aFormulaDrawPos = aPoint;
547     if (IsInlineEditEnabled())
548     {
549         //Draw cursor if any...
550         if (mrViewShell.GetDoc()->HasCursor() && IsLineVisible())
551             mrViewShell.GetDoc()->GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible());
552     }
553     else
554     {
555         SetIsCursorVisible(false);  // (old) cursor must be drawn again
556 
557         const SmEditWindow* pEdit = mrViewShell.GetEditWindow();
558         if (pEdit)
559         {   // get new position for formula-cursor (for possible altered formula)
560             sal_Int32  nRow;
561             sal_uInt16 nCol;
562             SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol);
563             const SmNode *pFound = SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
564 
565             SmModule *pp = SM_MOD();
566             if (pFound && pp->GetConfig()->IsShowFormulaCursor())
567                 ShowCursor(true);
568         }
569     }
570 }
571 
SetTotalSize()572 void SmGraphicWidget::SetTotalSize()
573 {
574     SmDocShell &rDoc = *mrViewShell.GetDoc();
575     OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
576     const Size aTmp(rDevice.PixelToLogic(rDevice.LogicToPixel(rDoc.GetSize())));
577     if (aTmp != mrGraphicWindow.GetTotalSize())
578         mrGraphicWindow.SetTotalSize(aTmp);
579 }
580 
KeyInput(const KeyEvent & rKEvt)581 bool SmGraphicWidget::KeyInput(const KeyEvent& rKEvt)
582 {
583     if (!IsInlineEditEnabled())
584         return mrViewShell.KeyInput(rKEvt);
585 
586     bool bConsumed = true;
587 
588     SmCursor& rCursor = mrViewShell.GetDoc()->GetCursor();
589     KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
590     if (eFunc == KeyFuncType::COPY)
591         rCursor.Copy();
592     else if (eFunc == KeyFuncType::CUT)
593         rCursor.Cut();
594     else if (eFunc == KeyFuncType::PASTE)
595         rCursor.Paste();
596     else {
597     OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
598     sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
599     switch(nCode)
600     {
601         case KEY_LEFT:
602         {
603             rCursor.Move(&rDevice, MoveLeft, !rKEvt.GetKeyCode().IsShift());
604         }break;
605         case KEY_RIGHT:
606         {
607             rCursor.Move(&rDevice, MoveRight, !rKEvt.GetKeyCode().IsShift());
608         }break;
609         case KEY_UP:
610         {
611             rCursor.Move(&rDevice, MoveUp, !rKEvt.GetKeyCode().IsShift());
612         }break;
613         case KEY_DOWN:
614         {
615             rCursor.Move(&rDevice, MoveDown, !rKEvt.GetKeyCode().IsShift());
616         }break;
617         case KEY_RETURN:
618         {
619             if(!rKEvt.GetKeyCode().IsShift())
620                 rCursor.InsertRow();
621         }break;
622         case KEY_DELETE:
623         {
624             if(!rCursor.HasSelection()){
625                 rCursor.Move(&rDevice, MoveRight, false);
626                 if(rCursor.HasComplexSelection()) break;
627             }
628             rCursor.Delete();
629         }break;
630         case KEY_BACKSPACE:
631         {
632             rCursor.DeletePrev(&rDevice);
633         }break;
634         case KEY_ADD:
635             rCursor.InsertElement(PlusElement);
636             break;
637         case KEY_SUBTRACT:
638             if(rKEvt.GetKeyCode().IsShift())
639                 rCursor.InsertSubSup(RSUB);
640             else
641                 rCursor.InsertElement(MinusElement);
642             break;
643         case KEY_MULTIPLY:
644             rCursor.InsertElement(CDotElement);
645             break;
646         case KEY_DIVIDE:
647             rCursor.InsertFraction();
648             break;
649         case KEY_LESS:
650             rCursor.InsertElement(LessThanElement);
651             break;
652         case KEY_GREATER:
653             rCursor.InsertElement(GreaterThanElement);
654             break;
655         case KEY_EQUAL:
656             rCursor.InsertElement(EqualElement);
657             break;
658         default:
659         {
660             sal_Unicode code = rKEvt.GetCharCode();
661 
662             if(code == ' ') {
663                 rCursor.InsertElement(BlankElement);
664             }else if(code == '^') {
665                 rCursor.InsertSubSup(RSUP);
666             }else if(code == '(') {
667                 rCursor.InsertBrackets(SmBracketType::Round);
668             }else if(code == '[') {
669                 rCursor.InsertBrackets(SmBracketType::Square);
670             }else if(code == '{') {
671                 rCursor.InsertBrackets(SmBracketType::Curly);
672             }else if(code == '!') {
673                 rCursor.InsertElement(FactorialElement);
674             }else if(code == '%') {
675                 rCursor.InsertElement(PercentElement);
676             }
677             else if ((code == ')' && rCursor.IsAtTailOfBracket(SmBracketType::Round))
678                      || (code == ']' && rCursor.IsAtTailOfBracket(SmBracketType::Square))
679                      || (code == '}' && rCursor.IsAtTailOfBracket(SmBracketType::Curly)))
680             {
681                 rCursor.Move(&rDevice, MoveRight);
682             }
683             else{
684                 if(code != 0){
685                     rCursor.InsertText(OUString(code));
686                 }else if (!mrViewShell.KeyInput(rKEvt))
687                     bConsumed = false;
688             }
689         }
690     }
691     }
692     CaretBlinkStop();
693     CaretBlinkStart();
694     SetIsCursorVisible(true);
695     RepaintViewShellDoc();
696 
697     return bConsumed;
698 }
699 
Command(const CommandEvent & rCEvt)700 bool SmGraphicWidget::Command(const CommandEvent& rCEvt)
701 {
702     bool bCallBase = true;
703     if (!mrViewShell.GetViewFrame()->GetFrame().IsInPlace())
704     {
705         switch ( rCEvt.GetCommand() )
706         {
707             case CommandEventId::ContextMenu:
708                 // purely for "ExecutePopup" taking a vcl::Window and
709                 // we assume SmGraphicWindow 0,0 is at SmEditWindow 0,0
710                 mrGraphicWindow.ShowContextMenu(rCEvt);
711                 bCallBase = false;
712             break;
713 
714             case CommandEventId::Wheel:
715             {
716                 const CommandWheelData* pWData = rCEvt.GetWheelData();
717                 if  ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() )
718                 {
719                     sal_uInt16 nTmpZoom = mrGraphicWindow.GetZoom();
720                     if( 0 > pWData->GetDelta() )
721                         nTmpZoom -= 10;
722                     else
723                         nTmpZoom += 10;
724                     mrGraphicWindow.SetZoom(nTmpZoom);
725                     bCallBase = false;
726                 }
727             }
728             break;
729 
730             default: break;
731         }
732     }
733     return !bCallBase;
734 }
735 
SetZoom(sal_uInt16 Factor)736 void SmGraphicWindow::SetZoom(sal_uInt16 Factor)
737 {
738     nZoom = std::clamp(Factor, MINZOOM, MAXZOOM);
739     Fraction aFraction(nZoom, 100);
740     SetGraphicMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction));
741     mxGraphic->SetTotalSize();
742     SmViewShell& rViewSh = mxGraphic->GetView();
743     rViewSh.GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOM);
744     rViewSh.GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER);
745 }
746 
ZoomToFitInWindow()747 void SmGraphicWindow::ZoomToFitInWindow()
748 {
749     SmViewShell& rViewSh = mxGraphic->GetView();
750     SmDocShell& rDoc = *rViewSh.GetDoc();
751 
752     // set defined mapmode before calling 'LogicToPixel' below
753     SetGraphicMapMode(MapMode(MapUnit::Map100thMM));
754 
755     OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
756     Size aSize(rDevice.LogicToPixel(rDoc.GetSize()));
757     Size aWindowSize(GetSizePixel());
758 
759     if (!aSize.IsEmpty())
760     {
761         tools::Long nVal = std::min ((85 * aWindowSize.Width())  / aSize.Width(),
762                       (85 * aWindowSize.Height()) / aSize.Height());
763         SetZoom ( sal::static_int_cast< sal_uInt16 >(nVal) );
764     }
765 }
766 
CreateAccessible()767 uno::Reference< XAccessible > SmGraphicWidget::CreateAccessible()
768 {
769     if (!mxAccessible.is())
770     {
771         mxAccessible = new SmGraphicAccessible( this );
772     }
773     return mxAccessible;
774 }
775 
776 /**************************************************************************/
SmGraphicController(SmGraphicWidget & rSmGraphic,sal_uInt16 nId_,SfxBindings & rBindings)777 SmGraphicController::SmGraphicController(SmGraphicWidget &rSmGraphic,
778                         sal_uInt16          nId_,
779                         SfxBindings     &rBindings) :
780     SfxControllerItem(nId_, rBindings),
781     rGraphic(rSmGraphic)
782 {
783 }
784 
StateChanged(sal_uInt16 nSID,SfxItemState eState,const SfxPoolItem * pState)785 void SmGraphicController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
786 {
787     rGraphic.SetTotalSize();
788     rGraphic.Invalidate();
789     SfxControllerItem::StateChanged (nSID, eState, pState);
790 }
791 
792 /**************************************************************************/
SmEditController(SmEditWindow & rSmEdit,sal_uInt16 nId_,SfxBindings & rBindings)793 SmEditController::SmEditController(SmEditWindow &rSmEdit,
794                      sal_uInt16       nId_,
795                      SfxBindings  &rBindings) :
796     SfxControllerItem(nId_, rBindings),
797     rEdit(rSmEdit)
798 {
799 }
800 
StateChanged(sal_uInt16 nSID,SfxItemState eState,const SfxPoolItem * pState)801 void SmEditController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
802 {
803     const SfxStringItem *pItem =  dynamic_cast<const SfxStringItem*>( pState);
804 
805     if ((pItem != nullptr) && (rEdit.GetText() != pItem->GetValue()))
806         rEdit.SetText(pItem->GetValue());
807     SfxControllerItem::StateChanged (nSID, eState, pState);
808 }
809 
810 /**************************************************************************/
SmCmdBoxWindow(SfxBindings * pBindings_,SfxChildWindow * pChildWindow,vcl::Window * pParent)811 SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow,
812                                vcl::Window *pParent)
813     : SfxDockingWindow(pBindings_, pChildWindow, pParent, "EditWindow", "modules/smath/ui/editwindow.ui")
814     , m_xEdit(new SmEditWindow(*this, *m_xBuilder))
815     , aController(*m_xEdit, SID_TEXT, *pBindings_)
816     , bExiting(false)
817 {
818     set_id("math_edit");
819 
820     SetHelpId( HID_SMA_COMMAND_WIN );
821     SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont)));
822     SetText(SmResId(STR_CMDBOXWINDOW));
823 
824     Hide();
825 
826     aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl));
827     aInitialFocusTimer.SetTimeout(100);
828 }
829 
WidgetToWindowPos(const weld::Widget & rWidget,const Point & rPos)830 Point SmCmdBoxWindow::WidgetToWindowPos(const weld::Widget& rWidget, const Point& rPos)
831 {
832     Point aRet(rPos);
833     int x(0), y(0), width(0), height(0);
834     rWidget.get_extents_relative_to(*m_xContainer, x, y, width, height);
835     aRet.Move(x, y);
836     aRet.Move(m_xBox->GetPosPixel().X(), m_xBox->GetPosPixel().Y());
837     return aRet;
838 }
839 
ShowContextMenu(const Point & rPos)840 void SmCmdBoxWindow::ShowContextMenu(const Point& rPos)
841 {
842     ToTop();
843     SmViewShell *pViewSh = GetView();
844     if (pViewSh)
845         pViewSh->GetViewFrame()->GetDispatcher()->ExecutePopup("edit", this, &rPos);
846 }
847 
Command(const CommandEvent & rCEvt)848 void SmCmdBoxWindow::Command(const CommandEvent& rCEvt)
849 {
850     if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
851     {
852         ShowContextMenu(rCEvt.GetMousePosPixel());
853         return;
854     }
855 
856     SfxDockingWindow::Command(rCEvt);
857 }
858 
~SmCmdBoxWindow()859 SmCmdBoxWindow::~SmCmdBoxWindow ()
860 {
861     disposeOnce();
862 }
863 
dispose()864 void SmCmdBoxWindow::dispose()
865 {
866     aInitialFocusTimer.Stop();
867     bExiting = true;
868     aController.dispose();
869     m_xEdit.reset();
870     SfxDockingWindow::dispose();
871 }
872 
GetView()873 SmViewShell * SmCmdBoxWindow::GetView()
874 {
875     SfxDispatcher *pDispatcher = GetBindings().GetDispatcher();
876     SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr;
877     return  dynamic_cast<SmViewShell*>( pView);
878 }
879 
CalcDockingSize(SfxChildAlignment eAlign)880 Size SmCmdBoxWindow::CalcDockingSize(SfxChildAlignment eAlign)
881 {
882     switch (eAlign)
883     {
884         case SfxChildAlignment::LEFT:
885         case SfxChildAlignment::RIGHT:
886             return Size();
887         default:
888             break;
889     }
890     return SfxDockingWindow::CalcDockingSize(eAlign);
891 }
892 
CheckAlignment(SfxChildAlignment eActual,SfxChildAlignment eWish)893 SfxChildAlignment SmCmdBoxWindow::CheckAlignment(SfxChildAlignment eActual,
894                                              SfxChildAlignment eWish)
895 {
896     switch (eWish)
897     {
898         case SfxChildAlignment::TOP:
899         case SfxChildAlignment::BOTTOM:
900         case SfxChildAlignment::NOALIGNMENT:
901             return eWish;
902         default:
903             break;
904     }
905 
906     return eActual;
907 }
908 
StateChanged(StateChangedType nStateChange)909 void SmCmdBoxWindow::StateChanged( StateChangedType nStateChange )
910 {
911     if (StateChangedType::InitShow == nStateChange)
912     {
913         Resize();   // avoid SmEditWindow not being painted correctly
914 
915         // set initial position of window in floating mode
916         if (IsFloatingMode())
917             AdjustPosition();   //! don't change pos in docking-mode !
918 
919         aInitialFocusTimer.Start();
920     }
921 
922     SfxDockingWindow::StateChanged( nStateChange );
923 }
924 
IMPL_LINK_NOARG(SmCmdBoxWindow,InitialFocusTimerHdl,Timer *,void)925 IMPL_LINK_NOARG( SmCmdBoxWindow, InitialFocusTimerHdl, Timer *, void )
926 {
927     // We want to have the focus in the edit window once Math has been opened
928     // to allow for immediate typing.
929     // Problem: There is no proper way to do this
930     // Thus: this timer based solution has been implemented (see GrabFocus below)
931 
932     // Follow-up problem (#i114910): grabbing the focus may bust the help system since
933     // it relies on getting the current frame which conflicts with grabbing the focus.
934     // Thus aside from the 'GrabFocus' call everything else is to get the
935     // help reliably working despite using 'GrabFocus'.
936 
937     try
938     {
939         uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( comphelper::getProcessComponentContext() );
940 
941         m_xEdit->GrabFocus();
942 
943         SmViewShell* pView = GetView();
944         assert(pView);
945         bool bInPlace = pView->GetViewFrame()->GetFrame().IsInPlace();
946         uno::Reference< frame::XFrame > xFrame( GetBindings().GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface());
947         if ( bInPlace )
948         {
949             uno::Reference<container::XChild> xModel(pView->GetDoc()->GetModel(),
950                                                      uno::UNO_QUERY_THROW);
951             uno::Reference< frame::XModel > xParent( xModel->getParent(), uno::UNO_QUERY_THROW );
952             uno::Reference< frame::XController > xParentCtrler( xParent->getCurrentController() );
953             uno::Reference< frame::XFramesSupplier > xParentFrame( xParentCtrler->getFrame(), uno::UNO_QUERY_THROW );
954             xParentFrame->setActiveFrame( xFrame );
955         }
956         else
957         {
958             xDesktop->setActiveFrame( xFrame );
959         }
960     }
961     catch (uno::Exception &)
962     {
963         SAL_WARN( "starmath", "failed to properly set initial focus to edit window" );
964     }
965 }
966 
AdjustPosition()967 void SmCmdBoxWindow::AdjustPosition()
968 {
969     const tools::Rectangle aRect( Point(), GetParent()->GetOutputSizePixel() );
970     Point aTopLeft( Point( aRect.Left(),
971                            aRect.Bottom() - GetSizePixel().Height() ) );
972     Point aPos( GetParent()->OutputToScreenPixel( aTopLeft ) );
973     if (aPos.X() < 0)
974         aPos.setX( 0 );
975     if (aPos.Y() < 0)
976         aPos.setY( 0 );
977     SetPosPixel( aPos );
978 }
979 
ToggleFloatingMode()980 void SmCmdBoxWindow::ToggleFloatingMode()
981 {
982     SfxDockingWindow::ToggleFloatingMode();
983 
984     if (GetFloatingWindow())
985         GetFloatingWindow()->SetMinOutputSizePixel(Size (200, 50));
986 }
987 
GetFocus()988 void SmCmdBoxWindow::GetFocus()
989 {
990     if (!bExiting)
991         m_xEdit->GrabFocus();
992 }
993 
994 SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW);
995 
SmCmdBoxWrapper(vcl::Window * pParentWindow,sal_uInt16 nId,SfxBindings * pBindings,SfxChildWinInfo * pInfo)996 SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId,
997                                  SfxBindings *pBindings,
998                                  SfxChildWinInfo *pInfo) :
999     SfxChildWindow(pParentWindow, nId)
1000 {
1001     VclPtrInstance<SmCmdBoxWindow> pDialog(pBindings, this, pParentWindow);
1002     SetWindow(pDialog);
1003     // make window docked to the bottom initially (after first start)
1004     SetAlignment(SfxChildAlignment::BOTTOM);
1005     pDialog->setDeferredProperties();
1006     pDialog->set_border_width(CMD_BOX_PADDING);
1007     pDialog->set_margin_top(CMD_BOX_PADDING_TOP);
1008     pDialog->Initialize(pInfo);
1009 }
1010 
SFX_IMPL_SUPERCLASS_INTERFACE(SmViewShell,SfxViewShell)1011 SFX_IMPL_SUPERCLASS_INTERFACE(SmViewShell, SfxViewShell)
1012 
1013 void SmViewShell::InitInterface_Impl()
1014 {
1015     GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
1016                                             SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
1017                                             ToolbarId::Math_Toolbox);
1018     //Dummy-Objectbar, to avoid quiver while activating
1019 
1020     GetStaticInterface()->RegisterChildWindow(SmCmdBoxWrapper::GetChildWindowId());
1021     GetStaticInterface()->RegisterChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId());
1022     GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
1023 }
1024 
1025 SFX_IMPL_NAMED_VIEWFACTORY(SmViewShell, "Default")
1026 {
1027     SFX_VIEW_REGISTRATION(SmDocShell);
1028 }
1029 
InnerResizePixel(const Point & rOfs,const Size & rSize,bool)1030 void SmViewShell::InnerResizePixel(const Point &rOfs, const Size &rSize, bool)
1031 {
1032     Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
1033     if ( !aObjSize.IsEmpty() )
1034     {
1035         Size aProvidedSize = GetWindow()->PixelToLogic(rSize, MapMode(MapUnit::Map100thMM));
1036         SfxViewShell::SetZoomFactor( Fraction( aProvidedSize.Width(), aObjSize.Width() ),
1037                         Fraction( aProvidedSize.Height(), aObjSize.Height() ) );
1038     }
1039 
1040     SetBorderPixel( SvBorder() );
1041     mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
1042     GetGraphicWidget().SetTotalSize();
1043 }
1044 
OuterResizePixel(const Point & rOfs,const Size & rSize)1045 void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize)
1046 {
1047     mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
1048     if (GetDoc()->IsPreview())
1049         mxGraphicWindow->ZoomToFitInWindow();
1050 }
1051 
QueryObjAreaPixel(tools::Rectangle & rRect) const1052 void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
1053 {
1054     rRect.SetSize(mxGraphicWindow->GetSizePixel());
1055 }
1056 
SetZoomFactor(const Fraction & rX,const Fraction & rY)1057 void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY )
1058 {
1059     const Fraction &rFrac = std::min(rX, rY);
1060     mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(tools::Long(rFrac * Fraction( 100, 1 ))));
1061 
1062     //To avoid rounding errors base class regulates crooked values too
1063     //if necessary
1064     SfxViewShell::SetZoomFactor( rX, rY );
1065 }
1066 
GetTextLineSize(OutputDevice const & rDevice,const OUString & rLine)1067 Size SmViewShell::GetTextLineSize(OutputDevice const & rDevice, const OUString& rLine)
1068 {
1069     Size   aSize(rDevice.GetTextWidth(rLine), rDevice.GetTextHeight());
1070     const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
1071 
1072     if (nTabPos)
1073     {
1074         aSize.setWidth( 0 );
1075         sal_Int32 nPos = 0;
1076         do
1077         {
1078             if (nPos > 0)
1079                 aSize.setWidth( ((aSize.Width() / nTabPos) + 1) * nTabPos );
1080 
1081             const OUString aText = rLine.getToken(0, '\t', nPos);
1082             aSize.AdjustWidth(rDevice.GetTextWidth(aText) );
1083         }
1084         while (nPos >= 0);
1085     }
1086 
1087     return aSize;
1088 }
1089 
GetTextSize(OutputDevice const & rDevice,const OUString & rText,tools::Long MaxWidth)1090 Size SmViewShell::GetTextSize(OutputDevice const & rDevice, const OUString& rText, tools::Long MaxWidth)
1091 {
1092     Size aSize;
1093     Size aTextSize;
1094     if (rText.isEmpty())
1095         return aTextSize;
1096 
1097     sal_Int32 nPos = 0;
1098     do
1099     {
1100         OUString aLine = rText.getToken(0, '\n', nPos);
1101         aLine = aLine.replaceAll("\r", "");
1102 
1103         aSize = GetTextLineSize(rDevice, aLine);
1104 
1105         if (aSize.Width() > MaxWidth)
1106         {
1107             do
1108             {
1109                 OUString aText;
1110                 sal_Int32 m = aLine.getLength();
1111                 sal_Int32 nLen = m;
1112 
1113                 for (sal_Int32 n = 0; n < nLen; n++)
1114                 {
1115                     sal_Unicode cLineChar = aLine[n];
1116                     if ((cLineChar == ' ') || (cLineChar == '\t'))
1117                     {
1118                         aText = aLine.copy(0, n);
1119                         if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
1120                             m = n;
1121                         else
1122                             break;
1123                     }
1124                 }
1125 
1126                 aText = aLine.copy(0, m);
1127                 aLine = aLine.replaceAt(0, m, "");
1128                 aSize = GetTextLineSize(rDevice, aText);
1129                 aTextSize.AdjustHeight(aSize.Height() );
1130                 aTextSize.setWidth( std::clamp(aSize.Width(), aTextSize.Width(), MaxWidth) );
1131 
1132                 aLine = comphelper::string::stripStart(aLine, ' ');
1133                 aLine = comphelper::string::stripStart(aLine, '\t');
1134                 aLine = comphelper::string::stripStart(aLine, ' ');
1135             }
1136             while (!aLine.isEmpty());
1137         }
1138         else
1139         {
1140             aTextSize.AdjustHeight(aSize.Height() );
1141             aTextSize.setWidth( std::max(aTextSize.Width(), aSize.Width()) );
1142         }
1143     }
1144     while (nPos >= 0);
1145 
1146     return aTextSize;
1147 }
1148 
DrawTextLine(OutputDevice & rDevice,const Point & rPosition,const OUString & rLine)1149 void SmViewShell::DrawTextLine(OutputDevice& rDevice, const Point& rPosition, const OUString& rLine)
1150 {
1151     Point aPoint(rPosition);
1152     const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
1153 
1154     if (nTabPos)
1155     {
1156         sal_Int32 nPos = 0;
1157         do
1158         {
1159             if (nPos > 0)
1160                 aPoint.setX( ((aPoint.X() / nTabPos) + 1) * nTabPos );
1161 
1162             OUString aText = rLine.getToken(0, '\t', nPos);
1163             rDevice.DrawText(aPoint, aText);
1164             aPoint.AdjustX(rDevice.GetTextWidth(aText) );
1165         }
1166         while ( nPos >= 0 );
1167     }
1168     else
1169         rDevice.DrawText(aPoint, rLine);
1170 }
1171 
DrawText(OutputDevice & rDevice,const Point & rPosition,const OUString & rText,sal_uInt16 MaxWidth)1172 void SmViewShell::DrawText(OutputDevice& rDevice, const Point& rPosition, const OUString& rText, sal_uInt16 MaxWidth)
1173 {
1174     if (rText.isEmpty())
1175         return;
1176 
1177     Point aPoint(rPosition);
1178     Size aSize;
1179 
1180     sal_Int32 nPos = 0;
1181     do
1182     {
1183         OUString aLine = rText.getToken(0, '\n', nPos);
1184         aLine = aLine.replaceAll("\r", "");
1185         aSize = GetTextLineSize(rDevice, aLine);
1186         if (aSize.Width() > MaxWidth)
1187         {
1188             do
1189             {
1190                 OUString aText;
1191                 sal_Int32 m = aLine.getLength();
1192                 sal_Int32 nLen = m;
1193 
1194                 for (sal_Int32 n = 0; n < nLen; n++)
1195                 {
1196                     sal_Unicode cLineChar = aLine[n];
1197                     if ((cLineChar == ' ') || (cLineChar == '\t'))
1198                     {
1199                         aText = aLine.copy(0, n);
1200                         if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
1201                             m = n;
1202                         else
1203                             break;
1204                     }
1205                 }
1206                 aText = aLine.copy(0, m);
1207                 aLine = aLine.replaceAt(0, m, "");
1208 
1209                 DrawTextLine(rDevice, aPoint, aText);
1210                 aPoint.AdjustY(aSize.Height() );
1211 
1212                 aLine = comphelper::string::stripStart(aLine, ' ');
1213                 aLine = comphelper::string::stripStart(aLine, '\t');
1214                 aLine = comphelper::string::stripStart(aLine, ' ');
1215             }
1216             while (GetTextLineSize(rDevice, aLine).Width() > MaxWidth);
1217 
1218             // print the remaining text
1219             if (!aLine.isEmpty())
1220             {
1221                 DrawTextLine(rDevice, aPoint, aLine);
1222                 aPoint.AdjustY(aSize.Height() );
1223             }
1224         }
1225         else
1226         {
1227             DrawTextLine(rDevice, aPoint, aLine);
1228             aPoint.AdjustY(aSize.Height() );
1229         }
1230     }
1231     while ( nPos >= 0 );
1232 }
1233 
Impl_Print(OutputDevice & rOutDev,const SmPrintUIOptions & rPrintUIOptions,tools::Rectangle aOutRect)1234 void SmViewShell::Impl_Print(OutputDevice &rOutDev, const SmPrintUIOptions &rPrintUIOptions, tools::Rectangle aOutRect )
1235 {
1236     const bool bIsPrintTitle = rPrintUIOptions.getBoolValue( PRTUIOPT_TITLE_ROW, true );
1237     const bool bIsPrintFrame = rPrintUIOptions.getBoolValue( PRTUIOPT_BORDER, true );
1238     const bool bIsPrintFormulaText = rPrintUIOptions.getBoolValue( PRTUIOPT_FORMULA_TEXT, true );
1239     SmPrintSize ePrintSize( static_cast< SmPrintSize >( rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL ) ));
1240     const sal_uInt16 nZoomFactor = static_cast< sal_uInt16 >(rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_SCALE, 100 ));
1241 
1242     rOutDev.Push();
1243     rOutDev.SetLineColor( COL_BLACK );
1244 
1245     // output text on top
1246     if (bIsPrintTitle)
1247     {
1248         Size aSize600 (0, 600);
1249         Size aSize650 (0, 650);
1250         vcl::Font aFont(FAMILY_DONTKNOW, aSize600);
1251 
1252         aFont.SetAlignment(ALIGN_TOP);
1253         aFont.SetWeight(WEIGHT_BOLD);
1254         aFont.SetFontSize(aSize650);
1255         aFont.SetColor( COL_BLACK );
1256         rOutDev.SetFont(aFont);
1257 
1258         Size aTitleSize (GetTextSize(rOutDev, GetDoc()->GetTitle(), aOutRect.GetWidth() - 200));
1259 
1260         aFont.SetWeight(WEIGHT_NORMAL);
1261         aFont.SetFontSize(aSize600);
1262         rOutDev.SetFont(aFont);
1263 
1264         Size aDescSize (GetTextSize(rOutDev, GetDoc()->GetComment(), aOutRect.GetWidth() - 200));
1265 
1266         if (bIsPrintFrame)
1267             rOutDev.DrawRect(tools::Rectangle(aOutRect.TopLeft(),
1268                                Size(aOutRect.GetWidth(), 100 + aTitleSize.Height() + 200 + aDescSize.Height() + 100)));
1269         aOutRect.AdjustTop(200 );
1270 
1271         // output title
1272         aFont.SetWeight(WEIGHT_BOLD);
1273         aFont.SetFontSize(aSize650);
1274         rOutDev.SetFont(aFont);
1275         Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aTitleSize.Width())  / 2,
1276                      aOutRect.Top());
1277         DrawText(rOutDev, aPoint, GetDoc()->GetTitle(),
1278                  sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1279         aOutRect.AdjustTop(aTitleSize.Height() + 200 );
1280 
1281         // output description
1282         aFont.SetWeight(WEIGHT_NORMAL);
1283         aFont.SetFontSize(aSize600);
1284         rOutDev.SetFont(aFont);
1285         aPoint.setX( aOutRect.Left() + (aOutRect.GetWidth()  - aDescSize.Width())  / 2 );
1286         aPoint.setY( aOutRect.Top() );
1287         DrawText(rOutDev, aPoint, GetDoc()->GetComment(),
1288                  sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1289         aOutRect.AdjustTop(aDescSize.Height() + 300 );
1290     }
1291 
1292     // output text on bottom
1293     if (bIsPrintFormulaText)
1294     {
1295         vcl::Font aFont(FAMILY_DONTKNOW, Size(0, 600));
1296         aFont.SetAlignment(ALIGN_TOP);
1297         aFont.SetColor( COL_BLACK );
1298 
1299         // get size
1300         rOutDev.SetFont(aFont);
1301 
1302         Size aSize (GetTextSize(rOutDev, GetDoc()->GetText(), aOutRect.GetWidth() - 200));
1303 
1304         aOutRect.AdjustBottom( -(aSize.Height() + 600) );
1305 
1306         if (bIsPrintFrame)
1307             rOutDev.DrawRect(tools::Rectangle(aOutRect.BottomLeft(),
1308                                Size(aOutRect.GetWidth(), 200 + aSize.Height() + 200)));
1309 
1310         Point aPoint (aOutRect.Left() + (aOutRect.GetWidth()  - aSize.Width())  / 2,
1311                       aOutRect.Bottom() + 300);
1312         DrawText(rOutDev, aPoint, GetDoc()->GetText(),
1313                  sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1314         aOutRect.AdjustBottom( -200 );
1315     }
1316 
1317     if (bIsPrintFrame)
1318         rOutDev.DrawRect(aOutRect);
1319 
1320     aOutRect.AdjustTop(100 );
1321     aOutRect.AdjustLeft(100 );
1322     aOutRect.AdjustBottom( -100 );
1323     aOutRect.AdjustRight( -100 );
1324 
1325     Size aSize (GetDoc()->GetSize());
1326 
1327     MapMode    OutputMapMode;
1328     // PDF export should always use PRINT_SIZE_NORMAL ...
1329     if (!rPrintUIOptions.getBoolValue( "IsPrinter" ) )
1330         ePrintSize = PRINT_SIZE_NORMAL;
1331     switch (ePrintSize)
1332     {
1333         case PRINT_SIZE_NORMAL:
1334             OutputMapMode = MapMode(MapUnit::Map100thMM);
1335             break;
1336 
1337         case PRINT_SIZE_SCALED:
1338             if (!aSize.IsEmpty())
1339             {
1340                 Size     OutputSize (rOutDev.LogicToPixel(Size(aOutRect.GetWidth(),
1341                                                             aOutRect.GetHeight()), MapMode(MapUnit::Map100thMM)));
1342                 Size     GraphicSize (rOutDev.LogicToPixel(aSize, MapMode(MapUnit::Map100thMM)));
1343                 sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width()  * 100, GraphicSize.Width())),
1344                                                                           tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
1345                 nZ -= 10;
1346                 Fraction aFraction (std::clamp(nZ, MINZOOM, sal_uInt16(100)));
1347 
1348                 OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
1349             }
1350             else
1351                 OutputMapMode = MapMode(MapUnit::Map100thMM);
1352             break;
1353 
1354         case PRINT_SIZE_ZOOMED:
1355         {
1356             Fraction aFraction( nZoomFactor, 100 );
1357 
1358             OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
1359             break;
1360         }
1361     }
1362 
1363     aSize = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aSize, OutputMapMode),
1364                                    MapMode(MapUnit::Map100thMM));
1365 
1366     Point aPos (aOutRect.Left() + (aOutRect.GetWidth()  - aSize.Width())  / 2,
1367                 aOutRect.Top()  + (aOutRect.GetHeight() - aSize.Height()) / 2);
1368 
1369     aPos     = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aPos, MapMode(MapUnit::Map100thMM)),
1370                                           OutputMapMode);
1371     aOutRect   = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aOutRect, MapMode(MapUnit::Map100thMM)),
1372                                           OutputMapMode);
1373 
1374     rOutDev.SetMapMode(OutputMapMode);
1375     rOutDev.SetClipRegion(vcl::Region(aOutRect));
1376     GetDoc()->DrawFormula(rOutDev, aPos);
1377     rOutDev.SetClipRegion();
1378 
1379     rOutDev.Pop();
1380 }
1381 
GetPrinter(bool bCreate)1382 SfxPrinter* SmViewShell::GetPrinter(bool bCreate)
1383 {
1384     SmDocShell* pDoc = GetDoc();
1385     if (pDoc->HasPrinter() || bCreate)
1386         return pDoc->GetPrinter();
1387     return nullptr;
1388 }
1389 
SetPrinter(SfxPrinter * pNewPrinter,SfxPrinterChangeFlags nDiffFlags)1390 sal_uInt16 SmViewShell::SetPrinter(SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
1391 {
1392     SfxPrinter *pOld = GetDoc()->GetPrinter();
1393     if ( pOld && pOld->IsPrinting() )
1394         return SFX_PRINTERROR_BUSY;
1395 
1396     if ((nDiffFlags & SfxPrinterChangeFlags::PRINTER) == SfxPrinterChangeFlags::PRINTER)
1397         GetDoc()->SetPrinter( pNewPrinter );
1398 
1399     if ((nDiffFlags & SfxPrinterChangeFlags::OPTIONS) == SfxPrinterChangeFlags::OPTIONS)
1400     {
1401         SmModule *pp = SM_MOD();
1402         pp->GetConfig()->ItemSetToConfig(pNewPrinter->GetOptions());
1403     }
1404     return 0;
1405 }
1406 
HasPrintOptionsPage() const1407 bool SmViewShell::HasPrintOptionsPage() const
1408 {
1409     return true;
1410 }
1411 
CreatePrintOptionsPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & rOptions)1412 std::unique_ptr<SfxTabPage> SmViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController,
1413                                                        const SfxItemSet &rOptions)
1414 {
1415     return SmPrintOptionsTabPage::Create(pPage, pController, rOptions);
1416 }
1417 
GetEditWindow()1418 SmEditWindow *SmViewShell::GetEditWindow()
1419 {
1420     SmCmdBoxWrapper* pWrapper = static_cast<SmCmdBoxWrapper*>(
1421                                     GetViewFrame()->GetChildWindow(SmCmdBoxWrapper::GetChildWindowId()));
1422 
1423     if (pWrapper != nullptr)
1424     {
1425         SmEditWindow& rEditWin = pWrapper->GetEditWindow();
1426         return &rEditWin;
1427     }
1428 
1429     return nullptr;
1430 }
1431 
GetDockingWindow()1432 SmElementsDockingWindow* SmViewShell::GetDockingWindow()
1433 {
1434     auto eldockwinwrap = GetViewFrame()->GetChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId());
1435     if(eldockwinwrap)
1436         return dynamic_cast<SmElementsDockingWindow*>(eldockwinwrap->GetWindow());
1437     else
1438         return nullptr;
1439 }
1440 
SetStatusText(const OUString & rText)1441 void SmViewShell::SetStatusText(const OUString& rText)
1442 {
1443     maStatusText = rText;
1444     GetViewFrame()->GetBindings().Invalidate(SID_TEXTSTATUS);
1445 }
1446 
ShowError(const SmErrorDesc * pErrorDesc)1447 void SmViewShell::ShowError(const SmErrorDesc* pErrorDesc)
1448 {
1449     assert(GetDoc());
1450     if (pErrorDesc || nullptr != (pErrorDesc = GetDoc()->GetParser()->GetError()) )
1451     {
1452         SetStatusText( pErrorDesc->m_aText );
1453         GetEditWindow()->MarkError( Point( pErrorDesc->m_pNode->GetColumn(),
1454                                            pErrorDesc->m_pNode->GetRow()));
1455     }
1456 }
1457 
NextError()1458 void SmViewShell::NextError()
1459 {
1460     assert(GetDoc());
1461     const SmErrorDesc   *pErrorDesc = GetDoc()->GetParser()->NextError();
1462 
1463     if (pErrorDesc)
1464         ShowError( pErrorDesc );
1465 }
1466 
PrevError()1467 void SmViewShell::PrevError()
1468 {
1469     assert(GetDoc());
1470     const SmErrorDesc   *pErrorDesc = GetDoc()->GetParser()->PrevError();
1471 
1472     if (pErrorDesc)
1473         ShowError( pErrorDesc );
1474 }
1475 
Insert(SfxMedium & rMedium)1476 void SmViewShell::Insert( SfxMedium& rMedium )
1477 {
1478     SmDocShell *pDoc = GetDoc();
1479     bool bRet = false;
1480 
1481     uno::Reference <embed::XStorage> xStorage = rMedium.GetStorage();
1482     if (xStorage.is() && xStorage->getElementNames().hasElements())
1483     {
1484         if (xStorage->hasByName("content.xml"))
1485         {
1486             // is this a fabulous math package ?
1487             Reference<css::frame::XModel> xModel(pDoc->GetModel());
1488             SmXMLImportWrapper aEquation(xModel);    //!! modifies the result of pDoc->GetText() !!
1489             bRet = ERRCODE_NONE == aEquation.Import(rMedium);
1490         }
1491     }
1492 
1493     if (!bRet)
1494         return;
1495 
1496     OUString aText = pDoc->GetText();
1497     SmEditWindow *pEditWin = GetEditWindow();
1498     if (pEditWin)
1499         pEditWin->InsertText( aText );
1500     else
1501     {
1502         SAL_WARN( "starmath", "EditWindow missing" );
1503     }
1504 
1505     pDoc->Parse();
1506     pDoc->SetModified();
1507 
1508     SfxBindings &rBnd = GetViewFrame()->GetBindings();
1509     rBnd.Invalidate(SID_GRAPHIC_SM);
1510     rBnd.Invalidate(SID_TEXT);
1511 }
1512 
InsertFrom(SfxMedium & rMedium)1513 void SmViewShell::InsertFrom(SfxMedium &rMedium)
1514 {
1515     bool bSuccess = false;
1516     SmDocShell* pDoc = GetDoc();
1517     SvStream* pStream = rMedium.GetInStream();
1518 
1519     if (pStream)
1520     {
1521         const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
1522         if ( rFltName == MATHML_XML )
1523         {
1524             Reference<css::frame::XModel> xModel(pDoc->GetModel());
1525             SmXMLImportWrapper aEquation(xModel);    //!! modifies the result of pDoc->GetText() !!
1526             bSuccess = ERRCODE_NONE == aEquation.Import(rMedium);
1527         }
1528     }
1529 
1530     if (!bSuccess)
1531         return;
1532 
1533     OUString aText = pDoc->GetText();
1534     SmEditWindow *pEditWin = GetEditWindow();
1535     if (pEditWin)
1536         pEditWin->InsertText(aText);
1537     else
1538         SAL_WARN( "starmath", "EditWindow missing" );
1539 
1540     pDoc->Parse();
1541     pDoc->SetModified();
1542 
1543     SfxBindings& rBnd = GetViewFrame()->GetBindings();
1544     rBnd.Invalidate(SID_GRAPHIC_SM);
1545     rBnd.Invalidate(SID_TEXT);
1546 }
1547 
Execute(SfxRequest & rReq)1548 void SmViewShell::Execute(SfxRequest& rReq)
1549 {
1550     SmEditWindow *pWin = GetEditWindow();
1551 
1552     switch (rReq.GetSlot())
1553     {
1554         case SID_FORMULACURSOR:
1555         {
1556             SmModule *pp = SM_MOD();
1557 
1558             const SfxItemSet  *pArgs = rReq.GetArgs();
1559             const SfxPoolItem *pItem;
1560 
1561             bool  bVal;
1562             if ( pArgs &&
1563                  SfxItemState::SET == pArgs->GetItemState( SID_FORMULACURSOR, false, &pItem))
1564                 bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
1565             else
1566                 bVal = !pp->GetConfig()->IsShowFormulaCursor();
1567 
1568             pp->GetConfig()->SetShowFormulaCursor(bVal);
1569             if (!IsInlineEditEnabled())
1570                 GetGraphicWidget().ShowCursor(bVal);
1571             break;
1572         }
1573         case SID_DRAW:
1574             if (pWin)
1575             {
1576                 GetDoc()->SetText( pWin->GetText() );
1577                 SetStatusText(OUString());
1578                 ShowError( nullptr );
1579                 GetDoc()->Repaint();
1580             }
1581             break;
1582 
1583         case SID_ZOOM_OPTIMAL:
1584             mxGraphicWindow->ZoomToFitInWindow();
1585             break;
1586 
1587         case SID_ZOOMIN:
1588             mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() + 25);
1589             break;
1590 
1591         case SID_ZOOMOUT:
1592             SAL_WARN_IF( mxGraphicWindow->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" );
1593             mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() - 25);
1594             break;
1595 
1596         case SID_COPYOBJECT:
1597         {
1598             //TODO/LATER: does not work because of UNO Tunneling - will be fixed later
1599             Reference< datatransfer::XTransferable > xTrans( GetDoc()->GetModel(), uno::UNO_QUERY );
1600             if( xTrans.is() )
1601             {
1602                 auto pTrans = comphelper::getUnoTunnelImplementation<TransferableHelper>(xTrans);
1603                 if (pTrans)
1604                 {
1605                     SmEditWindow *pEditWin = GetEditWindow();
1606                     pTrans->CopyToClipboard(pEditWin->GetClipboard());
1607                 }
1608             }
1609         }
1610         break;
1611 
1612         case SID_PASTEOBJECT:
1613         {
1614             SmEditWindow *pEditWin = GetEditWindow();
1615             TransferableDataHelper aData(TransferableDataHelper::CreateFromClipboard(pEditWin->GetClipboard()));
1616             uno::Reference < io::XInputStream > xStrm;
1617             SotClipboardFormatId nId;
1618             if( aData.GetTransferable().is() &&
1619                 ( aData.HasFormat( nId = SotClipboardFormatId::EMBEDDED_OBJ ) ||
1620                   (aData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) &&
1621                    aData.HasFormat( nId = SotClipboardFormatId::EMBED_SOURCE ))))
1622                 xStrm = aData.GetInputStream(nId, OUString());
1623 
1624             if (xStrm.is())
1625             {
1626                 try
1627                 {
1628                     uno::Reference < embed::XStorage > xStorage =
1629                             ::comphelper::OStorageHelper::GetStorageFromInputStream( xStrm, ::comphelper::getProcessComponentContext() );
1630                     SfxMedium aMedium( xStorage, OUString() );
1631                     Insert( aMedium );
1632                     GetDoc()->UpdateText();
1633                 }
1634                 catch (uno::Exception &)
1635                 {
1636                     SAL_WARN( "starmath", "SmViewShell::Execute (SID_PASTEOBJECT): failed to get storage from input stream" );
1637                 }
1638             }
1639         }
1640         break;
1641 
1642 
1643         case SID_CUT:
1644             if (pWin)
1645                 pWin->Cut();
1646             break;
1647 
1648         case SID_COPY:
1649             if (pWin)
1650             {
1651                 if (pWin->IsAllSelected())
1652                 {
1653                     GetViewFrame()->GetDispatcher()->ExecuteList(
1654                                 SID_COPYOBJECT, SfxCallMode::RECORD,
1655                                 { new SfxVoidItem(SID_COPYOBJECT) });
1656                 }
1657                 else
1658                     pWin->Copy();
1659             }
1660             break;
1661 
1662         case SID_PASTE:
1663             {
1664                 bool bCallExec = nullptr == pWin;
1665                 if( !bCallExec )
1666                 {
1667                     SmEditWindow *pEditWin = GetEditWindow();
1668                     TransferableDataHelper aDataHelper(
1669                         TransferableDataHelper::CreateFromClipboard(
1670                                                     pEditWin->GetClipboard()));
1671 
1672                     if( aDataHelper.GetTransferable().is() &&
1673                         aDataHelper.HasFormat( SotClipboardFormatId::STRING ))
1674                         pWin->Paste();
1675                     else
1676                         bCallExec = true;
1677                 }
1678                 if( bCallExec )
1679                 {
1680                     GetViewFrame()->GetDispatcher()->ExecuteList(
1681                             SID_PASTEOBJECT, SfxCallMode::RECORD,
1682                             { new SfxVoidItem(SID_PASTEOBJECT) });
1683                 }
1684             }
1685             break;
1686 
1687         case SID_DELETE:
1688             if (pWin)
1689                 pWin->Delete();
1690             break;
1691 
1692         case SID_SELECT:
1693             if (pWin)
1694                 pWin->SelectAll();
1695             break;
1696 
1697         case SID_INSERTCOMMANDTEXT:
1698         {
1699             const SfxStringItem& rItem = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT));
1700 
1701             if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
1702             {
1703                 pWin->InsertText(rItem.GetValue());
1704             }
1705             if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
1706             {
1707                 GetDoc()->GetCursor().InsertCommandText(rItem.GetValue());
1708                 GetGraphicWidget().GrabFocus();
1709             }
1710             break;
1711 
1712         }
1713 
1714         case SID_INSERTSPECIAL:
1715         {
1716             const SfxStringItem& rItem =
1717                 static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTSPECIAL));
1718 
1719             if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
1720                 pWin->InsertText(rItem.GetValue());
1721             if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
1722                 GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
1723             break;
1724         }
1725 
1726         case SID_IMPORT_FORMULA:
1727         {
1728             mpRequest.reset(new SfxRequest( rReq ));
1729             mpDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr,
1730                               GetDoc()->GetFactory().GetFactoryName()));
1731             mpDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) );
1732             break;
1733         }
1734 
1735         case SID_IMPORT_MATHML_CLIPBOARD:
1736         {
1737             SmEditWindow *pEditWin = GetEditWindow();
1738             TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pEditWin->GetClipboard()));
1739             uno::Reference < io::XInputStream > xStrm;
1740             if  ( aDataHelper.GetTransferable().is() )
1741             {
1742                 SotClipboardFormatId nId = SotClipboardFormatId::MATHML;
1743                 if (aDataHelper.HasFormat(nId))
1744                 {
1745                     xStrm = aDataHelper.GetInputStream(nId, "");
1746                     if (xStrm.is())
1747                     {
1748                         SfxMedium aClipboardMedium;
1749                         aClipboardMedium.GetItemSet(); //generate initial itemset, not sure if necessary
1750                         std::shared_ptr<const SfxFilter> pMathFilter =
1751                             SfxFilter::GetFilterByName(MATHML_XML);
1752                         aClipboardMedium.SetFilter(pMathFilter);
1753                         aClipboardMedium.setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/);
1754                         InsertFrom(aClipboardMedium);
1755                         GetDoc()->UpdateText();
1756                     }
1757                 }
1758                 else
1759                 {
1760                     nId = SotClipboardFormatId::STRING;
1761                     if (aDataHelper.HasFormat(nId))
1762                     {
1763                         // In case of FORMAT_STRING no stream exists, need to generate one
1764                         OUString aString;
1765                         if (aDataHelper.GetString( nId, aString))
1766                         {
1767                             // tdf#117091 force xml declaration to exist
1768                             if (!aString.startsWith("<?xml"))
1769                                 aString = "<?xml version=\"1.0\"?>\n" + aString;
1770 
1771                             SfxMedium aClipboardMedium;
1772                             aClipboardMedium.GetItemSet(); //generates initial itemset, not sure if necessary
1773                             std::shared_ptr<const SfxFilter> pMathFilter =
1774                                 SfxFilter::GetFilterByName(MATHML_XML);
1775                             aClipboardMedium.SetFilter(pMathFilter);
1776 
1777                             std::unique_ptr<SvMemoryStream> pStrm;
1778                             // The text to be imported might asserts encoding like 'encoding="utf-8"' but FORMAT_STRING is UTF-16.
1779                             // Force encoding to UTF-16, if encoding exists.
1780                             bool bForceUTF16 = false;
1781                             sal_Int32 nPosL = aString.indexOf("encoding=\"");
1782                             sal_Int32 nPosU = -1;
1783                             if ( nPosL >= 0 && nPosL +10 < aString.getLength() )
1784                             {
1785                                 nPosL += 10;
1786                                 nPosU = aString.indexOf( '"',nPosL);
1787                                 if (nPosU > nPosL)
1788                                 {
1789                                     bForceUTF16 = true;
1790                                 }
1791                             }
1792                             if ( bForceUTF16 )
1793                             {
1794                                 OUString aNewString = aString.replaceAt( nPosL,nPosU-nPosL,"UTF-16");
1795                                 pStrm.reset(new SvMemoryStream( const_cast<sal_Unicode *>(aNewString.getStr()), aNewString.getLength() * sizeof(sal_Unicode), StreamMode::READ));
1796                             }
1797                             else
1798                             {
1799                                 pStrm.reset(new SvMemoryStream( const_cast<sal_Unicode *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ));
1800                             }
1801                             uno::Reference<io::XInputStream> xStrm2( new ::utl::OInputStreamWrapper(*pStrm) );
1802                             aClipboardMedium.setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/);
1803                             InsertFrom(aClipboardMedium);
1804                             GetDoc()->UpdateText();
1805                         }
1806                     }
1807                 }
1808             }
1809             break;
1810         }
1811 
1812         case SID_NEXTERR:
1813             NextError();
1814             if (pWin)
1815                 pWin->GrabFocus();
1816             break;
1817 
1818         case SID_PREVERR:
1819             PrevError();
1820             if (pWin)
1821                 pWin->GrabFocus();
1822             break;
1823 
1824         case SID_NEXTMARK:
1825             if (pWin)
1826             {
1827                 pWin->SelNextMark();
1828                 pWin->GrabFocus();
1829             }
1830             break;
1831 
1832         case SID_PREVMARK:
1833             if (pWin)
1834             {
1835                 pWin->SelPrevMark();
1836                 pWin->GrabFocus();
1837             }
1838             break;
1839 
1840         case SID_TEXTSTATUS:
1841         {
1842             if (rReq.GetArgs() != nullptr)
1843             {
1844                 const SfxStringItem& rItem =
1845                     static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_TEXTSTATUS));
1846 
1847                 SetStatusText(rItem.GetValue());
1848             }
1849 
1850             break;
1851         }
1852 
1853         case SID_GETEDITTEXT:
1854             if (pWin && !pWin->GetText().isEmpty())
1855                 GetDoc()->SetText( pWin->GetText() );
1856             break;
1857 
1858         case SID_ATTR_ZOOM:
1859         {
1860             if ( !GetViewFrame()->GetFrame().IsInPlace() )
1861             {
1862                 const SfxItemSet *pSet = rReq.GetArgs();
1863                 if ( pSet )
1864                 {
1865                     ZoomByItemSet(pSet);
1866                 }
1867                 else
1868                 {
1869                     SfxItemSet aSet( SmDocShell::GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{});
1870                     aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
1871                     SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1872                     ScopedVclPtr<AbstractSvxZoomDialog> xDlg(pFact->CreateSvxZoomDialog(GetViewFrame()->GetWindow().GetFrameWeld(), aSet));
1873                     xDlg->SetLimits( MINZOOM, MAXZOOM );
1874                     if (xDlg->Execute() != RET_CANCEL)
1875                         ZoomByItemSet(xDlg->GetOutputItemSet());
1876                 }
1877             }
1878         }
1879         break;
1880 
1881         case SID_ATTR_ZOOMSLIDER:
1882         {
1883             const SfxItemSet *pArgs = rReq.GetArgs();
1884             const SfxPoolItem* pItem;
1885 
1886             if ( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) )
1887             {
1888                 const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
1889                 mxGraphicWindow->SetZoom(nCurrentZoom);
1890             }
1891         }
1892         break;
1893 
1894         case SID_ELEMENTSDOCKINGWINDOW:
1895         {
1896             GetViewFrame()->ToggleChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
1897             GetViewFrame()->GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW );
1898 
1899             rReq.Ignore ();
1900         }
1901         break;
1902 
1903         case SID_UNICODE_NOTATION_TOGGLE:
1904         {
1905             EditEngine* pEditEngine = nullptr;
1906             if( pWin )
1907                 pEditEngine = pWin->GetEditEngine();
1908 
1909             EditView* pEditView = nullptr;
1910             if( pEditEngine )
1911                 pEditView = pEditEngine->GetView();
1912 
1913             if( pEditView )
1914             {
1915                 const OUString sInput = pEditView->GetSurroundingText();
1916                 ESelection aSel(  pWin->GetSelection() );
1917 
1918                 if ( aSel.nStartPos > aSel.nEndPos )
1919                     aSel.nEndPos = aSel.nStartPos;
1920 
1921                 //calculate a valid end-position by reading logical characters
1922                 sal_Int32 nUtf16Pos=0;
1923                 while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
1924                 {
1925                     sInput.iterateCodePoints(&nUtf16Pos);
1926                     if( nUtf16Pos > aSel.nEndPos )
1927                         aSel.nEndPos = nUtf16Pos;
1928                 }
1929 
1930                 ToggleUnicodeCodepoint aToggle;
1931                 while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
1932                     --nUtf16Pos;
1933                 const OUString sReplacement = aToggle.ReplacementString();
1934                 if( !sReplacement.isEmpty() )
1935                 {
1936                     pEditView->SetSelection( aSel );
1937                     pEditEngine->UndoActionStart(EDITUNDO_REPLACEALL);
1938                     aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
1939                     pWin->SetSelection( aSel );
1940                     pEditView->InsertText( sReplacement, true );
1941                     pEditEngine->UndoActionEnd();
1942                     pWin->Flush();
1943                 }
1944             }
1945         }
1946         break;
1947 
1948         case SID_SYMBOLS_CATALOGUE:
1949         {
1950 
1951             // get device used to retrieve the FontList
1952             SmDocShell *pDoc = GetDoc();
1953             OutputDevice *pDev = pDoc->GetPrinter();
1954             if (!pDev || pDev->GetDevFontCount() == 0)
1955                 pDev = &SM_MOD()->GetDefaultVirtualDev();
1956             SAL_WARN_IF( !pDev, "starmath", "device for font list missing" );
1957 
1958             SmModule *pp = SM_MOD();
1959             SmSymbolDialog aDialog(pWin ? pWin->GetFrameWeld() : nullptr, pDev, pp->GetSymbolManager(), *this);
1960             aDialog.run();
1961         }
1962         break;
1963     }
1964     rReq.Done();
1965 }
1966 
1967 
GetState(SfxItemSet & rSet)1968 void SmViewShell::GetState(SfxItemSet &rSet)
1969 {
1970     SfxWhichIter aIter(rSet);
1971 
1972     SmEditWindow *pEditWin = GetEditWindow();
1973     for (sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich())
1974     {
1975         switch (nWh)
1976         {
1977         case SID_CUT:
1978         case SID_COPY:
1979         case SID_DELETE:
1980             if (! pEditWin || ! pEditWin->IsSelected())
1981                 rSet.DisableItem(nWh);
1982             break;
1983 
1984         case SID_PASTE:
1985             if (pEditWin)
1986             {
1987                 TransferableDataHelper aDataHelper(
1988                         TransferableDataHelper::CreateFromClipboard(
1989                                                         pEditWin->GetClipboard()) );
1990 
1991                 mbPasteState = aDataHelper.GetTransferable().is() &&
1992                  ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ||
1993                    aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ||
1994                    (aDataHelper.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )
1995                       && aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )));
1996             }
1997             if( !mbPasteState )
1998                 rSet.DisableItem( nWh );
1999             break;
2000 
2001         case SID_ATTR_ZOOM:
2002             rSet.Put(SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
2003             [[fallthrough]];
2004         case SID_ZOOMIN:
2005         case SID_ZOOMOUT:
2006         case SID_ZOOM_OPTIMAL:
2007             if ( GetViewFrame()->GetFrame().IsInPlace() )
2008                 rSet.DisableItem( nWh );
2009             break;
2010 
2011         case SID_ATTR_ZOOMSLIDER :
2012             {
2013                 const sal_uInt16 nCurrentZoom = mxGraphicWindow->GetZoom();
2014                 SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM );
2015                 aZoomSliderItem.AddSnappingPoint( 100 );
2016                 rSet.Put( aZoomSliderItem );
2017             }
2018         break;
2019 
2020         case SID_NEXTERR:
2021         case SID_PREVERR:
2022         case SID_NEXTMARK:
2023         case SID_PREVMARK:
2024         case SID_DRAW:
2025         case SID_SELECT:
2026             if (! pEditWin || pEditWin->IsEmpty())
2027                 rSet.DisableItem(nWh);
2028             break;
2029 
2030         case SID_TEXTSTATUS:
2031             {
2032                 rSet.Put(SfxStringItem(nWh, maStatusText));
2033             }
2034             break;
2035 
2036         case SID_FORMULACURSOR:
2037             {
2038                 SmModule *pp = SM_MOD();
2039                 rSet.Put(SfxBoolItem(nWh, pp->GetConfig()->IsShowFormulaCursor()));
2040             }
2041             break;
2042         case SID_ELEMENTSDOCKINGWINDOW:
2043             {
2044                 bool bState = false;
2045                 SfxChildWindow *pChildWnd = GetViewFrame()->
2046                         GetChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
2047                 if (pChildWnd  &&  pChildWnd->GetWindow()->IsVisible())
2048                     bState = true;
2049                 rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState));
2050             }
2051             break;
2052         }
2053     }
2054 }
2055 
SmViewShell(SfxViewFrame * pFrame_,SfxViewShell *)2056 SmViewShell::SmViewShell(SfxViewFrame *pFrame_, SfxViewShell *)
2057     : SfxViewShell(pFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS)
2058     , mxGraphicWindow(VclPtr<SmGraphicWindow>::Create(*this))
2059     , maGraphicController(mxGraphicWindow->GetGraphicWidget(), SID_GRAPHIC_SM, pFrame_->GetBindings())
2060     , mbPasteState(false)
2061     , mbInsertIntoEditWindow(false)
2062 {
2063     SetStatusText(OUString());
2064     SetWindow(mxGraphicWindow.get());
2065     SfxShell::SetName("SmView");
2066     SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() );
2067 }
2068 
~SmViewShell()2069 SmViewShell::~SmViewShell()
2070 {
2071     //!! this view shell is not active anymore !!
2072     // Thus 'SmGetActiveView' will give a 0 pointer.
2073     // Thus we need to supply this view as argument
2074     SmEditWindow *pEditWin = GetEditWindow();
2075     if (pEditWin)
2076         pEditWin->DeleteEditView();
2077     mxGraphicWindow.disposeAndClear();
2078 }
2079 
Deactivate(bool bIsMDIActivate)2080 void SmViewShell::Deactivate( bool bIsMDIActivate )
2081 {
2082     SmEditWindow *pEdit = GetEditWindow();
2083     if ( pEdit )
2084         pEdit->Flush();
2085 
2086     SfxViewShell::Deactivate( bIsMDIActivate );
2087 }
2088 
Activate(bool bIsMDIActivate)2089 void SmViewShell::Activate( bool bIsMDIActivate )
2090 {
2091     SfxViewShell::Activate( bIsMDIActivate );
2092 
2093     SmEditWindow *pEdit = GetEditWindow();
2094     if ( pEdit )
2095     {
2096         //! Since there is no way to be informed if a "drag and drop"
2097         //! event has taken place, we call SetText here in order to
2098         //! synchronize the GraphicWindow display with the text in the
2099         //! EditEngine.
2100         SmDocShell *pDoc = GetDoc();
2101         pDoc->SetText( pDoc->GetEditEngine().GetText() );
2102 
2103         if ( bIsMDIActivate )
2104             pEdit->GrabFocus();
2105     }
2106 }
2107 
IMPL_LINK(SmViewShell,DialogClosedHdl,sfx2::FileDialogHelper *,_pFileDlg,void)2108 IMPL_LINK( SmViewShell, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
2109 {
2110     assert(_pFileDlg && "SmViewShell::DialogClosedHdl(): no file dialog");
2111     assert(mpDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter");
2112 
2113     if ( ERRCODE_NONE == _pFileDlg->GetError() )
2114     {
2115         std::unique_ptr<SfxMedium> pMedium = mpDocInserter->CreateMedium();
2116 
2117         if ( pMedium )
2118         {
2119             if ( pMedium->IsStorage() )
2120                 Insert( *pMedium );
2121             else
2122                 InsertFrom( *pMedium );
2123             pMedium.reset();
2124 
2125             SmDocShell* pDoc = GetDoc();
2126             pDoc->UpdateText();
2127             pDoc->ArrangeFormula();
2128             pDoc->Repaint();
2129             // adjust window, repaint, increment ModifyCount,...
2130             GetViewFrame()->GetBindings().Invalidate(SID_GRAPHIC_SM);
2131         }
2132     }
2133 
2134     mpRequest->SetReturnValue( SfxBoolItem( mpRequest->GetSlot(), true ) );
2135     mpRequest->Done();
2136 }
2137 
Notify(SfxBroadcaster &,const SfxHint & rHint)2138 void SmViewShell::Notify( SfxBroadcaster& , const SfxHint& rHint )
2139 {
2140     switch( rHint.GetId() )
2141     {
2142         case SfxHintId::ModeChanged:
2143         case SfxHintId::DocChanged:
2144             GetViewFrame()->GetBindings().InvalidateAll(false);
2145         break;
2146         default:
2147         break;
2148     }
2149 }
2150 
IsInlineEditEnabled()2151 bool SmViewShell::IsInlineEditEnabled()
2152 {
2153     return officecfg::Office::Common::Misc::ExperimentalMode::get();
2154 }
2155 
ZoomByItemSet(const SfxItemSet * pSet)2156 void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet)
2157 {
2158     assert(pSet);
2159     const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM);
2160     switch( rZoom.GetType() )
2161     {
2162         case SvxZoomType::PERCENT:
2163             mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(rZoom.GetValue ()));
2164             break;
2165 
2166         case SvxZoomType::OPTIMAL:
2167             mxGraphicWindow->ZoomToFitInWindow();
2168             break;
2169 
2170         case SvxZoomType::PAGEWIDTH:
2171         case SvxZoomType::WHOLEPAGE:
2172         {
2173             const MapMode aMap( MapUnit::Map100thMM );
2174             SfxPrinter *pPrinter = GetPrinter( true );
2175             tools::Rectangle  OutputRect(Point(), pPrinter->GetOutputSize());
2176             Size       OutputSize(pPrinter->LogicToPixel(Size(OutputRect.GetWidth(),
2177                                                               OutputRect.GetHeight()), aMap));
2178             Size       GraphicSize(pPrinter->LogicToPixel(GetDoc()->GetSize(), aMap));
2179             sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width()  * 100, GraphicSize.Width())),
2180                                                                       tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
2181             mxGraphicWindow->SetZoom(nZ);
2182             break;
2183         }
2184         default:
2185             break;
2186     }
2187 }
2188 
2189 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2190