1 /*
2  * This file is part of the LibreOffice project.
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  *
8  * This file incorporates work covered by the following license notice:
9  *
10  *   Licensed to the Apache Software Foundation (ASF) under one or more
11  *   contributor license agreements. See the NOTICE file distributed
12  *   with this work for additional information regarding copyright
13  *   ownership. The ASF licenses this file to you under the Apache
14  *   License, Version 2.0 (the "License"); you may not use this file
15  *   except in compliance with the License. You may obtain a copy of
16  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
17  */
18 
19 package complex.sfx2.undo;
20 
21 import com.sun.star.awt.Rectangle;
22 import com.sun.star.document.XUndoManager;
23 import com.sun.star.document.XUndoManagerSupplier;
24 import com.sun.star.document.XUndoAction;
25 import com.sun.star.awt.Point;
26 import com.sun.star.awt.Size;
27 import com.sun.star.beans.XPropertySet;
28 import com.sun.star.drawing.CircleKind;
29 import com.sun.star.drawing.XDrawPages;
30 import com.sun.star.drawing.XDrawPagesSupplier;
31 import com.sun.star.drawing.XShape;
32 import com.sun.star.drawing.XShapes;
33 import com.sun.star.lang.XMultiServiceFactory;
34 import com.sun.star.uno.UnoRuntime;
35 import org.openoffice.test.tools.DocumentType;
36 import static org.junit.Assert.*;
37 
38 /**
39  * implements the {@link DocumentTest} interface on top of a drawing document
40  */
41 public abstract class DrawingOrPresentationDocumentTest extends DocumentTestBase
42 {
DrawingOrPresentationDocumentTest( XMultiServiceFactory i_orb, final DocumentType i_docType )43     public DrawingOrPresentationDocumentTest( XMultiServiceFactory i_orb, final DocumentType i_docType ) throws com.sun.star.uno.Exception
44     {
45         super( i_orb, i_docType );
46     }
47 
initializeDocument()48     public void initializeDocument() throws com.sun.star.uno.Exception
49     {
50         // remove all shapes - Impress has two default shapes in a new doc; just get rid of them
51         final XShapes firstPageShapes = getFirstPageShapes();
52         while ( firstPageShapes.getCount() > 0 )
53             firstPageShapes.remove( UnoRuntime.queryInterface( XShape.class, firstPageShapes.getByIndex( 0 ) ) );
54     }
55 
doSingleModification()56     public void doSingleModification() throws com.sun.star.uno.Exception
57     {
58         // add a simple centered shape to the first page
59         Rectangle pagePlayground = impl_getFirstPagePlayground();
60         impl_createCircleShape(
61             ( pagePlayground.X + ( pagePlayground.Width - BIG_CIRCLE_SIZE ) / 2 ),
62             ( pagePlayground.Y + ( pagePlayground.Height - BIG_CIRCLE_SIZE ) / 2 ),
63             BIG_CIRCLE_SIZE,
64             FILL_COLOR
65         );
66     }
67 
verifyInitialDocumentState()68     public void verifyInitialDocumentState() throws com.sun.star.uno.Exception
69     {
70         final XShapes firstPageShapes = getFirstPageShapes();
71         assertEquals( "there should be no shapes at all", 0, firstPageShapes.getCount() );
72     }
73 
verifySingleModificationDocumentState()74     public void verifySingleModificationDocumentState() throws com.sun.star.uno.Exception
75     {
76         final XShapes firstPageShapes = getFirstPageShapes();
77         assertEquals( "there should be one shape, not more, not less", 1, firstPageShapes.getCount() );
78 
79         final Object shape = firstPageShapes.getByIndex(0);
80         verifyShapeGeometry( shape, BIG_CIRCLE_SIZE, BIG_CIRCLE_SIZE );
81         final XPropertySet shapeProps = UnoRuntime.queryInterface( XPropertySet.class, shape );
82         assertEquals( "wrong circle type", CIRCLE_TYPE.getValue(), ((CircleKind)shapeProps.getPropertyValue( "CircleKind" )).getValue() );
83         //assertEquals( "wrong circle fill color", FILL_COLOR, ((Integer)shapeProps.getPropertyValue( "FillColor" )).intValue() );
84             // disable this particular check: A bug in the drawing layer API restores the FillColor to its
85             // default value upon re-insertion. This is issue #i115080#
86     }
87 
doMultipleModifications()88     public int doMultipleModifications() throws com.sun.star.uno.Exception
89     {
90         // add a simple centered shape to the first page
91         Rectangle pagePlayground = impl_getFirstPagePlayground();
92         impl_createCircleShape(
93             pagePlayground.X,
94             pagePlayground.Y,
95             SMALL_CIRCLE_SIZE,
96             ALTERNATE_FILL_COLOR
97         );
98         impl_createCircleShape(
99             pagePlayground.X + pagePlayground.Width - SMALL_CIRCLE_SIZE,
100             pagePlayground.Y,
101             SMALL_CIRCLE_SIZE,
102             ALTERNATE_FILL_COLOR
103         );
104         impl_createCircleShape(
105             pagePlayground.X,
106             pagePlayground.Y + pagePlayground.Height - SMALL_CIRCLE_SIZE,
107             SMALL_CIRCLE_SIZE,
108             ALTERNATE_FILL_COLOR
109         );
110         impl_createCircleShape(
111             pagePlayground.X + pagePlayground.Width - SMALL_CIRCLE_SIZE,
112             pagePlayground.Y + pagePlayground.Height - SMALL_CIRCLE_SIZE,
113             SMALL_CIRCLE_SIZE,
114             ALTERNATE_FILL_COLOR
115         );
116         return 4;
117     }
118 
impl_createCircleShape( final int i_x, final int i_y, final int i_size, final int i_color )119     private void impl_createCircleShape( final int i_x, final int i_y, final int i_size, final int i_color ) throws com.sun.star.uno.Exception
120     {
121         final XPropertySet shapeProps = getDocument().createInstance( "com.sun.star.drawing.EllipseShape", XPropertySet.class );
122         shapeProps.setPropertyValue( "CircleKind", CIRCLE_TYPE );
123         shapeProps.setPropertyValue( "FillColor", i_color );
124 
125         final XShape shape = UnoRuntime.queryInterface( XShape.class, shapeProps );
126         final Size shapeSize = new Size( i_size, i_size );
127         shape.setSize( shapeSize );
128         final Point shapePos = new Point( i_x, i_y );
129         shape.setPosition( shapePos );
130 
131         final XShapes pageShapes = UnoRuntime.queryInterface( XShapes.class, getFirstPageShapes() );
132         pageShapes.add( shape );
133 
134         // Sadly, Draw/Impress currently do not create Undo actions for programmatic changes to the document.
135         // Which renders the test here slightly useless ... unless we fake the Undo actions ourself.
136         final XUndoManagerSupplier suppUndoManager = UnoRuntime.queryInterface( XUndoManagerSupplier.class, getDocument().getDocument() );
137         final XUndoManager undoManager = suppUndoManager.getUndoManager();
138         undoManager.addUndoAction( new ShapeInsertionUndoAction( shape, pageShapes ) );
139     }
140 
impl_getFirstPagePlayground()141     private Rectangle impl_getFirstPagePlayground() throws com.sun.star.uno.Exception
142     {
143         final XShapes firstPageShapes = getFirstPageShapes();
144         final XPropertySet firstPageProps = UnoRuntime.queryInterface( XPropertySet.class, firstPageShapes );
145         final int pageWidth = ((Integer)firstPageProps.getPropertyValue( "Width" )).intValue();
146         final int pageHeight = ((Integer)firstPageProps.getPropertyValue( "Height" )).intValue();
147         final int borderLeft = ((Integer)firstPageProps.getPropertyValue( "BorderLeft" )).intValue();
148         final int borderTop = ((Integer)firstPageProps.getPropertyValue( "BorderTop" )).intValue();
149         final int borderRight = ((Integer)firstPageProps.getPropertyValue( "BorderRight" )).intValue();
150         final int borderBottom = ((Integer)firstPageProps.getPropertyValue( "BorderBottom" )).intValue();
151         return new Rectangle( borderLeft, borderTop, pageWidth - borderLeft - borderRight, pageHeight - borderTop - borderBottom );
152     }
153 
154     /**
155      * returns the XShapes interface of the first page of our drawing document
156      */
getFirstPageShapes()157     private XShapes getFirstPageShapes() throws com.sun.star.uno.Exception
158     {
159         final XDrawPagesSupplier suppPages = UnoRuntime.queryInterface( XDrawPagesSupplier.class, getDocument().getDocument() );
160         final XDrawPages pages = suppPages.getDrawPages();
161         return UnoRuntime.queryInterface( XShapes.class, pages.getByIndex( 0 ) );
162     }
163 
164     /**
165      * verifies the given shape has the given size
166      */
verifyShapeGeometry( final Object i_shapeObject, final int i_expectedWidth, final int i_expectedHeight )167     private void verifyShapeGeometry( final Object i_shapeObject, final int i_expectedWidth, final int i_expectedHeight )
168     {
169         final XShape shape = UnoRuntime.queryInterface( XShape.class, i_shapeObject );
170         final Size shapeSize = shape.getSize();
171         assertEquals( "unexpected shape width", i_expectedWidth, shapeSize.Width );
172         assertEquals( "unexpected shape height", i_expectedHeight, shapeSize.Height );
173     }
174 
175     private static class ShapeInsertionUndoAction implements XUndoAction
176     {
ShapeInsertionUndoAction( final XShape i_shape, final XShapes i_shapeCollection )177         ShapeInsertionUndoAction( final XShape i_shape, final XShapes i_shapeCollection )
178         {
179             m_shape = i_shape;
180             m_shapeCollection = i_shapeCollection;
181         }
182 
getTitle()183         public String getTitle()
184         {
185             return "insert shape";
186         }
187 
undo()188         public void undo()
189         {
190             m_shapeCollection.remove( m_shape );
191         }
192 
redo()193         public void redo()
194         {
195             m_shapeCollection.add( m_shape );
196         }
197 
198         private final XShape m_shape;
199         private final XShapes m_shapeCollection;
200     }
201 
202     private static CircleKind   CIRCLE_TYPE = CircleKind.FULL;
203     private static int          FILL_COLOR = 0xCC2244;
204     private static int          ALTERNATE_FILL_COLOR = 0x44CC22;
205     private static int          BIG_CIRCLE_SIZE = 5000;
206     private static int          SMALL_CIRCLE_SIZE = 2000;
207 }
208