Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / starmath / source / view.cxx
blob76eef9fe6d88360a4463534f3206def014226374
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 <sal/config.h>
22 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
23 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
24 #include <com/sun/star/frame/Desktop.hpp>
25 #include <com/sun/star/frame/XFramesSupplier.hpp>
26 #include <com/sun/star/frame/XModel.hpp>
27 #include <com/sun/star/container/XChild.hpp>
29 #include <comphelper/lok.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/servicehelper.hxx>
32 #include <comphelper/storagehelper.hxx>
33 #include <comphelper/string.hxx>
34 #include <i18nutil/unicode.hxx>
35 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
36 #include <officecfg/Office/Common.hxx>
37 #include <sfx2/dispatch.hxx>
38 #include <sfx2/docfile.hxx>
39 #include <sfx2/docfilt.hxx>
40 #include <sfx2/docinsert.hxx>
41 #include <sfx2/filedlghelper.hxx>
42 #include <sfx2/infobar.hxx>
43 #include <sfx2/lokcomponenthelpers.hxx>
44 #include <sfx2/lokhelper.hxx>
45 #include <sfx2/msg.hxx>
46 #include <sfx2/objface.hxx>
47 #include <sfx2/printer.hxx>
48 #include <sfx2/request.hxx>
49 #include <sfx2/sfxbasecontroller.hxx>
50 #include <sfx2/sidebar/Sidebar.hxx>
51 #include <sfx2/sidebar/SidebarChildWindow.hxx>
52 #include <sfx2/sidebar/SidebarController.hxx>
53 #include <sfx2/viewfac.hxx>
54 #include <svl/eitem.hxx>
55 #include <svl/itemset.hxx>
56 #include <svl/poolitem.hxx>
57 #include <svl/stritem.hxx>
58 #include <vcl/transfer.hxx>
59 #include <svtools/colorcfg.hxx>
60 #include <svl/whiter.hxx>
61 #include <svx/sidebar/SelectionChangeHandler.hxx>
62 #include <svx/zoomslideritem.hxx>
63 #include <editeng/editeng.hxx>
64 #include <editeng/editview.hxx>
65 #include <svx/svxdlg.hxx>
66 #include <sfx2/zoomitem.hxx>
67 #include <vcl/commandevent.hxx>
68 #include <vcl/event.hxx>
69 #include <vcl/settings.hxx>
70 #include <vcl/virdev.hxx>
71 #include <sal/log.hxx>
72 #include <tools/svborder.hxx>
73 #include <o3tl/string_view.hxx>
74 #include <o3tl/temporary.hxx>
76 #include <unotools/streamwrap.hxx>
78 #include <unomodel.hxx>
79 #include <view.hxx>
80 #include <cfgitem.hxx>
81 #include <dialog.hxx>
82 #include <document.hxx>
83 #include <starmath.hrc>
84 #include <strings.hrc>
85 #include <smmod.hxx>
86 #include <mathmlimport.hxx>
87 #include <cursor.hxx>
88 #include "accessibility.hxx"
89 #include <ElementsDockingWindow.hxx>
90 #include <helpids.h>
92 #define MINZOOM sal_uInt16(25)
93 #define MAXZOOM sal_uInt16(800)
95 // space around the edit window, in pixels
96 // fdo#69111: Increased border on the top so that the window is
97 // easier to tear off.
98 #define CMD_BOX_PADDING 3
99 #define CMD_BOX_PADDING_TOP 11
101 #define ShellClass_SmViewShell
102 #include <smslots.hxx>
104 using namespace css;
105 using namespace css::accessibility;
106 using namespace css::uno;
108 SmGraphicWindow::SmGraphicWindow(SmViewShell& rShell)
109 : InterimItemWindow(&rShell.GetViewFrame().GetWindow(), "modules/smath/ui/mathwindow.ui", "MathWindow")
110 , nLinePixH(GetSettings().GetStyleSettings().GetScrollBarSize())
111 , nColumnPixW(nLinePixH)
112 , nZoom(100)
113 // continue to use user-scrolling to make this work equivalent to how it 'always' worked
114 , mxScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow", true))
115 , mxGraphic(new SmGraphicWidget(rShell, *this))
116 , mxGraphicWin(new weld::CustomWeld(*m_xBuilder, "mathview", *mxGraphic))
118 InitControlBase(mxGraphic->GetDrawingArea());
120 mxScrolledWindow->connect_hadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
121 mxScrolledWindow->connect_vadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
123 // docking windows are usually hidden (often already done in the
124 // resource) and will be shown by the sfx framework.
125 Hide();
128 void SmGraphicWindow::dispose()
130 InitControlBase(nullptr);
131 mxGraphicWin.reset();
132 mxGraphic.reset();
133 mxScrolledWindow.reset();
134 InterimItemWindow::dispose();
137 SmGraphicWindow::~SmGraphicWindow()
139 disposeOnce();
142 void SmGraphicWindow::Resize()
144 InterimItemWindow::Resize();
146 // get the new output-size in pixel
147 Size aOutPixSz = GetOutputSizePixel();
149 // determine the size of the output-area and if we need scrollbars
150 const auto nScrSize = mxScrolledWindow->get_scroll_thickness();
151 bool bVVisible = false; // by default no vertical-ScrollBar
152 bool bHVisible = false; // by default no horizontal-ScrollBar
153 bool bChanged; // determines if a visiblility was changed
156 bChanged = false;
158 // does we need a vertical ScrollBar
159 if ( aOutPixSz.Width() < aTotPixSz.Width() && !bHVisible )
161 bHVisible = true;
162 aOutPixSz.AdjustHeight( -nScrSize );
163 bChanged = true;
166 // does we need a horizontal ScrollBar
167 if ( aOutPixSz.Height() < aTotPixSz.Height() && !bVVisible )
169 bVVisible = true;
170 aOutPixSz.AdjustWidth( -nScrSize );
171 bChanged = true;
175 while ( bChanged ); // until no visibility has changed
177 // store the old offset and map-mode
178 MapMode aMap(GetGraphicMapMode());
179 Point aOldPixOffset(aPixOffset);
181 // justify (right/bottom borders should never exceed the virtual window)
182 Size aPixDelta;
183 if ( aPixOffset.X() < 0 &&
184 aPixOffset.X() + aTotPixSz.Width() < aOutPixSz.Width() )
185 aPixDelta.setWidth(
186 aOutPixSz.Width() - ( aPixOffset.X() + aTotPixSz.Width() ) );
187 if ( aPixOffset.Y() < 0 &&
188 aPixOffset.Y() + aTotPixSz.Height() < aOutPixSz.Height() )
189 aPixDelta.setHeight(
190 aOutPixSz.Height() - ( aPixOffset.Y() + aTotPixSz.Height() ) );
191 if ( aPixDelta.Width() || aPixDelta.Height() )
193 aPixOffset.AdjustX(aPixDelta.Width() );
194 aPixOffset.AdjustY(aPixDelta.Height() );
197 // for axis without scrollbar restore the origin
198 if ( !bVVisible || !bHVisible )
200 aPixOffset = Point(
201 bHVisible
202 ? aPixOffset.X()
203 : (aOutPixSz.Width()-aTotPixSz.Width()) / 2,
204 bVVisible
205 ? aPixOffset.Y()
206 : (aOutPixSz.Height()-aTotPixSz.Height()) / 2 );
208 if (bHVisible && mxScrolledWindow->get_hpolicy() == VclPolicyType::NEVER)
209 aPixOffset.setX( 0 );
210 if (bVVisible && mxScrolledWindow->get_vpolicy() == VclPolicyType::NEVER)
211 aPixOffset.setY( 0 );
213 // select the shifted map-mode
214 if (aPixOffset != aOldPixOffset)
215 SetGraphicMapMode(aMap);
217 // show or hide scrollbars
218 mxScrolledWindow->set_vpolicy(bVVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
219 mxScrolledWindow->set_hpolicy(bHVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
221 // resize scrollbars and set their ranges
222 if ( bHVisible )
224 mxScrolledWindow->hadjustment_configure(-aPixOffset.X(), 0, aTotPixSz.Width(), nColumnPixW,
225 aOutPixSz.Width(), aOutPixSz.Width());
227 if ( bVVisible )
229 mxScrolledWindow->vadjustment_configure(-aPixOffset.Y(), 0, aTotPixSz.Height(), nLinePixH,
230 aOutPixSz.Height(), aOutPixSz.Height());
234 IMPL_LINK_NOARG(SmGraphicWindow, ScrollHdl, weld::ScrolledWindow&, void)
236 MapMode aMap(GetGraphicMapMode());
237 Point aNewPixOffset(aPixOffset);
239 // scrolling horizontally?
240 if (mxScrolledWindow->get_hpolicy() == VclPolicyType::ALWAYS)
241 aNewPixOffset.setX(-mxScrolledWindow->hadjustment_get_value());
243 // scrolling vertically?
244 if (mxScrolledWindow->get_vpolicy() == VclPolicyType::ALWAYS)
245 aNewPixOffset.setY(-mxScrolledWindow->vadjustment_get_value());
247 // scrolling?
248 if (aPixOffset == aNewPixOffset)
249 return;
251 // recompute the logical scroll units
252 aPixOffset = aNewPixOffset;
254 SetGraphicMapMode(aMap);
257 void SmGraphicWindow::SetGraphicMapMode(const MapMode& rNewMapMode)
259 OutputDevice& rDevice = mxGraphic->GetOutputDevice();
260 MapMode aMap( rNewMapMode );
261 aMap.SetOrigin( aMap.GetOrigin() + rDevice.PixelToLogic( aPixOffset, aMap ) );
262 rDevice.SetMapMode( aMap );
263 mxGraphic->Invalidate();
266 MapMode SmGraphicWindow::GetGraphicMapMode() const
268 OutputDevice& rDevice = mxGraphic->GetOutputDevice();
269 MapMode aMap(rDevice.GetMapMode());
270 aMap.SetOrigin( aMap.GetOrigin() - rDevice.PixelToLogic( aPixOffset ) );
271 return aMap;
274 void SmGraphicWindow::SetTotalSize( const Size& rNewSize )
276 aTotPixSz = mxGraphic->GetOutputDevice().LogicToPixel(rNewSize);
277 Resize();
280 Size SmGraphicWindow::GetTotalSize() const
282 return mxGraphic->GetOutputDevice().PixelToLogic(aTotPixSz);
285 void SmGraphicWindow::ShowContextMenu(const CommandEvent& rCEvt)
287 GetParent()->ToTop();
288 Point aPos(5, 5);
289 if (rCEvt.IsMouseEvent())
290 aPos = rCEvt.GetMousePosPixel();
292 // added for replaceability of context menus
293 SfxDispatcher::ExecutePopup( this, &aPos );
296 SmGraphicWidget::SmGraphicWidget(SmViewShell& rShell, SmGraphicWindow& rGraphicWindow)
297 : mrGraphicWindow(rGraphicWindow)
298 , bIsCursorVisible(false)
299 , bIsLineVisible(false)
300 , aCaretBlinkTimer("SmGraphicWidget aCaretBlinkTimer")
301 , mrViewShell(rShell)
305 void SmGraphicWidget::SetDrawingArea(weld::DrawingArea* pDrawingArea)
307 weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
309 OutputDevice& rDevice = GetOutputDevice();
311 rDevice.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
313 if (comphelper::LibreOfficeKit::isActive())
315 // Disable map mode, so that it's possible to send mouse event coordinates
316 // directly in twips.
317 rDevice.EnableMapMode(false);
319 else
321 const Fraction aFraction(1, 1);
322 rDevice.SetMapMode(MapMode(SmMapUnit(), Point(), aFraction, aFraction));
325 SetTotalSize();
327 SetHelpId(HID_SMA_WIN_DOCUMENT);
329 ShowLine(false);
330 CaretBlinkInit();
333 SmGraphicWidget::~SmGraphicWidget()
335 if (mxAccessible.is())
336 mxAccessible->ClearWin(); // make Accessible nonfunctional
337 mxAccessible.clear();
338 CaretBlinkStop();
341 SmDocShell* SmGraphicWidget::GetDoc() { return GetView().GetDoc(); }
343 SmCursor& SmGraphicWidget::GetCursor()
345 assert(GetDoc());
346 return GetDoc()->GetCursor();
349 bool SmGraphicWidget::MouseButtonDown(const MouseEvent& rMEvt)
351 GrabFocus();
353 // set formula-cursor and selection of edit window according to the
354 // position clicked at
356 SAL_WARN_IF( rMEvt.GetClicks() == 0, "starmath", "0 clicks" );
357 if ( !rMEvt.IsLeft() )
358 return true;
360 OutputDevice& rDevice = GetOutputDevice();
361 // get click position relative to formula
362 Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
364 const SmNode *pTree = GetDoc()->GetFormulaTree();
365 if (!pTree)
366 return true;
368 if (SmViewShell::IsInlineEditEnabled()) {
369 GetCursor().MoveTo(&rDevice, aPos, !rMEvt.IsShift());
370 // 'on grab' window events are missing in lok, do it explicitly
371 if (comphelper::LibreOfficeKit::isActive())
372 SetIsCursorVisible(true);
373 return true;
375 const SmNode *pNode = nullptr;
376 // if it was clicked inside the formula then get the appropriate node
377 if (pTree->OrientedDist(aPos) <= 0)
378 pNode = pTree->FindRectClosestTo(aPos);
380 if (!pNode)
381 return true;
383 SmEditWindow* pEdit = GetView().GetEditWindow();
384 if (!pEdit)
385 return true;
387 // set selection to the beginning of the token
388 pEdit->SetSelection(pNode->GetSelection());
389 SetCursor(pNode);
391 // allow for immediate editing and
392 //! implicitly synchronize the cursor position mark in this window
393 pEdit->GrabFocus();
395 return true;
398 bool SmGraphicWidget::MouseMove(const MouseEvent &rMEvt)
400 if (rMEvt.IsLeft() && SmViewShell::IsInlineEditEnabled())
402 OutputDevice& rDevice = GetOutputDevice();
403 Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
404 GetCursor().MoveTo(&rDevice, aPos, false);
406 CaretBlinkStop();
407 SetIsCursorVisible(true);
408 CaretBlinkStart();
409 RepaintViewShellDoc();
411 return true;
414 void SmGraphicWidget::GetFocus()
416 if (!SmViewShell::IsInlineEditEnabled())
417 return;
418 if (SmEditWindow* pEdit = GetView().GetEditWindow())
419 pEdit->Flush();
420 //Let view shell know what insertions should be done in visual editor
421 GetView().SetInsertIntoEditWindow(false);
422 SetIsCursorVisible(true);
423 ShowLine(true);
424 CaretBlinkStart();
425 RepaintViewShellDoc();
428 void SmGraphicWidget::LoseFocus()
430 if (mxAccessible.is())
432 uno::Any aOldValue, aNewValue;
433 aOldValue <<= AccessibleStateType::FOCUSED;
434 // aNewValue remains empty
435 mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED,
436 aOldValue, aNewValue );
438 if (!SmViewShell::IsInlineEditEnabled())
439 return;
440 SetIsCursorVisible(false);
441 ShowLine(false);
442 CaretBlinkStop();
443 RepaintViewShellDoc();
446 void SmGraphicWidget::RepaintViewShellDoc()
448 if (SmDocShell* pDoc = GetDoc())
449 pDoc->Repaint();
452 IMPL_LINK_NOARG(SmGraphicWidget, CaretBlinkTimerHdl, Timer *, void)
454 if (IsCursorVisible())
455 SetIsCursorVisible(false);
456 else
457 SetIsCursorVisible(true);
459 RepaintViewShellDoc();
462 void SmGraphicWidget::CaretBlinkInit()
464 if (comphelper::LibreOfficeKit::isActive())
465 return; // No blinking in lok case
466 aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWidget, CaretBlinkTimerHdl));
467 aCaretBlinkTimer.SetTimeout(Application::GetSettings().GetStyleSettings().GetCursorBlinkTime());
470 void SmGraphicWidget::CaretBlinkStart()
472 if (!SmViewShell::IsInlineEditEnabled() || comphelper::LibreOfficeKit::isActive())
473 return;
474 if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME)
475 aCaretBlinkTimer.Start();
478 void SmGraphicWidget::CaretBlinkStop()
480 if (!SmViewShell::IsInlineEditEnabled() || comphelper::LibreOfficeKit::isActive())
481 return;
482 aCaretBlinkTimer.Stop();
485 // shows or hides the formula-cursor depending on 'bShow' is true or not
486 void SmGraphicWidget::ShowCursor(bool bShow)
488 if (SmViewShell::IsInlineEditEnabled())
489 return;
491 bool bInvert = bShow != IsCursorVisible();
492 if (bInvert)
493 InvertFocusRect(GetOutputDevice(), aCursorRect);
495 SetIsCursorVisible(bShow);
498 void SmGraphicWidget::ShowLine(bool bShow)
500 if (!SmViewShell::IsInlineEditEnabled())
501 return;
503 bIsLineVisible = bShow;
506 void SmGraphicWidget::SetIsCursorVisible(bool bVis)
508 bIsCursorVisible = bVis;
509 if (comphelper::LibreOfficeKit::isActive())
511 mrViewShell.SendCaretToLOK();
512 mrViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE,
513 OString::boolean(bVis));
517 void SmGraphicWidget::SetCursor(const SmNode *pNode)
519 if (SmViewShell::IsInlineEditEnabled())
520 return;
522 const SmNode *pTree = GetDoc()->GetFormulaTree();
524 // get appropriate rectangle
525 Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
526 aTLPos (GetFormulaDrawPos() + aOffset);
527 aTLPos.AdjustX( -(pNode->GetItalicLeftSpace()) );
528 Size aSize (pNode->GetItalicSize());
530 SetCursor(tools::Rectangle(aTLPos, aSize));
533 void SmGraphicWidget::SetCursor(const tools::Rectangle &rRect)
534 // sets cursor to new position (rectangle) 'rRect'.
535 // The old cursor will be removed, and the new one will be shown if
536 // that is activated in the ConfigItem
538 if (SmViewShell::IsInlineEditEnabled())
539 return;
541 SmModule *pp = SM_MOD();
543 if (IsCursorVisible())
544 ShowCursor(false); // clean up remainings of old cursor
545 aCursorRect = rRect;
546 if (pp->GetConfig()->IsShowFormulaCursor())
547 ShowCursor(true); // draw new cursor
550 const SmNode * SmGraphicWidget::SetCursorPos(sal_uInt16 nRow, sal_uInt16 nCol)
551 // looks for a VISIBLE node in the formula tree with its token at
552 // (or around) the position 'nRow', 'nCol' in the edit window
553 // (row and column numbering starts with 1 there!).
554 // If there is such a node the formula-cursor is set to cover that nodes
555 // rectangle. If not the formula-cursor will be hidden.
556 // In any case the search result is being returned.
558 if (SmViewShell::IsInlineEditEnabled())
559 return nullptr;
561 // find visible node with token at nRow, nCol
562 const SmNode *pTree = GetDoc()->GetFormulaTree(),
563 *pNode = nullptr;
564 if (pTree)
565 pNode = pTree->FindTokenAt(nRow, nCol);
567 if (pNode)
568 SetCursor(pNode);
569 else
570 ShowCursor(false);
572 return pNode;
575 void SmGraphicWidget::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
577 assert(GetDoc());
578 SmDocShell& rDoc = *GetDoc();
579 Point aPoint;
581 rDoc.DrawFormula(rRenderContext, aPoint, true); //! modifies aPoint to be the topleft
582 //! corner of the formula
583 aFormulaDrawPos = aPoint;
584 if (SmViewShell::IsInlineEditEnabled())
586 //Draw cursor if any...
587 if (rDoc.HasCursor() && IsLineVisible())
588 rDoc.GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible());
590 else
592 SetIsCursorVisible(false); // (old) cursor must be drawn again
594 if (const SmEditWindow* pEdit = GetView().GetEditWindow())
595 { // get new position for formula-cursor (for possible altered formula)
596 sal_Int32 nRow;
597 sal_uInt16 nCol;
598 SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol);
599 const SmNode *pFound = SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
601 SmModule *pp = SM_MOD();
602 if (pFound && pp->GetConfig()->IsShowFormulaCursor())
603 ShowCursor(true);
608 void SmGraphicWidget::SetTotalSize()
610 assert(GetDoc());
611 OutputDevice& rDevice = GetOutputDevice();
612 const Size aTmp(rDevice.PixelToLogic(rDevice.LogicToPixel(GetDoc()->GetSize())));
613 if (aTmp != mrGraphicWindow.GetTotalSize())
614 mrGraphicWindow.SetTotalSize(aTmp);
617 namespace
619 SmBracketType BracketTypeOf(sal_uInt32 c)
621 switch (c)
623 case '(':
624 case ')':
625 return SmBracketType::Round;
626 case '[':
627 case ']':
628 return SmBracketType::Square;
629 case '{':
630 case '}':
631 return SmBracketType::Curly;
633 assert(false); // Unreachable
634 return SmBracketType::Round;
637 bool CharInput(sal_uInt32 c, SmCursor& rCursor, OutputDevice& rDevice)
639 switch (c)
641 case 0:
642 return false;
643 case ' ':
644 rCursor.InsertElement(BlankElement);
645 break;
646 case '!':
647 rCursor.InsertElement(FactorialElement);
648 break;
649 case '%':
650 rCursor.InsertElement(PercentElement);
651 break;
652 case '*':
653 rCursor.InsertElement(CDotElement);
654 break;
655 case '+':
656 rCursor.InsertElement(PlusElement);
657 break;
658 case '-':
659 rCursor.InsertElement(MinusElement);
660 break;
661 case '<':
662 rCursor.InsertElement(LessThanElement);
663 break;
664 case '=':
665 rCursor.InsertElement(EqualElement);
666 break;
667 case '>':
668 rCursor.InsertElement(GreaterThanElement);
669 break;
670 case '^':
671 rCursor.InsertSubSup(RSUP);
672 break;
673 case '_':
674 rCursor.InsertSubSup(RSUB);
675 break;
676 case '/':
677 rCursor.InsertFraction();
678 break;
679 case '(':
680 case '[':
681 case '{':
682 rCursor.InsertBrackets(BracketTypeOf(c));
683 break;
684 case ')':
685 case ']':
686 case '}':
687 if (rCursor.IsAtTailOfBracket(BracketTypeOf(c)))
689 rCursor.Move(&rDevice, MoveRight);
690 break;
692 [[fallthrough]];
693 default:
694 rCursor.InsertText(OUString(&c, 1));
695 break;
697 return true;
701 bool SmGraphicWidget::KeyInput(const KeyEvent& rKEvt)
703 if (!SmViewShell::IsInlineEditEnabled())
704 return GetView().KeyInput(rKEvt);
706 bool bConsumed = true;
708 SmCursor& rCursor = GetCursor();
709 switch (rKEvt.GetKeyCode().GetFunction())
711 case KeyFuncType::COPY:
712 rCursor.Copy();
713 break;
714 case KeyFuncType::CUT:
715 rCursor.Cut();
716 break;
717 case KeyFuncType::PASTE:
718 rCursor.Paste();
719 break;
720 case KeyFuncType::UNDO:
721 GetDoc()->Execute(o3tl::temporary(SfxRequest(*GetView().GetFrame(), SID_UNDO)));
722 break;
723 case KeyFuncType::REDO:
724 GetDoc()->Execute(o3tl::temporary(SfxRequest(*GetView().GetFrame(), SID_REDO)));
725 break;
726 default:
727 switch (rKEvt.GetKeyCode().GetCode())
729 case KEY_LEFT:
730 rCursor.Move(&GetOutputDevice(), MoveLeft, !rKEvt.GetKeyCode().IsShift());
731 break;
732 case KEY_RIGHT:
733 rCursor.Move(&GetOutputDevice(), MoveRight, !rKEvt.GetKeyCode().IsShift());
734 break;
735 case KEY_UP:
736 rCursor.Move(&GetOutputDevice(), MoveUp, !rKEvt.GetKeyCode().IsShift());
737 break;
738 case KEY_DOWN:
739 rCursor.Move(&GetOutputDevice(), MoveDown, !rKEvt.GetKeyCode().IsShift());
740 break;
741 case KEY_RETURN:
742 if (!rKEvt.GetKeyCode().IsShift())
743 rCursor.InsertRow();
744 break;
745 case KEY_DELETE:
746 if (!rCursor.HasSelection())
748 rCursor.Move(&GetOutputDevice(), MoveRight, false);
749 if (rCursor.HasComplexSelection())
750 break;
752 rCursor.Delete();
753 break;
754 case KEY_BACKSPACE:
755 rCursor.DeletePrev(&GetOutputDevice());
756 break;
757 default:
758 if (!CharInput(rKEvt.GetCharCode(), rCursor, GetOutputDevice()))
759 bConsumed = GetView().KeyInput(rKEvt);
762 CaretBlinkStop();
763 CaretBlinkStart();
764 SetIsCursorVisible(true);
765 RepaintViewShellDoc();
767 return bConsumed;
770 bool SmGraphicWidget::Command(const CommandEvent& rCEvt)
772 bool bCallBase = true;
773 if (!GetView().GetViewFrame().GetFrame().IsInPlace())
775 switch ( rCEvt.GetCommand() )
777 case CommandEventId::ContextMenu:
778 // purely for "ExecutePopup" taking a vcl::Window and
779 // we assume SmGraphicWindow 0,0 is at SmEditWindow 0,0
780 mrGraphicWindow.ShowContextMenu(rCEvt);
781 bCallBase = false;
782 break;
784 case CommandEventId::Wheel:
786 const CommandWheelData* pWData = rCEvt.GetWheelData();
787 if ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() )
789 sal_uInt16 nTmpZoom = mrGraphicWindow.GetZoom();
790 if( 0 > pWData->GetDelta() )
791 nTmpZoom -= 10;
792 else
793 nTmpZoom += 10;
794 mrGraphicWindow.SetZoom(nTmpZoom);
795 bCallBase = false;
797 break;
799 case CommandEventId::GestureZoom:
801 const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData();
802 if (pData)
804 if (pData->meEventType == GestureEventZoomType::Begin)
806 mfLastZoomScale = pData->mfScaleDelta;
808 else if (pData->meEventType == GestureEventZoomType::Update)
810 double deltaBetweenEvents = (pData->mfScaleDelta - mfLastZoomScale) / mfLastZoomScale;
811 mfLastZoomScale = pData->mfScaleDelta;
813 // Accumulate fractional zoom to avoid small zoom changes from being ignored
814 mfAccumulatedZoom += deltaBetweenEvents;
815 int nZoomChangePercent = mfAccumulatedZoom * 100;
816 mfAccumulatedZoom -= nZoomChangePercent / 100.0;
818 sal_uInt16 nZoom = mrGraphicWindow.GetZoom();
819 nZoom += nZoomChangePercent;
820 mrGraphicWindow.SetZoom(nZoom);
822 bCallBase = false;
824 break;
827 default: break;
830 else
832 switch (rCEvt.GetCommand())
834 case CommandEventId::ExtTextInput:
835 if (comphelper::LibreOfficeKit::isActive())
837 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
838 assert(pData);
839 const OUString& rText = pData->GetText();
840 SmCursor& rCursor = GetCursor();
841 OutputDevice& rDevice = GetOutputDevice();
842 for (sal_Int32 i = 0; i < rText.getLength();)
843 CharInput(rText.iterateCodePoints(&i), rCursor, rDevice);
844 bCallBase = false;
846 break;
847 default:
848 break;
851 return !bCallBase;
854 void SmGraphicWindow::SetZoom(sal_uInt16 Factor)
856 if (comphelper::LibreOfficeKit::isActive())
857 return;
858 nZoom = std::clamp(Factor, MINZOOM, MAXZOOM);
859 Fraction aFraction(nZoom, 100);
860 SetGraphicMapMode(MapMode(SmMapUnit(), Point(), aFraction, aFraction));
861 mxGraphic->SetTotalSize();
862 SmViewShell& rViewSh = mxGraphic->GetView();
863 rViewSh.GetViewFrame().GetBindings().Invalidate(SID_ATTR_ZOOM);
864 rViewSh.GetViewFrame().GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER);
867 void SmGraphicWindow::ZoomToFitInWindow()
869 // set defined mapmode before calling 'LogicToPixel' below
870 SetGraphicMapMode(MapMode(SmMapUnit()));
872 assert(mxGraphic->GetDoc());
873 Size aSize(mxGraphic->GetOutputDevice().LogicToPixel(mxGraphic->GetDoc()->GetSize()));
874 Size aWindowSize(GetSizePixel());
876 if (!aSize.IsEmpty())
878 tools::Long nVal = std::min ((85 * aWindowSize.Width()) / aSize.Width(),
879 (85 * aWindowSize.Height()) / aSize.Height());
880 SetZoom ( sal::static_int_cast< sal_uInt16 >(nVal) );
884 uno::Reference< XAccessible > SmGraphicWidget::CreateAccessible()
886 if (!mxAccessible.is())
888 mxAccessible = new SmGraphicAccessible( this );
890 return mxAccessible;
893 /**************************************************************************/
894 SmGraphicController::SmGraphicController(SmGraphicWidget &rSmGraphic,
895 sal_uInt16 nId_,
896 SfxBindings &rBindings) :
897 SfxControllerItem(nId_, rBindings),
898 rGraphic(rSmGraphic)
902 void SmGraphicController::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
904 rGraphic.SetTotalSize();
905 rGraphic.Invalidate();
906 SfxControllerItem::StateChangedAtToolBoxControl (nSID, eState, pState);
909 /**************************************************************************/
910 SmEditController::SmEditController(SmEditWindow &rSmEdit,
911 sal_uInt16 nId_,
912 SfxBindings &rBindings) :
913 SfxControllerItem(nId_, rBindings),
914 rEdit(rSmEdit)
918 void SmEditController::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
920 const SfxStringItem *pItem = dynamic_cast<const SfxStringItem*>( pState);
922 if ((pItem != nullptr) && (rEdit.GetText() != pItem->GetValue()))
923 rEdit.SetText(pItem->GetValue());
924 SfxControllerItem::StateChangedAtToolBoxControl (nSID, eState, pState);
927 /**************************************************************************/
928 SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow,
929 vcl::Window *pParent)
930 : SfxDockingWindow(pBindings_, pChildWindow, pParent, "EditWindow", "modules/smath/ui/editwindow.ui")
931 , m_xEdit(new SmEditWindow(*this, *m_xBuilder))
932 , aController(*m_xEdit, SID_TEXT, *pBindings_)
933 , bExiting(false)
934 , aInitialFocusTimer("SmCmdBoxWindow aInitialFocusTimer")
936 set_id("math_edit");
938 SetHelpId( HID_SMA_COMMAND_WIN );
939 SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont)));
940 SetText(SmResId(STR_CMDBOXWINDOW));
942 Hide();
944 // Don't try to grab focus in LOK inline edit mode
945 if (!comphelper::LibreOfficeKit::isActive())
947 aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl));
948 aInitialFocusTimer.SetTimeout(100);
952 Point SmCmdBoxWindow::WidgetToWindowPos(const weld::Widget& rWidget, const Point& rPos)
954 Point aRet(rPos);
955 int x(0), y(0), width(0), height(0);
956 rWidget.get_extents_relative_to(*m_xContainer, x, y, width, height);
957 aRet.Move(x, y);
958 aRet.Move(m_xBox->GetPosPixel().X(), m_xBox->GetPosPixel().Y());
959 return aRet;
962 void SmCmdBoxWindow::ShowContextMenu(const Point& rPos)
964 ToTop();
965 SmViewShell *pViewSh = GetView();
966 if (pViewSh)
967 pViewSh->GetViewFrame().GetDispatcher()->ExecutePopup("edit", this, &rPos);
970 void SmCmdBoxWindow::Command(const CommandEvent& rCEvt)
972 if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
974 ShowContextMenu(rCEvt.GetMousePosPixel());
975 return;
978 SfxDockingWindow::Command(rCEvt);
981 SmCmdBoxWindow::~SmCmdBoxWindow ()
983 disposeOnce();
986 void SmCmdBoxWindow::dispose()
988 aInitialFocusTimer.Stop();
989 bExiting = true;
990 aController.dispose();
991 m_xEdit.reset();
992 SfxDockingWindow::dispose();
995 SmViewShell * SmCmdBoxWindow::GetView()
997 SfxDispatcher *pDispatcher = GetBindings().GetDispatcher();
998 SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr;
999 return dynamic_cast<SmViewShell*>( pView);
1002 Size SmCmdBoxWindow::CalcDockingSize(SfxChildAlignment eAlign)
1004 switch (eAlign)
1006 case SfxChildAlignment::LEFT:
1007 case SfxChildAlignment::RIGHT:
1008 return Size();
1009 default:
1010 break;
1012 return SfxDockingWindow::CalcDockingSize(eAlign);
1015 SfxChildAlignment SmCmdBoxWindow::CheckAlignment(SfxChildAlignment eActual,
1016 SfxChildAlignment eWish)
1018 switch (eWish)
1020 case SfxChildAlignment::TOP:
1021 case SfxChildAlignment::BOTTOM:
1022 case SfxChildAlignment::NOALIGNMENT:
1023 return eWish;
1024 default:
1025 break;
1028 return eActual;
1031 void SmCmdBoxWindow::StateChanged( StateChangedType nStateChange )
1033 if (StateChangedType::InitShow == nStateChange)
1035 Resize(); // avoid SmEditWindow not being painted correctly
1037 // set initial position of window in floating mode
1038 if (IsFloatingMode())
1039 AdjustPosition(); //! don't change pos in docking-mode !
1041 aInitialFocusTimer.Start();
1044 SfxDockingWindow::StateChanged( nStateChange );
1047 IMPL_LINK_NOARG( SmCmdBoxWindow, InitialFocusTimerHdl, Timer *, void )
1049 // We want to have the focus in the edit window once Math has been opened
1050 // to allow for immediate typing.
1051 // Problem: There is no proper way to do this
1052 // Thus: this timer based solution has been implemented (see GrabFocus below)
1054 // Follow-up problem (#i114910): grabbing the focus may bust the help system since
1055 // it relies on getting the current frame which conflicts with grabbing the focus.
1056 // Thus aside from the 'GrabFocus' call everything else is to get the
1057 // help reliably working despite using 'GrabFocus'.
1061 uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( comphelper::getProcessComponentContext() );
1063 m_xEdit->GrabFocus();
1065 SmViewShell* pView = GetView();
1066 assert(pView);
1067 bool bInPlace = pView->GetViewFrame().GetFrame().IsInPlace();
1068 uno::Reference< frame::XFrame > xFrame( GetBindings().GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface());
1069 if ( bInPlace )
1071 uno::Reference<container::XChild> xModel(pView->GetDoc()->GetModel(),
1072 uno::UNO_QUERY_THROW);
1073 uno::Reference< frame::XModel > xParent( xModel->getParent(), uno::UNO_QUERY_THROW );
1074 uno::Reference< frame::XController > xParentCtrler( xParent->getCurrentController() );
1075 uno::Reference< frame::XFramesSupplier > xParentFrame( xParentCtrler->getFrame(), uno::UNO_QUERY_THROW );
1076 xParentFrame->setActiveFrame( xFrame );
1078 else
1080 xDesktop->setActiveFrame( xFrame );
1083 catch (uno::Exception &)
1085 SAL_WARN( "starmath", "failed to properly set initial focus to edit window" );
1089 void SmCmdBoxWindow::AdjustPosition()
1091 const tools::Rectangle aRect( Point(), GetParent()->GetOutputSizePixel() );
1092 Point aTopLeft( Point( aRect.Left(),
1093 aRect.Bottom() - GetSizePixel().Height() ) );
1094 Point aPos( GetParent()->OutputToScreenPixel( aTopLeft ) );
1095 if (aPos.X() < 0)
1096 aPos.setX( 0 );
1097 if (aPos.Y() < 0)
1098 aPos.setY( 0 );
1099 SetPosPixel( aPos );
1102 void SmCmdBoxWindow::ToggleFloatingMode()
1104 SfxDockingWindow::ToggleFloatingMode();
1106 if (GetFloatingWindow())
1107 GetFloatingWindow()->SetMinOutputSizePixel(Size (200, 50));
1110 void SmCmdBoxWindow::GetFocus()
1112 if (!bExiting)
1113 m_xEdit->GrabFocus();
1116 SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW);
1118 SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId,
1119 SfxBindings *pBindings,
1120 SfxChildWinInfo *pInfo) :
1121 SfxChildWindow(pParentWindow, nId)
1123 VclPtrInstance<SmCmdBoxWindow> pDialog(pBindings, this, pParentWindow);
1124 SetWindow(pDialog);
1125 // make window docked to the bottom initially (after first start)
1126 SetAlignment(SfxChildAlignment::BOTTOM);
1127 pDialog->setDeferredProperties();
1128 pDialog->set_border_width(CMD_BOX_PADDING);
1129 pDialog->set_margin_top(CMD_BOX_PADDING_TOP);
1130 pDialog->Initialize(pInfo);
1133 SFX_IMPL_SUPERCLASS_INTERFACE(SmViewShell, SfxViewShell)
1135 void SmViewShell::InitInterface_Impl()
1137 GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
1138 SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
1139 ToolbarId::Math_Toolbox);
1140 //Dummy-Objectbar, to avoid quiver while activating
1142 GetStaticInterface()->RegisterChildWindow(SmCmdBoxWrapper::GetChildWindowId());
1143 GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
1145 GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
1148 SFX_IMPL_NAMED_VIEWFACTORY(SmViewShell, "Default")
1150 SFX_VIEW_REGISTRATION(SmDocShell);
1153 void SmViewShell::InnerResizePixel(const Point &rOfs, const Size &rSize, bool)
1155 Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
1156 if ( !aObjSize.IsEmpty() )
1158 Size aProvidedSize = GetWindow()->PixelToLogic(rSize, MapMode(SmMapUnit()));
1159 Fraction aZoomX(aProvidedSize.Width(), aObjSize.Width());
1160 Fraction aZoomY(aProvidedSize.Height(), aObjSize.Height());
1161 MapMode aMap(mxGraphicWindow->GetGraphicMapMode());
1162 aMap.SetScaleX(aZoomX);
1163 aMap.SetScaleY(aZoomY);
1164 mxGraphicWindow->SetGraphicMapMode(aMap);
1167 SetBorderPixel( SvBorder() );
1168 mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
1169 GetGraphicWidget().SetTotalSize();
1172 void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize)
1174 mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
1175 if (GetDoc()->IsPreview())
1176 mxGraphicWindow->ZoomToFitInWindow();
1179 void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
1181 rRect.SetSize(mxGraphicWindow->GetSizePixel());
1184 void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY )
1186 const Fraction &rFrac = std::min(rX, rY);
1187 mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(tools::Long(rFrac * Fraction( 100, 1 ))));
1189 //To avoid rounding errors base class regulates crooked values too
1190 //if necessary
1191 SfxViewShell::SetZoomFactor( rX, rY );
1194 Size SmViewShell::GetTextLineSize(OutputDevice const & rDevice, const OUString& rLine)
1196 Size aSize(rDevice.GetTextWidth(rLine), rDevice.GetTextHeight());
1197 const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
1199 if (nTabPos)
1201 aSize.setWidth( 0 );
1202 sal_Int32 nPos = 0;
1205 if (nPos > 0)
1206 aSize.setWidth( ((aSize.Width() / nTabPos) + 1) * nTabPos );
1208 const OUString aText = rLine.getToken(0, '\t', nPos);
1209 aSize.AdjustWidth(rDevice.GetTextWidth(aText) );
1211 while (nPos >= 0);
1214 return aSize;
1217 Size SmViewShell::GetTextSize(OutputDevice const & rDevice, std::u16string_view rText, tools::Long MaxWidth)
1219 Size aSize;
1220 Size aTextSize;
1221 if (rText.empty())
1222 return aTextSize;
1224 sal_Int32 nPos = 0;
1227 OUString aLine( o3tl::getToken(rText, 0, '\n', nPos) );
1228 aLine = aLine.replaceAll("\r", "");
1230 aSize = GetTextLineSize(rDevice, aLine);
1232 if (aSize.Width() > MaxWidth)
1236 OUString aText;
1237 sal_Int32 m = aLine.getLength();
1238 sal_Int32 nLen = m;
1240 for (sal_Int32 n = 0; n < nLen; n++)
1242 sal_Unicode cLineChar = aLine[n];
1243 if ((cLineChar == ' ') || (cLineChar == '\t'))
1245 aText = aLine.copy(0, n);
1246 if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
1247 m = n;
1248 else
1249 break;
1253 aText = aLine.copy(0, m);
1254 aLine = aLine.replaceAt(0, m, u"");
1255 aSize = GetTextLineSize(rDevice, aText);
1256 aTextSize.AdjustHeight(aSize.Height() );
1257 aTextSize.setWidth( std::clamp(aSize.Width(), aTextSize.Width(), MaxWidth) );
1259 aLine = comphelper::string::stripStart(aLine, ' ');
1260 aLine = comphelper::string::stripStart(aLine, '\t');
1261 aLine = comphelper::string::stripStart(aLine, ' ');
1263 while (!aLine.isEmpty());
1265 else
1267 aTextSize.AdjustHeight(aSize.Height() );
1268 aTextSize.setWidth( std::max(aTextSize.Width(), aSize.Width()) );
1271 while (nPos >= 0);
1273 return aTextSize;
1276 void SmViewShell::DrawTextLine(OutputDevice& rDevice, const Point& rPosition, const OUString& rLine)
1278 Point aPoint(rPosition);
1279 const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
1281 if (nTabPos)
1283 sal_Int32 nPos = 0;
1286 if (nPos > 0)
1287 aPoint.setX( ((aPoint.X() / nTabPos) + 1) * nTabPos );
1289 OUString aText = rLine.getToken(0, '\t', nPos);
1290 rDevice.DrawText(aPoint, aText);
1291 aPoint.AdjustX(rDevice.GetTextWidth(aText) );
1293 while ( nPos >= 0 );
1295 else
1296 rDevice.DrawText(aPoint, rLine);
1299 void SmViewShell::DrawText(OutputDevice& rDevice, const Point& rPosition, std::u16string_view rText, sal_uInt16 MaxWidth)
1301 if (rText.empty())
1302 return;
1304 Point aPoint(rPosition);
1305 Size aSize;
1307 sal_Int32 nPos = 0;
1310 OUString aLine( o3tl::getToken(rText, 0, '\n', nPos) );
1311 aLine = aLine.replaceAll("\r", "");
1312 aSize = GetTextLineSize(rDevice, aLine);
1313 if (aSize.Width() > MaxWidth)
1317 OUString aText;
1318 sal_Int32 m = aLine.getLength();
1319 sal_Int32 nLen = m;
1321 for (sal_Int32 n = 0; n < nLen; n++)
1323 sal_Unicode cLineChar = aLine[n];
1324 if ((cLineChar == ' ') || (cLineChar == '\t'))
1326 aText = aLine.copy(0, n);
1327 if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
1328 m = n;
1329 else
1330 break;
1333 aText = aLine.copy(0, m);
1334 aLine = aLine.replaceAt(0, m, u"");
1336 DrawTextLine(rDevice, aPoint, aText);
1337 aPoint.AdjustY(aSize.Height() );
1339 aLine = comphelper::string::stripStart(aLine, ' ');
1340 aLine = comphelper::string::stripStart(aLine, '\t');
1341 aLine = comphelper::string::stripStart(aLine, ' ');
1343 while (GetTextLineSize(rDevice, aLine).Width() > MaxWidth);
1345 // print the remaining text
1346 if (!aLine.isEmpty())
1348 DrawTextLine(rDevice, aPoint, aLine);
1349 aPoint.AdjustY(aSize.Height() );
1352 else
1354 DrawTextLine(rDevice, aPoint, aLine);
1355 aPoint.AdjustY(aSize.Height() );
1358 while ( nPos >= 0 );
1361 void SmViewShell::Impl_Print(OutputDevice &rOutDev, const SmPrintUIOptions &rPrintUIOptions, tools::Rectangle aOutRect )
1363 const bool bIsPrintTitle = rPrintUIOptions.getBoolValue( PRTUIOPT_TITLE_ROW, true );
1364 const bool bIsPrintFrame = rPrintUIOptions.getBoolValue( PRTUIOPT_BORDER, true );
1365 const bool bIsPrintFormulaText = rPrintUIOptions.getBoolValue( PRTUIOPT_FORMULA_TEXT, true );
1366 SmPrintSize ePrintSize( static_cast< SmPrintSize >( rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL ) ));
1367 const sal_uInt16 nZoomFactor = static_cast< sal_uInt16 >(rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_SCALE, 100 ));
1369 rOutDev.Push();
1370 rOutDev.SetLineColor( COL_BLACK );
1372 // output text on top
1373 if (bIsPrintTitle)
1375 Size aSize600 (0, 600);
1376 Size aSize650 (0, 650);
1377 vcl::Font aFont(FAMILY_DONTKNOW, aSize600);
1379 aFont.SetAlignment(ALIGN_TOP);
1380 aFont.SetWeight(WEIGHT_BOLD);
1381 aFont.SetFontSize(aSize650);
1382 aFont.SetColor( COL_BLACK );
1383 rOutDev.SetFont(aFont);
1385 Size aTitleSize (GetTextSize(rOutDev, GetDoc()->GetTitle(), aOutRect.GetWidth() - 200));
1387 aFont.SetWeight(WEIGHT_NORMAL);
1388 aFont.SetFontSize(aSize600);
1389 rOutDev.SetFont(aFont);
1391 Size aDescSize (GetTextSize(rOutDev, GetDoc()->GetComment(), aOutRect.GetWidth() - 200));
1393 if (bIsPrintFrame)
1394 rOutDev.DrawRect(tools::Rectangle(aOutRect.TopLeft(),
1395 Size(aOutRect.GetWidth(), 100 + aTitleSize.Height() + 200 + aDescSize.Height() + 100)));
1396 aOutRect.AdjustTop(200 );
1398 // output title
1399 aFont.SetWeight(WEIGHT_BOLD);
1400 aFont.SetFontSize(aSize650);
1401 rOutDev.SetFont(aFont);
1402 Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aTitleSize.Width()) / 2,
1403 aOutRect.Top());
1404 DrawText(rOutDev, aPoint, GetDoc()->GetTitle(),
1405 sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1406 aOutRect.AdjustTop(aTitleSize.Height() + 200 );
1408 // output description
1409 aFont.SetWeight(WEIGHT_NORMAL);
1410 aFont.SetFontSize(aSize600);
1411 rOutDev.SetFont(aFont);
1412 aPoint.setX( aOutRect.Left() + (aOutRect.GetWidth() - aDescSize.Width()) / 2 );
1413 aPoint.setY( aOutRect.Top() );
1414 DrawText(rOutDev, aPoint, GetDoc()->GetComment(),
1415 sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1416 aOutRect.AdjustTop(aDescSize.Height() + 300 );
1419 // output text on bottom
1420 if (bIsPrintFormulaText)
1422 vcl::Font aFont(FAMILY_DONTKNOW, Size(0, 600));
1423 aFont.SetAlignment(ALIGN_TOP);
1424 aFont.SetColor( COL_BLACK );
1426 // get size
1427 rOutDev.SetFont(aFont);
1429 Size aSize (GetTextSize(rOutDev, GetDoc()->GetText(), aOutRect.GetWidth() - 200));
1431 aOutRect.AdjustBottom( -(aSize.Height() + 600) );
1433 if (bIsPrintFrame)
1434 rOutDev.DrawRect(tools::Rectangle(aOutRect.BottomLeft(),
1435 Size(aOutRect.GetWidth(), 200 + aSize.Height() + 200)));
1437 Point aPoint (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
1438 aOutRect.Bottom() + 300);
1439 DrawText(rOutDev, aPoint, GetDoc()->GetText(),
1440 sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
1441 aOutRect.AdjustBottom( -200 );
1444 if (bIsPrintFrame)
1445 rOutDev.DrawRect(aOutRect);
1447 aOutRect.AdjustTop(100 );
1448 aOutRect.AdjustLeft(100 );
1449 aOutRect.AdjustBottom( -100 );
1450 aOutRect.AdjustRight( -100 );
1452 Size aSize (GetDoc()->GetSize());
1454 MapMode OutputMapMode;
1455 // PDF export should always use PRINT_SIZE_NORMAL ...
1456 if (!rPrintUIOptions.getBoolValue( "IsPrinter" ) )
1457 ePrintSize = PRINT_SIZE_NORMAL;
1458 switch (ePrintSize)
1460 case PRINT_SIZE_NORMAL:
1461 OutputMapMode = MapMode(SmMapUnit());
1462 break;
1464 case PRINT_SIZE_SCALED:
1465 if (!aSize.IsEmpty())
1467 Size OutputSize(rOutDev.LogicToPixel(aOutRect.GetSize(), MapMode(SmMapUnit())));
1468 Size GraphicSize(rOutDev.LogicToPixel(aSize, MapMode(SmMapUnit())));
1469 sal_uInt16 nZ = std::min(o3tl::convert(OutputSize.Width(), 100, GraphicSize.Width()),
1470 o3tl::convert(OutputSize.Height(), 100, GraphicSize.Height()));
1471 nZ -= 10;
1472 Fraction aFraction(std::clamp(nZ, MINZOOM, MAXZOOM), 100);
1474 OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction);
1476 else
1477 OutputMapMode = MapMode(SmMapUnit());
1478 break;
1480 case PRINT_SIZE_ZOOMED:
1482 Fraction aFraction( nZoomFactor, 100 );
1484 OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction);
1485 break;
1489 aSize = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aSize, OutputMapMode),
1490 MapMode(SmMapUnit()));
1492 Point aPos (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
1493 aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2);
1495 aPos = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aPos, MapMode(SmMapUnit())),
1496 OutputMapMode);
1497 aOutRect = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aOutRect, MapMode(SmMapUnit())),
1498 OutputMapMode);
1500 rOutDev.SetMapMode(OutputMapMode);
1501 rOutDev.SetClipRegion(vcl::Region(aOutRect));
1502 GetDoc()->DrawFormula(rOutDev, aPos);
1503 rOutDev.SetClipRegion();
1505 rOutDev.Pop();
1508 SfxPrinter* SmViewShell::GetPrinter(bool bCreate)
1510 SmDocShell* pDoc = GetDoc();
1511 if (pDoc->HasPrinter() || bCreate)
1512 return pDoc->GetPrinter();
1513 return nullptr;
1516 sal_uInt16 SmViewShell::SetPrinter(SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
1518 SfxPrinter *pOld = GetDoc()->GetPrinter();
1519 if ( pOld && pOld->IsPrinting() )
1520 return SFX_PRINTERROR_BUSY;
1522 if ((nDiffFlags & SfxPrinterChangeFlags::PRINTER) == SfxPrinterChangeFlags::PRINTER)
1523 GetDoc()->SetPrinter( pNewPrinter );
1525 if ((nDiffFlags & SfxPrinterChangeFlags::OPTIONS) == SfxPrinterChangeFlags::OPTIONS)
1527 SmModule *pp = SM_MOD();
1528 pp->GetConfig()->ItemSetToConfig(pNewPrinter->GetOptions());
1530 return 0;
1533 bool SmViewShell::HasPrintOptionsPage() const
1535 return true;
1538 std::unique_ptr<SfxTabPage> SmViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController,
1539 const SfxItemSet &rOptions)
1541 return SmPrintOptionsTabPage::Create(pPage, pController, rOptions);
1544 SmEditWindow *SmViewShell::GetEditWindow()
1546 SmCmdBoxWrapper* pWrapper = static_cast<SmCmdBoxWrapper*>(
1547 GetViewFrame().GetChildWindow(SmCmdBoxWrapper::GetChildWindowId()));
1549 if (pWrapper != nullptr)
1551 SmEditWindow& rEditWin = pWrapper->GetEditWindow();
1552 return &rEditWin;
1555 return nullptr;
1558 void SmViewShell::SetStatusText(const OUString& rText)
1560 maStatusText = rText;
1561 GetViewFrame().GetBindings().Invalidate(SID_TEXTSTATUS);
1564 void SmViewShell::ShowError(const SmErrorDesc* pErrorDesc)
1566 assert(GetDoc());
1567 if (pErrorDesc || nullptr != (pErrorDesc = GetDoc()->GetParser()->GetError()) )
1569 SetStatusText( pErrorDesc->m_aText );
1570 if (SmEditWindow* pEdit = GetEditWindow())
1571 pEdit->MarkError( Point( pErrorDesc->m_pNode->GetColumn(),
1572 pErrorDesc->m_pNode->GetRow()));
1576 void SmViewShell::NextError()
1578 assert(GetDoc());
1579 const SmErrorDesc *pErrorDesc = GetDoc()->GetParser()->NextError();
1581 if (pErrorDesc)
1582 ShowError( pErrorDesc );
1585 void SmViewShell::PrevError()
1587 assert(GetDoc());
1588 const SmErrorDesc *pErrorDesc = GetDoc()->GetParser()->PrevError();
1590 if (pErrorDesc)
1591 ShowError( pErrorDesc );
1594 void SmViewShell::Insert( SfxMedium& rMedium )
1596 SmDocShell *pDoc = GetDoc();
1597 bool bRet = false;
1599 uno::Reference <embed::XStorage> xStorage = rMedium.GetStorage();
1600 if (xStorage.is() && xStorage->getElementNames().hasElements())
1602 if (xStorage->hasByName("content.xml"))
1604 // is this a fabulous math package ?
1605 rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(pDoc->GetModel().get()));
1606 SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
1607 bRet = ERRCODE_NONE == aEquation.Import(rMedium);
1611 if (!bRet)
1612 return;
1614 OUString aText = pDoc->GetText();
1615 if (SmEditWindow *pEditWin = GetEditWindow())
1616 pEditWin->InsertText( aText );
1617 else
1619 SAL_WARN( "starmath", "EditWindow missing" );
1622 pDoc->Parse();
1623 pDoc->SetModified();
1625 SfxBindings &rBnd = GetViewFrame().GetBindings();
1626 rBnd.Invalidate(SID_GRAPHIC_SM);
1627 rBnd.Invalidate(SID_TEXT);
1630 void SmViewShell::InsertFrom(SfxMedium &rMedium)
1632 bool bSuccess = false;
1633 SmDocShell* pDoc = GetDoc();
1634 SvStream* pStream = rMedium.GetInStream();
1636 if (pStream)
1638 const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
1639 if ( rFltName == MATHML_XML )
1641 rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(pDoc->GetModel().get()));
1642 SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
1643 bSuccess = ERRCODE_NONE == aEquation.Import(rMedium);
1647 if (!bSuccess)
1648 return;
1650 OUString aText = pDoc->GetText();
1651 if (SmEditWindow *pEditWin = GetEditWindow())
1652 pEditWin->InsertText(aText);
1653 else
1654 SAL_WARN( "starmath", "EditWindow missing" );
1656 pDoc->Parse();
1657 pDoc->SetModified();
1659 SfxBindings& rBnd = GetViewFrame().GetBindings();
1660 rBnd.Invalidate(SID_GRAPHIC_SM);
1661 rBnd.Invalidate(SID_TEXT);
1664 void SmViewShell::Execute(SfxRequest& rReq)
1666 SmEditWindow *pWin = GetEditWindow();
1668 switch (rReq.GetSlot())
1670 case SID_FORMULACURSOR:
1672 SmModule *pp = SM_MOD();
1674 const SfxItemSet *pArgs = rReq.GetArgs();
1675 const SfxPoolItem *pItem;
1677 bool bVal;
1678 if ( pArgs &&
1679 SfxItemState::SET == pArgs->GetItemState( SID_FORMULACURSOR, false, &pItem))
1680 bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
1681 else
1682 bVal = !pp->GetConfig()->IsShowFormulaCursor();
1684 pp->GetConfig()->SetShowFormulaCursor(bVal);
1685 if (!IsInlineEditEnabled())
1686 GetGraphicWidget().ShowCursor(bVal);
1687 break;
1689 case SID_DRAW:
1690 if (pWin)
1692 GetDoc()->SetText( pWin->GetText() );
1693 SetStatusText(OUString());
1694 ShowError( nullptr );
1695 GetDoc()->Repaint();
1697 break;
1699 case SID_ZOOM_OPTIMAL:
1700 mxGraphicWindow->ZoomToFitInWindow();
1701 break;
1703 case SID_ZOOMIN:
1704 mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() + 25);
1705 break;
1707 case SID_ZOOMOUT:
1708 SAL_WARN_IF( mxGraphicWindow->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" );
1709 mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() - 25);
1710 break;
1712 case SID_COPYOBJECT:
1714 //TODO/LATER: does not work because of UNO Tunneling - will be fixed later
1715 Reference< datatransfer::XTransferable > xTrans( GetDoc()->GetModel(), uno::UNO_QUERY );
1716 if( xTrans.is() )
1718 auto pTrans = dynamic_cast<TransferableHelper*>(xTrans.get());
1719 if (pTrans)
1721 if (pWin)
1722 pTrans->CopyToClipboard(pWin->GetClipboard());
1726 break;
1728 case SID_PASTEOBJECT:
1730 uno::Reference < io::XInputStream > xStrm;
1731 if (pWin)
1733 TransferableDataHelper aData(TransferableDataHelper::CreateFromClipboard(pWin->GetClipboard()));
1734 SotClipboardFormatId nId;
1735 if( aData.GetTransferable().is() &&
1736 ( aData.HasFormat( nId = SotClipboardFormatId::EMBEDDED_OBJ ) ||
1737 (aData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) &&
1738 aData.HasFormat( nId = SotClipboardFormatId::EMBED_SOURCE ))))
1739 xStrm = aData.GetInputStream(nId, OUString());
1742 if (xStrm.is())
1746 uno::Reference < embed::XStorage > xStorage =
1747 ::comphelper::OStorageHelper::GetStorageFromInputStream( xStrm, ::comphelper::getProcessComponentContext() );
1748 SfxMedium aMedium( xStorage, OUString() );
1749 Insert( aMedium );
1750 GetDoc()->UpdateText();
1752 catch (uno::Exception &)
1754 SAL_WARN( "starmath", "SmViewShell::Execute (SID_PASTEOBJECT): failed to get storage from input stream" );
1758 break;
1761 case SID_CUT:
1762 if (pWin)
1763 pWin->Cut();
1764 break;
1766 case SID_COPY:
1767 if (pWin)
1769 if (pWin->IsAllSelected())
1771 GetViewFrame().GetDispatcher()->ExecuteList(
1772 SID_COPYOBJECT, SfxCallMode::RECORD,
1773 { new SfxVoidItem(SID_COPYOBJECT) });
1775 else
1776 pWin->Copy();
1778 break;
1780 case SID_PASTE:
1782 bool bCallExec = nullptr == pWin;
1783 if( !bCallExec )
1785 if (pWin)
1787 TransferableDataHelper aDataHelper(
1788 TransferableDataHelper::CreateFromClipboard(
1789 pWin->GetClipboard()));
1791 if( aDataHelper.GetTransferable().is() &&
1792 aDataHelper.HasFormat( SotClipboardFormatId::STRING ))
1793 pWin->Paste();
1794 else
1795 bCallExec = true;
1798 if( bCallExec )
1800 GetViewFrame().GetDispatcher()->ExecuteList(
1801 SID_PASTEOBJECT, SfxCallMode::RECORD,
1802 { new SfxVoidItem(SID_PASTEOBJECT) });
1805 break;
1807 case SID_DELETE:
1808 if (pWin)
1809 pWin->Delete();
1810 break;
1812 case SID_SELECT:
1813 if (pWin)
1814 pWin->SelectAll();
1815 break;
1817 case SID_INSERTCOMMANDTEXT:
1819 const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT);
1821 if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
1823 pWin->InsertText(rItem.GetValue());
1825 if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
1827 GetDoc()->GetCursor().InsertCommandText(rItem.GetValue());
1828 GetGraphicWidget().GrabFocus();
1830 break;
1834 case SID_INSERTSPECIAL:
1836 const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_INSERTSPECIAL);
1838 if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
1839 pWin->InsertText(rItem.GetValue());
1840 if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
1841 GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
1842 break;
1845 case SID_IMPORT_FORMULA:
1847 mpRequest.reset(new SfxRequest( rReq ));
1848 mpDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr,
1849 GetDoc()->GetFactory().GetFactoryName()));
1850 mpDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) );
1851 break;
1854 case SID_IMPORT_MATHML_CLIPBOARD:
1856 if (pWin)
1858 TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pWin->GetClipboard()));
1859 uno::Reference < io::XInputStream > xStrm;
1860 if ( aDataHelper.GetTransferable().is() )
1862 SotClipboardFormatId nId = SotClipboardFormatId::MATHML;
1863 if (aDataHelper.HasFormat(nId))
1865 xStrm = aDataHelper.GetInputStream(nId, "");
1866 if (xStrm.is())
1868 SfxMedium aClipboardMedium;
1869 aClipboardMedium.GetItemSet(); //generate initial itemset, not sure if necessary
1870 std::shared_ptr<const SfxFilter> pMathFilter =
1871 SfxFilter::GetFilterByName(MATHML_XML);
1872 aClipboardMedium.SetFilter(pMathFilter);
1873 aClipboardMedium.setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/);
1874 InsertFrom(aClipboardMedium);
1875 GetDoc()->UpdateText();
1878 else
1880 nId = SotClipboardFormatId::STRING;
1881 if (aDataHelper.HasFormat(nId))
1883 // In case of FORMAT_STRING no stream exists, need to generate one
1884 OUString aString;
1885 if (aDataHelper.GetString( nId, aString))
1887 // tdf#117091 force xml declaration to exist
1888 if (!aString.startsWith("<?xml"))
1889 aString = "<?xml version=\"1.0\"?>\n" + aString;
1891 SfxMedium aClipboardMedium;
1892 aClipboardMedium.GetItemSet(); //generates initial itemset, not sure if necessary
1893 std::shared_ptr<const SfxFilter> pMathFilter =
1894 SfxFilter::GetFilterByName(MATHML_XML);
1895 aClipboardMedium.SetFilter(pMathFilter);
1897 SvMemoryStream aStrm( const_cast<sal_Unicode *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ);
1898 uno::Reference<io::XInputStream> xStrm2( new ::utl::OInputStreamWrapper(aStrm) );
1899 aClipboardMedium.setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/);
1900 InsertFrom(aClipboardMedium);
1901 GetDoc()->UpdateText();
1907 break;
1910 case SID_NEXTERR:
1911 NextError();
1912 if (pWin)
1913 pWin->GrabFocus();
1914 break;
1916 case SID_PREVERR:
1917 PrevError();
1918 if (pWin)
1919 pWin->GrabFocus();
1920 break;
1922 case SID_NEXTMARK:
1923 if (pWin)
1925 pWin->SelNextMark();
1926 pWin->GrabFocus();
1928 break;
1930 case SID_PREVMARK:
1931 if (pWin)
1933 pWin->SelPrevMark();
1934 pWin->GrabFocus();
1936 break;
1938 case SID_TEXTSTATUS:
1940 if (rReq.GetArgs() != nullptr)
1942 const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_TEXTSTATUS);
1944 SetStatusText(rItem.GetValue());
1947 break;
1950 case SID_GETEDITTEXT:
1951 if (pWin && !pWin->GetText().isEmpty())
1952 GetDoc()->SetText( pWin->GetText() );
1953 break;
1955 case SID_ATTR_ZOOM:
1957 if ( !GetViewFrame().GetFrame().IsInPlace() )
1959 const SfxItemSet *pSet = rReq.GetArgs();
1960 if ( pSet )
1962 ZoomByItemSet(pSet);
1964 else
1966 SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( SmDocShell::GetPool() );
1967 aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
1968 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1969 ScopedVclPtr<AbstractSvxZoomDialog> xDlg(pFact->CreateSvxZoomDialog(GetViewFrame().GetWindow().GetFrameWeld(), aSet));
1970 xDlg->SetLimits( MINZOOM, MAXZOOM );
1971 if (xDlg->Execute() != RET_CANCEL)
1972 ZoomByItemSet(xDlg->GetOutputItemSet());
1976 break;
1978 case SID_ATTR_ZOOMSLIDER:
1980 const SfxItemSet *pArgs = rReq.GetArgs();
1981 const SfxPoolItem* pItem;
1983 if ( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) )
1985 const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
1986 mxGraphicWindow->SetZoom(nCurrentZoom);
1989 break;
1991 case SID_ELEMENTSDOCKINGWINDOW:
1993 // First make sure that the sidebar is visible
1994 GetViewFrame().ShowChildWindow(SID_SIDEBAR);
1996 sfx2::sidebar::Sidebar::TogglePanel(u"MathElementsPanel",
1997 GetViewFrame().GetFrame().GetFrameInterface());
1998 GetViewFrame().GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW );
2000 rReq.Ignore ();
2002 break;
2004 case SID_UNICODE_NOTATION_TOGGLE:
2006 EditEngine* pEditEngine = nullptr;
2007 if( pWin )
2008 pEditEngine = pWin->GetEditEngine();
2010 EditView* pEditView = nullptr;
2011 if( pEditEngine )
2012 pEditView = pEditEngine->GetView();
2014 if( pEditView )
2016 const OUString sInput = pEditView->GetSurroundingText();
2017 ESelection aSel( pWin->GetSelection() );
2019 if ( aSel.nStartPos > aSel.nEndPos )
2020 aSel.nEndPos = aSel.nStartPos;
2022 //calculate a valid end-position by reading logical characters
2023 sal_Int32 nUtf16Pos=0;
2024 while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
2026 sInput.iterateCodePoints(&nUtf16Pos);
2027 if( nUtf16Pos > aSel.nEndPos )
2028 aSel.nEndPos = nUtf16Pos;
2031 ToggleUnicodeCodepoint aToggle;
2032 while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
2033 --nUtf16Pos;
2034 const OUString sReplacement = aToggle.ReplacementString();
2035 if( !sReplacement.isEmpty() )
2037 pEditView->SetSelection( aSel );
2038 pEditEngine->UndoActionStart(EDITUNDO_REPLACEALL);
2039 aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
2040 pWin->SetSelection( aSel );
2041 pEditView->InsertText( sReplacement, true );
2042 pEditEngine->UndoActionEnd();
2043 pWin->Flush();
2047 break;
2049 case SID_SYMBOLS_CATALOGUE:
2052 // get device used to retrieve the FontList
2053 SmDocShell *pDoc = GetDoc();
2054 OutputDevice *pDev = pDoc->GetPrinter();
2055 if (!pDev || pDev->GetFontFaceCollectionCount() == 0)
2056 pDev = &SM_MOD()->GetDefaultVirtualDev();
2057 SAL_WARN_IF( !pDev, "starmath", "device for font list missing" );
2059 SmModule *pp = SM_MOD();
2060 SmSymbolDialog aDialog(pWin ? pWin->GetFrameWeld() : nullptr, pDev, pp->GetSymbolManager(), *this);
2061 aDialog.run();
2063 break;
2065 rReq.Done();
2069 void SmViewShell::GetState(SfxItemSet &rSet)
2071 SfxWhichIter aIter(rSet);
2073 SmEditWindow *pEditWin = GetEditWindow();
2074 for (sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich())
2076 switch (nWh)
2078 case SID_CUT:
2079 case SID_COPY:
2080 case SID_DELETE:
2081 if (! pEditWin || ! pEditWin->IsSelected())
2082 rSet.DisableItem(nWh);
2083 break;
2085 case SID_PASTE:
2086 if (pEditWin)
2088 TransferableDataHelper aDataHelper(
2089 TransferableDataHelper::CreateFromClipboard(
2090 pEditWin->GetClipboard()) );
2092 mbPasteState = aDataHelper.GetTransferable().is() &&
2093 ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ||
2094 aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ||
2095 (aDataHelper.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )
2096 && aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )));
2098 if( !mbPasteState )
2099 rSet.DisableItem( nWh );
2100 break;
2102 case SID_ATTR_ZOOM:
2103 rSet.Put(SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
2104 [[fallthrough]];
2105 case SID_ZOOMIN:
2106 case SID_ZOOMOUT:
2107 case SID_ZOOM_OPTIMAL:
2108 if ( GetViewFrame().GetFrame().IsInPlace() )
2109 rSet.DisableItem( nWh );
2110 break;
2112 case SID_ATTR_ZOOMSLIDER :
2114 const sal_uInt16 nCurrentZoom = mxGraphicWindow->GetZoom();
2115 SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM );
2116 aZoomSliderItem.AddSnappingPoint( 100 );
2117 rSet.Put( aZoomSliderItem );
2119 break;
2121 case SID_NEXTERR:
2122 case SID_PREVERR:
2123 case SID_NEXTMARK:
2124 case SID_PREVMARK:
2125 case SID_DRAW:
2126 case SID_SELECT:
2127 if (! pEditWin || pEditWin->IsEmpty())
2128 rSet.DisableItem(nWh);
2129 break;
2131 case SID_TEXTSTATUS:
2133 rSet.Put(SfxStringItem(nWh, maStatusText));
2135 break;
2137 case SID_FORMULACURSOR:
2139 SmModule *pp = SM_MOD();
2140 rSet.Put(SfxBoolItem(nWh, pp->GetConfig()->IsShowFormulaCursor()));
2142 break;
2143 case SID_ELEMENTSDOCKINGWINDOW:
2145 const bool bState = sfx2::sidebar::Sidebar::IsPanelVisible(
2146 u"MathElementsPanel", GetViewFrame().GetFrame().GetFrameInterface());
2147 rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState));
2149 break;
2154 namespace
2156 class SmController : public SfxBaseController
2158 public:
2159 SmController(SfxViewShell& rViewShell)
2160 : SfxBaseController(&rViewShell)
2161 , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler(
2162 GetContextName, this, vcl::EnumContext::Context::Math))
2164 rViewShell.SetContextName(GetContextName());
2166 // No need to call mpSelectionChangeHandler->Disconnect() unless SmController implements XSelectionSupplier
2167 // ~SmController() { mpSelectionChangeHandler->Disconnect(); }
2169 // css::frame::XController
2170 void SAL_CALL attachFrame(const css::uno::Reference<css::frame::XFrame>& xFrame) override
2172 SfxBaseController::attachFrame(xFrame);
2174 if (comphelper::LibreOfficeKit::isActive())
2175 CopyLokViewCallbackFromFrameCreator();
2177 // No need to call mpSelectionChangeHandler->Connect() unless SmController implements XSelectionSupplier
2178 mpSelectionChangeHandler->selectionChanged({}); // Installs the correct context
2181 virtual void SAL_CALL dispose() override
2183 if (comphelper::LibreOfficeKit::isActive())
2184 if (auto pViewShell = GetViewShell_Impl())
2185 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE,
2186 OString::boolean(false));
2188 SfxBaseController::dispose();
2191 private:
2192 static OUString GetContextName() { return "Math"; } // Static constant for now
2194 rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler;
2198 SmViewShell::SmViewShell(SfxViewFrame& rFrame_, SfxViewShell *)
2199 : SfxViewShell(rFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS)
2200 , mxGraphicWindow(VclPtr<SmGraphicWindow>::Create(*this))
2201 , maGraphicController(mxGraphicWindow->GetGraphicWidget(), SID_GRAPHIC_SM, rFrame_.GetBindings())
2202 , mbPasteState(false)
2203 , mbInsertIntoEditWindow(false)
2205 SetStatusText(OUString());
2206 SetWindow(mxGraphicWindow.get());
2207 SfxShell::SetName("SmView");
2208 SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() );
2209 SetController(new SmController(*this));
2212 SmViewShell::~SmViewShell()
2214 //!! this view shell is not active anymore !!
2215 // Thus 'SmGetActiveView' will give a 0 pointer.
2216 // Thus we need to supply this view as argument
2217 if (SmEditWindow *pEditWin = GetEditWindow())
2218 pEditWin->DeleteEditView();
2219 mxGraphicWindow.disposeAndClear();
2222 void SmViewShell::Deactivate( bool bIsMDIActivate )
2224 if (SmEditWindow *pEdit = GetEditWindow())
2225 pEdit->Flush();
2227 SfxViewShell::Deactivate( bIsMDIActivate );
2230 void SmViewShell::Activate( bool bIsMDIActivate )
2232 SfxViewShell::Activate( bIsMDIActivate );
2234 if (comphelper::LibreOfficeKit::isActive())
2236 // In LOK, activate in-place editing
2237 GetGraphicWidget().GrabFocus();
2239 else if (SmEditWindow *pEdit = GetEditWindow())
2241 //! Since there is no way to be informed if a "drag and drop"
2242 //! event has taken place, we call SetText here in order to
2243 //! synchronize the GraphicWindow display with the text in the
2244 //! EditEngine.
2245 SmDocShell *pDoc = GetDoc();
2246 pDoc->SetText( pDoc->GetEditEngine().GetText() );
2248 if ( bIsMDIActivate )
2249 pEdit->GrabFocus();
2253 IMPL_LINK( SmViewShell, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
2255 assert(_pFileDlg && "SmViewShell::DialogClosedHdl(): no file dialog");
2256 assert(mpDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter");
2258 if ( ERRCODE_NONE == _pFileDlg->GetError() )
2260 std::unique_ptr<SfxMedium> pMedium = mpDocInserter->CreateMedium();
2262 if ( pMedium )
2264 if ( pMedium->IsStorage() )
2265 Insert( *pMedium );
2266 else
2267 InsertFrom( *pMedium );
2268 pMedium.reset();
2270 SmDocShell* pDoc = GetDoc();
2271 pDoc->UpdateText();
2272 pDoc->ArrangeFormula();
2273 pDoc->Repaint();
2274 // adjust window, repaint, increment ModifyCount,...
2275 GetViewFrame().GetBindings().Invalidate(SID_GRAPHIC_SM);
2279 mpRequest->SetReturnValue( SfxBoolItem( mpRequest->GetSlot(), true ) );
2280 mpRequest->Done();
2283 void SmViewShell::Notify( SfxBroadcaster& , const SfxHint& rHint )
2285 switch( rHint.GetId() )
2287 case SfxHintId::ModeChanged:
2288 case SfxHintId::DocChanged:
2289 GetViewFrame().GetBindings().InvalidateAll(false);
2290 break;
2291 default:
2292 break;
2296 bool SmViewShell::IsInlineEditEnabled()
2298 return comphelper::LibreOfficeKit::isActive()
2299 || officecfg::Office::Common::Misc::ExperimentalMode::get();
2302 void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet)
2304 assert(pSet);
2305 const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM);
2306 switch( rZoom.GetType() )
2308 case SvxZoomType::PERCENT:
2309 mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(rZoom.GetValue ()));
2310 break;
2312 case SvxZoomType::OPTIMAL:
2313 mxGraphicWindow->ZoomToFitInWindow();
2314 break;
2316 case SvxZoomType::PAGEWIDTH:
2317 case SvxZoomType::WHOLEPAGE:
2319 const MapMode aMap( SmMapUnit() );
2320 SfxPrinter *pPrinter = GetPrinter( true );
2321 tools::Rectangle OutputRect(Point(), pPrinter->GetOutputSize());
2322 Size OutputSize(pPrinter->LogicToPixel(Size(OutputRect.GetWidth(),
2323 OutputRect.GetHeight()), aMap));
2324 Size GraphicSize(pPrinter->LogicToPixel(GetDoc()->GetSize(), aMap));
2325 if (GraphicSize.Width() <= 0 || GraphicSize.Height() <= 0)
2326 break;
2327 sal_uInt16 nZ = std::min(o3tl::convert(OutputSize.Width(), 100, GraphicSize.Width()),
2328 o3tl::convert(OutputSize.Height(), 100, GraphicSize.Height()));
2329 mxGraphicWindow->SetZoom(nZ);
2330 break;
2332 default:
2333 break;
2337 std::optional<OString> SmViewShell::getLOKPayload(int nType, int nViewId) const
2339 switch (nType)
2341 case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
2343 OString sRectangle;
2344 if (const SmGraphicWidget& widget = GetGraphicWidget(); widget.IsCursorVisible())
2346 SmCursor& rCursor = GetDoc()->GetCursor();
2347 OutputDevice& rOutDev = const_cast<SmGraphicWidget&>(widget).GetOutputDevice();
2348 tools::Rectangle aCaret = rCursor.GetCaretRectangle(rOutDev);
2349 Point aFormulaDrawPos = widget.GetFormulaDrawPos();
2350 aCaret.Move(aFormulaDrawPos.X(), aFormulaDrawPos.Y());
2351 LokStarMathHelper helper(SfxViewShell::Current());
2352 tools::Rectangle aBounds = helper.GetBoundingBox();
2353 aCaret.Move(aBounds.Left(), aBounds.Top());
2354 sRectangle = aCaret.toString();
2356 return SfxLokHelper::makeVisCursorInvalidation(nViewId, sRectangle, false, {});
2358 case LOK_CALLBACK_TEXT_SELECTION:
2360 OString sRectangle;
2361 if (const SmGraphicWidget& widget = GetGraphicWidget(); widget.IsCursorVisible())
2363 SmCursor& rCursor = GetDoc()->GetCursor();
2364 OutputDevice& rOutDev = const_cast<SmGraphicWidget&>(widget).GetOutputDevice();
2365 tools::Rectangle aSelection = rCursor.GetSelectionRectangle(rOutDev);
2366 if (!aSelection.IsEmpty())
2368 Point aFormulaDrawPos = widget.GetFormulaDrawPos();
2369 aSelection.Move(aFormulaDrawPos.X(), aFormulaDrawPos.Y());
2370 LokStarMathHelper helper(SfxViewShell::Current());
2371 tools::Rectangle aBounds = helper.GetBoundingBox();
2373 aSelection.Move(aBounds.Left(), aBounds.Top());
2374 sRectangle = aSelection.toString();
2377 return sRectangle;
2379 case LOK_CALLBACK_TEXT_SELECTION_START:
2380 case LOK_CALLBACK_TEXT_SELECTION_END:
2381 case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
2382 case LOK_CALLBACK_TEXT_VIEW_SELECTION:
2383 return {};
2385 return SfxViewShell::getLOKPayload(nType, nViewId); // aborts
2388 void SmViewShell::SendCaretToLOK() const
2390 const int nViewId = sal_Int32(GetViewShellId());
2391 if (const auto& payload = getLOKPayload(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, nViewId))
2393 libreOfficeKitViewCallbackWithViewId(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
2394 *payload, nViewId);
2396 if (const auto& payload = getLOKPayload(LOK_CALLBACK_TEXT_SELECTION, nViewId))
2398 libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, *payload);
2402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */