Compare commits

...

28 Commits

Author SHA1 Message Date
Jules Aguillon
0739c6bd2c Release 1.27.0 (39) 2024-03-03 23:19:02 +01:00
Yogesh-B
a1de69bcc4 Gujarati phonetic layout (#562)
added shift modifier characters
Add support for gujarati numerals

Co-authored-by: yogesh-b <yogesh@improwised.com>
2024-03-03 23:12:52 +01:00
Jules Aguillon
5e5937b0f4 Fix auto-capitalisation disabling locked shift
Shift locked via the "caps lock" key use the "fake pointer" mechanism
that is also used by auto-capitalisation.

Make sure that unlatching a fake pointer do not disabled a locked
modifier.

The implementation is moved into the Pointers class for a safer API and
easier implementation.
2024-03-03 17:14:01 +01:00
Chasm Solacer
ce38a4f151 Update pl/strings.xml (#578) 2024-03-03 01:06:07 +01:00
Jules Aguillon
0e0c761a92 Add QWERTY (Slovak) layout
The original author is @sejkm in https://github.com/Julow/Unexpected-Keyboard/issues/573
2024-03-02 20:39:22 +01:00
Jules Aguillon
940cf0deb8 compose: Parse json files and fix edge cases
There's no json file yet, this was part of an experiment.

Add a missing escape rule and detect colliding sequences.
2024-03-02 19:12:37 +01:00
Jules Aguillon
c2d5b14261 compose: Add a README.md
Mention the source of the sequences files.
2024-03-02 19:11:41 +01:00
Jules Aguillon
ebda7b2c3e Improve the AZERTY layout
Add éèàç directly to the layout, which removes the corresponding dead
keys.

Remove the requirement for ù and ÿ from method.xml, which are extremely
rare and can be typed via the compose key.
2024-03-02 17:42:57 +01:00
Jules Aguillon
39f6d294fb translations: Remove outdated store descriptions 2024-03-02 17:22:22 +01:00
Jules Aguillon
8b5d971fb5 Modify the number row according to the numpad script
The same way as the numpad is modified.
2024-02-28 23:19:50 +01:00
Jules Aguillon
8c7559d8f6 Fix localized numpad outputing wester arabic numbers
The number keys were constructed in the wrong way: the rendered symbol
was correct but not the underlying character that is typed.
2024-02-28 20:42:52 +01:00
Jules Aguillon
d7e5040873 method.xml: Add en-CA and fr-CA
Without this, the system might choose en-GB and fr-CH.
This makes them more similar to en-US and fr-FR (without € in extra keys).
2024-02-24 11:54:46 +01:00
Jules Aguillon
b29a344cd0 Remove € from en-GB extra keys 2024-02-22 19:34:23 +01:00
Jules Aguillon
b8e673b614 Ensure the layout contain the config key
Without the config key, the user risk being locked in a custom layout
that lacks an escape key like 'switch_numpad', 'change_method', etc..
2024-02-22 19:10:31 +01:00
Jules Aguillon
bae82d6ed8 Add FUNDING.yml
Thanks to everyone considering funding the development of the app.
2024-02-21 19:07:34 +01:00
Jules Aguillon
f913051dca Relicense layout definitions under CC0 (#528)
* Relicense layouts under CC0

Layout definitions are licensed differently from the rest of the
application source code to allow use in other projects related or
unrelated to Unexpected Keyboard.

Some layouts are not re-licensed and their original copyright continues
to apply. The copyright notice is added at the top of the files.

* Contributing: Mention layout licensing
2024-02-18 23:37:26 +01:00
Jules Aguillon
dac702ad0b CI: Improve layout and store description checks (#565)
* CI: Run 'check_layout.py'

Ensures that 'check_layout.output' is not outdated.
Also, update it.

* CI: Check new store descriptions

When a store description is added, 'python3 sync_translations.py' leaves
an untracked file that is not checked.

This makes sure that untracked store descriptions are noticed in CI.
2024-02-18 17:55:41 +01:00
matthiakl
f2f6ef0627 neo2: Update bottom row (#560) 2024-02-18 17:10:46 +01:00
Sergiy Stupar
6389d58553 Update Ukrainian translation (#561) 2024-02-18 17:08:56 +01:00
Jules Aguillon
e40558677e Allow to remove the compose key in the settings 2024-02-17 23:28:31 +01:00
Jules Aguillon
ae270b36f4 Add the compose key on alternate layouts
Though it is not useful in the greek-math layout, it could give ideas
for new sequences.
2024-02-17 23:28:31 +01:00
Jules Aguillon
146d520325 compose: Grey out keys that are not in sequence
Keys that are not in the pending compose sequence are greyed out with
the new 'FLAG_GREYED' flag.
2024-02-17 23:28:31 +01:00
Jules Aguillon
065d9520e5 Dim secondary keys in every themes
Themes do not dim secondary keys the same way due to the "offset"
mechanism. Instead, use a ratio that is the same for every themes.

It's still possible to override this ratio per theme.
2024-02-17 23:28:31 +01:00
Jules Aguillon
bb60ed9b13 Fix KeyValue flags layout
The kind field wasn't large enough to hold the new Compose_pending kind.
The flags field is reduced in size by removing a free spot.

The FLAGS_BITS mask is defined in a safer way.
2024-02-17 23:28:31 +01:00
Jules Aguillon
d56e4d129e compose: Add glyph 2024-02-17 23:28:31 +01:00
Jules Aguillon
e9f734b6cc compose: Don't remove keys not in sequence
Keys that are not part of any possible sequences are now dimmed and
still usable instead of being removed, which felt weird.
2024-02-17 23:28:31 +01:00
Jules Aguillon
63e7ac2e94 compose: Add X11 compose sequences
compile.py implements a parser for X11's Compose.pre files. A lot of
code is necessary to interpret character names but thanksfully, the name
of most characters is contained in the file.

The state machine is compiled into two char arrays which unfortunately
requires an expensive initialisation and allocation.
2024-02-17 23:28:31 +01:00
Jules Aguillon
8c29073260 Compose key
The COMPOSE_PENDING modifier indicate whether a compose sequence is in
progress. The new key of kind Compose_pending sets the current state of
the sequence.

The compose sequences are compiled into a state machine by a python
script into a compact encoding.

The state of the pending compose is determined by the index of a state.
2024-02-17 23:28:31 +01:00
56 changed files with 6285 additions and 185 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
srcs/juloo.keyboard2/ComposeKeyData.java -diff

View File

@@ -1,19 +1,26 @@
name: Check layouts name: Check layouts
# Runs 'gen_layouts.py' and checks that the generated file were uptodate.
# This doesn't run 'check_layout.py'.
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
pull_request: pull_request:
jobs: jobs:
check-layouts: gen-layouts:
name: Generated files
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v4
- run: python3 gen_layouts.py - run: python3 gen_layouts.py
- name: Check that the generated layouts.xml is uptodate, run python3 gen_layouts.py otherwise - name: "Check that the generated 'layouts.xml' is uptodate, otherwise run 'python3 gen_layouts.py'"
run: git diff --exit-code
check-layouts:
name: check_layout.output
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- run: python3 check_layout.py
- name: "Check that the generated 'check_layout.output' is uptodate, otherwise run 'python3 check_layout.py'"
run: git diff --exit-code run: git diff --exit-code

View File

@@ -12,5 +12,5 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v4
- run: python3 sync_translations.py - run: python3 sync_translations.py
- name: Check that strings files are uptodate, run python3 sync_translations.py otherwise - name: "Check that strings files are uptodate, otherwise run 'python3 sync_translations.py'"
run: git diff --exit-code run: git add -N . && git diff --exit-code

View File

@@ -110,6 +110,10 @@ Run `./gradlew checkKeyboardLayouts` to check some properties about your
layout. This will change the file `check_layout.output`, which you should layout. This will change the file `check_layout.output`, which you should
commit. commit.
Layouts are CC0 licensed by default. If you do not want your layout to be
released into the public domain, add a copyright notice at the top of the file
and a mention in `srcs/layouts/LICENSE`.
#### Adding a programming layout #### Adding a programming layout
A programming layout must contain all ASCII characters. A programming layout must contain all ASCII characters.

3
FUNDING.yml Normal file
View File

@@ -0,0 +1,3 @@
github: [ Julow ]
liberapay: Julow
custom: [ "https://paypal.me/JulesAguillon" ]

Binary file not shown.

View File

@@ -10,8 +10,8 @@ android {
applicationId "juloo.keyboard2" applicationId "juloo.keyboard2"
minSdk 11 minSdk 11
targetSdkVersion 33 targetSdkVersion 33
versionCode 38 versionCode 39
versionName "1.26.0" versionName "1.27.0"
} }
sourceSets { sourceSets {
@@ -85,7 +85,6 @@ android {
} }
dependencies { dependencies {
} }
tasks.register('buildKeyboardFont') { tasks.register('buildKeyboardFont') {
@@ -108,6 +107,7 @@ tasks.withType(Test).configureEach {
dependsOn 'genLayoutsList' dependsOn 'genLayoutsList'
dependsOn 'checkKeyboardLayouts' dependsOn 'checkKeyboardLayouts'
dependsOn 'syncTranslations' dependsOn 'syncTranslations'
dependsOn 'compileComposeSequences'
} }
tasks.register('genLayoutsList') { tasks.register('genLayoutsList') {
@@ -138,6 +138,19 @@ tasks.register('syncTranslations') {
} }
} }
tasks.register('compileComposeSequences') {
def out = "srcs/juloo.keyboard2/ComposeKeyData.java"
println "\nGenerating ${out}"
exec {
def sequences = new File(projectDir, "srcs/compose").listFiles().findAll {
it.name.endsWith(".pre") || it.name.endsWith(".json")
}
workingDir = projectDir
commandLine("python", "srcs/compose/compile.py", *sequences)
standardOutput = new FileOutputStream("${projectDir}/${out}")
}
}
tasks.named("preBuild") { tasks.named("preBuild") {
dependsOn += "initDebugKeystore" dependsOn += "initDebugKeystore"
dependsOn += "copyRawQwertyUS" dependsOn += "copyRawQwertyUS"
@@ -167,6 +180,7 @@ tasks.register('copyLayoutDefinitions')
{ {
copy { copy {
from "srcs/layouts" from "srcs/layouts"
include "*.xml"
into "build/generated-resources/xml" into "build/generated-resources/xml"
} }
} }

View File

@@ -50,6 +50,10 @@ Layout doesn't define some important keys, missing: f11_placeholder, f12_placeho
# grek_qwerty # grek_qwerty
Duplicate keys: ; Duplicate keys: ;
1 warnings 1 warnings
# guj_phonetic_in
Duplicate keys: ટ, ડ
Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
2 warnings
# hang_dubeolsik_kr # hang_dubeolsik_kr
0 warnings 0 warnings
# hebr_1_il # hebr_1_il
@@ -65,7 +69,7 @@ Layout doesn't define some important keys, missing: f11_placeholder, f12_placeho
1 warnings 1 warnings
# latn_bone # latn_bone
Layout includes some ASCII punctuation but not all, missing: $ Layout includes some ASCII punctuation but not all, missing: $
Layout redefines the bottom row but some important keys are missing, missing: cursor_left, cursor_right, loc end, loc home, loc page_down, loc page_up, loc switch_greekmath, loc voice_typing, switch_backward Layout redefines the bottom row but some important keys are missing, missing: cursor_left, cursor_right, loc compose, loc end, loc home, loc page_down, loc page_up, loc switch_greekmath, loc voice_typing, switch_backward
2 warnings 2 warnings
# latn_colemak # latn_colemak
Some keys contain whitespaces, unexpected: ́ Some keys contain whitespaces, unexpected: ́
@@ -73,8 +77,7 @@ Some keys contain whitespaces, unexpected: ́
# latn_dvorak # latn_dvorak
0 warnings 0 warnings
# latn_neo2 # latn_neo2
Layout redefines the bottom row but some important keys are missing, missing: loc end, loc home, loc page_down, loc page_up 0 warnings
1 warnings
# latn_qwerty_br # latn_qwerty_br
0 warnings 0 warnings
# latn_qwerty_cz # latn_qwerty_cz
@@ -97,6 +100,9 @@ Layout doesn't define some important keys, missing: f11_placeholder, f12_placeho
# latn_qwerty_se # latn_qwerty_se
Duplicate keys: !, ', ,, -, ., ? Duplicate keys: !, ', ,, -, ., ?
1 warnings 1 warnings
# latn_qwerty_sk
Layout doesn't define some important keys, missing: f11_placeholder, f12_placeholder
1 warnings
# latn_qwerty_tly # latn_qwerty_tly
Duplicate keys: a, c, j, q Duplicate keys: a, c, j, q
Layout doesn't define some important keys, missing: esc, f11_placeholder, f12_placeholder, tab Layout doesn't define some important keys, missing: esc, f11_placeholder, f12_placeholder, tab

View File

@@ -0,0 +1,6 @@
New layouts: QWERTY (Slovak), Gujarati phonetic
Add Linux Compose key to type a variety of characters using known combinations.
Fixed localized numpad and number row.
Many improvements and bug fixes.
Huge thanks to the contributors: @Yogesh-B, @ChasmSolacer, @matthiakl, @Sestowner, @RyanGibb, @BogdanLata, @RetrogisusDEV, @V6lhost, @ErrrorMaxx, @sdrapha, @vedamanavi

View File

@@ -0,0 +1,6 @@
Головна особливість полягає в тому, що ви можете вводити більше символів, проводячи клавіші до кутів.
Ця програма спочатку була розроблена для програмістів, які використовують Termux.
Тепер ідеально підходить для щоденного використання.
Ця програма не містить реклами, не надсилає жодних мережевих запитів і має відкритий код.

View File

@@ -0,0 +1 @@
Легка та конфіденційна віртуальна клавіатура для Android.

View File

@@ -3,24 +3,12 @@
<string name="app_name_release">Klávesnice Unexpected</string> <string name="app_name_release">Klávesnice Unexpected</string>
<string name="app_name_debug">Klávesnice Unexpected (pro ladění)</string> <string name="app_name_debug">Klávesnice Unexpected (pro ladění)</string>
<string name="short_description">Nenáročná virtuální klávesnice pro vývojáře.</string> <string name="short_description">Nenáročná virtuální klávesnice pro vývojáře.</string>
<string name="store_description">"Tato aplikace je virtuální klávesnící pro Android. Umožňuje rychlejší a plynulejší psaní písmen i symbolů (vč. diakritiky), a to ve vícero jazycích a vlastních rozloženích. To vše zdarma, bez reklam a bez plýtvání vašeho uložiště. Můžete psát libovolné znaky pomocí gest (ASCII i Unicode), používat mrtvé (univerzální) klávesy pro diakritická znaménka a mnohem více. <!-- <string name="store_description">"The main feature is that you can type more characters by swiping the keys towards the corners.
Klávesnice zobrazuje až 4 další znaky v rozích každé klávesy. Tyto znaky jsou vyvolány přejetím prstu do vybraného rohu. This application was originally designed for programmers using Termux.
Now perfect for everyday use.
No zkrátka...: This application contains no ads, doesn't make any network requests and is Open Source."</string> -->
- Obsahuje každý znak a speciální klávesu, která je běžnou součástí počítačové klávesnice. To přijde vhod obzvláště při používání aplikací jako např. Termux
- Můžete používat modifikační klávesy, obohaceny o speciální klávesy (např. Tab, Esc, šipky, F klávesy, ale také Ctrl nebo Alt !)
- Můžete psát vícero jazyky rychleji a bez chyb. Diakritická znaménka mohou být vyvolána i za pomocí mrtvých kláves. Nejdříve zvolte diakritické znaménko a pak znak, který chcete obohatit o toto znaménko.
- Je vysoce nenáročná a rychlá. Zabere 500x méně místa než klávesnice od Googlu (Gboard) a 15x méně než výchozí klávesnice systému. Bez reklam, bez sledování.
- Má vícero rozložení: QWERTY, QWERTZ, AZERTY. Motivy: Bílá, Tmavá, OLED Černá. A mnoho dalších které si s drobnou znalostí programování můžete upravovat dle libosti.
Nezapomeňte... Jako každá virtuální klávesnice, i tato musí být aktivována v nastavení systému (zařízení). Otevřte (Systémové) Nastavení a přejděte na:
(Další nastavení NEBO Nastavení systému) &gt; Jazyk &amp; způsob zadávání &gt; Spravovat klávesnice."</string>
<string name="settings_activity_label">Nastavení Klávesnice Unexpected</string> <string name="settings_activity_label">Nastavení Klávesnice Unexpected</string>
<string name="pref_portrait">V režimu na výšku</string> <string name="pref_portrait">V režimu na výšku</string>
<string name="pref_landscape">V režimu na šířku</string> <string name="pref_landscape">V režimu na šířku</string>
@@ -102,6 +90,7 @@ Nezapomeňte... Jako každá virtuální klávesnice, i tato musí být aktivov
<string name="launcher_tryhere">Po aktivaci můžete klávesnici rovnou vyzkoušet zde:</string> <string name="launcher_tryhere">Po aktivaci můžete klávesnici rovnou vyzkoušet zde:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<string name="key_descr_capslock">Caps lock</string> <string name="key_descr_capslock">Caps lock</string>
<!-- <string name="key_descr_compose">Compose</string> -->
<string name="key_descr_switch_greekmath">Řecké a matematické symboly</string> <string name="key_descr_switch_greekmath">Řecké a matematické symboly</string>
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<string name="key_descr_voice_typing">Hlasové zadávání</string> <string name="key_descr_voice_typing">Hlasové zadávání</string>

View File

@@ -90,6 +90,7 @@ Diese App enthält keine Werbung, benötigt keinen Netzwerkzugriff und ist quell
<string name="launcher_tryhere">Nach Aktivierung kannst du die Tastatur hier ausprobieren:</string> <string name="launcher_tryhere">Nach Aktivierung kannst du die Tastatur hier ausprobieren:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<string name="key_descr_capslock">Feststelltaste</string> <string name="key_descr_capslock">Feststelltaste</string>
<!-- <string name="key_descr_compose">Compose</string> -->
<string name="key_descr_switch_greekmath">Griechische &amp; mathematische Symbole</string> <string name="key_descr_switch_greekmath">Griechische &amp; mathematische Symbole</string>
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<string name="key_descr_voice_typing">Spracheingabe</string> <string name="key_descr_voice_typing">Spracheingabe</string>

View File

@@ -90,6 +90,7 @@ La misma no contiene ningún anuncio/publicidad, no realiza peticiones de red y
<string name="launcher_tryhere">Tras habilitarlo, puedes probar el teclado en este campo:</string> <string name="launcher_tryhere">Tras habilitarlo, puedes probar el teclado en este campo:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<string name="key_descr_capslock">Bloq Mayús</string> <string name="key_descr_capslock">Bloq Mayús</string>
<!-- <string name="key_descr_compose">Compose</string> -->
<string name="key_descr_switch_greekmath">Símb. griegos y matemáticos</string> <string name="key_descr_switch_greekmath">Símb. griegos y matemáticos</string>
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<string name="key_descr_voice_typing">Dictado por voz</string> <string name="key_descr_voice_typing">Dictado por voz</string>

View File

@@ -90,6 +90,7 @@ This application contains no ads, doesn't make any network requests and is Open
<string name="launcher_tryhere">بعد از فعالسازی، صفحه کلید را اینجا امتحان کنید:</string> <string name="launcher_tryhere">بعد از فعالسازی، صفحه کلید را اینجا امتحان کنید:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<!-- <string name="key_descr_capslock">Caps lock</string> --> <!-- <string name="key_descr_capslock">Caps lock</string> -->
<!-- <string name="key_descr_compose">Compose</string> -->
<!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> --> <!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> -->
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<!-- <string name="key_descr_voice_typing">Voice typing</string> --> <!-- <string name="key_descr_voice_typing">Voice typing</string> -->

View File

@@ -90,6 +90,7 @@ Cette application ne contient pas de publicité, n'accède pas au réseau et est
<string name="launcher_tryhere">Après l\'avoir activé, vous pouvez l\'essayer ici:</string> <string name="launcher_tryhere">Après l\'avoir activé, vous pouvez l\'essayer ici:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<string name="key_descr_capslock">Verrouillage majuscules</string> <string name="key_descr_capslock">Verrouillage majuscules</string>
<!-- <string name="key_descr_compose">Compose</string> -->
<string name="key_descr_switch_greekmath">Symboles mathématiques</string> <string name="key_descr_switch_greekmath">Symboles mathématiques</string>
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<string name="key_descr_voice_typing">Saisie vocale</string> <string name="key_descr_voice_typing">Saisie vocale</string>

View File

@@ -90,6 +90,7 @@ This application contains no ads, doesn't make any network requests and is Open
<!-- <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> --> <!-- <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> -->
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<!-- <string name="key_descr_capslock">Caps lock</string> --> <!-- <string name="key_descr_capslock">Caps lock</string> -->
<!-- <string name="key_descr_compose">Compose</string> -->
<!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> --> <!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> -->
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<!-- <string name="key_descr_voice_typing">Voice typing</string> --> <!-- <string name="key_descr_voice_typing">Voice typing</string> -->

View File

@@ -3,24 +3,12 @@
<string name="app_name_release">Unexpected Keyboard</string> <string name="app_name_release">Unexpected Keyboard</string>
<!-- <string name="app_name_debug">Unexpected Keyboard (Debug)</string> --> <!-- <string name="app_name_debug">Unexpected Keyboard (Debug)</string> -->
<string name="short_description">개발자들을 위한 가벼운 가상 키보드.</string> <string name="short_description">개발자들을 위한 가벼운 가상 키보드.</string>
<string name="store_description">"이 앱은 안드로이드용 가상 키보드입니다. 주요 기능은 스와이프 제스처를 사용하여 모든 ASCII 문자를 쉽게 입력할 수 있다는 점과 악센트 및 수정자 키를 위한 데드 키와 특수 키(tab, esc, 방향키 등)이 있다는 것입니다. <!-- <string name="store_description">"The main feature is that you can type more characters by swiping the keys towards the corners.
키보드는 각 키의 모서리에 최대 4개의 추가 문자를 표시합니다. 이러한 추가 문자는 키에서 손가락을 스와이프하여 적중됩니다. This application was originally designed for programmers using Termux.
Now perfect for everyday use.
일부 하이라이트 기능: This application contains no ads, doesn't make any network requests and is Open Source."</string> -->
- PC 키보드에서 사용할 수 있는 모든 문자 및 특수 키를 사요 가능합니다. 이것은 Termux와 같은 앱을 사용하는 데 효과적입니다.
- 여기에는 Tab, Esc, 방향키 및 function 키뿐만이 아닌 Ctrl 및 Alt 키도 포함되어 있습니다 !
- 악센트 키는 데드 키를 사용하여 액세스할 수 있습니다. 먼저 악센트 키를 활성화한 다음 악센트 문자를 입력합니다.
- 매우 가볍고 빠릅니다. Google 키보드보다 500배, 기본 키보드보다 15배 적은 공간을 사용합니다. 광고와 사용 기록 추적 없음.
- 다중 레이아웃: QWERTY, QWERTZ, AZERTY. 다양한 테마: White, Dark, OLED Black. 또한 다른 많은 옵션들.
다른 가상 키보드와 마찬가지로 시스템 설정에서 활성화해야 합니다. 시스템 설정을 열고 다음으로 이동합니다.
시스템 &gt; 언어 및 입력 &gt; 키보드 &gt; 키보드 관리."</string>
<string name="settings_activity_label">Unexpected Keyboard 설정</string> <string name="settings_activity_label">Unexpected Keyboard 설정</string>
<!-- <string name="pref_portrait">In portrait mode</string> --> <!-- <string name="pref_portrait">In portrait mode</string> -->
<!-- <string name="pref_landscape">In landscape mode</string> --> <!-- <string name="pref_landscape">In landscape mode</string> -->
@@ -102,6 +90,7 @@
<!-- <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> --> <!-- <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> -->
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<!-- <string name="key_descr_capslock">Caps lock</string> --> <!-- <string name="key_descr_capslock">Caps lock</string> -->
<!-- <string name="key_descr_compose">Compose</string> -->
<!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> --> <!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> -->
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<!-- <string name="key_descr_voice_typing">Voice typing</string> --> <!-- <string name="key_descr_voice_typing">Voice typing</string> -->

View File

@@ -92,6 +92,7 @@ Tagad lieliski piemērota izmantošanai ikdienā.
<string name="launcher_tryhere">Pēc iespējošanas šeit var izmēģināt tastatūru:</string> <string name="launcher_tryhere">Pēc iespējošanas šeit var izmēģināt tastatūru:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<string name="key_descr_capslock">Burtslēgs</string> <string name="key_descr_capslock">Burtslēgs</string>
<!-- <string name="key_descr_compose">Compose</string> -->
<string name="key_descr_switch_greekmath">Grieķu un matemātikas rakstzīmes</string> <string name="key_descr_switch_greekmath">Grieķu un matemātikas rakstzīmes</string>
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<string name="key_descr_voice_typing">Rakstīšana ar balsi</string> <string name="key_descr_voice_typing">Rakstīšana ar balsi</string>

View File

@@ -47,8 +47,8 @@ Aplikacja nie zawiera reklam, nie żąda dostępu do internetu, a jej kod źród
<string name="pref_autocapitalisation_summary">Naciśnij Shift na początku zdania</string> <string name="pref_autocapitalisation_summary">Naciśnij Shift na początku zdania</string>
<string name="pref_switch_input_immediate_title">Przełącz na ostatnio używaną klawiaturę</string> <string name="pref_switch_input_immediate_title">Przełącz na ostatnio używaną klawiaturę</string>
<string name="pref_switch_input_immediate_summary">Działanie klawisza przełączającego klawiaturę</string> <string name="pref_switch_input_immediate_summary">Działanie klawisza przełączającego klawiaturę</string>
<!-- <string name="pref_vibrate_custom">Custom vibration</string> --> <string name="pref_vibrate_custom">Własna wibracja</string>
<!-- <string name="pref_vibrate_duration_title">Vibration intensity</string> --> <string name="pref_vibrate_duration_title">Intensywność wibracji</string>
<string name="pref_pin_entry_enabled_title">Układ klawiatury PIN</string> <string name="pref_pin_entry_enabled_title">Układ klawiatury PIN</string>
<string name="pref_pin_entry_enabled_summary">Podczas wpisywania liczb, dat i numerów telefonu</string> <string name="pref_pin_entry_enabled_summary">Podczas wpisywania liczb, dat i numerów telefonu</string>
<string name="pref_category_style">Styl</string> <string name="pref_category_style">Styl</string>
@@ -74,9 +74,9 @@ Aplikacja nie zawiera reklam, nie żąda dostępu do internetu, a jej kod źród
<string name="pref_swipe_dist_e_very_far">Bardzo duża</string> <string name="pref_swipe_dist_e_very_far">Bardzo duża</string>
<string name="pref_key_horizontal_space">Odległość pomiędzy klawiszami w poziomie</string> <string name="pref_key_horizontal_space">Odległość pomiędzy klawiszami w poziomie</string>
<string name="pref_key_vertical_space">Odległość pomiędzy klawiszami w pionie</string> <string name="pref_key_vertical_space">Odległość pomiędzy klawiszami w pionie</string>
<!-- <string name="pref_border_config_title">Customize borders</string> --> <string name="pref_border_config_title">Dostosuj krawędzie</string>
<!-- <string name="pref_border_width_title">Border Width</string> --> <string name="pref_border_width_title">Grubość krawedzi</string>
<!-- <string name="pref_corners_radius_title">Corner radius</string> --> <string name="pref_corners_radius_title">Promień rogów</string>
<string name="key_action_next">Dalej</string> <string name="key_action_next">Dalej</string>
<string name="key_action_done">OK</string> <string name="key_action_done">OK</string>
<string name="key_action_go">Przejdź</string> <string name="key_action_go">Przejdź</string>
@@ -88,10 +88,11 @@ Aplikacja nie zawiera reklam, nie żąda dostępu do internetu, a jej kod źród
<string name="launcher_description">Ta aplikacja jest klawiaturą ekranową. Naciśnij poniższy przycisk, aby przejść do ustawień systemu i włącz Unexpected-Keyboard.</string> <string name="launcher_description">Ta aplikacja jest klawiaturą ekranową. Naciśnij poniższy przycisk, aby przejść do ustawień systemu i włącz Unexpected-Keyboard.</string>
<string name="launcher_sourcecode">Jest to darmowa aplikacja o otwartym kodzie źródłowym. Możesz zobaczyć kod źródłowy oraz zgłosić błedy na Githubie.</string> <string name="launcher_sourcecode">Jest to darmowa aplikacja o otwartym kodzie źródłowym. Możesz zobaczyć kod źródłowy oraz zgłosić błedy na Githubie.</string>
<string name="launcher_tryhere">Po jej włączeniu, możesz wypróbować klawiaturę tutaj:</string> <string name="launcher_tryhere">Po jej włączeniu, możesz wypróbować klawiaturę tutaj:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <string name="launcher_tryhere_hint">Wypróbuj tutaj</string>
<string name="key_descr_capslock">Caps lock</string> <string name="key_descr_capslock">Caps lock</string>
<string name="key_descr_compose">Komponuj</string>
<string name="key_descr_switch_greekmath">Symbole greckie i matematyczne</string> <string name="key_descr_switch_greekmath">Symbole greckie i matematyczne</string>
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <string name="key_descr_change_method">Przełącz klawiaturę</string>
<string name="key_descr_voice_typing">Pisanie głosowe</string> <string name="key_descr_voice_typing">Pisanie głosowe</string>
<string name="key_descr_copy">Kopiuj</string> <string name="key_descr_copy">Kopiuj</string>
<string name="key_descr_paste">Wklej</string> <string name="key_descr_paste">Wklej</string>

View File

@@ -3,24 +3,12 @@
<string name="app_name_release">Teclado Unexpected</string> <string name="app_name_release">Teclado Unexpected</string>
<string name="app_name_debug">Teclado Unexpected</string> <string name="app_name_debug">Teclado Unexpected</string>
<string name="short_description">Um teclado virtual leve para desenvolvedores.</string> <string name="short_description">Um teclado virtual leve para desenvolvedores.</string>
<string name="store_description">"Um teclado virtual para aparelhos Android. Os recursos principais são facilidade de digitar todos os caracteres ASCII deslizando o dedo, teclas "mortas" para acentos e teclas modificadoras e a presença de teclas especiais (tab, esc, setas, etc..). <!-- <string name="store_description">"The main feature is that you can type more characters by swiping the keys towards the corners.
O teclado mostra até 4 caracteres extras nos cantos de cada tecla. Esses caracteres são digitáveis com o deslizar do dedo na tecla. This application was originally designed for programmers using Termux.
Now perfect for everyday use.
Alguns dos recursos interessantes: This application contains no ads, doesn't make any network requests and is Open Source."</string> -->
- Todos caracteres e teclas especiais que também estão disponíveis num teclado de PC. Perfeito para usar com aplicativos como Termux.
- Incluindo Tab, Esc, as setas e teclas de função, e também Ctrl e Alt!
- Caracteres acentuados são digitáveis usando teclas "mortas". Primeiro ative o acento e depois digite a letra.
- Muito leve e rápido. Ocupa 500x menos espaço que o teclado da Google e 15x menos espaço que o teclado padrão. Sem propaganda, sem rastreio.
- Vários layouts: QWERTY, QWERTZ, AZERTY. Temas: Branco, Escuro, Preto OLED. E muitas outras opções.
Como qualquer outro teclado virtual, tem de ser ativado nas configurações de sistema. Abra as configurações e vá para:
Sistema &gt; Idioma e entrada &gt; Teclado virtual &gt; Gerenciar teclados virtuais."</string>
<string name="settings_activity_label">Configurações</string> <string name="settings_activity_label">Configurações</string>
<string name="pref_portrait">No modo retrato</string> <string name="pref_portrait">No modo retrato</string>
<string name="pref_landscape">No modo paisagem</string> <string name="pref_landscape">No modo paisagem</string>
@@ -102,6 +90,7 @@ Sistema &gt; Idioma e entrada &gt; Teclado virtual &gt; Gerenciar teclados virtu
<string name="launcher_tryhere">Após ativar, experimente aqui:</string> <string name="launcher_tryhere">Após ativar, experimente aqui:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<string name="key_descr_capslock">Caps lock</string> <string name="key_descr_capslock">Caps lock</string>
<!-- <string name="key_descr_compose">Compose</string> -->
<string name="key_descr_switch_greekmath">Grego &amp; símbolos matemáticos</string> <string name="key_descr_switch_greekmath">Grego &amp; símbolos matemáticos</string>
<string name="key_descr_change_method">Trocar de teclado</string> <string name="key_descr_change_method">Trocar de teclado</string>
<string name="key_descr_voice_typing">Digitação por voz</string> <string name="key_descr_voice_typing">Digitação por voz</string>

View File

@@ -90,6 +90,7 @@ Această aplicație nu conține publicitate, nu folosește rețeaua deloc și e
<string name="launcher_tryhere">După activare, puteți să încercați tastatura aici:</string> <string name="launcher_tryhere">După activare, puteți să încercați tastatura aici:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<!-- <string name="key_descr_capslock">Caps lock</string> --> <!-- <string name="key_descr_capslock">Caps lock</string> -->
<!-- <string name="key_descr_compose">Compose</string> -->
<!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> --> <!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> -->
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<!-- <string name="key_descr_voice_typing">Voice typing</string> --> <!-- <string name="key_descr_voice_typing">Voice typing</string> -->

View File

@@ -90,6 +90,7 @@
<string name="launcher_tryhere">После активации вы можете попробовать клавиатуру прямо здесь:</string> <string name="launcher_tryhere">После активации вы можете попробовать клавиатуру прямо здесь:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<!-- <string name="key_descr_capslock">Caps lock</string> --> <!-- <string name="key_descr_capslock">Caps lock</string> -->
<!-- <string name="key_descr_compose">Compose</string> -->
<!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> --> <!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> -->
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<!-- <string name="key_descr_voice_typing">Voice typing</string> --> <!-- <string name="key_descr_voice_typing">Voice typing</string> -->

View File

@@ -90,6 +90,7 @@ Bu uygulama açık kaynaklıdır. Reklam içermez ve internete bağlanmaz."</str
<string name="launcher_tryhere">Ayarlardan aktif ettikten sonra klavyeyi burada test edebilirsin:</string> <string name="launcher_tryhere">Ayarlardan aktif ettikten sonra klavyeyi burada test edebilirsin:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<string name="key_descr_capslock">CapsLock</string> <string name="key_descr_capslock">CapsLock</string>
<!-- <string name="key_descr_compose">Compose</string> -->
<string name="key_descr_switch_greekmath">Greek &amp; math sembolleri</string> <string name="key_descr_switch_greekmath">Greek &amp; math sembolleri</string>
<string name="key_descr_change_method">Klavye değiştir</string> <string name="key_descr_change_method">Klavye değiştir</string>
<string name="key_descr_voice_typing">Sesle yazma</string> <string name="key_descr_voice_typing">Sesle yazma</string>

View File

@@ -2,13 +2,13 @@
<resources> <resources>
<string name="app_name_release">Unexpected Keyboard</string> <string name="app_name_release">Unexpected Keyboard</string>
<string name="app_name_debug">Unexpected Keyboard (Налагодження)</string> <string name="app_name_debug">Unexpected Keyboard (Налагодження)</string>
<!-- <string name="short_description">Lightweight and privacy-conscious virtual keyboard for Android.</string> --> <string name="short_description">Легка та конфіденційна віртуальна клавіатура для Android.</string>
<!-- <string name="store_description">"The main feature is that you can type more characters by swiping the keys towards the corners. <string name="store_description">"Головна особливість полягає в тому, що ви можете вводити більше символів, проводячи клавіші до кутів.
This application was originally designed for programmers using Termux. Ця програма спочатку була розроблена для програмістів, які використовують Termux.
Now perfect for everyday use. Тепер ідеально підходить для щоденного використання.
This application contains no ads, doesn't make any network requests and is Open Source."</string> --> Ця програма не містить реклами, не надсилає жодних мережевих запитів і має відкритий код."</string>
<string name="settings_activity_label">Unexpected Keyboard Налаштування</string> <string name="settings_activity_label">Unexpected Keyboard Налаштування</string>
<string name="pref_portrait">У портретному режимі</string> <string name="pref_portrait">У портретному режимі</string>
<string name="pref_landscape">У альбомному режимі</string> <string name="pref_landscape">У альбомному режимі</string>
@@ -74,9 +74,9 @@ This application contains no ads, doesn't make any network requests and is Open
<string name="pref_swipe_dist_e_very_far">Дуже далека</string> <string name="pref_swipe_dist_e_very_far">Дуже далека</string>
<string name="pref_key_horizontal_space">Горизонтальна відстань між клавішами</string> <string name="pref_key_horizontal_space">Горизонтальна відстань між клавішами</string>
<string name="pref_key_vertical_space">Вертикальна відстань між клавішами</string> <string name="pref_key_vertical_space">Вертикальна відстань між клавішами</string>
<!-- <string name="pref_border_config_title">Customize borders</string> --> <string name="pref_border_config_title">Налаштувати межі</string>
<!-- <string name="pref_border_width_title">Border Width</string> --> <string name="pref_border_width_title">Ширина межі</string>
<!-- <string name="pref_corners_radius_title">Corner radius</string> --> <string name="pref_corners_radius_title">Радіус кута</string>
<string name="key_action_next">Далі</string> <string name="key_action_next">Далі</string>
<string name="key_action_done">Готово</string> <string name="key_action_done">Готово</string>
<string name="key_action_go">Іти</string> <string name="key_action_go">Іти</string>
@@ -88,8 +88,9 @@ This application contains no ads, doesn't make any network requests and is Open
<string name="launcher_description">Ця програма є віртуальною клавіатурою. Перейдіть до системних налаштувань, натиснувши кнопку нижче, і ввімкніть Unexpected-Keyboard.</string> <string name="launcher_description">Ця програма є віртуальною клавіатурою. Перейдіть до системних налаштувань, натиснувши кнопку нижче, і ввімкніть Unexpected-Keyboard.</string>
<string name="launcher_sourcecode">Це безкоштовна програма з відкритим кодом. Ви можете знайти початковий код або повідомити про помилки на GitHub.</string> <string name="launcher_sourcecode">Це безкоштовна програма з відкритим кодом. Ви можете знайти початковий код або повідомити про помилки на GitHub.</string>
<string name="launcher_tryhere">Після ввімкнення ви можете спробувати клавіатуру тут:</string> <string name="launcher_tryhere">Після ввімкнення ви можете спробувати клавіатуру тут:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <string name="launcher_tryhere_hint">Спробуйте тут</string>
<string name="key_descr_capslock">Caps lock</string> <string name="key_descr_capslock">Caps lock</string>
<string name="key_descr_compose">Compose</string>
<string name="key_descr_switch_greekmath">Грецькі та математичні символи</string> <string name="key_descr_switch_greekmath">Грецькі та математичні символи</string>
<string name="key_descr_change_method">Переключити клавіатуру</string> <string name="key_descr_change_method">Переключити клавіатуру</string>
<string name="key_descr_voice_typing">Голосове введення</string> <string name="key_descr_voice_typing">Голосове введення</string>

View File

@@ -90,6 +90,7 @@ Bây giờ đã hoàn hảo cho việc sử dụng hàng ngày.
<!-- <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> --> <!-- <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> -->
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<!-- <string name="key_descr_capslock">Caps lock</string> --> <!-- <string name="key_descr_capslock">Caps lock</string> -->
<!-- <string name="key_descr_compose">Compose</string> -->
<!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> --> <!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> -->
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<!-- <string name="key_descr_voice_typing">Voice typing</string> --> <!-- <string name="key_descr_voice_typing">Voice typing</string> -->

View File

@@ -90,6 +90,7 @@ This application contains no ads, doesn't make any network requests and is Open
<string name="launcher_tryhere">启用键盘后,可以在这里测试效果:</string> <string name="launcher_tryhere">启用键盘后,可以在这里测试效果:</string>
<!-- <string name="launcher_tryhere_hint">Try here</string> --> <!-- <string name="launcher_tryhere_hint">Try here</string> -->
<!-- <string name="key_descr_capslock">Caps lock</string> --> <!-- <string name="key_descr_capslock">Caps lock</string> -->
<!-- <string name="key_descr_compose">Compose</string> -->
<!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> --> <!-- <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> -->
<!-- <string name="key_descr_change_method">Switch keyboard</string> --> <!-- <string name="key_descr_change_method">Switch keyboard</string> -->
<!-- <string name="key_descr_voice_typing">Voice typing</string> --> <!-- <string name="key_descr_voice_typing">Voice typing</string> -->

View File

@@ -22,6 +22,7 @@
<item>deva_alt</item> <item>deva_alt</item>
<item>deva_inscript</item> <item>deva_inscript</item>
<item>grek_qwerty</item> <item>grek_qwerty</item>
<item>guj_phonetic_in</item>
<item>hang_dubeolsik_kr</item> <item>hang_dubeolsik_kr</item>
<item>hebr_1_il</item> <item>hebr_1_il</item>
<item>hebr_2_il</item> <item>hebr_2_il</item>
@@ -39,6 +40,7 @@
<item>latn_qwerty_pl</item> <item>latn_qwerty_pl</item>
<item>latn_qwerty_ro</item> <item>latn_qwerty_ro</item>
<item>latn_qwerty_se</item> <item>latn_qwerty_se</item>
<item>latn_qwerty_sk</item>
<item>latn_qwerty_tly</item> <item>latn_qwerty_tly</item>
<item>latn_qwerty_tr</item> <item>latn_qwerty_tr</item>
<item>latn_qwerty_vi</item> <item>latn_qwerty_vi</item>
@@ -73,6 +75,7 @@
<item>देवनागरी (हिंदी)-2</item> <item>देवनागरी (हिंदी)-2</item>
<item>देवनागरी (हिंदी)-1</item> <item>देवनागरी (हिंदी)-1</item>
<item>QWERTY (Greek)</item> <item>QWERTY (Greek)</item>
<item>ગુજરાતી ફોનેટિક - Gujarati Phonetic</item>
<item>두벌식 (Korean)</item> <item>두벌식 (Korean)</item>
<item>Hebrew 1</item> <item>Hebrew 1</item>
<item>Hebrew 2</item> <item>Hebrew 2</item>
@@ -90,6 +93,7 @@
<item>QWERTY (Polski)</item> <item>QWERTY (Polski)</item>
<item>QWERTY (Română)</item> <item>QWERTY (Română)</item>
<item>QWERTY (Swedish)</item> <item>QWERTY (Swedish)</item>
<item>QWERTY (Slovak)</item>
<item>QWERTY (Talysh New Latin)</item> <item>QWERTY (Talysh New Latin)</item>
<item>QWERTY (Türkçe)</item> <item>QWERTY (Türkçe)</item>
<item>QWERTY (Vietnamese)</item> <item>QWERTY (Vietnamese)</item>
@@ -124,6 +128,7 @@
<item>@xml/deva_alt</item> <item>@xml/deva_alt</item>
<item>@xml/deva_inscript</item> <item>@xml/deva_inscript</item>
<item>@xml/grek_qwerty</item> <item>@xml/grek_qwerty</item>
<item>@xml/guj_phonetic_in</item>
<item>@xml/hang_dubeolsik_kr</item> <item>@xml/hang_dubeolsik_kr</item>
<item>@xml/hebr_1_il</item> <item>@xml/hebr_1_il</item>
<item>@xml/hebr_2_il</item> <item>@xml/hebr_2_il</item>
@@ -141,6 +146,7 @@
<item>@xml/latn_qwerty_pl</item> <item>@xml/latn_qwerty_pl</item>
<item>@xml/latn_qwerty_ro</item> <item>@xml/latn_qwerty_ro</item>
<item>@xml/latn_qwerty_se</item> <item>@xml/latn_qwerty_se</item>
<item>@xml/latn_qwerty_sk</item>
<item>@xml/latn_qwerty_tly</item> <item>@xml/latn_qwerty_tly</item>
<item>@xml/latn_qwerty_tr</item> <item>@xml/latn_qwerty_tr</item>
<item>@xml/latn_qwerty_vi</item> <item>@xml/latn_qwerty_vi</item>

View File

@@ -90,6 +90,7 @@ This application contains no ads, doesn't make any network requests and is Open
<string name="launcher_tryhere">After enabling, you can try the keyboard here:</string> <string name="launcher_tryhere">After enabling, you can try the keyboard here:</string>
<string name="launcher_tryhere_hint">Try here</string> <string name="launcher_tryhere_hint">Try here</string>
<string name="key_descr_capslock">Caps lock</string> <string name="key_descr_capslock">Caps lock</string>
<string name="key_descr_compose">Compose</string>
<string name="key_descr_switch_greekmath">Greek &amp; math symbols</string> <string name="key_descr_switch_greekmath">Greek &amp; math symbols</string>
<string name="key_descr_change_method">Switch keyboard</string> <string name="key_descr_change_method">Switch keyboard</string>
<string name="key_descr_voice_typing">Voice typing</string> <string name="key_descr_voice_typing">Voice typing</string>

View File

@@ -11,7 +11,8 @@
<attr name="colorLabel" format="color"/> <attr name="colorLabel" format="color"/>
<attr name="colorLabelActivated" format="color"/> <attr name="colorLabelActivated" format="color"/>
<attr name="colorLabelLocked" format="color"/> <attr name="colorLabelLocked" format="color"/>
<attr name="secondaryLightOffset" format="float"/> <attr name="secondaryDimming" format="float"/>
<attr name="greyedDimming" format="float"/>
<!-- Corner labels --> <!-- Corner labels -->
<attr name="colorSubLabel" format="color"/> <attr name="colorSubLabel" format="color"/>
<!-- Borders --> <!-- Borders -->
@@ -38,6 +39,8 @@
<item name="keyBorderRadius">5dp</item> <item name="keyBorderRadius">5dp</item>
<item name="keyBorderWidth">0dp</item> <item name="keyBorderWidth">0dp</item>
<item name="keyBorderWidthActivated">0dp</item> <item name="keyBorderWidthActivated">0dp</item>
<item name="secondaryDimming">0.25</item>
<item name="greyedDimming">0.5</item>
<item name="emoji_key_bg" type="color">?attr/emoji_button_bg</item> <item name="emoji_key_bg" type="color">?attr/emoji_button_bg</item>
<item name="emoji_key_text" type="color">?attr/colorLabel</item> <item name="emoji_key_text" type="color">?attr/colorLabel</item>
</style> </style>
@@ -53,7 +56,6 @@
<item name="colorLabelActivated">#3399ff</item> <item name="colorLabelActivated">#3399ff</item>
<item name="colorLabelLocked">#33cc33</item> <item name="colorLabelLocked">#33cc33</item>
<item name="colorSubLabel">#cccccc</item> <item name="colorSubLabel">#cccccc</item>
<item name="secondaryLightOffset">-0.2</item>
<item name="emoji_button_bg" type="color">#202020</item> <item name="emoji_button_bg" type="color">#202020</item>
<item name="emoji_color" type="color">#ffffff</item> <item name="emoji_color" type="color">#ffffff</item>
</style> </style>
@@ -72,7 +74,6 @@
<item name="colorLabelActivated">#0066cc</item> <item name="colorLabelActivated">#0066cc</item>
<item name="colorLabelLocked">#33cc33</item> <item name="colorLabelLocked">#33cc33</item>
<item name="colorSubLabel">#333333</item> <item name="colorSubLabel">#333333</item>
<item name="secondaryLightOffset">+0.3</item>
<item name="emoji_button_bg">#dedede</item> <item name="emoji_button_bg">#dedede</item>
<item name="emoji_color">#000000</item> <item name="emoji_color">#000000</item>
</style> </style>
@@ -91,7 +92,6 @@
<item name="colorLabelActivated">#009dff</item> <item name="colorLabelActivated">#009dff</item>
<item name="colorLabelLocked">#00ff26</item> <item name="colorLabelLocked">#00ff26</item>
<item name="colorSubLabel">#bbbbbb</item> <item name="colorSubLabel">#bbbbbb</item>
<item name="secondaryLightOffset">-0.25</item>
<item name="keyBorderRadius">1dp</item> <item name="keyBorderRadius">1dp</item>
<item name="emoji_button_bg">#000000</item> <item name="emoji_button_bg">#000000</item>
<item name="emoji_color">#ffffff</item> <item name="emoji_color">#ffffff</item>
@@ -114,7 +114,6 @@
<item name="colorLabelActivated">#0066cc</item> <item name="colorLabelActivated">#0066cc</item>
<item name="colorLabelLocked">#33cc33</item> <item name="colorLabelLocked">#33cc33</item>
<item name="colorSubLabel">#333333</item> <item name="colorSubLabel">#333333</item>
<item name="secondaryLightOffset">+0.35</item>
<item name="emoji_button_bg">#ffffff</item> <item name="emoji_button_bg">#ffffff</item>
<item name="emoji_color">#000000</item> <item name="emoji_color">#000000</item>
</style> </style>
@@ -133,7 +132,6 @@
<item name="colorLabelActivated">#000000</item> <item name="colorLabelActivated">#000000</item>
<item name="colorLabelLocked">#33cc33</item> <item name="colorLabelLocked">#33cc33</item>
<item name="colorSubLabel">#333333</item> <item name="colorSubLabel">#333333</item>
<item name="secondaryLightOffset">+0.35</item>
<item name="emoji_button_bg">#ffffff</item> <item name="emoji_button_bg">#ffffff</item>
<item name="emoji_color">#000000</item> <item name="emoji_color">#000000</item>
</style> </style>
@@ -145,7 +143,6 @@
<item name="colorLabel">#000000</item> <item name="colorLabel">#000000</item>
<item name="colorLabelActivated">#ffffff</item> <item name="colorLabelActivated">#ffffff</item>
<item name="colorLabelLocked">#e65100</item> <item name="colorLabelLocked">#e65100</item>
<item name="secondaryLightOffset">0.1</item>
<item name="colorSubLabel">#333333</item> <item name="colorSubLabel">#333333</item>
<item name="keyBorderWidth">0.0dip</item> <item name="keyBorderWidth">0.0dip</item>
<item name="keyBorderWidthActivated">0.0dip</item> <item name="keyBorderWidthActivated">0.0dip</item>
@@ -164,7 +161,6 @@
<item name="colorLabel">#000000</item> <item name="colorLabel">#000000</item>
<item name="colorLabelActivated">#ffffff</item> <item name="colorLabelActivated">#ffffff</item>
<item name="colorLabelLocked">#64ffda</item> <item name="colorLabelLocked">#64ffda</item>
<item name="secondaryLightOffset">0.0</item>
<item name="colorSubLabel">#004d40</item> <item name="colorSubLabel">#004d40</item>
<item name="keyBorderWidth">0.0dip</item> <item name="keyBorderWidth">0.0dip</item>
<item name="keyBorderWidthActivated">0.0dip</item> <item name="keyBorderWidthActivated">0.0dip</item>

View File

@@ -3,6 +3,6 @@
<key width="1.7" key0="ctrl" key1="loc switch_greekmath" key2="loc meta" key4="switch_numeric"/> <key width="1.7" key0="ctrl" key1="loc switch_greekmath" key2="loc meta" key4="switch_numeric"/>
<key width="1.1" key0="fn" key1="loc alt" key2="loc change_method" key3="switch_emoji" key4="config"/> <key width="1.1" key0="fn" key1="loc alt" key2="loc change_method" key3="switch_emoji" key4="config"/>
<key width="4.4" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right" slider="true"/> <key width="4.4" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right" slider="true"/>
<key width="1.1" key7="up" key6="right" key5="left" key8="down" key1="loc home" key2="loc page_up" key3="loc end" key4="loc page_down"/> <key width="1.1" key0="loc compose" key7="up" key6="right" key5="left" key8="down" key1="loc home" key2="loc page_up" key3="loc end" key4="loc page_down"/>
<key width="1.7" key0="enter" key1="loc voice_typing" key2="action"/> <key width="1.7" key0="enter" key1="loc voice_typing" key2="action"/>
</row> </row>

View File

@@ -41,7 +41,7 @@
<key width="1.2" key0="fn"/> <key width="1.2" key0="fn"/>
<key width="2.8" key0="space" key1="superscript" key3="subscript"/> <key width="2.8" key0="space" key1="superscript" key3="subscript"/>
<key width="1.2" key0="0" key2="."/> <key width="1.2" key0="0" key2="."/>
<key width="1.2" key7="up" key6="right" key5="left" key8="down"/> <key width="1.2" key0="loc compose" key7="up" key6="right" key5="left" key8="down"/>
<key width="1.2" key0="enter" key1="=" key2="action"/> <key width="1.2" key0="enter" key1="=" key2="action"/>
</row> </row>
</keyboard> </keyboard>

View File

@@ -9,12 +9,14 @@
<subtype android:label="%s" android:languageTag="cs" android:imeSubtypeLocale="cs_CZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_cz,extra_keys=accent_aigu:á:é:í:ó:ú:ý|accent_ring:ů|accent_caron:č:ě:ň:ř:š:ž:ď:ť"/> <subtype android:label="%s" android:languageTag="cs" android:imeSubtypeLocale="cs_CZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_cz,extra_keys=accent_aigu:á:é:í:ó:ú:ý|accent_ring:ů|accent_caron:č:ě:ň:ř:š:ž:ď:ť"/>
<subtype android:label="%s" android:languageTag="de" android:imeSubtypeLocale="de_DE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_de,extra_keys=accent_trema:ä:ö:ü|ß|€"/> <subtype android:label="%s" android:languageTag="de" android:imeSubtypeLocale="de_DE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_de,extra_keys=accent_trema:ä:ö:ü|ß|€"/>
<subtype android:label="%s" android:languageTag="el" android:imeSubtypeLocale="el" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=grek_qwerty,extra_keys=£|€"/> <subtype android:label="%s" android:languageTag="el" android:imeSubtypeLocale="el" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=grek_qwerty,extra_keys=£|€"/>
<subtype android:label="%s" android:languageTag="en-GB" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_gb,extra_keys=£|€"/> <subtype android:label="%s" android:languageTag="en-CA" android:imeSubtypeLocale="en_CA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/>
<subtype android:label="%s" android:languageTag="en-GB" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_gb,extra_keys=£"/>
<subtype android:label="%s" android:languageTag="en-US" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/> <subtype android:label="%s" android:languageTag="en-US" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/>
<subtype android:label="%s" android:languageTag="es" android:imeSubtypeLocale="es_ES" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_es,extra_keys=accent_aigu:á:é:í:ó:ú|accent_tilde:ñ|accent_grave|accent_trema|€"/> <subtype android:label="%s" android:languageTag="es" android:imeSubtypeLocale="es_ES" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_es,extra_keys=accent_aigu:á:é:í:ó:ú|accent_tilde:ñ|accent_grave|accent_trema|€"/>
<subtype android:label="%s" android:languageTag="fa" android:imeSubtypeLocale="fa_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=arab_pc_ir"/> <subtype android:label="%s" android:languageTag="fa" android:imeSubtypeLocale="fa_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=arab_pc_ir"/>
<subtype android:label="%s" android:languageTag="fr-CA" android:imeSubtypeLocale="fr_CA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_fr,extra_keys=accent_grave:à:è:ù|accent_aigu:é|accent_circonflexe:â:ê:ô|accent_cedille:ç|accent_trema:ë:ï:ü:ÿ"/>
<subtype android:label="%s" android:languageTag="fr-CH" android:imeSubtypeLocale="fr_CH" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_fr_ch,extra_keys=accent_grave:à:è:ù|accent_aigu:é|accent_circonflexe:â:ê:ô|accent_cedille:ç|accent_trema:ë:ï:ü:ÿ|€"/> <subtype android:label="%s" android:languageTag="fr-CH" android:imeSubtypeLocale="fr_CH" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_fr_ch,extra_keys=accent_grave:à:è:ù|accent_aigu:é|accent_circonflexe:â:ê:ô|accent_cedille:ç|accent_trema:ë:ï:ü:ÿ|€"/>
<subtype android:label="%s" android:languageTag="fr-FR" android:imeSubtypeLocale="fr_FR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_fr,extra_keys=accent_grave:à:è@a|accent_aigu:é@e|accent_circonflexe:â:ê:ô@e|accent_cedille:ç@c|accent_trema:ë:ï:ü:ÿ@l|€"/> <subtype android:label="%s" android:languageTag="fr-FR" android:imeSubtypeLocale="fr_FR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_azerty_fr,extra_keys=accent_grave:à:è@a|accent_aigu:é@e|accent_circonflexe:â:ê:ô@e|accent_cedille:ç@c|accent_trema:ë:ï:ü@l|€"/>
<subtype android:label="%s" android:languageTag="he" android:imeSubtypeLocale="he_IL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=hebrew,default_layout=hebr_1_il,extra_keys=₪|€"/> <subtype android:label="%s" android:languageTag="he" android:imeSubtypeLocale="he_IL" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=hebrew,default_layout=hebr_1_il,extra_keys=₪|€"/>
<subtype android:label="%s" android:languageTag="hi" android:imeSubtypeLocale="hi_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,extra_keys=₹"/> <subtype android:label="%s" android:languageTag="hi" android:imeSubtypeLocale="hi_IN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=devanagari,default_layout=deva_inscript,extra_keys=₹"/>
<subtype android:label="%s" android:languageTag="hu" android:imeSubtypeLocale="hu_HU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_hu,extra_keys=accent_aigu:á:é:í:ó:ú|accent_trema:ö:ü|accent_ogonek|accent_double_aigu:ő:ű|€"/> <subtype android:label="%s" android:languageTag="hu" android:imeSubtypeLocale="hu_HU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_hu,extra_keys=accent_aigu:á:é:í:ó:ú|accent_trema:ö:ü|accent_ogonek|accent_double_aigu:ő:ű|€"/>
@@ -33,9 +35,9 @@
<subtype android:label="%s" android:languageTag="ru" android:imeSubtypeLocale="ru_RU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_jcuken_ru"/> <subtype android:label="%s" android:languageTag="ru" android:imeSubtypeLocale="ru_RU" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=cyrl_jcuken_ru"/>
<subtype android:label="%s" android:languageTag="sk" android:imeSubtypeLocale="sk_SK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_sk,extra_keys=accent_caron:ě:ř:ž:š:č:ň:ď:ľ:ť|accent_ring:ů|accent_circonflexe:ô|accent_trema:ä:ü:ö|accent_aigu:á:é:í:ó:ú:ŕ:ś:ĺ:ý"/> <subtype android:label="%s" android:languageTag="sk" android:imeSubtypeLocale="sk_SK" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwertz_sk,extra_keys=accent_caron:ě:ř:ž:š:č:ň:ď:ľ:ť|accent_ring:ů|accent_circonflexe:ô|accent_trema:ä:ü:ö|accent_aigu:á:é:í:ó:ú:ŕ:ś:ĺ:ý"/>
<subtype android:label="%s" android:languageTag="sv" android:imeSubtypeLocale="sv_SE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_se,extra_keys=accent_aigu:á|accent_trema:ä:ö|accent_ring:å|€"/> <subtype android:label="%s" android:languageTag="sv" android:imeSubtypeLocale="sv_SE" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_se,extra_keys=accent_aigu:á|accent_trema:ä:ö|accent_ring:å|€"/>
<subtype android:label="%s" android:languageTag="tr" android:imeSubtypeLocale="tr_TR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tr,extra_keys=accent_cedille:ç:ş|accent_trema:ö:ü|accent_circonflexe:â:î:û|₺|ı|ğ"/>
<subtype android:label="%s" android:languageTag="tly" android:imeSubtypeLocale="tly_AZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tly,extra_keys=á|ú|â|ê|ı|š|ž"/> <subtype android:label="%s" android:languageTag="tly" android:imeSubtypeLocale="tly_AZ" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tly,extra_keys=á|ú|â|ê|ı|š|ž"/>
<subtype android:label="%s" android:languageTag="tly" android:imeSubtypeLocale="tly_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=arab_hamvaj_tly"/> <subtype android:label="%s" android:languageTag="tly" android:imeSubtypeLocale="tly_IR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="default_layout=arab_hamvaj_tly"/>
<subtype android:label="%s" android:languageTag="tr" android:imeSubtypeLocale="tr_TR" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_tr,extra_keys=accent_cedille:ç:ş|accent_trema:ö:ü|accent_circonflexe:â:î:û|₺|ı|ğ"/>
<subtype android:label="%s" android:languageTag="uk" android:imeSubtypeLocale="uk_UA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_jcuken_uk,extra_keys=ґ|є|і|ї|₴"/> <subtype android:label="%s" android:languageTag="uk" android:imeSubtypeLocale="uk_UA" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=cyrillic,default_layout=cyrl_jcuken_uk,extra_keys=ґ|є|і|ї|₴"/>
<subtype android:label="%s" android:languageTag="vi" android:imeSubtypeLocale="vi_VN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_vi"/> <subtype android:label="%s" android:languageTag="vi" android:imeSubtypeLocale="vi_VN" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_vi"/>
</input-method> </input-method>

View File

@@ -30,7 +30,7 @@
<key width="1.5" key0="switch_text" key2="ctrl"/> <key width="1.5" key0="switch_text" key2="ctrl"/>
<key width="1.5" key0="0" key3="f11_placeholder" key4="f12_placeholder"/> <key width="1.5" key0="0" key3="f11_placeholder" key4="f12_placeholder"/>
<key width="0.75" key0="." key1=":" key2="," key3=";"/> <key width="0.75" key0="." key1=":" key2="," key3=";"/>
<key width="0.75" key0="space" key1="&quot;" key2="'" key4="_"/> <key width="0.75" key0="space" key1="&quot;" key2="'" key3="loc compose" key4="_"/>
<key width="1.5" key0="enter" key1="±" key2="action" key3="="/> <key width="1.5" key0="enter" key1="±" key2="action" key3="="/>
</row> </row>
</keyboard> </keyboard>

8
srcs/compose/README.md Normal file
View File

@@ -0,0 +1,8 @@
# Compose sequences
The `compose.py` program parses the compose sequences found in this directory
and generates `srcs/juloo.keyboard2/ComposeKeyData.java`.
## `en_US_UTF_8_Compose.pre`
This file is copied from the `xorg` project. Copyright applies.

217
srcs/compose/compile.py Normal file
View File

@@ -0,0 +1,217 @@
import textwrap, sys, re, string, json
# Names not defined in Compose.pre
xkb_char_extra_names = {
"space": " ",
"minus": "-",
"asterisk": "*",
"colon": ":",
"equal": "=",
"exclam": "!",
"grave": "`",
"parenleft": "(",
"parenright": ")",
"percent": "%",
"period": ".",
"plus": "+",
"question": "?",
"semicolon": ";",
"underscore": "_",
}
dropped_sequences = 0
# Parse XKB's Compose.pre files
def parse_sequences_file_xkb(fname):
# Parse a line of the form:
# <Multi_key> <minus> <space> : "~" asciitilde # TILDE
# Sequences not starting with <Multi_key> are ignored.
line_re = re.compile(r'^((?:\s*<[^>]+>)+)\s*:\s*"((?:[^"\\]+|\\.)+)"\s*(\S+)?\s*(?:#.+)?$')
char_re = re.compile(r'\s*<(?:U([a-fA-F0-9]{4,6})|([^>]+))>')
def parse_seq_line(line):
global dropped_sequences
prefix = "<Multi_key>"
if not line.startswith(prefix):
return None
m = re.match(line_re, line[len(prefix):])
if m == None:
return None
def_ = m.group(1)
try:
def_ = parse_seq_chars(def_)
result = parse_seq_result(m.group(2))
except Exception as e:
# print(str(e) + ". Sequence dropped: " + line.strip(), file=sys.stderr)
dropped_sequences += 1
return None
return def_, result
char_names = { **xkb_char_extra_names }
# Interpret character names of the form "U0000" or using [char_names].
def parse_seq_char(c):
uchar, named_char = c
if uchar != "":
return chr(int(uchar, 16))
# else is a named char
if len(named_char) == 1:
return named_char
if not named_char in char_names:
raise Exception("Unknown char: " + named_char)
return char_names[named_char]
# Interpret the left hand side of a sequence.
def parse_seq_chars(def_):
return list(map(parse_seq_char, re.findall(char_re, def_)))
# Interpret the result of a sequence, as outputed by [line_re].
def parse_seq_result(r):
if len(r) == 2 and r[0] == '\\':
return r[1]
# The state machine can't represent characters that do not fit in a
# 16-bit char. This breaks some sequences that output letters with
# combined diacritics or emojis.
if len(r) > 1 or ord(r[0]) > 65535:
raise Exception("Char out of range: " + r)
return r
# Populate [char_names] with the information present in the file.
with open(fname, "r") as inp:
for line in inp:
m = re.match(line_re, line)
if m == None or m.group(3) == None:
continue
try:
char_names[m.group(3)] = parse_seq_result(m.group(2))
except Exception:
pass
# Parse the sequences
with open(fname, "r") as inp:
seqs = []
for line in inp:
s = parse_seq_line(line)
if s != None:
seqs.append(s)
return seqs
# Parse from a json file containing a dictionary sequence → result string.
def parse_sequences_file_json(fname):
with open(fname, "r") as inp:
seqs = json.load(inp)
return list(seqs.items())
# Format of the sequences file is determined by its extension
def parse_sequences_file(fname):
if fname.endswith(".pre"):
return parse_sequences_file_xkb(fname)
if fname.endswith(".json"):
return parse_sequences_file_json(fname)
raise Exception(fname + ": Unsupported format")
# Turn a list of sequences into a trie.
def add_sequences_to_trie(seqs, trie):
def add_seq_to_trie(t_, seq, result):
t_ = trie
i = 0
while i < len(seq) - 1:
c = seq[i]
if c not in t_:
t_[c] = {}
if isinstance(t_[c], str):
global dropped_sequences
dropped_sequences += 1
print("Sequence collide: '%s = %s' '%s = %s'" % (
seq[:i+1], t_[c], seq, result),
file=sys.stderr)
return
t_ = t_[c]
i += 1
c = seq[i]
t_[c] = result
for seq, result in seqs:
add_seq_to_trie(trie, seq, result)
# Compile the trie into a state machine.
def make_automata(tree_root):
states = []
def add_tree(t):
# Index and size of the new node
i = len(states)
s = len(t.keys())
# Add node header
states.append(("\0", s + 1))
i += 1
# Reserve space for the current node in both arrays
for c in range(s):
states.append((None, None))
# Add nested nodes and fill the current node
for c in sorted(t.keys()):
node_i = len(states)
add_node(t[c])
states[i] = (c, node_i)
i += 1
def add_leaf(c):
states.append((c, 1))
def add_node(n):
if type(n) == str:
add_leaf(n)
else:
add_tree(n)
add_tree(tree_root)
return states
def batched(ar, n):
i = 0
while i + n < len(ar):
yield ar[i:i+n]
i += n
if i < len(ar):
yield ar[i:]
# Print the state machine compiled by make_automata into java code that can be
# used by [ComposeKeyData.java].
def gen_java(machine):
chars_map = {
# These characters cannot be used in unicode form as Java's parser
# unescape unicode sequences before parsing.
"\"": "\\\"",
"\\": "\\\\",
"\n": "\\n",
"\r": "\\r",
ord("\""): "\\\"",
ord("\\"): "\\\\",
ord("\n"): "\\n",
ord("\r"): "\\r",
}
def char_repr(c):
if c in chars_map:
return chars_map[c]
if type(c) == int: # The edges array contains ints
return "\\u%04x" % c
if c in string.printable:
return c
return "\\u%04x" % ord(c)
def gen_array(array):
chars = list(map(char_repr, array))
return "\" +\n \"".join(map(lambda b: "".join(b), batched(chars, 72)))
print("""package juloo.keyboard2;
/** This file is generated, see [srcs/compose/compile.py]. */
public final class ComposeKeyData
{
public static final char[] states =
("%s").toCharArray();
public static final char[] edges =
("%s").toCharArray();
}""" % (
# Break the edges array every few characters using string concatenation.
gen_array(map(lambda s: s[0], machine)),
gen_array(map(lambda s: s[1], machine)),
))
total_sequences = 0
trie = {}
for fname in sys.argv[1:]:
sequences = parse_sequences_file(fname)
add_sequences_to_trie(sequences, trie)
total_sequences += len(sequences)
automata = make_automata(trie)
gen_java(automata)
print("Compiled %d sequences into %d states. Dropped %d sequences." % (total_sequences, len(automata), dropped_sequences), file=sys.stderr)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
package juloo.keyboard2;
import java.util.Arrays;
public final class ComposeKey
{
/** Apply the pending compose sequence to [kv]. Returns [null] if [kv] is not
part of the pending sequence. */
public static KeyValue apply(int state, KeyValue kv)
{
switch (kv.getKind())
{
case Char:
KeyValue res = apply(state, kv.getChar());
// Dim characters not part of any sequence instead of removing them.
if (res == null)
return kv.withFlags(kv.getFlags() | KeyValue.FLAG_GREYED);
return res;
/* These keys must not be removed. */
case Event:
case Modifier:
return kv;
/* These keys cannot be part of sequences. */
case String:
case Keyevent:
case Editing:
case Placeholder:
return kv.withFlags(kv.getFlags() | KeyValue.FLAG_GREYED);
case Compose_pending: return null;
}
return null;
}
/** Apply the pending compose sequence to char [c]. */
static KeyValue apply(int state, char c)
{
char[] states = ComposeKeyData.states;
char[] edges = ComposeKeyData.edges;
int length = edges[state];
int next = Arrays.binarySearch(states, state + 1, state + length, c);
if (next < 0)
return null;
next = edges[next];
// The next state is the end of a sequence, show the result.
if (edges[next] == 1)
return KeyValue.makeCharKey(states[next]);
return KeyValue.makeComposePending(String.valueOf(c), next, 0);
}
/** The [states] array represents the different states and their transition.
A state occupies one or several cells of the array:
- The first cell is the result of the compose sequence if the state is of
size 1, unspecified otherwise.
- The remaining cells are the transitions, sorted alphabetically.
The [edges] array represents the transition state corresponding to each
accepted inputs.
Id [states[i]] is the first cell of a state, [edges[i]] is the number of
cells occupied by the state [i].
If [states[i]] is a transition, [edges[i]] is the index of the state to
jump into. */
}

View File

@@ -0,0 +1,150 @@
package juloo.keyboard2;
/** This file is generated, see [srcs/compose/compile.py]. */
public final class ComposeKeyData
{
public static final char[] states =
("\u0000 !\"#%'()*+,-./01234578:;<=>?ABCDEFGHIKLMNOPQRSTUVWXYZ[\\]^_`abcdefghiklm" +
"noprstuvwxyz{|~\u00a8\u00af\u00b4\u00b8\u00f7\u02d8\u05b7\u05c1\u05c2\u0654\u093c\u09bc\u09c7\u0a3c\u0b3c\u0b47\u0bc6\u0bc7\u0bd7\u0c46\u0cbf\u0cc6\u0cca\u0d46\u0d47\u0dd9\u0ddc\u0f71\u0f90\u0f92\u0f9c\u0fa1\u0fa6\u0fab\u0fb2\u0fb3\u0fb5\u0fb7\u102e\u1100\u1102\u1103\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110e\u1111\u1112\u1121\u1132\u113c\u113e" +
"\u114e\u1150\u1161\u1163\u1165\u1167\u1169\u116a\u116d\u116e\u116f\u1172\u1173\u1174\u1175\u119e\u11a8\u11aa\u11ab\u11ae\u11af\u11b0\u11b1\u11b2\u11b3\u11b7\u11b8\u11ba\u11bc\u11c1\u11c2\u11ce\u11dd\u11ec\u11f0\u2190\u2191\u2192\u2193\u2203\u2206\u2207\u2208\u220a\u220b\u2218\u2223\u2225\u2227\u2228\u2229\u222a\u223c\u2243\u2248\u224d\u2260\u2272\u2273\u2276\u2277\u227a\u227b\u227c\u227d\u2282\u2283\u2286\u2287\u2291\u2292\u22a4" +
"\u22a5\u22a8\u22a9\u22ab\u22b2\u22b3\u22b4\u22b5\u22c4\u2373\u2375\u237a\u2395\u25cb\u2add\u0000 (,-.<>_\u00a0\u02d8\u00b8~\u2008\u02c7^\u00af\u0000!+?ABDEHIKLMNORSTUVWYZ^abdehiklmnorstuv" +
"wyz\u00a1\u0000OUou\u1ee2\u1ef0\u1ee3\u1ef1\u203d\u1ea0\u1e04\u1e0c\u1eb8\u1e24\u1eca\u1e32\u1e36\u1e42\u1e46\u1ecc\u1e5a\u1e62\u1e6c\u1ee4\u1e7e\u1e88\u1ef4\u1e92\u00a6\u1ea1\u1e05\u1e0d\u1eb9\u1e25\u1ecb\u1e33\u1e37\u1e43\u1e47\u1ecd\u1e5b\u1e63\u1e6d\u1ee5\u1e7f\u1e89\u1ef5\u1e93\u0000 \"',<>AEHIOUWXY_ae" +
"hiotuwxy~\u00af\u00b4\u00d5\u00f5\u03d2\u04d8\u04d9\u04e8\u04e9\u00a8\u00a8\u0344\u201e\u201c\u201d\u00c4\u00cb\u1e26\u00cf\u00d6\u00dc\u1e84\u1e8c\u0178\u0000Uu\u1e7a\u1e7b\u00e4\u00eb\u1e27\u00ef\u00f6\u1e97\u00fc\u1e85\u1e8d\u00ff\u0000Oo\u1e4e\u1e4f\u0000Uu\u1e7a\u1e7b\u0344\u1e4e\u1e4f\u03d4\u04da\u04db\u04ea\u04eb\u0000#ESbe" +
"fq\u266f\u266b\u266c\u266d\u266a\u266e\u2669\u0000o\u2030\u0000\"'+,/<>ACEGIKLMNOPRSUWYZ^_abcegiklmnoprsuwyz~\u00af\u00b8\u00c2\u00c5\u00c6\u00c7\u00ca\u00cf\u00d4\u00d5\u00dc\u00e2\u00e5\u00e6" +
"\u00e7\u00ea\u00ef\u00f4\u00f5\u00f8\u00fc\u2395\u0000 IUiu\u0385\u1e2e\u01d7\u1e2f\u01d8\u00b4\u0000OUou\u1eda\u1ee8\u1edb\u1ee9\u201a\u0000Oo\u01fe\u01ff\u2018\u2019\u00c1\u0106\u00c9\u01f4\u00cd\u1e30\u0139\u1e3e\u0143\u00d3\u1e54\u0154\u015a\u00da\u1e82\u00dd\u0179\u0000AEOaeo\u1ea4\u1ebe\u1ed0\u1ea5\u1ebf\u1ed1\u0000EOeo" +
"\u1e16\u1e52\u1e17\u1e53\u00e1\u0000Aa\u1eae\u1eaf\u0107\u00e9\u01f5\u00ed\u1e31\u013a\u1e3f\u0144\u00f3\u1e55\u0155\u015b\u00fa\u1e83\u00fd\u017a\u0000OUou\u1e4c\u1e78\u1e4d\u1e79\u0000EOeo\u1e16\u1e52\u1e17\u1e53\u0000Cc\u1e08\u1e09\u1ea4\u01fa\u01fc\u1e08\u1ebe\u1e2e\u1ed0\u1e4c\u01d7\u1ea5\u01fb\u01fd\u1e09\u1ebf\u1e2f\u1ed1\u1e4d\u01ff\u01d8\u235e\u0000 (" +
"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\u1100\u1102\u1103\u1105\u1106\u1107\u1109\u110b\u110c" +
"\u110e\u110f\u1110\u1111\u1112\u30f0\u30f1\u4e00\u4e03\u4e09\u4e0a\u4e0b\u4e2d\u4e5d\u4e8c\u4e94\u4f01\u4f11\u512a\u516b\u516d\u5199\u52b4\u533b\u5341\u5354\u5370\u53f3\u540d\u56db\u571f\u591c\u5973\u5b66\u5b97\u5de6\u65e5\u6708\u6709\u6728\u682a\u6b63\u6c34\u6ce8\u706b\u7279\u7537\u76e3\u793e\u795d\u79d8\u8ca1\u8cc7\u9069\u91d1\u9805\u02d8[{\u0000)\u24ea\u0000)01234567" +
"89\u2460\u0000)\u2469\u0000)\u246a\u0000)\u246b\u0000)\u246c\u0000)\u246d\u0000)\u246e\u0000)\u246f\u0000)\u2470\u0000)\u2471\u0000)\u2472\u0000)0123456789\u2461\u0000)\u2473\u0000)\u3251\u0000)\u3252\u0000)\u3253\u0000)\u3254\u0000)\u3255\u0000)\u3256\u0000)\u3257\u0000)" +
"\u3258\u0000)\u3259\u0000)0123456789\u2462\u0000)\u325a\u0000)\u325b\u0000)\u325c\u0000)\u325d\u0000)\u325e\u0000)\u325f\u0000)\u32b1\u0000)\u32b2\u0000)\u32b3\u0000)\u32b4\u0000)0123456789\u2463\u0000)\u32b5\u0000)\u32b6\u0000)\u32b7\u0000)\u32b8" +
"\u0000)\u32b9\u0000)\u32ba\u0000)\u32bb\u0000)\u32bc\u0000)\u32bd\u0000)\u32be\u0000)0\u2464\u0000)\u32bf\u0000)\u2465\u0000)\u2466\u0000)\u2467\u0000)\u2468\u0000)\u24b6\u0000)\u24b7\u0000)\u24b8\u0000)\u24b9\u0000)\u24ba\u0000)\u24bb\u0000)\u24bc\u0000)\u24bd\u0000)\u24be\u0000)\u24bf\u0000)\u24c0\u0000)" +
"\u24c1\u0000)\u24c2\u0000)\u24c3\u0000)\u24c4\u0000)\u24c5\u0000)\u24c6\u0000)\u24c7\u0000)\u24c8\u0000)\u24c9\u0000)\u24ca\u0000)\u24cb\u0000)\u24cc\u0000)\u24cd\u0000)\u24ce\u0000)\u24cf\u0000)\u24d0\u0000)\u24d1\u0000)\u24d2\u0000)\u24d3\u0000)\u24d4\u0000)\u24d5\u0000)\u24d6\u0000)\u24d7\u0000)\u24d8\u0000)" +
"\u24d9\u0000)\u24da\u0000)\u24db\u0000)\u24dc\u0000)\u24dd\u0000)\u24de\u0000)\u24df\u0000)\u24e0\u0000)\u24e1\u0000)\u24e2\u0000)\u24e3\u0000)\u24e4\u0000)\u24e5\u0000)\u24e6\u0000)\u24e7\u0000)\u24e8\u0000)\u24e9\u0000)\u1161\u3260\u0000)\u326e\u0000)\u1161\u3261\u0000)\u326f\u0000)\u1161\u3262\u0000)\u3270\u0000)" +
"\u1161\u3263\u0000)\u3271\u0000)\u1161\u3264\u0000)\u3272\u0000)\u1161\u3265\u0000)\u3273\u0000)\u1161\u3266\u0000)\u3274\u0000)\u1161\u3267\u0000)\u3275\u0000)\u1161\u3268\u0000)\u3276\u0000)\u1161\u3269\u0000)\u3277\u0000)\u1161\u326a\u0000)\u3278\u0000)\u1161\u326b\u0000)\u3279\u0000)\u1161\u326c\u0000)\u327a\u0000)\u1161\u326d" +
"\u0000)\u327b\u0000)\u32fc\u0000)\u32fd\u0000)\u3280\u0000)\u3286\u0000)\u3282\u0000)\u32a4\u0000)\u32a6\u0000)\u32a5\u0000)\u3288\u0000)\u3281\u0000)\u3284\u0000)\u32ad\u0000)\u32a1\u0000)\u329d\u0000)\u3287\u0000)\u3285\u0000)\u32a2\u0000)\u3298\u0000)\u32a9\u0000)\u3289\u0000)\u32af\u0000)\u329e\u0000)\u32a8" +
"\u0000)\u3294\u0000)\u3283\u0000)\u328f\u0000)\u32b0\u0000)\u329b\u0000)\u32ab\u0000)\u32aa\u0000)\u32a7\u0000)\u3290\u0000)\u328a\u0000)\u3292\u0000)\u328d\u0000)\u3291\u0000)\u32a3\u0000)\u328c\u0000)\u329f\u0000)\u328b\u0000)\u3295\u0000)\u329a\u0000)\u32ac\u0000)\u3293\u0000)\u3297\u0000)\u3299\u0000)\u3296" +
"\u0000)\u32ae\u0000)\u329c\u0000)\u328e\u0000)\u32a0\u0000)-]}\u0000'0AUau\u00a8\u25cb\u0000Aa\u01fa\u01fb\u00b0\u00c5\u016e\u00e5\u016f\u2363\u235f\u0000+-OUou#\u00b1\u01a0\u01af\u01a1\u01b0\u0000 \"',-ACDEGHIKLNORSTU" +
"acdeghiklnorstu\u00b8\u201e\u201a\u00b8\u00ac\u0104\u00c7\u1e10\u0118\u0122\u1e28\u012e\u0136\u013b\u0145\u01ea\u0156\u015e\u0162\u0172\u0105\u00e7\u1e11\u0119\u0123\u1e29\u012f\u0137\u013c\u0146\u01eb\u0157\u015f\u0163\u0173\u0000 ()+,-/:>ADEIKLOU\\^_a" +
"deilou\u2191\u2193\u25cb~{}\u00b1\u00ac\u0000 -.\u00ad\u2014\u2013\u233f\u00f7\u2192\u0100\u0110\u0112\u012a\u20ad\u00a3\u014c\u016a\u2340\u00af\u2212\u0101\u0111\u0113\u012b\u00a3\u014d\u016b\u234f\u2356\u2296\u0000 !'-.:<=>ABCDEFGHIMNOPRSTW" +
"XYZ^abcdefghimnoprstwxyz\u00b4\u017f\u1e62\u1e63\u25cb\u02d9\u0000Ss\u1e68\u1e69\u0000Ss\u1e64\u1e65\u00b7\u2026\u2235\u2039\u2022\u203a\u0226\u1e02\u010a\u1e0a\u0116\u1e1e\u0120\u1e22\u0130\u1e40\u1e44\u022e\u1e56\u1e58\u1e60\u1e6a\u1e86\u1e8a\u1e8e\u017b\u00b7\u0227\u1e03\u010b\u1e0b\u0117" +
"\u1e1f\u0121\u1e23\u0131\u1e41\u1e45\u022f\u1e57\u1e59\u1e61\u1e6b\u1e87\u1e8b\u1e8f\u017c\u0000Ss\u1e64\u1e65\u1e9b\u1e68\u1e69\u2299\u0000-/<=BCDGHILOTZ^bcdghilmotuvz\u0294\u04ae\u04af\u2194\u2395\u233f\\\\\u2260\u0243\u20a1\u0110\u01e4\u0126\u0197\u0141\u00d8\u0166\u01b5" +
"|\u0180\u00a2\u0111\u01e5\u0127\u0268\u0142\u20a5\u00f8\u0167\u00b5\u221a\u01b6\u02a1\u04b0\u04b1\u21ae\u2341\u0000*3~\u00b0\u2189\u236c\u0000123456789^\u00000\u2152\u00bd\u2153\u00bc\u2155\u2159\u2150\u215b\u2151\u00b9\u000035^\u2154\u2156\u00b2\u0000458^\u00be\u2157\u215c\u00b3\u00005\u2158\u000068\u215a" +
"\u215d\u00008\u215e\u00008\u221e\u0000()-.\u2395\u2639\u263a\u00f7\u2234\u2360\u0000 AEIOSTU_aeiostu\u02db\u0104\u0118\u012e\u01ea\u0218\u021a\u0172\u236e\u0105\u0119\u012f\u01eb\u0219\u021b\u0173\u0000 \"'-/3<=>CDELNRSTZ_c" +
"delnrstz\u2395\u02c7\u201c\u2018\u2190\u226e\u2665\u00ab\u2264\u22c4\u010c\u010e\u011a\u013d\u0147\u0158\u0160\u0164\u017d\u2264\u010d\u010f\u011b\u013e\u0148\u0159\u0161\u0165\u017e\u2343\u0000/<>CELNOPRTUWY^_cdeopruvy\u2395\u2260\u21d0\u21d2\u20ac\u20ac\u20a4\u20a6" +
"\u0150\u20bd\u20b9\u20ae\u0170\u20a9\u00a5\u21d1\u2261\u20ac\u20ab\u20ac\u0151\u20bd\u20b9\u0171\u21d3\u00a5\u2338\u0000 \"'/<=>AEIOU_aeiou\u00a8\u2395^\u201d\u2019\u226f\u22c4\u2265\u00bb\u00c2\u00ca\u00ce\u00d4\u00db\u2265\u00e2\u00ea\u00ee\u00f4\u00fb\u2369\u2344\u0000!+?AEIOUY\\^" +
"abeiouy\u00c2\u00ca\u00d4\u00e2\u00ea\u00f4\u2395\u2e18\u0000OUou\u1ede\u1eec\u1edf\u1eed\u00bf\u1ea2\u1eba\u1ec8\u1ece\u1ee6\u1ef6\u262d\u0000AEOaeo\u1ea8\u1ec2\u1ed4\u1ea9\u1ec3\u1ed5\u1ea3\u0000Aa\u1eb2\u1eb3\u1ebb\u1ec9\u1ecf\u1ee7\u1ef7\u1ea8\u1ec2\u1ed4\u1ea9\u1ec3\u1ed5\u2370\u0000\"'(*,-;>" +
"AET^_`~\u00a8\u00b4\u00c4\u00c1\u0102\u00c5\u0104\u0100\u0104\u00c2\u00c5\u00c6@\u00c2\u0100\u00c0\u00c3\u00c4\u00c1\u0000.|\u1e02\u0e3f\u0000',./<=CEOr|\u0106\u00c7\u010a\u20a1\u010c\u20ac\u2102\u20a0\u00a9\u20a2\u20b5\u0000,-.<H\u1e10\u0110\u1e0a\u010e\u00d0\u0000\"',-.;" +
"<=>E^_`\u00a8\u00b4\u00cb\u00c9\u0118\u0112\u0116\u0118\u011a\u20ac\u00ca\u018f\u00ca\u0112\u00c8\u00cb\u00c9\u0000.ilr\u1e1e\ufb03\ufb04\u20a3\u0000(,.TU|\u02d8\u011e\u0122\u0120>\u011e\u20b2\u011e\u0000,\u1e28\u0000\"',-.;>J^_`j~\u00a8\u00b4\u00cf\u00cd\u012e\u012a\u0130" +
"\u012e\u00ce\u0132\u00ce\u012a\u00cc\u0132\u0128\u00cf\u00cd\u0000,-\u0136\u20ad\u0000',-/<=TV\u0139\u013b\u00a3\u0141\u013d\u20a4<|\u0000.\u1e40\u0000',<=GNOo~\u0143\u0145\u0147\u20a6\u014a\u2115\u2116\u2116\u00d1\u0000\"',-/;>ACERSXY^_`" +
"~\u00a8\u00b4\u00d6\u00d3\u01ea\u014c\u00d8\u01ea\u00d4\u24b6\u00a9\u0152\u00ae\u00a7\u00a4\u262e\u00d4\u014c\u00d2\u00d5\u00d6\u00d3\u0000!.=Pt\u00b6\u1e56\u20bd\u00b6\u20a7\u0000Q\u211a\u0000',<=ORs\u0154\u0156\u0158\u20b9\u00ae\u211d\u20a8\u0000!',.;<MOS|\u00a7\u015a\u015e\u1e60\u0218\u0160\u2120\u00a7\u1e9e" +
"$\u0000,-./;<=HM\u0162\u0166\u1e6a\u0166\u021a\u0164\u20ae\u00de\u2122\u0000 !\"'*,-;>AEGIOU^_`aegiou~\u00a8\u00b4\u00b8\u0000,\u0000Ee\u1e1c\u1e1d\u0000Aa\u1eb6\u1eb7\u00dc\u00da\u016e\u0172\u016a\u0172\u00db\u0102\u0114\u011e\u012c" +
"\u014e\u016c\u00db\u016a\u00d9\u0103\u0115\u011f\u012d\u014f\u016d\u0168\u00dc\u00da\u0000Ee\u1e1c\u1e1d\u0000L|\u0000=^\u20a9\u0174\u0000O\u00a4\u0000\"'=^\u00a8\u00b4\u0178\u00dd\u00a5\u0176\u0178\u00dd\u0000'.<Z\u0179\u017b\u017d\u2124\u0000]\u2337\u0000-?\u2395\u25cb\u2340\u262d\u2342\u2349\u0000[\u2337\u0000!()+" +
"-./0123456789=ACEGHIJOSUWYZ_aceghijosuwyz|\u1eb8\u1eb9\u1ecc\u1ecd\u2212\u4e00\u4e01\u4e09\u4e0a\u4e0b\u4e19\u4e2d\u4e59\u4e8c\u4eba\u56db\u5730\u5929\u7532\u0000AEOaeo\u1eac\u1ec6\u1ed8\u1ead" +
"\u1ec7\u1ed9\u207d\u207e\u207a\u207b\u00b7|\u2070\u00b9\u00b2\u00b3\u2074\u2075\u2076\u2077\u2078\u2079\u207c\u00c2\u0108\u00ca\u011c\u0124\u00ce\u0134\u00d4\u015c\u00db\u0174\u0176\u1e90\u0000ahijlnorswxy\u0263\u0266\u0279\u027b\u0281\u0295\u00aa\u02b0\u2071\u02b2\u02e1\u207f\u00ba\u02b3\u02e2\u02b7\u02e3\u02b8\u02e0\u02b1\u02b4\u02b5\u02b6\u02e4\u00e2\u0109\u00ea" +
"\u011d\u0125\u00ee\u0135\u00f4\u015d\u00fb\u0175\u0177\u1e91\u2191\u1ec6\u1ec7\u1ed8\u1ed9\u207b\u3192\u319c\u3194\u3196\u3198\u319b\u3197\u319a\u3193\u319f\u3195\u319e\u319d\u3199\u0000 !\"'()+-.0123456789;<=>AEGIOUY^_aegiouy~\u00c4" +
"\u00c6\u00d5\u00d6\u00dc\u00e4\u00e6\u00f5\u00f6\u00fc\u0226\u0227\u1e36\u1e37\u1e5a\u1e5b\u2206\u220a\u2212\u2218\u2260\u2282\u2283\u22a5\u22c4\u2373\u2375\u237a\u25cb\u00af\u0000LRlr\u1e38\u1e5c\u1e39\u1e5d\u0000AOUaou\u01de\u022a\u01d5\u01df\u022b\u01d6\u2358\u208d\u208e\u208a\u208b\u0000AOao\u01e0\u0230\u01e1\u0231\u2080\u2081\u2082\u2083\u2084\u2085\u2086" +
"\u2087\u2088\u2089\u0000Oo\u01ec\u01ed\u2264\u208c\u2265\u0100\u0112\u1e20\u012a\u014c\u016a\u0232\u00af\u00af\u0101\u0113\u1e21\u012b\u014d\u016b\u0233\u0000Oo\u022c\u022d\u01de\u01e2\u022c\u022a\u01d5\u01df\u01e3\u022d\u022b\u01d6\u01e0\u01e1\u1e38\u1e39\u1e5c\u1e5d\u2359\u2377\u208b\u235b\u2262\u2286\u2287\u234a\u235a\u2378\u2379\u2376\u235c\u0000\"+AEINOUWY" +
"^_abeinouwy\u00af\u00c2\u00ca\u00d4\u00dc\u00e2\u00ea\u00f4\u00fc\u0000Uu\u01db\u01dc\u0000OUou\u1edc\u1eea\u1edd\u1eeb\u00c0\u00c8\u00cc\u01f8\u00d2\u00d9\u1e80\u1ef2\u0000AEOaeo\u1ea6\u1ec0\u1ed2\u1ea7\u1ec1\u1ed3\u0000EOeo\u1e14\u1e50\u1e15\u1e51\u00e0\u0000Aa\u1eb0\u1eb1\u00e8\u00ec" +
"\u01f9\u00f2\u00f9\u1e81\u1ef3\u0000EOeo\u1e14\u1e50\u1e15\u1e51\u1ea6\u1ec0\u1ed2\u01db\u1ea7\u1ec1\u1ed3\u01dc\u0000\"'(*,-;>^_`ae~\u00a8\u00b4\u00e4\u00e1\u0103\u00e5\u0105\u0101\u0105\u00e2\u00e2\u0101\u00e0\u00e5\u00e6\u00e3\u00e4\u00e1\u0000!,.AEGIOUaegiou\u00b8" +
"\u0000Aa\u1eb6\u1eb7\u0000Ee\u1e1c\u1e1d\u1e03\u0102\u0114\u011e\u012c\u014e\u016c\u0103\u0115\u011f\u012d\u014f\u016d\u0000Ee\u1e1c\u1e1d\u0000\"',./<=ACDEGHIKLNORSTUZacdeghijklnorstuz|\u00dc\u00fc" +
"\u0000Uu\u01d9\u01da\u0107\u00e7\u010b\u00a2\u010d\u20ac\u01cd\u010c\u010e\u011a\u01e6\u021e\u01cf\u01e8\u013d\u0147\u01d1\u0158\u0160\u0164\u01d3\u017d\u01ce\u010d\u010f\u011b\u01e7\u021f\u01d0\u01f0\u01e9\u013e\u0148\u01d2\u0159\u0161\u0165\u01d4\u017e\u00a2\u01d9\u01da\u0000,-.<=hi\u1e11\u0111\u1e0b\u010f\u20ab\u00f0\u2300\u0000\"',-.;<=>" +
"^_`e\u00a8\u00b4\u00eb\u00e9\u0119\u0113\u0117\u0119\u011b\u20ac\u00ea\u00ea\u0113\u00e8\u0259\u00eb\u00e9\u0000.Sfils\u1e1f\u017f\ufb00\ufb01\ufb02\u017f\u0000(,.Ut\u02d8\u011f\u0123\u0121\u011f>\u011f\u0000,\u1e29\u0000\"',-.;>^_`j~\u00a8\u00b4\u00ef\u00ed\u012f\u012b\u0131\u012f\u00ee" +
"\u00ee\u012b\u00ec\u0133\u0129\u00ef\u00ed\u0000,k\u0137\u0138\u0000',-/<tv\u013a\u013c\u00a3\u0142\u013e<|\u0000./u\u1e41\u20a5\u00b5\u0000',<g~\u0144\u0146\u0148\u014b\u00f1\u0000\"',-/;>AU^_`aceorsuwxy~\u00a8\u00b4\u00f6" +
"\u00f3\u01eb\u014d\u00f8\u01eb\u00f4\u00c5\u016e\u00f4\u014d\u00f2\u00e5\u00a9\u0153\u00b0\u00ae\u00a7\u016f\u1e98\u00a4\u1e99\u00f5\u00f6\u00f3\u0000!.=\u00b6\u1e57\u20bd\u0000',<=\u0155\u0157\u0159\u20b9\u0000!',.;<mos\u00b8\u00a7\u015b\u015f\u1e61\u0219\u0161\u2120\u00a7\u00df\u015f\u0000,-./;<hm\u0163\u0167" +
"\u1e6b\u0167\u021b\u0165\u00fe\u2122\u0000\"'*,-/;>AEGIOU^_`aegiou~\u00a8\u00b4\u00fc\u00fa\u016f\u0173\u016b\u00b5\u0173\u00fb\u0102\u0114\u011e\u012c\u014e\u016c\u00fb\u016b\u00f9\u0103\u0115\u011f\u012d\u014f\u016d\u0169\u00fc\u00fa\u0000/ACDEGHIKNOR" +
"STUZacdeghijklnorstuz|\u221a\u01cd\u010c\u010e\u011a\u01e6\u021e\u01cf\u01e8\u0147\u01d1\u0158\u0160\u0164\u01d3\u017d\u01ce\u010d\u010f\u011b\u01e7\u021f\u01d0\u01f0\u01e9|\u0148\u01d2\u0159\u0161\u0165\u01d4\u017e\u2193\u0000^\u0175\u0000ox\u00a4\u00d7\u0000\"'=^\u00a8\u00b4\u00ff" +
"\u00fd\u00a5\u0177\u00ff\u00fd\u0000'.<\u017a\u017c\u017e\u0000}\u2205\u0000-=BCGS^cv~\u2190\u2192\u2206\u2207\u222a\u2282\u25cb\u2020\u2021\u0e3f\u20b5\u20b2$\u2191\u00a2\u2193\u236d\u2345\u2346\u234b\u2352\u2366\u2367\u233d\u0000+0AEINOUVY^abeinouvy|" +
"~\u00a8\u00c2\u00ca\u00d4\u00e2\u00ea\u00f4\u2207\u2227\u2228\u0000OUou\u1ee0\u1eee\u1ee1\u1eef\u236c\u00c3\u1ebc\u0128\u00d1\u00d5\u0168\u1e7c\u1ef8\u0000AEOaeo\u1eaa\u1ec4\u1ed6\u1eab\u1ec5\u1ed7\u00e3\u0000Aa\u1eb4\u1eb5\u1ebd\u0129\u00f1\u00f5\u0169\u1e7d\u1ef9\u236d\u2248\u2368\u1eaa\u1ec4\u1ed6\u1eab\u1ec5\u1ed7\u236b\u2372\u2371\u0000'*>A" +
"EIOUY`aeiouy~\u00b4\u2207\u2218\u22a4\u25cb\u0385\u2363\u2369\u00c4\u00cb\u00cf\u00d6\u00dc\u0178\u1fed\u00e4\u00eb\u00ef\u00f6\u00fc\u00ff\u1fc1\u0385\u2362\u2364\u2361\u2365\u0000!\".;AEGIOUYaegiouy~\u00c4\u00c6\u00d5\u00d6\u00dc\u00e4\u00e6\u00f5\u00f6\u00fc\u0226\u0227" +
"\u1e36\u1e37\u1e5a\u1e5b\u22a4\u0000LRlr\u1e38\u1e5c\u1e39\u1e5d\u0000AOUaou\u01de\u022a\u01d5\u01df\u022b\u01d6\u0000AOao\u01e0\u0230\u01e1\u0231\u0000Oo\u01ec\u01ed\u0100\u0112\u1e20\u012a\u014c\u016a\u0232\u0101\u0113\u1e21\u012b\u014d\u016b\u0233\u0000Oo\u022c\u022d\u01de\u01e2\u022c\u022a\u01d5\u01df\u01e3\u022d\u022b\u01d6\u01e0\u01e1" +
"\u1e38\u1e39\u1e5c\u1e5d\u2351\u0000\"+,/ACEGIKLMNOPRSUWYZ^_abcegiklmnoprsuwyz~\u00af\u00b8\u00c2\u00c5\u00c6\u00c7\u00ca\u00cf\u00d4\u00d5\u00dc\u00e2\u00e5\u00e6\u00e7\u00ea\u00ef\u00f4\u00f5\u00f8\u00fc\u0000IU" +
"iu\u1e2e\u01d7\u1e2f\u01d8\u0000OUou\u1eda\u1ee8\u1edb\u1ee9\u0000Cc\u1e08\u1e09\u0000Oo\u01fe\u01ff\u00c1\u0106\u00c9\u01f4\u00cd\u1e30\u0139\u1e3e\u0143\u00d3\u1e54\u0154\u015a\u00da\u1e82\u00dd\u0179\u0000AEOaeo\u1ea4\u1ebe\u1ed0\u1ea5\u1ebf\u1ed1\u0000EOeo\u1e16\u1e52\u1e17\u1e53\u00e1\u0000Aa\u1eae\u1eaf\u0107\u00e9" +
"\u01f5\u00ed\u1e31\u013a\u1e3f\u0144\u00f3\u1e55\u0155\u015b\u00fa\u1e83\u00fd\u017a\u0000OUou\u1e4c\u1e78\u1e4d\u1e79\u0000EOeo\u1e16\u1e52\u1e17\u1e53\u0000Cc\u1e08\u1e09\u1ea4\u01fa\u01fc\u1e08\u1ebe\u1e2e\u1ed0\u1e4c\u01d7\u1ea5\u01fb\u01fd\u1e09\u1ebf\u1e2f\u1ed1\u1e4d\u01ff\u01d8\u0000CDEGHKLNRSTcdeg" +
"hklnrst\u00c7\u1e10\u0228\u0122\u1e28\u0136\u013b\u0145\u0156\u015e\u0162\u00e7\u1e11\u0229\u0123\u1e29\u0137\u013c\u0146\u0157\u015f\u0163\u0000\u2395\u2339\u0000Gg\u011e\u011f\u0000\u05f2\ufb1f\u0000\ufb49\ufb2c\u0000\ufb49\ufb2d\u0000\u06c1\u06d2\u06d5\u06c2\u06d3\u06c0\u0000\u0915\u0916\u0917\u091c\u0921\u0922\u0928\u092b\u092f\u0930\u0933\u0958\u0959\u095a\u095b\u095c\u095d\u0929" +
"\u095e\u095f\u0931\u0934\u0000\u09a1\u09a2\u09af\u09dc\u09dd\u09df\u0000\u09be\u09d7\u09cb\u09cc\u0000\u0a16\u0a17\u0a1c\u0a2b\u0a32\u0a38\u0a59\u0a5a\u0a5b\u0a5e\u0a33\u0a36\u0000\u0b21\u0b22\u0b5c\u0b5d\u0000\u0b3e\u0b56\u0b57\u0b4b\u0b48\u0b4c\u0000\u0bbe\u0bd7\u0bca\u0bcc\u0000\u0bbe\u0bcb\u0000\u0b92\u0b94\u0000\u0c56\u0c48\u0000\u0cd5\u0cc0\u0000\u0cc2\u0cd5\u0cd6\u0cca\u0cc7\u0cc8\u0000\u0cd5\u0ccb\u0000\u0d3e\u0d57\u0d4a" +
"\u0d4c\u0000\u0d3e\u0d4b\u0000\u0dca\u0dcf\u0ddf\u0dda\u0ddc\u0dde\u0000\u0dca\u0ddd\u0000\u0f72\u0f74\u0f80\u0f73\u0f75\u0f81\u0000\u0fb5\u0fb9\u0000\u0fb7\u0f93\u0000\u0fb7\u0f9d\u0000\u0fb7\u0fa2\u0000\u0fb7\u0fa7\u0000\u0fb7\u0fac\u0000\u0f80\u0f76\u0000\u0f80\u0f78\u0000\u0f40\u0f69\u0000\u0f42\u0f4c\u0f51\u0f56\u0f5b\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0000\u1025\u1026\u0000\u1100\u1101\u0000\u1100\u1102\u1103\u1107\u1113\u1114" +
"\u1115\u1116\u0000\u1100\u1103\u1117\u1104\u0000\u1102\u1105\u110b\u1112\u1118\u1119\u111b\u111a\u0000\u1107\u110b\u111c\u111d\u0000\u1100\u1102\u1103\u1107\u1109\u110a\u110b\u110c\u110e\u1110\u1111\u112b\u112d\u112f\u1132\u1136\u111e\u111f\u1120\u1108\u1121\u1125\u112b\u1127\u1128\u1129\u112a\u112c\u1122\u1123\u1124\u1126\u0000\u110b\u112c\u0000\u1100\u1102\u1103\u1105\u1106\u1107\u1109\u110a\u110b\u110c\u110e\u110f\u1110\u1111" +
"\u1112\u111e\u112d\u112e\u112f\u1130\u1131\u1132\u110a\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u1133\u0000\u1109\u1134\u0000\u1100\u1103\u1106\u1107\u1109\u110b\u110c\u110e\u1110\u1111\u1140\u1141\u1142\u1143\u1144\u1145\u1147\u1148\u1149\u114a\u114b\u1146\u0000\u110b\u110c\u114d\u110d\u0000\u110f\u1112\u1152\u1153\u0000\u1107\u110b\u1156\u1157\u0000\u1112\u1158\u0000\u1100\u1103\u1107\u1109\u110c\u1122\u1123\u1124\u1125" +
"\u1126\u0000\u1100\u1133\u0000\u113c\u113d\u0000\u113e\u113f\u0000\u114e\u114f\u0000\u1150\u1151\u0000\u1169\u116e\u1175\u1176\u1177\u1162\u0000\u1169\u116d\u1175\u1178\u1179\u1164\u0000\u1169\u116e\u1173\u1175\u117a\u117b\u117c\u1166\u0000\u1169\u116e\u1175\u117d\u117e\u1168\u0000\u1161\u1162\u1165\u1166\u1168\u1169\u116e\u1175\u116a\u116b\u117f\u1180\u1181\u1182\u1183\u116c\u0000\u1175\u116b\u0000\u1163\u1164\u1167\u1169\u1175" +
"\u1184\u1185\u1186\u1187\u1188\u0000\u1161\u1162\u1165\u1166\u1168\u116e\u1175\u117c\u1189\u118a\u116f\u1170\u118c\u118d\u1171\u118b\u0000\u1173\u1175\u118b\u1170\u0000\u1161\u1165\u1166\u1167\u1168\u116e\u1175\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u0000\u116e\u1173\u1175\u1195\u1196\u1174\u0000\u116e\u1197\u0000\u1161\u1163\u1169\u116e\u1173\u119e\u1198\u1199\u119a\u119b\u119c\u119d\u0000\u1165\u116e\u1175\u119e\u119f\u11a0" +
"\u11a1\u11a2\u0000\u11a8\u11af\u11ba\u11e7\u11a9\u11c3\u11aa\u11c4\u0000\u11a8\u11c4\u0000\u11a8\u11ae\u11ba\u11bd\u11c0\u11c2\u11eb\u11c5\u11c6\u11c7\u11ac\u11c9\u11ad\u11c8\u0000\u11a8\u11af\u11ca\u11cb\u0000\u11a8\u11aa\u11ab\u11ae\u11af\u11b7\u11b8\u11b9\u11ba\u11bb\u11bf\u11c0\u11c1\u11c2\u11da\u11dd\u11e5\u11e6\u11eb\u11f9\u11b0\u11cc\u11cd\u11ce\u11d0\u11b1\u11b2\u11d3\u11b3\u11d6\u11d8\u11b4\u11b5\u11b6\u11d1\u11d2\u11d4" +
"\u11d5\u11d7\u11d9\u0000\u11ba\u11cc\u0000\u11a8\u11ba\u11d1\u11d2\u0000\u11ba\u11bc\u11c2\u11d3\u11d5\u11d4\u0000\u11ba\u11d6\u0000\u11a8\u11af\u11b8\u11ba\u11bb\u11bc\u11be\u11c2\u11eb\u11da\u11db\u11dc\u11dd\u11de\u11e2\u11e0\u11e1\u11df\u0000\u11af\u11ba\u11bc\u11c1\u11c2\u11e3\u11b9\u11e6\u11e4\u11e5\u0000\u11a8\u11ae\u11af\u11b8\u11ba\u11e7\u11e8\u11e9\u11ea\u11bb\u0000\u11a8\u11a9\u11bc\u11bf\u11ec\u11ed\u11ee\u11ef\u0000" +
"\u11b8\u11bc\u11f3\u11f4\u0000\u11ab\u11af\u11b7\u11b8\u11f5\u11f6\u11f7\u11f8\u0000\u11c2\u11cf\u0000\u11ba\u11de\u0000\u11a8\u11ed\u0000\u11ba\u11eb\u11f1\u11f2\u0000|\u2395\u2345\u2347\u0000-\u2395\u234f\u2350\u0000|\u2395\u2346\u2348\u0000-\u2395\u2356\u2357\u0000/\u2204\u0000_|\u2395\u2359\u234b\u234d\u0000|~\u00a8\u2395\u2352\u236b\u2362\u2354\u0000/\u2209\u0000_\u2377" +
"\u0000/\u220c\u0000_\u00a8\u2229\u22a4\u22a5\u2395\u25cb\u235b\u2364\u235d\u2355\u234e\u233b\u233e\u0000/\u2224\u0000/\u2226\u0000~\u2228\u2395\u2372\u22c4\u2353\u0000~\u2227\u2395\u2371\u22c4\u234c\u0000\u2218\u235d\u0000|\u2366\u0000/\u2241\u0000/\u2244\u0000/\u2249\u0000/\u226d\u0000_\u2395\u2262\u236f\u0000/\u2274\u0000/\u2275\u0000/\u2278\u0000/" +
"\u2279\u0000/\u2280\u0000/\u2281\u0000/\u22e0\u0000/\u22e1\u0000_|\u2286\u2367\u0000_\u2287\u0000/\u2288\u0000/\u2289\u0000/\u22e2\u0000/\u22e3\u0000\u00a8\u00af\u2218\u22a5\u2361\u2351\u2355\u2336\u0000_\u2218\u22a4\u234a\u234e\u2336\u0000/\u22ad\u0000/\u22ae\u0000/\u22af\u0000/\u22ea\u0000/\u22eb\u0000/\u22ec\u0000/\u22ed\u0000_" +
"\u2395\u235a\u233a\u0000_\u2378\u0000_\u2379\u0000_\u2376\u0000'/:<=>?\\\u00f7\u2190\u2191\u2192\u2193\u2206\u2207\u2218\u2227\u2228\u2260\u22c4\u25cb\u235e\u2341\u2360\u2343\u2338\u2344\u2370\u2342\u2339\u2347\u2350\u2348\u2357\u234d\u2354\u233b\u2353\u234c\u236f\u233a\u233c\u0000*-.\\_|\u00a8\u2218\u2395\u235f\u2296\u2299\u2349\u235c\u233d\u2365" +
"\u233e\u233c\u0000/\u2adc").toCharArray();
public static final char[] edges =
("\u00e7\u00e7\u00f8\u0155\u01aa\u01b9\u01bc\u0285\u0564\u0569\u057e\u058b\u05d2\u0615\u0690\u06d3\u06da\u06f1\u06f8\u0701\u0704\u0709\u070c\u070f\u071a\u073b\u0776\u07ab\u07d4\u081f\u0842\u0847\u085e\u0869\u0888\u0891\u08a0\u08a3\u08c2\u08c7\u08d8\u08db\u08ee\u0917\u0922\u0925\u0934\u0949\u095c\u09a3\u09a6\u09ab\u09ae\u09bb\u09c4\u09c7\u09d0\u09d3\u0a86\u0b35\u0b9e\u0bbf\u0bec\u0c47\u0c56\u0c75\u0c82\u0c8f\u0c92\u0caf\u0cb4\u0cc3" +
"\u0cca\u0cd5\u0d08\u0d0f\u0d18\u0d2d\u0d3e\u0d73\u0db8\u0dbb\u0dc0\u0dcd\u0dd4\u0dd7\u0dfa\u0e53\u0e80\u0eed\u0fb0\u0fdd\u0fe0\u0fe5\u0fe8\u0feb\u0fee\u0ff5\u100c\u1013\u1018\u1025\u102a\u1031\u1036\u1039\u103c\u103f\u1042\u1049\u104c\u1051\u1054\u105b\u105e\u1065\u1068\u106b\u106e\u1071\u1074\u1077\u107a\u107d\u1080\u108b\u108e\u1091\u109a\u109f\u10a8\u10ad\u10ce\u10d1\u10f2\u10f5\u110c\u1111\u1116\u111b\u111e\u1129\u112c\u112f" +
"\u1132\u1135\u1138\u113f\u1146\u114f\u1156\u1167\u116a\u1175\u1186\u118b\u119a\u11a1\u11a4\u11b1\u11ba\u11c3\u11c6\u11d5\u11da\u1203\u1206\u120b\u1212\u1215\u1228\u1233\u123e\u1247\u124c\u1255\u1258\u125b\u125e\u1263\u1268\u126d\u1272\u1277\u127a\u1281\u128a\u128d\u1290\u1293\u12a2\u12a5\u12a8\u12af\u12b6\u12b9\u12bc\u12bf\u12c2\u12c5\u12c8\u12cd\u12d0\u12d3\u12d6\u12d9\u12dc\u12df\u12e2\u12e5\u12ea\u12ed\u12f0\u12f3\u12f6\u12f9" +
"\u1302\u1309\u130c\u130f\u1312\u1315\u1318\u131b\u131e\u1323\u1326\u1329\u132c\u1357\u136a\u0009\u00f0\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f7\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u002b\u0123\u0124\u012d\u012e\u012f\u0130\u0131\u0132\u0133\u0134\u0135\u0136\u0137\u0138\u0139\u013a\u013b\u013c\u013d\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u0149\u014a\u014b\u014c\u014d\u014e\u014f\u0150\u0151" +
"\u0152\u0153\u0154\u0001\u0005\u0129\u012a\u012b\u012c\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0025\u017a\u017b\u017c\u017d\u017e\u017f\u0180\u0181\u0182\u0183\u0184\u0185\u0186\u0187\u0188\u0189\u018e\u018f" +
"\u0190\u0191\u0192\u0193\u0194\u0195\u0196\u0197\u0198\u019d\u01a2\u01a3\u01a4\u01a5\u01a6\u01a7\u01a8\u01a9\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u018c\u018d\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u019b\u019c\u0001\u0001\u0003\u01a0\u01a1\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0008\u01b2\u01b3\u01b4\u01b5\u01b6" +
"\u01b7\u01b8\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u01bb\u0001\u0044\u0200\u020b\u020c\u0215\u0216\u021b\u021c\u021d\u021e\u021f\u0220\u0221\u0222\u0223\u0224\u0225\u0226\u0227\u0228\u0229\u022a\u022b\u022c\u022d\u022e\u023b\u0244\u0245\u024a\u024b\u024c\u024d\u024e\u024f\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u0263\u026c\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c" +
"\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0006\u0206\u0207\u0208\u0209\u020a\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0211\u0212\u0213\u0214\u0001\u0001\u0001\u0001\u0001\u0003\u0219\u021a\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0235\u0236\u0237\u0238\u0239\u023a\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0240\u0241\u0242\u0243" +
"\u0001\u0001\u0001\u0001\u0001\u0003\u0248\u0249\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u025f\u0260\u0261\u0262\u0001\u0001\u0001\u0001\u0005\u0268\u0269\u026a\u026b\u0001\u0001\u0001\u0001\u0003\u026f\u0270\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0083\u0308\u0309" +
"\u030a\u030b\u030e\u0339\u0364\u038f\u03ba\u03c1\u03c4\u03c7\u03ca\u03cd\u03d0\u03d3\u03d6\u03d9\u03dc\u03df\u03e2\u03e5\u03e8\u03eb\u03ee\u03f1\u03f4\u03f7\u03fa\u03fd\u0400\u0403\u0406\u0409\u040c\u040f\u0412\u0415\u0418\u041b\u041e\u0421\u0424\u0427\u042a\u042d\u0430\u0433\u0436\u0439\u043c\u043f\u0442\u0445\u0448\u044b\u044e\u0451\u0454\u0457\u045a\u045d\u0460\u0463\u0466\u0469\u0470\u0477\u047e\u0485\u048c\u0493\u049a\u04a1" +
"\u04a8\u04af\u04b6\u04bd\u04c4\u04cb\u04ce\u04d1\u04d4\u04d7\u04da\u04dd\u04e0\u04e3\u04e6\u04e9\u04ec\u04ef\u04f2\u04f5\u04f8\u04fb\u04fe\u0501\u0504\u0507\u050a\u050d\u0510\u0513\u0516\u0519\u051c\u051f\u0522\u0525\u0528\u052b\u052e\u0531\u0534\u0537\u053a\u053d\u0540\u0543\u0546\u0549\u054c\u054f\u0552\u0555\u0558\u055b\u055e\u0561\u0001\u0001\u0001\u0002\u030d\u0001\u000c\u031a\u031b\u031e\u0321\u0324\u0327\u032a\u032d\u0330" +
"\u0333\u0336\u0001\u0002\u031d\u0001\u0002\u0320\u0001\u0002\u0323\u0001\u0002\u0326\u0001\u0002\u0329\u0001\u0002\u032c\u0001\u0002\u032f\u0001\u0002\u0332\u0001\u0002\u0335\u0001\u0002\u0338\u0001\u000c\u0345\u0346\u0349\u034c\u034f\u0352\u0355\u0358\u035b\u035e\u0361\u0001\u0002\u0348\u0001\u0002\u034b\u0001\u0002\u034e\u0001\u0002\u0351\u0001\u0002\u0354\u0001\u0002\u0357\u0001\u0002\u035a\u0001\u0002\u035d\u0001\u0002\u0360" +
"\u0001\u0002\u0363\u0001\u000c\u0370\u0371\u0374\u0377\u037a\u037d\u0380\u0383\u0386\u0389\u038c\u0001\u0002\u0373\u0001\u0002\u0376\u0001\u0002\u0379\u0001\u0002\u037c\u0001\u0002\u037f\u0001\u0002\u0382\u0001\u0002\u0385\u0001\u0002\u0388\u0001\u0002\u038b\u0001\u0002\u038e\u0001\u000c\u039b\u039c\u039f\u03a2\u03a5\u03a8\u03ab\u03ae\u03b1\u03b4\u03b7\u0001\u0002\u039e\u0001\u0002\u03a1\u0001\u0002\u03a4\u0001\u0002\u03a7\u0001" +
"\u0002\u03aa\u0001\u0002\u03ad\u0001\u0002\u03b0\u0001\u0002\u03b3\u0001\u0002\u03b6\u0001\u0002\u03b9\u0001\u0003\u03bd\u03be\u0001\u0002\u03c0\u0001\u0002\u03c3\u0001\u0002\u03c6\u0001\u0002\u03c9\u0001\u0002\u03cc\u0001\u0002\u03cf\u0001\u0002\u03d2\u0001\u0002\u03d5\u0001\u0002\u03d8\u0001\u0002\u03db\u0001\u0002\u03de\u0001\u0002\u03e1\u0001\u0002\u03e4\u0001\u0002\u03e7\u0001\u0002\u03ea\u0001\u0002\u03ed\u0001\u0002\u03f0" +
"\u0001\u0002\u03f3\u0001\u0002\u03f6\u0001\u0002\u03f9\u0001\u0002\u03fc\u0001\u0002\u03ff\u0001\u0002\u0402\u0001\u0002\u0405\u0001\u0002\u0408\u0001\u0002\u040b\u0001\u0002\u040e\u0001\u0002\u0411\u0001\u0002\u0414\u0001\u0002\u0417\u0001\u0002\u041a\u0001\u0002\u041d\u0001\u0002\u0420\u0001\u0002\u0423\u0001\u0002\u0426\u0001\u0002\u0429\u0001\u0002\u042c\u0001\u0002\u042f\u0001\u0002\u0432\u0001\u0002\u0435\u0001\u0002\u0438" +
"\u0001\u0002\u043b\u0001\u0002\u043e\u0001\u0002\u0441\u0001\u0002\u0444\u0001\u0002\u0447\u0001\u0002\u044a\u0001\u0002\u044d\u0001\u0002\u0450\u0001\u0002\u0453\u0001\u0002\u0456\u0001\u0002\u0459\u0001\u0002\u045c\u0001\u0002\u045f\u0001\u0002\u0462\u0001\u0002\u0465\u0001\u0002\u0468\u0001\u0003\u046c\u046d\u0001\u0002\u046f\u0001\u0003\u0473\u0474\u0001\u0002\u0476\u0001\u0003\u047a\u047b\u0001\u0002\u047d\u0001\u0003\u0481" +
"\u0482\u0001\u0002\u0484\u0001\u0003\u0488\u0489\u0001\u0002\u048b\u0001\u0003\u048f\u0490\u0001\u0002\u0492\u0001\u0003\u0496\u0497\u0001\u0002\u0499\u0001\u0003\u049d\u049e\u0001\u0002\u04a0\u0001\u0003\u04a4\u04a5\u0001\u0002\u04a7\u0001\u0003\u04ab\u04ac\u0001\u0002\u04ae\u0001\u0003\u04b2\u04b3\u0001\u0002\u04b5\u0001\u0003\u04b9\u04ba\u0001\u0002\u04bc\u0001\u0003\u04c0\u04c1\u0001\u0002\u04c3\u0001\u0003\u04c7\u04c8\u0001" +
"\u0002\u04ca\u0001\u0002\u04cd\u0001\u0002\u04d0\u0001\u0002\u04d3\u0001\u0002\u04d6\u0001\u0002\u04d9\u0001\u0002\u04dc\u0001\u0002\u04df\u0001\u0002\u04e2\u0001\u0002\u04e5\u0001\u0002\u04e8\u0001\u0002\u04eb\u0001\u0002\u04ee\u0001\u0002\u04f1\u0001\u0002\u04f4\u0001\u0002\u04f7\u0001\u0002\u04fa\u0001\u0002\u04fd\u0001\u0002\u0500\u0001\u0002\u0503\u0001\u0002\u0506\u0001\u0002\u0509\u0001\u0002\u050c\u0001\u0002\u050f\u0001" +
"\u0002\u0512\u0001\u0002\u0515\u0001\u0002\u0518\u0001\u0002\u051b\u0001\u0002\u051e\u0001\u0002\u0521\u0001\u0002\u0524\u0001\u0002\u0527\u0001\u0002\u052a\u0001\u0002\u052d\u0001\u0002\u0530\u0001\u0002\u0533\u0001\u0002\u0536\u0001\u0002\u0539\u0001\u0002\u053c\u0001\u0002\u053f\u0001\u0002\u0542\u0001\u0002\u0545\u0001\u0002\u0548\u0001\u0002\u054b\u0001\u0002\u054e\u0001\u0002\u0551\u0001\u0002\u0554\u0001\u0002\u0557\u0001" +
"\u0002\u055a\u0001\u0002\u055d\u0001\u0002\u0560\u0001\u0002\u0563\u0001\u0003\u0567\u0568\u0001\u0001\u0009\u0572\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u0003\u0575\u0576\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0585\u0586\u0587\u0588\u0589\u058a\u0001\u0001\u0001\u0001\u0001\u0001\u0024\u05af\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05ba\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2" +
"\u05c3\u05c4\u05c5\u05c6\u05c7\u05c8\u05c9\u05ca\u05cb\u05cc\u05cd\u05ce\u05cf\u05d0\u05d1\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u001f\u05f1\u05f2\u05f3\u05f4\u05f5\u05f6\u05fd\u05fe\u05ff\u0600\u0601\u0602\u0603\u0604\u0605\u0606\u0607\u0608\u0609\u060a\u060b" +
"\u060c\u060d\u060e\u060f\u0610\u0611\u0612\u0613\u0614\u0001\u0001\u0001\u0001\u0001\u0004\u05fa\u05fb\u05fc\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0038\u064d\u064e\u0653\u0658\u0659\u065a\u065b\u065c\u065d\u065e\u065f\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u066a\u066b\u066c\u066d\u066e" +
"\u066f\u0670\u0671\u0672\u0673\u0674\u0675\u0676\u0677\u0678\u0679\u067a\u067b\u067c\u067d\u067e\u067f\u0680\u0681\u0682\u0683\u0684\u0685\u0686\u0687\u068c\u068d\u068e\u068f\u0001\u0003\u0651\u0652\u0001\u0001\u0003\u0656\u0657\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u068a\u068b\u0001\u0001\u0001\u0001\u0001\u0001\"\u06b2\u06b3\u06b4\u06b5\u06b6\u06b7\u06b8\u06b9\u06ba\u06bb\u06bc\u06bd\u06be\u06bf\u06c0\u06c1\u06c2\u06c3\u06c4\u06c5\u06c6\u06c7\u06c8\u06c9\u06ca\u06cb\u06cc\u06cd\u06ce\u06cf\u06d0\u06d1\u06d2\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u06d7\u06d8\u06d9\u0001\u0001\u0001\u000b\u06e5\u06e8\u06e9\u06ea\u06eb\u06ec\u06ed\u06ee\u06ef\u06f0\u0002\u06e7\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u06f5\u06f6\u06f7\u0001\u0001\u0001\u0005\u06fd\u06fe\u06ff\u0700\u0001\u0001\u0001\u0001\u0002\u0703\u0001\u0003\u0707\u0708\u0001" +
"\u0001\u0002\u070b\u0001\u0002\u070e\u0001\u0006\u0715\u0716\u0717\u0718\u0719\u0001\u0001\u0001\u0001\u0001\u0011\u072b\u072c\u072d\u072e\u072f\u0730\u0731\u0732\u0733\u0734\u0735\u0736\u0737\u0738\u0739\u073a\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u001e\u0759\u075a\u075b\u075c\u075d\u075e\u075f\u0760\u0761\u0762\u0763\u0764\u0765\u0766\u0767\u0768\u0769\u076a\u076b\u076c" +
"\u076d\u076e\u076f\u0770\u0771\u0772\u0773\u0774\u0775\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u001b\u0791\u0792\u0793\u0794\u0795\u0796\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u07a2\u07a3\u07a4\u07a5\u07a6\u07a7\u07a8\u07a9\u07aa\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0015\u07c0\u07c1\u07c2\u07c3\u07c4\u07c5\u07c6\u07c7\u07c8\u07c9\u07ca\u07cb\u07cc\u07cd\u07ce\u07cf\u07d0\u07d1\u07d2\u07d3\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u001a\u07ee\u07ef\u07f8\u07f9\u07fa\u07fb\u07fc\u07fd\u07fe\u07ff\u0800" +
"\u080d\u080e\u0813\u0814\u0815\u0816\u0817\u0818\u0819\u081a\u081b\u081c\u081d\u081e\u0001\u0005\u07f4\u07f5\u07f6\u07f7\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0807\u0808\u0809\u080a\u080b\u080c\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0811\u0812\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0012\u0831\u0832\u0833\u0834\u0835\u0836\u0837\u0838" +
"\u0839\u083a\u083b\u083c\u083d\u083e\u083f\u0840\u0841\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0845\u0846\u0001\u0001\u000c\u0853\u0854\u0855\u0856\u0857\u0858\u0859\u085a\u085b\u085c\u085d\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u0864\u0865\u0866\u0867\u0868\u0001\u0001\u0001\u0001\u0001\u0010\u0879\u087a\u087b\u087c\u087d\u087e" +
"\u087f\u0880\u0881\u0882\u0883\u0884\u0885\u0886\u0887\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u088d\u088e\u088f\u0890\u0001\u0001\u0001\u0001\u0008\u0899\u089a\u089b\u089c\u089d\u089e\u089f\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u08a2\u0001\u0010\u08b3\u08b4\u08b5\u08b6\u08b7\u08b8\u08b9\u08ba\u08bb\u08bc\u08bd\u08be\u08bf\u08c0\u08c1\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u08c5\u08c6\u0001\u0001\u0009\u08d0\u08d1\u08d2\u08d3\u08d4\u08d5\u08d6\u08d7\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u08da\u0001\n\u08e5\u08e6\u08e7\u08e8\u08e9\u08ea\u08eb\u08ec\u08ed\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0015\u0903\u0904\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090d\u090e\u090f\u0910\u0911\u0912\u0913" +
"\u0914\u0915\u0916\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u091d\u091e\u091f\u0920\u0921\u0001\u0001\u0001\u0001\u0001\u0002\u0924\u0001\u0008\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u093f\u0940\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\n\u0953\u0954\u0955\u0956\u0957\u0958\u0959\u095a\u095b\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u001d\u0979\u0980\u0985\u0986\u0987\u0988\u0989\u098a\u098b\u098c\u098d\u098e\u098f\u0990\u0991\u0992\u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\u099b\u099c\u099d\u099e\u0002\u097b\u0003\u097e\u097f\u0001\u0001\u0003\u0983\u0984\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u09a1\u09a2\u0001\u0001\u0002\u09a5\u0001\u0003\u09a9\u09aa\u0001\u0001\u0002\u09ad\u0001\u0007\u09b5\u09b6\u09b7\u09b8\u09b9\u09ba\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u09c0\u09c1\u09c2\u09c3\u0001\u0001\u0001\u0001\u0002\u09c6\u0001\u0005\u09cc\u09cd\u09ce\u09cf\u0001\u0001\u0001\u0001\u0002\u09d2\u0001\u0042\u0a15\u0a22\u0a23\u0a24" +
"\u0a25\u0a26\u0a27\u0a28\u0a29\u0a2a\u0a2b\u0a2c\u0a2d\u0a2e\u0a2f\u0a30\u0a31\u0a32\u0a33\u0a34\u0a35\u0a36\u0a37\u0a38\u0a39\u0a3a\u0a3b\u0a3c\u0a3d\u0a3e\u0a3f\u0a40\u0a65\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0a70\u0a71\u0a72\u0a73\u0a74\u0a75\u0a76\u0a77\u0a78\u0a79\u0a7a\u0a7b\u0a7c\u0a7d\u0a7e\u0a7f\u0a80\u0a81\u0a82\u0a83\u0a84\u0a85\u0007\u0a1c\u0a1d\u0a1e\u0a1f\u0a20\u0a21\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0013\u0a53\u0a54\u0a55\u0a56\u0a57\u0a58\u0a59\u0a5a\u0a5b\u0a5c\u0a5d\u0a5e\u0a5f\u0a60\u0a61\u0a62\u0a63\u0a64\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0046\u0acc\u0acd\u0ad6\u0ae3\u0ae4\u0ae5\u0ae6\u0ae7\u0ae8\u0af1\u0af2\u0af3\u0af4\u0af5\u0af6\u0af7\u0af8\u0af9\u0afa\u0afb\u0b00\u0b01\u0b02\u0b03\u0b04\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c\u0b0d\u0b0e\u0b0f\u0b10\u0b11\u0b12\u0b13\u0b18" +
"\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24\u0b25\u0b26\u0b27\u0b28\u0b29\u0b2a\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b31\u0b32\u0b33\u0b34\u0001\u0005\u0ad2\u0ad3\u0ad4\u0ad5\u0001\u0001\u0001\u0001\u0007\u0add\u0ade\u0adf\u0ae0\u0ae1\u0ae2\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0aed\u0aee\u0aef\u0af0\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0003\u0afe\u0aff\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0b16\u0b17\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u001f\u0b54\u0b59\u0b62\u0b63\u0b64\u0b65\u0b66\u0b67\u0b68\u0b69" +
"\u0b6a\u0b77\u0b80\u0b81\u0b86\u0b87\u0b88\u0b89\u0b8a\u0b8b\u0b8c\u0b8d\u0b96\u0b97\u0b98\u0b99\u0b9a\u0b9b\u0b9c\u0b9d\u0003\u0b57\u0b58\u0001\u0001\u0005\u0b5e\u0b5f\u0b60\u0b61\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0b71\u0b72\u0b73\u0b74\u0b75\u0b76\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0b7c\u0b7d\u0b7e\u0b7f\u0001\u0001\u0001\u0001\u0001\u0003\u0b84\u0b85\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0005\u0b92\u0b93\u0b94\u0b95\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0011\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9\u0bba\u0bbb\u0bbc\u0bbd\u0bbe\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0011\u0bd0\u0bd5\u0bda\u0bdb\u0bdc\u0bdd\u0bde\u0bdf\u0be0\u0be1\u0be2\u0be3\u0be4\u0be5\u0be6\u0be7" +
"\u0003\u0bd3\u0bd4\u0001\u0001\u0003\u0bd8\u0bd9\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0bea\u0beb\u0001\u0001\u002c\u0c18\u0c1d\u0c1e\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24\u0c25\u0c26\u0c27\u0c28\u0c29\u0c2a\u0c2b\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33\u0c34\u0c35\u0c36\u0c37\u0c38\u0c39\u0c3a\u0c3b\u0c3c\u0c3d\u0c3e\u0c3f\u0c40\u0c41\u0c42\u0c43\u0c44\u0c45\u0c46" +
"\u0003\u0c1b\u0c1c\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0008\u0c4f\u0c50\u0c51\u0c52\u0c53\u0c54\u0c55\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0010\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e" +
"\u0c6f\u0c70\u0c71\u0c72\u0c73\u0c74\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0c7c\u0c7d\u0c7e\u0c7f\u0c80\u0c81\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0c89\u0c8a\u0c8b\u0c8c\u0c8d\u0c8e\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0c91\u0001\u000f\u0ca1\u0ca2\u0ca3\u0ca4\u0ca5\u0ca6\u0ca7\u0ca8\u0ca9\u0caa\u0cab\u0cac\u0cad\u0cae\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0cb2\u0cb3\u0001\u0001\u0008\u0cbc\u0cbd\u0cbe\u0cbf\u0cc0\u0cc1\u0cc2\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0cc7\u0cc8\u0cc9\u0001\u0001\u0001\u0006\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0001\u0001\u0001\u0001\u0001\u001a\u0cef\u0cf0\u0cf1\u0cf2\u0cf3\u0cf4\u0cf5\u0cf6\u0cf7\u0cf8\u0cf9\u0cfa\u0cfb\u0cfc\u0cfd\u0cfe\u0cff\u0d00\u0d01\u0d02\u0d03\u0d04\u0d05\u0d06\u0d07\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0d0c\u0d0d\u0d0e\u0001\u0001\u0001\u0005\u0d14\u0d15\u0d16\u0d17\u0001\u0001\u0001\u0001\u000b\u0d23\u0d24\u0d25\u0d26\u0d27\u0d28\u0d29\u0d2a\u0d2b\u0d2c\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0009\u0d36\u0d37\u0d38\u0d39\u0d3a\u0d3b\u0d3c\u0d3d\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u001b\u0d59\u0d5a\u0d5b\u0d5c\u0d5d\u0d5e\u0d5f\u0d60\u0d61\u0d62\u0d63\u0d64\u0d65\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0d70\u0d71\u0d72\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0023\u0d96\u0d97\u0d98\u0d99\u0d9a\u0d9b\u0d9c\u0d9d\u0d9e\u0d9f\u0da0\u0da1" +
"\u0da2\u0da3\u0da4\u0da5\u0da6\u0da7\u0da8\u0da9\u0daa\u0dab\u0dac\u0dad\u0dae\u0daf\u0db0\u0db1\u0db2\u0db3\u0db4\u0db5\u0db6\u0db7\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0dba\u0001\u0003\u0dbe\u0dbf\u0001\u0001\u0007\u0dc7\u0dc8\u0dc9\u0dca\u0dcb\u0dcc\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0004\u0dd1\u0dd2\u0dd3\u0001\u0001\u0001\u0002\u0dd6\u0001\u0012\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df2\u0df3\u0df4\u0df5\u0df6\u0df7\u0df8\u0df9\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0021\u0e1b\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e3a\u0e3b\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e46\u0e47" +
"\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0e4f\u0e50\u0e51\u0e52\u0005\u0e20\u0e21\u0e22\u0e23\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0e3e\u0e3f\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0017\u0e6a\u0e6b\u0e6c\u0e6d" +
"\u0e6e\u0e6f\u0e70\u0e71\u0e72\u0e73\u0e74\u0e75\u0e76\u0e77\u0e78\u0e79\u0e7a\u0e7b\u0e7c\u0e7d\u0e7e\u0e7f\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0025\u0ea5\u0eae\u0ebb\u0ec4\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0ece\u0ecf\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0edc\u0edd\u0ede\u0edf\u0ee0\u0ee1\u0ee2\u0ee3\u0ee4\u0ee5\u0ee6\u0ee7" +
"\u0ee8\u0ee9\u0eea\u0eeb\u0eec\u0005\u0eaa\u0eab\u0eac\u0ead\u0001\u0001\u0001\u0001\u0007\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\u0eba\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0ec0\u0ec1\u0ec2\u0ec3\u0001\u0001\u0001\u0001\u0003\u0ec7\u0ec8\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0eda\u0edb\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0040\u0f2d\u0f36\u0f3f\u0f44\u0f49\u0f4a\u0f4b\u0f4c\u0f4d\u0f4e\u0f4f\u0f50\u0f51\u0f52\u0f53\u0f54\u0f55\u0f56\u0f57\u0f58\u0f59\u0f5a\u0f67\u0f70\u0f71\u0f76\u0f77\u0f78\u0f79\u0f7a\u0f7b\u0f7c\u0f7d\u0f7e\u0f7f\u0f80\u0f81\u0f82\u0f83\u0f84\u0f85\u0f86\u0f8f\u0f98\u0f9d\u0f9e\u0f9f\u0fa0\u0fa1\u0fa2\u0fa3\u0fa4\u0fa5\u0fa6\u0fa7\u0fa8\u0fa9\u0faa\u0fab\u0fac\u0fad\u0fae\u0faf\u0005\u0f32\u0f33" +
"\u0f34\u0f35\u0001\u0001\u0001\u0001\u0005\u0f3b\u0f3c\u0f3d\u0f3e\u0001\u0001\u0001\u0001\u0003\u0f42\u0f43\u0001\u0001\u0003\u0f47\u0f48\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0f61\u0f62\u0f63\u0f64\u0f65\u0f66\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0f6c\u0f6d\u0f6e\u0f6f\u0001\u0001\u0001\u0001\u0001\u0003\u0f74\u0f75\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0f8b\u0f8c\u0f8d\u0f8e\u0001\u0001\u0001\u0001\u0005\u0f94\u0f95\u0f96\u0f97\u0001\u0001\u0001\u0001\u0003\u0f9b\u0f9c\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0017\u0fc7\u0fc8\u0fc9\u0fca\u0fcb\u0fcc\u0fcd\u0fce\u0fcf\u0fd0\u0fd1\u0fd2\u0fd3\u0fd4\u0fd5" +
"\u0fd6\u0fd7\u0fd8\u0fd9\u0fda\u0fdb\u0fdc\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0fdf\u0001\u0003\u0fe3\u0fe4\u0001\u0001\u0002\u0fe7\u0001\u0002\u0fea\u0001\u0002\u0fed\u0001\u0004\u0ff2\u0ff3\u0ff4\u0001\u0001\u0001\u000c\u1001\u1002\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100a\u100b\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0001\u0004\u1010\u1011\u1012\u0001\u0001\u0001\u0003\u1016\u1017\u0001\u0001\u0007\u101f\u1020\u1021\u1022\u1023\u1024\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1028\u1029\u0001\u0001\u0004\u102e\u102f\u1030\u0001\u0001\u0001\u0003\u1034\u1035\u0001\u0001\u0002\u1038\u0001\u0002\u103b\u0001\u0002\u103e\u0001\u0002\u1041\u0001\u0004\u1046\u1047\u1048\u0001\u0001\u0001\u0002\u104b\u0001\u0003\u104f\u1050\u0001" +
"\u0001\u0002\u1053\u0001\u0004\u1058\u1059\u105a\u0001\u0001\u0001\u0002\u105d\u0001\u0004\u1062\u1063\u1064\u0001\u0001\u0001\u0002\u1067\u0001\u0002\u106a\u0001\u0002\u106d\u0001\u0002\u1070\u0001\u0002\u1073\u0001\u0002\u1076\u0001\u0002\u1079\u0001\u0002\u107c\u0001\u0002\u107f\u0001\u0006\u1086\u1087\u1088\u1089\u108a\u0001\u0001\u0001\u0001\u0001\u0002\u108d\u0001\u0002\u1090\u0001\u0005\u1096\u1097\u1098\u1099\u0001\u0001" +
"\u0001\u0001\u0003\u109d\u109e\u0001\u0001\u0005\u10a4\u10a5\u10a6\u10a7\u0001\u0001\u0001\u0001\u0003\u10ab\u10ac\u0001\u0001\u0011\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u10c6\u10c7\u10c8\u10c9\u10ca\u10cb\u10cc\u10cd\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u10d0\u0001\u0011\u10e2\u10e3\u10e4\u10e5\u10e6\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef" +
"\u10f0\u10f1\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u10f4\u0001\u000c\u1101\u1102\u1103\u1104\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u110f\u1110\u0001\u0001\u0003\u1114\u1115\u0001\u0001\u0003\u1119\u111a\u0001\u0001\u0002\u111d\u0001\u0006\u1124\u1125\u1126\u1127\u1128\u0001\u0001\u0001\u0001" +
"\u0001\u0002\u112b\u0001\u0002\u112e\u0001\u0002\u1131\u0001\u0002\u1134\u0001\u0002\u1137\u0001\u0004\u113c\u113d\u113e\u0001\u0001\u0001\u0004\u1143\u1144\u1145\u0001\u0001\u0001\u0005\u114b\u114c\u114d\u114e\u0001\u0001\u0001\u0001\u0004\u1153\u1154\u1155\u0001\u0001\u0001\u0009\u115f\u1160\u1161\u1162\u1163\u1164\u1165\u1166\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1169\u0001\u0006\u1170\u1171\u1172\u1173\u1174" +
"\u0001\u0001\u0001\u0001\u0001\u0009\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u1185\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1189\u118a\u0001\u0001\u0008\u1193\u1194\u1195\u1196\u1197\u1198\u1199\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u119e\u119f\u11a0\u0001\u0001\u0001\u0002\u11a3\u0001\u0007\u11ab\u11ac\u11ad\u11ae\u11af\u11b0\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u11b6\u11b7\u11b8\u11b9\u0001\u0001" +
"\u0001\u0001\u0005\u11bf\u11c0\u11c1\u11c2\u0001\u0001\u0001\u0001\u0002\u11c5\u0001\u0008\u11ce\u11cf\u11d0\u11d1\u11d2\u11d3\u11d4\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u11d8\u11d9\u0001\u0001\u0015\u11ef\u11f0\u11f1\u11f2\u11f3\u11f4\u11f5\u11f6\u11f7\u11f8\u11f9\u11fa\u11fb\u11fc\u11fd\u11fe\u11ff\u1200\u1201\u1202\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0001\u0002\u1205\u0001\u0003\u1209\u120a\u0001\u0001\u0004\u120f\u1210\u1211\u0001\u0001\u0001\u0002\u1214\u0001\n\u121f\u1220\u1221\u1222\u1223\u1224\u1225\u1226\u1227\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u122e\u122f\u1230\u1231\u1232\u0001\u0001\u0001\u0001\u0001\u0006\u1239\u123a\u123b\u123c\u123d\u0001\u0001\u0001\u0001\u0001\u0005\u1243\u1244\u1245\u1246\u0001\u0001\u0001\u0001\u0003" +
"\u124a\u124b\u0001\u0001\u0005\u1251\u1252\u1253\u1254\u0001\u0001\u0001\u0001\u0002\u1257\u0001\u0002\u125a\u0001\u0002\u125d\u0001\u0003\u1261\u1262\u0001\u0001\u0003\u1266\u1267\u0001\u0001\u0003\u126b\u126c\u0001\u0001\u0003\u1270\u1271\u0001\u0001\u0003\u1275\u1276\u0001\u0001\u0002\u1279\u0001\u0004\u127e\u127f\u1280\u0001\u0001\u0001\u0005\u1286\u1287\u1288\u1289\u0001\u0001\u0001\u0001\u0002\u128c\u0001\u0002\u128f\u0001" +
"\u0002\u1292\u0001\u0008\u129b\u129c\u129d\u129e\u129f\u12a0\u12a1\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u12a4\u0001\u0002\u12a7\u0001\u0004\u12ac\u12ad\u12ae\u0001\u0001\u0001\u0004\u12b3\u12b4\u12b5\u0001\u0001\u0001\u0002\u12b8\u0001\u0002\u12bb\u0001\u0002\u12be\u0001\u0002\u12c1\u0001\u0002\u12c4\u0001\u0002\u12c7\u0001\u0003\u12cb\u12cc\u0001\u0001\u0002\u12cf\u0001\u0002\u12d2\u0001\u0002\u12d5\u0001\u0002\u12d8" +
"\u0001\u0002\u12db\u0001\u0002\u12de\u0001\u0002\u12e1\u0001\u0002\u12e4\u0001\u0003\u12e8\u12e9\u0001\u0001\u0002\u12ec\u0001\u0002\u12ef\u0001\u0002\u12f2\u0001\u0002\u12f5\u0001\u0002\u12f8\u0001\u0005\u12fe\u12ff\u1300\u1301\u0001\u0001\u0001\u0001\u0004\u1306\u1307\u1308\u0001\u0001\u0001\u0002\u130b\u0001\u0002\u130e\u0001\u0002\u1311\u0001\u0002\u1314\u0001\u0002\u1317\u0001\u0002\u131a\u0001\u0002\u131d\u0001\u0003\u1321" +
"\u1322\u0001\u0001\u0002\u1325\u0001\u0002\u1328\u0001\u0002\u132b\u0001\u0016\u1342\u1343\u1344\u1345\u1346\u1347\u1348\u1349\u134a\u134b\u134c\u134d\u134e\u134f\u1350\u1351\u1352\u1353\u1354\u1355\u1356\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\n\u1361\u1362\u1363\u1364\u1365\u1366\u1367\u1368\u1369\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
"\u0001\u0001\u0002\u136c\u0001").toCharArray();
}

View File

@@ -209,6 +209,9 @@ public final class Config
// first iteration then automatically added. // first iteration then automatically added.
final Map<KeyValue, KeyboardData.PreferredPos> extra_keys = new HashMap<KeyValue, KeyboardData.PreferredPos>(); final Map<KeyValue, KeyboardData.PreferredPos> extra_keys = new HashMap<KeyValue, KeyboardData.PreferredPos>();
final Set<KeyValue> remove_keys = new HashSet<KeyValue>(); final Set<KeyValue> remove_keys = new HashSet<KeyValue>();
// Make sure the config key is accessible to avoid being locked in a custom
// layout.
extra_keys.put(KeyValue.getKeyByName("config"), KeyboardData.PreferredPos.ANYWHERE);
extra_keys.putAll(extra_keys_param); extra_keys.putAll(extra_keys_param);
extra_keys.putAll(extra_keys_custom); extra_keys.putAll(extra_keys_custom);
if (extra_keys_subtype != null) if (extra_keys_subtype != null)
@@ -220,9 +223,11 @@ public final class Config
extra_keys_subtype.compute(extra_keys, extra_keys_subtype.compute(extra_keys,
new ExtraKeys.Query(kw.script, present)); new ExtraKeys.Query(kw.script, present));
} }
boolean number_row = this.number_row && !show_numpad; KeyboardData.Row number_row = null;
if (number_row) if (this.number_row && !show_numpad)
remove_keys.addAll(KeyboardData.number_row.getKeys(0).keySet()); number_row = modify_number_row(KeyboardData.number_row, kw);
if (number_row != null)
remove_keys.addAll(number_row.getKeys(0).keySet());
kw = kw.mapKeys(new KeyboardData.MapKeyValues() { kw = kw.mapKeys(new KeyboardData.MapKeyValues() {
public KeyValue apply(KeyValue key, boolean localized) public KeyValue apply(KeyValue key, boolean localized)
{ {
@@ -275,8 +280,8 @@ public final class Config
}); });
if (show_numpad) if (show_numpad)
kw = kw.addNumPad(modify_numpad(KeyboardData.num_pad, kw)); kw = kw.addNumPad(modify_numpad(KeyboardData.num_pad, kw));
if (number_row) if (number_row != null)
kw = kw.addNumberRow(); kw = kw.addTopRow(number_row);
if (extra_keys.size() > 0) if (extra_keys.size() > 0)
kw = kw.addExtraKeys(extra_keys.entrySet().iterator()); kw = kw.addExtraKeys(extra_keys.entrySet().iterator());
return kw; return kw;
@@ -315,7 +320,7 @@ public final class Config
c = inverse_numpad_char(c); c = inverse_numpad_char(c);
String modified = map_digit.apply(c); String modified = map_digit.apply(c);
if (modified != null) // Was modified by script if (modified != null) // Was modified by script
return key.withSymbol(modified); return KeyValue.makeStringKey(modified);
if (prev_c != c) // Was inverted if (prev_c != c) // Was inverted
return key.withChar(c); return key.withChar(c);
break; break;
@@ -325,12 +330,10 @@ public final class Config
}); });
} }
/** Modify the pin entry layout. [main_kw] is used to map the digits into the static KeyboardData.MapKeyValues numpad_script_map(String numpad_script)
same script. */
public KeyboardData modify_pinentry(KeyboardData kw, KeyboardData main_kw)
{ {
final KeyModifier.Map_char map_digit = KeyModifier.modify_numpad_script(main_kw.numpad_script); final KeyModifier.Map_char map_digit = KeyModifier.modify_numpad_script(numpad_script);
return kw.mapKeys(new KeyboardData.MapKeyValues() { return new KeyboardData.MapKeyValues() {
public KeyValue apply(KeyValue key, boolean localized) public KeyValue apply(KeyValue key, boolean localized)
{ {
switch (key.getKind()) switch (key.getKind())
@@ -338,12 +341,26 @@ public final class Config
case Char: case Char:
String modified = map_digit.apply(key.getChar()); String modified = map_digit.apply(key.getChar());
if (modified != null) if (modified != null)
return key.withSymbol(modified); return KeyValue.makeStringKey(modified);
break; break;
} }
return key; return key;
} }
}); };
}
/** Modify the pin entry layout. [main_kw] is used to map the digits into the
same script. */
public KeyboardData modify_pinentry(KeyboardData kw, KeyboardData main_kw)
{
return kw.mapKeys(numpad_script_map(main_kw.numpad_script));
}
/** Modify the number row according to [main_kw]'s script. */
public KeyboardData.Row modify_number_row(KeyboardData.Row row,
KeyboardData main_kw)
{
return row.mapKeys(numpad_script_map(main_kw.numpad_script));
} }
private float get_dip_pref(DisplayMetrics dm, String pref_name, float def) private float get_dip_pref(DisplayMetrics dm, String pref_name, float def)

View File

@@ -69,6 +69,9 @@ public final class KeyEventHandler implements Config.IKeyEventHandler
case META: case META:
_autocap.stop(); _autocap.stop();
break; break;
case COMPOSE_PENDING:
KeyModifier.set_compose_pending(0);
break;
} }
break; break;
default: break; default: break;
@@ -91,6 +94,10 @@ public final class KeyEventHandler implements Config.IKeyEventHandler
case Keyevent: send_key_down_up(key.getKeyevent()); break; case Keyevent: send_key_down_up(key.getKeyevent()); break;
case Modifier: break; case Modifier: break;
case Editing: handle_editing_key(key.getEditing()); break; case Editing: handle_editing_key(key.getEditing()); break;
case Compose_pending:
KeyModifier.set_compose_pending(key.getPendingCompose());
_recv.set_compose_pending(true);
break;
} }
update_meta_state(old_mods); update_meta_state(old_mods);
} }
@@ -293,6 +300,7 @@ public final class KeyEventHandler implements Config.IKeyEventHandler
{ {
public void handle_event_key(KeyValue.Event ev); public void handle_event_key(KeyValue.Event ev);
public void set_shift_state(boolean state, boolean lock); public void set_shift_state(boolean state, boolean lock);
public void set_compose_pending(boolean pending);
public InputConnection getCurrentInputConnection(); public InputConnection getCurrentInputConnection();
} }

View File

@@ -10,6 +10,10 @@ public final class KeyModifier
private static HashMap<KeyValue, HashMap<Pointers.Modifiers, KeyValue>> _cache = private static HashMap<KeyValue, HashMap<Pointers.Modifiers, KeyValue>> _cache =
new HashMap<KeyValue, HashMap<Pointers.Modifiers, KeyValue>>(); new HashMap<KeyValue, HashMap<Pointers.Modifiers, KeyValue>>();
/** The current compose state. Whether a compose is pending is signaled by
the [COMPOSE_PENDING] modifier. */
static int _compose_pending = -1;
/** Modify a key according to modifiers. */ /** Modify a key according to modifiers. */
public static KeyValue modify(KeyValue k, Pointers.Modifiers mods) public static KeyValue modify(KeyValue k, Pointers.Modifiers mods)
{ {
@@ -27,7 +31,11 @@ public final class KeyModifier
ks.put(mods, r); ks.put(mods, r);
} }
/* Keys with an empty string are placeholder keys. */ /* Keys with an empty string are placeholder keys. */
return (r.getString().length() == 0) ? null : r; if (r.getString().length() == 0)
return null;
if (mods.has(KeyValue.Modifier.COMPOSE_PENDING))
r = ComposeKey.apply(_compose_pending, r);
return r;
} }
public static KeyValue modify(KeyValue k, KeyValue.Modifier mod) public static KeyValue modify(KeyValue k, KeyValue.Modifier mod)
@@ -95,10 +103,16 @@ public final class KeyModifier
case "bengali": return map_char_numpad_bengali; case "bengali": return map_char_numpad_bengali;
case "devanagari": return map_char_numpad_devanagari; case "devanagari": return map_char_numpad_devanagari;
case "persian": return map_char_numpad_persian; case "persian": return map_char_numpad_persian;
case "gujarati": return map_char_numpad_gujarati;
default: return map_char_none; default: return map_char_none;
} }
} }
public static void set_compose_pending(int state)
{
_compose_pending = state;
}
private static KeyValue apply_map_char(KeyValue k, Map_char map) private static KeyValue apply_map_char(KeyValue k, Map_char map)
{ {
switch (k.getKind()) switch (k.getKind())
@@ -490,6 +504,31 @@ public final class KeyModifier
has the inconvenient of swapping i and ı on the keyboard. */ has the inconvenient of swapping i and ı on the keyboard. */
case 'ı': return 'İ'; case 'ı': return 'İ';
case '₹': return '₨'; case '₹': return '₨';
// Gujarati alternate characters
case 'અ': return 'આ';
case 'ઇ': return 'ઈ';
case 'િ': return 'ી';
case 'ઉ': return 'ઊ';
case 'ુ': return 'ૂ';
case 'એ': return 'ઐ';
case 'ે': return 'ૈ';
case 'ઓ': return 'ઔ';
case 'ો': return 'ૌ';
case 'ક': return 'ખ';
case 'ગ': return 'ઘ';
case 'ચ': return 'છ';
case 'જ': return 'ઝ';
case 'ટ': return 'ઠ';
case 'ડ': return 'ઢ';
case 'ન': return 'ણ';
case 'ત': return 'થ';
case 'દ': return 'ધ';
case 'પ': return 'ફ';
case 'બ': return 'ભ';
case 'મ': return 'ં';
case 'લ': return 'ળ';
case 'સ': return 'શ';
case 'હ': return '';
default: return c; default: return c;
} }
} }
@@ -1085,4 +1124,25 @@ public final class KeyModifier
} }
} }
}; };
private static final Map_char map_char_numpad_gujarati =
new Map_char() {
public String apply(char c)
{
switch (c)
{
case '0': return "";
case '1': return "";
case '2': return "";
case '3': return "";
case '4': return "";
case '5': return "";
case '6': return "";
case '7': return "";
case '8': return "";
case '9': return "";
default: return null;
}
}
};
} }

View File

@@ -26,6 +26,7 @@ public final class KeyValue
// Must be evaluated in the reverse order of their values. // Must be evaluated in the reverse order of their values.
public static enum Modifier public static enum Modifier
{ {
COMPOSE_PENDING,
SHIFT, SHIFT,
CTRL, CTRL,
ALT, ALT,
@@ -88,31 +89,42 @@ public final class KeyValue
public static enum Kind public static enum Kind
{ {
Char, String, Keyevent, Event, Modifier, Editing, Placeholder Char, String, Keyevent, Event, Modifier, Editing, Placeholder,
Compose_pending
} }
private static final int FLAGS_OFFSET = 19;
private static final int KIND_OFFSET = 28;
// Behavior flags. // Behavior flags.
public static final int FLAG_LATCH = (1 << 20); public static final int FLAG_LATCH = (1 << FLAGS_OFFSET << 0);
public static final int FLAG_LOCK = (1 << 21); public static final int FLAG_LOCK = (1 << FLAGS_OFFSET << 1);
// Special keys are not repeated and don't clear latched modifiers. // Special keys are not repeated and don't clear latched modifiers.
public static final int FLAG_SPECIAL = (1 << 22); public static final int FLAG_SPECIAL = (1 << FLAGS_OFFSET << 2);
// Free flag: (1 << 23); // Whether the symbol should be greyed out. For example, keys that are not
// part of the pending compose sequence.
public static final int FLAG_GREYED = (1 << FLAGS_OFFSET << 3);
// Rendering flags. // Rendering flags.
public static final int FLAG_KEY_FONT = (1 << 24); // special font file public static final int FLAG_KEY_FONT = (1 << FLAGS_OFFSET << 4); // special font file
public static final int FLAG_SMALLER_FONT = (1 << 25); // 25% smaller symbols public static final int FLAG_SMALLER_FONT = (1 << FLAGS_OFFSET << 5); // 25% smaller symbols
public static final int FLAG_SECONDARY = (1 << 26); // dimmer public static final int FLAG_SECONDARY = (1 << FLAGS_OFFSET << 6); // dimmer
// Used by [Pointers]. // Used by [Pointers].
public static final int FLAG_LOCKED = (1 << 28); public static final int FLAG_LOCKED = (1 << FLAGS_OFFSET << 7);
public static final int FLAG_FAKE_PTR = (1 << 29); public static final int FLAG_FAKE_PTR = (1 << FLAGS_OFFSET << 8);
// Ranges for the different components // Ranges for the different components
private static final int FLAGS_BITS = (0b111111111 << 20); // 9 bits wide private static final int FLAGS_BITS =
private static final int KIND_BITS = (0b111 << 29); // 3 bits wide FLAG_LATCH | FLAG_LOCK | FLAG_SPECIAL | FLAG_GREYED | FLAG_KEY_FONT |
FLAG_SMALLER_FONT | FLAG_SECONDARY | FLAG_LOCKED | FLAG_FAKE_PTR;
private static final int KIND_BITS = (0b1111 << KIND_OFFSET); // 4 bits wide
private static final int VALUE_BITS = ~(FLAGS_BITS | KIND_BITS); // 20 bits wide private static final int VALUE_BITS = ~(FLAGS_BITS | KIND_BITS); // 20 bits wide
static static
{ {
check((FLAGS_BITS & KIND_BITS) == 0); // No overlap check((FLAGS_BITS & KIND_BITS) == 0); // No overlap
check((FLAGS_BITS | KIND_BITS | VALUE_BITS) == ~0); // No holes check((FLAGS_BITS | KIND_BITS | VALUE_BITS) == ~0); // No holes
// No kind is out of range
check((((Kind.values().length - 1) << KIND_OFFSET) & ~KIND_BITS) == 0);
} }
private final String _symbol; private final String _symbol;
@@ -122,7 +134,7 @@ public final class KeyValue
public Kind getKind() public Kind getKind()
{ {
return Kind.values()[(_code & KIND_BITS) >>> 29]; return Kind.values()[(_code & KIND_BITS) >>> KIND_OFFSET];
} }
public int getFlags() public int getFlags()
@@ -130,9 +142,9 @@ public final class KeyValue
return (_code & FLAGS_BITS); return (_code & FLAGS_BITS);
} }
public boolean hasFlags(int has) public boolean hasFlagsAny(int has)
{ {
return ((_code & has) == has); return ((_code & has) != 0);
} }
/** The string to render on the keyboard. /** The string to render on the keyboard.
@@ -172,11 +184,18 @@ public final class KeyValue
return Editing.values()[(_code & VALUE_BITS)]; return Editing.values()[(_code & VALUE_BITS)];
} }
/** Defined only when [getKind() == Kind.Placeholder]. */
public Placeholder getPlaceholder() public Placeholder getPlaceholder()
{ {
return Placeholder.values()[(_code & VALUE_BITS)]; return Placeholder.values()[(_code & VALUE_BITS)];
} }
/** Defined only when [getKind() == Kind.Compose_pending]. */
public int getPendingCompose()
{
return (_code & VALUE_BITS);
}
/* Update the char and the symbol. */ /* Update the char and the symbol. */
public KeyValue withChar(char c) public KeyValue withChar(char c)
{ {
@@ -220,16 +239,13 @@ public final class KeyValue
public KeyValue(String s, int kind, int value, int flags) public KeyValue(String s, int kind, int value, int flags)
{ {
check((kind & ~KIND_BITS) == 0);
check((flags & ~FLAGS_BITS) == 0);
check((value & ~VALUE_BITS) == 0);
_symbol = s; _symbol = s;
_code = kind | flags | value; _code = (kind & KIND_BITS) | (flags & FLAGS_BITS) | (value & VALUE_BITS);
} }
public KeyValue(String s, Kind k, int v, int f) public KeyValue(String s, Kind k, int v, int f)
{ {
this(s, (k.ordinal() << 29), v, f); this(s, (k.ordinal() << KIND_OFFSET), v, f);
} }
private static KeyValue charKey(String symbol, char c, int flags) private static KeyValue charKey(String symbol, char c, int flags)
@@ -303,6 +319,17 @@ public final class KeyValue
return makeStringKey(str, 0); return makeStringKey(str, 0);
} }
public static KeyValue makeCharKey(char c)
{
return new KeyValue(String.valueOf(c), Kind.Char, c, 0);
}
public static KeyValue makeComposePending(String symbol, int state, int flags)
{
return new KeyValue(symbol, Kind.Compose_pending, state,
flags | FLAG_SPECIAL);
}
/** Make a key that types a string. A char key is returned for a string of /** Make a key that types a string. A char key is returned for a string of
length 1. */ length 1. */
public static KeyValue makeStringKey(String str, int flags) public static KeyValue makeStringKey(String str, int flags)
@@ -464,6 +491,9 @@ public final class KeyValue
case "textAssist": return editingKey(0xE038, Editing.ASSIST); case "textAssist": return editingKey(0xE038, Editing.ASSIST);
case "autofill": return editingKey("auto", Editing.AUTOFILL); case "autofill": return editingKey("auto", Editing.AUTOFILL);
/* The compose key */
case "compose": return modifierKey(0xE016, Modifier.COMPOSE_PENDING, FLAG_SECONDARY | FLAG_SMALLER_FONT);
/* Placeholder keys */ /* Placeholder keys */
case "removed": return placeholderKey(Placeholder.REMOVED); case "removed": return placeholderKey(Placeholder.REMOVED);
case "f11_placeholder": return placeholderKey(Placeholder.F11); case "f11_placeholder": return placeholderKey(Placeholder.F11);

View File

@@ -440,6 +440,11 @@ public class Keyboard2 extends InputMethodService
_keyboardView.set_shift_state(state, lock); _keyboardView.set_shift_state(state, lock);
} }
public void set_compose_pending(boolean pending)
{
_keyboardView.set_compose_pending(pending);
}
public InputConnection getCurrentInputConnection() public InputConnection getCurrentInputConnection()
{ {
return Keyboard2.this.getCurrentInputConnection(); return Keyboard2.this.getCurrentInputConnection();

View File

@@ -25,6 +25,10 @@ public class Keyboard2View extends View
private KeyValue _shift_kv; private KeyValue _shift_kv;
private KeyboardData.Key _shift_key; private KeyboardData.Key _shift_key;
/** Used to add fake pointers. */
private KeyValue _compose_kv;
private KeyboardData.Key _compose_key;
private Pointers _pointers; private Pointers _pointers;
private Pointers.Modifiers _mods; private Pointers.Modifiers _mods;
@@ -98,6 +102,8 @@ public class Keyboard2View extends View
_shift_kv = _shift_kv.withFlags(_shift_kv.getFlags() | KeyValue.FLAG_LOCK); _shift_kv = _shift_kv.withFlags(_shift_kv.getFlags() | KeyValue.FLAG_LOCK);
_shift_key = _keyboard.findKeyWithValue(_shift_kv); _shift_key = _keyboard.findKeyWithValue(_shift_kv);
} }
_compose_kv = KeyValue.getKeyByName("compose");
_compose_key = _keyboard.findKeyWithValue(_compose_kv);
reset(); reset();
} }
@@ -109,24 +115,24 @@ public class Keyboard2View extends View
invalidate(); invalidate();
} }
/** Called by auto-capitalisation. */ void set_fake_ptr_latched(KeyboardData.Key key, KeyValue kv, boolean latched,
public void set_shift_state(boolean state, boolean lock) boolean lock)
{ {
if (_keyboard == null || _shift_key == null) if (_keyboard == null || key == null)
return; return;
int flags = _pointers.getKeyFlags(_shift_key, _shift_kv); _pointers.set_fake_pointer_state(key, kv, latched, lock);
if (state)
{
if (flags != -1 && !lock)
return; // Don't replace an existing pointer
_pointers.add_fake_pointer(_shift_kv, _shift_key, lock);
} }
else
/** Called by auto-capitalisation. */
public void set_shift_state(boolean latched, boolean lock)
{ {
if ((flags & KeyValue.FLAG_FAKE_PTR) == 0) set_fake_ptr_latched(_shift_key, _shift_kv, latched, lock);
return; // Don't remove locked pointers
_pointers.remove_fake_pointer(_shift_kv, _shift_key);
} }
/** Called from [KeyEventHandler]. */
public void set_compose_pending(boolean pending)
{
set_fake_ptr_latched(_compose_key, _compose_kv, pending, false);
} }
public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods) public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods)
@@ -393,8 +399,12 @@ public class Keyboard2View extends View
return _theme.activatedColor; return _theme.activatedColor;
} }
} }
if (k.hasFlags(KeyValue.FLAG_SECONDARY)) if (k.hasFlagsAny(KeyValue.FLAG_SECONDARY | KeyValue.FLAG_GREYED))
{
if (k.hasFlagsAny(KeyValue.FLAG_GREYED))
return _theme.greyedLabelColor;
return _theme.secondaryLabelColor; return _theme.secondaryLabelColor;
}
return sublabel ? _theme.subLabelColor : _theme.labelColor; return sublabel ? _theme.subLabelColor : _theme.labelColor;
} }
@@ -404,7 +414,7 @@ public class Keyboard2View extends View
if (kv == null) if (kv == null)
return; return;
float textSize = scaleTextSize(kv, _config.labelTextSize, keyH); float textSize = scaleTextSize(kv, _config.labelTextSize, keyH);
Paint p = _theme.labelPaint(kv.hasFlags(KeyValue.FLAG_KEY_FONT)); Paint p = _theme.labelPaint(kv.hasFlagsAny(KeyValue.FLAG_KEY_FONT));
p.setColor(labelColor(kv, isKeyDown, false)); p.setColor(labelColor(kv, isKeyDown, false));
p.setAlpha(_config.labelBrightness); p.setAlpha(_config.labelBrightness);
p.setTextSize(textSize); p.setTextSize(textSize);
@@ -420,7 +430,7 @@ public class Keyboard2View extends View
if (kv == null) if (kv == null)
return; return;
float textSize = scaleTextSize(kv, _config.sublabelTextSize, keyH); float textSize = scaleTextSize(kv, _config.sublabelTextSize, keyH);
Paint p = _theme.subLabelPaint(kv.hasFlags(KeyValue.FLAG_KEY_FONT), a); Paint p = _theme.subLabelPaint(kv.hasFlagsAny(KeyValue.FLAG_KEY_FONT), a);
p.setColor(labelColor(kv, isKeyDown, true)); p.setColor(labelColor(kv, isKeyDown, true));
p.setAlpha(_config.labelBrightness); p.setAlpha(_config.labelBrightness);
p.setTextSize(textSize); p.setTextSize(textSize);
@@ -454,7 +464,7 @@ public class Keyboard2View extends View
private float scaleTextSize(KeyValue k, float rel_size, float keyH) private float scaleTextSize(KeyValue k, float rel_size, float keyH)
{ {
float smaller_font = k.hasFlags(KeyValue.FLAG_SMALLER_FONT) ? 0.75f : 1.f; float smaller_font = k.hasFlagsAny(KeyValue.FLAG_SMALLER_FONT) ? 0.75f : 1.f;
return keyH * rel_size * smaller_font * _config.characterSize; return keyH * rel_size * smaller_font * _config.characterSize;
} }
} }

View File

@@ -132,10 +132,10 @@ public final class KeyboardData
return new KeyboardData(this, extendedRows); return new KeyboardData(this, extendedRows);
} }
public KeyboardData addNumberRow() public KeyboardData addTopRow(Row row)
{ {
ArrayList<Row> rows_ = new ArrayList<Row>(this.rows); ArrayList<Row> rows_ = new ArrayList<Row>(this.rows);
rows_.add(0, number_row.updateWidth(keysWidth)); rows_.add(0, row.updateWidth(keysWidth));
return new KeyboardData(this, rows_); return new KeyboardData(this, rows_);
} }

View File

@@ -85,13 +85,10 @@ public final class Pointers implements Handler.Callback
return ptr.flags; return ptr.flags;
} }
/** Fake pointers are latched and not lockable. */ /** The key must not be already latched . */
public void add_fake_pointer(KeyValue kv, KeyboardData.Key key, boolean locked) void add_fake_pointer(KeyboardData.Key key, KeyValue kv, boolean locked)
{ {
Pointer ptr = getLatched(key, kv); Pointer ptr = new Pointer(-1, key, kv, 0.f, 0.f, Modifiers.EMPTY);
if (ptr != null)
removePtr(ptr); // Already latched, replace pointer.
ptr = new Pointer(-1, key, kv, 0.f, 0.f, Modifiers.EMPTY);
ptr.flags &= ~(KeyValue.FLAG_LATCH | KeyValue.FLAG_LOCK); ptr.flags &= ~(KeyValue.FLAG_LATCH | KeyValue.FLAG_LOCK);
ptr.flags |= KeyValue.FLAG_FAKE_PTR; ptr.flags |= KeyValue.FLAG_FAKE_PTR;
if (locked) if (locked)
@@ -100,13 +97,40 @@ public final class Pointers implements Handler.Callback
_handler.onPointerFlagsChanged(false); _handler.onPointerFlagsChanged(false);
} }
public void remove_fake_pointer(KeyValue kv, KeyboardData.Key key) /** Set whether a key is latched or locked by adding a "fake" pointer, a
pointer that is not due to user interaction.
This is used by auto-capitalisation.
When [lock] is true, [latched] control whether the modifier is locked or disabled.
When [lock] is false, an existing locked pointer is not affected. */
public void set_fake_pointer_state(KeyboardData.Key key, KeyValue kv,
boolean latched, boolean lock)
{ {
Pointer ptr = getLatched(key, kv); Pointer ptr = getLatched(key, kv);
if (ptr != null && (ptr.flags & KeyValue.FLAG_FAKE_PTR) != 0) if (ptr == null)
{
// No existing pointer, latch the key.
if (latched)
add_fake_pointer(key, kv, lock);
}
else if ((ptr.flags & KeyValue.FLAG_FAKE_PTR) != 0)
{} // Key already latched but not by a fake ptr, do nothing.
else if (lock)
{
// Acting on locked modifiers, replace the pointer each time.
removePtr(ptr);
if (latched)
add_fake_pointer(key, kv, lock);
}
else if ((ptr.flags & KeyValue.FLAG_LOCKED) != 0)
{} // Existing ptr is locked but [lock] is false, do not continue.
else if (!latched)
{
// Key is latched by a fake ptr. Unlatch if requested.
removePtr(ptr); removePtr(ptr);
_handler.onPointerFlagsChanged(false); _handler.onPointerFlagsChanged(false);
} }
}
// Receiving events // Receiving events
@@ -391,7 +415,7 @@ public final class Pointers implements Handler.Callback
return true; return true;
} }
// Stop repeating: Special keys // Stop repeating: Special keys
if (kv.hasFlags(KeyValue.FLAG_SPECIAL)) if (kv.hasFlagsAny(KeyValue.FLAG_SPECIAL))
return false; return false;
_handler.onPointerHold(kv, ptr.modifiers); _handler.onPointerHold(kv, ptr.modifiers);
return true; return true;

View File

@@ -17,6 +17,7 @@ public class Theme
public final int labelColor; public final int labelColor;
public final int subLabelColor; public final int subLabelColor;
public final int secondaryLabelColor; public final int secondaryLabelColor;
public final int greyedLabelColor;
public final float keyBorderRadius; public final float keyBorderRadius;
public final float keyBorderWidth; public final float keyBorderWidth;
@@ -48,8 +49,10 @@ public class Theme
activatedColor = s.getColor(R.styleable.keyboard_colorLabelActivated, 0); activatedColor = s.getColor(R.styleable.keyboard_colorLabelActivated, 0);
lockedColor = s.getColor(R.styleable.keyboard_colorLabelLocked, 0); lockedColor = s.getColor(R.styleable.keyboard_colorLabelLocked, 0);
subLabelColor = s.getColor(R.styleable.keyboard_colorSubLabel, 0); subLabelColor = s.getColor(R.styleable.keyboard_colorSubLabel, 0);
float secondaryLightOffset = s.getFloat(R.styleable.keyboard_secondaryLightOffset, 1.f); secondaryLabelColor = adjustLight(labelColor,
secondaryLabelColor = adjustLight(labelColor, secondaryLightOffset); s.getFloat(R.styleable.keyboard_secondaryDimming, 0.25f));
greyedLabelColor = adjustLight(labelColor,
s.getFloat(R.styleable.keyboard_greyedDimming, 0.5f));
keyBorderRadius = s.getDimension(R.styleable.keyboard_keyBorderRadius, 0); keyBorderRadius = s.getDimension(R.styleable.keyboard_keyBorderRadius, 0);
keyBorderWidth = s.getDimension(R.styleable.keyboard_keyBorderWidth, 0); keyBorderWidth = s.getDimension(R.styleable.keyboard_keyBorderWidth, 0);
keyBorderWidthActivated = s.getDimension(R.styleable.keyboard_keyBorderWidthActivated, 0); keyBorderWidthActivated = s.getDimension(R.styleable.keyboard_keyBorderWidthActivated, 0);
@@ -85,11 +88,13 @@ public class Theme
return _indicationPaint; return _indicationPaint;
} }
int adjustLight(int color, float offset) /** Interpolate the 'value' component toward its opposite by 'alpha'. */
int adjustLight(int color, float alpha)
{ {
float[] hsv = new float[3]; float[] hsv = new float[3];
Color.colorToHSV(color, hsv); Color.colorToHSV(color, hsv);
hsv[2] += offset; float v = hsv[2];
hsv[2] = alpha - (2 * alpha - 1) * v;
return Color.HSVToColor(hsv); return Color.HSVToColor(hsv);
} }

View File

@@ -22,6 +22,7 @@ public class ExtraKeysPreference extends PreferenceCategory
{ {
"alt", "alt",
"meta", "meta",
"compose",
"voice_typing", "voice_typing",
"accent_aigu", "accent_aigu",
"accent_grave", "accent_grave",
@@ -74,6 +75,7 @@ public class ExtraKeysPreference extends PreferenceCategory
{ {
case "voice_typing": case "voice_typing":
case "change_method": case "change_method":
case "compose":
return true; return true;
default: default:
return false; return false;
@@ -87,25 +89,26 @@ public class ExtraKeysPreference extends PreferenceCategory
switch (name) switch (name)
{ {
case "capslock": id = R.string.key_descr_capslock; break; case "capslock": id = R.string.key_descr_capslock; break;
case "switch_greekmath": id = R.string.key_descr_switch_greekmath; break;
case "change_method": id = R.string.key_descr_change_method; break; case "change_method": id = R.string.key_descr_change_method; break;
case "voice_typing": id = R.string.key_descr_voice_typing; break; case "compose": id = R.string.key_descr_compose; break;
case "copy": id = R.string.key_descr_copy; break; case "copy": id = R.string.key_descr_copy; break;
case "paste": id = R.string.key_descr_paste; break;
case "cut": id = R.string.key_descr_cut; break; case "cut": id = R.string.key_descr_cut; break;
case "end": id = R.string.key_descr_end; break;
case "home": id = R.string.key_descr_home; break;
case "page_down": id = R.string.key_descr_page_down; break;
case "page_up": id = R.string.key_descr_page_up; break;
case "paste": id = R.string.key_descr_paste; break;
case "pasteAsPlainText": id = R.string.key_descr_pasteAsPlainText; break;
case "redo": id = R.string.key_descr_redo; break;
case "selectAll": id = R.string.key_descr_selectAll; break; case "selectAll": id = R.string.key_descr_selectAll; break;
case "shareText": id = R.string.key_descr_shareText; break; case "shareText": id = R.string.key_descr_shareText; break;
case "pasteAsPlainText": id = R.string.key_descr_pasteAsPlainText; break; case "subscript": id = R.string.key_descr_subscript; break;
case "superscript": id = R.string.key_descr_superscript; break;
case "switch_greekmath": id = R.string.key_descr_switch_greekmath; break;
case "undo": id = R.string.key_descr_undo; break; case "undo": id = R.string.key_descr_undo; break;
case "redo": id = R.string.key_descr_redo; break; case "voice_typing": id = R.string.key_descr_voice_typing; break;
case "ª": id = R.string.key_descr_ª; break; case "ª": id = R.string.key_descr_ª; break;
case "º": id = R.string.key_descr_º; break; case "º": id = R.string.key_descr_º; break;
case "superscript": id = R.string.key_descr_superscript; break;
case "subscript": id = R.string.key_descr_subscript; break;
case "page_up": id = R.string.key_descr_page_up; break;
case "page_down": id = R.string.key_descr_page_down; break;
case "home": id = R.string.key_descr_home; break;
case "end": id = R.string.key_descr_end; break;
} }
if (id == 0) if (id == 0)
return null; return null;
@@ -166,7 +169,7 @@ public class ExtraKeysPreference extends PreferenceCategory
setKey(pref_key_of_key_name(key_name)); setKey(pref_key_of_key_name(key_name));
setDefaultValue(default_checked); setDefaultValue(default_checked);
setTitle(title); setTitle(title);
_key_font = kv.hasFlags(KeyValue.FLAG_KEY_FONT); _key_font = kv.hasFlagsAny(KeyValue.FLAG_KEY_FONT);
} }
@Override @Override

134
srcs/layouts/LICENSE Normal file
View File

@@ -0,0 +1,134 @@
Layout definitions are licensed differently from the rest of the application
source code to allow use in other projects related or unrelated to Unexpected
Keyboard.
The following license applies to all the files in the srcs/layouts directory
whose name ends in .xml with the exception of:
- latn_neo2.xml
- latn_qwertz.xml
Files listed as exceptions are licensed under the same license as the rest of
the project and might contain copyright notices.
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<keyboard name="ગુજરાતી ફોનેટિક - Gujarati Phonetic" script="gujarati">
<row>
<key key0="ટ" key1="`" key2="1" key3="!" key4="esc"/>
<key key0="ડ" key1="~" key2="2" key3="\@" key4="\#"/>
<key key0="ે" key1="એ" key2="3" key3="$" key4="૱"/>
<key key0="ર" key1="ઋ" key2="4" key3="ૃ" key4="₹"/>
<key key0="ત" key1="ટ" key2="5" key3="%"/>
<key key0="ય" key2="6" key3="^"/>
<key key0="ુ" key1="ઉ" key2="7" key3="&amp;"/>
<key key0="િ" key1="ઇ" key2="8" key3="*" key4="ઁ"/>
<key key0="ો" key1="ઓ" key2="9" key3="(" key4=")"/>
<key key0="પ" key1="૰" key2="0" key4="॒"/>
</row>
<row>
<key shift="0.5" key0="ા" key1="tab" key3="અ"/>
<key key0="સ" key1="“" key4="”"/>
<key key0="દ" key1="ડ"/>
<key key0="્" key1="ૠ" key3="ૄ"/>
<key key0="ગ" key2="-" key3="_"/>
<key key0="હ" key2="=" key3="+"/>
<key key0="જ" key3="{" key4="}"/>
<key key0="ક" key1="ૢ" key2="ૡ" key3="[" key4="]"/>
<key key0="લ" key1="।" key2="|" key3="\\" key4="॥"/>
</row>
<row>
<key width="1.5" key0="shift" key2="loc capslock"/>
<key key0="ઞ" key1="જ્ઞ" key2="ૅ" key3="ઙ" key4="ઍ"/>
<key key0="ષ" key1="ક્ષ" key2="ૉ" key3="઼" key4="ઑ"/>
<key key0="ચ" key2="&lt;" key3="."/>
<key key0="વ" key2=">" key3=","/>
<key key0="બ" key2="\?" key3="/"/>
<key key0="ન" key2=":" key3=";"/>
<key key0="મ" key1="ૐ" key2="&quot;" key3="'" key4="॑" key6="ઽ"/>
<key width="1.5" key0="backspace" key2="delete"/>
</row>
</keyboard>

View File

@@ -9,7 +9,7 @@
<key key0="r" key2="4" key3="'"/> <key key0="r" key2="4" key3="'"/>
<key key0="t" key2="5" key3="(" key4=")"/> <key key0="t" key2="5" key3="(" key4=")"/>
<key key0="y" key2="6" key3="-" key4="|"/> <key key0="y" key2="6" key3="-" key4="|"/>
<key key0="u" key2="7" key4="`"/> <key key0="u" key2="7" key3="à" key4="`"/>
<key key0="i" key2="8" key3="_" key4="\\"/> <key key0="i" key2="8" key3="_" key4="\\"/>
<key key0="o" key2="9" key3="\@" key4="f11_placeholder"/> <key key0="o" key2="9" key3="\@" key4="f11_placeholder"/>
<key key0="p" key2="0" key3="f12_placeholder"/> <key key0="p" key2="0" key3="f12_placeholder"/>
@@ -17,12 +17,12 @@
<row> <row>
<key key0="q" key2="tab"/> <key key0="q" key2="tab"/>
<key key0="s" key3="loc ß"/> <key key0="s" key3="loc ß"/>
<key key0="d" key1="accent_grave" key3="accent_aigu"/> <key key0="d" key1="loc accent_grave" key2="é" key3="loc accent_aigu"/>
<key key0="f" key3="{" key4="}"/> <key key0="f" key3="{" key4="}"/>
<key key0="g" key3="[" key4="]"/> <key key0="g" key3="[" key4="]"/>
<key key0="h" key3="=" key4="+"/> <key key0="h" key3="=" key4="+"/>
<key key0="j" key1="accent_trema" key2="accent_circonflexe" key3="^"/> <key key0="j" key1="loc accent_trema" key2="loc accent_circonflexe" key3="^"/>
<key key0="k" key2="€" key3="$"/> <key key0="k" key1="è" key2="€" key3="$"/>
<key key0="l" key2="%"/> <key key0="l" key2="%"/>
<key key0="m" key3="*"/> <key key0="m" key3="*"/>
</row> </row>
@@ -30,7 +30,7 @@
<key width="2.0" key0="shift" key2="loc capslock"/> <key width="2.0" key0="shift" key2="loc capslock"/>
<key key0="w" key3="&lt;" key4="&gt;"/> <key key0="w" key3="&lt;" key4="&gt;"/>
<key key0="x" key1="loc †"/> <key key0="x" key1="loc †"/>
<key key0="c" key1="accent_cedille" key3="," key4="\?"/> <key key0="c" key1="loc accent_cedille" key2="ç" key3="," key4="\?"/>
<key key0="v" key3=";" key4="."/> <key key0="v" key3=";" key4="."/>
<key key0="b" key3=":" key4="/"/> <key key0="b" key3=":" key4="/"/>
<key key0="n" key1="loc accent_tilde" key2="§" key4="!"/> <key key0="n" key1="loc accent_tilde" key2="§" key4="!"/>

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- copyright 2022 matthiakl <t-m.42@mailpost.spdns.org> -->
<!-- https://neo-layout.org/Layouts/neo/ --> <!-- https://neo-layout.org/Layouts/neo/ -->
<keyboard name="Neo 2" bottom_row="false" script="latin"> <keyboard name="Neo 2" bottom_row="false" script="latin">
<row> <row>
@@ -40,11 +41,11 @@
<key width="1.5" key0="backspace" key2="delete"/> <key width="1.5" key0="backspace" key2="delete"/>
</row> </row>
<row height="0.95"> <row height="0.95">
<key width="1.8" key0="ctrl" key1="loc switch_greekmath" key2="loc meta" key4="switch_numeric"/> <key width="1.7" key0="ctrl" key1="loc switch_greekmath" key2="loc meta" key4="switch_numeric"/>
<key width="1.2" key0="fn" key1="loc alt" key2="loc change_method" key3="switch_emoji" key4="config"/> <key width="1.1" key0="fn" key1="loc alt" key2="loc change_method" key3="switch_emoji" key4="config"/>
<key width="4.0" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right" slider="true"/> <key width="4.4" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right" slider="true"/>
<key width="1.2" key7="up" key6="right" key5="left" key8="down"/> <key width="1.1" key0="loc compose" key7="up" key6="right" key5="left" key8="down" key1="loc home" key2="loc page_up" key3="loc end" key4="loc page_down"/>
<key key0="j" key4=";"/> <key key0="j" key4=";"/>
<key width="1.8" key0="enter" key1="loc voice_typing" key2="action"/> <key width="1.7" key0="enter" key1="loc voice_typing" key2="action"/>
</row> </row>
</keyboard> </keyboard>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<keyboard name="QWERTY (Slovak)" script="latin">
<row>
<key key0="q" key1="1" key4="esc"/>
<key key0="w" key1="2" key3="`"/>
<key key0="e" key1="3" key2="€" key3="é" key4="ě"/>
<key key0="r" key1="4" key3="ŕ" key4="ř"/>
<key key0="t" key1="5" key4="ť"/>
<key key0="y" key1="6" key3="ý"/>
<key key0="u" key1="7" key2="ü" key3="ú" key4="ů"/>
<key key0="i" key1="8" key3="í"/>
<key key0="o" key1="9" key2="ö" key3="ó" key4="ô"/>
<key key0="p" key1="0" key3="="/>
</row>
<row>
<key key0="a" key1="tab" key2="&amp;" key3="á" key4="ä"/>
<key key0="s" key1="loc §" key2="$" key3="ś" key4="š"/>
<key key0="d" key4="ď"/>
<key key0="f" key1="%" key3="\\" key4="/"/>
<key key0="g" key1="~" key2="^"/>
<key key0="h" key1="+" key2="-" key3="*" key4="÷"/>
<key key0="j" key1="{" key2="}" key3="|"/>
<key key0="k" key1="[" key2="]" key3="'" key4="&quot;"/>
<key key0="l" key1="(" key2=")" key3="ĺ" key4="ľ"/>
<key key0="." key1="!" key3="\?"/>
</row>
<row>
<key width="1.5" key0="shift" key2="loc capslock"/>
<key key0="z" key4="ž"/>
<key key0="x" key1="loc †" key2="\#"/>
<key key0="c" key4="č"/>
<key key0="v" key4="\@"/>
<key key0="b" key1="&lt;" key2="&gt;" key3="_" key4=";"/>
<key key0="n" key4="ň"/>
<key key0="m" key3="," key4=":"/>
<key width="1.5" key0="backspace" key3="delete"/>
</row>
</keyboard>

View File

@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- copyright 2022 Moini <Moini@users.noreply.github.com> -->
<keyboard name="QWERTZ" script="latin"> <keyboard name="QWERTZ" script="latin">
<row> <row>
<key key0="q" key2="1" key4="esc"/> <key key0="q" key2="1" key4="esc"/>

8
srcs/special_font/16.svg Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- By Vectorization: Mrmw - Own work using: iso.org/obp/graphics/grs/8f631f6d-89af-4962-bd14-a94dd3c1a2bf 200.png, Public Domain, https://commons.wikimedia.org/w/index.php?curid=20206736 -->
<svg width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<g fill="none">
<rect x="22.5" y="33.5" width="103" height="132" stroke="#000" stroke-width="5"/>
<circle cx="125.5" cy="99.5" r="66" stroke="#000" stroke-width="5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 519 B