Resize and modify selected chart with libreoffice basic - charts

I'm writing a macro with libreoffice basic to modify the styles and size of a selected (and only selected) chart in my spreadsheets.
After many documentation reading and many tries, I managed to have a partial solution to my problem:
Modifying all charts style
I managed to select a chart by it's indexe and modify it's styles with this macro:
Sub ModifyChart
Dim oDoc As Object
Dim oChart As Object
Dim aSize as new com.sun.star.awt.Size
oSheet = ThisComponent.sheets(0)
oCharts = oSheet.getCharts()
oChart = oCharts.getByIndex(0).getEmbeddedObject()
MsgBox oChart.ImplementationName
oChart.Title.String = "My title"
oChart.Title.CharColor = RGB(0,0,200)
oChart.Title.CharFontName = "Arial"
oChart.Title.CharHeight = 16
oChart.SubTitle.String = "My subtitle"
oChart.SubTitle.CharColor = RGB(0,0,200)
oChart.SubTitle.CharFontName = "Arial"
oChart.SubTitle.CharHeight = 12
oChart.Diagram.Wall.FillStyle = com.sun.star.drawing.FillStyle.SOLID
oChart.Diagram.Wall.FillColor = RGB(200,50,150)
oChart.Area.FillStyle = com.sun.star.drawing.FillStyle.SOLID
oChart.Area.FillColor = RGB(100,50,250)
aSize.Width=640
aSize.Height=400
oChart.setsize(aSize) ' Error occur at this point
End Sub
Problem: It's doesn't work with the selected chart and I can't resize it. The method setsize doesn't work for this kind of object. I tryed to get the parent object of the chart ( frame ? or shape ?) without success.
With ImplementationName, I found that the object is a com.sun.star.comp.sc.scshapeobj
Modifying the selected chart size
I managed to modify the selected chart size by using this macro
Sub ResizeChart
Dim oCurrSel As Object
Dim oItem As Object
Dim aSize As New com.sun.star.awt.Size
oCurrSel = ThisComponent.getCurrentSelection()
oItem = oCurrSel.getByIndex(0)
MsgBox oItem.ImplementationName
aSize.width=16000
aSize.height=12000
oItem.setsize(aSize)
End Sub
Problem: I can't access to the other styles of the chart. I tryed to find a method to get the object content without success. I also tryed to investigate the object with oItem.dbg_properties and oItem.dbg_methods but I didn't found anything useful.
With ImplementationName, I found that the object is a com.sun.star.comp.chart2.chartmodel
I have a look to the libreoffice api but I didn't find how these two kind of object are connected.
Is it possible to make what I want with libreoffice basic ?
Does anyone could explain me the hierachical structure of a libreoffice chart object (parents, childs, ...) and how to deal with it ?

Yes, you are absolutely right - parsing the current selection is not a trivial task.
Since the current selection may contain a variety of objects - Cell, CellRange, SheetCellRanges, Shapes, ShapeCollection, GraphicObjectShape and even in some cases just Text, parsing becomes similar to the game "Miner" - each next step requires additional checks (or error handling " blindly "with the help On Error Resume Next)
Your idea of ​​using ImplementationName to identify objects is generally good. But Andrew Pitonyak in 4.1. Debugging And Inspecting Macros wrote "To determine the document type, look at the services it supports ... I assume that this is safer than using getImplementationName()" and I tend to believe him.
The transition from the current selection to the embedded chart can be something like this:
Sub ModifyChartInCurrentSelection
Dim oCurrentSelection As Variant
Dim i As Long
Dim oNextElementOfSelection As Variant
Dim oEmbeddedObject As Variant
Dim oComponent As Variant
Dim aSize As New com.sun.star.awt.Size
oCurrentSelection = ThisComponent.getCurrentSelection()
If oCurrentSelection.supportsService("com.sun.star.drawing.ShapeCollection") Then
For i = 0 To oCurrentSelection.getCount()-1
oNextElementOfSelection = oCurrentSelection.getByIndex(i)
If oNextElementOfSelection.supportsService("com.sun.star.drawing.OLE2Shape") Then
Rem Size of shape (outer wrapper around the chart)
If oNextElementOfSelection.supportsService("com.sun.star.drawing.Shape") Then
aSize = oNextElementOfSelection.getSize()
aSize.Height = aSize.Height * 2 ' or any other
aSize.Width = aSize.Width / 2
oNextElementOfSelection.setSize(aSize)
EndIf
Rem Properties of EmbeddedObject
oEmbeddedObject = oNextElementOfSelection.EmbeddedObject
If Not IsEmpty(oEmbeddedObject) Then
oComponent = oEmbeddedObject.getComponent()
oComponent.getTitle().String = "Foo-bar Foo-bar Foo-bar"
Rem and other settings...
EndIf
EndIf
Next i
EndIf
End Sub

Related

Inserting and removing text into a textbox on a SHEET in Libreoffice calc BASIC

As I can't solve my problem I'd like to ask someone more experienced.I created simple dialog (4 fields) to let the user enter few data. After clicking "Submit" button those data should be inserted into textboxes put ON THE SHEET (not on any dialog). How to refer to those sheet texboxes in code to insert those data? Other thing is deleting those data after clicking other button "Clear". Suppose it will be similar to inserting but how this piece of code should look like?
Thanks in advance.
The trick is to create a com.sun.star.drawing.TextShape object and add it to the Draw Page of the target sheet. The following works for me. You should be able to assign it to the appropriate button on your dialog.
Sub InsertTextBox()
Dim oDocument As Object
oDocument = ThisComponent
If oDocument.SupportsService("com.sun.star.sheet.SpreadsheetDocument") Then
Dim sText As String
sText = "Blah,blah, blah!"
Dim oPosition As New com.sun.star.awt.Point
oPosition.X = 1000
oPosition.Y = 1000
Dim oSize As New com.sun.star.awt.Size
oSize.Width = 10000
oSize.Height = 5000
Dim oTextShape As Object
oTextShape = oDocument.createInstance("com.sun.star.drawing.TextShape")
oTextShape.setPosition(oPosition)
oTextShape.setSize(oSize)
oTextShape.setPropertyValue("FillStyle", "SOLID")
oTextShape.Visible = 1
' Give it a name so you can find it again when you want to delete it
oTextShape.setPropertyValue("Name", "Thingy")
Dim oDrawPage As Object
oDrawPage = oDocument.getSheets().getByIndex(0).getDrawPage()
oDrawPage.add(oTextShape)
' Set the string of the text shape AFTER adding it to the
' draw page, otherwise the text will not be set.
oTextShape.setString(sText)
End If
End Sub
In the above routine the TextShape object was given the name "Thingy". You can obviously give it any name you like, but it should be unique. To delete it, you need to loop through all the objects in the draw page, find the one that is a TextShape and has the name you gave it (in this case "Thingy") and remove it. This can be done as follows:
Sub DeleteTextBox()
Dim oDocument As Object
oDocument = ThisComponent
If oDocument.SupportsService("com.sun.star.sheet.SpreadsheetDocument") Then
Dim oDrawPage As Object
oDrawPage = oDocument.getSheets().getByIndex(0).getDrawPage()
Dim oShape As Object
Dim i As Long
For i = (oDrawPage.getCount() - 1) To 0 Step -1
oShape = oDrawPage.getByIndex(i)
If oShape.SupportsService("com.sun.star.drawing.TextShape") Then
If StrComp(oShape.getPropertyValue("Name"), "Thingy") = 0 Then
oDrawPage.remove(oShape)
End If
End If
Next i
End If
End Sub
This will delete all objects of type TextShape and named "Thingy"

In LibreOffice Calc, how do I change through LibreOffice Basic the value of a cell with an event listener set to it without crashing the program?

I am trying to create two tables which mirror changes made to any of them to one another automatically.
To that end, I added event listeners which are triggered when the cells of these tables are edited by the user.
Unfortunately, editing one of the tables causes LibreOffice to crash, even though the changes are indeed reflected correctly, as seen upon reopening the file.
I thought the crash might be due to a never-ending circular reference, but it still crashes after it has been made non-circular (by commenting out the relevant parts of the code so that changes are reflected only one way rather than both ways).
I noticed the code worked fine when writing to a cell that didn't have an event listener set to it.
How can I write to one of the cells with event listeners set to them without causing LibreOffice to crash?
You may want to download the following file. Please run Main and then try editing the cell C3 of the Planning sheet. The arbitrary string "C" should be written in the cell C4 of the Services sheet.
Here is a simplified version of the code :
REM ***** BASIC *****
const SERVICESSHEET_NUMBER = 2
const SERVICESSHEET_SERVICES_COLUMN = 2
Type cellStruct
columnNumber As Integer
rowNumber As Integer
End Type
Sub UpdateServicesSheet(editedCell As cellStruct, newValue As String)
Dim oSheets
Dim servicesSheet
oSheets = ThisComponent.getSheets()
servicesSheet = oSheets.getByIndex(SERVICESSHEET_NUMBER)
servicesSheet.getCellByPosition(SERVICESSHEET_SERVICES_COLUMN, 3).setString(newValue)
End Sub
Private oListener, cellRange as Object
Sub AddListener
Dim sheet, cell as Object
sheet = ThisComponent.Sheets.getByIndex(0) 'get leftmost sheet
servicesSheet = ThisComponent.Sheets.getByIndex(2)
cellRange = sheet.getCellrangeByName("C3")
oListener = createUnoListener("Modify_","com.sun.star.util.XModifyListener") 'create a listener
cellRange.addModifyListener(oListener) 'register the listener
cellRange = servicesSheet.getCellrangeByName("C4")
oListener = createUnoListener("Modify_","com.sun.star.util.XModifyListener") 'create a listener
cellRange.addModifyListener(oListener) 'register the listener
End Sub
global CircularReferenceAllowed As boolean
Sub Modify_modified(oEv)
Dim editedCell As cellStruct
Dim newValue As String
editedCell.columnNumber = 2
editedCell.rowNumber = 2
If CircularReferenceAllowed Then
CircularReferenceAllowed = false
UpdateServicesSheet(editedCell, "C")
End If
End Sub
Sub Modify_disposing(oEv)
End Sub
Sub RmvListener
cellRange.removeModifyListener(oListener)
End Sub
Sub Main
CircularReferenceAllowed = true
AddListener
End Sub
Crossposted to :
OpenOffice forums
LibreOffice discourse platform
It seems like the event trigger is within another event's function is causing the crash. In any case, the solution is to remove the listener, then add it back after modifying the other cell.
You do need to global the Listener and the Cell objects to make this work.
This code is simplified to work on C3 and C15 on the first sheet. It would also output some information on C14, which isn't really necessary for your purpose, but I use it to see what's happening. You need to adopt the according to what you need.
global goListener as Object
global goListener2 as Object
global goCellR as Object
global goCellR2 as Object
global goSheet as Object
global giRun as integer
global giUpd as Integer
Sub Modify_modified(oEv)
Dim sCurStr$
Dim sNewStr As String
'xRay oEv
giRun = giRun + 1
sCurStr = oEv.source.string
oCell = goSheet.getCellByPosition(2, 14)
If (oCell.getString() <> sCurStr) Then
' only update if it's different.
giUpd = giUpd + 1
goCellR2.removeModifyListener(goListener2)
oCell.setString(sCurStr)
goCellR2.addModifyListener(goListener2)
End If
sNewStr =sCurStr & " M1 Run=" & giRun & " Upd=" & giUpd
goSheet.getCellByPosition(2, 13).setString(sNewStr)
End Sub
Sub Modify2_modified(oEv)
Dim sCurStr$
Dim sNewStr As String
Dim oCell as Object
'xRay oEv
giRun = giRun + 1
sCurStr = oEv.source.string
oCell = goSheet.getCellByPosition(2, 2)
If (oCell.getString() <> sCurStr) Then
' only update if it's different.
giUpd = giUpd + 1
goCellR.removeModifyListener(goListener)
oCell.setString(sCurStr)
goCellR.addModifyListener(goListener)
End If
sNewStr =sCurStr & " M2 Run=" & giRun & " Upd=" & giUpd
goSheet.getCellByPosition(2, 13).setString(sNewStr)
End Sub
Sub Modify_disposing(oEv)
MsgBox "In Modify_disposing"
End Sub
Sub Modify2_disposing(oEv)
MsgBox "In Modify2_disposing"
End Sub
Sub RmvListener
MsgBox "In RmvListener"
goCellR.removeModifyListener(goListener)
goCellR2.removeModifyListener(goListener2)
End Sub
Sub AddListener
goSheet = ThisComponent.Sheets.getByIndex(0) 'get leftmost goSheet
'servicesSheet = ThisComponent.Sheets.getByIndex(2)
goCellR = goSheet.getCellrangeByName("C3")
goListener = createUnoListener("Modify_","com.sun.star.util.XModifyListener") 'create a listener
goCellR.addModifyListener(goListener) 'register the listener
goCellR2 = goSheet.getCellrangeByName("C15")
goListener2 = createUnoListener("Modify2_","com.sun.star.util.XModifyListener") 'create a listener
goCellR2.addModifyListener(goListener2) 'register the listener
End Sub
Sub Main
giRun = 0
giUpd = 0
AddListener
End Sub

convert simple VB script to MATLAB

I have some problem converting simples VB scripts (formating) into MATLAB:
VB script:
Range("A1").Select
Selection.Font.Italic = True
With Selection.Borders(xlEdgeBottom)
.LineStyle = xlContinuous
End With
I tried:
xlswrite('test.xls',1,'A1');
Excel = actxserver('Excel.Application');
Excel.Workbooks.Open('test.xls');
Range = Excel.Range('A1');
Range.Font.Italic = True; % Doesnt work
Range.Border.Item('xlEdgeRight').LineStyle = 1; % Doesnt work
Excel.Visible = 1;
Any workaround? Thanks
The problem is probably this line:
Range = Excel.Range('A1');
Your Excel object is an Application object, which doesn't have a Range property. The VBA example you follow uses a (IMHO) poor practice of using the global default objects that are only available within the context of the application itself. You need to grab a reference to the Workbook returned by this call:
Excel.Workbooks.Open('test.xls');
After that, you need to get the worksheet from it's indexed Worksheets property, then get the Range from that. Also, "xlEdgeRight" is an enumeration member so external calls have to use the integer values for them instead of a string. You can replace xlEdgeRight with 10.
I know next to nothing about Matlab, but this is what the more explicit code would look like in VBA:
Dim book As Workbook, sheet As Worksheet, rng As Range
Set book = Application.Workbooks.Open("test.xls")
Set sheet = book.Worksheets("Sheet1")
Set rng = sheet.Range("A1")
rng.Font.Italic = True
rng.Borders.Item(10).LineStyle = 1
You should be able to get it from there.

VBA form - Returning a textbox object to be manipulated

I'm creating a game in a vba form. Right now it creates an array 9x9 of textboxes and fills and disables the textboxes with the given information for the game. When creating the textboxes I named them "fieldx-y" so I could look them up easily. I want to somehow put them into an array so that I can look them up like field(x,y) and then do things to them like change the background color of the textbox or change information in it.
Here is the function I wanted to use to find the object using its name and return it to be manipulated.
Public Function getField(x As Integer, y As Integer) As MSForms.TextBox
Dim field As MSForms.TextBox
For Each field In Me.Controls
If Right(field.Name, 1) = y And Left(Right(field.Name, 3), 1) = x Then
getField = field
End If
Next
End Function
And here is how I would like to manipulate it from my userform initialize sub
getField(5,5).Enabled=False
I'm sure I must be doing something very wrong and it's probably because of my lacking understanding of OOP and vba.
Thanks
Since you have chosen a predictable naming convention you can call these controls directly using your naming convention. There is no need to loop through all the controls. Also, I changed your fieldx-y to fieldx_y because - is an illegal character variable names.
Public Function getField(x As Integer, y As Integer) As MSForms.TextBox
set getField = me.controls("field" & x & "_" & y)
End Function
If all you are doing is enabling the control, then you may not actually need to return the textbox, in which case do not add the tb variable to the calling procedure, and change your function to a sub, like:
Public Sub getField(x As Integer, y As Integer)
Dim field As MSForms.TextBox
For Each field In Me.Controls
If Right(field.Name, 1) = y And Left(Right(field.Name, 3), 1) = x Then
'## Disable this textbox
field.Enabled = False
Exit For
End If
Next
End Sub
If you do need to return a textbox to the calling procedure, then do this:
In your calling procedure, you need an object variable to represent the returned MSForms.TextBox:
Dim tb as MSForms.TextBox
Set tb = getField(5,5)
tb.Enabled = False
Then in your function routine, because it is an object, you need the Set keyword:
Set getField = field
Exit For '## You need to escape the loop otherwise it will keep going, giving undesired results.

OpenOffice.org: macro help

In OOo Calc
I need to copy a column (only the values, not the format) from one sheet to another (in the same worksheet) using a macro assigned to a button.
I browsed a bit around but found nothing significant :-(
To answer the original question:
use a data array, which will be significally faster on large ranges of cells
Source = ThisWeek.getCellRangeByName("H12:H206")
source_data = Source.getDataArray()
Target = Steering.getCellRangeByName("M12:AU206").setDataArray(source_data())
OK, I could build the answer and started learning OOo Basic, which I managed to avoid until now ;-)
I give it as is.
Sub UpdateThisWeek
Dim Doc As Object
Dim ThisWeek As Object
Dim Steering As Object
Dim Source As Object
Dim Target As Object
Dim Week as Integer
Doc = ThisComponent
ThisWeek = Doc.Sheets.getByName("This week")
Steering = Doc.Sheets.getByName("Steering")
Week = Steering.getCellByPosition(6,4).Value
Source = ThisWeek.getCellRangeByName("H12:H206")
Target = Steering.getCellRangeByName("M12:AU206").getCellRangeByPosition(Week-19,0,Week-19,194)
Dim i, s
For i = 0 To 194
s = Source.getCellByPosition(0, i).Value
If s > 0 Then
Target.getCellByPosition(0, i).Value = s
Else
Target.getCellByPosition(0, i).String = ""
End If
Next i
End Sub