4

I am mimicking another victim of keyboard layout chaos, which suffers from similar yet slightly different issue.

I am using German and Russian keyboard layouts extensively on my Windows 10 workstation and switch often between them. The problem is related to the fact that letters Y and Z are switched in the German keyboard layout comparing to the standard English QWERTY layout.

If I use German layout, the Undo shortcut (Ctrl-Z) is binded to the key Z, which is located in the middle of the first letter row between T and U. The Redo (Ctrl-Y) is binded to the key Y, which is located left to X in the low row of letters on the German QWERTZ layout.

If you switch the layout to the Russian, suddenly an evil thing is happening. Undo and Redo shortcuts mutually change their position on the keyboard. Undo (Ctrl-Z) is located now on Y (lower row, left to X) and the Redo on Z (upper row, between T and U). This is location is identical to the location of Ctrl-Z and Ctrl-Y on the standard English keyboard layout.

That means that the location of Undo and Redo shortcuts is changing to the opposite all the time I switch the keyboard layout. As you can imagine I am always pressing the wrong shortcut, because it is impossible to learn two osciallating opposite patterns, at least my spine brain protests. Since I use Undo and Redo shortcuts very often, this behavior irritates me a lot.

I want to fix the physical location (keys) of the Redo and Undo shortcuts in the whole set of Windows applications either to German or better English layout.

Using English layout permanently for all kind of latin based texts although fixes the issue with jumping Undo/Redo shortcuts, but is not an option for me, because I need to type German texts with all umlauts and ß (do not suggest an international layout to enter accents, because German is my primary language and I want to type its native letters with one keystroke).

As the colleague in the referenced question already mentioned, there is no obvious answer in the Internet. The same problem is solved on Ubuntu by creating a custom keyboard layout. I cannot find any information about creating custom layout for Windows or patching the current one.

This behavior is consistent among all version of Windows I know and related deeply in the initial keyboard layout designs, which are just incompatible for users happen to use German-Russian language pair like me.

Good solution shall be implemented in Windows directly without any additional software, or only free open source software. Working solutions involving AutoHotKey are welcome.

With the help of @miroxlav I came up with this half-working AHK script:

; Undo
$^z::
    hWnd := WinExist("A")
    ThreadID := DllCall("GetWindowThreadProcessId", "UInt", hWnd, "UInt", 0)
    hKL := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    If (hKL = 0x4070409)   ; revert undo-redo hotkey mapping if in German layout
        Send ^y
    Else
        Send ^н            ; pass the keystroke through
Return

; Redo
$^y::
    hWnd := WinExist("A")
    ThreadID := DllCall("GetWindowThreadProcessId", "UInt", hWnd, "UInt", 0)
    hKL := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    If (hKL = 0x4070409)   ; revert undo-redo hotkey mapping if in German layout
        Send ^z
    Else
        Send ^я            ; pass the keystroke through
Return

This script perfectly remaps Ctrl-Y <-> Ctrl-Z on German layout, but corrupts both hotkeys on Russian layout. I use ^н and ^я with the hope to emulate Ctrl-Y or Ctrl-Z scan-code equivalents for Russian layout, but it does not work (generates н for Ctrl-Y and nothing visible for Ctrl-Z). Using ^z and ^y for Russian does not work either, generating letters z and y respectively.

Different to Miroxlav answer I had to use keyboard hook ($) to avoid looping and remove labels (probably copy paste mistake)

Solution suggested by Williams:

^SC02c::Send ^y
^SC015::Send ^z

unfortunately does not work either. It generates plain letters 'y' and 'z' on Russian layout and no change on German layout (Ctrl-Z and Ctrl-Y passed through in the original mapping).

Still looking for the solution.

4
  • So the above works on my keyboard when I switch between Dvorak and . Qwerty. I understand why it is doing this it because the keyboard shortcuts are different. You are going to figure what keys those keys are on your keyboard and will have to detect what key is being sent I deleted my answer. mirolax is probably about what I would come up with.
    – William
    Sep 1, 2017 at 22:42
  • Yes, @William, I tried sending keystrokes I believe are send to Undo and Redo when Russian layout is active. I code them as ^я and ^н consequently. Unfortunately they generate н in first and nothing visible in second case. Sep 1, 2017 at 23:07
  • So this is how I do it on my keyboard with Dvorak and Qwerty. I have it set to Qwerty and then remap all the keys on keyboard using Autohotkey. I believe it should work for you and can post mine if it helps. This should get around the issue of the keys changing between layouts. But it means the keyboard shortcuts need to map 1 to 1.
    – William
    Sep 2, 2017 at 0:22
  • You need to use the scan codes.
    – William
    Sep 2, 2017 at 0:27

4 Answers 4

1

You may simply create your own keyboard layout using Microsoft Keyboard Layout Creator 1.4 from Official Microsoft Download Center.

Youtube provides a plenty of video tutorials how to use it (like this with Russian locale) - just search for its name. But basically, you clone one of existing keyboard layouts and modify it according to your needs.

AutoHotKey solution:

(note: replace 0x4090409 constant (US keyboard) with value which applies to your layout)

$^z::
    hWnd := WinExist("A")
    ThreadID := DllCall("GetWindowThreadProcessId", "UInt", hWnd, "UInt", 0)
    hKL := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    If (hKL = 0x4090409)
        Send ^z
    Else
        Send ^y
Return

$^y::
    hWnd := WinExist("A")
    ThreadID := DllCall("GetWindowThreadProcessId", "UInt", hWnd, "UInt", 0)
    hKL := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    If (hKL = 0x4090409)
        Send ^y
    Else
        Send ^z
Return

Hook ($) is needed to avoid recursion, i.e. Send ^z or Send ^y invoking just another AHK macro.

I tested that keyboard detection condition, it worked nicely.

Of course, you can place keyboard detection into function and therefore optimize the code etc. I was lazy to do that. :)

9
  • Thank you @miroxlav for your prompt reply! It is a possible solution, thought it is not an ideal, because if I switch Y and Z in the German layout, I will have to re-learn its new location. I don't know if it would be easy for me after so many years using the standard layout. Also I am using about 4 different PC and will have to install new layout to all of them. Since I am already using AutoHotKey and its script is automatically shared among all my PCs, I am currently investigating in the solution involving AutoHotKey. Sep 1, 2017 at 20:50
  • Just use SC010 in autohotkey
    – William
    Sep 1, 2017 at 21:08
  • @William – thank you for hint. I don't have much time to experiment with SC now, maybe you can therefore add an answer which will be more elegant than mine. :) I found keyboard layout detection here and found that it works.
    – miroxlav
    Sep 1, 2017 at 21:11
  • Thank you @miroxlav! Unfortunately your suggestion works only for German layout. See my revised question. Probably need a bit more hacking. Any ideas? Sep 1, 2017 at 22:08
  • @AntonGolubev – thank you for description of the problem. I think that and must work – are you running Unicode version of AHK and is your script saved in UTF-8 encoding? (Both are a must.) Could you test with latin characters instead of cyrillic ones? My test (just with A and B worked 100%) I think your problem is with Send ^н and Send ^я and is not related to keyboard layout detection. Just play with AHK and fix the issues is described. Otherwise the code should be working and the answer is complete.
    – miroxlav
    Sep 2, 2017 at 0:33
1

I found finally a working solution for me (AutoHotKey script):

; Undo Ctrl-Z
^sc02C::
    Send, ^{sc015}
Return

; Redo Ctrl-Y
^sc015::
    Send, ^{sc02C}
Return

It perfectly keeps undo key (Ctrl-Z) on its logical place - low left row - independently from the keyboard layout.

Thank you @Miroxlav and and @Williams for bringing pieces for the solution.

6
  • This is a life saver! If it will not cause some problems somewhere down the line (writing this just after a few hours of use), this is really a headache solver! In so many programs I was temporarily switching layouts just to get that one handed Undo. My Undo basicaly was Alt+Shift, Ctrl+Z, Alt+Shift 😁. Jan 5 at 23:21
  • But how does this work? I can understand this is sending the opposite scan code, but why wouldn't it flip the Y and Z in every layout? Jan 5 at 23:22
  • I'm glad the trick helped you. It is indeed a bit obscure solution. It flips scan codes (Ctrl-Y<->Ctrl-Z) on German layout only thus normalizing the Undo=Lower-Left-Key behavior. Why the flip rule is inactive when other layouts are selected, it is still a puzzle for me. Anyhow it still helps me everyday Jan 6 at 12:52
  • Same here... I am using CZE and EN layout. The fact that it flips SCs is obvious, but why it does that selectively is really a conundrum... anyway, lets hope it stays that way. It would be helpful to also add Shift+Ctrl variants to this, since a lot of programs now uses Shift+Ctrl+Z for redo. Important thing I found out is that you have to place the Shift modifier (+) after the Ctrl modifier (^). Here is a compact setup for AHKv2 #Requires AutoHotkey v2.0 ;Undo Ctrl-Z ^sc015::Send "^{sc02C}" ^+sc015::Send "^+{sc02C}" ;Redo Ctrl-Y ^sc02C::Send "^{sc015}" ^+sc02C::Send "^+{sc015}" Jan 6 at 14:07
  • Great, Lukas you found solution for Shift+Ctrl-* combination. Maybe you'd like to share it as an additional answer with proper formatting so people can easily find it? Jan 7 at 12:44
1

Based on the Anton's answer I have updated the code to AHK v2 and added support for Ctrl+Shift variant, since many programs now use that for Redo.

#Requires AutoHotkey v2.0

;Remapping upper mid Y/Z key (Y in EN layout)
^sc015::Send "^{sc02C}"
^+sc015::Send "^+{sc02C}"

;Remapping lower left Y/Z key (Z in EN layout)
^sc02C::Send "^{sc015}"
^+sc02C::Send "^+{sc015}"

The point of this script is to fix Undo action to lower left Y/Z key, since different layouts (QWERTZ vs QWERTY) move the Z to upper mid, which is impractical. The same goes for Redo, which is now fixed to upper mid Y/Z key.

Since Undo/Redo are usually performed by Ctrl+Z/Y, this can be achieved by capturing the pressed key and reemiting the "correct" key. Those are the ^sc0** lines. The ^+sc0** lines are for reemiting Ctrl+Shift+Z/Y events, since many programs use Ctrl+Shift+Z for Redo.

The "correct" key is in quotes because if you look at the script, it should be actually just flipping the Ctrl(+Shift)+Y/Z everywhere... therefore achieving nothing. However that is not what happens and it just works... even though I and AFAIK the original author don't know why.

If you have any insight into why this works or it doesn't work for you... speak up 😉.

1
  • OK... found the culprit. The AHK script must be executed with the keyboard layout you want to flip active. Jan 7 at 14:04
0

Despite the fact that the accepted answer works, it only works if the selected layout at the time was the German layout, otherwise it flips the undo function in all other layouts.

To solve this you just have to replace the US code 0x4090409 with the German code 0x4070407. Also you have change the if statement like this:

$^z::
    hWnd := WinExist("A")
    ThreadID := DllCall("GetWindowThreadProcessId", "UInt", hWnd, "UInt", 0)
    hKL := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    If (hKL = 0x4070407)
        Send ^y
    Else
        Send ^z
Return

$^y::
    hWnd := WinExist("A")
    ThreadID := DllCall("GetWindowThreadProcessId", "UInt", hWnd, "UInt", 0)
    hKL := DllCall("GetKeyboardLayout", "UInt", ThreadID, "UInt")
    If (hKL = 0x4070407)
        Send ^z
    Else
        Send ^y
Return
2
  • "Selected layout at the time"... at what "time" do you mean? I am using it with CZE/EN layouts and it works no problem. Jan 6 at 14:12
  • @LukášŘádek I frequently switch between different keyboard layouts like German, English, and Turkish. When I'm using the German layout, the accepted solution works as intended. However, when I'm using the Turkish layout, the letters y and z are also flipped, which is not what I want. This is because in the Turkish layout, the positions of y and z are the same as in the English layout. Jan 13 at 20:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .