commit 9c7b65e6b12e03b54cb7dee71418b60387078e59 Author: Sean Yeh Date: Tue Dec 16 13:34:16 2014 -0600 Initial commit diff --git a/src/vibreoffice.vbs b/src/vibreoffice.vbs new file mode 100644 index 0000000..819886f --- /dev/null +++ b/src/vibreoffice.vbs @@ -0,0 +1,421 @@ +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 +End Function + +Function getTextCursor + dim oTextCursor + oTextCursor = getCursor().getText.createTextCursorByRange(getCursor()) + ' oTextCursor.gotoRange(oTextCursor.getStart(), False) + + getTextCursor = oTextCursor +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") +End Sub + +Sub setRawStatus(rawText) + thisComponent.Currentcontroller.StatusIndicator.Start(rawText, 0) +End Sub + +Sub setStatus(statusText) + setRawStatus(MODE & " | " & statusText) +End Sub + +Sub setMode(modeName) + MODE = modeName + setRawStatus(modeName) +End Sub + +Function gotoMode(sMode) + Select Case sMode + Case "NORMAL": + setMode("NORMAL") + 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) + End Select +End Function + + +' -------------------- +' Multiplier functions +' -------------------- +Sub _setMultiplier(n as integer) + MULTIPLIER = n +End Sub + +Sub resetMultiplier() + _setMultiplier(0) +End Sub + +Sub addToMultiplier(n as integer) + dim sMultiplierStr as String + 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 +End Function + + +' ------------- +' Key Handling +' ------------- +Sub sStartXKeyHandler + sStopXKeyHandler() + + oXKeyHandler = CreateUnoListener("KeyHandler_", "com.sun.star.awt.XKeyHandler") + thisComponent.CurrentController.AddKeyHandler(oXKeyHandler) +End Sub + +Sub sStopXKeyHandler + thisComponent.CurrentController.removeKeyHandler(oXKeyHandler) +End Sub + +Sub XKeyHandler_Disposing(oEvent) +End Sub + + +' -------------------- +' Main Key Processing +' -------------------- +function KeyHandler_KeyPressed(oEvent) as boolean + ' Exit if plugin is not enabled + If IsMissing(VIBREOFFICE_ENABLED) Or Not VIBREOFFICE_ENABLED Then + KeyHandler_KeyPressed = False + Exit Function + End If + + dim bConsumeInput, bIsMultiplier, bIsModified, oTextCursor + bConsumeInput = True ' Block all inputs by default + bIsMultiplier = False ' reset multiplier by default + bIsModified = oEvent.Modifiers > 1 ' If Ctrl or Alt is held down. (Shift=1) + + ' -------------------------- + ' Process global shortcuts, exit if matched (like ESC) + If ProcessGlobalKey(oEvent) Then + ' Pass + + ElseIf MODE = "INSERT" Then + bConsumeInput = False ' Allow all inputs + + ' If Change Mode + ElseIf ProcessModeKey(oEvent) Then + ' Pass + + ElseIf ProcessNumberKey(oEvent) Then + bIsMultiplier = True + + ' Normal Key + ElseIf Not ProcessNormalKey(oEvent) and bIsModified Then + ' If is modified but doesn't match a normal command, allow input + ' (Useful for built-in shortcuts like Ctrl+s, Ctrl+w) + bConsumeInput = False + End If + ' -------------------------- + + + ' Reset multiplier + If not bIsMultiplier Then resetMultiplier() + setStatus(getMultiplier()) + + ' Show terminal-like cursor + oTextCursor = getTextCursor() + If MODE = "NORMAL" Then + oTextCursor.gotoRange(oTextCursor.getStart(), False) + oTextCursor.goRight(1, False) + oTextCursor.goLeft(1, True) + thisComponent.getCurrentController.Select(oTextCursor) + + ElseIf MODE = "INSERT" Then + oTextCursor.gotoRange(oTextCursor.getStart(), False) + thisComponent.getCurrentController.Select(oTextCursor) + End If + + KeyHandler_KeyPressed = bConsumeInput +End Function + +Function KeyHandler_KeyReleased(oEvent) As boolean + KeyHandler_KeyReleased = (MODE = "NORMAL") 'cancel KeyReleased +End Function + + +' ---------------- +' Processing Keys +' ---------------- +Function ProcessGlobalKey(oEvent) + dim bMatched + bMatched = True + Select Case oEvent.KeyCode + ' PRESSED ESCAPE + Case 1281: + ' 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 + + gotoMode("NORMAL") + Case Else: + bMatched = False + End Select + ProcessGlobalKey = bMatched +End Function + + +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 +End Function + + +Function ProcessModeKey(oEvent) + dim bMatched + bMatched = True + Select Case oEvent.KeyChar + ' Insert modes + Case "i", "a", "I", "A": + If oEvent.KeyChar = "a" Then getCursor().goRight(1, False) + If oEvent.KeyChar = "I" Then ProcessMovementKey("^") + If oEvent.KeyChar = "A" Then ProcessMovementKey("$") + + gotoMode("INSERT") + Case "v": + gotoMode("VISUAL") + Case Else: + bMatched = False + End Select + ProcessModeKey = bMatched +End Function + + +Function ProcessNormalKey(oEvent) + dim i, bMatched, bIsVisual + bMatched = False + bIsVisual = (MODE = "VISUAL") ' is this hardcoding bad? what about visual block? + For i = 1 To getMultiplier() + dim bMatchedMovement, bMatchedDelete + + bMatchedMovement = ProcessMovementKey(oEvent.KeyChar, bIsVisual, oEvent.Modifiers) + bMatchedDelete = ProcessDeleteKey(oEvent) + bMatched = bMatched or bMatchedMovement or bMatchedDelete + + ' Special case: Break from For loop if in visual mode and has deleted, + ' since multiplier should not be applied + If bIsVisual and bMatchedDelete Then Exit For + Next i + + ProcessNormalKey = bMatched +End Function + + +Function ProcessDeleteKey(oEvent) + dim oTextCursor, bMatched + oTextCursor = getTextCursor() + bMatched = True + Select Case oEvent.KeyChar + ' Case "d": + ' setSpecial("d") + + Case "x": + thisComponent.getCurrentController.Select(oTextCursor) + oTextCursor.setString("") + Case Else: + bMatched = False + + End Select + + ProcessDeleteKey = bMatched +End Function + + +' ----------------------- +' Main Movement Function +' ----------------------- +' Default: bExpand = False, keyModifiers = 0 +Function ProcessMovementKey(keyChar, Optional bExpand, Optional keyModifiers) + dim oTextCursor, bSetCursor, bMatched + 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 + dim isControl + isControl = (keyModifiers = 2) or (keyModifiers = 8) + + ' Ctrl+d and Ctrl+u + If isControl and keyChar = "d" Then + getCursor().ScreenDown(bExpand) + ElseIf isControl and keyChar = "u" Then + 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 + + Select Case keyChar + Case "l": + oTextCursor.goRight(1, bExpand) + Case "h": + oTextCursor.goLeft(1, bExpand) + + ' oTextCursor.goUp and oTextCursor.goDown SHOULD work, but doesn't (I dont know why). + ' So this is a weird hack + Case "k": + 'oTextCursor.goUp(1, False) + getCursor().goUp(1, bExpand) + bSetCursor = False + Case "j": + 'oTextCursor.goDown(1, False) + getCursor().goDown(1, bExpand) + bSetCursor = False + ' ---------- + + Case "^": + getCursor().gotoStartOfLine(bExpand) + bSetCursor = False + Case "$": + 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 + + Case "w", "W": + oTextCursor.gotoNextWord(bExpand) + Case "b", "B": + oTextCursor.gotoPreviousWord(bExpand) + Case "e": + oTextCursor.gotoEndOfWord(bExpand) + + Case ")": + oTextCursor.gotoNextSentence(bExpand) + Case "(": + oTextCursor.gotoPreviousSentence(bExpand) + Case "}": + oTextCursor.gotoNextParagraph(bExpand) + Case "{": + oTextCursor.gotoPreviousParagraph(bExpand) + Case Else: + bSetCursor = False + bMatched = False + End Select + + ' If oTextCursor was moved, set global cursor to its position + If bSetCursor Then + getCursor().gotoRange(oTextCursor.getStart(), False) + 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 + + + resetMultiplier() + gotoMode("NORMAL") + + ' Show terminal cursor + oTextCursor = getTextCursor() + oTextCursor.goRight(1, False) + oTextCursor.goLeft(1, True) + thisComponent.getCurrentController.Select(oTextCursor) + + sStartXKeyHandler() +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