2

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.

  • 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 '17 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. – Anton Golubev Sep 1 '17 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 '17 at 0:22
  • You need to use the scan codes. – William Sep 2 '17 at 0:27
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. :)

  • 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. – Anton Golubev Sep 1 '17 at 20:50
  • Just use SC010 in autohotkey – William Sep 1 '17 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 '17 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? – Anton Golubev Sep 1 '17 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 '17 at 0:33
0

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.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.