How to get Document-Coordinates from a Mouse Click in an OpenOffice BASIC Macro - position

Background:
I want to paste (like [CTRL+V]) anything (preferably image, shape) at the position I click or hover with the mouse (when using a key to activate). I don't know how to get the position on the document (X, Y) I clicked.
(Apache OpenOffice, SDraw-Document, OpenOffice BASIC Macro)
What I need:
Hint/Tip how to get the location from the mouse-click / mouse-position on the document. (Which class, listener, component I need)
Notes:
Something like a com.sun.star.awt.XMouseClickHandler would be perfect, if the given oEvent gave me the X+Y of the document, where I clicked.
(Maybe you know how to "activate" PopupTrigger? (com.sun.star.awt.MouseEvent))
My code so far:
I tried using the mentioned XMouseClickHandler to get X+Y.
Sadly, X+Y refer to the relative position of the window and not the actual position a shape or text would have on the document.
Execution: My Sub Main is executed via a Menu-Button at the top.
Then clicking anywhere will output (via MsgBox) the coordinates of that click.
Only Problem: Coordinates are relative to the corner of the window, not the corner of the document.
Global gListener As Object
Sub Main
gListener = CreateUnoListener("Listener_","com.sun.star.awt.XMouseClickHandler")
ThisComponent.CurrentController.addMouseClickHandler(gListener)
End Sub
Sub Listener_mousePressed(oMouseEvent) As Boolean
ThisComponent.CurrentController.removeMouseClickHandler(gListener)
Msg = "Position: "
Msg = Msg & oMouseEvent.X & "/" & oMouseEvent.Y
MsgBox(Msg)
REM :: I want something like:
REM :: Msg = "Position: " & oMouseEvent.PositionOnDocument.X
REM :: Msg = Msg & "/" & oMouseEvent.PositionOnDocument.Y
REM :: MsgBox(Msg)
End Sub
My references:
All my information come from the official references/docs so far, since all my searches did not find anything helpful.
Class-List: http://api.libreoffice.org/docs/idl/ref/annotated.html Here you can see docs for the used classes (com.sun.star.awt.XMouseClickHandler, com.sun.star.awt.MouseEvent)
Infos about listener: https://help.libreoffice.org/3.6/Basic/CreateUnoListener_Function_Runtime
Thanks in advance.

I finally found a way to get the exact coordinates of a mouse click (relative to the document).
I managed to get the information from the StatusBar at the bottom, which usually shows the coordinates (for me in centimeters).
Here is the function I now use to get the position (X / Y):
REM // Warning: If there is currently a selection, the returning Point will instead show the coordinates of the selection!
Sub GetMousePositionOnDocument as com.sun.star.awt.Point
Dim aPosition As New com.sun.star.awt.Point
Dim o1, o2, o3, o4, o5, o6
REM // First get AccessibleContext of the Window of the active Frame of the Application
o1 = StarDesktop.ActiveFrame.ContainerWindow.AccessibleContext
REM // 7th AC of o1 is the StatusBar at the bottom;
o2 = o1.GetAccessibleChild(6).AccessibleContext
REM // 2nd AC of o2 is the Position + Size of the Selection (e.g: "10,95 / 14,980,00 x 0,00")
o3 = o2.GetAccessibleChild(1)
o4 = o3.GetText()
REM // Taking out only the coordinates from o4
REM // TODO: Check for negatives (longer)
o5 = LEFT(o4, 4)
o6 = MID(o4, 8, 5)
aPosition.X = o5
aPosition.Y = o6
REM // Return
GetMousePositionOnDocument = aPosition
End Sub
Note: This function is called inside my previous Listener_mousePressed from above.
Hopefully this will work for others, too.
How I found it?
I spent very much time on checking every single ApplicationContext of the Window(s) of ThisComponent and StarDesktop manually in the debugger.
This is the starting point for iterating through ThisDesktop if needed for other values.
ThisComponent.CurrentController.Frame.ComponentWindow.AccessibleContext
Future Improvements
I "know" the indexes for the GetAccessibleChild()-Function because I inspected the debugger. There certainly are better ways to get to o3 and you should not expect everyone to have the same AccessibleContext's.

It turns out that the DrawingDocumentDrawView service has a member called VisibleArea that helps. Subtract (actually add because they have the opposite sign) those coordinates from the mouse position to get the location relative to the document.
Here is an example that creates a rectangle at the location of the mouse click.
Sub Listener_mousePressed(oMouseEvent) As Boolean
ThisComponent.CurrentController.removeMouseClickHandler(gListener)
xpos = (oMouseEvent.X + ThisComponent.VisibleArea.X / 25.4) / 100
ypos = (oMouseEvent.Y + ThisComponent.VisibleArea.Y / 25.4) / 100
Msg = "Position: " & xpos & "/" & ypos
MsgBox(Msg)
InsertProcessShape(xpos, ypos)
End Sub
Sub InsertProcessShape(xpos, ypos)
Dim oDoc As Object
Dim oDrawPage As Object
Dim oShape As Object
Dim shapeGeometry(0) as new com.sun.star.beans.PropertyValue
Dim oSize As new com.sun.star.awt.Size
oSize.width = 3000
oSize.height = 1000
oDoc = ThisComponent
odrawPage = oDoc.DrawPages(0)
oShape = oDoc.createInstance("com.sun.star.drawing.CustomShape")
shapeGeometry(0).Name = "Type"
shapeGeometry(0).Value = "flowchart-process"
oDrawPage.add(oShape)
oShape.CustomShapeGeometry = shapeGeometry
oShape.Size = oSize
' Position the object
IN_TO_CM = 2540 ' converts 1/1000 cm to inches
Dim aPosition As New com.sun.star.awt.Point
aPosition.X = xpos * IN_TO_CM
aPosition.Y = ypos * IN_TO_CM
oShape.setposition(aPosition)
End Sub
To figure this out, I used xray. The formula may need to be fine-tuned. But the left-top corner of the rectangles seemed to go roughly where the mouse was clicked when I tested it, as intended.
InsertProcessShape is from https://forum.openoffice.org/en/forum/viewtopic.php?f=45&t=46682, plus some code from Listing 5.84 of Andrew Pitonyak's Macro Document.

Related

VBA Excel - Type Mismatch (Err 13) in Shapes.AddPicture

This one is driving me nuts.
I'm trying to place an image in a spreadsheet.
The following is a created snippet to show what I'm trying to do, but fails at .AddPicture with a TypeMismatch error. What is curious is that despite the failure it still places the image in the correct position, correctly sized.
`
Private Sub cmdLayImage_Click(y as Integer, x as Integer)
Dim lsF As String
Dim loShps As Excel.Shapes
Dim loS As Shape
Dim c As Integer
lsFilename = "Moorland.jpg"
goXsTT.Cells(Y, x).Select
'Fails with Err 13 at this line
Set loShps = goXsTT.Shapes.AddPicture(gsTerrainImagePath & lsFilename, _
msoFalse, msoTrue, ActiveCell.Left, ActiveCell.Top, -1, -1)
c = loShps.Count
loS = loShps(c)
loS.Name = c
MsgBox "Shape Number/Name = " & loS.Name
End Sub
`
I've tried various settings for Link To File and Save To Document, I've used absolute figures for height, width, top and bottom I also tried Csng() each to be sure; nothing works - yet the image is laid, but loShps never gets set.
Any help greatly appreciated.
Thanks Dave
I'm attempting to use the Shapes object as placing an image directly into a selected cell using:
Set loPicInCell = goXsTT.Pictures.Insert(gsTerrainImagePath & lsFilename)
whilst it works, is limiting in the methods available (in particular .OnAction)

Update an Excel style in VBA

My Excel macro reads the answers to a survey from a set of Excel files. The answers of a survey contain a score (from 1 to 4) and a description. The goal is to generate a a matrix. Each cell of the matrix has a color that represents the score. I would like the user to be able to modify the layout of these cell. To make it easy to the user, I created a template matrix and a button. The user should be able to modify the layout of the cells and on a click of a button, a set of styles (Score 1, Score 2,...) should be generated. Once the matrix is created, the Workbook should be to function without the survey files.
I have tried a couple of things:
Try 1
ThisWorkbook.Styles.Add "Score 1", BasedOn:=cell1
This gives errors. I don't fully understand when they occur, but one of the causes is when the user modifies the cell layout by selecting another style.
Try 2
ThisWorkbook.Styles("Score 1").Delete
ThisWorkbook.Styles.Add "Score 1", BasedOn:=cell1
This is not a good idea: all cells loose their styling when it is executed a second time.
Try 3: Current
Copy the most frequently used properties of the cells layout and copy them to the style. If this style is deleted by the user, it is recreated. This procedures is not ideal, since most style properties won't be covered.
Is there a way to update a cell style that is more general? I would like there to be as little room as possible to make the workbook in an inconsistent and non-functional state.
I sticked with try 3. Because it required a lot of code for all properties that seemed possible to be edited, and because of copying borders is tricky, I post the result.
'xR1_Template: the cell to base the style on
'nm_Style: the name of the style
Public Function Upsert_Style(xR1_Template As Excel.Range, nm_Style As String) As Excel.Style
Dim xStyle As Excel.Style
Set xStyle = Fn.TryGet(ThisWorkbook.Styles, nm_Style)
If Fn.IsNothing(xStyle) Then
Set xStyle = ThisWorkbook.Styles.Add(nm_Style)
End If
xStyle.Font.Color = xR1_Template.Font.Color
xStyle.Font.Bold = xR1_Template.Font.Bold
xStyle.Font.Name = xR1_Template.Font.Name
xStyle.Font.Italic = xR1_Template.Font.Italic
xStyle.Font.Size = xR1_Template.Font.Size
xStyle.Font.Strikethrough = xR1_Template.Font.Strikethrough
xStyle.Font.Subscript = xR1_Template.Font.Subscript
xStyle.Font.Superscript = xR1_Template.Font.Superscript
xStyle.Font.Underline = xR1_Template.Font.Underline
xStyle.Interior.Color = xR1_Template.Interior.Color
xStyle.Interior.Pattern = xR1_Template.Interior.Pattern
xStyle.Interior.PatternColor = xR1_Template.Interior.PatternColor
'NOTE: necessary to delete all borders first. There's no way to delete them one by one.
xStyle.Borders.LineStyle = xlNone
Dim iBorder As Long
For iBorder = 1 To xR1_Template.Borders.Count
Dim xBorder As Excel.Border
'NOTE: The Borders property claims to work with xlBordersIndex argument, but this is not true.
' Normal indexing is used.
Set xBorder = xR1_Template.Borders(iBorder)
'NOTE: "none-style" borders (=no border), should be skipped.
' Once they are retrieved using the Borders property, they are always visible.
' Setting them with xlLineStyle.xlLineStyleNone does not hide them.
If xBorder.LineStyle <> XlLineStyle.xlLineStyleNone Then
Dim xBorder_Style As Excel.Border
Set xBorder_Style = xStyle.Borders(iBorder)
xBorder_Style.Color = xBorder.Color
xBorder_Style.LineStyle = xBorder.LineStyle
xBorder_Style.Weight = xBorder.Weight
End If
Next iBorder
xStyle.AddIndent = xR1_Template.AddIndent
xStyle.FormulaHidden = xR1_Template.FormulaHidden
xStyle.HorizontalAlignment = xR1_Template.HorizontalAlignment
xStyle.IndentLevel = xR1_Template.IndentLevel
xStyle.NumberFormat = xR1_Template.NumberFormat
xStyle.NumberFormatLocal = xR1_Template.NumberFormatLocal
xStyle.Orientation = xR1_Template.Orientation
xStyle.ShrinkToFit = xR1_Template.ShrinkToFit
xStyle.VerticalAlignment = xR1_Template.VerticalAlignment
xStyle.WrapText = xR1_Template.WrapText
xStyle.IndentLevel = xR1_Template.IndentLevel
Set Upsert_Style = xStyle
End Function

How to find the parent userform of a control in VBA

This code is part of a class module.
Private pImg As Image 'For example, pImg = U_Cursor.Img
Public Property Let ItemID(ID As Byte)
pImg.Picture = LoadPicture(ThisWorkbook.Path & "\Images\Img" & ID & ".gif")
*ParentUserform*.Repaint 'ParentUserform is the userform I'm looking for
End Property
I have to force Excel to repaint the userform, otherwise the image sometimes doesn't update on screen.
Is there a way to know which userform "pImg" is in?
If there is no efficient way to do it, I can add another variable to the class module containing the userform name, but I'd prefer not to.
pImg.Parent will work if pImg is the direct child of the user form. If it is within another container (like a multi or a frame) you'll need to climb the .Parent tree.
With your code in break mode take a look in the Locals window and look at the properties of pImg
I know this is an old thread, but I wanted to add to the collective wisdom on this "Finding the Parent Userform" topic.
My Excel VBA project includes object/controls located directly on the Userform as well as obj/ctrls within several Frames. I use the frames to easily adjust the layout of groups of related obj/ctrls instead of coding each of their .Left/.Top individually (ie. code for 1 frame instead of 20 obj/ctrls).
The specific task that I was trying to accomplish was to code a popup menu for the right/click event on several different ListBoxes in order to edit that ListItem's data. The problem that I ran into, was running the Do/Loop up the .Parent tree to find the main Userform, so that I could calc the XY coords to position the menu just below the cursor.
After a couple of days ... and a reduction of hair (along with their follicles) ... I finally figured out that Excel considers the Frame control to be a Userform. An excerpt from my final loop is as follows ...
'the callingCtrl obj is declared as Public in a module
'then Set in the MouseUp event for the Listbox
'then in frm_Popup_Menu's Initialize sub ...
Private Sub UserForm_Initialize()
'==============================
' un-related stuff happens here
'==============================
'zero the coordinates - (0,0)
zeroX = 0
zeroY = 0
'adjust zero for the cursor position
zeroX = zeroX + curX
zeroY = zeroY + curY
'adjust zero for the calling control position
zeroX = zeroX + callingCtrl.Left
zeroY = zeroY + callingCtrl.Top
'adjust zero for any intermediate control/object positions
Dim myParent As Object
Set myParent = callingCtrl.Parent
Do Until TypeOf myParent Is MSForms.UserForm And Not LCase(TypeName(myParent)) = "frame"
zeroX = zeroX + myParent.Left
zeroY = zeroY + myParent.Top
'reset to previous parent in the tree
Set myParent = myParent.Parent
Loop
'adjust zero for the form's position
zeroX = zeroX + myParent.Left
zeroY = zeroY + myParent.Top
'adjust zero for borders & margins
zeroX = zeroX + 7
zeroY = zeroY + 25
'set the final coordinates for the cursor position
frm_Popup_Menu.Left = zeroX
frm_Popup_Menu.Top = zeroY
'==============================
' end of related code
'==============================
Hope you find this helpful. Enjoy.

How to resize a graphic object in the LINK field?

After a Paste special linking of a range of cells from Excel to Word (2013) the field looks like this:
{ LINK Excel.SheetMacroEnabled.12 D:\\20181228\\SC.xlsm Sheet1!R10C1:R10C20" \a \p }
If you click on the object with the right button, select "Format object" and then click on "?", the Format AutoShape reference article opens.
However, ActiveDocument.Shapes.SelectAll does not detect this object.
This code also does not work, although the error message says that this component is available for pictures and OLE objects:
With ActiveDocument.Shapes(1).PictureFormat
.ColorType = msoPictureGrayScale
.CropBottom = 18
End With
What is this object?
I cannot find it in Object model (Word).
How to access it through VBA?
I want to programmatically resize a group of such objects to 90% of the original.
Upd. #Cindy Meister suggested where to dig, thanks.
I wrote the code, it seems to work fine:
Sub ResizeImages()
Dim img As Long
With ActiveDocument
For img = 1 To .InlineShapes.Count
With .InlineShapes(img)
.ScaleHeight = 90
.ScaleWidth = 90
End With
Next img
End With
End Sub
A Link field must be an InlineShape - it can't be a Shape, not if you can display the field using Alt+F9. Since Shape objects have text wrap formatting any field codes associated with them (usually none) aren't accessible.
Therefore, any object that's displayed via a Link field should be available via the InlineShape object model.
For example, the following code loops the fields in the document and, if they're link fields with an Excel source and contain an InlineShape, the InlineShape's dimensions are scaled:
Dim fld as Word.Field
For Each fld In ActiveDocument.Fields
If fld.Type = wdFieldLink
If fld.Result.InlineShapes.Count > 1 And _
InStr(fld.OLEFormat.ClassType, "Excel") Then
Set ils = fld.Result.InlineShapes(1)
ils.ScaleWidth = 90
ils.ScaleHeight = 90
End If
End If
Next

How To: Hover Over Shape Shows TextBox

I am trying to make it so when a user hovers over a triangle as shown below, a textbox pops up with certain information pertaining to that triangle.
Triangles are drawn with the following function...
Public Sub drawTriangle(theRow As Integer, theColumn As Integer, Optional myColor As System.Drawing.Color = Nothing)
myColor = System.Drawing.Color.Black
Dim theShape As Microsoft.Office.Interop.Excel.Shape = xlWorkSheet.Shapes.AddShape(MsoAutoShapeType.msoShapeIsoscelesTriangle, (xlWorkSheet.Cells(theRow, theColumn)).Left + 18, (xlWorkSheet.Cells(theRow, theColumn)).Top, 15, 14)
theShape.Fill.ForeColor.RGB = ToBgr(myColor)
theShape.Line.ForeColor.RGB = ToBgr(myColor)
End Sub
I haven't been able to find any examples or good documentation about how I would do this, so I thought I'd ask here. Any and all help is much appreciated!!
According to MSDN, comments can be added to ranges. For this to work for your application, you simply need to select the range that corresponds to your shape, and then call AddComment().
The numeric coordinates of a cell can be used to determine the actual Cell name (i.e. E5) by using code like the following(source):
address = xlWorkSheet.Cells(RowVariable, ColVariable).Address
This can be followed up with:
xlWorkSheet.Range(address).AddComment("This is a comment")

Resources