1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <editeng/editeng.hxx>
21 #include <editeng/outlobj.hxx>
22 #include <svx/svdobj.hxx>
23 #include <svx/svdoole2.hxx>
24 #include <svx/svdouno.hxx>
25 #include <svx/ImageMapInfo.hxx>
26 #include <sfx2/dispatch.hxx>
27 #include <sfx2/viewfrm.hxx>
28 #include <vcl/uitest/logger.hxx>
29 #include <vcl/uitest/eventdescription.hxx>
34 #include <tabvwsh.hxx>
35 #include <drwlayer.hxx>
36 #include <userdat.hxx>
38 #include <drawview.hxx>
39 #include <comphelper/lok.hxx>
40 #include <com/sun/star/embed/EmbedVerbs.hpp>
45 void collectUIInformation( const OUString
& aevent
)
47 EventDescription aDescription
;
48 aDescription
.aID
= "grid_window";
49 aDescription
.aParameters
= {{ aevent
, ""}};
50 aDescription
.aAction
= "COMMENT";
51 aDescription
.aParent
= "MainWindow";
52 aDescription
.aKeyWord
= "ScGridWinUIObject";
53 UITestLogger::getInstance().logEvent(aDescription
);
58 // base class for draw module specific functions
59 FuDraw::FuDraw(ScTabViewShell
& rViewSh
, vcl::Window
* pWin
, ScDrawView
* pViewP
,
60 SdrModel
* pDoc
, const SfxRequest
& rReq
)
61 : FuPoor(rViewSh
, pWin
, pViewP
, pDoc
, rReq
)
62 , aNewPointer(PointerStyle::Arrow
)
63 , aOldPointer(PointerStyle::Arrow
)
71 void FuDraw::DoModifiers(const MouseEvent
& rMEvt
)
73 // Shift = Ortho and AngleSnap
74 // Control = Snap (Toggle)
77 bool bShift
= rMEvt
.IsShift();
78 bool bAlt
= rMEvt
.IsMod2();
81 bool bAngleSnap
= bShift
;
85 if(doConstructOrthogonal())
90 if (pView
->IsOrtho() != bOrtho
)
91 pView
->SetOrtho(bOrtho
);
92 if (pView
->IsAngleSnapEnabled() != bAngleSnap
)
93 pView
->SetAngleSnapEnabled(bAngleSnap
);
95 if (pView
->IsCreate1stPointAsCenter() != bCenter
)
96 pView
->SetCreate1stPointAsCenter(bCenter
);
97 if (pView
->IsResizeAtCenter() != bCenter
)
98 pView
->SetResizeAtCenter(bCenter
);
102 void FuDraw::ResetModifiers()
107 ScViewData
& rViewData
= rViewShell
.GetViewData();
108 const ScViewOptions
& rOpt
= rViewData
.GetOptions();
109 const ScGridOptions
& rGrid
= rOpt
.GetGridOptions();
110 bool bGridOpt
= rGrid
.GetUseGridSnap();
112 if (pView
->IsOrtho())
113 pView
->SetOrtho(false);
114 if (pView
->IsAngleSnapEnabled())
115 pView
->SetAngleSnapEnabled(false);
117 if (pView
->IsGridSnap() != bGridOpt
)
118 pView
->SetGridSnap(bGridOpt
);
119 if (pView
->IsSnapEnabled() != bGridOpt
)
120 pView
->SetSnapEnabled(bGridOpt
);
122 if (pView
->IsCreate1stPointAsCenter())
123 pView
->SetCreate1stPointAsCenter(false);
124 if (pView
->IsResizeAtCenter())
125 pView
->SetResizeAtCenter(false);
128 bool FuDraw::MouseButtonDown(const MouseEvent
& rMEvt
)
130 // remember button state for creation of own MouseEvents
131 SetMouseButtonCode(rMEvt
.GetButtons());
133 DoModifiers( rMEvt
);
137 bool FuDraw::MouseMove(const MouseEvent
& rMEvt
)
139 // evaluate modifiers only if in a drawing layer action
140 // (don't interfere with keyboard shortcut handling)
141 if (pView
->IsAction())
142 DoModifiers( rMEvt
);
147 bool FuDraw::MouseButtonUp(const MouseEvent
& rMEvt
)
149 // remember button state for creation of own MouseEvents
150 SetMouseButtonCode(rMEvt
.GetButtons());
156 // Process Keyboard events. Return true if an event is being handled
157 static bool lcl_KeyEditMode( SdrObject
* pObj
, ScTabViewShell
& rViewShell
, const KeyEvent
* pInitialKey
)
159 bool bReturn
= false;
160 if ( DynCastSdrTextObj( pObj
) != nullptr && dynamic_cast<const SdrUnoObj
*>( pObj
) == nullptr )
163 // start text edit - like FuSelection::MouseButtonUp,
164 // but with bCursorToEnd instead of mouse position
166 OutlinerParaObject
* pOPO
= pObj
->GetOutlinerParaObject();
167 bool bVertical
= ( pOPO
&& pOPO
->IsEffectivelyVertical() );
168 sal_uInt16 nTextSlotId
= bVertical
? SID_DRAW_TEXT_VERTICAL
: SID_DRAW_TEXT
;
170 // don't switch shells if text shell is already active
171 FuPoor
* pPoor
= rViewShell
.GetViewData().GetView()->GetDrawFuncPtr();
172 if ( !pPoor
|| pPoor
->GetSlotID() != nTextSlotId
)
174 rViewShell
.GetViewData().GetDispatcher().
175 Execute(nTextSlotId
, SfxCallMode::SYNCHRON
| SfxCallMode::RECORD
);
178 // get the resulting FuText and set in edit mode
179 pPoor
= rViewShell
.GetViewData().GetView()->GetDrawFuncPtr();
180 if ( pPoor
&& pPoor
->GetSlotID() == nTextSlotId
) // no RTTI
182 FuText
* pText
= static_cast<FuText
*>(pPoor
);
183 pText
->SetInEditMode( pObj
, nullptr, true, pInitialKey
);
184 //! set cursor to end of text
191 bool FuDraw::KeyInput(const KeyEvent
& rKEvt
)
193 bool bReturn
= false;
194 ScViewData
& rViewData
= rViewShell
.GetViewData();
196 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
197 switch ( rKEvt
.GetKeyCode().GetCode() )
200 if ( rViewShell
.IsDrawTextShell() || aSfxRequest
.GetSlot() == SID_DRAW_NOTEEDIT
)
202 collectUIInformation(u
"CLOSE"_ustr
);
203 // if object selected -> normal draw-shell, else turn off drawing
204 rViewData
.GetDispatcher().Execute(aSfxRequest
.GetSlot(), SfxCallMode::SLOT
| SfxCallMode::RECORD
);
207 else if ( rViewShell
.IsDrawSelMode() )
210 rViewData
.GetDispatcher().Execute(SID_OBJECT_SELECT
, SfxCallMode::SLOT
| SfxCallMode::RECORD
);
213 else if ( rMarkList
.GetMarkCount() != 0 )
216 SdrHdlList
& rHdlList
= const_cast< SdrHdlList
& >( pView
->GetHdlList() );
217 if( rHdlList
.GetFocusHdl() )
218 rHdlList
.ResetFocusHdl();
222 // while bezier editing, object is selected
223 if (rMarkList
.GetMarkCount() == 0)
224 rViewShell
.SetDrawShell( false );
230 case KEY_DELETE
: //! via accelerator
231 pView
->DeleteMarked();
237 if( rKEvt
.GetKeyCode().GetModifier() == 0 )
239 // activate OLE object on RETURN for selected object
240 // put selected text object in edit mode
241 if( !pView
->IsTextEdit() && 1 == rMarkList
.GetMarkCount() )
243 bool bOle
= rViewShell
.GetViewFrame().GetFrame().IsInPlace();
244 SdrObject
* pObj
= rMarkList
.GetMark( 0 )->GetMarkedSdrObj();
245 auto pOleObj
= dynamic_cast<SdrOle2Obj
*>(pObj
);
246 if( pOleObj
&& !bOle
)
248 rViewShell
.ActivateObject(pOleObj
, css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY
);
253 else if ( lcl_KeyEditMode( pObj
, rViewShell
, nullptr ) ) // start text edit for suitable object
262 if( rKEvt
.GetKeyCode().GetModifier() == 0 )
264 // put selected text object in edit mode
265 // (this is not SID_SETINPUTMODE, but F2 hardcoded, like in Writer)
266 if( !pView
->IsTextEdit() && 1 == rMarkList
.GetMarkCount() )
268 SdrObject
* pObj
= rMarkList
.GetMark( 0 )->GetMarkedSdrObj();
269 bool isMobilePhone
= comphelper::LibreOfficeKit::isActive() && rViewShell
.isLOKMobilePhone();
270 // Double tapping on charts on phone may result in activating the edit mode which is not wanted.
271 // It happens due to the delay of selection message of the object from kit to javascript
272 // in that case F2 is sent instead of double click
273 if (isMobilePhone
&& ScDocument::IsChart(pObj
))
275 rViewShell
.ActivateObject(static_cast<SdrOle2Obj
*>(pObj
), css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY
);
278 if ( lcl_KeyEditMode( pObj
, rViewShell
, nullptr ) ) // start text edit for suitable object
287 // in calc do NOT start draw object selection using TAB/SHIFT-TAB when
288 // there is not yet an object selected
289 if(rMarkList
.GetMarkCount() != 0)
291 vcl::KeyCode aCode
= rKEvt
.GetKeyCode();
293 if ( !aCode
.IsMod1() && !aCode
.IsMod2() )
295 // changeover to the next object
296 if(!pView
->MarkNextObj( !aCode
.IsShift() ))
298 //If there is only one object, don't do the UnmarkAllObj() & MarkNextObj().
299 if ( pView
->HasMultipleMarkableObjects() && pView
->HasMarkableObj() )
301 // No next object: go over open end and
302 // get first from the other side
303 pView
->UnmarkAllObj();
304 pView
->MarkNextObj(!aCode
.IsShift());
309 if(rMarkList
.GetMarkCount() != 0)
310 pView
->MakeVisible(pView
->GetAllMarkedRect(), *pWindow
);
315 // handle Mod1 and Mod2 to get travelling running on different systems
316 if(rKEvt
.GetKeyCode().IsMod1() || rKEvt
.GetKeyCode().IsMod2())
318 // II do something with a selected handle?
319 const SdrHdlList
& rHdlList
= pView
->GetHdlList();
320 bool bForward(!rKEvt
.GetKeyCode().IsShift());
322 const_cast<SdrHdlList
&>(rHdlList
).TravelFocusHdl(bForward
);
324 // guarantee visibility of focused handle
325 SdrHdl
* pHdl
= rHdlList
.GetFocusHdl();
329 Point
aHdlPosition(pHdl
->GetPos());
330 tools::Rectangle
aVisRect(aHdlPosition
- Point(100, 100), Size(200, 200));
331 pView
->MakeVisible(aVisRect
, *pWindow
);
343 // in calc do NOT select the last draw object when
344 // there is not yet an object selected
345 if(rMarkList
.GetMarkCount() != 0)
347 vcl::KeyCode aCode
= rKEvt
.GetKeyCode();
349 if ( aCode
.IsMod1() )
352 pView
->UnmarkAllObj();
353 pView
->MarkNextObj();
356 if(rMarkList
.GetMarkCount() != 0)
357 pView
->MakeVisible(pView
->GetAllMarkedRect(), *pWindow
);
367 // in calc do NOT select the first draw object when
368 // there is not yet an object selected
369 if(rMarkList
.GetMarkCount() != 0)
371 vcl::KeyCode aCode
= rKEvt
.GetKeyCode();
373 if ( aCode
.IsMod1() )
376 pView
->UnmarkAllObj();
377 pView
->MarkNextObj(true);
380 if(rMarkList
.GetMarkCount() != 0)
381 pView
->MakeVisible(pView
->GetAllMarkedRect(), *pWindow
);
394 // in calc do cursor travelling of draw objects only when
395 // there is an object selected yet
396 if(rMarkList
.GetMarkCount() != 0)
399 if(rMarkList
.GetMarkCount() == 1)
401 // disable cursor travelling on note objects as the tail connector position
403 SdrObject
* pObj
= rMarkList
.GetMark( 0 )->GetMarkedSdrObj();
404 if( ScDrawLayer::IsNoteCaption( pObj
) )
410 sal_uInt16 nCode
= rKEvt
.GetKeyCode().GetCode();
418 else if (nCode
== KEY_DOWN
)
424 else if (nCode
== KEY_LEFT
)
430 else if (nCode
== KEY_RIGHT
)
437 bool bReadOnly
= rViewData
.GetDocShell()->IsReadOnly();
439 if(!rKEvt
.GetKeyCode().IsMod1() && !bReadOnly
)
441 if(rKEvt
.GetKeyCode().IsMod2())
443 // move in 1 pixel distance
444 Size aLogicSizeOnePixel
= pWindow
? pWindow
->PixelToLogic(Size(1,1)) : Size(100, 100);
445 nX
*= aLogicSizeOnePixel
.Width();
446 nY
*= aLogicSizeOnePixel
.Height();
448 else if(rKEvt
.GetKeyCode().IsShift()) // #i121236# Support for shift key in calc
455 // old, fixed move distance
460 // is there a movement to do?
461 if(0 != nX
|| 0 != nY
)
464 const SdrHdlList
& rHdlList
= pView
->GetHdlList();
465 SdrHdl
* pHdl
= rHdlList
.GetFocusHdl();
469 // only take action when move is allowed
470 if(pView
->IsMoveAllowed())
472 // restrict movement to WorkArea
473 const tools::Rectangle
& rWorkArea
= pView
->GetWorkArea();
475 if(!rWorkArea
.IsEmpty())
477 tools::Rectangle
aMarkRect(pView
->GetMarkedObjRect());
478 aMarkRect
.Move(nX
, nY
);
480 if(!aMarkRect
.Contains(rWorkArea
))
482 if(aMarkRect
.Left() < rWorkArea
.Left())
484 nX
+= rWorkArea
.Left() - aMarkRect
.Left();
487 if(aMarkRect
.Right() > rWorkArea
.Right())
489 nX
-= aMarkRect
.Right() - rWorkArea
.Right();
492 if(aMarkRect
.Top() < rWorkArea
.Top())
494 nY
+= rWorkArea
.Top() - aMarkRect
.Top();
497 if(aMarkRect
.Bottom() > rWorkArea
.Bottom())
499 nY
-= aMarkRect
.Bottom() - rWorkArea
.Bottom();
504 // now move the selected draw objects
505 pView
->MoveAllMarked(Size(nX
, nY
));
508 pView
->MakeVisible(pView
->GetAllMarkedRect(), *pWindow
);
515 // move handle with index nHandleIndex
518 // now move the Handle (nX, nY)
519 Point
aStartPoint(pHdl
->GetPos());
520 Point
aEndPoint(pHdl
->GetPos() + Point(nX
, nY
));
521 const SdrDragStat
& rDragStat
= pView
->GetDragStat();
524 pView
->BegDragObj(aStartPoint
, nullptr, pHdl
, 0);
526 if(pView
->IsDragObj())
528 bool bWasNoSnap
= rDragStat
.IsNoSnap();
529 bool bWasSnapEnabled
= pView
->IsSnapEnabled();
531 // switch snapping off
533 const_cast<SdrDragStat
&>(rDragStat
).SetNoSnap();
535 pView
->SetSnapEnabled(false);
537 pView
->MovAction(aEndPoint
);
542 const_cast<SdrDragStat
&>(rDragStat
).SetNoSnap(bWasNoSnap
);
544 pView
->SetSnapEnabled(bWasSnapEnabled
);
547 // make moved handle visible
548 tools::Rectangle
aVisRect(aEndPoint
- Point(100, 100), Size(200, 200));
549 pView
->MakeVisible(aVisRect
, *pWindow
);
562 // in calc do only something when draw objects are selected
563 if(rMarkList
.GetMarkCount() != 0)
565 const SdrHdlList
& rHdlList
= pView
->GetHdlList();
566 SdrHdl
* pHdl
= rHdlList
.GetFocusHdl();
570 if(pHdl
->GetKind() == SdrHdlKind::Poly
)
572 // rescue ID of point with focus
573 sal_uInt32
nPol(pHdl
->GetPolyNum());
574 sal_uInt32
nPnt(pHdl
->GetPointNum());
576 if(pView
->IsPointMarked(*pHdl
))
578 if(rKEvt
.GetKeyCode().IsShift())
580 pView
->UnmarkPoint(*pHdl
);
585 if(!rKEvt
.GetKeyCode().IsShift())
587 pView
->UnmarkAllPoints();
590 pView
->MarkPoint(*pHdl
);
593 if(nullptr == rHdlList
.GetFocusHdl())
595 // restore point with focus
596 SdrHdl
* pNewOne
= nullptr;
598 for(size_t a
= 0; !pNewOne
&& a
< rHdlList
.GetHdlCount(); ++a
)
600 SdrHdl
* pAct
= rHdlList
.GetHdl(a
);
603 && pAct
->GetKind() == SdrHdlKind::Poly
604 && pAct
->GetPolyNum() == nPol
605 && pAct
->GetPointNum() == nPnt
)
613 const_cast<SdrHdlList
&>(rHdlList
).SetFocusHdl(pNewOne
);
627 bReturn
= FuPoor::KeyInput(rKEvt
);
632 // allow direct typing into a selected text object
634 if( !pView
->IsTextEdit() && 1 == rMarkList
.GetMarkCount() && EditEngine::IsSimpleCharInput(rKEvt
) )
636 SdrObject
* pObj
= rMarkList
.GetMark( 0 )->GetMarkedSdrObj();
638 // start text edit for suitable object, pass key event to OutlinerView
639 if ( lcl_KeyEditMode( pObj
, rViewShell
, &rKEvt
) )
647 // toggle mouse-pointer
648 static bool lcl_UrlHit( const SdrView
* pView
, const Point
& rPosPixel
, const vcl::Window
* pWindow
)
651 MouseEvent
aMEvt( rPosPixel
, 1, MouseEventModifiers::NONE
, MOUSE_LEFT
);
652 SdrHitKind eHit
= pView
->PickAnything( aMEvt
, SdrMouseEventKind::BUTTONDOWN
, aVEvt
);
654 if (eHit
!= SdrHitKind::NONE
&& aVEvt
.mpObj
!= nullptr)
656 if ( SvxIMapInfo::GetIMapInfo(aVEvt
.mpObj
) && SvxIMapInfo::GetHitIMapObject(
657 aVEvt
.mpObj
, pWindow
->PixelToLogic(rPosPixel
), pWindow
->GetOutDev() ) )
660 if (aVEvt
.meEvent
== SdrEventKind::ExecuteUrl
)
667 void FuDraw::ForcePointer(const MouseEvent
* pMEvt
)
669 if ( pView
->IsAction() )
672 Point aPosPixel
= pWindow
->GetPointerPosPixel();
673 bool bAlt
= pMEvt
&& pMEvt
->IsMod2();
674 Point aPnt
= pWindow
->PixelToLogic( aPosPixel
);
675 SdrHdl
* pHdl
= pView
->PickHandle(aPnt
);
677 SdrObject
* pMacroPickObj
;
679 ScMacroInfo
* pInfo
= nullptr;
680 SdrObject
* pObj
= pView
->PickObj(aPnt
, pView
->getHitTolLog(), pPV
, SdrSearchOptions::ALSOONMASTER
);
683 if ( pObj
->IsGroupObject() )
685 SdrObject
* pHit
= pView
->PickObj(aMDPos
, pView
->getHitTolLog(), pPV
, SdrSearchOptions::DEEP
);
689 pInfo
= ScDrawLayer::GetMacroInfo( pObj
);
692 if ( pView
->IsTextEdit() )
694 rViewShell
.SetActivePointer(PointerStyle::Text
); // can't be ?
698 rViewShell
.SetActivePointer(
699 pView
->GetPreferredPointer( aPnt
, pWindow
->GetOutDev() ) );
701 else if ( pView
->IsMarkedHit(aPnt
) )
703 rViewShell
.SetActivePointer( PointerStyle::Move
);
705 else if ( !bAlt
&& ( !pMEvt
|| !pMEvt
->GetButtons() )
706 && lcl_UrlHit( pView
, aPosPixel
, pWindow
) )
708 // could be suppressed with ALT
709 pWindow
->SetPointer( PointerStyle::RefHand
); // Text-URL / ImageMap
711 else if ( !bAlt
&& (pMacroPickObj
= pView
->PickObj(aPnt
, pView
->getHitTolLog(), pPV
, SdrSearchOptions::PICKMACRO
)) )
713 // could be suppressed with ALT
714 SdrObjMacroHitRec aHitRec
; //! something missing ????
715 rViewShell
.SetActivePointer(pMacroPickObj
->GetMacroPointer(aHitRec
));
717 else if ( !bAlt
&& pInfo
&& (!pInfo
->GetMacro().isEmpty() || !pObj
->getHyperlink().isEmpty()) )
718 pWindow
->SetPointer( PointerStyle::RefHand
);
719 else if ( IsDetectiveHit( aPnt
) )
720 rViewShell
.SetActivePointer( PointerStyle::Detective
);
723 const bool bIsThemed
= rViewShell
.GetViewData().IsThemedCursor();
724 rViewShell
.SetActivePointer( bIsThemed
? PointerStyle::FatCross
: PointerStyle::Arrow
); //! in Gridwin?
728 bool FuDraw::IsEditingANote() const
730 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
731 const size_t backval
=rMarkList
.GetMarkCount();
732 for (size_t nlv1
=0; nlv1
<backval
; ++nlv1
)
734 SdrObject
* pObj
= rMarkList
.GetMark( nlv1
)->GetMarkedSdrObj();
735 if ( ScDrawLayer::IsNoteCaption( pObj
) )
743 bool FuDraw::IsSizingOrMovingNote( const MouseEvent
& rMEvt
) const
745 bool bIsSizingOrMoving
= false;
746 if ( rMEvt
.IsLeft() )
748 const SdrMarkList
& rMarkList
= pView
->GetMarkedObjectList();
749 if(rMarkList
.GetMarkCount() == 1)
751 SdrObject
* pObj
= rMarkList
.GetMark( 0 )->GetMarkedSdrObj();
752 if ( ScDrawLayer::IsNoteCaption( pObj
) )
754 Point aMPos
= pWindow
->PixelToLogic( rMEvt
.GetPosPixel() );
756 pView
->PickHandle( aMPos
) || // handles to resize the note
757 pView
->IsTextEditFrameHit( aMPos
); // frame for moving the note
761 return bIsSizingOrMoving
;
764 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */