/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sd.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include "View.hxx" #include "sdresid.hxx" #include "annotations.hrc" #include "annotationmanagerimpl.hxx" #include "annotationwindow.hxx" #include "annotationtag.hxx" #include "sdpage.hxx" #include "ViewShell.hxx" #include "app.hrc" #include "Window.hxx" #include "drawdoc.hxx" using ::rtl::OUString; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::drawing; using namespace ::com::sun::star::office; using namespace ::com::sun::star::geometry; namespace sd { const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; static const int DRGPIX = 2; // Drag MinMove in Pixel // -------------------------------------------------------------------- static OUString getInitials( const OUString& rName ) { OUString sInitials; const sal_Unicode * pStr = rName.getStr(); sal_Int32 nLength = rName.getLength(); while( nLength ) { // skip whitespace while( nLength && (*pStr <= ' ') ) { nLength--; pStr++; } // take letter if( nLength ) { sInitials += OUString( *pStr ); nLength--; pStr++; } // skip letters until whitespace while( nLength && (*pStr > ' ') ) { nLength--; pStr++; } } return sInitials; } // -------------------------------------------------------------------- // -------------------------------------------------------------------- class AnnotationDragMove : public SdrDragMove { public: AnnotationDragMove(SdrDragView& rNewView, const rtl::Reference & xTag); virtual bool BeginSdrDrag(); virtual bool EndSdrDrag(bool bCopy); virtual void MoveSdrDrag(const Point& rNoSnapPnt); virtual void CancelSdrDrag(); private: rtl::Reference mxTag; Point maOrigin; }; AnnotationDragMove::AnnotationDragMove(SdrDragView& rNewView, const rtl::Reference & xTag) : SdrDragMove(rNewView) , mxTag( xTag ) { } bool AnnotationDragMove::BeginSdrDrag() { DragStat().Ref1()=GetDragHdl()->GetPos(); DragStat().SetShown(!DragStat().IsShown()); maOrigin = GetDragHdl()->GetPos(); DragStat().SetActionRect(Rectangle(maOrigin,maOrigin)); return true; } void AnnotationDragMove::MoveSdrDrag(const Point& rNoSnapPnt) { Point aPnt(rNoSnapPnt); if (DragStat().CheckMinMoved(rNoSnapPnt)) { if (aPnt!=DragStat().GetNow()) { Hide(); DragStat().NextMove(aPnt); GetDragHdl()->SetPos( maOrigin + Point( DragStat().GetDX(), DragStat().GetDY() ) ); Show(); DragStat().SetActionRect(Rectangle(aPnt,aPnt)); } } } bool AnnotationDragMove::EndSdrDrag(bool /*bCopy*/) { Hide(); if( mxTag.is() ) mxTag->Move( DragStat().GetDX(), DragStat().GetDY() ); return sal_True; } void AnnotationDragMove::CancelSdrDrag() { Hide(); } // -------------------------------------------------------------------- class AnnotationHdl : public SmartHdl { public: AnnotationHdl( const SmartTagReference& xTag, const Reference< XAnnotation >& xAnnotation, const Point& rPnt ); virtual ~AnnotationHdl(); virtual void CreateB2dIAObject(); virtual sal_Bool IsFocusHdl() const; virtual Pointer GetSdrDragPointer() const; virtual bool isMarkable() const; private: Reference< XAnnotation > mxAnnotation; rtl::Reference< AnnotationTag > mxTag; }; // -------------------------------------------------------------------- AnnotationHdl::AnnotationHdl( const SmartTagReference& xTag, const Reference< XAnnotation >& xAnnotation, const Point& rPnt ) : SmartHdl( xTag, rPnt ) , mxAnnotation( xAnnotation ) , mxTag( dynamic_cast< AnnotationTag* >( xTag.get() ) ) { } // -------------------------------------------------------------------- AnnotationHdl::~AnnotationHdl() { } // -------------------------------------------------------------------- void AnnotationHdl::CreateB2dIAObject() { // first throw away old one GetRidOfIAObject(); if( mxAnnotation.is() ) { const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); const Point aTagPos( GetPos() ); basegfx::B2DPoint aPosition( aTagPos.X(), aTagPos.Y() ); const bool bFocused = IsFocusHdl() && pHdlList && (pHdlList->GetFocusHdl() == this); BitmapEx aBitmapEx( mxTag->CreateAnnotationBitmap(mxTag->isSelected()) ); BitmapEx aBitmapEx2; if( bFocused ) aBitmapEx2 = mxTag->CreateAnnotationBitmap(!mxTag->isSelected() ); if(pHdlList) { SdrMarkView* pView = pHdlList->GetView(); if(pView && !pView->areMarkHandlesHidden()) { SdrPageView* pPageView = pView->GetSdrPageView(); if(pPageView) { for(sal_uInt32 b = 0; b < pPageView->PageWindowCount(); b++) { // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b]; const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); SdrPaintWindow& rPaintWindow = rPageWindow.GetPaintWindow(); if(rPaintWindow.OutputToWindow() && rPageWindow.GetOverlayManager() ) { ::sdr::overlay::OverlayObject* pOverlayObject = 0; // animate focused handles if(bFocused) { const sal_uInt32 nBlinkTime = sal::static_int_cast(rStyleSettings.GetCursorBlinkTime()); pOverlayObject = new ::sdr::overlay::OverlayAnimatedBitmapEx(aPosition, aBitmapEx, aBitmapEx2, nBlinkTime, 0, 0, 0, 0 ); /* (sal_uInt16)(aBitmapEx.GetSizePixel().Width() - 1) >> 1, (sal_uInt16)(aBitmapEx.GetSizePixel().Height() - 1) >> 1, (sal_uInt16)(aBitmapEx2.GetSizePixel().Width() - 1) >> 1, (sal_uInt16)(aBitmapEx2.GetSizePixel().Height() - 1) >> 1); */ } else { pOverlayObject = new ::sdr::overlay::OverlayBitmapEx( aPosition, aBitmapEx, 0, 0 ); } rPageWindow.GetOverlayManager()->add(*pOverlayObject); maOverlayGroup.append(*pOverlayObject); } } } } } } } // -------------------------------------------------------------------- sal_Bool AnnotationHdl::IsFocusHdl() const { return sal_True; } // -------------------------------------------------------------------- bool AnnotationHdl::isMarkable() const { return false; } // -------------------------------------------------------------------- Pointer AnnotationHdl::GetSdrDragPointer() const { PointerStyle eStyle = POINTER_NOTALLOWED; if( mxTag.is() ) { if( mxTag->isSelected() ) { eStyle = POINTER_MOVE; } else { eStyle = POINTER_ARROW; } } return Pointer( eStyle ); } // ==================================================================== AnnotationTag::AnnotationTag( AnnotationManagerImpl& rManager, ::sd::View& rView, const Reference< XAnnotation >& xAnnotation, Color& rColor, int nIndex, const Font& rFont ) : SmartTag( rView ) , mrManager( rManager ) , mxAnnotation( xAnnotation ) , maColor( rColor ) , mnIndex( nIndex ) , mrFont( rFont ) , mnClosePopupEvent( 0 ) , mpListenWindow( 0 ) { } // -------------------------------------------------------------------- AnnotationTag::~AnnotationTag() { DBG_ASSERT( !mxAnnotation.is(), "sd::AnnotationTag::~AnnotationTag(), dispose me first!" ); Dispose(); } // -------------------------------------------------------------------- /** returns true if the AnnotationTag handled the event. */ bool AnnotationTag::MouseButtonDown( const MouseEvent& rMEvt, SmartHdl& /*rHdl*/ ) { if( !mxAnnotation.is() ) return false; bool bRet = false; if( !isSelected() ) { SmartTagReference xTag( this ); mrView.getSmartTags().select( xTag ); bRet = true; } /* if( rMEvt.IsLeft() && (rMEvt.GetClicks() == 2) ) { // double click; return true; } else */ if( rMEvt.IsLeft() && !rMEvt.IsRight() ) { Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); if( pWindow ) { maMouseDownPos = pWindow->PixelToLogic( rMEvt.GetPosPixel() ); if( mpListenWindow ) mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); mpListenWindow = pWindow; mpListenWindow->AddEventListener( LINK(this, AnnotationTag, WindowEventHandler)); } bRet = true; } return bRet; } // -------------------------------------------------------------------- /** returns true if the SmartTag consumes this event. */ bool AnnotationTag::KeyInput( const KeyEvent& rKEvt ) { if( !mxAnnotation.is() ) return false; sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); switch( nCode ) { case KEY_DELETE: mrManager.DeleteAnnotation( mxAnnotation ); return true; case KEY_DOWN: case KEY_UP: case KEY_LEFT: case KEY_RIGHT: return OnMove( rKEvt ); case KEY_ESCAPE: { SmartTagReference xThis( this ); mrView.getSmartTags().deselect(); return true; } case KEY_TAB: mrManager.SelectNextAnnotation(!rKEvt.GetKeyCode().IsShift()); return true; case KEY_RETURN: case KEY_SPACE: OpenPopup( true ); return true; default: return false; } } /** returns true if the SmartTag consumes this event. */ bool AnnotationTag::RequestHelp( const HelpEvent& /*rHEvt*/ ) { /* ::Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); if( mxAnnotation.is() && pWindow ) { OUString aHelpText( mrManager.GetHelpText( mxAnnotation ) ); RealPoint2D aPosition( mxAnnotation->getPosition() ); Point aPos( pWindow->LogicToPixel( Point( static_cast(aPosition.X * 100.0), static_cast(aPosition.Y * 100.0) ) ) ); Rectangle aRect( aPos, maSize ); if (Help::IsBalloonHelpEnabled()) Help::ShowBalloon( pWindow, aPos, aRect, aHelpText); else if (Help::IsQuickHelpEnabled()) Help::ShowQuickHelp( pWindow, aRect, aHelpText); return true; } */ return false; } /** returns true if the SmartTag consumes this event. */ bool AnnotationTag::Command( const CommandEvent& rCEvt ) { if ( rCEvt.GetCommand() == COMMAND_CONTEXTMENU ) { ::Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); if( pWindow ) { Rectangle aContextRect(rCEvt.GetMousePosPixel(),Size(1,1)); mrManager.ExecuteAnnotationContextMenu( mxAnnotation, pWindow, aContextRect ); return true; } } return false; } void AnnotationTag::Move( int nDX, int nDY ) { if( mxAnnotation.is() ) { if( mrManager.GetDoc()->IsUndoEnabled() ) mrManager.GetDoc()->BegUndo( String( SdResId( STR_ANNOTATION_UNDO_MOVE ) ) ); RealPoint2D aPosition( mxAnnotation->getPosition() ); aPosition.X += (double)nDX / 100.0; aPosition.Y += (double)nDY / 100.0; mxAnnotation->setPosition( aPosition ); if( mrManager.GetDoc()->IsUndoEnabled() ) mrManager.GetDoc()->EndUndo(); mrView.updateHandles(); } } bool AnnotationTag::OnMove( const KeyEvent& rKEvt ) { long nX = 0; long nY = 0; switch( rKEvt.GetKeyCode().GetCode() ) { case KEY_UP: nY = -1; break; case KEY_DOWN: nY = 1; break; case KEY_LEFT: nX = -1; break; case KEY_RIGHT: nX = 1; break; default: break; } if(rKEvt.GetKeyCode().IsMod2()) { OutputDevice* pOut = mrView.GetViewShell()->GetActiveWindow(); Size aLogicSizeOnePixel = (pOut) ? pOut->PixelToLogic(Size(1,1)) : Size(100, 100); nX *= aLogicSizeOnePixel.Width(); nY *= aLogicSizeOnePixel.Height(); } else { // old, fixed move distance nX *= 100; nY *= 100; } if( nX || nY ) { // move the annotation Move( nX, nY ); } return true; } // -------------------------------------------------------------------- void AnnotationTag::CheckPossibilities() { } // -------------------------------------------------------------------- sal_uLong AnnotationTag::GetMarkablePointCount() const { return 0; } // -------------------------------------------------------------------- sal_uLong AnnotationTag::GetMarkedPointCount() const { return 0; } // -------------------------------------------------------------------- sal_Bool AnnotationTag::MarkPoint(SdrHdl& /*rHdl*/, sal_Bool /*bUnmark*/ ) { sal_Bool bRet=sal_False; return bRet; } // -------------------------------------------------------------------- sal_Bool AnnotationTag::MarkPoints(const Rectangle* /*pRect*/, sal_Bool /*bUnmark*/ ) { sal_Bool bChgd=sal_False; return bChgd; } // -------------------------------------------------------------------- bool AnnotationTag::getContext( SdrViewContext& /*rContext*/ ) { return false; } // -------------------------------------------------------------------- void AnnotationTag::addCustomHandles( SdrHdlList& rHandlerList ) { if( mxAnnotation.is() ) { SmartTagReference xThis( this ); Point aPoint; AnnotationHdl* pHdl = new AnnotationHdl( xThis, mxAnnotation, aPoint ); pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); pHdl->SetPageView( mrView.GetSdrPageView() ); RealPoint2D aPosition( mxAnnotation->getPosition() ); Point aBasePos( static_cast(aPosition.X * 100.0), static_cast(aPosition.Y * 100.0) ); pHdl->SetPos( aBasePos ); rHandlerList.AddHdl( pHdl ); } } // -------------------------------------------------------------------- void AnnotationTag::disposing() { if( mpListenWindow ) { mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); } if( mnClosePopupEvent ) { Application::RemoveUserEvent( mnClosePopupEvent ); mnClosePopupEvent = 0; } mxAnnotation.clear(); ClosePopup(); SmartTag::disposing(); } // -------------------------------------------------------------------- void AnnotationTag::select() { SmartTag::select(); mrManager.onTagSelected( *this ); Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); if( pWindow ) { RealPoint2D aPosition( mxAnnotation->getPosition() ); Point aPos( static_cast(aPosition.X * 100.0), static_cast(aPosition.Y * 100.0) ); Rectangle aVisRect( aPos, pWindow->PixelToLogic(maSize) ); mrView.MakeVisible(aVisRect, *pWindow); } } // -------------------------------------------------------------------- void AnnotationTag::deselect() { SmartTag::deselect(); ClosePopup(); mrManager.onTagDeselected( *this ); } // -------------------------------------------------------------------- BitmapEx AnnotationTag::CreateAnnotationBitmap( bool bSelected ) { VirtualDevice aVDev; OUString sAuthor( getInitials( mxAnnotation->getAuthor() ) ); sAuthor += OUString( sal_Unicode( ' ' ) ); sAuthor += OUString::valueOf( (sal_Int32)mnIndex ); aVDev.SetFont( mrFont ); const int BORDER_X = 4; // pixels const int BORDER_Y = 4; // pixels maSize = Size( aVDev.GetTextWidth( sAuthor ) + 2*BORDER_X, aVDev.GetTextHeight() + 2*BORDER_Y ); aVDev.SetOutputSizePixel( maSize, sal_False ); Color aBorderColor( maColor ); if( bSelected ) { aBorderColor.Invert(); } else { if( maColor.IsDark() ) { aBorderColor.IncreaseLuminance( 32 ); } else { aBorderColor.DecreaseLuminance( 32 ); } } Point aPos; Rectangle aBorderRect( aPos, maSize ); aVDev.SetLineColor(aBorderColor); aVDev.SetFillColor(maColor); aVDev.DrawRect( aBorderRect ); aVDev.SetTextColor( maColor.IsDark() ? COL_WHITE : COL_BLACK ); aVDev.DrawText( Point( BORDER_X, BORDER_Y ), sAuthor ); return aVDev.GetBitmapEx( aPos, maSize ); } void AnnotationTag::OpenPopup( bool bEdit ) { if( !mxAnnotation.is() ) return; if( !mpAnnotationWindow.get() ) { ::Window* pWindow = dynamic_cast< ::Window* >( getView().GetFirstOutputDevice() ); if( pWindow ) { RealPoint2D aPosition( mxAnnotation->getPosition() ); Point aPos( pWindow->OutputToScreenPixel( pWindow->LogicToPixel( Point( static_cast(aPosition.X * 100.0), static_cast(aPosition.Y * 100.0) ) ) ) ); aPos.X() += 4; // magic! aPos.Y() += 1; Rectangle aRect( aPos, maSize ); mpAnnotationWindow.reset( new AnnotationWindow( mrManager, mrView.GetDocSh(), pWindow->GetWindow(WINDOW_FRAME) ) ); mpAnnotationWindow->InitControls(); mpAnnotationWindow->setAnnotation(mxAnnotation); sal_uInt16 nArrangeIndex = 0; Point aPopupPos( FloatingWindow::CalcFloatingPosition( mpAnnotationWindow.get(), aRect, FLOATWIN_POPUPMODE_RIGHT, nArrangeIndex ) ); Size aPopupSize( 320, 240 ); mpAnnotationWindow->SetPosSizePixel( aPopupPos, aPopupSize ); mpAnnotationWindow->DoResize(); mpAnnotationWindow->Show(); mpAnnotationWindow->GrabFocus(); mpAnnotationWindow->AddEventListener( LINK(this, AnnotationTag, WindowEventHandler)); } } if( bEdit && mpAnnotationWindow.get() ) mpAnnotationWindow->StartEdit(); } void AnnotationTag::ClosePopup() { if( mpAnnotationWindow.get() ) { mpAnnotationWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); mpAnnotationWindow->Deactivate(); mpAnnotationWindow.reset(); } } IMPL_LINK(AnnotationTag, WindowEventHandler, VclWindowEvent*, pEvent) { if( pEvent != NULL ) { ::Window* pWindow = pEvent->GetWindow(); if( pWindow ) { if( pWindow == mpAnnotationWindow.get() ) { if( pEvent->GetId() == VCLEVENT_WINDOW_DEACTIVATE ) { if( mnClosePopupEvent ) Application::RemoveUserEvent( mnClosePopupEvent ); mnClosePopupEvent = Application::PostUserEvent( LINK( this, AnnotationTag, ClosePopupHdl ) ); } } else if( pWindow == mpListenWindow ) { switch( pEvent->GetId() ) { case VCLEVENT_WINDOW_MOUSEBUTTONUP: { // if we stop pressing the button without a mouse move we open the popup mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); mpListenWindow = 0; if( mpAnnotationWindow.get() == 0 ) OpenPopup(false); } break; case VCLEVENT_WINDOW_MOUSEMOVE: { // if we move the mouse after a button down we wan't to start draging mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); mpListenWindow = 0; SdrHdl* pHdl = mrView.PickHandle(maMouseDownPos); if( pHdl ) { mrView.BrkAction(); const sal_uInt16 nDrgLog = (sal_uInt16)pWindow->PixelToLogic(Size(DRGPIX,0)).Width(); rtl::Reference< AnnotationTag > xTag( this ); SdrDragMethod* pDragMethod = new AnnotationDragMove( mrView, xTag ); mrView.BegDragObj(maMouseDownPos, NULL, pHdl, nDrgLog, pDragMethod ); } } break; case VCLEVENT_OBJECT_DYING: mpListenWindow = 0; break; } } } } return sal_True; } IMPL_LINK( AnnotationTag, ClosePopupHdl, void *, EMPTYARG ) { mnClosePopupEvent = 0; ClosePopup(); return 0; } } // end of namespace sd /* vim:set shiftwidth=4 softtabstop=4 expandtab: */