vibreoffice/src/vibreoffice.vbs

1038 lines
29 KiB
Plaintext
Raw Normal View History

' vibreoffice - Vi Mode for LibreOffice/OpenOffice
'
' The MIT License (MIT)
'
' Copyright (c) 2014 Sean Yeh
'
' Permission is hereby granted, free of charge, to any person obtaining a copy
' of this software and associated documentation files (the "Software"), to deal
' in the Software without restriction, including without limitation the rights
' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
' copies of the Software, and to permit persons to whom the Software is
' furnished to do so, subject to the following conditions:
'
' The above copyright notice and this permission notice shall be included in
' all copies or substantial portions of the Software.
'
' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
' THE SOFTWARE.
2014-12-16 13:34:16 -06:00
Option Explicit
' --------
' Globals
' --------
global VIBREOFFICE_STARTED as boolean ' Defaults to False
global VIBREOFFICE_ENABLED as boolean ' Defaults to False
global oXKeyHandler as object
' Global State
global MODE as string
global VIEW_CURSOR as object
global MULTIPLIER as integer
' -----------
' Singletons
' -----------
Function getCursor
getCursor = VIEW_CURSOR
2014-12-16 13:34:16 -06:00
End Function
Function getTextCursor
dim oTextCursor
2015-07-24 08:37:44 -05:00
On Error Goto ErrorHandler
2014-12-16 13:34:16 -06:00
oTextCursor = getCursor().getText.createTextCursorByRange(getCursor())
getTextCursor = oTextCursor
2015-07-24 08:37:44 -05:00
Exit Function
ErrorHandler:
' Text Cursor does not work in some instances, such as in Annotations
getTextCursor = Nothing
2014-12-16 13:34:16 -06:00
End Function
' -----------------
' Helper Functions
' -----------------
Sub restoreStatus 'restore original statusbar
dim oLayout
oLayout = thisComponent.getCurrentController.getFrame.LayoutManager
oLayout.destroyElement("private:resource/statusbar/statusbar")
oLayout.createElement("private:resource/statusbar/statusbar")
2014-12-16 13:34:16 -06:00
End Sub
Sub setRawStatus(rawText)
thisComponent.Currentcontroller.StatusIndicator.Start(rawText, 0)
2014-12-16 13:34:16 -06:00
End Sub
Sub setStatus(statusText)
2014-12-18 15:10:00 -06:00
setRawStatus(MODE & " | " & statusText & " | special: " & getSpecial() & " | " & "modifier: " & getMovementModifier())
2014-12-16 13:34:16 -06:00
End Sub
Sub setMode(modeName)
MODE = modeName
setRawStatus(modeName)
2014-12-16 13:34:16 -06:00
End Sub
Function gotoMode(sMode)
Select Case sMode
Case "NORMAL":
setMode("NORMAL")
2014-12-18 15:10:00 -06:00
setMovementModifier("")
2014-12-16 13:34:16 -06:00
Case "INSERT":
setMode("INSERT")
Case "VISUAL":
setMode("VISUAL")
dim oTextCursor
oTextCursor = getTextCursor()
' Deselect TextCursor
oTextCursor.gotoRange(oTextCursor.getStart(), False)
' Show TextCursor selection
thisComponent.getCurrentController.Select(oTextCursor)
2014-12-16 13:34:16 -06:00
End Select
End Function
Sub cursorReset(oTextCursor)
oTextCursor.gotoRange(oTextCursor.getStart(), False)
oTextCursor.goRight(1, False)
oTextCursor.goLeft(1, True)
thisComponent.getCurrentController.Select(oTextCursor)
End Sub
2014-12-18 15:10:00 -06:00
Function samePos(oPos1, oPos2)
samePos = oPos1.X() = oPos2.X() And oPos1.Y() = oPos2.Y()
End Function
2014-12-18 15:10:00 -06:00
2014-12-20 17:11:53 -06:00
Function genString(sChar, iLen)
dim sResult, i
sResult = ""
For i = 1 To iLen
sResult = sResult & sChar
Next i
genString = sResult
End Function
' Yanks selection to system clipboard.
' If bDelete is true, will delete selection.
Sub yankSelection(bDelete)
dim dispatcher As Object
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
dispatcher.executeDispatch(ThisComponent.CurrentController.Frame, ".uno:Copy", "", 0, Array())
If bDelete Then
getTextCursor().setString("")
End If
End Sub
2015-07-22 15:01:24 -05:00
Sub pasteSelection()
dim oTextCursor, dispatcher As Object
' Deselect if in NORMAL mode to avoid overwriting the character underneath
' the cursor
If MODE = "NORMAL" Then
oTextCursor = getTextCursor()
oTextCursor.gotoRange(oTextCursor.getStart(), False)
thisComponent.getCurrentController.Select(oTextCursor)
End If
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
dispatcher.executeDispatch(ThisComponent.CurrentController.Frame, ".uno:Paste", "", 0, Array())
End Sub
' -----------------------------------
' Special Mode (for chained commands)
' -----------------------------------
global SPECIAL_MODE As string
global SPECIAL_COUNT As integer
Sub setSpecial(specialName)
SPECIAL_MODE = specialName
If specialName = "" Then
SPECIAL_COUNT = 0
Else
SPECIAL_COUNT = 2
End If
End Sub
Function getSpecial()
getSpecial = SPECIAL_MODE
End Function
Sub delaySpecialReset()
SPECIAL_COUNT = SPECIAL_COUNT + 1
End Sub
Sub resetSpecial(Optional bForce)
If IsMissing(bForce) Then bForce = False
SPECIAL_COUNT = SPECIAL_COUNT - 1
If SPECIAL_COUNT <= 0 Or bForce Then
setSpecial("")
End If
End Sub
2014-12-16 13:34:16 -06:00
2014-12-18 15:10:00 -06:00
' -----------------
' Movement Modifier
' -----------------
'f,i,a
global MOVEMENT_MODIFIER As string
Sub setMovementModifier(modifierName)
MOVEMENT_MODIFIER = modifierName
End Sub
Function getMovementModifier()
getMovementModifier = MOVEMENT_MODIFIER
End Function
2014-12-16 13:34:16 -06:00
' --------------------
' Multiplier functions
' --------------------
Sub _setMultiplier(n as integer)
MULTIPLIER = n
2014-12-16 13:34:16 -06:00
End Sub
Sub resetMultiplier()
_setMultiplier(0)
2014-12-16 13:34:16 -06:00
End Sub
Sub addToMultiplier(n as integer)
dim sMultiplierStr as String
2014-12-16 13:34:16 -06:00
dim iMultiplierInt as integer
' Max multiplier: 10000 (stop accepting additions after 1000)
If MULTIPLIER <= 1000 then
sMultiplierStr = CStr(MULTIPLIER) & CStr(n)
_setMultiplier(CInt(sMultiplierStr))
End If
End Sub
' Should only be used if you need the raw value
Function getRawMultiplier()
getRawMultiplier = MULTIPLIER
End Function
' Same as getRawMultiplier, but defaults to 1 if it is unset (0)
Function getMultiplier()
If MULTIPLIER = 0 Then
getMultiplier = 1
Else
getMultiplier = MULTIPLIER
End If
2014-12-16 13:34:16 -06:00
End Function
' -------------
' Key Handling
' -------------
Sub sStartXKeyHandler
sStopXKeyHandler()
2014-12-16 13:34:16 -06:00
oXKeyHandler = CreateUnoListener("KeyHandler_", "com.sun.star.awt.XKeyHandler")
thisComponent.CurrentController.AddKeyHandler(oXKeyHandler)
2014-12-16 13:34:16 -06:00
End Sub
Sub sStopXKeyHandler
thisComponent.CurrentController.removeKeyHandler(oXKeyHandler)
2014-12-16 13:34:16 -06:00
End Sub
Sub XKeyHandler_Disposing(oEvent)
End Sub
' --------------------
' Main Key Processing
' --------------------
function KeyHandler_KeyPressed(oEvent) as boolean
2015-07-24 08:37:44 -05:00
dim oTextCursor
2014-12-16 13:34:16 -06:00
' Exit if plugin is not enabled
If IsMissing(VIBREOFFICE_ENABLED) Or Not VIBREOFFICE_ENABLED Then
KeyHandler_KeyPressed = False
Exit Function
End If
2015-07-24 08:37:44 -05:00
' Exit if TextCursor does not work (as in Annotations)
oTextCursor = getTextCursor()
If oTextCursor Is Nothing Then
KeyHandler_KeyPressed = False
Exit Function
End If
2015-07-30 17:24:17 -05:00
dim bConsumeInput, bIsMultiplier, bIsModified, bIsControl, bIsSpecial
bConsumeInput = True ' Block all inputs by default
bIsMultiplier = False ' reset multiplier by default
2014-12-16 13:34:16 -06:00
bIsModified = oEvent.Modifiers > 1 ' If Ctrl or Alt is held down. (Shift=1)
2015-07-30 17:24:17 -05:00
bIsControl = (oEvent.Modifiers = 2) or (oEvent.Modifiers = 8)
bIsSpecial = getSpecial() <> ""
2014-12-16 13:34:16 -06:00
2014-12-16 13:34:16 -06:00
' --------------------------
' Process global shortcuts, exit if matched (like ESC)
If ProcessGlobalKey(oEvent) Then
' Pass
2014-12-16 13:34:16 -06:00
' If INSERT mode, allow all inputs
ElseIf MODE = "INSERT" Then
bConsumeInput = False
2014-12-16 13:34:16 -06:00
' If Change Mode
2014-12-18 15:10:00 -06:00
' ElseIf MODE = "NORMAL" And Not bIsSpecial And getMovementModifier() = "" And ProcessModeKey(oEvent) Then
ElseIf ProcessModeKey(oEvent) Then
2014-12-16 13:34:16 -06:00
' Pass
2014-12-20 17:11:53 -06:00
' Replace Key
ElseIf getSpecial() = "r" And Not bIsModified Then
dim iLen
iLen = Len(getCursor().getString())
getCursor().setString(genString(oEvent.KeyChar, iLen))
2014-12-17 14:50:51 -06:00
' Multiplier Key
2014-12-16 13:34:16 -06:00
ElseIf ProcessNumberKey(oEvent) Then
bIsMultiplier = True
delaySpecialReset()
2014-12-16 13:34:16 -06:00
' Normal Key
2014-12-20 09:27:11 -06:00
ElseIf ProcessNormalKey(oEvent.KeyChar, oEvent.Modifiers) Then
' Pass
' If is modified but doesn't match a normal command, allow input
2015-07-30 17:24:17 -05:00
' (Useful for built-in shortcuts like Ctrl+a, Ctrl+s, Ctrl+w)
ElseIf bIsModified Then
2015-07-30 17:24:17 -05:00
' Ctrl+a (select all) sets mode to VISUAL
If bIsControl And oEvent.KeyChar = "a" Then
gotoMode("VISUAL")
End If
2014-12-16 13:34:16 -06:00
bConsumeInput = False
2014-12-18 15:10:00 -06:00
' Movement modifier here?
ElseIf ProcessMovementModifierKey(oEvent.KeyChar) Then
2014-12-19 01:52:23 -06:00
delaySpecialReset()
' If standard movement key (in VISUAL mode) like arrow keys, home, end
ElseIf MODE = "VISUAL" And ProcessStandardMovementKey(oEvent) Then
' Pass
' If bIsSpecial but nothing matched, return to normal mode
ElseIf bIsSpecial Then
gotoMode("NORMAL")
' Allow non-letter keys if unmatched
ElseIf asc(oEvent.KeyChar) = 0 Then
bConsumeInput = False
2014-12-16 13:34:16 -06:00
End If
' --------------------------
' Reset Special
resetSpecial()
2014-12-16 13:34:16 -06:00
' Reset multiplier if last input was not number and not in special mode
2014-12-18 15:10:00 -06:00
If not bIsMultiplier and getSpecial() = "" and getMovementModifier() = "" Then
resetMultiplier()
End If
setStatus(getMultiplier())
2014-12-16 13:34:16 -06:00
KeyHandler_KeyPressed = bConsumeInput
End Function
Function KeyHandler_KeyReleased(oEvent) As boolean
dim oTextCursor
2014-12-16 13:34:16 -06:00
' Show terminal-like cursor
oTextCursor = getTextCursor()
2015-07-24 08:37:44 -05:00
If oTextCursor Is Nothing Then
' Do nothing
ElseIf oEvent.Modifiers = 2 Or oEvent.Modifiers = 8 And oEvent.KeyChar = "c" Then
2014-12-18 17:05:51 -06:00
' Allow Ctrl+c for Copy, so don't change cursor
' Pass
ElseIf MODE = "NORMAL" Then
cursorReset(oTextCursor)
2014-12-16 13:34:16 -06:00
ElseIf MODE = "INSERT" Then
oTextCursor.gotoRange(oTextCursor.getStart(), False)
thisComponent.getCurrentController.Select(oTextCursor)
2014-12-16 13:34:16 -06:00
End If
KeyHandler_KeyReleased = (MODE = "NORMAL") 'cancel KeyReleased
End Function
' ----------------
' Processing Keys
' ----------------
Function ProcessGlobalKey(oEvent)
2015-02-27 08:20:27 -06:00
dim bMatched, bIsControl
bMatched = True
2015-02-27 08:20:27 -06:00
bIsControl = (oEvent.Modifiers = 2) or (oEvent.Modifiers = 8)
' PRESSED ESCAPE (or ctrl+[)
if oEvent.KeyCode = 1281 Or (oEvent.KeyCode = 1315 And bIsControl) Then
' Move cursor back if was in INSERT (but stay on same line)
If MODE <> "NORMAL" And Not getCursor().isAtStartOfLine() Then
getCursor().goLeft(1, False)
End If
resetSpecial(True)
gotoMode("NORMAL")
Else
bMatched = False
End If
ProcessGlobalKey = bMatched
2014-12-16 13:34:16 -06:00
End Function
Function ProcessStandardMovementKey(oEvent)
dim c, bMatched
c = oEvent.KeyCode
bMatched = True
If MODE <> "VISUAL" Then
bMatched = False
'Pass
ElseIf c = 1024 Then
ProcessMovementKey("j", True)
ElseIf c = 1025 Then
ProcessMovementKey("k", True)
ElseIf c = 1026 Then
ProcessMovementKey("h", True)
ElseIf c = 1027 Then
ProcessMovementKey("l", True)
ElseIf c = 1028 Then
ProcessMovementKey("^", True)
ElseIf c = 1029 Then
ProcessMovementKey("$", True)
Else
bMatched = False
End If
ProcessStandardMovementKey = bMatched
End Function
2014-12-16 13:34:16 -06:00
Function ProcessNumberKey(oEvent)
dim c
c = CStr(oEvent.KeyChar)
If c >= "0" and c <= "9" Then
addToMultiplier(CInt(c))
ProcessNumberKey = True
Else
ProcessNumberKey = False
End If
2014-12-16 13:34:16 -06:00
End Function
Function ProcessModeKey(oEvent)
2014-12-18 17:05:51 -06:00
dim bIsModified
bIsModified = oEvent.Modifiers > 1 ' If Ctrl or Alt is held down. (Shift=1)
2014-12-18 15:10:00 -06:00
' Don't change modes in these circumstances
2014-12-18 17:05:51 -06:00
If MODE <> "NORMAL" Or bIsModified Or getSpecial <> "" Or getMovementModifier() <> "" Then
2014-12-18 15:10:00 -06:00
ProcessModeKey = False
Exit Function
End If
' Mode matching
dim bMatched
bMatched = True
Select Case oEvent.KeyChar
2014-12-16 13:34:16 -06:00
' Insert modes
2014-12-20 21:03:38 -06:00
Case "i", "a", "I", "A", "o", "O":
2014-12-16 13:34:16 -06:00
If oEvent.KeyChar = "a" Then getCursor().goRight(1, False)
If oEvent.KeyChar = "I" Then ProcessMovementKey("^")
If oEvent.KeyChar = "A" Then ProcessMovementKey("$")
2014-12-20 21:03:38 -06:00
If oEvent.KeyChar = "o" Then
ProcessMovementKey("$")
ProcessMovementKey("l")
getCursor().setString(chr(13))
If Not getCursor().isAtStartOfLine() Then
getCursor().setString(chr(13) & chr(13))
ProcessMovementKey("l")
End If
End If
If oEvent.KeyChar = "O" Then
ProcessMovementKey("^")
getCursor().setString(chr(13))
If Not getCursor().isAtStartOfLine() Then
ProcessMovementKey("h")
getCursor().setString(chr(13))
ProcessMovementKey("l")
End If
End If
gotoMode("INSERT")
2014-12-16 13:34:16 -06:00
Case "v":
gotoMode("VISUAL")
Case Else:
bMatched = False
End Select
ProcessModeKey = bMatched
2014-12-16 13:34:16 -06:00
End Function
2014-12-20 09:27:11 -06:00
Function ProcessNormalKey(keyChar, modifiers)
2015-05-25 23:22:38 -05:00
dim i, bMatched, bIsVisual, iIterations, bIsControl
bIsControl = (modifiers = 2) or (modifiers = 8)
2014-12-16 13:34:16 -06:00
bIsVisual = (MODE = "VISUAL") ' is this hardcoding bad? what about visual block?
' ----------------------
' 1. Check Movement Key
' ----------------------
iIterations = getMultiplier()
bMatched = False
For i = 1 To iIterations
dim bMatchedMovement
2014-12-16 13:34:16 -06:00
2014-12-17 14:50:51 -06:00
' Movement Key
2014-12-20 09:27:11 -06:00
bMatchedMovement = ProcessMovementKey(KeyChar, bIsVisual, modifiers)
bMatched = bMatched or bMatchedMovement
2014-12-18 15:10:00 -06:00
' If Special: d/c + movement
2015-07-30 17:04:06 -05:00
If bMatched And (getSpecial() = "d" Or getSpecial() = "c" Or getSpecial() = "y") Then
yankSelection((getSpecial() <> "y"))
End If
Next i
' Reset Movement Modifier
setMovementModifier("")
' Exit already if movement key was matched
If bMatched Then
' If Special: d/c : change mode
2015-07-30 17:04:06 -05:00
If getSpecial() = "d" Or getSpecial() = "y" Then gotoMode("NORMAL")
If getSpecial() = "c" Then gotoMode("INSERT")
ProcessNormalKey = True
Exit Function
End If
2014-12-18 17:05:51 -06:00
' --------------------
2015-05-25 23:22:38 -05:00
' 2. Undo/Redo
' --------------------
If keyChar = "u" Or (bIsControl And keyChar = "r") Then
For i = 1 To iIterations
Undo(keyChar = "u")
Next i
ProcessNormalKey = True
Exit Function
End If
' --------------------
2015-07-22 15:01:24 -05:00
' 3. Paste
' Note: in vim, paste will result in cursor being over the last character
' of the pasted content. Here, the cursor will be the next character
' after that. Fix?
' --------------------
If keyChar = "p" or keyChar = "P" Then
' Move cursor right if "p" to paste after cursor
If keyChar = "p" Then
ProcessMovementKey("l", False)
End If
For i = 1 To iIterations
pasteSelection()
Next i
ProcessNormalKey = True
Exit Function
End If
' --------------------
' 4. Check Special/Delete Key
' --------------------
2014-12-20 17:11:53 -06:00
' There are no special/delete keys with modifier keys, so exit early
2014-12-20 09:27:11 -06:00
If modifiers > 1 Then
2014-12-18 17:05:51 -06:00
ProcessNormalKey = False
Exit Function
End If
2014-12-17 21:27:38 -06:00
' Only 'x' or Special (dd, cc) can be done more than once
2014-12-20 09:27:11 -06:00
If keyChar <> "x" and getSpecial() = "" Then
2014-12-17 21:27:38 -06:00
iIterations = 1
End If
For i = 1 To iIterations
2014-12-20 17:11:53 -06:00
dim bMatchedSpecial
2014-12-17 14:50:51 -06:00
2014-12-20 17:11:53 -06:00
' Special/Delete Key
bMatchedSpecial = ProcessSpecialKey(keyChar)
2014-12-16 13:34:16 -06:00
2014-12-20 17:11:53 -06:00
bMatched = bMatched or bMatchedSpecial
2014-12-16 13:34:16 -06:00
Next i
2015-05-25 23:22:38 -05:00
2014-12-16 13:34:16 -06:00
ProcessNormalKey = bMatched
End Function
2015-05-25 23:22:38 -05:00
' Function for both undo and redo
Sub Undo(bUndo)
On Error Goto ErrorHandler
If bUndo Then
thisComponent.getUndoManager().undo()
Else
thisComponent.getUndoManager().redo()
End If
Exit Sub
' Ignore errors from no more undos/redos in stack
ErrorHandler:
Resume Next
End Sub
2014-12-20 17:11:53 -06:00
Function ProcessSpecialKey(keyChar)
2015-07-30 17:04:06 -05:00
dim oTextCursor, bMatched, bIsSpecial, bIsDelete
2014-12-16 13:34:16 -06:00
bMatched = True
2014-12-17 21:27:38 -06:00
bIsSpecial = getSpecial() <> ""
2014-12-16 13:34:16 -06:00
2015-07-30 17:04:06 -05:00
If keyChar = "d" Or keyChar = "c" Or keyChar = "s" Or keyChar = "y" Then
bIsDelete = (keyChar <> "y")
2014-12-17 21:27:38 -06:00
' Special Cases: 'dd' and 'cc'
If bIsSpecial Then
dim bIsSpecialCase
bIsSpecialCase = (keyChar = "d" And getSpecial() = "d") Or (keyChar = "c" And getSpecial() = "c")
2014-12-17 21:27:38 -06:00
If bIsSpecialCase Then
2014-12-17 14:50:51 -06:00
ProcessMovementKey("^", False)
2014-12-17 21:27:38 -06:00
ProcessMovementKey("j", True)
2014-12-17 14:50:51 -06:00
oTextCursor = getTextCursor()
thisComponent.getCurrentController.Select(oTextCursor)
2015-07-30 17:04:06 -05:00
yankSelection(bIsDelete)
2014-12-17 21:27:38 -06:00
Else
bMatched = False
2014-12-17 14:50:51 -06:00
End If
2014-12-17 21:27:38 -06:00
' Go to INSERT mode after 'cc', otherwise NORMAL
If bIsSpecialCase And keyChar = "c" Then
2014-12-17 14:50:51 -06:00
gotoMode("INSERT")
2014-12-17 21:27:38 -06:00
Else
gotoMode("NORMAL")
End If
2014-12-17 14:50:51 -06:00
2014-12-20 09:27:11 -06:00
' visual mode: delete selection
2014-12-17 21:27:38 -06:00
ElseIf MODE = "VISUAL" Then
oTextCursor = getTextCursor()
thisComponent.getCurrentController.Select(oTextCursor)
2015-07-30 17:04:06 -05:00
yankSelection(bIsDelete)
2014-12-17 21:27:38 -06:00
2014-12-20 09:27:11 -06:00
If keyChar = "c" Or keyChar = "s" Then gotoMode("INSERT")
2015-07-30 17:04:06 -05:00
If keyChar = "d" Or keyChar = "y" Then gotoMode("NORMAL")
2014-12-17 14:50:51 -06:00
2014-12-20 09:27:11 -06:00
2015-07-30 17:04:06 -05:00
' Enter Special mode: 'd', 'c', or 'y' ('s' => 'cl')
2014-12-17 21:27:38 -06:00
ElseIf MODE = "NORMAL" Then
2014-12-20 09:27:11 -06:00
' 's' => 'cl'
If keyChar = "s" Then
setSpecial("c")
gotoMode("VISUAL")
ProcessNormalKey("l", 0)
Else
setSpecial(keyChar)
gotoMode("VISUAL")
End If
2014-12-17 21:27:38 -06:00
End If
2014-12-17 14:50:51 -06:00
2014-12-20 17:11:53 -06:00
' If is 'r' for replace
ElseIf keyChar = "r" Then
setSpecial("r")
2014-12-17 14:50:51 -06:00
2014-12-17 21:27:38 -06:00
' Otherwise, ignore if bIsSpecial
ElseIf bIsSpecial Then
bMatched = False
ElseIf keyChar = "x" Then
oTextCursor = getTextCursor()
thisComponent.getCurrentController.Select(oTextCursor)
yankSelection(True)
2014-12-17 21:27:38 -06:00
' Reset Cursor
cursorReset(oTextCursor)
' Goto NORMAL mode (in the case of VISUAL mode)
gotoMode("NORMAL")
ElseIf keyChar = "D" Or keyChar = "C" Then
If MODE = "VISUAL" Then
ProcessMovementKey("^", False)
ProcessMovementKey("$", True)
ProcessMovementKey("l", True)
Else
' Deselect
oTextCursor = getTextCursor()
oTextCursor.gotoRange(oTextCursor.getStart(), False)
thisComponent.getCurrentController.Select(oTextCursor)
ProcessMovementKey("$", True)
End If
yankSelection(True)
2014-12-17 21:27:38 -06:00
If keyChar = "D" Then
gotoMode("NORMAL")
ElseIf keyChar = "C" Then
gotoMode("INSERT")
End IF
2014-12-20 09:27:11 -06:00
' S only valid in NORMAL mode
ElseIf keyChar = "S" And MODE = "NORMAL" Then
ProcessMovementKey("^", False)
ProcessMovementKey("$", True)
yankSelection(True)
2014-12-20 09:27:11 -06:00
gotoMode("INSERT")
2014-12-17 21:27:38 -06:00
Else
bMatched = False
End If
2014-12-16 13:34:16 -06:00
2014-12-20 17:11:53 -06:00
ProcessSpecialKey = bMatched
2014-12-16 13:34:16 -06:00
End Function
2014-12-18 15:10:00 -06:00
Function ProcessMovementModifierKey(keyChar)
dim bMatched
bMatched = True
Select Case keyChar
2014-12-19 22:22:42 -06:00
Case "f", "t", "F", "T", "i", "a":
2014-12-18 15:10:00 -06:00
setMovementModifier(keyChar)
Case Else:
bMatched = False
End Select
ProcessMovementModifierKey = bMatched
End Function
Function ProcessSearchKey(oTextCursor, searchType, keyChar, bExpand)
'-----------
' Searching
'-----------
2014-12-19 22:22:42 -06:00
dim bMatched, oSearchDesc, oFoundRange, bIsBackwards, oStartRange
bMatched = True
2014-12-18 15:10:00 -06:00
bIsBackwards = (searchType = "F" Or searchType = "T")
If Not bIsBackwards Then
2014-12-19 01:52:23 -06:00
' VISUAL mode will goRight AFTER the selection
If MODE <> "VISUAL" Then
' Start searching from next character
oTextCursor.goRight(1, bExpand)
End If
2014-12-18 15:10:00 -06:00
oStartRange = oTextCursor.getEnd()
2014-12-19 01:52:23 -06:00
' Go back one
oTextCursor.goLeft(1, bExpand)
2014-12-18 15:10:00 -06:00
Else
oStartRange = oTextCursor.getStart()
End If
oSearchDesc = thisComponent.createSearchDescriptor()
oSearchDesc.setSearchString(keyChar)
oSearchDesc.SearchCaseSensitive = True
oSearchDesc.SearchBackwards = bIsBackwards
oFoundRange = thisComponent.findNext( oStartRange, oSearchDesc )
If not IsNull(oFoundRange) Then
2014-12-19 01:52:23 -06:00
dim oText, foundPos, curPos, bSearching
oText = oTextCursor.getText()
foundPos = oFoundRange.getStart()
' Unfortunately, we must go go to this "found" position one character at
' a time because I have yet to find a way to consistently move the
' Start range of the text cursor and leave the End range intact.
If bIsBackwards Then
curPos = oTextCursor.getEnd()
Else
curPos = oTextCursor.getStart()
End If
do until oText.compareRegionStarts(foundPos, curPos) = 0
If bIsBackwards Then
bSearching = oTextCursor.goLeft(1, bExpand)
curPos = oTextCursor.getStart()
Else
bSearching = oTextCursor.goRight(1, bExpand)
curPos = oTextCursor.getEnd()
End If
' Prevent infinite if unable to find, but shouldn't ever happen (?)
2014-12-19 22:22:42 -06:00
If Not bSearching Then
bMatched = False
Exit Do
End If
2014-12-19 01:52:23 -06:00
Loop
2014-12-18 15:10:00 -06:00
2014-12-19 22:22:42 -06:00
If searchType = "t" Then
2014-12-18 15:10:00 -06:00
oTextCursor.goLeft(1, bExpand)
2014-12-19 22:22:42 -06:00
ElseIf searchType = "T" Then
2014-12-18 15:10:00 -06:00
oTextCursor.goRight(1, bExpand)
End If
Else
2014-12-19 22:22:42 -06:00
bMatched = False
End If
' If matched, then we want to select PAST the character
' Else, this will counteract some weirdness. hack either way
If Not bIsBackwards And MODE = "VISUAL" Then
oTextCursor.goRight(1, bExpand)
2014-12-18 15:10:00 -06:00
End If
2014-12-19 22:22:42 -06:00
ProcessSearchKey = bMatched
2014-12-18 15:10:00 -06:00
End Function
2014-12-19 22:22:42 -06:00
Function ProcessInnerKey(oTextCursor, movementModifier, keyChar, bExpand)
dim bMatched, searchType1, searchType2, search1, search2
' Setting searchType
If movementModifier = "i" Then
searchType1 = "T" : searchType2 = "t"
ElseIf movementModifier = "a" Then
searchType1 = "F" : searchType2 = "f"
Else ' Shouldn't happen
ProcessInnerKey = False
Exit Function
End If
Select Case keyChar
2014-12-22 16:26:16 -06:00
Case "(", ")", "{", "}", "[", "]", "<", ">", "t", "'", """":
2014-12-19 22:22:42 -06:00
Select Case keyChar
Case "(", ")":
search1 = "(" : search2 = ")"
Case "{", "}":
search1 = "{" : search2 = "}"
Case "[", "]":
search1 = "[" : search2 = "}"
2014-12-22 16:26:16 -06:00
Case "<", ">":
search1 = "<" : search2 = ">"
2014-12-19 22:22:42 -06:00
Case "t":
search1 = ">" : search2 = "<"
Case "'":
search1 = "'" : search2 = "'"
Case """":
' Matches "smart" quotes, which is default in libreoffice
search1 = "ā€œ" : search2 = "ā€"
End Select
dim bMatched1, bMatched2
bMatched1 = ProcessSearchKey(oTextCursor, searchType1, search1, False)
bMatched2 = ProcessSearchKey(oTextCursor, searchType2, search2, True)
bMatched = (bMatched1 And bMatched2)
Case Else:
bMatched = False
End Select
ProcessInnerKey = bMatched
End Function
2014-12-16 13:34:16 -06:00
' -----------------------
' Main Movement Function
' -----------------------
' Default: bExpand = False, keyModifiers = 0
Function ProcessMovementKey(keyChar, Optional bExpand, Optional keyModifiers)
dim oTextCursor, bSetCursor, bMatched
2014-12-16 13:34:16 -06:00
oTextCursor = getTextCursor()
bMatched = True
If IsMissing(bExpand) Then bExpand = False
If IsMissing(keyModifiers) Then keyModifiers = 0
' Check for modified keys (Ctrl, Alt, not Shift)
If keyModifiers > 1 Then
2014-12-17 14:50:51 -06:00
dim bIsControl
bIsControl = (keyModifiers = 2) or (keyModifiers = 8)
2014-12-16 13:34:16 -06:00
' Ctrl+d and Ctrl+u
2014-12-17 14:50:51 -06:00
If bIsControl and keyChar = "d" Then
2014-12-16 13:34:16 -06:00
getCursor().ScreenDown(bExpand)
2014-12-17 14:50:51 -06:00
ElseIf bIsControl and keyChar = "u" Then
2014-12-16 13:34:16 -06:00
getCursor().ScreenUp(bExpand)
Else
bMatched = False
End If
ProcessMovementKey = bMatched
Exit Function
End If
' Set global cursor to oTextCursor's new position if moved
bSetCursor = True
2014-12-18 15:10:00 -06:00
' ------------------
' Movement matching
' ------------------
2014-12-16 13:34:16 -06:00
2014-12-19 22:22:42 -06:00
' ---------------------------------
2014-12-18 15:10:00 -06:00
' Special Case: Modified movements
If getMovementModifier() <> "" Then
Select Case getMovementModifier()
' f,F,t,T searching
Case "f", "t", "F", "T":
2014-12-19 22:22:42 -06:00
bMatched = ProcessSearchKey(oTextCursor, getMovementModifier(), keyChar, bExpand)
Case "i", "a":
bMatched = ProcessInnerKey(oTextCursor, getMovementModifier(), keyChar, bExpand)
2014-12-17 21:27:38 -06:00
2014-12-18 15:10:00 -06:00
Case Else:
bSetCursor = False
bMatched = False
End Select
2014-12-19 22:22:42 -06:00
If Not bMatched Then
bSetCursor = False
End If
' ---------------------------------
2014-12-18 15:10:00 -06:00
ElseIf keyChar = "l" Then
oTextCursor.goRight(1, bExpand)
ElseIf keyChar = "h" Then
oTextCursor.goLeft(1, bExpand)
' oTextCursor.goUp and oTextCursor.goDown SHOULD work, but doesn't (I dont know why).
' So this is a weird hack
ElseIf keyChar = "k" Then
'oTextCursor.goUp(1, False)
getCursor().goUp(1, bExpand)
bSetCursor = False
ElseIf keyChar = "j" Then
'oTextCursor.goDown(1, False)
getCursor().goDown(1, bExpand)
bSetCursor = False
' ----------
ElseIf keyChar = "^" Then
getCursor().gotoStartOfLine(bExpand)
bSetCursor = False
ElseIf keyChar = "$" Then
dim oldPos, newPos
oldPos = getCursor().getPosition()
getCursor().gotoEndOfLine(bExpand)
newPos = getCursor().getPosition()
' If the result is at the start of the line, then it must have
' jumped down a line; goLeft to return to the previous line.
' Except for: Empty lines (check for oldPos = newPos)
If getCursor().isAtStartOfLine() And oldPos.Y() <> newPos.Y() Then
getCursor().goLeft(1, bExpand)
End If
' maybe eventually cursorGoto... should return True/False for bsetCursor
bSetCursor = False
ElseIf keyChar = "w" or keyChar = "W" Then
oTextCursor.gotoNextWord(bExpand)
ElseIf keyChar = "b" or keyChar = "B" Then
oTextCursor.gotoPreviousWord(bExpand)
ElseIf keyChar = "e" Then
oTextCursor.gotoEndOfWord(bExpand)
ElseIf keyChar = ")" Then
oTextCursor.gotoNextSentence(bExpand)
ElseIf keyChar = "(" Then
oTextCursor.gotoPreviousSentence(bExpand)
ElseIf keyChar = "}" Then
oTextCursor.gotoNextParagraph(bExpand)
ElseIf keyChar = "{" Then
oTextCursor.gotoPreviousParagraph(bExpand)
Else
bSetCursor = False
bMatched = False
End If
2014-12-16 13:34:16 -06:00
' If oTextCursor was moved, set global cursor to its position
If bSetCursor Then
getCursor().gotoRange(oTextCursor.getStart(), False)
' ---- REALLY BAD HACK
' I can't seem to get the View Cursor (getCursor()) to update its
' position without calling its own movement functions.
' Theoretically, the above call to gotoRange should work, but I don't
' know why it doesn't. Visually it works, but its X position is reset
' when you move lines. Bug??
dim oTempPos
oTempPos = getCursor().getPosition()
' Move left 1 and then right 1 to stay in same position
getCursor().goLeft(1, False)
If Not samePos(oTempPos, getCursor().getPosition()) Then
getCursor().goRight(1, False)
End If
2014-12-16 13:34:16 -06:00
End If
' If oTextCursor was moved and is in VISUAL mode, update selection
if bSetCursor and bExpand then
thisComponent.getCurrentController.Select(oTextCursor)
end if
ProcessMovementKey = bMatched
End Function
Sub initVibreoffice
dim oTextCursor
' Initializing
VIBREOFFICE_STARTED = True
VIEW_CURSOR = thisComponent.getCurrentController.getViewCursor
2014-12-16 13:34:16 -06:00
resetMultiplier()
gotoMode("NORMAL")
2014-12-16 13:34:16 -06:00
' Show terminal cursor
oTextCursor = getTextCursor()
2015-07-24 08:37:44 -05:00
If oTextCursor Is Nothing Then
' Do nothing
Else
cursorReset(oTextCursor)
End If
2014-12-16 13:34:16 -06:00
sStartXKeyHandler()
2014-12-16 13:34:16 -06:00
End Sub
Sub Main
If Not VIBREOFFICE_STARTED Then
initVibreoffice()
End If
' Toggle enable/disable
VIBREOFFICE_ENABLED = Not VIBREOFFICE_ENABLED
' Restore statusbar
If Not VIBREOFFICE_ENABLED Then restoreStatus()
End Sub