LocationOnScreen (absolute X/Y in pixels) for selected text in Write, how to?

Hi, I am looking for a method that returns the absolute screen coordinates of the selected text in the Write editor. I’ve read some threads about positioning dialog boxes near a sheet cell, but the methods that handle sheet objects don’t work in the Write document. Is there any way to extract LocationOnScreen from selected text or even a table cell, but in the Write editor?
I would appreciate any leads or suggestions on this matter.

Greetings with a smile
Racho

And here it is :-). Code to position the dialog box relative to the current position of the text cursor in the Writer editor. The key is the data returned by thisComponent.currentController.viewData transferred to an array:

      Array : T( 0 To 8 ) As string  

(0) | string : “7435” = X cursor
(1) | string : “4369” = Y cursor
(2) | string : “140” = % magnification
(3) | string : “0” = horizontal scroll position
(4) | string : “0” = vertical scroll position
(5) | string : “16586” = LO window width.
(6) | string : “10264” = LO window height
(7) | string : “0” = maybe you know?
(8) | string : “0” = maybe you know?

and the knowledge that the given values (except magnification) are given in units com.sun.star.util.MeasureUnit.TWIP

For this, you need to take into account the height of the menu bar and toolbars from the top and the rulers (if enabled) from the top and left.

The following code will work if you first initialize the DialogLibraries.Standard.Dialog1 dialog box

Use if helpful :slight_smile:

sub LocationOnScreen

    dim TextDoc as object
    dim TextView as object
    dim TextViewData as string
    dim TextViewDataScope as variant
    dim TextViewComponent as object
    dim TextViewAccesible as object
    dim TextViewAccesibleChildContext as object
    dim TextViewZoom as double
    
    dim HorizScrollOffset as long
    dim VertScrollOffset as long
    dim TopMenuAndToolbarsOffset as integer
    dim LeftRulerOffset as integer
    dim TopRulerOffset as integer
    
    dim CursorLocation as new com.sun.star.awt.Point
    dim CursorOnScreenLocation as new com.sun.star.awt.Point
    dim TextViewSize as new com.sun.star.awt.Size
    dim HDlgBox as integer
    dim ScreenGear as object
    dim i as integer
    '----------------------------------'

    TextDoc = thisComponent
    TextView = TextDoc.currentController
    TextViewData = TextView.viewData
    TextViewDataScope = split(TextViewData, ";")
    TextViewComponent = TextView.frame.componentWindow
    TextViewAccesible = TextViewComponent.AccessibleContext
    TextViewAccesibleChildContext = TextViewAccesible.getAccessibleChild(0).AccessibleContext

    LeftRulerOffset = 0
    TopRulerOffset = 0
    on error resume next
	for i = 0 to TextViewAccesibleChildContext.AccessibleChildcount - 1
		with TextViewAccesibleChildContext.getAccessibleChild(i)
			if .getAccessibleContext.accessibleRole = 77 then '[RULER = 77]'
				if .getAccessibleContext.size.width < 50 then LeftRulerOffset = .getAccessibleContext.size.width
				if .getAccessibleContext.size.height < 50 then TopRulerOffset = .getAccessibleContext.size.height
			endif
		end with
	next
	on error goto 0
       
    TopMenuAndToolbarsOffset = TextViewAccesible.AccessibleParent.AccessibleContext.Location.Y + TextViewAccesible.Location.Y
	TextViewZoom = cDbl(TextViewDataScope(2)) / 100
    
    HorizScrollOffset = cLng(TextViewDataScope(3))	'TWIP'
    VertScrollOffset = cLng(TextViewDataScope(4))	'TWIP'	
    
    CursorLocation.X = (cLng(TextViewDataScope(0)) - HorizScrollOffset) * TextViewZoom	'TWIP'
    CursorLocation.Y = (cLng(TextViewDataScope(1)) - VertScrollOffset) * TextViewZoom	'TWIP'

    TextViewSize.Width = cLng(TextViewDataScope(5)) * TextViewZoom		'TWIP'
    TextViewSize.Height = cLng(TextViewDataScope(6)) * TextViewZoom	'TWIP'

    ScreenGear = CreateUnoDialog(DialogLibraries.Standard.Dialog1)
    CursorOnScreenLocation = TextViewComponent.convertPointToPixel(CursorLocation, com.sun.star.util.MeasureUnit.TWIP)
	HDlgBox = 80
   	ScreenGear.setPosSize(CursorOnScreenLocation.X + LeftRulerOffset,_
   						  CursorOnScreenLocation.Y + TopMenuAndToolbarsOffset + TopRulerOffset - HDlgBox, 50, 50,_
   						  com.sun.star.awt.PosSize.POSSIZE)
    ScreenGear.setVisible(True) 
    ScreenGear.execute
end sub

Finally, a note - the position of the dialog is given relative to the upper left corner of the main Writer window, if you need coordinates strictly relative to the screen you need to add the offset of this window from the top/left edge of the screen.

1 Like

Well, it seems you’ve been quite diligent :hugs:

Hi there

Try these:

This returns the X/Y coordinates of the selected text on the document window

Sub GetSelectedTextCoordinates()

    Dim oDoc As Object
    Dim oViewCursor As Object
    Dim oTextRange As Object
    Dim oWindow As Object
    Dim oPos As New com.sun.star.awt.Point

    ' Get the current document
    oDoc = ThisComponent

    ' Get the view cursor
    oViewCursor = oDoc.CurrentController.getViewCursor()

    ' Get the text range of the selected text
    oTextRange = oViewCursor.getStart()

    ' Get the window of the current controller
    oWindow = oDoc.CurrentController.Frame.ContainerWindow

    ' Get the screen position of the selected text
    oPos = oDoc.CurrentController.getViewCursor().getPosition()

    ' Display the coordinates
    MsgBox "X: " & oPos.X & ", Y: " & oPos.Y

End Sub

This returns the screen coordinates of the selected text

Sub GetSelectedTextScreenCoordinates()

    Dim oDoc As Object
    Dim oViewCursor As Object
    Dim oTextRange As Object
    Dim oWindow As Object
    Dim oPos As New com.sun.star.awt.Point
    Dim oScreenPos As New com.sun.star.awt.Point

    ' Get the current document
    oDoc = ThisComponent

    ' Get the view cursor
    oViewCursor = oDoc.CurrentController.getViewCursor()

    ' Get the text range of the selected text
    oTextRange = oViewCursor.getStart()

    ' Get the window of the current controller
    oWindow = oDoc.CurrentController.Frame.ContainerWindow

    ' Get the position of the selected text in the document window
    oPos = oViewCursor.getPosition()

    ' Calculate the absolute screen coordinates
    oScreenPos.X = oWindow.getPosSize().X + oPos.X
    oScreenPos.Y = oWindow.getPosSize().Y + oPos.Y

    ' Display the screen coordinates
    MsgBox "Screen X: " & oScreenPos.X & ", Screen Y: " & oScreenPos.Y

End Sub

Thank you for your suggestions! I have reworked it in various ways, but the resulting X/Y does not meet the two assumptions I stated in the topic. It is not absolutely related to the whole screen and it is not in pixels. And these are the basic conditions for the solution I am looking for.

If you’r OS is Windows you can always use Windows API functions with LibreOffice Basic.
With the functions below you can retrieve and manipulate almost everything that happens on your screen.

REM  *****  BASIC  *****
Option VBASupport 1

Private Declare Function GetCursorPos Lib "user32" ( _
  ByRef lpPoint As POINT) As Long

Private Declare Function GetWindowRect Lib "user32" ( _
  ByVal hWnd As Long, ByRef lpRect As RECT) As Long
  
Private Declare Function GetSystemMetrics32 Lib "User32" _
    Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
    
Private Declare Function GetDeviceCaps Lib "gdi32" ( _
  ByVal hDC As Long, ByVal nIndex As Long) As Long
  
Private Declare Function GetDC Lib "user32" ( _
  ByVal hWnd As Long) As Long

Private Declare Function ReleaseDC Lib "user32" ( _
  ByVal hWnd As Long, ByVal hDC As Long) As Long

Private Const LOGPIXELSX = 88
Private Const LOGPIXELSY = 90
Private Const TwipsPerInch = 1440

Type POINT
  X As Long
  Y As Long
End Type

Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type
  
Const LOGPIXELSX = 88
Const LOGPIXELSY = 90 
Const TwipsPerInch = 1440

Sub ScreenRes()

    Dim w As Long, h As Long
    w = GetSystemMetrics32(0) ' width in points
    h = GetSystemMetrics32(1) ' height in points
    MsgBox w &  " " & h
    Dim Pixels As POINT
    Pixels = TwipsToPixels(w, h)
    MsgBox Pixels.X & " " & Pixels.Y
    
End Sub

Function TwipsToPixels(ByVal X As Long, ByVal Y As Long) As POINT

  Dim ScreenDC As Long
  Dim Pixels As POINT
  ScreenDC = GetDC(0)

  ' Convert twips to pixels
  Pixels.X = X / TwipsPerInch * GetDeviceCaps(ScreenDC, LOGPIXELSX)
  Pixels.Y = Y / TwipsPerInch * GetDeviceCaps(ScreenDC, LOGPIXELSY)

  ReleaseDC 0, ScreenDC
  
  TwipsToPixels = Pixels
  
End Function

Unfortunately, I have no experience with non-MS operating systems or non-PC devices.

Thx! It’s greate tip. But I am working on another method without VBA and independent of OS. I will post it in this topic when it will be work. Your sugestion may help in testing (I am working on MS WIN).

How about this:

*****  BASIC  *****
REM EDIT:
REM Option VBASupport 1 
REM Only the Round function needs VBA Support in the code

Sub GetDocumentSelectedTextCoordinates()

	Dim oDoc As Object
	Dim oViewCursor As Object
	Dim oTextRange As Object
	Dim oWindow As Object
	Dim oPos As New com.sun.star.awt.Point
	
	' Get the current document
	oDoc = ThisComponent
	textSelected = oDoc.CurrentSelection.getByIndex(0).getString()

	If textSelected = "" Then
		MsgBox "No text selected"
		Exit Sub
	End If

	' Get the view cursor
	oViewCursor = oDoc.CurrentController.getViewCursor()

	' Get the text range of the selected text
	oTextRange = oViewCursor.getStart()

	' Get the window of the current controller
	oWindow = oDoc.CurrentController.Frame.ContainerWindow

	' Get the screen position of the selected text
	oPos = oDoc.CurrentController.getViewCursor().getPosition()

	Dim locationX, locationY
	locationX = (oPos.X / TwipsPerPixelX())
	locationY = (oPos.Y / TwipsPerPixelY())
	MsgBox "X:" & Round(locationX) & " Y:" & Round(locationY)
    
	REM Rule of thumb:
	REM The number of pixels cannot be expressed as a fraction of a pixel.
	REM All calculations that result in a fractional pixel are rounded to
	REM the nearest full pixel.

End Sub

Function Round(m, Optional n)
	' Round a number "m" to "n" decimal places
	' If "n" isn't supplied, round to zero decimal places
	If IsMissing(n) Then n = 0
	Round = Int(m*10^n + .5)/10^n
End Function

I assume you know that Declaring API calls in VBA7 are different from what StarBasic accepts, i.e. StarBasic does not accept the Declare PtrSafe Function, nor the LongPtr variable. However, more information can be found here

Next great tip! In LO API are this variables: LibreOffice: com::sun::star::util::MeasureUnit Constant Group Reference

I wish you luck with your project. I would still like to point out that you have a better chance of success with this and your possible future projects if you clarify the difference between a constant and a variable. Namely, the page you refer to with your link does not say anything about variables, only constants.