OpenOffice.org Forum at OOoForum.orgThe OpenOffice.org Forum
 
 [Home]   [FAQ]   [Search]   [Memberlist]   [Usergroups]   [Register
 [Profile]   [Log in to check your private messages]   [Log in

Draw: duplicate selected drawing shape

 
Post new topic   Reply to topic    OOoForum.org Forum Index -> OpenOffice.org Code Snippets
View previous topic :: View next topic  
Author Message
DannyB
Moderator
Moderator


Joined: 02 Apr 2003
Posts: 3991
Location: Lawrence, Kansas, USA

PostPosted: Wed Jan 21, 2004 1:13 pm    Post subject: Draw: duplicate selected drawing shape Reply with quote

This message describes code that duplicates a selected drawing shape via. the clipboard. Not only do we duplicate the shape, but we then reposition it so that it is adjacent to, and touching the original shape it came from.

The main four functions introduced here are:

DupSelectedShapeDown()
DupSelectedShapeUp()
DupSelectedShapeRight()
DupSelectedShapeLeft()

If you call one of these functions, then the following happens...
1. the top window is located.
2. Top window must be a drawing window, otherwise an error MsgBox is displayed.
3. Selected shape is located. There must be exactly one shape selected, otherwise an error MsgBox is displayed.
4. Copy selected shape to clipboard.
5. De-select everything. (Nothing selected)
6. Paste from clipboard. (Since nothing is selected to paste over, a new shape is pasted, and remains selected.)
7. Get the currently selected, newly pasted shape.
8. Reposition the shape so that it is positioned directly below, but touching the original shape. (Or positioned to right, left or above, depending on which subroutine was called.)

Since these four functions might be useful, you might want to put these four functions (and the other functions that they require) into a library under "soffice", and then make four toolbar buttons or four menu commands that activate these four functions. This way you could select a shape in Draw, and click DOWN, DOWN, DOWN, DOWN, and then have five shapes (the original + 4 duplicates), with each shape touching the previous shape. Perhaps later, I will provide an installer that installs and uninstalls these four functions. It would be the first Basic macro installer that I know of that would install toolbar and menu icons.

This code was first inspired by this topic...
http://www.oooforum.org/forum/viewtopic.php?t=4824
I'm not sure if this code is actually the appropriate solution to the user's problem, but it was fun to write, which is my primary motivation.


First item of business is how do we get the topmost drawing document?

Each subroutine begins with some code like this...

Code:
   ' Which document are we working on?
   ' Using this statement,
   '  we will work on the document that contains this macro.
'   oDrawDoc = ThisComponent
   
   ' Which document are we working on?
   ' Using this short block of statements,
   '  we will work on the front window, assuming it is a drawing.
   oDrawDoc = GetCurrentDrawingDocument()
   If IsNull( oDrawDoc ) Then
      Exit Sub
   EndIf


These are two different ways to assign the drawing document to the variable oDrawDoc. You would want to assign it from ThisComponent when you are debugging and developing these routines. If you are planning to deploy these four functions into the office with toolbar buttons, then you want to use the second technique, of calling GetCurrentDrawingDocument(), to get the current drawing document.

So how does GetCurrentDrawingDocument() actually work?

Code:
' Return the current active drawing document.
' If there is not a drawing document currently active,
'  then an error message is displayed,
'  unless you passed True to the optional bSilent parameter.
' Null is returned if there is not a currently active drawing document.
Function GetCurrentDrawingDocument( Optional bSilent ) As Object
   If IsMissing( bSilent ) Then
      bSilent = False
   EndIf
   
   oCurrentComponent = StarDesktop.getCurrentComponent()
   
   If Not IsNull( oCurrentComponent ) Then
      ' See if component is a document.
      ' Any com.sun.star.document.OfficeDocument supports XModel.
      ' Of course, we could have just checked for the service OfficeDocument,
      '  see the Drawing example below for how to check for a service.
      If HasUnoInterfaces( oCurrentComponent, "com.sun.star.frame.XModel" ) Then
         ' Now see if the document is a DrawingDocument.
         If oCurrentComponent.SupportsService( "com.sun.star.drawing.DrawingDocument" ) Then
            GetCurrentDrawingDocument() = oCurrentComponent
            Exit Function
         EndIf
      EndIf   
   EndIf
   
   If Not bSilent Then
      MsgBox( "The top window must be a drawing document." )
   EndIf
End Function


Here is how this function works. First get the current component from the desktop. (StarDesktop variable in OOo Basic. In other languages, you would need to get the Desktop object from the ServiceManager.) Then check to see that something was actually returned. If so, then check to see if that object supports the com.sun.star.frame.XModel interface. Any service which inherits from com.sun.star.document.OfficeDocument will support the XModel interface. So now we know that we've got an office document. If it supports XModel, then we can safely assume that the method SupportsService can be called. So finally, we call SupportsService to test if the document supports the com.sun.star.drawing.DrawingDocument service. If so, then we have obtained the topmost window, and it is a Drawing document.

The above function displays a MsgBox error if the top document is not a drawing. If you want to suppress this error message, then pass True to the optional bSilent parameter. Either way, if a drawing is not the topmost window, then nothing (null in Basic) is returned, which can be tested in OOoBasic with the IsNull() builtin function.

So here is what the duplicate function looks like...
Code:
' This macro could be asssigned to a key or menu item.
' What this does...
'   The currently selected shape is copied and pasted on top of itself.
'   The newly pasted shape is identical to the originally selected shape.
'   The new shape is moved down so that its top just touches the
'    bottom edge of the original shape.
' So you could think of this routine as a "Duplicate Down" action.
'
' How this macro works.
'   1. Find currently selected shape.
'      (There MUST be EXACTLY ONE shape selected.)
'   2. Copy selected shape to clipboard.
'   3. Deselect everything.
'   4. Paste shape back to drawing.  (No objects overwritten
'      because nothing is currently selected.)
'   5. Find currently selected shape. (The one just pasted.)
'   6. Reposition the shape's location.
Sub DupSelectedShapeDown()
   ' Which document are we working on?
   ' Using this statement,
   '  we will work on the document that contains this macro.
'   oDrawDoc = ThisComponent
   
   ' Which document are we working on?
   ' Using this short block of statements,
   '  we will work on the front window, assuming it is a drawing.
   oDrawDoc = GetCurrentDrawingDocument()
   If IsNull( oDrawDoc ) Then
      Exit Sub
   EndIf
   
   ' Get the currently selected single shape.
   ' Display an error message if there is not exactly one shape selected.
   oOriginalShape = GetCurrentlySelectedSingleShape( oDrawDoc )

   ' If there was exactly one shape selected...
   If Not IsNull( oOriginalShape ) Then
   
      ' Make a duplicate of the original shape.
      ' Assign the new duplicate shape to oNewShape.
      oNewShape = DrawingDuplicateShapeViaClipboard( oDrawDoc, oOriginalShape )
      
      ' Reposition the NEW shape to be just BELOW the original shape.
      oNewShape.Position = MakePoint( oOriginalShape.Position.X,_
                              oOriginalShape.Position.Y + oOriginalShape.Size.Height )
   EndIf
End Sub


It looks simple enough.
1. Get the current top drawing document. (Previously described.)
2. If a drawing is open as top window...
3. Get the single selected shape by calling GetCurrentlySelectedSingleShape().
4. Duplicate the shape by calling DrawingDuplicateShapeViaClipboard().
5. Then reposition the new shape below the original shape.

The other three functions for Up, Right and Left are extremely similar. They only differ by changing this statement...
Code:
      ' Reposition the NEW shape to be just BELOW the original shape.
      oNewShape.Position = MakePoint( oOriginalShape.Position.X,_
                              oOriginalShape.Position.Y + oOriginalShape.Size.Height )


In the DupSelectedShapeUp() function, use this instead...
Code:
      ' Reposition the NEW shape to be just ABOVE the original shape.
      oNewShape.Position = MakePoint( oOriginalShape.Position.X,_
                              oOriginalShape.Position.Y - oNewShape.Size.Height )


In the DupSelectedShapeRight() function, use this instead...
Code:
      ' Reposition the NEW shape to be just TO THE RIGHT OF the original shape.
      oNewShape.Position = MakePoint( oOriginalShape.Position.X + oOriginalShape.Size.Width,_
                              oOriginalShape.Position.Y )


In the DupSelectedShapeLeft() function, use this instead...
Code:
      ' Reposition the NEW shape to be just TO THE LEFT OF the original shape.
      oNewShape.Position = MakePoint( oOriginalShape.Position.X - oNewShape.Size.Width,_
                              oOriginalShape.Position.Y )



At this point, it remains to explain how these two functions work...
GetCurrentlySelectedSingleShape()
DrawingDuplicateShapeViaClipboard()


The GetCurrentlySelectedSingleShape() function is as follows...
Code:
' Find and return the currently selected drawing shape.
' There must be EXACTLY ONE shape selected.
' If there is exactly one shape selected
'   then it is returned.
' If there is NOT exactly one shape selected, then...
'  an error message is displayed, unless you passed True
'  to the optional bSilent parameter.
'  Null is returned if exactly one shape is not selected.
' The parameter oDrawDoc can be any of the document model, one of
'  the document's controllers, or frames.
Function GetCurrentlySelectedSingleShape( oDrawDoc, Optional bSilent ) As Object
   If IsMissing( bSilent ) Then
      bSilent = False
   EndIf
   
   oSelectedShapes = DrawingGetSelection( oDrawDoc )

   If oSelectedShapes.getCount() <= 0 Then
      If Not bSilent Then
         MsgBox( "No shapes are selected on the drawing.  You must select one shape." )
      EndIf
   
   ElseIf oSelectedShapes.getCount() > 1 Then
      If Not bSilent Then
         MsgBox( "More than one shape is selected on the drawing.  You must select only one shape." )
      EndIf
   
   Else
      ' If we get to here, there is exactly one shape selected.
      ' Get a reference to it.
      oSingleSelectedShape = oSelectedShapes.getByIndex( 0 )
      
      GetCurrentlySelectedSingleShape() = oSingleSelectedShape
   EndIf
End Function


You pass in a drawing document and it returns the single shape that is selected. If there is NOT EXACTLY one shape selected, then the function returns nothing which can be tested using the IsNull() function. Also, an error MsgBox is displayed unless you passed True to the optional bSilent parameter.

Worth noting is that this function requires the drawing document as its first parameter. It is okay if you pass in any of: the document model, one of the document's controllers, or one of the controller's frames.

This function makes use of the DrawingGetSelection() function. That function returns a collection of shapes that are selected on the drawing. (Even though the API can return null when no shapes are selected, this always returns a shape collection, even an empty one.) You can call getCount() on the collection to determine how many shapes are selected (which might be zero). You can also call getByIndex() to get each of the shapes on the drawing which are selected (with little green knobs around the shape).

As you can see, GetCurrentlySelectedSingleShape() calls getCount() on the returned shape collection to see if no shapes, or too many shapes are selected. If only one shape is selected, then it calls getByIndex(0) to get the only selected shape.

The DrawingGetSelection() function is really part of a group of three related functions. Here is their implementation.
Code:
' Select nothing on a drawing document.
' Parameters:
'   oDrawDocCtrl
'      -   The document controller.
'         Note: is okay if you pass in either the
'          document model or frame instead of a controller.
Sub DrawingSelectNothing( ByVal oDrawDocCtrl )
   ' Select nothing -- i.e. an empty collection of shapes.
   DrawingSelectShapes( oDrawDocCtrl, createUnoService( "com.sun.star.drawing.ShapeCollection" ) )
End Sub

' Select some shapes on a drawing document.
' Parameters:
'   oDrawDocCtrl
'      -   The document controller.
'         Note: is okay if you pass in either the
'          document model or frame instead of a controller.
'   oShapes
'      -   A collection of shapes implementing com.sun.star.drawing.ShapeCollection.
'         As a convenience, if you pass in a *single* shape object,
'          this routine will encapsulate it into a shape collection
'          of just the single shape.  So you can easily call this
'          to just select one shape.
Sub DrawingSelectShapes( ByVal oDrawDocCtrl, ByVal oShapes )
   ' If they gave us the incorrect parameter...
   If Not HasUnoInterfaces( oDrawDocCtrl, "com.sun.star.frame.XController" ) Then
      ' Be sure that we've got the document frame.
      ' Someone might have passed us the document model or one of
      '  its controller's.
      oDrawDocCtrl = GetDocumentController( oDrawDocCtrl )
   EndIf
   
   ' If they gave us a shape instead of a shape group...
   If oShapes.SupportsService( "com.sun.star.drawing.Shape" ) Then
      ' ...then create a shape collection
      oShapeCollection = createUnoService( "com.sun.star.drawing.ShapeCollection" )
      ' and add the one shape they gave us to it.
      oShapeCollection.add( oShapes ) ' add the one shape to the collection
   
   ' If they gave us a shape collection, as they were supposed to...
   ElseIf HasUnoInterfaces( oShapes, "com.sun.star.drawing.XShapes" ) Then
      ' Then they gave us what we need.
      oShapeCollection = oShapes
   
   ' If they didn't give us either a shape or shape collection...
   Else
      ' Just create an empty shape collection of no shapes.
      ' Thus, nothing will be selected on the drawing.
      oShapeCollection = createUnoService( "com.sun.star.drawing.ShapeCollection" )
   EndIf
   
   ' Select the shapes.
   oDrawDocCtrl.select( oShapeCollection )
End Sub

' Get the collection of shapes on a drawing.
' This returns a com.sun.star.drawing.ShapeCollection,
'   whose getCount() may be zero thus indicating that
'   there are no shapes to getByIndex(), and thus nothing selected.
' Parameters:
'   oDrawDocCtrl
'      -   The document controller.
'         Note: is okay if you pass in either the
'          document model or frame instead of a controller.
Function DrawingGetSelection( ByVal oDrawDocCtrl )
   ' If they gave us the incorrect parameter...
   If Not HasUnoInterfaces( oDrawDocCtrl, "com.sun.star.frame.XController" ) Then
      ' Be sure that we've got the document frame.
      ' Someone might have passed us the document model or one of
      '  its controller's.
      oDrawDocCtrl = GetDocumentController( oDrawDocCtrl )
   EndIf
   
   oSelectedShapes = oDrawDocCtrl.getSelection()
   
   ' If nothing was returned...
   If IsEmpty( oSelectedShapes ) Then
      ' Then for the convenience of the caller,
      '  return an empty collection of shapes.
      oSelectedShapes = createUnoService( "com.sun.star.drawing.ShapeCollection" )
   EndIf
   
   DrawingGetSelection() = oSelectedShapes
End Function



The DrawingSelectNothing() function is simple enough. Call it, passing the drawing document, and it makes sure that nothing on the drawing is selected. It deselects everything.

The DrawingSelectShapes() function is also simple. Call it, passing a drawing document and a shape or collection of shapes. It makes that shape or collection of shapes be selected. (That is, the selected shape(s) get little green knobs around them.)

Don't know how to create a collection of shapes? Look at DrawingSelectShapes(). If you pass it a single shape, it creates a collection, and adds the single shape to the collection. A shape collection, even of only one shape, must always be passed to the document controller's select() method.

The DrawingGetSelection() function is also simple. Call it, passing the drawing document, and it returns a shape collection of the selected shapes. You can call getCount() and getByIndex() (and other methods) on the shape collection. As you can see, the returned shape collection is the service com.sun.star.drawing.ShapeCollection.



Finally, we come to the function DrawingDuplicateShapeViaClipboard(). This is the one that duplicates a shape. The shape is duplicated by copying and pasting via. the clipboard.

Code:
' Using the clipboard, make a duplicate of any shape which
'  is passed in as a parameter.
' How this macro works.
'   1. Select the shape we want to duplicate.
'   2. Copy selected shape to clipboard.
'   3. Deselect everything.
'   4. Paste shape back to drawing.  (No objects overwritten
'      because nothing is currently selected.)
'   5. Find currently selected shape. (The one just pasted.)
'   6. Return the currently selected shape.
' The parameter oDrawDoc can be any of the document model, one of
'  the document's controllers, or frames.
Function DrawingDuplicateShapeViaClipboard( oDrawDoc, oShape )
   ' Make the original shape be the only thing selected.
   DrawingSelectShapes( oDrawDoc, oShape )

   ' Copy the shape.
   ClipboardCopy( oDrawDoc )
   
   ' Make sure nothing is currently selected.
   ' (So that pasting doesn't paste over the currently selected shape.)
   DrawingSelectNothing( oDrawDoc )
   
   ' Paste the shape copied earlier.
   ClipboardPaste( oDrawDoc )
   
   ' Get the currently selected shapes on the drawing.
   ' The only thing selected is the shape we just pasted.
   oSelectedShapes = DrawingGetSelection( oDrawDoc )
   ' Blindly assume that there is one shape selected
   '  and retrieve it.
   oNewShape = oSelectedShapes.getByIndex( 0 )
   
   DrawingDuplicateShapeViaClipboard() = oNewShape
End Function


Call DrawingDuplicateShapeViaClipboard(), passing it a drawing document and a shape. It copies and pastes the shape. It then reteurns the new shape as the function result. Note that the new shape is exactly on top of the original shape, so you cannot visually see that anything changed on the drawing, unless you reposition the new or the old shape.




This code makes use of subroutines introduced in these two previous messages.

Making the Dispatcher easier to use
http://www.oooforum.org/forum/viewtopic.php?t=5058
Subroutines needed...
ClipboardPaste()
ClipboardCopy()
DocumentDispatch()

Document model, controller and frame
http://www.oooforum.org/forum/viewtopic.php?t=5057
Subroutines needed...
GetDocumentController()
GetDocumentFrame()


The above code also makes use of a minor function...
Code:
'----------
'   Create and return a new Point object.
'
'   This is syntax sugar to make it easy to
'    create a com.sun.star.awt.Point object.
'
Function MakePoint( x As Long, y As Long ) As com.sun.star.awt.Point
   Dim aPoint As New com.sun.star.awt.Point
   aPoint.x = x
   aPoint.y = y
   MakePoint() = aPoint
End Function



A complete working version of the four primary functions introduced here can be downloaded from...
http://kosh.datateamsys.com/~danny/OOo/Examples/Draw/
Download the document named "DupSelectedShape" that has the highest revision number. This document has four buttons. Clicking the buttons makes it easy to see how these routines are useful.
_________________
Want to make OOo Drawings like the colored flower design to the left?
Back to top
View user's profile Send private message
DannyB
Moderator
Moderator


Joined: 02 Apr 2003
Posts: 3991
Location: Lawrence, Kansas, USA

PostPosted: Tue Jun 01, 2004 11:15 am    Post subject: Reply with quote

FYI....

the subroutine above named DrawingSelectShapes() has been revised.
_________________
Want to make OOo Drawings like the colored flower design to the left?
Back to top
View user's profile Send private message
DannyB
Moderator
Moderator


Joined: 02 Apr 2003
Posts: 3991
Location: Lawrence, Kansas, USA

PostPosted: Wed Jul 14, 2004 6:50 am    Post subject: Reply with quote

If you liked this thread, then see this for more....

Draw Examples
http://www.oooforum.org/forum/viewtopic.php?t=10795
_________________
Want to make OOo Drawings like the colored flower design to the left?
Back to top
View user's profile Send private message
gamp-ba
General User
General User


Joined: 06 Feb 2011
Posts: 11

PostPosted: Thu Jun 30, 2011 9:55 am    Post subject: semms, doesn't work any longer Reply with quote

DannyB, I tried the DrawingGetSelection-Statement, you use in the example, but it does not work at all

I also tried the statements

oDrawDocCtrl = oDrawDoc.getCurrentController()
oSelectedShapes = oDrawDocCtrl.getSelection()

from another thread, you reported. The statements work, but the following
oSelectedShapes.getcount()

doesn't work either.

are there any changes in the interface since that time? But I am sorry, I didn't find any answer
thanks for looking for an answer
Back to top
View user's profile Send private message
Matherion
Newbie
Newbie


Joined: 03 Aug 2011
Posts: 1

PostPosted: Wed Aug 03, 2011 1:00 am    Post subject: Writer? Reply with quote

Dear DannyB and others,

I found your code snippet and tried to use it for Writer, but of course, it requires Draw. I don't know how to adjust it though . . . I tried removing the part where it verifies that the topmost document is a Draw object, but of course that didn't work Smile

It would be great if this could work in Writer too, as it's very useful (for me at least) if you're able to make quick and dirty diagrams and schema's in Writer - I need that a lot. Being forced to go back to another program (draw or gimp) for these drawings is quite a pain, and basically forces me to move back to MS again :-/

I don't even need the objects to be located in a certain position - I just need to be able to duplicate them, without having to do CTRL-C, click somewhere else to deselect everything, CTRL-V.

If this snippet can be easily adjusted to work in Writer as well, that would be great! If not, may not be worth the effort, don't know how many people would find such functionality useful . . .

At any rate, thanks for having made the original version, and making this kind of stuff in general Smile
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    OOoForum.org Forum Index -> OpenOffice.org Code Snippets All times are GMT - 8 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group