setting custom key maps

Post a reply

:D :) ;) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :!: :?: :idea: :arrow: :| :mrgreen: :geek: :ugeek:
BBCode is ON
[img] is ON
[flash] is OFF
[url] is ON
Smilies are ON
Topic review

Expand view Topic review: setting custom key maps

Re: setting custom key maps

Post by robert » Sun Jun 17, 2012 1:57 pm

Sorry, nothing jumps out at me. I'd like to create a smaller test case in Java (I don't know Clojure!), but haven't had a lot of time the past week or so due to pressures from work. I'll check into this when I can though.

Re: setting custom key maps

Post by Guest » Fri Jun 15, 2012 4:33 pm

So you were right about the createKeymap not being called. Once I recreated that in my code and call it after set input map I can enter text again. For some reason though now I can't backspace once back in default mode. Here is how I am setting and resetting (sorry it's in clojure but I think it should still be understandable :)

Code: Select all
(defn edit-mode!
  [kw rta input-map]
  (let [default-input-map-name "RTextAreaUI.inputMap"
        default-action-map-name "RTextAreaUI.actionMap"
        action-map (cast ActionMap (UIManager/get default-action-map-name))
        rta-input-map (cast InputMap (UIManager/get default-input-map-name))]
      (= kw :default)
          (swap! editor-mode (fn [_] :default))
          (set-input-map! rta input-map)         
;          (set-action-map! rta action-map)
          (.setKeymap rta (create-keymap))
           (println "edit mode to default"))
      (= kw :vim)
              (swap! editor-mode (fn [_] :vim))
            (set-input-map! rta input-map)
          (println "edit mode to vim"))
             (swap! editor-mode (fn [_] :default))
           (set-input-map! rta input-map)
          (.setKeymap rta (create-keymap))
           (println (name kw) "is not a valid edit mode. Setting edit mode to default")))))

cond is just like a switch case where you define a condition then the next element is the resulting function and let statements are how you define local vars in clojure.

My :default mode is supposed to be like a factory reset of key bindings. Do you see anything I might be missing in reseting the initial maps? For now the call to set-action-map! is commented out (comments are ;; in clojure) because when I call that no key commands work.

I am going to get cracking on the sequential key listener in the next couple days so for now I have implemented all of the single key commands for navigation etc. To make the vim commands, which will consist of multiple RecordableTextActions, work nicely with the macro recording system do I wrap the combinations in a new RecordableTextAction child classes? My initial thought is that I would just create a new RTA which calls the desired action combinations actionPerformedImpl from it's own.

thanks as always

Re: setting custom key maps

Post by robert » Tue Jun 12, 2012 12:44 pm

For your first issue, I think you'll have to roll your own. Store the keystrokes for sequential key commands in a Stack or List. Then you'll have to look back on previous keystrokes and use them as "parameters." You've found the first unforeseen problem! I'm not sure of an elegant way to do this, but if you find an appropriate place to store the keystroke stack, it might not be so bad.

For your bigger issue, if I'm understanding you correctly, it sounds like the default action (the one that catches regular keystrokes and inserts them) is missing. Are you saying that you can:

1. Start off in command mode
2. Type "i" to insert some text
3. Keystrokes for letters ("w", "a", etc.) don't work, but Cmd+X does for copying

Is that right? If so, then check the value of textArea.getKeymap().getDefaultAction() and see if it is null or some value. I can do some digging later, but check out RTextAreaUI, you'll find a method named createKeymap() where we're replacing the default key-typed action with our own. My first guess is that somehow, when replacing the InputMap and ActionMap, you're stomping on this action. The Swing text package is rather delicate (as you can probably tell) and I may have forgotten some intricacy. I can try to look into it myself later if you're still having troubles.

Re: setting custom key maps

Post by jonrose » Sun Jun 10, 2012 8:53 pm

Thanks for the pointers. So I have working edit modes and toggling vim command mode key bindings but I have hit a couple road blocks. Changing the input mappings for the single key commands is easy but I can't find any information on doing sequential key combinations with swing. Do you think that is something I will have to make from scratch? I don't think it would be to difficult to store time stamped keys and look up combinations but I would rather let swing handle that if I can.

The big issue I am having though is when I try to reset the inputMap with the default input map I can use all of the RText key commands (cmd-d, cmd-c, etc) but I can not enter text. All I can really think of as a possible cause is that I need to reset the JTextArea inputMap? Lastly, do you think I need to have multiple action maps or can I just have one actionMap with all of my actions added to the the defaults. Right now I am just updating the input map which is working for everything but the text issues.



Re: setting custom key maps

Post by robert » Sat Jun 09, 2012 1:16 am

You might consider having a different InputMap and ActionMap for editing vs. command mode. For example, for your "command mode" map pair, map "h" to prevCharAction, "l" to nextCharAction, etc. Pressing "i" would switch into text entry mode by swapping the InputMap and ActionMap for new ones. This new pair would behave fairly similarly to the standard text component Input/ActionMaps, but would map the Escape key to a "CommandModeAction," which would swap the maps back to their command-mode values.

It seems simple (besides actually writing all the actions for command mode), but I'm sure there would be pitfalls I'm not thinking of at the moment.

I'm not sure I would mess with EditorKits or UI classes as a first pass. I'd just create some class with a static setter such as "installViKeyMappings(RSyntaxTextArea)" that manually sets the InputMap and ActionMap, and work on getting them working. Then you have a quick 'n' dirty way of testing and/or integrating it into your application, and once everything seems robust, then look at how to better plug it into the library.

Re: setting custom key maps

Post by jonrose » Wed Jun 06, 2012 11:01 pm

Hi Robert,

Thanks for the quick reply. After going through the code I ended up finding the methods you suggested and I am trying to figure out what would be the best way to manage key binding modes. After seeing how things are set up in RTADefaultInputMap I think this won't be too difficult to do. I had initially thought I would add new key bindings through menu items to build up a pallet of vim commands and figured some organized menus wouldn't be too annoying. I tested this by adding a new menu item for DeleteRestOfLine by making the menu item, key command, and attaching the RTextAreaEditorKit.DeleteRestOfLineAction but nothing happens when I press my key command. The good news is that I can still add my menus if I want, but it looks like I should be able to update all TextArea keys at once through setInputMap and setActionMap.

My initial though was to do as you have suggested and store an edit mode state then replace the entire inputMap for the desired text areas. Where do you think the edit mode state should be stored? It seems like I could add a mode field and then a setEditMode(int mode, int subMode) function in RTextAreaUI which would manage the swapping of input/action maps. Then it would be easy enough to make a new RTAVimInputMap class which could be in clojure or java but still compatible for RSTA.

As for the actions, could I extend RTextAreaEditorKit and add the additional vim command actions? Then the setMode(int mode, int subMode) function would handle reverting back to default when not in vi mode as well as toggling the different vi modes. Sorry to have so many questions, i am new to input and action maps.

It would be awesome to make some RTSA contributions especially since it has added so much functionality to my project ( from the start;) I have to say, i was pretty surprised to see that there isn't already a vim project for JTextArea. For Java parent classes to be supported though I guess I wouldn't want to extend RTextAreaEditorKit but I could use it as a template to create new vi EditorKits.

Re: setting custom key maps

Post by robert » Wed Jun 06, 2012 9:40 pm

Hi Jon,

Your project sounds interesting. I believe you can simply use textArea.setActionMap(ActionMap) and textArea.setInputMap(JComponent.WHEN_FOCUSED, myInputMap) to modify the key mappings. Let me know if you have any specific questions or problems.

I've never used emacs, but a set of classes to mimic vi-style input would be really cool. I imagine you'd have to dynamically swap out the Input/ActionMaps to mimic switching from various vi modes to editing, etc. Such a thing would be really cool to package up and ship with or along-side RSTA! In fact, I imagine it could work for JTextAreas and JEditorPanes as well.

setting custom key maps

Post by jonrose » Wed Jun 06, 2012 6:44 pm


I have been digging into the RText and RSyntaxTextArea code quite a bit for a project I am working on and I have a few questions. I'm planning on using RSyntaxTextArea in my Google Summer of Code project making a light weight Clojure editor for creating music with Overtone (a sound synthesis library). Since I am using the RSTA lib via Clojure interop, I am trying to figure out the best way to have different key binding modes. My goal is to have a default key map as well as vim and emacs style mappings.

I have looked through the code in RTextAreaUI and I think I understand how RTSA is using the swing UIManager to set the inputMap and actionMaps for the most part.Unfortunately though I can't find any access functions for getting or setting these input and action maps. I am also not familiar with these swing classes and I am worried about building some additional key mapping system on top of something already there. Not only could this create issues potentially but it would be nice to handle this in a proper format if there is one. The perfect scenario for me would be if I could define my own map of keys and actions (or groups of actions) and then just pass them to a setInputMap if there is one.

If anyone has any tips or suggestions I would really appreciate it.