Tässä hieman edistyneempi väännös LibreOffice Basic ympäristössä interaktiivisen dynaamisen taulukon luomiseksi valintaikkunaan.
'Komponentit:
'Valitaikkuna (Dialog1)
'Levyes: 140
'Korkeus 50
'Ohjausobjekti:
'Kaksi valintaruutua (CheckBox1, CheckBox2)
'Selite: Sisällytä data (CheckBox1)
'Selite: Sisällytä interaktiivisuus (CheckBox2)
'Komentopainike (CommandButton1)
'Sido: Suorita toiminto -> StartGridBuilding
'Yksi Label (niemeä: Header1) leveys 50, korkeus 11
'Taustaväri tummanharmaa
'Aseta piirtoalueen (Dialogin ulkopuolelle)
'Toinen Label (nimeä: RowHeader1) leveys 13, korkeus 11
'Taustaväri tummanharmaa (sama kuin Header1)
'Aseta piirtoalueen (Dialogin ulkopuolelle)
'Kolmas Label (Label1) leveys 50, korkeus 11
'Aseta piirtoalueen (Dialogin ulkopuolelle)
'Sido: Hiiren painike vapautettu -> LabelMouseUp
'Tekstiruutu (nimeä: TextField1) leveys 48, korkeus 9
'Aseta piirtoalueen (Dialogin ulkopuolelle)
'Sido: Tekstiä muokattu -> EditBoxTextChange
'Sido: Näppäin vapautettu -> EditBoxKeyUp
'Line objekti (nimeä: bottomL) vaaka, leveys 50, korkeus 2
'Taustaväri tummanharmaa
'Aseta piirtoalueen (Dialogin ulkopuolelle)
REM ***** BASIC *****
Private oDlg As Object
Private Continue As Boolean
'Private Done As Boolean
'Private baseUrl As String
Private oEditBox As Object
Private lastActive As Object
Private index As Long
Private oHeader As Object
Private rHeader As Object
Private bL As Object
Private oEditBoxOriginaX As Long
Private oEditBoxOriginaY As Long
Private blOriginalX As Long
Private blOriginalY As Long
Private rHeaderOriginaX As Long
Private rHeaderOriginaY As Long
Private ncols() As Long
Private nrows() As Long
Private Interactive As Boolean
Private includeData As Boolean
Type CellInfoStruct
X As Long
Y As Long
W As Long
H As Long
Row As Integer
Col As Integer
Value As String
Control As Object
Cell As Object
'Formula As Variant
'DataType As Variant
End Type
Private CellInfo() As CellInfoStruct
Sub ShowDialog
If Continue Then Exit Sub
DialogLibraries.LoadLibrary("Standard")
oDlg = CreateUnoDialog(DialogLibraries.Standard.Dialog1)
Dim oListenerTop As Object
oListenerTop=createUnoListener("TopListen_", "com.sun.star.awt.XTopWindowListener")
oDlg.addTopWindowlistener(oListenerTop)
oEditBox = oDlg.getControl("TextField1")
oEditBoxOriginaX = oEditBox.Model.PositionX
oEditBoxOriginaY = oEditBox.Model.PositionY
rHeader = oDlg.getControl("RowHeader1")
rHeaderOriginaX = rHeader.Model.PositionX
rHeaderOriginaY = rHeader.Model.PositionY
oHeader = oDlg.getControl("Header1")
bL = oDlg.getControl("bottomL")
blOriginalX = bl.Model.PositionX
blOriginalY = bl.Model.PositionY
CenterDialog
Continue = True
'Done = False
Do While Continue
Wait 20
oDlg.setVisible(true)
'If Not Done Then
'Done = True
'Do something
'End If
Loop
oDlg.dispose()
End Sub
Sub TopListen_WindowClosing
Continue = False
ThisComponent.setModified(False)
End Sub
Sub TopListen_windowOpened
End Sub
Sub TopListen_windowClosed
End Sub
Sub TopListen_windowMinimized
End Sub
Sub TopListen_windowNormalized
End Sub
Sub TopListen_windowActivated
End Sub
Sub TopListen_windowDeactivated
End Sub
Sub TopListen_disposing
End Sub
Sub MouseListener_mousePressed(oEvent)
' Ei tarvita
End Sub
Sub MouseListener_mouseReleased(oEvent)
Call LabelMouseUp(oEvent)
End Sub
Sub MouseListener_mouseEntered(oEvent)
End Sub
Sub MouseListener_mouseExited(oEvent)
End Sub
Sub MouseListener_disposing
End Sub
Sub CenterDialog
Dim oSize As New com.sun.star.awt.Size, factor As Double
Dim oCC, oComponentWindow, oTopWindowPosSize
oCC = ThisComponent.getCurrentController()
oComponentWindow = oCC.ComponentWindow
oTopWindowPosSize = oComponentWindow.Toolkit.ActiveTopWindow.getPosSize()
oSize.Width = oDlg.Model.Width
oSize.Height = oDlg.Model.Height
factor = oSize.Width / oDlg.convertSizeToPixel(oSize, com.sun.star.util.MeasureUnit.APPFONT).Width
With oDlg.Model
.PositionX = (factor * oTopWindowPosSize.Width - .Width) / 2
.PositionY = (factor * oTopWindowPosSize.Height - .Height) / 1.7
End With
End Sub
Sub StartGridBuilding
Dim oDoc As Object
Dim oSheet As Object
Dim oSel As Object
Dim startRow As Long, startCol As Long
Dim endRow As Long, endCol As Long
Dim rows As Long, cols As Long
If oDlg.getControl("CheckBox1").State = 1 Then
includeData = True
End If
If oDlg.getControl("CheckBox2").State = 1 Then
Interactive = True
End If
oDoc = ThisComponent
oSheet = oDoc.CurrentController.getActiveSheet()
oSel = oDoc.getCurrentSelection()
If oSel.supportsService("com.sun.star.sheet.SheetCellRanges") Then
MsgBox "Only one range can be selected at a time."
Exit Sub
End If
If Not oSel.supportsService("com.sun.star.sheet.SheetCellRange") Then
MsgBox "No range selected in the active sheet"
Exit Sub
End If
rows = oSel.Rows.Count
cols = oSel.Columns.Count
If rows > 28 Or cols > 13 Then
MsgBox "Max. Range: 28 Rows 13 Columns"
Exit Sub
End If
If rows < 2 Or cols < 2 Then
MsgBox " Please select a range containing " & Chr(10) & _
"at least two rows and two colunms"
Exit Sub
End If
startRow = oSel.RangeAddress.StartRow
startCol = oSel.RangeAddress.StartColumn
endRow = oSel.RangeAddress.EndRow
endCol = oSel.RangeAddress.EndColumn
oDlg.getControl("CommandButton1").dispose()
oDlg.Model.removeByName("CommandButton1")
oDlg.getControl("CheckBox1").dispose()
oDlg.Model.removeByName("CheckBox1")
oDlg.getControl("CheckBox2").dispose()
oDlg.Model.removeByName("CheckBox2")
oDlg.getPeer().invalidate(0)
Dim cnt As Long : cnt = 0
Redim ncols(cols)
For i = startCol To endCol
ncols(cnt) = (i + 1) - 1
cnt = cnt + 1
Next i
Redim nrows(rows) : cnt = 0
For i = startRow To endRow
nrows(cnt) = i + 1
cnt = cnt + 1
Next
cnt = 0
Dim totalWidth As Long, singleWidth As Long
Dim totalHeight As Long, singleHeight As Long, c As Long
totalWidth = 0 : totalHeight = 0
For c = startCol To endCol
singleWidth = 50
totalWidth = totalWidth + singleWidth
Next c
For c = startRow To endRow
singleHeight = 11
totalHeight = totalHeight + singleHeight
Next c
oSize = CreateUnoStruct("com.sun.star.awt.Size")
oSize.Width = totalWidth
oSize.Height = totalHeight
Dim oldWidth As Long, newWidth As Long
Dim oldHeight As Long, newHeight As Long
If oDlg.Model.Width < oSize.Width Then
oldWidth = oDlg.Model.Width
oDlg.Model.Width = totalWidth + 33
newWidth = oDlg.Model.Width
oDlg.Model.PositionX = oDlg.Model.PositionX - (newWidth - oldWidth) / 2
End If
If oDlg.Model.Height < oSize.Height + 10 Then
oldHeight = oDlg.Model.Height
oDlg.Model.Height = totalHeight + 25
newHeight = oDlg.Model.Height
oDlg.Model.PositionY = oDlg.Model.PositionY - ((newHeight - oldHeight) / 2)
If oDlg.Model.PositionY < 20 Then oDlg.Model.PositionY = oDlg.Model.PositionY + oDlg.Model.PositionY + 20
End If
oDlg.getPeer().setVisible(False)
oDlg.getPeer().setVisible(True)
Dim cell As Object, header As Object
cell = oDlg.getControl("Label1")
cell.Model.PositionX = 23
header = oDlg.getControl("Header1")
header.model.PositionX = 23
rheader = oDlg.getControl("RowHeader1")
rheader.model.PositionX = 10
rheader.model.PositionY = cell.Model.PositionY
LastActive = cell
BuildColumnHeaders cols, startCol, rows
BuildRowHeaders rows
BuildGridCells rows, cols, startRow, startCol, endRow, endCol
oEditBox.Model.Enabled = True
oEditBox.Model.PositionX = cell.Model.PositionX + 1
oEditBox.Model.PositionY = cell.Model.PositionY + 1
oEditBox.setFocus
oEditBox.Model.Align = LastActive.Model.Align
oEditBox.Model.Text = " "
SetTextSelection oEditBox
oEditBox.Model.Text = ""
bL.Model.PositionX = oEditBox.Model.PositionX - 1
bL.Model.PositionY = oEditBox.Model.PositionY - 1
Initialize
If includeData Then
GetRealSelectionCells
End If
End Sub
Sub GetRealSelectionCells
Dim oDoc As Object
Dim oSheet As Object
Dim oSel As Object
Dim oRange As Object
Dim aAddr As Object
Dim r As Long, c As Long
Dim oCell As Object
oDoc = ThisComponent
oSheet = oDoc.CurrentController.getActiveSheet()
oSel = oDoc.getCurrentSelection()
aAddr = oSel.RangeAddress
oRange = oSheet.getCellRangeByPosition( _
aAddr.StartColumn, aAddr.StartRow, _
aAddr.EndColumn, aAddr.EndRow )
Dim cnt As Long : cnt = 0
For r = 0 To oRange.Rows.getCount() - 1
For c = 0 To oRange.Columns.getCount() - 1
oCell = oRange.getCellByPosition(c, r)
CellInfo(cnt).Control.Model.Label = oCell.String
CellInfo(cnt).Value = oCell.String
CellInfo(cnt).Cell = oCell
cnt = cnt + 1
Next c
Next r
End Sub
Sub BuildRowHeaders(rowCount)
Dim rbase As Object
rbase = oDlg.Model.RowHeader1
Dim oNew As Object
Dim i As Long
prevX = rbase.PositionX
prevW = rbase.Width
prevH = rbase.Height
prevY = rbase.PositionY + prevH
prevAlign = rbase.Align
prevB = rbase.Border
prevPgc = rbase.BackGroundColor
rbase.Label = nrows(0)
For i = 2 To rowCount
oNew = oDlg.Model.createInstance("com.sun.star.awt.UnoControlFixedTextModel")
oNew.Name = "RowHeader" & i
oNew.BackGroundColor = prevPgc
oNew.PositionX = prevX
oNew.PositionY = prevY + prewH
oNew.Border = 1
oNew.Label = nrows(i-1)
oNew.FontWeight = com.sun.star.awt.FontWeight.BOLD
oNew.Align = prevAlign
oNew.Width = prevW
oNew.Height = prevH
oDlg.Model.insertByName(oNew.Name, oNew)
prevX = oNew.PositionX
prevY = oNew.PositionY
prevW = oNew.Width
prewH = oNew.Height
prevPgc = oNew.BackGroundColor
prevAlign = oNew.Align
Next i
End Sub
Sub BuildColumnHeaders(colCount, startc, rowCount)
Dim cbase As Object
Dim prevX As Long, prevW As Long
Dim i As Long, coln As Long
Dim oNew As Object
cbase = oDlg.Model.Header1
prevX = cbase.PositionX
prevW = cbase.Width
prevAlign = cbase.Align
prevB = cbase.Border
prevPgc = cbase.BackGroundColor
cbase.Label = ColumnNumberToName(startc)
For i = 2 To colCount
oNew = oDlg.Model.createInstance("com.sun.star.awt.UnoControlFixedTextModel")
oNew.Name = "Header" & i
oNew.BackGroundColor = prevPgc
oNew.PositionX = prevX + prevW
oNew.PositionY = cbase.PositionY
oNew.Border = 1
oNew.Label = ColumnNumberToName(ncols(i - 1))
oNew.FontWeight = com.sun.star.awt.FontWeight.BOLD
oNew.Align = prevAlign
oNew.Width = cbase.Width
oNew.Height = cbase.Height
oDlg.Model.insertByName(oNew.Name, oNew)
prevX = oNew.PositionX
prevW = oNew.Width
prevPgc = oNew.BackGroundColor
prevAlign = oNew.Align
Next i
End Sub
Function ColumnNumberToName(coln As Long) As String
Dim name As String
Dim r As Long
coln = coln + 1 ' Calc uses 0-based, convert to 1-based
Do
r = (coln - 1) Mod 26
name = Chr(65 + r) & name
coln = (coln - r - 1) \ 26
Loop While coln > 0
ColumnNumberToName = name
End Function
Sub BuildGridCells(rows, cols, srow, scol, erow, ecol)
Dim cbase As Object
Dim oNew As Object
Dim i As Long, j As Long
Dim nextIndex As Long
cbase = oDlg.getControl("Label1").Model
nextIndex = 2
For i = 1 To rows
For j = 1 To cols
' Ohita Label1 (i=1, j=1)
If Not (i = 1 And j = 1) Then
oNew = oDlg.Model.createInstance("com.sun.star.awt.UnoControlFixedTextModel")
oNew.Name = "Label" & nextIndex
nextIndex = nextIndex + 1
oNew.Width = cbase.Width
oNew.Height = cbase.Height
oNew.Border = cbase.Border
oNew.Align = cbase.Align
oNew.BackGroundColor = cbase.BackGroundColor
oNew.FontWeight = cbase.FontWeight
oNew.FontHeight = cbase.FontHeight
oNew.FontName = cbase.FontName
oNew.PositionX = cbase.PositionX + (j - 1) * cbase.Width
oNew.PositionY = cbase.PositionY + (i - 1) * cbase.Height
oNew.Label = ""
oDlg.Model.insertByName(oNew.Name, oNew)
End If
Next j
Next i
oDlg.Title = "Sheets " & ThisComponent.CurrentController.getActiveSheet().Name & _
" Range " & oHeader.Model.Label & srow + 1 & ":" & oDlg.getControl("Header" & Cstr(cols)).Model.Label & erow + 1
AddMouseListenerToAllLabels
End Sub
Sub AddMouseListenerToAllLabels()
Dim oMouse As Object
oMouse = CreateUnoListener("MouseListener_", "com.sun.star.awt.XMouseListener")
Dim i As Long
Dim name As String
Dim oCtrl As Object
i = 2
Do While oDlg.Model.hasByName("Label" & i)
name = "Label" & i
oCtrl = oDlg.getControl(name)
oCtrl.addMouseListener(oMouse)
i = i + 1
Loop
End Sub
Sub Initialize
Dim i As Integer, lblcount As Integer
Dim controls As Object
controls = oDlg.getControls()
lblcount = -1
For i = 0 To Ubound(controls)
If InStr(controls(i).Model.Name, "Label") > 0 Then
lblcount = lblcount + 1
End If
Next i
Redim CellInfo(lblcount)
Dim ctl As Object, lastctl As Object, col As Integer, row As Integer
col = 0 : row = 0
lastctl = oDlg.getControl("Label1")
For i = 0 To Ubound(CellInfo)
ctl = oDlg.getControl("Label" & CStr(i + 1))
CellInfo(i).Control = ctl
CellInfo(i).X = ctl.Model.PositionX
CellInfo(i).Y = ctl.Model.PositionY
CellInfo(i).W =ctl.Model.Width
CellInfo(i).H = ctl.Model.Height
If lastctl.Model.PositionX = ctl.Model.PositionX Then
CellInfo(i).Col = col
ElseIf lastctl.Model.PositionX < ctl.Model.PositionX Then
col = col + 1
CellInfo(i).Col = col
End If
If lastctl.Model.PositionY = ctl.Model.PositionY Then
CellInfo(i).Row = row
ElseIf lastctl.Model.PositionY < ctl.Model.PositionY Then
row = row + 1 : col = 0
CellInfo(i).Row = row
End If
'ctl.Model.Align = 2
CellInfo(i).Control = ctl
lastctl = ctl
ctl = Nothing
'If CellInfo(i).Col = 0 Then CellInfo(i).Control.Model.Align = 0 ' (0=Left, 1= Center, 2=Right)
Next i
If Not IsNull(thisComponent.Url) Then
dataUrl = Left(thisComponent.Url, InStrReverse(thisComponent.Url, "/")) & "CellValues.data"
GlobalScope.BasicLibraries.LoadLibrary("ScriptForge")
Set fso = CreateScriptService("FileSystem")
If fso.FileExists(dataUrl) Then
Dim inpFile As Object, fileData As String
Set inpFile= fso.OpenTextFile(dataUrl)
fileData = inpFile.ReadAll()
inpFile.CloseFile()
fileData = Replace(fileData, Chr(13) & Chr(10), "")
Set inpFile = Nothing
Set fso = Nothing
If fileData <> "" Then
Dim values() As String
values = Split(fileData, ";")
For i = Lbound(values ) To Ubound(values)
CellInfo(i).Value = values(i)
If IsNumeric(values(i)) Then
CellInfo(i).Control.Model.Align = 2
Else
CellInfo(i).Control.Model.Align = 0
End If
CellInfo(i).Control.Model.Label = values(i)
Next i
Erase values
End If
End If
End If
LastActive = oDlg.getControl("Label1") : Index = 0
oEditBoxOriginaX = oEditBox.Model.PositionX
oEditBoxOriginaY = oEditBox.Model.PositionY
End Sub
Sub LabelMouseUp(oEvent)
If oEvent.Buttons = 1 Then
If Not oEditBox.Model.Enabled Then oEditBox.Model.Enabled = True
If Not oEditBox.hasFocus Then oEditBox.setFocus()
oEditBox.Model.PositionX = oEvent.Source.Model.PositionX + 1
oEditBox.Model.PositionY = oEvent.Source.Model.PositionY + 1
bL.Model.PositionX = oEvent.Source.Model.PositionX
bL.Model.PositionY = oEvent.Source.Model.PositionY + oEvent.Source.Model.Height - 1
oEditBox.Model.Align = oEvent.Source.Model.Align
oEditBox.Model.Text = oEvent.Source.getText()
LastActive = oEvent.Source
index = Cint(Replace(LastActive.Model.Name, "Label", "")) - 1
SetTextSelection oEditBox
End If
End Sub
Sub EditBoxKeyUp(oEvent)
Dim i As Integer
If oEvent.KeyCode = com.sun.star.awt.Key.RETURN Then
LastActive.Model.Label = oEditBox.Model.Text
If Interactive Then
CellInfo(index).Cell.Value = LastActive.Model.Label
End If
CellInfo(index).Value = LastActive.Model.Label
Exit Sub
ElseIf oEvent.KeyCode = 1027 Then 'RightArrow
index = index + 1
If index > Ubound(CellInfo) Then index = 0
oEditBox.Model.PositionX = CellInfo(index).Control.Model.PositionX + 1
oEditBox.Model.PositionY = CellInfo(index).Control.Model.PositionY + 1
bL.Model.PositionX =CellInfo(index).Control.Model.PositionX
bL.Model.PositionY =CellInfo(index).Control.Model.PositionY + CellInfo(index).Control.Model.Height - 1
oEditBox.Model.Align = CellInfo(index).Control.Model.Align
oEditBox.Model.Text = CellInfo(index ).Value
LastActive = CellInfo(index).Control
oEditBox.Model.Text = LastActive.Model.Label
ElseIf oEvent.KeyCode = 1026 Then 'LeftArrow
index = index - 1
If index < 0 Then index = Ubound(CellInfo)
oEditBox.Model.PositionX = CellInfo(index).Control.Model.PositionX + 1
oEditBox.Model.PositionY = CellInfo(index).Control.Model.PositionY + 1
bL.Model.PositionX =CellInfo(index).Control.Model.PositionX
bL.Model.PositionY =CellInfo(index).Control.Model.PositionY + CellInfo(index).Control.Model.Height
oEditBox.Model.Align = CellInfo(index).Control.Model.Align
oEditBox.Model.Text = CellInfo(index).Value
LastActive = CellInfo(index).Control
oEditBox.Model.Text = LastActive.Model.Label
ElseIf oEvent.KeyCode = 1024 Then 'UpArrow
If CellInfo(index).Row < CellInfo(Ubound(CellInfo)).Row Then
For i = 0 To Ubound(CellInfo)
If CellInfo(i).Row > CellInfo(index).Row And CellInfo(i).Col = CellInfo(index).Col Then
index = i
oEditBox.Model.PositionX = CellInfo(index).Control.Model.PositionX + 1
oEditBox.Model.PositionY = CellInfo(index).Control.Model.PositionY + 1
bL.Model.PositionX =CellInfo(index).Control.Model.PositionX
bL.Model.PositionY =CellInfo(index).Control.Model.PositionY + CellInfo(index).Control.Model.Height
LastActive = CellInfo(index).Control
oEditBox.Model.Text = CellInfo(index).Value
Exit For
End If
Next i
End If
ElseIf oEvent.KeyCode = 1025Then 'DownArrow
If CellInfo(index).Row > 0 Then
For i = 0 To Ubound(CellInfo)
If CellInfo(i).Row = CellInfo(index).Row - 1 And CellInfo(i).Col = CellInfo(index).Col Then
index = i
oEditBox.Model.PositionX = CellInfo(index).Control.Model.PositionX + 1
oEditBox.Model.PositionY = CellInfo(index).Control.Model.PositionY + 1
bL.Model.PositionX = CellInfo(index).Control.Model.PositionX
bL.Model.PositionY = CellInfo(index).Control.Model.PositionY + CellInfo(index).Control.Model.Height
LastActive = CellInfo(index).Control
oEditBox.Model.Align = CellInfo(index).Control.Model.Align
oEditBox.Model.Text = CellInfo(index).Value
Exit For
End If
Next i
End If
End If
SetTextSelection oEvent.Source
End Sub
Sub SetTextSelection(Source As Object)
oView = Source.getView()
Source.setFocus()
oSelection = New com.sun.star.awt.Selection
textLen = Len(Source.Model.Text)
oSelection.Min = textLen
oSelection.Max = Len(Source.Model.Text)
oView.setSelection(oSelection)
oSelection = Nothing
End Sub
Sub DlgKeyUp(oEvent) 'Focus To EditBox
If oEvent.KeyCode = 1282 Then
oEditBox.Model.Enabled = True
oEditBox.setFocus
oEditBox.Model.PositionX = CellInfo(0).Control.Model.PositionX + 1
oEditBox.Model.PositionY = CellInfo(0).Control.Model.PositionY + 1
bLModel.PositionY = CellInfo(0).Control.Model.PositionY + CellInfo(0).Control.Model.Heght
oEditBox.Model.Enabled = True
oEditBox.Model.Text = CellInfo(0).Value
LastActive = CellInfo(0).Control
index = 0
End If
End Sub
Sub HeaderMouseDown(oEvent)
Dim sTitle As String
sTitle = oDlg.Title
If oEvent.ClickCount = 2 And oEvent.Buttons = 1 Then
If dataUrl <> "" Then
Dim i As Integer, dataStr As String
Open dataUrl For Output As #1
For i = Lbound(CellInfo) To Ubound(CellInfo)
dataStr = dataStr & CellInfo(i).Value
If i < Ubound(CellInfo) Then dataStr = dataStr & ";"
Next i
Print #1, dataStr : Close #1
End If
oDlg.Title = "Data tallennettu tiedostoon"
Wait 1000
oDlg.Title = sTitle
End If
End Sub
Sub EditBoxTextChange
If oEditBox.Model.Text = "" Then Exit Sub
If IsNumeric(oEditBox.Model.Text) Then
oEditBox.Model.Align = 2
LastActive.Model.Align = 2
Else
oEditBox.Model.Align = 0
LastActive.Model.Align = 0
End If
End Sub
Function InStrReverse(sText As String, search As String) As Long
If sText = "" Or search = "" Or Len(search) > Len(sText) Then
InStrReverse = 0
Exit Function
End If
Dim i As Long
For i = Len(sText) To 1 Step -1
If Mid(sText, i, Len(search)) = search Then
InStrReverse = i : Exit Function
End If
Next i
End FunctionKuvaus ja käyttö selviää katsomalla tämän
Halutessaan testiprojektin voi imaista täältä
HV (hyvää vappua jo etukäteen)