Bump version to 21.06.18.1
[LibreOffice.git] / starmath / source / view.cxx
blobabd832340d99001c7e6e3e97d8690183e1a09518
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
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/decoview.hxx>
58 #include <vcl/menu.hxx>
59 #include <vcl/settings.hxx>
60 #include <vcl/virdev.hxx>
61 #include <sal/log.hxx>
62 #include <tools/svborder.hxx>
64 #include <unotools/streamwrap.hxx>
66 #include <unomodel.hxx>
67 #include <view.hxx>
68 #include "cfgitem.hxx"
69 #include <dialog.hxx>
70 #include <document.hxx>
71 #include <starmath.hrc>
72 #include <strings.hrc>
73 #include <smmod.hxx>
74 #include "mathmlimport.hxx"
75 #include <cursor.hxx>
76 #include "accessibility.hxx"
77 #include <ElementsDockingWindow.hxx>
78 #include <helpids.h>
79 #include <cassert>
80 #include <memory>
82 #define MINZOOM sal_uInt16(25)
83 #define MAXZOOM sal_uInt16(800)
85 // space around the edit window, in pixels
86 // fdo#69111: Increased border on the top so that the window is
87 // easier to tear off.
88 #define CMD_BOX_PADDING 4
89 #define CMD_BOX_PADDING_TOP 10
91 #define ShellClass_SmViewShell
92 #include <smslots.hxx>
94 using namespace css;
95 using namespace css::accessibility;
96 using namespace css::uno;
98 SmGraphicWindow::SmGraphicWindow(SmViewShell* pShell)
99 : ScrollableWindow(&pShell->GetViewFrame()->GetWindow())
100 , pViewShell(pShell)
101 , nZoom(100)
103 assert(pViewShell);
104 // docking windows are usually hidden (often already done in the
105 // resource) and will be shown by the sfx framework.
106 Hide();
108 const Fraction aFraction(1, 1);
109 SetMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction));
111 SetTotalSize();
113 SetHelpId(HID_SMA_WIN_DOCUMENT);
115 ShowLine(false);
116 CaretBlinkInit();
119 SmGraphicWindow::~SmGraphicWindow()
121 disposeOnce();
124 void SmGraphicWindow::dispose()
126 if (mxAccessible.is())
127 mxAccessible->ClearWin(); // make Accessible nonfunctional
128 mxAccessible.clear();
129 CaretBlinkStop();
130 ScrollableWindow::dispose();
133 void SmGraphicWindow::StateChanged(StateChangedType eType)
135 if (eType == StateChangedType::InitShow)
136 Show();
137 ScrollableWindow::StateChanged(eType);
140 void SmGraphicWindow::MouseButtonDown(const MouseEvent& rMEvt)
142 ScrollableWindow::MouseButtonDown(rMEvt);
144 GrabFocus();
146 // set formula-cursor and selection of edit window according to the
147 // position clicked at
149 SAL_WARN_IF( rMEvt.GetClicks() == 0, "starmath", "0 clicks" );
150 if ( !rMEvt.IsLeft() )
151 return;
153 // get click position relative to formula
154 Point aPos (PixelToLogic(rMEvt.GetPosPixel())
155 - GetFormulaDrawPos());
157 const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree();
158 if (!pTree)
159 return;
161 if (IsInlineEditEnabled()) {
162 pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, !rMEvt.IsShift());
163 return;
165 const SmNode *pNode = nullptr;
166 // if it was clicked inside the formula then get the appropriate node
167 if (pTree->OrientedDist(aPos) <= 0)
168 pNode = pTree->FindRectClosestTo(aPos);
170 if (!pNode)
171 return;
173 SmEditWindow *pEdit = pViewShell->GetEditWindow();
174 if (!pEdit)
175 return;
176 const SmToken aToken (pNode->GetToken());
178 // set selection to the beginning of the token
179 ESelection aSel (aToken.nRow - 1, aToken.nCol - 1);
181 if (rMEvt.GetClicks() != 1 || aToken.eType == TPLACE)
182 aSel.nEndPos = aSel.nEndPos + sal::static_int_cast< sal_uInt16 >(aToken.aText.getLength());
184 pEdit->SetSelection(aSel);
185 SetCursor(pNode);
187 // allow for immediate editing and
188 //! implicitly synchronize the cursor position mark in this window
189 pEdit->GrabFocus();
192 void SmGraphicWindow::MouseMove(const MouseEvent &rMEvt)
194 ScrollableWindow::MouseMove(rMEvt);
196 if (rMEvt.IsLeft() && IsInlineEditEnabled())
198 Point aPos(PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
199 pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, false);
201 CaretBlinkStop();
202 SetIsCursorVisible(true);
203 CaretBlinkStart();
204 RepaintViewShellDoc();
208 bool SmGraphicWindow::IsInlineEditEnabled()
210 return SmViewShell::IsInlineEditEnabled();
213 void SmGraphicWindow::GetFocus()
215 if (!IsInlineEditEnabled())
216 return;
217 if (pViewShell->GetEditWindow())
218 pViewShell->GetEditWindow()->Flush();
219 //Let view shell know what insertions should be done in visual editor
220 pViewShell->SetInsertIntoEditWindow(false);
221 SetIsCursorVisible(true);
222 ShowLine(true);
223 CaretBlinkStart();
224 RepaintViewShellDoc();
227 void SmGraphicWindow::LoseFocus()
229 ScrollableWindow::LoseFocus();
230 if (mxAccessible.is())
232 uno::Any aOldValue, aNewValue;
233 aOldValue <<= AccessibleStateType::FOCUSED;
234 // aNewValue remains empty
235 mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED,
236 aOldValue, aNewValue );
238 if (!IsInlineEditEnabled())
239 return;
240 SetIsCursorVisible(false);
241 ShowLine(false);
242 CaretBlinkStop();
243 RepaintViewShellDoc();
246 void SmGraphicWindow::RepaintViewShellDoc()
248 SmDocShell* pDoc = pViewShell->GetDoc();
249 if (pDoc)
250 pDoc->Repaint();
253 IMPL_LINK_NOARG(SmGraphicWindow, CaretBlinkTimerHdl, Timer *, void)
255 if (IsCursorVisible())
256 SetIsCursorVisible(false);
257 else
258 SetIsCursorVisible(true);
260 RepaintViewShellDoc();
263 void SmGraphicWindow::CaretBlinkInit()
265 aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWindow, CaretBlinkTimerHdl));
266 aCaretBlinkTimer.SetTimeout( ScrollableWindow::GetSettings().GetStyleSettings().GetCursorBlinkTime() );
269 void SmGraphicWindow::CaretBlinkStart()
271 if (!IsInlineEditEnabled())
272 return;
273 if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME)
274 aCaretBlinkTimer.Start();
277 void SmGraphicWindow::CaretBlinkStop()
279 if (!IsInlineEditEnabled())
280 return;
281 aCaretBlinkTimer.Stop();
284 void SmGraphicWindow::ShowCursor(bool bShow)
285 // shows or hides the formula-cursor depending on 'bShow' is true or not
287 if (IsInlineEditEnabled())
288 return;
290 bool bInvert = bShow != IsCursorVisible();
292 if (bInvert)
293 InvertTracking(aCursorRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow);
295 SetIsCursorVisible(bShow);
298 void SmGraphicWindow::ShowLine(bool bShow)
300 if (!IsInlineEditEnabled())
301 return;
303 bIsLineVisible = bShow;
306 void SmGraphicWindow::SetCursor(const SmNode *pNode)
308 if (IsInlineEditEnabled())
309 return;
311 const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree();
313 // get appropriate rectangle
314 Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
315 aTLPos (GetFormulaDrawPos() + aOffset);
316 aTLPos.AdjustX( -(pNode->GetItalicLeftSpace()) );
317 Size aSize (pNode->GetItalicSize());
319 SetCursor(tools::Rectangle(aTLPos, aSize));
322 void SmGraphicWindow::SetCursor(const tools::Rectangle &rRect)
323 // sets cursor to new position (rectangle) 'rRect'.
324 // The old cursor will be removed, and the new one will be shown if
325 // that is activated in the ConfigItem
327 if (IsInlineEditEnabled())
328 return;
330 SmModule *pp = SM_MOD();
332 if (IsCursorVisible())
333 ShowCursor(false); // clean up remainings of old cursor
334 aCursorRect = rRect;
335 if (pp->GetConfig()->IsShowFormulaCursor())
336 ShowCursor(true); // draw new cursor
339 const SmNode * SmGraphicWindow::SetCursorPos(sal_uInt16 nRow, sal_uInt16 nCol)
340 // looks for a VISIBLE node in the formula tree with its token at
341 // (or around) the position 'nRow', 'nCol' in the edit window
342 // (row and column numbering starts with 1 there!).
343 // If there is such a node the formula-cursor is set to cover that nodes
344 // rectangle. If not the formula-cursor will be hidden.
345 // In any case the search result is being returned.
347 if (IsInlineEditEnabled())
348 return nullptr;
350 // find visible node with token at nRow, nCol
351 const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(),
352 *pNode = nullptr;
353 if (pTree)
354 pNode = pTree->FindTokenAt(nRow, nCol);
356 if (pNode)
357 SetCursor(pNode);
358 else
359 ShowCursor(false);
361 return pNode;
364 void SmGraphicWindow::ApplySettings(vcl::RenderContext& rRenderContext)
366 rRenderContext.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
369 void SmGraphicWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
371 SmDocShell& rDoc = *pViewShell->GetDoc();
372 Point aPoint;
374 rDoc.DrawFormula(rRenderContext, aPoint, true); //! modifies aPoint to be the topleft
375 //! corner of the formula
376 aFormulaDrawPos = aPoint;
377 if (IsInlineEditEnabled())
379 //Draw cursor if any...
380 if (pViewShell->GetDoc()->HasCursor() && IsLineVisible())
381 pViewShell->GetDoc()->GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible());
383 else
385 SetIsCursorVisible(false); // (old) cursor must be drawn again
387 const SmEditWindow* pEdit = pViewShell->GetEditWindow();
388 if (pEdit)
389 { // get new position for formula-cursor (for possible altered formula)
390 sal_Int32 nRow;
391 sal_uInt16 nCol;
392 SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol);
393 nRow++;
394 nCol++;
395 const SmNode *pFound = SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
397 SmModule *pp = SM_MOD();
398 if (pFound && pp->GetConfig()->IsShowFormulaCursor())
399 ShowCursor(true);
405 void SmGraphicWindow::SetTotalSize ()
407 SmDocShell &rDoc = *pViewShell->GetDoc();
408 const Size aTmp( PixelToLogic( LogicToPixel( rDoc.GetSize() )));
409 if ( aTmp != ScrollableWindow::GetTotalSize() )
410 ScrollableWindow::SetTotalSize( aTmp );
413 void SmGraphicWindow::KeyInput(const KeyEvent& rKEvt)
415 if (!IsInlineEditEnabled()) {
416 if (! (GetView() && GetView()->KeyInput(rKEvt)) )
417 ScrollableWindow::KeyInput(rKEvt);
418 return;
421 SmCursor& rCursor = pViewShell->GetDoc()->GetCursor();
422 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
423 if (eFunc == KeyFuncType::COPY)
424 rCursor.Copy();
425 else if (eFunc == KeyFuncType::CUT)
426 rCursor.Cut();
427 else if (eFunc == KeyFuncType::PASTE)
428 rCursor.Paste();
429 else {
430 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
431 switch(nCode)
433 case KEY_LEFT:
435 rCursor.Move(this, MoveLeft, !rKEvt.GetKeyCode().IsShift());
436 }break;
437 case KEY_RIGHT:
439 rCursor.Move(this, MoveRight, !rKEvt.GetKeyCode().IsShift());
440 }break;
441 case KEY_UP:
443 rCursor.Move(this, MoveUp, !rKEvt.GetKeyCode().IsShift());
444 }break;
445 case KEY_DOWN:
447 rCursor.Move(this, MoveDown, !rKEvt.GetKeyCode().IsShift());
448 }break;
449 case KEY_RETURN:
451 if(!rKEvt.GetKeyCode().IsShift())
452 rCursor.InsertRow();
453 }break;
454 case KEY_DELETE:
456 if(!rCursor.HasSelection()){
457 rCursor.Move(this, MoveRight, false);
458 if(rCursor.HasComplexSelection()) break;
460 rCursor.Delete();
461 }break;
462 case KEY_BACKSPACE:
464 rCursor.DeletePrev(this);
465 }break;
466 case KEY_ADD:
467 rCursor.InsertElement(PlusElement);
468 break;
469 case KEY_SUBTRACT:
470 if(rKEvt.GetKeyCode().IsShift())
471 rCursor.InsertSubSup(RSUB);
472 else
473 rCursor.InsertElement(MinusElement);
474 break;
475 case KEY_MULTIPLY:
476 rCursor.InsertElement(CDotElement);
477 break;
478 case KEY_DIVIDE:
479 rCursor.InsertFraction();
480 break;
481 case KEY_LESS:
482 rCursor.InsertElement(LessThanElement);
483 break;
484 case KEY_GREATER:
485 rCursor.InsertElement(GreaterThanElement);
486 break;
487 case KEY_EQUAL:
488 rCursor.InsertElement(EqualElement);
489 break;
490 default:
492 sal_Unicode code = rKEvt.GetCharCode();
494 if(code == ' ') {
495 rCursor.InsertElement(BlankElement);
496 }else if(code == '^') {
497 rCursor.InsertSubSup(RSUP);
498 }else if(code == '(') {
499 rCursor.InsertBrackets(SmBracketType::Round);
500 }else if(code == '[') {
501 rCursor.InsertBrackets(SmBracketType::Square);
502 }else if(code == '{') {
503 rCursor.InsertBrackets(SmBracketType::Curly);
504 }else if(code == '!') {
505 rCursor.InsertElement(FactorialElement);
506 }else if(code == '%') {
507 rCursor.InsertElement(PercentElement);
509 else if ((code == ')' && rCursor.IsAtTailOfBracket(SmBracketType::Round))
510 || (code == ']' && rCursor.IsAtTailOfBracket(SmBracketType::Square))
511 || (code == '}' && rCursor.IsAtTailOfBracket(SmBracketType::Curly)))
513 rCursor.Move(this, MoveRight);
515 else{
516 if(code != 0){
517 rCursor.InsertText(OUString(code));
518 }else if (! (GetView() && GetView()->KeyInput(rKEvt)) )
519 ScrollableWindow::KeyInput(rKEvt);
524 CaretBlinkStop();
525 CaretBlinkStart();
526 SetIsCursorVisible(true);
527 RepaintViewShellDoc();
531 void SmGraphicWindow::Command(const CommandEvent& rCEvt)
533 bool bCallBase = true;
534 if ( !pViewShell->GetViewFrame()->GetFrame().IsInPlace() )
536 switch ( rCEvt.GetCommand() )
538 case CommandEventId::ContextMenu:
540 GetParent()->ToTop();
541 Point aPos(5, 5);
542 if (rCEvt.IsMouseEvent())
543 aPos = rCEvt.GetMousePosPixel();
545 // added for replaceability of context menus
546 SfxDispatcher::ExecutePopup( this, &aPos );
548 bCallBase = false;
550 break;
552 case CommandEventId::Wheel:
554 const CommandWheelData* pWData = rCEvt.GetWheelData();
555 if ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() )
557 sal_uInt16 nTmpZoom = GetZoom();
558 if( 0 > pWData->GetDelta() )
559 nTmpZoom -= 10;
560 else
561 nTmpZoom += 10;
562 SetZoom( nTmpZoom );
563 bCallBase = false;
566 break;
568 default: break;
571 if ( bCallBase )
572 ScrollableWindow::Command (rCEvt);
576 void SmGraphicWindow::SetZoom(sal_uInt16 Factor)
578 nZoom = std::clamp(Factor, MINZOOM, MAXZOOM);
579 Fraction aFraction (nZoom, 100);
580 SetMapMode( MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction) );
581 SetTotalSize();
582 SmViewShell *pViewSh = GetView();
583 if (pViewSh)
585 pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOM);
586 pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER);
588 Invalidate();
592 void SmGraphicWindow::ZoomToFitInWindow()
594 SmDocShell &rDoc = *pViewShell->GetDoc();
596 // set defined mapmode before calling 'LogicToPixel' below
597 SetMapMode(MapMode(MapUnit::Map100thMM));
599 Size aSize (LogicToPixel(rDoc.GetSize()));
600 Size aWindowSize (GetSizePixel());
602 if (!aSize.IsEmpty())
604 tools::Long nVal = std::min ((85 * aWindowSize.Width()) / aSize.Width(),
605 (85 * aWindowSize.Height()) / aSize.Height());
606 SetZoom ( sal::static_int_cast< sal_uInt16 >(nVal) );
610 uno::Reference< XAccessible > SmGraphicWindow::CreateAccessible()
612 if (!mxAccessible.is())
614 mxAccessible = new SmGraphicAccessible( this );
616 return mxAccessible.get();
619 /**************************************************************************/
622 SmGraphicController::SmGraphicController(SmGraphicWindow &rSmGraphic,
623 sal_uInt16 nId_,
624 SfxBindings &rBindings) :
625 SfxControllerItem(nId_, rBindings),
626 rGraphic(rSmGraphic)
631 void SmGraphicController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
633 rGraphic.SetTotalSize();
634 rGraphic.Invalidate();
635 SfxControllerItem::StateChanged (nSID, eState, pState);
639 /**************************************************************************/
642 SmEditController::SmEditController(SmEditWindow &rSmEdit,
643 sal_uInt16 nId_,
644 SfxBindings &rBindings) :
645 SfxControllerItem(nId_, rBindings),
646 rEdit(rSmEdit)
652 void SmEditController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
654 const SfxStringItem *pItem = dynamic_cast<const SfxStringItem*>( pState);
656 if ((pItem != nullptr) && (rEdit.GetText() != pItem->GetValue()))
657 rEdit.SetText(pItem->GetValue());
658 SfxControllerItem::StateChanged (nSID, eState, pState);
661 /**************************************************************************/
662 SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow,
663 vcl::Window *pParent) :
664 SfxDockingWindow(pBindings_, pChildWindow, pParent, WB_MOVEABLE|WB_CLOSEABLE|WB_SIZEABLE|WB_DOCKABLE),
665 aEdit (VclPtr<SmEditWindow>::Create(*this)),
666 aController (*aEdit, SID_TEXT, *pBindings_),
667 bExiting (false)
669 SetHelpId( HID_SMA_COMMAND_WIN );
670 SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont)));
671 SetText(SmResId(STR_CMDBOXWINDOW));
673 Hide();
675 aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl));
676 aInitialFocusTimer.SetTimeout(100);
679 SmCmdBoxWindow::~SmCmdBoxWindow ()
681 disposeOnce();
684 void SmCmdBoxWindow::dispose()
686 aInitialFocusTimer.Stop();
687 bExiting = true;
688 aController.dispose();
689 aEdit.disposeAndClear();
690 SfxDockingWindow::dispose();
693 SmViewShell * SmCmdBoxWindow::GetView()
695 SfxDispatcher *pDispatcher = GetBindings().GetDispatcher();
696 SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr;
697 return dynamic_cast<SmViewShell*>( pView);
700 void SmCmdBoxWindow::Resize()
702 tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
703 aRect.AdjustLeft(CMD_BOX_PADDING );
704 aRect.AdjustTop(CMD_BOX_PADDING_TOP );
705 aRect.AdjustRight( -(CMD_BOX_PADDING) );
706 aRect.AdjustBottom( -(CMD_BOX_PADDING) );
708 DecorationView aView(this);
709 aRect = aView.DrawFrame(aRect, DrawFrameStyle::In, DrawFrameFlags::NoDraw);
711 aEdit->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize());
712 SfxDockingWindow::Resize();
713 Invalidate();
716 void SmCmdBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
718 tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel());
719 aRect.AdjustLeft(CMD_BOX_PADDING );
720 aRect.AdjustTop(CMD_BOX_PADDING_TOP );
721 aRect.AdjustRight( -(CMD_BOX_PADDING) );
722 aRect.AdjustBottom( -(CMD_BOX_PADDING) );
724 aEdit->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize());
726 DecorationView aView(&rRenderContext);
727 aView.DrawFrame( aRect, DrawFrameStyle::In );
730 Size SmCmdBoxWindow::CalcDockingSize(SfxChildAlignment eAlign)
732 switch (eAlign)
734 case SfxChildAlignment::LEFT:
735 case SfxChildAlignment::RIGHT:
736 return Size();
737 default:
738 break;
740 return SfxDockingWindow::CalcDockingSize(eAlign);
743 SfxChildAlignment SmCmdBoxWindow::CheckAlignment(SfxChildAlignment eActual,
744 SfxChildAlignment eWish)
746 switch (eWish)
748 case SfxChildAlignment::TOP:
749 case SfxChildAlignment::BOTTOM:
750 case SfxChildAlignment::NOALIGNMENT:
751 return eWish;
752 default:
753 break;
756 return eActual;
759 void SmCmdBoxWindow::StateChanged( StateChangedType nStateChange )
761 if (StateChangedType::InitShow == nStateChange)
763 Resize(); // avoid SmEditWindow not being painted correctly
765 // set initial position of window in floating mode
766 if (IsFloatingMode())
767 AdjustPosition(); //! don't change pos in docking-mode !
769 aInitialFocusTimer.Start();
772 SfxDockingWindow::StateChanged( nStateChange );
775 IMPL_LINK_NOARG( SmCmdBoxWindow, InitialFocusTimerHdl, Timer *, void )
777 // We want to have the focus in the edit window once Math has been opened
778 // to allow for immediate typing.
779 // Problem: There is no proper way to do this
780 // Thus: this timer based solution has been implemented (see GrabFocus below)
782 // Follow-up problem (#i114910): grabbing the focus may bust the help system since
783 // it relies on getting the current frame which conflicts with grabbing the focus.
784 // Thus aside from the 'GrabFocus' call everything else is to get the
785 // help reliably working despite using 'GrabFocus'.
789 uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( comphelper::getProcessComponentContext() );
791 aEdit->GrabFocus();
793 SmViewShell* pView = GetView();
794 assert(pView);
795 bool bInPlace = pView->GetViewFrame()->GetFrame().IsInPlace();
796 uno::Reference< frame::XFrame > xFrame( GetBindings().GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface());
797 if ( bInPlace )
799 uno::Reference<container::XChild> xModel(pView->GetDoc()->GetModel(),
800 uno::UNO_QUERY_THROW);
801 uno::Reference< frame::XModel > xParent( xModel->getParent(), uno::UNO_QUERY_THROW );
802 uno::Reference< frame::XController > xParentCtrler( xParent->getCurrentController() );
803 uno::Reference< frame::XFramesSupplier > xParentFrame( xParentCtrler->getFrame(), uno::UNO_QUERY_THROW );
804 xParentFrame->setActiveFrame( xFrame );
806 else
808 xDesktop->setActiveFrame( xFrame );
811 catch (uno::Exception &)
813 SAL_WARN( "starmath", "failed to properly set initial focus to edit window" );
817 void SmCmdBoxWindow::AdjustPosition()
819 const tools::Rectangle aRect( Point(), GetParent()->GetOutputSizePixel() );
820 Point aTopLeft( Point( aRect.Left(),
821 aRect.Bottom() - GetSizePixel().Height() ) );
822 Point aPos( GetParent()->OutputToScreenPixel( aTopLeft ) );
823 if (aPos.X() < 0)
824 aPos.setX( 0 );
825 if (aPos.Y() < 0)
826 aPos.setY( 0 );
827 SetPosPixel( aPos );
830 void SmCmdBoxWindow::ToggleFloatingMode()
832 SfxDockingWindow::ToggleFloatingMode();
834 if (GetFloatingWindow())
835 GetFloatingWindow()->SetMinOutputSizePixel(Size (200, 50));
838 void SmCmdBoxWindow::GetFocus()
840 if (!bExiting)
841 aEdit->GrabFocus();
844 SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW);
846 SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId,
847 SfxBindings *pBindings,
848 SfxChildWinInfo *pInfo) :
849 SfxChildWindow(pParentWindow, nId)
851 SetWindow(VclPtr<SmCmdBoxWindow>::Create(pBindings, this, pParentWindow));
853 // make window docked to the bottom initially (after first start)
854 SetAlignment(SfxChildAlignment::BOTTOM);
855 static_cast<SfxDockingWindow *>(GetWindow())->Initialize(pInfo);
858 SFX_IMPL_SUPERCLASS_INTERFACE(SmViewShell, SfxViewShell)
860 void SmViewShell::InitInterface_Impl()
862 GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
863 SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
864 ToolbarId::Math_Toolbox);
865 //Dummy-Objectbar, to avoid quiver while activating
867 GetStaticInterface()->RegisterChildWindow(SmCmdBoxWrapper::GetChildWindowId());
868 GetStaticInterface()->RegisterChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId());
869 GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
872 SFX_IMPL_NAMED_VIEWFACTORY(SmViewShell, "Default")
874 SFX_VIEW_REGISTRATION(SmDocShell);
877 void SmViewShell::InnerResizePixel(const Point &rOfs, const Size &rSize, bool)
879 Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
880 if ( !aObjSize.IsEmpty() )
882 Size aProvidedSize = GetWindow()->PixelToLogic(rSize, MapMode(MapUnit::Map100thMM));
883 SfxViewShell::SetZoomFactor( Fraction( aProvidedSize.Width(), aObjSize.Width() ),
884 Fraction( aProvidedSize.Height(), aObjSize.Height() ) );
887 SetBorderPixel( SvBorder() );
888 GetGraphicWindow().SetPosSizePixel(rOfs, rSize);
889 GetGraphicWindow().SetTotalSize();
892 void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize)
894 SmGraphicWindow &rWin = GetGraphicWindow();
895 rWin.SetPosSizePixel(rOfs, rSize);
896 if (GetDoc()->IsPreview())
897 rWin.ZoomToFitInWindow();
898 rWin.PaintImmediately();
901 void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
903 rRect.SetSize( GetGraphicWindow().GetSizePixel() );
906 void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY )
908 const Fraction &rFrac = std::min(rX, rY);
909 GetGraphicWindow().SetZoom(sal::static_int_cast<sal_uInt16>(tools::Long(rFrac * Fraction( 100, 1 ))));
911 //To avoid rounding errors base class regulates crooked values too
912 //if necessary
913 SfxViewShell::SetZoomFactor( rX, rY );
916 Size SmViewShell::GetTextLineSize(OutputDevice const & rDevice, const OUString& rLine)
918 Size aSize(rDevice.GetTextWidth(rLine), rDevice.GetTextHeight());
919 const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
921 if (nTabPos)
923 aSize.setWidth( 0 );
924 sal_Int32 nPos = 0;
927 if (nPos > 0)
928 aSize.setWidth( ((aSize.Width() / nTabPos) + 1) * nTabPos );
930 const OUString aText = rLine.getToken(0, '\t', nPos);
931 aSize.AdjustWidth(rDevice.GetTextWidth(aText) );
933 while (nPos >= 0);
936 return aSize;
939 Size SmViewShell::GetTextSize(OutputDevice const & rDevice, const OUString& rText, tools::Long MaxWidth)
941 Size aSize;
942 Size aTextSize;
943 if (rText.isEmpty())
944 return aTextSize;
946 sal_Int32 nPos = 0;
949 OUString aLine = rText.getToken(0, '\n', nPos);
950 aLine = aLine.replaceAll("\r", "");
952 aSize = GetTextLineSize(rDevice, aLine);
954 if (aSize.Width() > MaxWidth)
958 OUString aText;
959 sal_Int32 m = aLine.getLength();
960 sal_Int32 nLen = m;
962 for (sal_Int32 n = 0; n < nLen; n++)
964 sal_Unicode cLineChar = aLine[n];
965 if ((cLineChar == ' ') || (cLineChar == '\t'))
967 aText = aLine.copy(0, n);
968 if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
969 m = n;
970 else
971 break;
975 aText = aLine.copy(0, m);
976 aLine = aLine.replaceAt(0, m, "");
977 aSize = GetTextLineSize(rDevice, aText);
978 aTextSize.AdjustHeight(aSize.Height() );
979 aTextSize.setWidth( std::clamp(aSize.Width(), aTextSize.Width(), MaxWidth) );
981 aLine = comphelper::string::stripStart(aLine, ' ');
982 aLine = comphelper::string::stripStart(aLine, '\t');
983 aLine = comphelper::string::stripStart(aLine, ' ');
985 while (!aLine.isEmpty());
987 else
989 aTextSize.AdjustHeight(aSize.Height() );
990 aTextSize.setWidth( std::max(aTextSize.Width(), aSize.Width()) );
993 while (nPos >= 0);
995 return aTextSize;
998 void SmViewShell::DrawTextLine(OutputDevice& rDevice, const Point& rPosition, const OUString& rLine)
1000 Point aPoint(rPosition);
1001 const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
1003 if (nTabPos)
1005 sal_Int32 nPos = 0;
1008 if (nPos > 0)
1009 aPoint.setX( ((aPoint.X() / nTabPos) + 1) * nTabPos );
1011 OUString aText = rLine.getToken(0, '\t', nPos);
1012 rDevice.DrawText(aPoint, aText);
1013 aPoint.AdjustX(rDevice.GetTextWidth(aText) );
1015 while ( nPos >= 0 );
1017 else
1018 rDevice.DrawText(aPoint, rLine);
1022 void SmViewShell::DrawText(OutputDevice& rDevice, const Point& rPosition, const OUString& rText, sal_uInt16 MaxWidth)
1024 if (rText.isEmpty())
1025 return;
1027 Point aPoint(rPosition);
1028 Size aSize;
1030 sal_Int32 nPos = 0;
1033 OUString aLine = rText.getToken(0, '\n', nPos);
1034 aLine = aLine.replaceAll("\r", "");
1035 aSize = GetTextLineSize(rDevice, aLine);
1036 if (aSize.Width() > MaxWidth)
1040 OUString aText;
1041 sal_Int32 m = aLine.getLength();
1042 sal_Int32 nLen = m;
1044 for (sal_Int32 n = 0; n < nLen; n++)
1046 sal_Unicode cLineChar = aLine[n];
1047 if ((cLineChar == ' ') || (cLineChar == '\t'))
1049 aText = aLine.copy(0, n);
1050 if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
1051 m = n;
1052 else
1053 break;
1056 aText = aLine.copy(0, m);
1057 aLine = aLine.replaceAt(0, m, "");
1059 DrawTextLine(rDevice, aPoint, aText);
1060 aPoint.AdjustY(aSize.Height() );
1062 aLine = comphelper::string::stripStart(aLine, ' ');
1063 aLine = comphelper::string::stripStart(aLine, '\t');
1064 aLine = comphelper::string::stripStart(aLine, ' ');
1066 while (GetTextLineSize(rDevice, aLine).Width() > MaxWidth);
1068 // print the remaining text
1069 if (!aLine.isEmpty())
1071 DrawTextLine(rDevice, aPoint, aLine);
1072 aPoint.AdjustY(aSize.Height() );
1075 else
1077 DrawTextLine(rDevice, aPoint, aLine);
1078 aPoint.AdjustY(aSize.Height() );
1081 while ( nPos >= 0 );
1084 void SmViewShell::Impl_Print(OutputDevice &rOutDev, const SmPrintUIOptions &rPrintUIOptions, tools::Rectangle aOutRect )
1086 const bool bIsPrintTitle = rPrintUIOptions.getBoolValue( PRTUIOPT_TITLE_ROW, true );
1087 const bool bIsPrintFrame = rPrintUIOptions.getBoolValue( PRTUIOPT_BORDER, true );
1088 const bool bIsPrintFormulaText = rPrintUIOptions.getBoolValue( PRTUIOPT_FORMULA_TEXT, true );
1089 SmPrintSize ePrintSize( static_cast< SmPrintSize >( rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL ) ));
1090 const sal_uInt16 nZoomFactor = static_cast< sal_uInt16 >(rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_SCALE, 100 ));
1092 rOutDev.Push();
1093 rOutDev.SetLineColor( COL_BLACK );
1095 // output text on top
1096 if (bIsPrintTitle)
1098 Size aSize600 (0, 600);
1099 Size aSize650 (0, 650);
1100 vcl::Font aFont(FAMILY_DONTKNOW, aSize600);
1102 aFont.SetAlignment(ALIGN_TOP);
1103 aFont.SetWeight(WEIGHT_BOLD);
1104 aFont.SetFontSize(aSize650);
1105 aFont.SetColor( COL_BLACK );
1106 rOutDev.SetFont(aFont);
1108 Size aTitleSize (GetTextSize(rOutDev, GetDoc()->GetTitle(), aOutRect.GetWidth() - 200));
1110 aFont.SetWeight(WEIGHT_NORMAL);
1111 aFont.SetFontSize(aSize600);
1112 rOutDev.SetFont(aFont);
1114 Size aDescSize (GetTextSize(rOutDev, GetDoc()->GetComment(), aOutRect.GetWidth() - 200));
1116 if (bIsPrintFrame)
1117 rOutDev.DrawRect(tools::Rectangle(aOutRect.TopLeft(),
1118 Size(aOutRect.GetWidth(), 100 + aTitleSize.Height() + 200 + aDescSize.Height() + 100)));
1119 aOutRect.AdjustTop(200 );
1121 // output title
1122 aFont.SetWeight(WEIGHT_BOLD);
1123 aFont.SetFontSize(aSize650);
1124 rOutDev.SetFont(aFont);
1125 Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aTitleSize.Width()) / 2,
1126 aOutRect.Top());
1127 DrawText(rOutDev, aPoint, GetDoc()->GetTitle(),
1128 sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1129 aOutRect.AdjustTop(aTitleSize.Height() + 200 );
1131 // output description
1132 aFont.SetWeight(WEIGHT_NORMAL);
1133 aFont.SetFontSize(aSize600);
1134 rOutDev.SetFont(aFont);
1135 aPoint.setX( aOutRect.Left() + (aOutRect.GetWidth() - aDescSize.Width()) / 2 );
1136 aPoint.setY( aOutRect.Top() );
1137 DrawText(rOutDev, aPoint, GetDoc()->GetComment(),
1138 sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1139 aOutRect.AdjustTop(aDescSize.Height() + 300 );
1142 // output text on bottom
1143 if (bIsPrintFormulaText)
1145 vcl::Font aFont(FAMILY_DONTKNOW, Size(0, 600));
1146 aFont.SetAlignment(ALIGN_TOP);
1147 aFont.SetColor( COL_BLACK );
1149 // get size
1150 rOutDev.SetFont(aFont);
1152 Size aSize (GetTextSize(rOutDev, GetDoc()->GetText(), aOutRect.GetWidth() - 200));
1154 aOutRect.AdjustBottom( -(aSize.Height() + 600) );
1156 if (bIsPrintFrame)
1157 rOutDev.DrawRect(tools::Rectangle(aOutRect.BottomLeft(),
1158 Size(aOutRect.GetWidth(), 200 + aSize.Height() + 200)));
1160 Point aPoint (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
1161 aOutRect.Bottom() + 300);
1162 DrawText(rOutDev, aPoint, GetDoc()->GetText(),
1163 sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1164 aOutRect.AdjustBottom( -200 );
1167 if (bIsPrintFrame)
1168 rOutDev.DrawRect(aOutRect);
1170 aOutRect.AdjustTop(100 );
1171 aOutRect.AdjustLeft(100 );
1172 aOutRect.AdjustBottom( -100 );
1173 aOutRect.AdjustRight( -100 );
1175 Size aSize (GetDoc()->GetSize());
1177 MapMode OutputMapMode;
1178 // PDF export should always use PRINT_SIZE_NORMAL ...
1179 if (!rPrintUIOptions.getBoolValue( "IsPrinter" ) )
1180 ePrintSize = PRINT_SIZE_NORMAL;
1181 switch (ePrintSize)
1183 case PRINT_SIZE_NORMAL:
1184 OutputMapMode = MapMode(MapUnit::Map100thMM);
1185 break;
1187 case PRINT_SIZE_SCALED:
1188 if (!aSize.IsEmpty())
1190 Size OutputSize (rOutDev.LogicToPixel(Size(aOutRect.GetWidth(),
1191 aOutRect.GetHeight()), MapMode(MapUnit::Map100thMM)));
1192 Size GraphicSize (rOutDev.LogicToPixel(aSize, MapMode(MapUnit::Map100thMM)));
1193 sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())),
1194 tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
1195 nZ -= 10;
1196 Fraction aFraction (std::clamp(nZ, MINZOOM, sal_uInt16(100)));
1198 OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
1200 else
1201 OutputMapMode = MapMode(MapUnit::Map100thMM);
1202 break;
1204 case PRINT_SIZE_ZOOMED:
1206 Fraction aFraction( nZoomFactor, 100 );
1208 OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
1209 break;
1213 aSize = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aSize, OutputMapMode),
1214 MapMode(MapUnit::Map100thMM));
1216 Point aPos (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
1217 aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2);
1219 aPos = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aPos, MapMode(MapUnit::Map100thMM)),
1220 OutputMapMode);
1221 aOutRect = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aOutRect, MapMode(MapUnit::Map100thMM)),
1222 OutputMapMode);
1224 rOutDev.SetMapMode(OutputMapMode);
1225 rOutDev.SetClipRegion(vcl::Region(aOutRect));
1226 GetDoc()->DrawFormula(rOutDev, aPos);
1227 rOutDev.SetClipRegion();
1229 rOutDev.Pop();
1232 SfxPrinter* SmViewShell::GetPrinter(bool bCreate)
1234 SmDocShell* pDoc = GetDoc();
1235 if (pDoc->HasPrinter() || bCreate)
1236 return pDoc->GetPrinter();
1237 return nullptr;
1240 sal_uInt16 SmViewShell::SetPrinter(SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
1242 SfxPrinter *pOld = GetDoc()->GetPrinter();
1243 if ( pOld && pOld->IsPrinting() )
1244 return SFX_PRINTERROR_BUSY;
1246 if ((nDiffFlags & SfxPrinterChangeFlags::PRINTER) == SfxPrinterChangeFlags::PRINTER)
1247 GetDoc()->SetPrinter( pNewPrinter );
1249 if ((nDiffFlags & SfxPrinterChangeFlags::OPTIONS) == SfxPrinterChangeFlags::OPTIONS)
1251 SmModule *pp = SM_MOD();
1252 pp->GetConfig()->ItemSetToConfig(pNewPrinter->GetOptions());
1254 return 0;
1257 bool SmViewShell::HasPrintOptionsPage() const
1259 return true;
1262 std::unique_ptr<SfxTabPage> SmViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController,
1263 const SfxItemSet &rOptions)
1265 return SmPrintOptionsTabPage::Create(pPage, pController, rOptions);
1268 SmEditWindow *SmViewShell::GetEditWindow()
1270 SmCmdBoxWrapper* pWrapper = static_cast<SmCmdBoxWrapper*>(
1271 GetViewFrame()->GetChildWindow(SmCmdBoxWrapper::GetChildWindowId()));
1273 if (pWrapper != nullptr)
1275 SmEditWindow& rEditWin = pWrapper->GetEditWindow();
1276 return &rEditWin;
1279 return nullptr;
1282 void SmViewShell::SetStatusText(const OUString& rText)
1284 maStatusText = rText;
1285 GetViewFrame()->GetBindings().Invalidate(SID_TEXTSTATUS);
1288 void SmViewShell::ShowError(const SmErrorDesc* pErrorDesc)
1290 assert(GetDoc());
1291 if (pErrorDesc || nullptr != (pErrorDesc = GetDoc()->GetParser().GetError()) )
1293 SetStatusText( pErrorDesc->m_aText );
1294 GetEditWindow()->MarkError( Point( pErrorDesc->m_pNode->GetColumn(),
1295 pErrorDesc->m_pNode->GetRow()));
1299 void SmViewShell::NextError()
1301 assert(GetDoc());
1302 const SmErrorDesc *pErrorDesc = GetDoc()->GetParser().NextError();
1304 if (pErrorDesc)
1305 ShowError( pErrorDesc );
1308 void SmViewShell::PrevError()
1310 assert(GetDoc());
1311 const SmErrorDesc *pErrorDesc = GetDoc()->GetParser().PrevError();
1313 if (pErrorDesc)
1314 ShowError( pErrorDesc );
1317 void SmViewShell::Insert( SfxMedium& rMedium )
1319 SmDocShell *pDoc = GetDoc();
1320 bool bRet = false;
1322 uno::Reference <embed::XStorage> xStorage = rMedium.GetStorage();
1323 if (xStorage.is() && xStorage->getElementNames().hasElements())
1325 if (xStorage->hasByName("content.xml"))
1327 // is this a fabulous math package ?
1328 Reference<css::frame::XModel> xModel(pDoc->GetModel());
1329 SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
1330 bRet = ERRCODE_NONE == aEquation.Import(rMedium);
1334 if (!bRet)
1335 return;
1337 OUString aText = pDoc->GetText();
1338 SmEditWindow *pEditWin = GetEditWindow();
1339 if (pEditWin)
1340 pEditWin->InsertText( aText );
1341 else
1343 SAL_WARN( "starmath", "EditWindow missing" );
1346 pDoc->Parse();
1347 pDoc->SetModified();
1349 SfxBindings &rBnd = GetViewFrame()->GetBindings();
1350 rBnd.Invalidate(SID_GAPHIC_SM);
1351 rBnd.Invalidate(SID_TEXT);
1354 void SmViewShell::InsertFrom(SfxMedium &rMedium)
1356 bool bSuccess = false;
1357 SmDocShell* pDoc = GetDoc();
1358 SvStream* pStream = rMedium.GetInStream();
1360 if (pStream)
1362 const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
1363 if ( rFltName == MATHML_XML )
1365 Reference<css::frame::XModel> xModel(pDoc->GetModel());
1366 SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
1367 bSuccess = ERRCODE_NONE == aEquation.Import(rMedium);
1371 if (!bSuccess)
1372 return;
1374 OUString aText = pDoc->GetText();
1375 SmEditWindow *pEditWin = GetEditWindow();
1376 if (pEditWin)
1377 pEditWin->InsertText(aText);
1378 else
1379 SAL_WARN( "starmath", "EditWindow missing" );
1381 pDoc->Parse();
1382 pDoc->SetModified();
1384 SfxBindings& rBnd = GetViewFrame()->GetBindings();
1385 rBnd.Invalidate(SID_GAPHIC_SM);
1386 rBnd.Invalidate(SID_TEXT);
1389 void SmViewShell::Execute(SfxRequest& rReq)
1391 SmEditWindow *pWin = GetEditWindow();
1393 switch (rReq.GetSlot())
1395 case SID_FORMULACURSOR:
1397 SmModule *pp = SM_MOD();
1399 const SfxItemSet *pArgs = rReq.GetArgs();
1400 const SfxPoolItem *pItem;
1402 bool bVal;
1403 if ( pArgs &&
1404 SfxItemState::SET == pArgs->GetItemState( SID_FORMULACURSOR, false, &pItem))
1405 bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
1406 else
1407 bVal = !pp->GetConfig()->IsShowFormulaCursor();
1409 pp->GetConfig()->SetShowFormulaCursor(bVal);
1410 if (!IsInlineEditEnabled())
1411 GetGraphicWindow().ShowCursor(bVal);
1412 break;
1414 case SID_DRAW:
1415 if (pWin)
1417 GetDoc()->SetText( pWin->GetText() );
1418 SetStatusText(OUString());
1419 ShowError( nullptr );
1420 GetDoc()->Repaint();
1422 break;
1424 case SID_ZOOM_OPTIMAL:
1425 mpGraphic->ZoomToFitInWindow();
1426 break;
1428 case SID_ZOOMIN:
1429 mpGraphic->SetZoom(mpGraphic->GetZoom() + 25);
1430 break;
1432 case SID_ZOOMOUT:
1433 SAL_WARN_IF( mpGraphic->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" );
1434 mpGraphic->SetZoom(mpGraphic->GetZoom() - 25);
1435 break;
1437 case SID_COPYOBJECT:
1439 //TODO/LATER: does not work because of UNO Tunneling - will be fixed later
1440 Reference< datatransfer::XTransferable > xTrans( GetDoc()->GetModel(), uno::UNO_QUERY );
1441 if( xTrans.is() )
1443 auto pTrans = comphelper::getUnoTunnelImplementation<TransferableHelper>(xTrans);
1444 if( pTrans )
1445 pTrans->CopyToClipboard(GetEditWindow());
1448 break;
1450 case SID_PASTEOBJECT:
1452 TransferableDataHelper aData( TransferableDataHelper::CreateFromSystemClipboard(GetEditWindow()) );
1453 uno::Reference < io::XInputStream > xStrm;
1454 SotClipboardFormatId nId;
1455 if( aData.GetTransferable().is() &&
1456 ( aData.HasFormat( nId = SotClipboardFormatId::EMBEDDED_OBJ ) ||
1457 (aData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) &&
1458 aData.HasFormat( nId = SotClipboardFormatId::EMBED_SOURCE ))))
1459 xStrm = aData.GetInputStream(nId, OUString());
1461 if (xStrm.is())
1465 uno::Reference < embed::XStorage > xStorage =
1466 ::comphelper::OStorageHelper::GetStorageFromInputStream( xStrm, ::comphelper::getProcessComponentContext() );
1467 SfxMedium aMedium( xStorage, OUString() );
1468 Insert( aMedium );
1469 GetDoc()->UpdateText();
1471 catch (uno::Exception &)
1473 SAL_WARN( "starmath", "SmViewShell::Execute (SID_PASTEOBJECT): failed to get storage from input stream" );
1477 break;
1480 case SID_CUT:
1481 if (pWin)
1482 pWin->Cut();
1483 break;
1485 case SID_COPY:
1486 if (pWin)
1488 if (pWin->IsAllSelected())
1490 GetViewFrame()->GetDispatcher()->ExecuteList(
1491 SID_COPYOBJECT, SfxCallMode::RECORD,
1492 { new SfxVoidItem(SID_COPYOBJECT) });
1494 else
1495 pWin->Copy();
1497 break;
1499 case SID_PASTE:
1501 bool bCallExec = nullptr == pWin;
1502 if( !bCallExec )
1504 TransferableDataHelper aDataHelper(
1505 TransferableDataHelper::CreateFromSystemClipboard(
1506 GetEditWindow()) );
1508 if( aDataHelper.GetTransferable().is() &&
1509 aDataHelper.HasFormat( SotClipboardFormatId::STRING ))
1510 pWin->Paste();
1511 else
1512 bCallExec = true;
1514 if( bCallExec )
1516 GetViewFrame()->GetDispatcher()->ExecuteList(
1517 SID_PASTEOBJECT, SfxCallMode::RECORD,
1518 { new SfxVoidItem(SID_PASTEOBJECT) });
1521 break;
1523 case SID_DELETE:
1524 if (pWin)
1525 pWin->Delete();
1526 break;
1528 case SID_SELECT:
1529 if (pWin)
1530 pWin->SelectAll();
1531 break;
1533 case SID_INSERTCOMMANDTEXT:
1535 const SfxStringItem& rItem = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT));
1537 if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
1539 pWin->InsertText(rItem.GetValue());
1541 if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
1543 GetDoc()->GetCursor().InsertCommandText(rItem.GetValue());
1544 GetGraphicWindow().GrabFocus();
1546 break;
1550 case SID_INSERTSPECIAL:
1552 const SfxStringItem& rItem =
1553 static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTSPECIAL));
1555 if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
1556 pWin->InsertText(rItem.GetValue());
1557 if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
1558 GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
1559 break;
1562 case SID_IMPORT_FORMULA:
1564 mpRequest.reset(new SfxRequest( rReq ));
1565 mpDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr,
1566 GetDoc()->GetFactory().GetFactoryName()));
1567 mpDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) );
1568 break;
1571 case SID_IMPORT_MATHML_CLIPBOARD:
1573 TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard(GetEditWindow()) );
1574 uno::Reference < io::XInputStream > xStrm;
1575 if ( aDataHelper.GetTransferable().is() )
1577 SotClipboardFormatId nId = SotClipboardFormatId::MATHML;
1578 if (aDataHelper.HasFormat(nId))
1580 xStrm = aDataHelper.GetInputStream(nId, "");
1581 if (xStrm.is())
1583 std::unique_ptr<SfxMedium> pClipboardMedium(new SfxMedium());
1584 pClipboardMedium->GetItemSet(); //generate initial itemset, not sure if necessary
1585 std::shared_ptr<const SfxFilter> pMathFilter =
1586 SfxFilter::GetFilterByName(MATHML_XML);
1587 pClipboardMedium->SetFilter(pMathFilter);
1588 pClipboardMedium->setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/);
1589 InsertFrom(*pClipboardMedium);
1590 GetDoc()->UpdateText();
1593 else
1595 nId = SotClipboardFormatId::STRING;
1596 if (aDataHelper.HasFormat(nId))
1598 // In case of FORMAT_STRING no stream exists, need to generate one
1599 OUString aString;
1600 if (aDataHelper.GetString( nId, aString))
1602 // tdf#117091 force xml declaration to exist
1603 if (!aString.startsWith("<?xml"))
1604 aString = "<?xml version=\"1.0\"?>\n" + aString;
1606 std::unique_ptr<SfxMedium> pClipboardMedium(new SfxMedium());
1607 pClipboardMedium->GetItemSet(); //generates initial itemset, not sure if necessary
1608 std::shared_ptr<const SfxFilter> pMathFilter =
1609 SfxFilter::GetFilterByName(MATHML_XML);
1610 pClipboardMedium->SetFilter(pMathFilter);
1612 std::unique_ptr<SvMemoryStream> pStrm;
1613 // The text to be imported might asserts encoding like 'encoding="utf-8"' but FORMAT_STRING is UTF-16.
1614 // Force encoding to UTF-16, if encoding exists.
1615 bool bForceUTF16 = false;
1616 sal_Int32 nPosL = aString.indexOf("encoding=\"");
1617 sal_Int32 nPosU = -1;
1618 if ( nPosL >= 0 && nPosL +10 < aString.getLength() )
1620 nPosL += 10;
1621 nPosU = aString.indexOf( '"',nPosL);
1622 if (nPosU > nPosL)
1624 bForceUTF16 = true;
1627 if ( bForceUTF16 )
1629 OUString aNewString = aString.replaceAt( nPosL,nPosU-nPosL,"UTF-16");
1630 pStrm.reset(new SvMemoryStream( const_cast<sal_Unicode *>(aNewString.getStr()), aNewString.getLength() * sizeof(sal_Unicode), StreamMode::READ));
1632 else
1634 pStrm.reset(new SvMemoryStream( const_cast<sal_Unicode *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ));
1636 uno::Reference<io::XInputStream> xStrm2( new ::utl::OInputStreamWrapper(*pStrm) );
1637 pClipboardMedium->setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/);
1638 InsertFrom(*pClipboardMedium);
1639 GetDoc()->UpdateText();
1644 break;
1647 case SID_NEXTERR:
1648 NextError();
1649 if (pWin)
1650 pWin->GrabFocus();
1651 break;
1653 case SID_PREVERR:
1654 PrevError();
1655 if (pWin)
1656 pWin->GrabFocus();
1657 break;
1659 case SID_NEXTMARK:
1660 if (pWin)
1662 pWin->SelNextMark();
1663 pWin->GrabFocus();
1665 break;
1667 case SID_PREVMARK:
1668 if (pWin)
1670 pWin->SelPrevMark();
1671 pWin->GrabFocus();
1673 break;
1675 case SID_TEXTSTATUS:
1677 if (rReq.GetArgs() != nullptr)
1679 const SfxStringItem& rItem =
1680 static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_TEXTSTATUS));
1682 SetStatusText(rItem.GetValue());
1685 break;
1688 case SID_GETEDITTEXT:
1689 if (pWin && !pWin->GetText().isEmpty())
1690 GetDoc()->SetText( pWin->GetText() );
1691 break;
1693 case SID_ATTR_ZOOM:
1695 if ( !GetViewFrame()->GetFrame().IsInPlace() )
1697 const SfxItemSet *pSet = rReq.GetArgs();
1698 if ( pSet )
1700 ZoomByItemSet(pSet);
1702 else
1704 SfxItemSet aSet( SmDocShell::GetPool(), svl::Items<SID_ATTR_ZOOM, SID_ATTR_ZOOM>{});
1705 aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mpGraphic->GetZoom()));
1706 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1707 ScopedVclPtr<AbstractSvxZoomDialog> xDlg(pFact->CreateSvxZoomDialog(GetViewFrame()->GetWindow().GetFrameWeld(), aSet));
1708 xDlg->SetLimits( MINZOOM, MAXZOOM );
1709 if (xDlg->Execute() != RET_CANCEL)
1710 ZoomByItemSet(xDlg->GetOutputItemSet());
1714 break;
1716 case SID_ATTR_ZOOMSLIDER:
1718 const SfxItemSet *pArgs = rReq.GetArgs();
1719 const SfxPoolItem* pItem;
1721 if ( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) )
1723 const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
1724 mpGraphic->SetZoom( nCurrentZoom );
1727 break;
1729 case SID_ELEMENTSDOCKINGWINDOW:
1731 GetViewFrame()->ToggleChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
1732 GetViewFrame()->GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW );
1734 rReq.Ignore ();
1736 break;
1738 case SID_UNICODE_NOTATION_TOGGLE:
1740 EditEngine* pEditEngine = nullptr;
1741 if( pWin )
1742 pEditEngine = pWin->GetEditEngine();
1744 EditView* pEditView = nullptr;
1745 if( pEditEngine )
1746 pEditView = pEditEngine->GetView();
1748 if( pEditView )
1750 const OUString sInput = pEditView->GetSurroundingText();
1751 ESelection aSel( pWin->GetSelection() );
1753 if ( aSel.nStartPos > aSel.nEndPos )
1754 aSel.nEndPos = aSel.nStartPos;
1756 //calculate a valid end-position by reading logical characters
1757 sal_Int32 nUtf16Pos=0;
1758 while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
1760 sInput.iterateCodePoints(&nUtf16Pos);
1761 if( nUtf16Pos > aSel.nEndPos )
1762 aSel.nEndPos = nUtf16Pos;
1765 ToggleUnicodeCodepoint aToggle;
1766 while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
1767 --nUtf16Pos;
1768 const OUString sReplacement = aToggle.ReplacementString();
1769 if( !sReplacement.isEmpty() )
1771 pEditView->SetSelection( aSel );
1772 pEditEngine->UndoActionStart(EDITUNDO_REPLACEALL);
1773 aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
1774 pWin->SetSelection( aSel );
1775 pEditView->InsertText( sReplacement, true );
1776 pEditEngine->UndoActionEnd();
1777 pWin->Flush();
1781 break;
1783 case SID_SYMBOLS_CATALOGUE:
1786 // get device used to retrieve the FontList
1787 SmDocShell *pDoc = GetDoc();
1788 OutputDevice *pDev = pDoc->GetPrinter();
1789 if (!pDev || pDev->GetDevFontCount() == 0)
1790 pDev = &SM_MOD()->GetDefaultVirtualDev();
1791 SAL_WARN_IF( !pDev, "starmath", "device for font list missing" );
1793 SmModule *pp = SM_MOD();
1794 SmSymbolDialog aDialog(pWin ? pWin->GetFrameWeld() : nullptr, pDev, pp->GetSymbolManager(), *this);
1795 aDialog.run();
1797 break;
1799 rReq.Done();
1803 void SmViewShell::GetState(SfxItemSet &rSet)
1805 SfxWhichIter aIter(rSet);
1807 SmEditWindow *pEditWin = GetEditWindow();
1808 for (sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich())
1810 switch (nWh)
1812 case SID_CUT:
1813 case SID_COPY:
1814 case SID_DELETE:
1815 if (! pEditWin || ! pEditWin->IsSelected())
1816 rSet.DisableItem(nWh);
1817 break;
1819 case SID_PASTE:
1820 if (pEditWin)
1822 TransferableDataHelper aDataHelper(
1823 TransferableDataHelper::CreateFromSystemClipboard(
1824 pEditWin) );
1826 mbPasteState = aDataHelper.GetTransferable().is() &&
1827 ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ||
1828 aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ||
1829 (aDataHelper.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )
1830 && aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )));
1832 if( !mbPasteState )
1833 rSet.DisableItem( nWh );
1834 break;
1836 case SID_ATTR_ZOOM:
1837 rSet.Put(SvxZoomItem( SvxZoomType::PERCENT, mpGraphic->GetZoom()));
1838 [[fallthrough]];
1839 case SID_ZOOMIN:
1840 case SID_ZOOMOUT:
1841 case SID_ZOOM_OPTIMAL:
1842 if ( GetViewFrame()->GetFrame().IsInPlace() )
1843 rSet.DisableItem( nWh );
1844 break;
1846 case SID_ATTR_ZOOMSLIDER :
1848 const sal_uInt16 nCurrentZoom = mpGraphic->GetZoom();
1849 SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM );
1850 aZoomSliderItem.AddSnappingPoint( 100 );
1851 rSet.Put( aZoomSliderItem );
1853 break;
1855 case SID_NEXTERR:
1856 case SID_PREVERR:
1857 case SID_NEXTMARK:
1858 case SID_PREVMARK:
1859 case SID_DRAW:
1860 case SID_SELECT:
1861 if (! pEditWin || pEditWin->IsEmpty())
1862 rSet.DisableItem(nWh);
1863 break;
1865 case SID_TEXTSTATUS:
1867 rSet.Put(SfxStringItem(nWh, maStatusText));
1869 break;
1871 case SID_FORMULACURSOR:
1873 SmModule *pp = SM_MOD();
1874 rSet.Put(SfxBoolItem(nWh, pp->GetConfig()->IsShowFormulaCursor()));
1876 break;
1877 case SID_ELEMENTSDOCKINGWINDOW:
1879 bool bState = false;
1880 SfxChildWindow *pChildWnd = GetViewFrame()->
1881 GetChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
1882 if (pChildWnd && pChildWnd->GetWindow()->IsVisible())
1883 bState = true;
1884 rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState));
1886 break;
1891 SmViewShell::SmViewShell(SfxViewFrame *pFrame_, SfxViewShell *)
1892 : SfxViewShell(pFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS)
1893 , mpGraphic(VclPtr<SmGraphicWindow>::Create(this))
1894 , maGraphicController(*mpGraphic, SID_GAPHIC_SM, pFrame_->GetBindings())
1895 , mbPasteState(false)
1896 , mbInsertIntoEditWindow(false)
1898 SetStatusText(OUString());
1899 SetWindow(mpGraphic.get());
1900 SfxShell::SetName("SmView");
1901 SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() );
1905 SmViewShell::~SmViewShell()
1907 //!! this view shell is not active anymore !!
1908 // Thus 'SmGetActiveView' will give a 0 pointer.
1909 // Thus we need to supply this view as argument
1910 SmEditWindow *pEditWin = GetEditWindow();
1911 if (pEditWin)
1912 pEditWin->DeleteEditView();
1913 mpGraphic.disposeAndClear();
1916 void SmViewShell::Deactivate( bool bIsMDIActivate )
1918 SmEditWindow *pEdit = GetEditWindow();
1919 if ( pEdit )
1920 pEdit->Flush();
1922 SfxViewShell::Deactivate( bIsMDIActivate );
1925 void SmViewShell::Activate( bool bIsMDIActivate )
1927 SfxViewShell::Activate( bIsMDIActivate );
1929 SmEditWindow *pEdit = GetEditWindow();
1930 if ( pEdit )
1932 //! Since there is no way to be informed if a "drag and drop"
1933 //! event has taken place, we call SetText here in order to
1934 //! synchronize the GraphicWindow display with the text in the
1935 //! EditEngine.
1936 SmDocShell *pDoc = GetDoc();
1937 pDoc->SetText( pDoc->GetEditEngine().GetText() );
1939 if ( bIsMDIActivate )
1940 pEdit->GrabFocus();
1944 IMPL_LINK( SmViewShell, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
1946 assert(_pFileDlg && "SmViewShell::DialogClosedHdl(): no file dialog");
1947 assert(mpDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter");
1949 if ( ERRCODE_NONE == _pFileDlg->GetError() )
1951 std::unique_ptr<SfxMedium> pMedium = mpDocInserter->CreateMedium();
1953 if ( pMedium )
1955 if ( pMedium->IsStorage() )
1956 Insert( *pMedium );
1957 else
1958 InsertFrom( *pMedium );
1959 pMedium.reset();
1961 SmDocShell* pDoc = GetDoc();
1962 pDoc->UpdateText();
1963 pDoc->ArrangeFormula();
1964 pDoc->Repaint();
1965 // adjust window, repaint, increment ModifyCount,...
1966 GetViewFrame()->GetBindings().Invalidate(SID_GAPHIC_SM);
1970 mpRequest->SetReturnValue( SfxBoolItem( mpRequest->GetSlot(), true ) );
1971 mpRequest->Done();
1974 void SmViewShell::Notify( SfxBroadcaster& , const SfxHint& rHint )
1976 switch( rHint.GetId() )
1978 case SfxHintId::ModeChanged:
1979 case SfxHintId::DocChanged:
1980 GetViewFrame()->GetBindings().InvalidateAll(false);
1981 break;
1982 default:
1983 break;
1987 bool SmViewShell::IsInlineEditEnabled()
1989 return officecfg::Office::Common::Misc::ExperimentalMode::get();
1992 void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet)
1994 assert(pSet);
1995 const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM);
1996 switch( rZoom.GetType() )
1998 case SvxZoomType::PERCENT:
1999 mpGraphic->SetZoom(sal::static_int_cast<sal_uInt16>(rZoom.GetValue ()));
2000 break;
2002 case SvxZoomType::OPTIMAL:
2003 mpGraphic->ZoomToFitInWindow();
2004 break;
2006 case SvxZoomType::PAGEWIDTH:
2007 case SvxZoomType::WHOLEPAGE:
2009 const MapMode aMap( MapUnit::Map100thMM );
2010 SfxPrinter *pPrinter = GetPrinter( true );
2011 tools::Rectangle OutputRect(Point(), pPrinter->GetOutputSize());
2012 Size OutputSize(pPrinter->LogicToPixel(Size(OutputRect.GetWidth(),
2013 OutputRect.GetHeight()), aMap));
2014 Size GraphicSize(pPrinter->LogicToPixel(GetDoc()->GetSize(), aMap));
2015 sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())),
2016 tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
2017 mpGraphic->SetZoom (nZ);
2018 break;
2020 default:
2021 break;
2025 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */