lørdag den 19. november 2016

Makroer med arrays

Jeg har i flere artikler benyttet arrays til at håndtere data. En variabel er en midlertidig erstatning for en værdi, hvor et array er en midlertidig erstatning for en liste af værdier.

I eksemplerne tildeles variable og arrays værdier i koden, men i virkeligheden vil værdierne være generiske, og f.eks. komme fra celler i et regneark, en InputBox eller blive indlæst fra en fil.

Name="John"    'Variabel
Names()=Array("Peter", "Poul", "Birthe", "Anne") 'Array


Jeg navngiver array'et Names() men kunne lige så godt have skrevet Names.

Vi kan undersøge om en variabel indeholder et array:

IsArray (Names())        'True

Indholdet af et array kan vi finde frem med et indeks:

Print Names(1)            '"Poul"

Som standard er et array indeks-nul, hvilket betyder at det første element i array'et har indeks 0. Det er muligt at anvende indeks 1, men det er upraktisk, og i resten af artiklen antager vi at vi benytter indeks-nul.

LBound(Names()) udtrykker den nederst grænse, hvilket normalt er 0.

UBound (Names()) udtrykker den øverste grænse, altså hvor mange elementer der er (men husk at lægge en til, for at kompensere for indeks-nul).

UBound af et tomt array returnerer -1:

NavneArray=Array()
Print UBound(NavneArray)     '-1 hvis array er tom


Et lidt større eksempel:

Sub SimpleArray
    Names=Array("Peter", "Poul", "Birthe", "Anne")
    Print IsArray(Names)    'True
    Print Names(1)        'Poul
    Print LBound(Names)    '0
    Print UBound(Names)    '3
End Sub


Arrays er tollerante for datatyper:

Names=Array("Peter", "Poul", "Birthe", "Anne", 15)
Print UBound(Names)    '4
Print Names(4)    '15


Vi kan også tildele elementer på en anden måde:

Dim Persons(4)
Persons(0) = "Peter" : Persons(1) = "Poul": Persons(2) = "Birthe" : Persons(3) = "Anne" : Persons(4) = 15


Hvis du ønsker at tilføje et nyt element til et eksisterende array, skal du først gøre plads (redimensionere):

ReDim Persons(5)
Persons(5) = "John"


Et array kan også indeholde flere dimensioner:

Sub ComplexArray
Dim Persons(3,2)
Persons(0,0)="Peter":Persons(1,0)="Poul":Persons(2,0)="Birthe":Persons(3,0)="Anne"
Persons(0,1)=17:Persons(1,1)=21:Persons(2,1)=45:Persons(3,1)=12
Persons(0,2)="Mand":Persons(1,2)="Mand":Persons(2,2)="Kvinde":Persons(3,2)="Kvinde"

For i=0 to UBound(Persons(),1)          'Øvre grænse første dimension
    v=""                                'Nulstil
    For j = 0 to UBound(Persons(),2)    'Øvre grænse anden dimension
        v= v+Persons(i,j) + chr(13)     'chr(13) = linjeskift
    Next j
    MsgBox(v)
Next i
End Sub

Vi kan konvertere tekst til arrray og omvendt. 
Split opdeler en tekststreng, og returnerer et array.
Join samler elementerne fra et array og returnerer en tekststreng.

Eksempel:

Sub Split_Join
Navneliste="Peter, Poul, Birthe, Anne"    'Tekststreng
Print IsArray(Navneliste)
NavneArray()=Split(Navneliste,",")
Print IsArray(NavneArray)
For i = 0 to UBound(NavneArray)
    NavneArray(i) = Trim(NavneArray(i))   'Fjern mellemrum
Next i
NyNavneListe()= Join(NavneArray,"#")      'Ny tekststreng
Print NyNavneListe                        'Peter#Poul#Birthe#Anne
End Sub

onsdag den 16. november 2016

Ungarns regering investerer i open source kontorarbejdsplads

Ungarns regering har besluttet at investere hvad der svarer til 600.000 EUR i et pilotprojekt i forsøget på at skabe en komplet arbejdsplads (PC-konfiguration) baseret på open source software. Arbejdspladsen skal i første omgang bestå af et program til e-mail, en webbrowser og en kontorpakke. Samtidig skal der udvikles et sæt skabeloner til kontorpakken.

Baggrunden er at regeringen føler sig bundet af altid at skulle købe ind hos den samme gruppe af forhandlere, hvilket betyder kunstigt høje priser.

Til at overvåge pilotprojektet premierministerens kontor engageret sammen med National University of Public Service.

Kilde: https://joinup.ec.europa.eu/node/156821

tirsdag den 15. november 2016

Dialoger og makroer

Jeg har skrevet om dialog med brugeren i en tidligere artikel her: http://libreofficedk.blogspot.dk/2016/11/dialog-med-brugeren.html

Men den vigtigste og mest avancerede form for dialog med brugeren er...dialoger.

Når vi skal arbejde med dialoger i LibreOffice makroer, skal vi igennem en række trin:

1) Designe dialogen
2) Programmere eksekvering af dialogen
3) Programmere eksekvering af OK og aflæse brugerens valg
4) Programmere Annuller
5) Knytte makroerne til (OK og Annuller) knapper i dialogen

Vi starter fra toppen og hen ad vejen lærer vi begreberne lidt hen ad vejen.

1) Oprette og designe dialog

Du kan oprette en dialog fra LibreOffice med Funktioner - Makroer - Administrer dialoger... eller fra Funktioner - Makroer - Administrer makroer...

Fra makroeditoren ser du i bunden en omvendt fane, med navnet "Module1" Der er fordi du har et enkelt makromodul med navnet Module1. Højreklik og vælg Indsæt - BASIC-dialog.

Nu er der endnu en omvendt fane, sandsynligvis med navnet "Dialog1". Samtidig ser du i det store vindue en dialog-editor. Nederst i skærmbilledet ser du en palette af værktøj, som du kan bruge til at designe din dialog. Her finder du også de kontrolelementer, som du kan indsætte i dialogen. På værktøjslinjen er også et ikon med navnet Forhåndsvisningsdialog, som du kan bruge til at vise hvordan dialogen ser ud når den eksekveres.


Hvis du markerer dialogen, ser du i venstre side af skærmbilledet et område, hvod du kan indtaste forskellige egenskaber om dialogen, blandt andet give den et nyt navn (ikke synligt i dialogen) eller tildele en titel (synligt i dialogen).

Nu kan du tilføje kontrolelementer til dialogen, blandt andet felter og knapper. Eksperimenter med forskellige typer af kontrolelementer.

Rullelisten fodrer du med valgmuligheder i egenskaben Listeelementer - en valgmulighed per linje.




2) Eksekvere af dialogen

Dialogen kan vi eksekvere fra programkode i BASIC. Der er to forskellige måder:

Sub Dialog1Show
    BasicLibraries.LoadLibrary("Tools")
    oDialog1 = LoadDialog("Standard", "Dialog1")
    oDialog1.Execute()
End Sub

Hvis dialogen opbevares i et dokument, skal du anvende GlobalScope.BasicLibraries.LoadLibrary("Tools") i den første linje.

Uden brug af "LoadDialog" kan du kalde koden som følger:

Sub Dialog1Show
    DialogLibraries.LoadLibrary("Standard")
    oDialog1 = CreateUnoDialog( DialogLibraries.Standard.Dialog1 )
    oDialog1.Execute()
End Sub

Sidstnævnte eksempel er sikrest og lettest at få til at fungere, specielt hvis makroen benytter dokumentet som makrobibliotek.

3) Programmere eksekvering af OK og aflæse brugerens valg

Eksekvering af OK-knappen skal igangsætte en række begivenheder, som skal foregå i den rigtige rækkefølge.

Først skal vi finde data i de forskellige kontrolelementer for til sidst at lukke dialogen.

Når vi skal aflæse hvad brugeren har udfyldt i formularen, skal vi kende lidt til hvordan LibreOffice "tænker". Dialogen har nogle egenskaber, som vi kan aflæse af variablen oDialog1. F.eks. er oDialog.Title= "Min helt egen dialog" og oDialog1.Size.Height=235.

Dialogens indhold derimod, finder vi i dialogens model.

oModel= oDialog.Model

F.eks. kan vi ændre dialogens baggrundsfarve sådan:

oModel.BackgroundColor = rgb(255,255,255)

De enkelte kontrolelementer kan vi få fat i med deres navn:

oModel.getByName("TextField1")

Det vi skal nå frem til er at aflæse indholdet af de forskellige kontrolelementer. Vi skal oprette en ny subrutine, som eksekveres når OK-knappen trykkes på. Fordi variablen oDialog1 blev dimensioneret udenfor subrutinen, er den global og eksisterer altså stadig når vi kommer ud af den subrutine som åbner dialogen.

Sub OK_trykket
    oModel= oDialog1.Model
    Navn= oModel.getByName("TextField1").Text

    Print Navn

    oDialog1.endExecute()
End sub

Det skal vi gøre for hver enkelt felt brugeren kan udfylde. For tekstfeltet (TextField1) skal vi hente indformationen med .text, men for de øvrige felttyper er det lidt anderledes.

Radioknapperne håndterer vi ved for hver enkelt at aflæse .State:

    Linux= oModel.getByName("OptionButton1").State
    Mac= oModel.getByName("OptionButton2").State
    Windows= oModel.getByName("OptionButton3").State

Listboksen (Region) er lidt speciel, fordi vi skal håndtere at der ikke behøver at være noget vlagt. Heldigvis er det forholdsvis let. Desuden får vi i første omgang kun oplysninger om hvilket element (nummer i rækken) der er valgt (RegionNumber). Det skal vi så efterfølgende oversætte til noget brugbar tekst. Det gør vi sådan:

    Region= oModel.getByName("ListBox1").StringItemList(RegionNumber)

Tjekboksen Nyhedsbrev aflæser vi også med .State

Til sidst skal vi så aflevere informationerne, hvilket jeg af praktiske årsager går ved at vise en MsgBox. Måske skulle vi indsætte resultatet i celler i et regneark? Det må blive en anden dag.

Hele makroen (som starter med den subrutine som eksekverer dialogen) tilknyttes OK-knappen. I dialog-editoren vælger du OK-knappen og kigger i venstre side. Her ser du egenskaber for knappen. Vælg fanen "Hændelser" og find hændelsen "Museknap trykket". Tilknyt makroen til hændelsen.

4) Programmere Annuller-knappen

Programmet som vi skal bruge til at lukke dialogen uden videre er temmelig meget mere overskuelig:

Sub CloseDialog
  oDialog1.endExecute()
End Sub

Denne makro skal så tilknyttes til Annuller-knappen.

Hele makroen


Herunder er hele makroen:

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


Dim oDialog1 as Object

Sub Dialog1Show
    DialogLibraries.LoadLibrary("Standard")
    oDialog1 = CreateUnoDialog( DialogLibraries.Standard.Dialog1 )   
    oModel= oDialog1.Model
    oModel.BackgroundColor = rgb(255,255,255)
    oDialog1.Execute()
End Sub

Sub CloseDialog
  oDialog1.endExecute()
End Sub

Sub OK_trykket
    oModel= oDialog1.Model
    Navn= oModel.getByName("TextField1").Text
    Linux= oModel.getByName("OptionButton1").State
    Mac= oModel.getByName("OptionButton2").State
    Windows= oModel.getByName("OptionButton3").State

    If ubound (oModel.getByName("ListBox1").SelectedItems) >-1 then
        RegionNumber=oModel.getByName("ListBox1").SelectedItems(0)
        Region= oModel.getByName("ListBox1").StringItemList(RegionNumber)
    End if

    Nyhedsbrev=oModel.getByName("CheckBox1").State

    oDialog1.endExecute()

    MsgBox("Navn: " & Navn & Chr(13) & "Linux: " & Linux & Chr(13) & "Mac: " & Mac  & Chr(13) & "Windows: " & Windows  & Chr(13) & "Region: " & Region & Chr(13) & "Nyhedsbrev: " & Nyhedsbrev)

End sub




mandag den 14. november 2016

Nyt i 5.3: Indsæt PDF som billeder

Version 5.3 frigives en gang i starten af januar eller februar 2017.

En af de nye funktioner vi kan se frem til, er muligheden for at indsætte PDF-filer som var det et billede. Filen fremstår som et billede, og det er "kun" første side der er synlig. Hele PDF-filen gemmes i dokumentet og kan genskabes med et højre-klik og "Gem".

I LibreOffice dokumentet fremstår den indsatte fil som var det et billede, og du kan bearbejde billedet, f.eks. beskære, spejlvende det eller tilføje kanter.

Af hensyn til tidligere versioner af LibreOffice gemmes der også en png-version af PDF-filens første side, således at den indsatte fil også vises med tidligere versioner.

Ulempen er dog, at hvis du indsætter en PDF-fil i et dokument med LibreOffice 5.3 og efterfølgende åbner og gemmer det med en tidligere version af LibreOffice, så forsvinder PDF-filen.

Læs programmørens beskrivelse af løsningen her: http://vmiklos.hu/blog/lo-insert-pdf-image.html

søndag den 13. november 2016

Dialog med brugeren

Når vi programmerer makroer i LibreOffice, vil vi fra tid til anden, gerne involvere brugeren. Resultatet af brugerens valg kan vi så anvende til at regne videre eller dirigere programmet hen i den rigtige retning.

Vi har flere forskellige metoder:

Print

Print-funktionen er den simpleste måde at kontakte brugeren på. Du sender en besked i form at en tekst, men får ikke noget resultat tilbage.


Print-funktionen printer enkeltlinjer som særskilte dialoger. Hver af følgende vil derfor resultere i to små dialoger:

Print "expression1" & chr(13) & "expression2"    'chr(13) svarer til "Enter" (Carriage return)
Print "expression1" & chr(10) & "expression2"    'chr(10) svarer til "Skift" + "Enter" (Line feed)


Print bruges ofte til at vise resultatet af en variabel:

Print date(now)

Print kan håndtere alle dataformater, så det er ligemeget om du printer en dato, en tekst eller et tal. Dog er det ikke muligt at kombinere variable med forskellige datatyper. Derfor skal du selv konvertere f.eks. en dato, før du kan kombinere den med en tekst.

Selv bruger jeg kun Print til debugging, hvor jeg indsætter en masse print-sætnigner i koden, som udskriver variabel-værdier.

Der er flere parametre for Print-sætnignen som jeg ikke kommer mere ind på. Du kan selv kigge i hjælpen for mere information.

MsgBox

MsgBox er mere egnet for egentlig bruger-dialog. Dialogen består i at du kan lytte til hvad brugeren svarer tilbage (hvilekt vlag brugeren gjorde).



Syntaks:

MsgBox (Text As String [,Type As Integer [,DialogTitle As String]])

Resultatet af brugerens valg kan du få fat i ved at tilknytte MsgBox til en variabel:

Result= MsgBox ("Tekst", type "Overskrift")

Teksten kan, til forskel fra Print, godt indeholde linjeskift, hvilket er praktisk, hvis du f.eks. skal vise en liste over værdier. Men det mest interessante er parameteren 'Type', som giver dig kontrol over, hvordan dialogen ser ud.

Ikoner og knapper som brugeren kan trykke på kontrolleres sådan:

0 : Vis kun knappen OK.
1 : Vis knapperne OK og Annuller.
2 : Vis knapperne Annuller, Prøv igen og Ignorer.
3 : Vis knapperne Ja, Nej og Annuller.
4 : Vis knapperne Ja og Nej.
5 : Vis knapperne Prøv igen og Annuller.
16 : Føj stop-ikonet til dialogen.
32 : Føj spørgsmåls-ikonet til dialogen.
48 : Føj udråbs-ikonet til dialogen.
64 : Føj informations-ikonet til dialogen.
128 : Første knap i dialogen som standardknap.
256 : Anden knap i dialogen som standardknap.
512 : Tredje knap i dialogen som standardknap.

Hvis du ønsker at kombinere valgmulighederne, skal du lægge værdierne sammen, f.eks. 3+64=67 svarer til "Vis knapperne Ja, Nej og Annuller" og "Føj informations-ikonet til dialogen".

Returværdier (returneres til dig i variablen):
1 : OK
2 : Annuller
3 : Afbryd
4 : Prøv igen
5 : Ignorer
6 : Ja
7 : Nej

Om du angiver parameteren Type som et tal eller et regnestykker er det samme:

Result= MsgBox ("Tekst", 67+512, "Overskrift")
Result= MsgBox ("Tekst", 579, "Overskrift")


Resultatet vil dog altiv være 6 (Ja), 7 (Nej) eller 2 (Annuller).

MsgBox returnerer altid resultater fra en afgrænset mængde af muligheder, og Select...Case er dermed en oplagt måde at håndtere resultatet på. Eksempel:

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

Sub Main
Resultat = MsgBox("Tryk på en tast", 67, "Tastetryk")

Select Case Resultat

Case 6
    Print "Du trykkede på Ja"
Case 7
    Print "Du trykkede på Nej"
Case 2
    Print "Du trykkede på Annuller"

end Select
End Sub


InputBox

Med InputBox kan du bede brugeren om at indtaste en tekst, som du så modtager retur. Syntaksen er:

InputBox (Msg As String[, Title As String[, Default As String[, x_pos As Integer, y_pos As Integer]]]])

Returværdien er altid en tekststreng, også selvom brugeren skriver et tal. Hvis du beder brugeren om at indtaste et tal, skal du selv konvertere tekststrengen til et tal.

Et eksempel:

    sText = InputBox ("Indtast en sætning:","Kære bruger","Jeg gætter på")
    Print sText




Læg mærke til at resultatet kan aflæses af variablen sText.

Endnu et eksempel:
   
Sub InputBoxTest
    Resultat = InputBox("Indtast et tal","Tal", 8)
    ResultatTal = Val(Resultat)
    Print Resultat + 12        '812
    Print ResultatTal + 12    '20
End Sub



Jeg bruger InputBox i eksemplet i artiklen "Regneark i Calc (Årskalender)", som du kan læse her: http://libreofficedk.blogspot.dk/2016/10/regneark-i-calc-arskalender.html

this_year = InputBox ("Indtast årstal mellem 1583 og 3000", "Vælg årstal", year(date))
If (1583 > val(this_year) OR val(this_year) > 3000) then
    MsgBox("Årstallet skal være mellem 1583 og 3000", 48, "Fejl")
Stop
End If


I eksemplet bruger jeg year(date) som standardværdi (indeværende år).

Dialog

Det er muligt at designe sine helt egne dialoger i LibreOffice. Det emne er lidt mere kompliceret, og det vil jeg behandle i en særskilt artikel senere.

Hvad nu hvis?

Programkontrol er noget af det centrale i al programmering. Programmet skal gerne udføre handlinger afhængigt af forskellige betingelser. Betingelserne kan være noget brugeren indtaster, noget der opstår som følge af tid, eller noget der kommer fra data. Under alle omstændigheder er det altid noget vi kan måle og veje.

IF-sætning

Den mest brugte måde at styre programmet på, er med IF-sætninger. If...then...else. HVIS en bestemt betingelse er opfyldt SÅ skal der ske noget bestemt ELLERS skal der ske noget andet.

If betingelse=true Then
  programudtryk
[ElseIf betingelse=true Then]
  programudtryk
[Else]
  programudtryk
End If


  • Betingelsen skal altid resultere i en boolean (True/False).
  • Programudtryk er et stykke program som skal udføres.
Det er et krav at blokken starter med IF og slutter med END IF, men der behøver ikke være andet end en enkelt betingelse og et enkelt programudtryk. Resten er valgfrit, men jeg vil anbefale altid at anvende en ELSE-blok, bare for at være sikker på at tage hånd om fejl og undtagelser.

Et eksempel:

If x < 0 Then
  MsgBox "Tallet er negativt"
ElseIf x > 0 Then
  MsgBox "Tallet er positivt"
Else
  MsgBox "Tallet er nul"
End If


Det er muligt at indbygge IF-sætninger i IF-sætninger, hvilket ikke er usædvanligt at se.

If...Then...Else ses også ofte brugt som manuel fejlhåndtering, f.eks. til at kontrollere for helt konkrete fejlsituationer. Men det er en helt anden historie.

IIF-sætning

En mere simpel løsning er en IIF-sætning:

IIf (betingelse, SandUdtryk, FalskUdtryk)

Eksempel:

hoejeste_alder = IIf(johns_alder > peters_alder, johns_alder, peters_alder)

Samme blok kunne vi have skrevet med en IF-sætning sådan:

If johns_alder > peters_alder Then
  hoejeste_alder= johns_alder
Else
  hoejeste_alder= peters_alder
End If


IIF-sætningen egner sig bedst til små og korte betingelses-blokke.

CHOOSE-sætning

Choose-sætningen giver dig muighed for at vælge fra en liste, baseret på et indeks.

Choose (Indeks, Valg_1[, Valg_2, ... [,Valg__n]])

Hvis Indeks er 1 returneres Valg_1, hvis Indeks er 2 returneres Vlag_2 osv.

FOR...NEXT-loop

Denne funktion gentager en blok et bestemt antal gange.

For taeller=start To slut [Step trin]
  programudtryk
  [Exit For]
  programudtryk
Next [taeller]


Det er muligt at angive hvordan loopet skal tælle ("Step trin"), men denne parameter udelades ofte, hvilket også gør sig gældende for "Exit For".

Et eksempel:

  For n = LBound(oPrinter) To UBound(oPrinter)
    sMsg = sMsg + oPrinter(n).Name + Chr(13)
  Next n


I eksemplet herover bruger vi UBound(...) som aflutning, hvilket er særdeles praktisk. UBound angiver øvre grænse eller største indeksværdi i et array. På den måde kan for..next bruges til at gennemløbe et array trin for trin.

I min tidligere artikel om årskalender bruger jeg for...next loop rigtig meget: http://libreofficedk.blogspot.dk/2016/10/regneark-i-calc-arskalender.html


DO WHILE loop

Så længe en betingelse er opfyldt, fortsæt med at udføre ...

Do While betingelse
  programudtryk
Loop


Alternativ syntakt:
Indtil en betingelse er opfyldt, fortsæt med at udføre ...

Do Until betingelse
  programudtryk
Loop


Et eksempel fra min artikel om fortløbende nummerering:

Do While NOT EOF(n)          
    Input #n, s                
    If result="" then
        result = s
    Else
        result = result + chr(13) + s
    end if
Loop


Hele artiklen kan læses her: https://libreofficedk.blogspot.dk/2016/11/fortlbende-nummerering-fortsat_8.html

Vær meget opmærksom på at Do...While...Loop er lidt farlig, fordi den kan ende med et af enhver programmørs værste mareridt, nemlig et endeløst loop. Du skal være 100% sikker på at du faktsik når frem til betingelsen. Ellers vil din makro fortsætte uendeligt (eller til en bruger vælger at afbryde den).

SELECT...CASE-sætning

Vælg fra en liste baseret på et valg.

Syntaks:

Select Case betingelses_udtryk
  Case tilfælde_1
    programudtryk_1
  Case tilfælde_2
    programudtryk_2
  Case Else
    programudtryk_3
End Select


Select...Case bruges ofte i tilfælde, hvor antallet af valgmuligheder er fastlagt af omstændighederne. Et godt eksempel er ugedagene, som der er syv af, og det er temmelig fastsat på forhånd.

Funktionen Weekday(dato) returnerer et heltal mellem 1 og 7, hvor 1 er søndag.

    Select Case WeekDay( Now )
        Case 1
            sDay="Søndag"
        Case 2
            sDay="Mandag"
        Case 3
            sDay="Tirsdag"
        Case 4
            sDay="Onsdag"
        Case 5
            sDay="Torsdag"
        Case 6
            sDay="Fredag"
        Case 7
            sDay="Lørdag"
    End Select
    Msgbox sDay,64,"I dag er det"

torsdag den 10. november 2016

Undersøge markeret tekst i Writer (med makro)


Vi kan med en makro aflæse hvad der er markeret i et tekstdokument.

Problemet er at vi ikke på forhånd kan vide om der overhovedet er markeret noget, og om markeringen dækker et enkelt eller flere områder. Flere markerede områder er hvis du holder CTRL-tasten nede mens du markerer flere områder.

Vi kommer til at anvende to centrale begreber: Selection og Cursor
Selection er en liste med alle markerede områder:

oSelections = ThisComponent.getCurrentSelection()

Resultatet kan f.eks. vises sådan:

Print oSelections.getCount

For at udlede noget af det markerede, må vi håndtere hver enkelt markeret område et ad gangen. Her aflæser vi helt konkret hvad der står i hver enkelt markering:

Sub Main
oSelections = ThisComponent.getCurrentSelection()
For i = 0 To oSelections.getCount() - 1
oSel = oSelections.getByIndex(i)
Print oSel.String
Next
End Sub

Der findes to typer cursor i LibreOffice makroer:
  • ViewCursor ved, hvordan dataene vises, men ikke noget om data. 
  • TextCursor (non-view cursor), ved en masse om data, men meget lidt om, hvordan de vises. 
For eksempel kender ViewCursor ikke hvilke ord der er markeret, og TextCursor kender intet til linjer, skærme, eller sider.

Vi skal bruge en TextCursor ud fra hver enkelt af vores markeringer:

oCursor = oDoc.Text.CreateTextCursorByRange(oSel)

Nu kan vi kontrollere om noget er markeret, hvilket lyder enkelt, men tro om igen. Hvis antallet er markeringer er 0, så er intet markeret. Men der er altid en markør i dokumentet, så antallet af markeringer er aldrig 0. Principielt kan vi komme ud for at der ingen markering er, hvis LibreOffice afvikles på en server, uden brugergrænsefladen vises.

Hvis længden af markeringen er 0, er intet markeret. Længden skal måles på hver enkelt markering, hvilket er lidt farligt. Det er fordi en tekststreng ikke kan/må være længere end 64k, så brug ikke den metode. Du risikerer at LibreOffice crasher.

Den bedste måde er at skabe en tekstcursor fra den aktuelle markering, og så undersøge om start og slutpunktet er det samme. Kontrol af om der er noget markeret er altså relativt kompliceret men dog muligt. Vi kan lave denne funktion til formålet:

Function IsAnythingSelected(oDoc As Object) As Boolean
Dim oSelections 'Contains all of the selections
Dim oSel 'Contains one specific selection
Dim oCursor 'Text cursor to check for a collapsed range
REM Assume nothing is selected
IsAnythingSelected = False
If IsNull(oDoc) Then Exit Function
' The current selection in the current controller.
'If there is no current controller, it returns NULL.
oSelections = oDoc.getCurrentSelection()
If IsNull(oSelections) Then Exit Function
If oSelections.getCount() = 0 Then Exit Function
If oSelections.getCount() > 1 Then
REM There is more than one selection so return True
IsAnythingSelected = True
Else
REM There is only one selection so obtain the first selection
oSel = oSelections.getByIndex(0)
REM Create a text cursor that covers the range and then see if it is
REM collapsed.
oCursor = oDoc.Text.CreateTextCursorByRange(oSel)
If Not oCursor.IsCollapsed() Then IsAnythingSelected = True

REM You can also compare these to see if the selection starts and ends at
REM the same location.
REM If oDoc.Text.compareRegionStarts(oSel.getStart(),_
REM oSel.getEnd()) <> 0 Then
REM IsAnythingSelected = True
REM End If
End If
End Function

Funktionen IsAnythingSelected er fra bogen OpenOffice.org Macros Explained af Andrew D. Pitonyak.