[BASIC] Open Writer graphics group with right-click in Draw

Creating a macro - Writing a Script - Using the API (OpenOffice Basic, Python, BeanShell, JavaScript)
Post Reply
sdaau
Posts: 5
Joined: Tue Jan 05, 2010 1:29 am

[BASIC] Open Writer graphics group with right-click in Draw

Post by sdaau »

Hi all,

I always end up needing to open a grouped graphic in OOO Draw, and I always spend hours looking for how can I do it from Writer in single click, without opening Draw separately, and copy pasting stuff in it. So here is a BASIC macro which should do it (tested on Ubuntu 9.04 + OOO 3.0)
opindraw.png
  • Simply copy the code below in a new BASIC module, and enter the path to that module as Const MACRO_LOC (keep the dot at end)
  • Then, for each document you open in Writer, you have to register the macro separately, by running its registerContextMenuInterceptor sub. (I couldn't figure out how to 'install' it for all Writer documents)
  • Once its registered, whenever you right-click on a graphic or a group, you will get a context menu entry, "Open SvxShapeCollection in Draw"
  • When you select that option, the graphics selection in Writer is grouped, Draw is automatically opened with a page size matching the Writer group (good for exporting), and the Writer group is pasted in Draw.
  • A toolbar button is show in Draw to close and finish. Upon close from this button, you have the option of updating your originating Writer content with the one currently in Draw (which is possibly edited). (Upon updating the content, the Draw content is again re-grouped in a single group.)
The script was put together from many sources - many of original comments still remain (sorry for the mess :) )

Well, hope someone can find this useful,
Cheers!

Code: Select all

REM  *****  BASIC  *****
REM FROM http://codesnippets.services.openoffice.org/Office/Office.ContexMenuInterceptor.snip?passthru=1?passthru=1?passthru=1
REM FOR ClipboardCopy, SelectAll YOU ALSO NEED UtilAPI module  http://www.oooforum.org/forum/viewtopic.phtml?p=73054#73054

REM  *****  BASIC  *****

Option Explicit

Global oDocView As Object
Global oContextMenuInterceptor As Object

Global oStore As Object
Global oPropSetRegistry As Object

Global oSelection As Object 

Global dDoc As Object
Global orDoc As Object
Global selGroupWriter As Object

Const MNU_PREFIX = "pmxMenu_"
Const MACRO_LOC = "Standard.MyModuleOpinDraw."

Sub reregisterContextMenuInterceptor
	releaseContextMenuInterceptor
	registerContextMenuInterceptor
End Sub

'_______________________________________________________________________________
Sub registerContextMenuInterceptor
	
	InitMenuFactory

	oDocView = ThisComponent.CurrentController
	oContextMenuInterceptor = _
	CreateUnoListener("ThisDocument_", "com.sun.star.ui.XContextMenuInterceptor")
	
	oDocView.registerContextMenuInterceptor(oContextMenuInterceptor)
	
End Sub


'_______________________________________________________________________________
Sub releaseContextMenuInterceptor

	On Error Resume Next
	oDocView.releaseContextMenuInterceptor(oContextMenuInterceptor)
	
	TerminateMenuFactory
	
End Sub


'_______________________________________________________________________________
Function ThisDocument_notifyContextMenuExecute(ContextMenuExecuteEvent As Object) As Variant
Dim oSrcWin As Object
Dim oExePoint As Object
Dim oATContainer As Object
' move to Global, we'll need it there; macros from the custom menu entry are called by 
' a string URL, and hence we cannot pass a reference to oSelection to our click handler
'Dim oSelection As Object 

Dim oMenuItem As Object
Dim I As Integer

BasicLibraries.loadLibrary("XrayTool")

	With ContextMenuExecuteEvent
	
		'contains the window where the context
		'menu has been requested 
		oSrcWin = .SourceWindow
		
		'contains the position the context menu
		'will be executed at (css.awt.Point)
		oExePoint = .ExecutePosition
		
		'enables the access to the menu content. 
		'The implementing object has to support the
		'service ActionTriggerContainer
		oATContainer = .ActionTriggerContainer 
		
		'provides the current selection
		'inside the source window
		oSelection = .Selection 				
	End With


	'remove all menu entries:
	'For I = oATContainer.Count - 1 To 0 Step -1
	'	oATContainer.removeByIndex(I)
	'Next I

	' react only on graphics objects / groups
	'If (oSelection.Selection.ImplementationName = "com.sun.star.drawing.SvxShapeCollection") Then
	' rather, check if SvxShapeCollection is InStr ImplementationName ... (last ', 0' - case sensitive)
	Dim imnm As String 
	Dim	imns As String
	Dim	cpos As Integer ' must be here, else crash on OOO 3.0/Ubuntu !
	
	imnm = oSelection.Selection.ImplementationName
	imns = "SvxShapeCollection"
	cpos = InStr(1, imnm, imns, 1) ' at least startPos must be 1 !! 
	If cpos > 0 Then
		
		'add some context menu entry
		oMenuItem = GetSimpleMenuItem("Entry1", "Open "+imns+" in Draw", "macro:///"+MACRO_LOC+"openDrawClickHandler")
		oATContainer.insertByIndex(0, oMenuItem)
	
		'xray oSelection
			
		'oMenuItem = GetMenuSeparator("Entry2") ' separator, so it will not accept other arguments
		'oMenuItem = GetSimpleMenuItem("Entry2", oSelection.Selection.ImplementationName, "")
	
		'oATContainer.insertByIndex(1, oMenuItem)
		
		'oMenuItem = GetSimpleMenuItem("Entry3", "Run the Snippet Creator", "macro:///SnippetCreator.Main.Main")
		'oATContainer.insertByIndex(2, oMenuItem)
	
	End If


' POSSIBLE RESULTS FOR THIS FUNCTION
' This function must result one of the following values:

' com.sun.star.ui.ContextMenuInterceptorAction.IGNORED
'	the XContextMenuInterceptor has ignored the call. 
'	The next registered XContextMenuInterceptor should be notified.

' com.sun.star.ui.ContextMenuInterceptorAction.CANCELLED
'	the context menu must not be executed. 
'	The next registered XContextMenuInterceptor should not be notified.

' com.sun.star.ui.ContextMenuInterceptorAction.EXECUTE_MODIFIED
'	the menu has been modified and should be executed
'	without notifying the next registered XContextMenuInterceptor.

' com.sun.star.ui.ContextMenuInterceptorAction.CONTINUE_MODIFIED
'	the menu has been modified and the next registered
'	XContextMenuInterceptor should be notified.

	ThisDocument_notifyContextMenuExecute = _
		com.sun.star.ui.ContextMenuInterceptorAction.EXECUTE_MODIFIED

End Function


'_______________________________________________________________________________
'	MENU FACTORY ROUTINES
'_______________________________________________________________________________
Sub InitMenuFactory()

	oStore = CreateUnoService("com.sun.star.ucb.Store")
	oPropSetRegistry = oStore.createPropertySetRegistry("")

End Sub


'_______________________________________________________________________________
Sub TerminateMenuFactory()
Dim mNames()
Dim sName As String
Dim I As Integer

	mNames() = oPropSetRegistry.getElementNames
	
	For I = LBound(mNames()) To UBound(mNames())
		sName = mNames(I)
		
		If Left(sName, Len(MNU_PREFIX)) = MNU_PREFIX Then
			oPropSetRegistry.removePropertySet ( sName ) 
		End If
	Next I
	
	oPropSetRegistry.dispose
	oStore.dispose
	
End Sub

'_______________________________________________________________________________
' Sorry: menu icon and sub-menues not supported
Function GetSimpleMenuItem( sName As String, sText As String, _
sCommandUrl As String, Optional sHelpUrl As String ) As Object

Dim oPropSet As Object
Dim sInternalName As String

	sInternalName = MNU_PREFIX & sName
	If oPropSetRegistry.hasByName(sInternalName) Then
		oPropSetRegistry.removePropertySet(sInternalName)
	End If
		
	oPropSet = oPropSetRegistry.openPropertySet(sInternalName, True)

	oPropSet.addProperty("Text", 0, sText)
	oPropSet.addProperty("CommandURL", 0, sCommandUrl)

	If Not IsMissing(sHelpUrl) Then
		oPropSet.addProperty("HelpURL", 0, sHelpUrl)
	End If
		
	GetSimpleMenuItem = oPropSet
	
End Function


'_______________________________________________________________________________
Function GetMenuSeparator( sName As String ) As Object

Dim oPropSet As Object
Dim sInternalName As String
Dim iSeparatorType As Integer

	sInternalName = MNU_PREFIX & sName
	If oPropSetRegistry.hasByName(sInternalName) Then
		oPropSetRegistry.removePropertySet(sInternalName)
	End If
	
	oPropSet = oPropSetRegistry.openPropertySet(sInternalName, True)
	
	'constant group com.sun.star.ui.ActionTriggerSeparatorType not supported?
	'unfortunately, the only separator-type working is the "SPACE"
	'regardless for the iSeparatorType passed...
	iSeparatorType = 1
	oPropSet.addProperty("SeparatorType", 0, iSeparatorType)

	GetMenuSeparator = oPropSet
	
End Function

'_______________________________________________________________________________
'	CLICK HANDLER
'_______________________________________________________________________________
Sub openDrawClickHandler()

'	msgbox("openDrawClickHandler! " + oSelection.Selection.ImplementationName)
'	xray oSelection
	
	' OK, right-click has been clicked - over a graphics/group selection, and we have the selection
	' copy selection, open Draw, paste the selection there. 
	
	' first global link to ThisComponent - the originating Writer document
	orDoc = ThisComponent
	
	' ClipboardCopy argument refers to Doc = ThisComponent, and thus 
	' automatically the selection there? but can we try to put link to selection instead?
	ClipboardCopy( orDoc ) 
	
	' find size of selection group; '	サービス名を取得しているとバージョンによって値が変化するので好ましくない。
	' http://hermione.s41.xrea.com/pukiwiki/pukiwiki.php?OOobbs%2F50
	' see also there for how to check for shapes in different OOO versions 
	Dim selShapes
	Dim selPage
	' Dim selGroup ' need it global, selGroupWriter
	Dim selW
	Dim selH
	selShapes = orDoc.getCurrentSelection()
	selPage = orDoc.CurrentController.Model.getDrawPage()

	' autogroup, if not grouped already 
	if selShapes.Count = 1 then			 '--> 1つの時はグループ化しない。(主として Writer への対応措置)
		selGroupWriter = selShapes.getByIndex(0)
	else
		' 選択されたオブジェクトをグループ化
		selGroupWriter = selPage.group(selShapes)
	end if
	' 選択されたオブジェクトの情報表示
	selW = selGroupWriter.Size.Width
	selH = selGroupWriter.Size.Height
	
	' open Draw
	'Dim dDoc ' must have this in Ubuntu! else "variable not defined" trouble.. - need it global
	dDoc = StarDesktop.loadComponentFromURL( "private:factory/sdraw", "_blank", 0, Array() ) 
	'xray dDoc
	
	' first make the page size of the draw doc match the selection
	
	' http://www.pitonyak.org/AndrewMacro.pdf
	'dFrame = dDoc.getCurrentController().Frame
	'dFramePosSize = dFrame.getComponentWindow.PosSize
	''dWindowHeight = dFramePosSize.Height
	''dWindowWidth = dFramePosSize.Width
	
	' http://hektor.umcs.lublin.pl/~mikosmul/computing/articles/openoffice-macros.html
	'Dim oStyle
	'oStyle = dDoc.StyleFamilies.getByName("PageStyles").getByName("Default")
	' units of 1/1000 cm : 21000 + 29700
	'oStyle.Width = 2100
	'oStyle.Height = 2970
	
	
	' this works to control the page size.. 
	dDoc.CurrentController.CurrentPage.Width = selW
	dDoc.CurrentController.CurrentPage.Height = selH

	' show the 'special' close button for returning to Writer 
	initToolbar( dDoc )
		
	ClipboardPaste( dDoc ) 
End Sub



'_______________________________________________________________________________
'	TOOLBARS
'_______________________________________________________________________________
' http://user.services.openoffice.org/en/forum/viewtopic.php?f=21&t=4352
' http://www.mail-archive.com/dev@api.openoffice.org/msg04779.html

'define the strings we need - global, too
Global toolbarResourceUrl, toolbarUIName, itemLabel, itemCommandUrl, itemService

'http://www.oooforum.org/forum/viewtopic.phtml?t=47503&p=287453
'const ResourceURL = "private:resource/toolbar/ms777_23"
'const sUnoUrl = ".uno:CommandLineToolbar"
'const sService = "ms777.ToolbarCombobox"
'const sToolbarUIName = "ms777_23" 

sub initToolbar(ByRef innDoc)
	toolbarResourceUrl = "private:resource/toolbar/custom_toolbar_opindraw"
	toolbarUIName = "Close button for Draw" '  "custom_toolbar_opindraw"
	itemLabel = "Close and back to Writer" ' "closeDrawClickHandler"
	'itemCommandUrl = "macro:///"+MACRO_LOC+"closeDrawClickHandler" ' works too... with () on end or not
	'itemCommandUrl = ".uno:CommandLineToolbar"
	'itemCommandUrl = ".uno:OpenUrl" ' shows a dropdown
	'itemCommandUrl = ".uno:Open" ' shows the filo open icon 
	itemCommandUrl = "vnd.sun.star.script:"+MACRO_LOC+"closeDrawClickHandler?language=Basic&location=application"
	
	' not using this here
	'itemCommandUrl = ".uno:opindrawCombobox"	
	'itemService = "opindraw.Toolbar" ' "opindraw.ToolbarCombobox"
	
	createToolbar(innDoc, toolbarResourceUrl, toolbarUIName, itemLabel, itemCommandUrl, itemService)
	'createToolbar2() ' doesn't show ... most of the time...
	
end sub


sub createToolbar(ByRef inDoc, toolbarResourceUrl as String, toolbarUIName as String, itemLabel as String, _
                  itemCommandUrl as String, itemService as String)
'I heavily borrowed from Peter http://www.oooforum.org/forum/viewtopic.phtml?t=50877 when writing this
	
	'register the itemService at the Toolbarfactory
	Dim oo, oLM, oEL, oToolbarSettings, cDockArea
	
	' THIS is a portion of the orig code that needs a service (http://user.services.openoffice.org/en/forum/viewtopic.php?f=21&t=4351)
	' and that method uses extra beanshell
	' but, if it is executed here without the beanshell, it screws the clicks !!
	'oo = createUnoService("com.sun.star.frame.ToolBarControllerFactory") 
	'if oo.hasController( itemCommandUrl, "") then oo.deregisterController( itemCommandUrl, "")
	'oo.registerController( itemCommandUrl, "", itemService)
	
	'delete toolbar if existing
	oLM = inDoc.CurrentController.Frame.Layoutmanager
	if not IsNull(oLM.getElement(toolbarResourceUrl)) then oLM.destroyElement(toolbarResourceUrl)
	
	'create toolbar
	oLM.createElement(toolbarResourceUrl)
	
	'insert one component into toolbar and set the UIName
	oEL = oLM.getElement(toolbarResourceUrl)
	oToolbarSettings = oEL.getSettings(true)
	
	Dim srep as String
	Dim x, xx, xxx

	Dim aItem
	aItem = CreateToolbarItem( itemCommandUrl, itemLabel )
	
	oToolbarSettings.insertByIndex( 0, aItem )
	oToolbarSettings.UIName = toolbarUIName
	oEL.setSettings(oToolbarSettings)
	'oEL.updateSettings
	oLM.showElement(toolbarResourceUrl)

	REM old: make sure that the toolbar is docked in a separate new line in the docking area
	'cDockArea = com.sun.star.ui.DockingArea.DOCKINGAREA_TOP
	'Dim aPos as New com.sun.star.awt.Point
	'aPos.X = 0
	'aPos.Y = 0
	'oLM.dockWindow(toolbarResourceUrl,cDockArea,aPos)
	REM here we want float instead:
	oLM.floatWindow(toolbarResourceUrl)

end sub

Function CreateToolbarItem( Command as String, Label as String ) as Variant
	Dim aToolbarItem(4) as new com.sun.star.beans.PropertyValue
	
	' older attempts: 
	'Dim aItem(1) as new com.sun.star.beans.PropertyValue
	'aItem(0).Name = "CommandURL"
	'aItem(0).Value = itemCommandUrl
	'aItem(1).Name = "Label"
	'aItem(1).Value = itemLabel	
	
	'aToolbarItem(0).Name = "CommandURL"
	'aToolbarItem(0).Value = Command
	'aToolbarItem(1).Name = "Label"
	'aToolbarItem(1).Value = Label
	'aToolbarItem(2).Name = "Type"
	'aToolbarItem(2).Value = 0 ' must be 0 to get a button..
	'aToolbarItem(3).Name = "Visible"
	'aToolbarItem(3).Value = true

	aToolbarItem(0).Name = "CommandURL"
	aToolbarItem(0).Value = Command
	aToolbarItem(1).Name = "Type"
	aToolbarItem(1).Value = 0 ' must be 0 to get a button..	
	aToolbarItem(2).Name = "Label"
	aToolbarItem(2).Value = Label
	aToolbarItem(3).Name = "HelpURL"
	aToolbarItem(3).Value = Command	
	aToolbarItem(4).Name = "IsVisible"
	aToolbarItem(4).Value = true	
	CreateToolbarItem = aToolbarItem()
End Function


'_______________________________________________________________________________
'	RETURN FROM DRAW
'_______________________________________________________________________________
sub closeDrawClickHandler()
	'msgbox("closeDrawClickHandler !! " + dDoc.CurrentController.CurrentPage.Width)
	dim mret 
	mret = MsgBox ("About to close Draw. Would you like to update originating content in Writer with the changes made in Draw?", MB_YESNO + MB_DEFBUTTON2 + MB_ICONQUESTION)
	If mret = IDYES Then
		'msgbox("YES closeDrawClickHandler !! " + dDoc.CurrentController.CurrentPage.Width)
		' select all in dDoc
		SelectAll(dDoc)
		
		' same trick as above to regroup
		Dim selShapes, selGroup, selPage
		selShapes = dDoc.getCurrentSelection()
		selPage = dDoc.CurrentController.getCurrentPage()
	
		' autogroup, if not grouped already 
		if selShapes.Count = 1 then			 
			selGroup = selShapes.getByIndex(0)
		else
			selGroup = selPage.group(selShapes)
		end if
		
		' we have selGroupWriter which should be replaced.. 
		' so cut it here, so cursor remains in position in orDoc;
		' the cut contents will be overwritten by the next copy 
		ClipboardCut( orDoc ) 

		' ClipboardCopy argument refers to Doc = ThisComponent, and thus 
		' automatically the selection there? 
		SelectAll(dDoc) ' again, for new group 
		ClipboardCopy( dDoc ) 
		
		' finally paste in orDoc
		ClipboardPaste( orDoc ) 
	'Else
		'msgbox("NO closeDrawClickHandler !! " + dDoc.CurrentController.CurrentPage.Width)
		' do nothing
	End If

	dDoc.close(true) ' bForce, so we're not prompted to save
end sub





	REM ' small debug
    REM For Each x in oLM.getElements()
      REM Dim ots
      REM ots = x.getSettings(true)
      REM Dim otsc, otscl
      REM On Error Resume Next
      REM otscl = LBound(ots) ' no work?
      REM For xx = 0 to ots.getCount()-1
      	REM otsc =  ots.getByIndex( xx ) 
      	REM 'Dim otsco
      	REM Dim ogc
      	REM On Error Resume Next
      	REM 'otsco = otsc(0) ' CommandURL   
      	REM srep = srep & xx & CHR$(10)   	
      	REM ogc = LBound(otsc)
      	REM For xxx = ogc to UBound(otsc) 
      		REM srep = srep & xx & "	 " & otsc(xxx).Name & " " & otsc(xxx).Value & CHR$(10)
      	REM Next
      REM Next
      REM srep = srep & " ******** " & CHR$(10) & " ******** " & x.ResourceURL & " " & ots.UIName & CHR$(10)
	  REM 'xray ots      
    REM Next
    REM 'msgbox srep
    REM 'TextToClipboard(srep)
    REM ThisComponent.getText().insertString( ThisComponent.getText().createTextCursor(), srep, False )


' couldn't get createToolbar2 to work.. 
sub createToolbar2() 
	Dim oModuleCfgMgrSupplier, oWindowState, oModuleCfgMgr, oBasicWindowState, oToolbarItem, oToolbarSettings
	Dim oLM
	
	REM *** Retrieve the module configuration manager from central module configuration manager supplier  
	oModuleCfgMgrSupplier = createUnoService("com.sun.star.ui.ModuleUIConfigurationManagerSupplier")
	'oWindowState = createUnoService("com.sun.star.ui.WindowStateConfiguration")
	
	REM *** Retrieve the module configuration manager with the module identifier
	REM *** See com.sun.star.frame.ModuleManager for more information
	'oModuleCfgMgr = oModuleCfgMgrSupplier.getUIConfigurationManager( "com.sun.star.script.BasicIDE" )
	'oModuleCfgMgr = oModuleCfgMgrSupplier.getUIConfigurationManager( ThisComponent ) 'NO
	'oModuleCfgMgr =	ThisComponent.getUIConfigurationManager()
	oModuleCfgMgr = oModuleCfgMgrSupplier.getUIConfigurationManager( "com.sun.star.text.TextDocument" )
	
	REM *** Retrieve the window state configuration for the Basic IDE
	' oBasicWindowState = oWindowState.getByName( sBasicIDEModuleIdentifier )
	
	
	REM *** Create a settings container which will define the structure of our new
	REM *** custom toolbar.
	oToolbarSettings = oModuleCfgMgr.createSettings()
	
	REM *** Set a title for our new custom toolbar
	oToolbarSettings.UIName = toolbarUIName
	
	REM *** Create a button for our new custom toolbar
	oToolbarItem = CreateToolbarItem( itemCommandUrl, itemLabel )
	oToolbarSettings.insertByIndex( 0, oToolbarItem )
	
	REM *** Set the settings for our new custom toolbar. (replace/insert)
	if ( oModuleCfgMgr.hasSettings( toolbarResourceUrl )) then
		oModuleCfgMgr.replaceSettings( toolbarResourceUrl, oToolbarSettings )
	else
		oModuleCfgMgr.insertSettings( toolbarResourceUrl, oToolbarSettings )
	endif
	
	oLM = ThisComponent.CurrentController.Frame.Layoutmanager
	oLM.showElement(toolbarResourceUrl)
	oLM.floatWindow(toolbarResourceUrl)
end sub

OpenOffice 3.0 on Windows XP
OpenOffice 3.0 on Ubuntu 9.04 (or 3.1?)
OpenOffice 3.2 on Windows XP
Post Reply