Merge lp:~cherojeong/ubuntu-keyboard/korean-layout into lp:ubuntu-keyboard

Proposed by chero jeong
Status: Merged
Approved by: Michael Sheldon
Approved revision: 475
Merged at revision: 499
Proposed branch: lp:~cherojeong/ubuntu-keyboard/korean-layout
Merge into: lp:ubuntu-keyboard
Diff against target: 1703 lines (+1508/-1)
26 files modified
debian/control (+11/-1)
debian/server.conf (+1/-0)
debian/ubuntu-keyboard-korean.install (+1/-0)
plugins/ko/ko.pro (+9/-0)
plugins/ko/qml/Keyboard_ko.qml (+92/-0)
plugins/ko/qml/Keyboard_ko_email.qml (+93/-0)
plugins/ko/qml/Keyboard_ko_url.qml (+92/-0)
plugins/ko/qml/Keyboard_ko_url_search.qml (+93/-0)
plugins/ko/qml/keys/HBackspaceKey.qml (+81/-0)
plugins/ko/qml/keys/HCharKey.qml (+58/-0)
plugins/ko/qml/keys/key_constants.js (+19/-0)
plugins/ko/qml/keys/languages.js (+19/-0)
plugins/ko/qml/keys/parser.js (+245/-0)
plugins/ko/qml/qml.pro (+24/-0)
plugins/ko/src/korean.txt (+253/-0)
plugins/ko/src/koreanlanguagefeatures.cpp (+89/-0)
plugins/ko/src/koreanlanguagefeatures.h (+41/-0)
plugins/ko/src/koreanplugin.cpp (+102/-0)
plugins/ko/src/koreanplugin.h (+61/-0)
plugins/ko/src/koreanplugin.json (+7/-0)
plugins/ko/src/src.pro (+67/-0)
plugins/plugins.pro (+1/-0)
qml/keys/CharKey.qml (+11/-0)
qml/keys/languages.js (+1/-0)
tests/autopilot/ubuntu_keyboard/emulators/keypad.py (+1/-0)
tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py (+36/-0)
To merge this branch: bzr merge lp:~cherojeong/ubuntu-keyboard/korean-layout
Reviewer Review Type Date Requested Status
Ken VanDine packaging Approve
Michael Sheldon (community) Approve
Review via email: mp+292348@code.launchpad.net

Commit message

Add Korean layout

Description of the change

Hi

Add Korean keyboard layout and predictive text database & spellchecking for hangul.

Q. Are there any related MPs required for this MP to build/function as expected? Please list.
A. No, but this requires hunspell-ko package for spellchecking to work.

Q. Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes)
A. Yes.

Q. Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator?
A. Yes, I built the package, installed it and tested it on a device (vega-iron) also
   on emulator i386 on the rc-proposed/ubuntu channel, release r377

   please refer this video for typing hangul keyboard
   (https://www.youtube.com/watch?v=-O1BHlm29l0)

Q. Did you successfully run all tests found in your component's Test Plan (https://wiki.ubuntu.com/Process/Merges/TestPlan/ubuntu-keyboard) on device or emulator?
A. Yes.

Q. If you changed the UI, was the change specified/approved by design?
A. No UI changes, just the addition of a new layout.

Q. If you changed UI labels, did you update the pot file?
A. No UI label changes.

Q.If you changed the packaging (debian), did you add a core-dev as a reviewer to this MP?
A. TBD

Let me know if there is what I have to do more.
Thanks

To post a comment you must log in.
Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 That looks great! Thanks for that, I'll give it a full review shortly, one small thing I noticed quickly glancing through the code: could you set the copyright year to 2016 for any new files that you created?

Thanks!
 Mike

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

 Also, you'll need to sign the CLA before we can merge this, you can find details here: http://www.ubuntu.com/legal/contributors

 If you have any problems just let me know :)

463. By chero jeong

fix copyright year of new files and hangul checking routine

464. By chero jeong

fix copyright year missing file

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike

first of all Thanks for review.

As you mentioned I fixed copyright year of new files.

I am trying to sign the CLA.
But I don't know who is canonical project manager so I could not fill in project contact field.
(http://www.ubuntu.com/legal/contributors/submit)

please let me know how to fill in this field.

Thanks :)
Chero

465. By chero jeong

chanage prediction text file.

466. By chero jeong

remake prediction database file

Revision history for this message
chero jeong (cherojeong) wrote :

> Also, you'll need to sign the CLA before we can merge this, you can find
> details here: http://www.ubuntu.com/legal/contributors
>
> If you have any problems just let me know :)

Hi Mike

I have done it.
Please let me know what is the next step?

Thanks
Chero

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 The next step is for me to do a full code review and test of the new layout, I'll try to get that done this week and then get back to you with any feedback.

Thanks,
 Mike

Revision history for this message
chero jeong (cherojeong) wrote :

> Hi Chero,
>
> The next step is for me to do a full code review and test of the new layout,
> I'll try to get that done this week and then get back to you with any
> feedback.
>
> Thanks,
> Mike

Hi Mike

I wish there are no any issues or some critical problems.
Thanks for your comment.
chero

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 The keyboard seems to work very nicely, there's a couple of small things that need fixing though:

 * Due to the recent keyboard landing on Friday there's now a simple merge conflict in this branch that needs fixing

 * The Korean autopilot test currently fails, this is because the Korean keyboard is keeping the input in pre-edit, so the text property doesn't yet have the input committed. This can be easily fixed by having that test press the space key at the end of the word to commit it and then change the expected result to "한글 ".

 * The URL keys on the URL and email layouts only have the English/general TLDs, without any Korean specific ones.

 * HCharKey.qml duplicates a lot of code, it'd be nice if this could be refactored to become a subclass of CharKey (this might require some minor changes in CharKey as well)

Thanks!
 Mike

review: Needs Fixing
467. By chero jeong

Change TLDs for Korean on the URL and email layouts

468. By chero jeong

Refactoring HCharKey.qml
Add variable and property in CharKey.qml

Each Hangul represents one syllable.
The Hangul are composed of jamo.
It need to manipulate preedit until compose one syllable.

469. By chero jeong

fix pre-edit string problem in autopilot test

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike

Thanks for your comments.

As you mentioned I fixed TDLs and reworked HCharKey.qml for reduce dupulicate codes.
Also I added proper variable and property in CharKey.qml.

Let me know if you have any question.

Thanks

chero

470. By chero jeong

fix text conflict in test/autopilot/ubuntu_keyboard/test/test_keyboard.py

471. By chero jeong

remove ko language in class UbuntuKeyboardTests

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike

I have fixed text conflict issue in test_keyboard.py.
I wish this version is merged and there is no critical issue.

Thanks
chero

Revision history for this message
Ben (fisherson0225) wrote :

Hi, Chero

I'm very new to this site. Can you tell me how I can use your Korean keyboard in Ubuntu 15.04?
And one more question: Are you Korean? If you are, please let me know, because I'm also Korean and I have problems on
using LTE on my ubnutu phone.

Thanks!
Ben

472. By chero jeong

Fix No used file in qml/qml.pro

Revision history for this message
chero jeong (cherojeong) wrote :

Hi ben
If you ask me how to use korean layout,
I would like to ask me by email not this channel. As you know this site is for developing ubuntu-keyboard.

I will reply your request in korean language.

Thanks
Chero

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike

I'm waiting for your review.
As you know , current ubuntu-keyboard was updated to new version.

so I wonder whether this korean-layout using old version is available to merge.
I mean I want to know whether I have to rework with new version.

Let me know if you have any question or other opinion.

Thanks
chero

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 Unfortunately the final review of this is currently blocked by a unity8 bug (https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1591192) which is causing unity8 to crash when running the keyboard tests; as soon as that's fixed I'll complete the final review of this, however it does look good from what I've seen of it so far.

 It'd probably be a good idea to merge from trunk to make sure there aren't going to be any conflicts though (this should just be a matter of running "bzr merge lp:ubuntu-keyboard", then fix any conflicts and commit).

473. By chero jeong

Merge from trunk lp:ubuntu-keyboard

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike

I have done merge from trunk (lp:ubuntu-keyboard). when I try to merge from trunk , there are no any conflicts.
I could test this version through someone who has a meizu pro 5 (was upgraded to OTA 11)
and I will try to test it on emulator I386.
As you mentioned I hope this issue is fixed as soon as possible. I'm waiting for good news.

Thanks
chero

Revision history for this message
chero jeong (cherojeong) wrote :

Merge from trunk (lp:ubuntu-keyboard).

I updated to latest version and some conflicts was fixed.
 - merged revision "Releasing 0.99.trunk.phablet2+16.10.20160615.1-0ubuntu1"

474. By chero jeong

Merge from trunk

Revision history for this message
Jiwon Kim (yesdi) wrote :

Hi. I want to test this. I have also meizu mx5pro.
before that. I have question. Is this keyboard is installable or replacable in runtime Image?
Or should I build whole image and Refuse it?

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Jiwon

if you want to install this package. please visit this URL.(It is described in korean)

https://github.com/cherojeong/ubuntu_packages/blob/master/README.md

You could find how to install ubuntu-keyboard-korean-layout to your phone.
Before installing this package, you should check OTA version in your phone. and then You should select proper version for your phone.
Currently there are two prebuilt packages (OTA 11/10).

This sources in launchpad have been preparing OTA12 version.
So Do not install this package through build it by yourself.

Github site is temporary for supporting korean-layout package.
I am going to prepare PPA.

Regards
chero

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike

I wonder when you start to review this branch again.
if there are some bug or something to merge from main branch, Let me know it.

Regards
chero

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 I'm hoping to finish off the review this week, I've got it built in silo 31 alongside some other keyboard branches, so hopefully we should be able to land them all together once the review is complete

Cheers,
 Mike

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 I'm just doing some final testing of this now, and I noticed that the input validator in the contacts app doesn't allow Korean characters for email addresses; are they valid in email addresses? If so this is a bug in the contacts app that we can address separately, otherwise if they're not valid then maybe it'd make sense for the email layout to contain latin characters?

Thanks,
 Mike

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike

They (Korean charactoers) are not valid in email addresses.
So This is not a bug in Contacts app.

Generally when we type proper email address in this field, we have to change latin layout(english email layout).
In other case (name, memo) , we usally use korean email layout.

So I thought that no need to contain latin charaters in korean email layout.

Thanks
chero

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 I've just tested on Android and they switch to a latin layout for email fields, so I think we should probably do the same; it's just a matter of replacing the Keyboard_ko_email.qml file with the one from the English layout. You'd still be able to enter Korean in all the other fields, it's only fields that explicitly request an email address that'll get this layout.

Cheers,
 Mike

475. By chero jeong

replace latin layout for email field

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike
Thank you for your comments.
I replaced latin charater with korean charaters to Keyboard_ko_email.qml.
As I mentioned , korean charaters is not used for email address.
so I think email qml using latin layout is correct method.

Let me know if you have any question.

Thanks
chero

Revision history for this message
chero jeong (cherojeong) wrote :

Hi Mike
I'm waiting for your review.
I wonder whether my modification is correct or not.
And I would like to know remained process or something like that.

I wish this package is included in OTA13 release. just hope.

Thanks
Chero

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Hi Chero,

 The latest change looks good, your branch is in a silo alongside a number of other branches which I'll be giving a final test to all together before they all get approved; one of those branches still needs a little work though (should be finished within the next couple of days). After which they'll all land together, and will be in OTA 13.

Cheers,
 Mike

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

All looks good for merging, there might be a delay in getting it into the image however, as it seems that the Korean hunspell dictionary takes up 17mb, so I'm afraid distribution of this may have to wait until keyboards can be distributed as click packages (there's only very limited space available on the system partition)

review: Approve
Revision history for this message
Ken VanDine (ken-vandine) wrote :

Packaging is fine

review: Approve (packaging)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2016-06-15 12:43:55 +0000
3+++ debian/control 2016-07-21 02:52:13 +0000
4@@ -103,6 +103,7 @@
5 ubuntu-keyboard-italian (>= ${source:Version}),
6 ubuntu-keyboard-japanese (>= ${source:Version}),
7 ubuntu-keyboard-latvian (>= ${source:Version}),
8+ ubuntu-keyboard-korean (>= ${source:Version}),
9 ubuntu-keyboard-norwegian-bokmal (>= ${source:Version}),
10 ubuntu-keyboard-polish (>= ${source:Version}),
11 ubuntu-keyboard-portuguese (>= ${source:Version}),
12@@ -306,10 +307,19 @@
13 Depends: ubuntu-keyboard (= ${binary:Version}),
14 myspell-lv,
15 ${misc:Depends},
16- ${shlibs:Depends},
17+ ${shlibs:Depends},
18 Description: Ubuntu on-screen keyboard data files - Latvian
19 Data files for the Ubuntu virtual keyboard - Latvian
20
21+Package: ubuntu-keyboard-korean
22+Architecture: any
23+Depends: ubuntu-keyboard (= ${binary:Version}),
24+ hunspell-ko,
25+ ${misc:Depends},
26+ ${shlibs:Depends},
27+Description: Ubuntu on-screen keyboard data files - Korean
28+ Data files for the Ubuntu virtual keyboard - Korean
29+
30 Package: ubuntu-keyboard-norwegian-bokmal
31 Architecture: any
32 Depends: ubuntu-keyboard (= ${binary:Version}),
33
34=== modified file 'debian/server.conf'
35--- debian/server.conf 2015-08-15 06:10:39 +0000
36+++ debian/server.conf 2016-07-21 02:52:13 +0000
37@@ -11,6 +11,7 @@
38 libubuntu-keyboard-plugin.so:es, \
39 libubuntu-keyboard-plugin.so:ca, \
40 libubuntu-keyboard-plugin.so:ja, \
41+libubuntu-keyboard-plugin.so:ko, \
42 libubuntu-keyboard-plugin.so:zh_cn_cangjie, \
43 libubuntu-keyboard-plugin.so:zh_cn_pinyin, \
44 libubuntu-keyboard-plugin.so:zh_cn_zhuyin,
45
46=== added file 'debian/ubuntu-keyboard-korean.install'
47--- debian/ubuntu-keyboard-korean.install 1970-01-01 00:00:00 +0000
48+++ debian/ubuntu-keyboard-korean.install 2016-07-21 02:52:13 +0000
49@@ -0,0 +1,1 @@
50+usr/share/maliit/plugins/com/ubuntu/lib/ko/
51\ No newline at end of file
52
53=== added directory 'plugins/ko'
54=== added file 'plugins/ko/ko.pro'
55--- plugins/ko/ko.pro 1970-01-01 00:00:00 +0000
56+++ plugins/ko/ko.pro 2016-07-21 02:52:13 +0000
57@@ -0,0 +1,9 @@
58+CONFIG += ordered
59+TEMPLATE = subdirs
60+SUBDIRS = \
61+ src \
62+ qml
63+
64+QMAKE_EXTRA_TARGETS += check
65+check.target = check
66+check.CONFIG = recursive
67
68=== added directory 'plugins/ko/qml'
69=== added file 'plugins/ko/qml/Keyboard_ko.qml'
70--- plugins/ko/qml/Keyboard_ko.qml 1970-01-01 00:00:00 +0000
71+++ plugins/ko/qml/Keyboard_ko.qml 2016-07-21 02:52:13 +0000
72@@ -0,0 +1,92 @@
73+/*
74+ * Copyright 2016 Canonical Ltd.
75+ *
76+ * This program is free software; you can redistribute it and/or modify
77+ * it under the terms of the GNU Lesser General Public License as published by
78+ * the Free Software Foundation; version 3.
79+ *
80+ * This program is distributed in the hope that it will be useful,
81+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
82+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
83+ * GNU Lesser General Public License for more details.
84+ *
85+ * You should have received a copy of the GNU Lesser General Public License
86+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
87+ */
88+
89+import QtQuick 2.4
90+import "keys/"
91+import keys 1.0
92+
93+KeyPad {
94+ anchors.fill: parent
95+
96+ content: c1
97+ symbols: "languages/Keyboard_symbols.qml"
98+
99+ Column {
100+ id: c1
101+ anchors.fill: parent
102+ spacing: 0
103+
104+ Row {
105+ anchors.horizontalCenter: parent.horizontalCenter;
106+ spacing: 0
107+
108+ HCharKey { label: "ㅂ"; shifted: "ㅃ"; extended: ["1"]; extendedShifted: ["1"]; leftSide: true; }
109+ HCharKey { label: "ㅈ"; shifted: "ㅉ"; extended: ["2"]; extendedShifted: ["2"] }
110+ HCharKey { label: "ㄷ"; shifted: "ㄸ"; extended: ["3"]; extendedShifted: ["3"] }
111+ HCharKey { label: "ㄱ"; shifted: "ㄲ"; extended: ["4"]; extendedShifted: ["4"] }
112+ HCharKey { label: "ㅅ"; shifted: "ㅆ"; extended: ["5"]; extendedShifted: ["5"] }
113+ HCharKey { label: "ㅛ"; shifted: "ㅛ"; extended: ["6"]; extendedShifted: ["6"] }
114+ HCharKey { label: "ㅕ"; shifted: "ㅕ"; extended: ["7"]; extendedShifted: ["7"] }
115+ HCharKey { label: "ㅑ"; shifted: "ㅑ"; extended: ["8"]; extendedShifted: ["8"] }
116+ HCharKey { label: "ㅐ"; shifted: "ㅒ"; extended: ["9"]; extendedShifted: ["9"] }
117+ HCharKey { label: "ㅔ"; shifted: "ㅖ"; extended: ["0"]; extendedShifted: ["0"]; rightSide: true; }
118+ }
119+
120+ Row {
121+ anchors.horizontalCenter: parent.horizontalCenter;
122+ spacing: 0
123+
124+ HCharKey { label: "ㅁ"; shifted: "ㅁ"; leftSide: true; }
125+ HCharKey { label: "ㄴ"; shifted: "ㄴ"; }
126+ HCharKey { label: "ㅇ"; shifted: "ㅇ"; }
127+ HCharKey { label: "ㄹ"; shifted: "ㄹ"; }
128+ HCharKey { label: "ㅎ"; shifted: "ㅎ"; }
129+ HCharKey { label: "ㅗ"; shifted: "ㅗ"; }
130+ HCharKey { label: "ㅓ"; shifted: "ㅓ"; }
131+ HCharKey { label: "ㅏ"; shifted: "ㅏ"; }
132+ HCharKey { label: "ㅣ"; shifted: "ㅣ"; rightSide: true; }
133+ }
134+
135+ Row {
136+ anchors.horizontalCenter: parent.horizontalCenter;
137+ spacing: 0
138+
139+ ShiftKey {}
140+ HCharKey { label: "ㅋ"; shifted: "ㅋ"; }
141+ HCharKey { label: "ㅌ"; shifted: "ㅌ"; }
142+ HCharKey { label: "ㅊ"; shifted: "ㅊ"; }
143+ HCharKey { label: "ㅍ"; shifted: "ㅍ"; }
144+ HCharKey { label: "ㅠ"; shifted: "ㅠ"; }
145+ HCharKey { label: "ㅜ"; shifted: "ㅜ"; }
146+ HCharKey { label: "ㅡ"; shifted: "ㅡ"; }
147+ HBackspaceKey {}
148+ }
149+
150+ Item {
151+ anchors.left: parent.left
152+ anchors.right: parent.right
153+
154+ height: panel.keyHeight + units.gu(UI.row_margin);
155+
156+ SymbolShiftKey { id: symShiftKey; anchors.left: parent.left; height: parent.height; }
157+ LanguageKey { id: languageMenuButton; anchors.left: symShiftKey.right; height: parent.height; }
158+ HCharKey { id: commaKey; label: ","; shifted: ","; extended: ["'", "\"", ";", ":", "@", "&", "(", ")"]; extendedShifted: ["'", "\"", ";", ":", "@", "&", "(", ")"]; anchors.left: languageMenuButton.right; height: parent.height; }
159+ SpaceKey { id: spaceKey; anchors.left: commaKey.right; anchors.right: dotKey.left; noMagnifier: true; height: parent.height; }
160+ HCharKey { id: dotKey; label: "."; shifted: "."; extended: ["?", "-", "_", "!", "+", "%","#","/"]; extendedShifted: ["?", "-", "_", "!", "+", "%","#","/"]; anchors.right: enterKey.left; height: parent.height; }
161+ ReturnKey { id: enterKey; anchors.right: parent.right; height: parent.height; }
162+ }
163+ } // column
164+}
165
166=== added file 'plugins/ko/qml/Keyboard_ko_email.qml'
167--- plugins/ko/qml/Keyboard_ko_email.qml 1970-01-01 00:00:00 +0000
168+++ plugins/ko/qml/Keyboard_ko_email.qml 2016-07-21 02:52:13 +0000
169@@ -0,0 +1,93 @@
170+/*
171+ * Copyright 2016 Canonical Ltd.
172+ *
173+ * This program is free software; you can redistribute it and/or modify
174+ * it under the terms of the GNU Lesser General Public License as published by
175+ * the Free Software Foundation; version 3.
176+ *
177+ * This program is distributed in the hope that it will be useful,
178+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
179+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
180+ * GNU Lesser General Public License for more details.
181+ *
182+ * You should have received a copy of the GNU Lesser General Public License
183+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
184+ */
185+
186+import QtQuick 2.4
187+import "keys/"
188+import keys 1.0
189+
190+KeyPad {
191+ anchors.fill: parent
192+
193+ content: c1
194+ symbols: "languages/Keyboard_symbols.qml"
195+
196+ Column {
197+ id: c1
198+ anchors.fill: parent
199+ spacing: 0
200+
201+ Row {
202+ anchors.horizontalCenter: parent.horizontalCenter;
203+ spacing: 0
204+
205+ CharKey { label: "q"; shifted: "Q"; extended: ["1"]; extendedShifted: ["1"]; leftSide: true; }
206+ CharKey { label: "w"; shifted: "W"; extended: ["2"]; extendedShifted: ["2"] }
207+ CharKey { label: "e"; shifted: "E"; extended: ["3", "è", "é", "ê", "ë", "€"]; extendedShifted: ["3", "È","É", "Ê", "Ë", "€"] }
208+ CharKey { label: "r"; shifted: "R"; extended: ["4"]; extendedShifted: ["4"] }
209+ CharKey { label: "t"; shifted: "T"; extended: ["5", "þ"]; extendedShifted: ["5", "Þ"] }
210+ CharKey { label: "y"; shifted: "Y"; extended: ["6", "ý", "¥"]; extendedShifted: ["6", "Ý", "¥"] }
211+ CharKey { label: "u"; shifted: "U"; extended: ["7", "û","ù","ú","ü"]; extendedShifted: ["7", "Û","Ù","Ú","Ü"] }
212+ CharKey { label: "i"; shifted: "I"; extended: ["8", "î","ï","ì","í"]; extendedShifted: ["8", "Î","Ï","Ì","Í"] }
213+ CharKey { label: "o"; shifted: "O"; extended: ["9", "ö","ô","ò","ó"]; extendedShifted: ["9", "Ö","Ô","Ò","Ó"] }
214+ CharKey { label: "p"; shifted: "P"; extended: ["0"]; extendedShifted: ["0"]; rightSide: true; }
215+ }
216+
217+ Row {
218+ anchors.horizontalCenter: parent.horizontalCenter;
219+ spacing: 0
220+
221+ CharKey { label: "a"; shifted: "A"; extended: ["ä","à","â","á","ã","å"]; extendedShifted: ["Ä","À","Â","Á","Ã","Å"]; leftSide: true; }
222+ CharKey { label: "s"; shifted: "S"; extended: ["ß","$"]; extendedShifted: ["$"] }
223+ CharKey { label: "d"; shifted: "D"; extended: ["ð"]; extendedShifted: ["Ð"] }
224+ CharKey { label: "f"; shifted: "F"; }
225+ CharKey { label: "g"; shifted: "G"; }
226+ CharKey { label: "h"; shifted: "H"; }
227+ CharKey { label: "j"; shifted: "J"; }
228+ CharKey { label: "k"; shifted: "K"; }
229+ CharKey { label: "l"; shifted: "L"; rightSide: true; }
230+ }
231+
232+ Row {
233+ anchors.horizontalCenter: parent.horizontalCenter;
234+ spacing: 0
235+
236+ ShiftKey {}
237+ CharKey { label: "z"; shifted: "Z"; }
238+ CharKey { label: "x"; shifted: "X"; }
239+ CharKey { label: "c"; shifted: "C"; extended: ["ç"]; extendedShifted: ["Ç"] }
240+ CharKey { label: "v"; shifted: "V"; }
241+ CharKey { label: "b"; shifted: "B"; }
242+ CharKey { label: "n"; shifted: "N"; extended: ["ñ"]; extendedShifted: ["Ñ"] }
243+ CharKey { label: "m"; shifted: "M"; }
244+ BackspaceKey {}
245+ }
246+
247+ Item {
248+ anchors.left: parent.left
249+ anchors.right: parent.right
250+
251+ height: panel.keyHeight + units.gu(UI.row_margin);
252+
253+ SymbolShiftKey { id: symShiftKey; anchors.left: parent.left; height: parent.height; }
254+ LanguageKey { id: languageMenuButton; anchors.left: symShiftKey.right; height: parent.height; }
255+ CharKey { id: atKey; label: "@"; shifted: "@"; anchors.left: languageMenuButton.right; height: parent.height; }
256+ SpaceKey { id: spaceKey; anchors.left: atKey.right; anchors.right: urlKey.left; noMagnifier: true; height: parent.height; }
257+ UrlKey { id: urlKey; label: ".com"; extended: [".co.kr", ".or.kr", ".go.kr", ".ac.kr", ".kr"]; anchors.right: dotKey.left; height: parent.height; }
258+ CharKey { id: dotKey; label: "."; shifted: "."; extended: ["?", "-", "_", "!", "+", "%","#","/"]; extendedShifted: ["?", "-", "_", "!", "+", "%","#","/"]; anchors.right: enterKey.left; height: parent.height; }
259+ ReturnKey { id: enterKey; anchors.right: parent.right; height: parent.height; }
260+ }
261+ } // column
262+}
263
264=== added file 'plugins/ko/qml/Keyboard_ko_url.qml'
265--- plugins/ko/qml/Keyboard_ko_url.qml 1970-01-01 00:00:00 +0000
266+++ plugins/ko/qml/Keyboard_ko_url.qml 2016-07-21 02:52:13 +0000
267@@ -0,0 +1,92 @@
268+/*
269+ * Copyright 2016 Canonical Ltd.
270+ *
271+ * This program is free software; you can redistribute it and/or modify
272+ * it under the terms of the GNU Lesser General Public License as published by
273+ * the Free Software Foundation; version 3.
274+ *
275+ * This program is distributed in the hope that it will be useful,
276+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
277+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
278+ * GNU Lesser General Public License for more details.
279+ *
280+ * You should have received a copy of the GNU Lesser General Public License
281+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
282+ */
283+
284+import QtQuick 2.4
285+import "keys/"
286+import keys 1.0
287+
288+KeyPad {
289+ anchors.fill: parent
290+
291+ content: c1
292+ symbols: "languages/Keyboard_symbols.qml"
293+
294+ Column {
295+ id: c1
296+ anchors.fill: parent
297+ spacing: 0
298+
299+ Row {
300+ anchors.horizontalCenter: parent.horizontalCenter;
301+ spacing: 0
302+
303+ HCharKey { label: "ㅂ"; shifted: "ㅃ"; extended: ["1"]; extendedShifted: ["1"]; leftSide: true; }
304+ HCharKey { label: "ㅈ"; shifted: "ㅉ"; extended: ["2"]; extendedShifted: ["2"] }
305+ HCharKey { label: "ㄷ"; shifted: "ㄸ"; extended: ["3"]; extendedShifted: ["3"] }
306+ HCharKey { label: "ㄱ"; shifted: "ㄲ"; extended: ["4"]; extendedShifted: ["4"] }
307+ HCharKey { label: "ㅅ"; shifted: "ㅆ"; extended: ["5"]; extendedShifted: ["5"] }
308+ HCharKey { label: "ㅛ"; shifted: "ㅛ"; extended: ["6"]; extendedShifted: ["6"] }
309+ HCharKey { label: "ㅕ"; shifted: "ㅕ"; extended: ["7"]; extendedShifted: ["7"] }
310+ HCharKey { label: "ㅑ"; shifted: "ㅑ"; extended: ["8"]; extendedShifted: ["8"] }
311+ HCharKey { label: "ㅐ"; shifted: "ㅒ"; extended: ["9"]; extendedShifted: ["9"] }
312+ HCharKey { label: "ㅔ"; shifted: "ㅖ"; extended: ["0"]; extendedShifted: ["0"]; rightSide: true; }
313+ }
314+
315+ Row {
316+ anchors.horizontalCenter: parent.horizontalCenter;
317+ spacing: 0
318+
319+ HCharKey { label: "ㅁ"; shifted: "ㅁ"; leftSide: true; }
320+ HCharKey { label: "ㄴ"; shifted: "ㄴ"; }
321+ HCharKey { label: "ㅇ"; shifted: "ㅇ"; }
322+ HCharKey { label: "ㄹ"; shifted: "ㄹ"; }
323+ HCharKey { label: "ㅎ"; shifted: "ㅎ"; }
324+ HCharKey { label: "ㅗ"; shifted: "ㅗ"; }
325+ HCharKey { label: "ㅓ"; shifted: "ㅓ"; }
326+ HCharKey { label: "ㅏ"; shifted: "ㅏ"; }
327+ HCharKey { label: "ㅣ"; shifted: "ㅣ"; rightSide: true; }
328+ }
329+
330+ Row {
331+ anchors.horizontalCenter: parent.horizontalCenter;
332+ spacing: 0
333+
334+ ShiftKey {}
335+ HCharKey { label: "ㅋ"; shifted: "ㅋ"; }
336+ HCharKey { label: "ㅌ"; shifted: "ㅌ"; }
337+ HCharKey { label: "ㅊ"; shifted: "ㅊ"; }
338+ HCharKey { label: "ㅍ"; shifted: "ㅍ"; }
339+ HCharKey { label: "ㅠ"; shifted: "ㅠ"; }
340+ HCharKey { label: "ㅜ"; shifted: "ㅜ"; }
341+ HCharKey { label: "ㅡ"; shifted: "ㅡ"; }
342+ HBackspaceKey {}
343+ }
344+
345+ Item {
346+ anchors.left: parent.left
347+ anchors.right: parent.right
348+
349+ height: panel.keyHeight + units.gu(UI.row_margin);
350+
351+ SymbolShiftKey { id: symShiftKey; anchors.left: parent.left; height: parent.height; }
352+ LanguageKey { id: languageMenuButton; anchors.left: symShiftKey.right; height: parent.height; }
353+ HCharKey { id: slashKey; label: "/"; shifted: "/"; anchors.left: languageMenuButton.right; height: parent.height; }
354+ UrlKey { id: urlKey; label: ".com"; extended: [".co.kr", ".or.kr", ".go.kr", ".ac.kr", ".kr"]; anchors.right: dotKey.left; height: parent.height; }
355+ HCharKey { id: dotKey; label: "."; shifted: "."; extended: ["?", "-", "_", "!", "+", "%","#","/"]; extendedShifted: ["?", "-", "_", "!", "+", "%","#","/"]; anchors.right: enterKey.left; height: parent.height; }
356+ ReturnKey { id: enterKey; anchors.right: parent.right; height: parent.height; }
357+ }
358+ } // column
359+}
360
361=== added file 'plugins/ko/qml/Keyboard_ko_url_search.qml'
362--- plugins/ko/qml/Keyboard_ko_url_search.qml 1970-01-01 00:00:00 +0000
363+++ plugins/ko/qml/Keyboard_ko_url_search.qml 2016-07-21 02:52:13 +0000
364@@ -0,0 +1,93 @@
365+/*
366+ * Copyright 2016 Canonical Ltd.
367+ *
368+ * This program is free software; you can redistribute it and/or modify
369+ * it under the terms of the GNU Lesser General Public License as published by
370+ * the Free Software Foundation; version 3.
371+ *
372+ * This program is distributed in the hope that it will be useful,
373+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
374+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
375+ * GNU Lesser General Public License for more details.
376+ *
377+ * You should have received a copy of the GNU Lesser General Public License
378+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
379+ */
380+
381+import QtQuick 2.4
382+import "keys/"
383+import keys 1.0
384+
385+KeyPad {
386+ anchors.fill: parent
387+
388+ content: c1
389+ symbols: "languages/Keyboard_symbols.qml"
390+
391+ Column {
392+ id: c1
393+ anchors.fill: parent
394+ spacing: 0
395+
396+ Row {
397+ anchors.horizontalCenter: parent.horizontalCenter;
398+ spacing: 0
399+
400+ HCharKey { label: "ㅂ"; shifted: "ㅃ"; extended: ["1"]; extendedShifted: ["1"]; leftSide: true; }
401+ HCharKey { label: "ㅈ"; shifted: "ㅉ"; extended: ["2"]; extendedShifted: ["2"] }
402+ HCharKey { label: "ㄷ"; shifted: "ㄸ"; extended: ["3"]; extendedShifted: ["3"] }
403+ HCharKey { label: "ㄱ"; shifted: "ㄲ"; extended: ["4"]; extendedShifted: ["4"] }
404+ HCharKey { label: "ㅅ"; shifted: "ㅆ"; extended: ["5"]; extendedShifted: ["5"] }
405+ HCharKey { label: "ㅛ"; shifted: "ㅛ"; extended: ["6"]; extendedShifted: ["6"] }
406+ HCharKey { label: "ㅕ"; shifted: "ㅕ"; extended: ["7"]; extendedShifted: ["7"] }
407+ HCharKey { label: "ㅑ"; shifted: "ㅑ"; extended: ["8"]; extendedShifted: ["8"] }
408+ HCharKey { label: "ㅐ"; shifted: "ㅒ"; extended: ["9"]; extendedShifted: ["9"] }
409+ HCharKey { label: "ㅔ"; shifted: "ㅖ"; extended: ["0"]; extendedShifted: ["0"]; rightSide: true; }
410+ }
411+
412+ Row {
413+ anchors.horizontalCenter: parent.horizontalCenter;
414+ spacing: 0
415+
416+ HCharKey { label: "ㅁ"; shifted: "ㅁ"; leftSide: true; }
417+ HCharKey { label: "ㄴ"; shifted: "ㄴ"; }
418+ HCharKey { label: "ㅇ"; shifted: "ㅇ"; }
419+ HCharKey { label: "ㄹ"; shifted: "ㄹ"; }
420+ HCharKey { label: "ㅎ"; shifted: "ㅎ"; }
421+ HCharKey { label: "ㅗ"; shifted: "ㅗ"; }
422+ HCharKey { label: "ㅓ"; shifted: "ㅓ"; }
423+ HCharKey { label: "ㅏ"; shifted: "ㅏ"; }
424+ HCharKey { label: "ㅣ"; shifted: "ㅣ"; rightSide: true; }
425+ }
426+
427+ Row {
428+ anchors.horizontalCenter: parent.horizontalCenter;
429+ spacing: 0
430+
431+ ShiftKey {}
432+ HCharKey { label: "ㅋ"; shifted: "ㅋ"; }
433+ HCharKey { label: "ㅌ"; shifted: "ㅌ"; }
434+ HCharKey { label: "ㅊ"; shifted: "ㅊ"; }
435+ HCharKey { label: "ㅍ"; shifted: "ㅍ"; }
436+ HCharKey { label: "ㅠ"; shifted: "ㅠ"; }
437+ HCharKey { label: "ㅜ"; shifted: "ㅜ"; }
438+ HCharKey { label: "ㅡ"; shifted: "ㅡ"; }
439+ HBackspaceKey {}
440+ }
441+
442+ Item {
443+ anchors.left: parent.left
444+ anchors.right: parent.right
445+
446+ height: panel.keyHeight + units.gu(UI.row_margin);
447+
448+ SymbolShiftKey { id: symShiftKey; anchors.left: parent.left; height: parent.height; }
449+ LanguageKey { id: languageMenuButton; anchors.left: symShiftKey.right; height: parent.height; }
450+ HCharKey { id: slashKey; label: "/"; shifted: "/"; anchors.left: languageMenuButton.right; height: parent.height; }
451+ SpaceKey { id: spaceKey; anchors.left: slashKey.right; anchors.right: urlKey.left; noMagnifier: true; height: parent.height; }
452+ UrlKey { id: urlKey; label: ".com"; extended: [".co.kr", ".or.kr", ".go.kr", ".ac.kr", ".kr"]; anchors.right: dotKey.left; height: parent.height; }
453+ HCharKey { id: dotKey; label: "."; shifted: "."; extended: ["?", "-", "_", "!", "+", "%","#","/"]; extendedShifted: ["?", "-", "_", "!", "+", "%","#","/"]; anchors.right: enterKey.left; height: parent.height; }
454+ ReturnKey { id: enterKey; anchors.right: parent.right; height: parent.height; }
455+ }
456+ } // column
457+}
458
459=== added directory 'plugins/ko/qml/keys'
460=== added file 'plugins/ko/qml/keys/HBackspaceKey.qml'
461--- plugins/ko/qml/keys/HBackspaceKey.qml 1970-01-01 00:00:00 +0000
462+++ plugins/ko/qml/keys/HBackspaceKey.qml 2016-07-21 02:52:13 +0000
463@@ -0,0 +1,81 @@
464+/*
465+ * Copyright 2016 Canonical Ltd.
466+ *
467+ * This program is free software; you can redistribute it and/or modify
468+ * it under the terms of the GNU Lesser General Public License as published by
469+ * the Free Software Foundation; version 3.
470+ *
471+ * This program is distributed in the hope that it will be useful,
472+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
473+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
474+ * GNU Lesser General Public License for more details.
475+ *
476+ * You should have received a copy of the GNU Lesser General Public License
477+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
478+ */
479+
480+import QtQuick 2.4
481+import keys 1.0
482+
483+import "parser.js" as Parser
484+
485+ActionKey {
486+ iconNormal: "erase";
487+ iconShifted: "erase";
488+ iconCapsLock: "erase";
489+ action: "backspace";
490+
491+ property string preedit: maliit_input_method.preedit
492+ property string m_preedit: ""
493+ property string syllable_preedit: ""
494+ property string last_preedit: ""
495+ property bool isPreedit: preedit != ""
496+
497+ overridePressArea: true;
498+
499+ onReleased: {
500+ if (isPreedit) {
501+ if (preedit.length > 1){ /* at least 2 length */
502+ syllable_preedit = preedit.substring(0,preedit.length - 1);
503+ last_preedit = preedit[preedit.length - 1]; /* last jamo or syllable */
504+
505+ m_preedit = Parser.erase_jamo(last_preedit);
506+ if (m_preedit != ""){ /* exsit jamo */
507+ maliit_input_method.preedit = syllable_preedit + m_preedit;
508+ } else {
509+ maliit_input_method.preedit = syllable_preedit;
510+ }
511+ } else {
512+ if (Parser.is_syllable(preedit)){ /* preedit is one syllable */
513+ m_preedit = Parser.erase_jamo(preedit);
514+ maliit_input_method.preedit = m_preedit;
515+ } else { /* it is only jamo like "ㄱ" or "ㅏ" */
516+ event_handler.onKeyReleased("", action);
517+ }
518+ }
519+ } else {
520+ event_handler.onKeyReleased("", action);
521+ }
522+ }
523+
524+ onPressed: {
525+ if (maliit_input_method.useAudioFeedback)
526+ audioFeedback.play();
527+
528+ if (maliit_input_method.useHapticFeedback)
529+ pressEffect.start();
530+
531+ if (!isPreedit) {
532+ event_handler.onKeyPressed("", action);
533+ } else {
534+ if (preedit.length == 1 && !Parser.is_syllable(preedit)) /* fixed erase action repeat */
535+ event_handler.onKeyPressed("", action);
536+ }
537+ }
538+
539+ onPressAndHold: {
540+ return;
541+ }
542+
543+
544+}
545
546=== added file 'plugins/ko/qml/keys/HCharKey.qml'
547--- plugins/ko/qml/keys/HCharKey.qml 1970-01-01 00:00:00 +0000
548+++ plugins/ko/qml/keys/HCharKey.qml 2016-07-21 02:52:13 +0000
549@@ -0,0 +1,58 @@
550+/*
551+ * Copyright 2016 Canonical Ltd.
552+ *
553+ * This program is free software; you can redistribute it and/or modify
554+ * it under the terms of the GNU Lesser General Public License as published by
555+ * the Free Software Foundation; version 3.
556+ *
557+ * This program is distributed in the hope that it will be useful,
558+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
559+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
560+ * GNU Lesser General Public License for more details.
561+ *
562+ * You should have received a copy of the GNU Lesser General Public License
563+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
564+ */
565+
566+import QtQuick 2.4
567+import QtMultimedia 5.0
568+import Ubuntu.Components 1.3
569+import Ubuntu.Components.Popups 1.3
570+
571+import keys 1.0
572+import "key_constants.js" as UI
573+import "parser.js" as Parser
574+
575+CharKey {
576+
577+ allowPreeditHandler: true
578+ preeditHandler: handler
579+
580+ Item {
581+ id: handler
582+
583+ //Each Hangul represents one syllable. The Hangul are composed of jamo.
584+ //It need to manipulate preedit until compose one syllable.
585+
586+ function onKeyReleased(keyString, action) {
587+ // get previous preedit string
588+ var preedit = maliit_input_method.preedit;
589+
590+ if (Parser.is_hangul(keyString)) {
591+ // parsing preedit until compose one syllable.
592+ if (preedit.length > 1) {
593+ var syllableString = preedit.substring(0,preedit.length - 1);
594+ var preeditString = preedit[preedit.length - 1];
595+ maliit_input_method.preedit = syllableString + Parser.add_jamo(preeditString, keyString);
596+ } else {
597+ maliit_input_method.preedit = Parser.add_jamo(preedit, keyString);
598+ }
599+
600+ return;
601+ }
602+
603+ maliit_input_method.preedit = preedit + keyString;
604+ event_handler.onKeyReleased("", "commit");
605+ }
606+ }
607+}
608
609=== added file 'plugins/ko/qml/keys/key_constants.js'
610--- plugins/ko/qml/keys/key_constants.js 1970-01-01 00:00:00 +0000
611+++ plugins/ko/qml/keys/key_constants.js 2016-07-21 02:52:13 +0000
612@@ -0,0 +1,19 @@
613+/*
614+ * Copyright 2016 Canonical Ltd.
615+ *
616+ * This program is free software; you can redistribute it and/or modify
617+ * it under the terms of the GNU General Public License as published by
618+ * the Free Software Foundation; version 3.
619+ *
620+ * This program is distributed in the hope that it will be useful,
621+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
622+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
623+ * GNU General Public License for more details.
624+ *
625+ * You should have received a copy of the GNU General Public License
626+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
627+ */
628+
629+.pragma library
630+
631+Qt.include("../../../keys/key_constants.js")
632
633=== added file 'plugins/ko/qml/keys/languages.js'
634--- plugins/ko/qml/keys/languages.js 1970-01-01 00:00:00 +0000
635+++ plugins/ko/qml/keys/languages.js 2016-07-21 02:52:13 +0000
636@@ -0,0 +1,19 @@
637+/*
638+ * Copyright 2016 Canonical Ltd.
639+ *
640+ * This program is free software; you can redistribute it and/or modify
641+ * it under the terms of the GNU General Public License as published by
642+ * the Free Software Foundation; version 3.
643+ *
644+ * This program is distributed in the hope that it will be useful,
645+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
646+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
647+ * GNU General Public License for more details.
648+ *
649+ * You should have received a copy of the GNU General Public License
650+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
651+ */
652+
653+.pragma library
654+
655+Qt.include("../../../keys/languages.js")
656
657=== added file 'plugins/ko/qml/keys/parser.js'
658--- plugins/ko/qml/keys/parser.js 1970-01-01 00:00:00 +0000
659+++ plugins/ko/qml/keys/parser.js 2016-07-21 02:52:13 +0000
660@@ -0,0 +1,245 @@
661+/*
662+* Copyright (C) 2014 Henry Carlson
663+*
664+* This library is free software; you can redistribute it and/or
665+* modify it under the terms of the GNU Lesser General Public
666+* License as published by the Free Software Foundation; either
667+* version 2.1 of the License, or (at your option) any later version.
668+*
669+* This library is distributed in the hope that it will be useful,
670+* but WITHOUT ANY WARRANTY; without even the implied warranty of
671+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
672+* Lesser General Public License for more details.
673+*
674+* You should have received a copy of the GNU Lesser General Public
675+* License along with this library; if not, write to the Free Software
676+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
677+*/
678+
679+/**
680+* Library for combining Hangul Jamo into syllables.
681+*
682+* References for hangul implementation in unicode:
683+* http://gernot-katzers-spice-pages.com/var/korean_hangul_unicode.html
684+* http://www.decodeunicode.org/en/hangul_jamo
685+* http://www.unicode.org/faq/korean.html
686+**/
687+
688+var _BASE_OFFS = 0xAC00;
689+var _COMP_OFFS = 0x3130;
690+
691+var _JAMO_LEAD = 0x1100;
692+var _JAMO_VOWEL = 0x1161;
693+var _JAMO_TRAIL = 0x11A8;
694+
695+var _VOWEL_DIV = 28;
696+var _LEAD_DIV = 588;
697+
698+
699+// map compatibility jamo (virtual keyboard rendering) to component jamo
700+var LEAD = {
701+ 'ㄱ': 0x1100, 'ㄲ': 0x1101, 'ㄴ': 0x1102, 'ㄷ': 0x1103, 'ㄸ': 0x1104,
702+ 'ㄹ': 0x1105, 'ㅁ': 0x1106, 'ㅂ': 0x1107, 'ㅃ': 0x1108, 'ㅅ': 0x1109,
703+ 'ㅆ': 0x110A, 'ㅇ': 0x110B, 'ㅈ': 0x110C, 'ㅉ': 0x110D, 'ㅊ': 0x110E,
704+ 'ㅋ': 0x110F, 'ㅌ': 0x1110, 'ㅍ': 0x1111, 'ㅎ': 0x1112
705+};
706+var VOWEL = {
707+ 'ㅏ': 0x1161, 'ㅐ': 0x1162, 'ㅑ': 0x1163, 'ㅒ': 0x1164, 'ㅓ': 0x1165,
708+ 'ㅔ': 0x1166, 'ㅕ': 0x1167, 'ㅖ': 0x1168, 'ㅗ': 0x1169, 'ㅛ': 0x116D,
709+ 'ㅜ': 0x116E, 'ㅠ': 0x1172, 'ㅡ': 0x1173, 'ㅣ': 0x1175
710+};
711+var TRAIL = {
712+ 'ㄱ': 0x11A8, 'ㄲ': 0x11A9, 'ㄴ': 0x11AB, 'ㄷ': 0x11AE, 'ㄹ': 0x11AF,
713+ 'ㅁ': 0x11B7, 'ㅂ': 0x11B8, 'ㅅ': 0x11BA, 'ㅆ': 0x11BB, 'ㅇ': 0x11BC,
714+ 'ㅈ': 0x11BD, 'ㅊ': 0x11BE, 'ㅋ': 0x11BF, 'ㅌ': 0x11C0, 'ㅍ': 0x11C1,
715+ 'ㅎ': 0x11C2
716+};
717+
718+// mappings for legal vowel diphthongs and compound consonant padchims
719+var VOWEL_COMPOUND = {
720+ 'ㅗㅏ': 0x116A, 'ㅗㅐ': 0x116B, 'ㅗㅣ': 0x116C, 'ㅜㅓ': 0x116F,
721+ 'ㅜㅔ': 0x1170, 'ㅜㅣ': 0x1171, 'ㅡㅣ': 0x1174
722+}
723+var TRAIL_COMPOUND = {
724+ 'ㄱㅅ': 0x11AA, 'ㄴㅈ': 0x11AC, 'ㄴㅎ': 0x11AD,
725+ 'ㄹㄱ': 0x11B0, 'ㄹㅁ': 0x11B1, 'ㄹㅂ': 0x11B2,
726+ 'ㄹㅅ': 0x11B3, 'ㄹㅌ': 0x11B4, 'ㄹㅍ': 0x11B5,
727+ 'ㄹㅎ': 0x11B6, 'ㅂㅅ': 0x11B9
728+};
729+
730+
731+function is_jamo(str) {
732+ if (typeof str !== "string")
733+ return false;
734+
735+ if ((str.charCodeAt(0) >= _COMP_OFFS) && (str.charCodeAt(0) <= 0x318F))
736+ return true;
737+ return false;
738+}
739+
740+function is_syllable(str) {
741+ if (typeof str !== "string")
742+ return false;
743+
744+ if ((str.charCodeAt(0) >= _BASE_OFFS) && (str.charCodeAt(0) <= 0xD7A3))
745+ return true;
746+ return false;
747+}
748+
749+function is_hangul(str) {
750+ if (is_jamo(str) || is_syllable(str))
751+ return true;
752+ return false;
753+}
754+
755+// convert compatiblity jamo into component jamo
756+function get_component(str, table) {
757+ if (table[str] != null)
758+ return String.fromCharCode(table[str]);
759+ return str;
760+}
761+
762+function normalise(str, table) {
763+ for (var jamo in table) {
764+ if (table[jamo] === str.charCodeAt(0))
765+ return jamo;
766+ }
767+ return str;
768+}
769+
770+/**
771+/ Get base offset for jamo type
772+/ Valid values: _JAMO_LEAD, _JAMO_VOWEL, _JAMO_TRAIL
773+*/
774+function get_base(str) {
775+ var code = str.charCodeAt(0);
776+ if (code >= _JAMO_TRAIL)
777+ return _JAMO_TRAIL;
778+ else if (code >= _JAMO_VOWEL)
779+ return _JAMO_VOWEL;
780+ else if (code >= _JAMO_LEAD)
781+ return _JAMO_LEAD;
782+ return -1;
783+}
784+
785+
786+
787+// merge jamo into a syllable block
788+function join(lead, vowel, trail) {
789+ if (is_jamo(lead))
790+ lead = get_component(lead, LEAD);
791+ if (is_jamo(vowel))
792+ vowel = get_component(vowel, VOWEL);
793+ if (is_jamo(trail))
794+ trail = get_component(trail, TRAIL);
795+
796+ var lead_offs = lead.charCodeAt(0) - _JAMO_LEAD;
797+ var vowel_offs = vowel.charCodeAt(0) - _JAMO_VOWEL;
798+ var trail_offs = -1;
799+
800+ if (trail !== "")
801+ trail_offs = trail.charCodeAt(0) - _JAMO_TRAIL;
802+
803+ return String.fromCharCode(
804+ trail_offs + vowel_offs*_VOWEL_DIV +
805+ lead_offs*_LEAD_DIV +
806+ _BASE_OFFS + 1
807+ );
808+}
809+
810+// split a syllable block into component jamo
811+function split(str) {
812+ var char_offs = str.charCodeAt(0) - _BASE_OFFS;
813+ var trail_offs = Math.floor(char_offs%_VOWEL_DIV - 1);
814+ var vowel_offs = Math.floor((char_offs - trail_offs)%_LEAD_DIV / _VOWEL_DIV);
815+ var lead_offs = Math.floor(char_offs/_LEAD_DIV);
816+
817+ var trail = "";
818+
819+ if (trail_offs !== -1)
820+ trail = normalise(String.fromCharCode(_JAMO_TRAIL + trail_offs), TRAIL);
821+
822+ return [
823+ normalise(String.fromCharCode(_JAMO_LEAD + lead_offs), LEAD),
824+ normalise(String.fromCharCode(_JAMO_VOWEL + vowel_offs), VOWEL),
825+ trail
826+ ];
827+}
828+
829+// merge new jamo with the existing string
830+function add_jamo(str, jamo) {
831+ // make sure merging is actually a valid option
832+ if (is_jamo(jamo) && is_hangul(str)) {
833+
834+
835+ // merge jamo with jamo
836+ if (is_jamo(str)) {
837+ var lead = get_component(str, LEAD);
838+ var vowel = get_component(jamo, VOWEL);
839+
840+ if ((get_base(lead) === _JAMO_LEAD) && (get_base(vowel) === _JAMO_VOWEL))
841+ return join(lead, vowel, '');
842+
843+ // merge syllable with jamo
844+ } else {
845+ var buffer = split(str);
846+
847+ // merging vowel to existing syllable
848+ if (get_base(get_component(jamo, VOWEL)) === _JAMO_VOWEL) {
849+ // if syllable has padchim, split into two complete syllables:
850+ if (buffer[2] !== "") {
851+ // verify that the padchim is a legal initial consonant:
852+ if (get_component(buffer[2], LEAD) !== buffer[2]) {
853+ return join(buffer[0], buffer[1], '') + join(normalise(buffer[2], TRAIL), jamo, '');
854+ } else {
855+ // if the padchim is a compound consonant, we want to split it:
856+ var split_trail = normalise(buffer[2], TRAIL_COMPOUND);
857+ if (split_trail !== buffer[2])
858+ return join(buffer[0], buffer[1], split_trail[0]) + join(split_trail[1], jamo, '');
859+ }
860+ return str + jamo;
861+ }
862+
863+ // attempt vowel mergers:
864+ var vowel = VOWEL_COMPOUND[buffer[1] + jamo];
865+ if (vowel != null)
866+ return join(buffer[0], String.fromCharCode(vowel), '');
867+
868+ // merging consonant to existing syllable
869+ } else if (get_base(get_component(jamo, TRAIL)) === _JAMO_TRAIL) {
870+ if (buffer[2] === "") {
871+ // verify that the consonant is a legal trailing consonant:
872+ if (get_component(jamo, TRAIL) !== jamo)
873+ return join(buffer[0], buffer[1], jamo);
874+ return str + jamo;
875+ }
876+
877+ // attempt consonant mergers:
878+ var trail = TRAIL_COMPOUND[buffer[2] + jamo];
879+ if (trail != null)
880+ return join(buffer[0], buffer[1], String.fromCharCode(trail));
881+ }
882+ }
883+ }
884+
885+ return str + jamo;
886+}
887+
888+// erase jamo from the syllable under creation
889+function erase_jamo(str) {
890+ if (is_jamo(str) || !is_hangul(str))
891+ return "";
892+
893+ var buffer = split(str);
894+ if (buffer[2] !== "") {
895+ var split_trail = normalise(buffer[2], TRAIL_COMPOUND);
896+ if (split_trail !== buffer[2])
897+ return join(buffer[0], buffer[1], split_trail[0]);
898+ return join(buffer[0], buffer[1], "");
899+ } else {
900+ var split_vowel = normalise(buffer[1], VOWEL_COMPOUND);
901+ if (split_vowel !== buffer[1])
902+ return join(buffer[0], split_vowel[0], '');
903+ return buffer[0];
904+ }
905+}
906
907=== added file 'plugins/ko/qml/qml.pro'
908--- plugins/ko/qml/qml.pro 1970-01-01 00:00:00 +0000
909+++ plugins/ko/qml/qml.pro 2016-07-21 02:52:13 +0000
910@@ -0,0 +1,24 @@
911+TOP_BUILDDIR = $$OUT_PWD/../../..
912+TOP_SRCDIR = $$PWD/../../..
913+
914+include($${TOP_SRCDIR}/config.pri)
915+
916+TARGET = dummy
917+TEMPLATE = lib
918+
919+lang_ko.path = "$$UBUNTU_KEYBOARD_LIB_DIR/ko/"
920+lang_ko.files = *.qml *.js
921+
922+lang_ko_keys.path = "$$UBUNTU_KEYBOARD_LIB_DIR/ko/keys"
923+lang_ko_keys.files = keys/*.qml keys/*.js
924+
925+INSTALLS += lang_ko lang_ko_keys
926+
927+# for QtCreator
928+OTHER_FILES += \
929+ Keyboard_ko.qml \
930+ Keyboard_ko_email.qml \
931+ Keyboard_ko_url.qml \
932+ Keyboard_ko_url_search.qml
933+
934+
935
936=== added directory 'plugins/ko/src'
937=== added file 'plugins/ko/src/database_ko.db'
938Binary files plugins/ko/src/database_ko.db 1970-01-01 00:00:00 +0000 and plugins/ko/src/database_ko.db 2016-07-21 02:52:13 +0000 differ
939=== added file 'plugins/ko/src/korean.txt'
940--- plugins/ko/src/korean.txt 1970-01-01 00:00:00 +0000
941+++ plugins/ko/src/korean.txt 2016-07-21 02:52:13 +0000
942@@ -0,0 +1,253 @@
943+산골 나그네
944+
945+김유
946+
947+밤이 깊어도 술군은 역시 들지 않는다. 메주 뜨는 냄새와 같이 퀴퀴한 냄새로 방안은 쾨쾨하다. 웃간에는 쥐들이 찍찍거린다. 홀어머니는 쪽 떨어진 화로를 끼고 앉아서 쓸쓸한 채로 곰곰 생각에 젖는다. 가뜩이나 침침한 반짝 등불이 북쪽 지게문에 뚫린 구멍으로 새 드는 바람에 반득이며 빛을 잃는다. 헌버선짝으로 구멍을 틀어막는다. 그러고 등잔 밑으로 반짇고리를 끌어당기며 시름없이 바늘을 집어 든다.
948+
949+산골의 가을은 왜 이리 고적할까? 앞뒤 울타리에서 부수수하고 떨잎은 진다. 바로 그것이 귀밑에서 들리는 듯 나직나직 속삭인다. 더욱 몹쓸 건 물소리, 골을 휘몰아 맑은 샘은 흘러내리고 야릇하게도 음률을 읊는다.
950+
951+퐁! 퐁 퐁! 쪼록 퐁!
952+
953+바깥에서 신발 소리가 자작자작 들린다. 귀가 번쩍 띄어 그는 방문을 가볍게 열어젖힌다. 머리를 내밀며,
954+
955+「덕돌이냐?」하고 반겼으나 잠잠하다. 앞뜰 건너편 수평을 감돌아 싸늘한 바람이 낙엽을 흩뿌리며 얼굴에 부딪친다.
956+
957+용마루가 쌩쌩 운다. 모진 바람 소리에 놀라 멀리서 밤개가 요란히 짖는다.
958+
959+「쥔 어른 계서유?」
960+
961+몸을 돌리어 바느질거리를 다시 들려 할 제 이번에는 짜장 인기가 난다. 황급하게,
962+
963+「누구유?」 하고 일어서며 문을 열어 보았다.
964+
965+「왜 그러유?」
966+
967+처음 보는 아낙네가 마루 끝에 와 섰다. 달빛에 비끼어 검붉은 얼굴이 해쓱하다. 추운 모양이다. 그는 한 손으로 머리에 둘렀던 왜수건을 벗어 들고는 다른 손으로 흩어진 머리칼을 쓰담아올리며 수줍은 듯이 쭈뼛쭈뼛한다.
968+
969+「저어, 하룻밤만 드새고 가게 해주세유.」
970+
971+남정네도 아닌데 이 밤중에 웬일인가, 맨발에 짚신짝으로, 그야 아무렇던……
972+
973+「어서 들어와 불 쬐게유.」
974+
975+나그네는 주춤주춤 방 안으로 들어와서 화로 곁에 도사려 앉는다. 낡은 치맛자락 위로 삐지려는 속살을 아무리자 허리를 지그시 튼다. 그리고는 묵묵하다. 주인은 물끄러미 보고 있다가 밥을 좀 주려느냐고 물어보아도 잠자코 있다.
976+
977+그러나 먹던 대궁을 주워 모아 짠지쪽하고 갖다 주니 감지덕지 받는다. 그리고 물 한 모금 마심 없이 잠깐 동안에 밥그릇의 밑바닥을 긁는다.
978+
979+밥숟갈을 놓기가 무섭게 주인은 이야기를 붙이기 시작하였다. 미주알고주알 물어보니 이야기는 지수가 없다. 자기로도 너무 지쳐 물은 듯싶은 만치 대구 추근거렸다. 나그네는 싫단 기색도 좋단 기색도 별로 없이 시나브로 대꾸하였다. 남편 없고 몸 붙일 곳 없다는 것을 간단히 말하고 난 뒤,
980+
981+「이리 저리 얻어먹고 단게유.」 하고 턱을 가슴에 묻는다.
982+
983+첫 닭이 홰를 칠 때 그제야 마을갔던 덕돌이가 돌아온다. 문을 열고 감사나운 머리를 디밀려다 낯선 아낙네를 보고 눈의 휘둥그렇게 주춤한다. 열린 문으로 억센 바람이 몰아들며 방 안이 캄캄하다. 주인은 문 앞으로 걸어와 서며 덕돌이의 등을 뚜덕거린다. 젊은 여자 자는 방에서 떠꺼머리 총각을 재우는 건 상서롭지 못한 일이었다.
984+
985+「얘, 덕돌아, 오늘은 마을 가 자고 아침에 온.」
986+
987+
988+가을할 때가 지났으니 돈냥이나 좋이 퍼질 때도 되었다. 그 돈들이 어디로 몰키는지 이 술집에서는 좀체 돈맛을 못 본다. 술을 판대야 한 초롱에 오륙십 전 떨어진다. 그 한 초롱을 잘 친대도 사날씩이나 걸리는 걸 요새 같아선 그 잘량한(‘알량한’ 사투리) 술군까지 씨가 말랐다. 어쩌다 전일에 퍼놓았던 외상값도 갖다줄 줄을 모른다. 홀어미는 열벙거지가 나서 이른 아침부터 돈을 받으러 돌아다녔다. 그러나 다리품을 들인 보람도 없었다. 낼 사람이 즐겨야 할 텐데 우물쭈물하며 한단 소리가 좀 두고 보자는 것이 고작이었다. 그렇다고 안 갈수도 없는 노릇이다. 나날이 양식은 달리고 지점집에서 집행을 하느니 뭘 하느니 독촉이 어지간치 않음에랴……
989+
990+「저도 이젠 떠나야겠세유.」
991+
992+그가 조반 후 나들이옷을 바꾸어 입고 나서니 나그네도 따라 일어선다. 그의 손을 자상히 붙잡으며 주인은,
993+
994+「고달플 테니 며칠 더 쉬어 가게유」 하였으나,
995+
996+「가야지유. 너무 오래 신세를……」
997+
998+「그런 염려는 말구.」라고 누르며 집 지켜 주는 셈치고 방에 누웠으라 하고는 집을 나섰다.
999+
1000+백두고개를 넘어서 안말로 들어가 해동갑으로 헤매었다. 헤실수로 간 곳도 있기야 하지만 맑았다. 해가 지고 어두울 녘에야 그는 흘부들해서 돌아왔다. 좁쌀 닷 되밖에는 못 받았다. 다른 사람들은 돈 낼 생각은커녕 이러면 다시 술 안 먹겠다고 도리어 얼러 보냈던 것이다. 그러나 이만도 다행이다. 아주 못 받느니보다는 끼니때 가지었다. 그는 좁쌀을 씻고 나그네는 솥에 불을 지피어 부랴부랴 밥을 짓고 일변 상을 보았다.
1001+
1002+밥들을 먹고 앉았으려니깐 갑자기 술군이 몰려든다. 이거 웬일일까. 처음에는 하나가 오더니 다음에는 세 사람 또 두 사람 모두 젊은 축들이다. 그러나 각각들 먹일 방이 없으므로 주인은 좀 망설이다가 그 연유를 말하였으나 뭐 한 동리 사람인데 어떠냐, 한데서 먹게 해달라는 바람에 얼씨구나 하였다. 이제야 운이 트나보다. 양푼에 막걸리를 딸쿠어 나그네에게 주어 솥에 넣고 좀 속히 데워 달라 하였다. 자기는 치마꼬리를 휘둘러 가며 잽싸게 안주를 장만한다. 짠지, 동치미, 고추장, 특별 안주로 삶은 밤도 놓았다. 사촌동생이 맛보라고 며칠 전에 갖다 준 것을 애껴 둔 것이었다.
1003+
1004+방안은 떠들썩하다. 벽을 두드리며 아리랑 찾는 놈에 건으로 너털웃음 치는 놈, 혹은 수군숙덕하는놈, 가지각색이다. 주인이 술상을 받쳐 들고 들어가니 짜기나 한 듯이 일제히 자리를 바로 잡는다. 그 중에 얼굴 넓적한 하이칼라 머리가 야로가 나서 상을 받으며 주인 귀에다 입을 비벼 대인다.
1005+
1006+「아주머니 젊은 갈보 사 왔다지유? 좀 보여 주게유.」
1007+
1008+영문 모를 소문도 다 듣는다.
1009+
1010+「갈보라니 웬 갈보?」 하고 어리삥삥하다 생각을 하니, 턱없는 소리는 아니다. 눈치 있게 부엌으로 내려가서 보강지1 앞에 앉았는 나그네의 머리를 은근히 끌어안았다. 자, 저패들이 새댁을 갈보로 횡보고 찾아온 맥이다. 물론 새댁편으론 망측스러운 일이겠지만 달포나 손님의 그림자가 드물던 우리 집으로 보면 재수의 빗발이다. 술국을 잡는다고 어디가 떨어지는 게 아니요, 욕이 아니니 나를 보아 오늘만 좀 팔아 주기 바란다, 이런 의미를 곰살궂게 간곡히 말하였다. 나그네의 낮은 별반 변함이 없다. 늘 한 양으로 예사로이 승낙하였다.
1011+
1012+술이 온몸에 돌고 나서야 뒷술이 잔풀이가 난다. 한 잔에 5전, 그저 마시긴 아깝다. 얼간한 상투배기가 계집의 손목을 탁 잡아 앞으로 끌어당기며,
1013+
1014+「권주가 좀 해, 이건 뀌어온 보릿자룬가?」
1015+
1016+「권주가? 뭐야유?」
1017+
1018+「권주가? 이 갈보가 권주가도 모르나. 으하하하하」 하고는 무안에 취하여 푹 숙인 계집 뺨에다 꺼칠꺼칠한 턱을 문질러 본다. 소리를 아무리 시켜도 아랫입술을 깨물고는 고개만 기울일 뿐, 소리는 못 하나 보다. 그러나 노래 못하는 꽃도 좋다. 계집은 영 내리는 대로 이 무릎 저 무릎으로 옮아 앉으며 턱 밑에다 술잔을 받쳐 올린다.
1019+
1020+술들이 담뿍 취하였다. 두 사람은 곯아져서 코를 곤다. 계집이 칼라머리 무릎 위에 앉아 담배를 피워 올릴 때 코웃음을 흥 치더니 그 무지스러운 손이 계집의 아래 뱃가죽을 사양 없이 움켜잡는다. 별안간 <아야!> 하고 퍼들껑하더니 계집의 몸뚱아리가 공중으로 뛰어오르다 도로 떨어진다.
1021+
1022+「이 자식아 너만 돈 내고 먹었니?」
1023+
1024+한 사람 새두고 앉았던 상투가 콧살을 찌푸린다. 그리고 맨발 벗은 계집의 두 발을 양손에 붙잡고 가랑이를 쩍 벌려 무릎 위로 지르르 끌어올린다. 계집은 앙탈을 한다. 눈시울에 눈물이 엉기더니 불현듯이 쪼록 쏟아진다.
1025+
1026+방 안에서 왱마가리 소리가 끓어오른다.
1027+
1028+「저 잡놈 보게, 으하하하하.」
1029+
1030+술은 연실 데워서 들어가면서도 주인은 불안하여 마음을 졸였다. 겨우 마음을 놓은 것은 훨씬 밝아서다.
1031+
1032+참새들은 소란히 지저귄다. 기직바닥이 부스럼 자죽보다 질배없다. 술, 짠지쪽, 가래침, 담뱃재 뭣해 너저분하다. 우선 한길치에 자리를 잡고 계배를 대 보았다. 마수걸이가 85전, 외상이 2원 각수다. 현금 85전, 두 손에 들고 앉아 세고 또 세어보고……
1033+
1034+뜰에서는 나그네의 혀로 끌어올리는 인사,
1035+
1036+「안녕히 가시게유.」
1037+
1038+「입이나 좀 맞추고 뽀! 뽀! 뽀!」
1039+
1040+「나두」
1041+
1042+
1043+찌르쿵! 찌르쿵! 찔거러쿵!
1044+
1045+「방앗머리가 무겁지유?…… 고만 까불을까.」
1046+
1047+「들 익었세유. 더 찧어야지유.」
1048+
1049+「그런데 얘는 어쩐 일이야……」
1050+
1051+덕돌이를 읍에 보냈는데 날이 저물어도 여태 오지 않는다. 흩어진 좁쌀을 확에 쓸어 넣으며 홀어머니는 퍽으나 애를 태운다. 요새 날씨가 차지니까 늑대, 호랑이가 차차 마을로 찾아 내린다. 밤길에 고개 같은 데서 만나면 끽 소리도 못하고 욕을 당한다.
1052+
1053+나그네가 방아를 괴놓고 내려와서 키로 확의 좁쌀을 담아 올린다. 주인은 그 머리를 쓰담고 자기의 행주치마를 벗어서 그 위에 씌워 준다. 계집의 나이 열아홉이면 활짝 필 때이건만 버케된 머리칼이며 야윈 얼굴이며 벌써부터 외양이 시들어 간다. 아마 고생을 진한 탓이리라.
1054+
1055+날씬한 허리를 재빨리 놀려가며 일이 끊일 새 없이 다구지게2 덤벼드는 그를 볼 때 주인은 지극히 사랑스러웠다. 그리고 일변 측은도 하였다. 뭣하면 딸과 같이 자기 집에서 길게 살아주었으면 상팔자일 듯싶었다. 그럴 수 있다면 그 소 한 마리와 바꾼대도 이것만은 안 내놓으리라고 생각도 하였다.
1056+
1057+아들만 데리고 홀어머니의 생활은 무던히 호젓하였다. 그런데다 동리에서는 속 모르는 소리까지 한다. 떠꺼머리 총각을 그냥 늙힐 테냐고. 그러나 형세가 부치므로 감히 엄두도 못 내다가 겨우 올 봄에서야 다붙어 서둘게 되었다. 의외로 일은 손쉽게 되었다. 이리저리 언론이 돌더니 남촌산에 어느 집 둘째 딸과 혼약하였다. 일부러 홀어미는 40리 밖이나 걸어서 색시의 손등을 문질러 보고는,
1058+
1059+「참 애기 잘도 생겹세!」
1060+
1061+좋아서 사돈에게 칭찬을 뇌고 뇌곤 하였다.
1062+
1063+그런데 없는 살림에 빚을 내어 혼수를 다 꼬매 놓은 뒤였다. 혼인날을 불과 이틀 격해 놓고 일이 그만 빗났다. 처음에야 그런 말이 없더니 난데 없는 선채금 30원을 가져 오란다. 남의 돈 3원과 집의 돈 5원으로 거추군에게 품삵 노비 주고 혼수하고 단지 2원……잔치에 쓸 것밖에 안 남고 보니 30원이란 입내도 못 낼 소리다. 그 밤, 그는 이리 뒤척 저리 뒤척 넋 잃은 팔을 던져 가며 통 밤을 새웠던 것이다.
1064+
1065+「어머님! 진지 잡수세유.」
1066+
1067+새댁에게 이런 소리를 듣는다면 끔찍이 귀여우리라. 이것이 단 하나의 그의 소원이었다.
1068+
1069+「다리 아프지유? 너머 일만 시켜서……」
1070+
1071+주인은 저녁 좁쌀을 쓸어 넣다가 방앗다리에 깝신대는 나그네를 걸쌈스럽게 쳐다본다. 방아가 무거워서 껍적이며 잘 오르지 않는다. 가냘픈 몸이라 상혈이 되어 두 볼이 샛밝아케 색색거린다. 치마도 치마려니와 명지 저고리는 어찌 삭았는지 어깨께가 손바닥만 하게 척 나갔다. 그러나 덕돌이가 왜포 다섯 자를 바꿔 오거든 첫대 사발허통된 솟곳부터 해입히고 차차 할 수밖엔 없다.
1072+
1073+「같이 찝시다유.」
1074+
1075+주인도 남저지3 방앗다리에 올라섰다. 그리고 찌껑 위에 놓인 나그네의 손을 눈치 채지 않게 슬며시 쥐어보았다. 더도 덜도 말고 그저 이만한 며느리만 얻어도 좋으련만. 나그네와 눈이 마주치자 그는 열적어서4 시선을 돌렸다.
1076+
1077+「퍽도 쓸쓸하지유!」 하며 손으로 울 밖을 가리킨다. 첫밤 같은 석양판이다. 색동저고리를 떨쳐입고 산들은 거방진 방앗소리를 은은히 전한다. 찔그러쿵! 찌러쿵!
1078+
1079+그는 나그네를 금덩이같이 위하였다. 없는 대로 자긔옷가지도 서로 서로 별러 입었다. 그리고 잘 때에는 딸과 진배없이 이불 속에서 품에 꼭 품고 재우곤 하였다. 하지만 자기의 은근한 속심은 차마 입에 드러내어 말을 못 건넸다. 잘 들어주면이어니와 뭣하게 안다면 피차의 낯이 뜨뜻할 일이었다.
1080+
1081+그러나 맘먹지 않았던 우연한 일로 인하여 마침내 기회를 얻게 되었다. 나그네가 온 지 나흘 되던 날이었다. 거문관이 산기슭에 있는 영길네 벼방아를 좀 와서 찧어 달라고 한다. 나그네는 줄밤을 새우므로 낮에나 푸근히 자라고 두고 그는 홀로 나섰다.
1082+
1083+머리에 겨를 보얗기 쓰고 맥이 풀려서 집에 돌아온 것은 이럭저럭 으스레하였다. 늙은 다리를 끌고 뜰 앞으로 향하다가 그는 주춤하였다. 나그네 홀로 자는 방에 덕돌이가 들어갈 리 만무한데 정녕코 그놈일 게다. 마루 끝에 자그마한 나그네의 짚세기5이 놓인 그 옆으로 질목채 벗은 왕달 짚세기이 왁살스럽게 놓였다. 그리고 방에서는 수군수군 낮은 말소리가 흘러나온다. 그는 무심코 닫은 방문께로 귀를 기울였다.
1084+
1085+「그럼 와 그러는 게유? 우리 집이 굶을까봐 그러시유?」
1086+
1087+「……」
1088+
1089+「어머이도 사람은 좋아유……올해 잘만 하면 내년에는 소 한 마리 사 놀 게구, 농사만 해두 한 해에 쌀 넉 섬, 조 엿 섬, 그만하면 고만이지유……내가 싫은 게유?」
1090+
1091+「……」
1092+
1093+「사내가 죽었으니 아뭏든 얻을 게지유?」
1094+
1095+옷 터지는 소리, 부시럭거린다.
1096+
1097+「아이! 아이! 아이! 참! 이거 노세유」
1098+
1099+쥐죽은 듯이 감감하다. 허공에 아롱거리는 낙엽을 이윽히 바라보며 그는 빙그레 한다. 신발 소리를 죽이고 뜰 밖으로 다시 돌쳐섰다.
1100+
1101+저녁상을 물린 후 그는 시치미를 딱 떼고 나그네의 기색을 살펴보다가 입을 열었다.
1102+
1103+「젊은 아낙네가 홀몸으로 돌아다닌대두 고생일 게유. 또 어차피 사내는……」
1104+
1105+여기서부터 사리에 맞도록 이 말 저 말을 주섬주섬 꺼내 오다가 나의 며느리가 되어 줌이 어떻겠느냐고 확 토파를 지었다. 치마를 흡싸고 앉아 갸웃이 듣고 있던 나그네는 치마끈을 깨물며 이마를 떨어뜨린다. 그러고는 두 볼이 발개진다. 젊은 계집이 나 시집 가겠소 하고 누가 나서랴. 이만하면 합의한 거나 틀림없을 것이다.
1106+
1107+혼수는 전에 해둔 것이 있으니 한시름 잊었다. 그대로 이앙이나 고쳐서 입히면 고만이다. 돈 2원은 은비녀, 은가락지 사다가 각별히 색시에게 선물 내리고……
1108+
1109+일은 밀수록 낭패가 많다. 급시로 날을 받아서 대례를 치렀다. 한편에서는 국수를 누른다. 잔치 보러 온 아낙네들은 국수 그릇을 얼른 받아서 후룩후룩 들이마시며 시악시 잘났다고 추었다.
1110+
1111+주인은 즐거움에 너무 겨워서 축배를 흥건히 들었다. 여간 경사가 아니다. 뭇사람을 비집고 안팎으로 드나들며 분부하기에 손이 돌지 않는다.
1112+
1113+「얘 메누라! 국수 한 그릇 더 가져온!」
1114+
1115+어째 말이 좀 어색하구먼……다시 한 번,
1116+
1117+「메누라, 애야! 얼른 가져와.」
1118+
1119+30을 바라보자 동곳을 찔러 보니 제불에 멋이 질려 비드름하다. 덕돌이는 첫날을 치르고 부썩부썩 기운이 난다. 남이 두 단을 털 제면 그의 볏단은 석 단 째 풀려 나간다. 연방 손바닥에 침을 뱉어 붙이며 어깨를 으쓱거린다.
1120+
1121+「끅! 끅! 끅! 찍어라. 굴려라 끅! 끅!」
1122+
1123+동무의 품앗이 일이다. 거무무투록한 젊은 농군 댓이 볏단을 번차례로 집어든다. 열에 뜬 사람같이 식식거리며 세차게 벼알을 절구통배에서 주룩주룩 흘러내린다.
1124+
1125+「얘! 장가 들고 한 턱 안 내니?」
1126+
1127+「일색이더라. 단단히 먹자. 닭이냐? 술이냐? 국수냐?」
1128+
1129+「웬 국수는? 너는 국수만 아느냐?」
1130+
1131+저희끼리 찧고 까분다. 그들은 일을 놓으며 옷깃으로 땀을 씻는다. 골바람이 벼 깔치6를 부옇게 풍긴다. 옆 산에서 푸드덕 하고 꿩이 날며 머리 위를 지나간다. 갈퀴질을 하던 얼굴 넓적이가 갈퀴를 놓고 씽긋하더니 달려든다. 장난군이다. 여러 사람의 힘을 빌어 덕돌이 입에다 헌 짚신짝을 물린다. 버들껑거린다. 다시 양 귀를 두 손에 잔뜩 훔켜잡고 끌어 와서는 털어놓은 벼 무더기 위에 머리를 틀어박으며 동서남북으로 큰절을 시킨다.
1132+
1133+「야아! 야아! 아!」
1134+
1135+「아니다, 아니야. 장갈 갔으면 산신령에게 이러하다 말이 있어야지, 괜시리 산신령이 노하면 눈깔망나니(호랑이) 내려보낸다.」
1136+
1137+뭇 웃음이 터져 오른다. 새신랑의 옷이 이게 뭐냐, 볼기짝에 구멍이 뚫리고……빈정대는 사람도 있다. 그러나 덕돌이는 상투의 먼지를 털고 나서 곰방대를 피워 물고는 싱그레 웃어치운다. 좋은 옷은 집에 두었다. 인조견 조끼, 저고리, 새하얀 옥당목 겹바지, 그러나 애끼는 것이다. 일할 때엔 헌 웃을 입고 집에 돌아와 쉴 참에나 입는다. 잘 때에도 모조리 벗어서 더럽지 않게 착착 개어 머리맡 위에 놓고 자곤 한다. 의복이 남루하면 인상이 추하다. 모처럼 얻은 귀여운 아내니 행여나 마음이 돌아앉을까 미리미리 사려 두지 않을 수도 없는 노릇이다. 그야말로 29년 만에 누런 이 조각에다 이제서야 소금을 발라 본 것도 이 까닭이었다.
1138+
1139+덕돌이가 볏단을 다시 집어 올릴 제 그 이웃에 사는 돌쇠가 옆으로 와서 품을 안는다.
1140+
1141+「얘 덕돌아! 너 내일 우리 조마댕이 좀 해 줄래?」
1142+
1143+「뭐 어째?」 하고 소리를 빽 지르고는 그는 눈귀가 실룩하였다.
1144+
1145+「누구보고 해라야? 응? 이 자식 까놀라.」
1146+
1147+이제까진 턱없이 지냈단대도 오늘의 상투를 못 보는가?
1148+
1149+바로 그날이었다. 웃간에서 혼자 새우잠을 자고 있던 홀어머니는 놀라 눈이 번쩍 띄었다. 만뢰 잠잠한 밤중이다.
1150+
1151+「어머니! 그게 달아났에유, 내 옷도 없구……」
1152+
1153+「응?」 하고 반마디 소리를 치며 얼떨김에 그는 캄캄한 방안을 더듬어 아랫간으로 넘어섰다. 황량히 등잔에 불을 댕기며,
1154+
1155+「그래 어디로 갔단 말이야?」
1156+
1157+영산이 나서 묻는다. 아들은 벌거벗은 채 이불로 앞을 가리고 앉아서 징징거린다. 옆자리에는 빈 베개뿐 사람은 간 곳이 없다. 들어본즉 온종일 일하기에 피곤하여 아들은 자리에 들자 고만 세상을 잊었다. 하기야 그때 안해도 옷을 벗고 누워서 맞붙어 잤던 것이다. 그는 보통 때와 조금도 다름없이 새침하니 드러누워서 천정만 쳐다보았다. 그런데 자다가 벼란간 오줌이 마렵기에 요강을 좀 집어 달래려고 보니 뜻밖에 품안이 허룩하다. 불러 보아도 대답이 없다. 그래서는 어림짐작으로 우선 머리맡 위에 놓았던 옷을 더듬어 보았다. 딴은 없다.
1158+
1159+필연 잠든 틈을 타서 살며시 옷을 입고 자기의 옷이며 버선까지 들고 내뺐음이 분명하리라.
1160+
1161+「도적년!」
1162+
1163+모자는 관솔에 불을 켜 들고 나섰다. 부엌과 잿간을 뒤졌다. 그리고 뜰 앞 수풀 속도 낱낱이 찾아봤으나 흔적도 없다.
1164+
1165+「그래도 방 안을 다시 한 번 찾아보자.」
1166+
1167+홀어미는 구태여 며느리를 도적년으로까지는 생각하고 싶지 않았다. 거반 울상이 되어 허벙저벙 방 안으로 들어왔다. 마음을 가라앉혀 들쳐 보니 아니나 다르랴 며느리 베개 밑에서 은비녀가 나온다. 달아날 계집 같으면 이 비싼 은비녀를 그냥 두고 갈 리 없다.
1168+
1169+두말없이 무슨 병패가 생겼다. 홀어미는 아들을 데리고 덜미를 잡히는 듯 문 밖으로 찾아 나섰다.
1170+
1171+
1172+마을에서 산길로 빠져나는 어귀에 우거진 숲 사이로 비스듬히 언덕길이 놓였다. 바로 그 밑에 석벽을 끼고 깊고 푸른 웅덩이가 묻히고 넓은 그 물이 겹겹 산을 에돌아 약 10리를 흘러내리면 신영강 중턱을 뚫는다. 시새에 반쯤 파묻히어 번들대는 큰 바위는 내를 싸고 양쪽으로 질펀하다. 꼬부랑길은 그 틈바귀로 뻗었다. 좀체 걷지 못할 자갈길이다. 내를 몇 번 건너고 험상궂은 산들을 비켜서 한 5마장 넘어야 겨우 길다란 길을 만난다. 그리고 거기서 좀더 간 곳에 냇가에 외지게 잃어진 오막살이 한 간을 볼 수 있다. 물방앗간이다. 그러나 이제는 밥을 찾아 흘러가는 뜬 몸들의 하룻밤 숙소로 변하였다. 벽이 확 나가고 네 기둥뿐인 그 속에 힘을 잃은 물방아는 을씨년궂게 모로 누웠다. 거지도 그 옆에 홑이불 위에 거적을 쓰고 누웠다. 거푸진 신음이다. 으! 으! 으응! 서까래 사이로 달빛은 쌀쌀히 흘러든다. 가끔 마른 잎을 뿌리며……
1173+
1174+「여보 자우? 일어나게유 얼핀.」
1175+
1176+계집의 음성이 나자 그는 꾸물거리며 일어나 앉는다. 그리고 너털대는 홑적삼의 깃을 여며 잡고는 덜덜 떤다.
1177+
1178+「인제 고만 떠날테이야? 쿨룩……」
1179+
1180+말라빠진 얼굴로 계집을 바라보며 그는 이렇게 물었다.
1181+
1182+10분 가량 지났다. 거지는 호사하였다. 달빛에 번쩍거리는 겹옷을 입고서 지팡이를 끌며 물방앗간을 등졌다. 골골 하는 그를 부축하여 계집은 뒤에 따른다. 술집 며느리다.
1183+
1184+「옷이 너무 커, 좀 작았으면……」
1185+
1186+「잔말 말고 어여 갑시다, 펄쩍…….」
1187+
1188+계집은 부리나케 그를 재촉한다. 그리고 연해 돌아다보길 잊지 않았다. 그들은 강길로 향한다. 개울을 건너 불거져 내린 산모퉁이를 막 꼽뜨리려 할 제다. 멀리 뒤에서 사람 욱이는 소리가 끊일 듯 날 듯 간신히 들려온다. 바람에 묻히어 말소리는 모르겠으나 재없이 덕돌이의 목성임은 넉히 짐작할 수 있다.
1189+
1190+「아 얼른 좀 오게유.」
1191+
1192+똥끝이 마르는 듯이 계집은 사정내의 손목을 겁겁히 잡아끈다. 병든 몸이라 끌리는 대로 뒤툭거리며 거지도 으슥한 산 저편으로 같이 사라진다. 수은빛 같은 물방울을 뿜으며 물결은 산벽에 부닥뜨린다. 어디선지 지정치 못할 늑대 소리는 이산 저산서 와글와글 굴러 내린다.
1193+
1194+
1195+
1196
1197=== added file 'plugins/ko/src/koreanlanguagefeatures.cpp'
1198--- plugins/ko/src/koreanlanguagefeatures.cpp 1970-01-01 00:00:00 +0000
1199+++ plugins/ko/src/koreanlanguagefeatures.cpp 2016-07-21 02:52:13 +0000
1200@@ -0,0 +1,89 @@
1201+/*
1202+ * Copyright 2016 Canonical Ltd.
1203+ *
1204+ * This program is free software; you can redistribute it and/or modify
1205+ * it under the terms of the GNU Lesser General Public License as published by
1206+ * the Free Software Foundation; version 3.
1207+ *
1208+ * This program is distributed in the hope that it will be useful,
1209+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1210+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1211+ * GNU Lesser General Public License for more details.
1212+ *
1213+ * You should have received a copy of the GNU Lesser General Public License
1214+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1215+ */
1216+
1217+#include "koreanlanguagefeatures.h"
1218+
1219+KoreanLanguageFeatures::KoreanLanguageFeatures(QObject *parent) :
1220+ QObject(parent)
1221+{
1222+}
1223+
1224+KoreanLanguageFeatures::~KoreanLanguageFeatures()
1225+{
1226+}
1227+
1228+bool KoreanLanguageFeatures::alwaysShowSuggestions() const
1229+{
1230+ return false;
1231+}
1232+
1233+bool KoreanLanguageFeatures::autoCapsAvailable() const
1234+{
1235+ return false;
1236+}
1237+
1238+bool KoreanLanguageFeatures::activateAutoCaps(const QString &preedit) const
1239+{
1240+ Q_UNUSED(preedit)
1241+ return false;
1242+}
1243+
1244+QString KoreanLanguageFeatures::appendixForReplacedPreedit(const QString &preedit) const
1245+{
1246+ if (preedit.isEmpty())
1247+ return QString("");
1248+
1249+ return QString(" ");
1250+}
1251+
1252+bool KoreanLanguageFeatures::isSeparator(const QString &text) const
1253+{
1254+ static const QString separators = QString::fromUtf8("。、,!?:;.\r\n");
1255+
1256+ if (text.isEmpty()) {
1257+ return false;
1258+ }
1259+
1260+ if (separators.contains(text.right(1))) {
1261+ return true;
1262+ }
1263+
1264+ return false;
1265+}
1266+
1267+bool KoreanLanguageFeatures::isSymbol(const QString &text) const
1268+{
1269+ static const QString symbols = QString::fromUtf8("*#+=()@~/\\€£$¥₹%<>[]`^|_§{}¡¿«»\"“”„&0123456789");
1270+
1271+ if (text.isEmpty()) {
1272+ return false;
1273+ }
1274+
1275+ if (symbols.contains(text.right(1))) {
1276+ return true;
1277+ }
1278+
1279+ return false;
1280+}
1281+
1282+bool KoreanLanguageFeatures::ignoreSimilarity() const
1283+{
1284+ return true;
1285+}
1286+bool KoreanLanguageFeatures::wordEngineAvailable() const
1287+{
1288+ return true;
1289+}
1290
1291=== added file 'plugins/ko/src/koreanlanguagefeatures.h'
1292--- plugins/ko/src/koreanlanguagefeatures.h 1970-01-01 00:00:00 +0000
1293+++ plugins/ko/src/koreanlanguagefeatures.h 2016-07-21 02:52:13 +0000
1294@@ -0,0 +1,41 @@
1295+/*
1296+ * Copyright 2016 Canonical Ltd.
1297+ *
1298+ * This program is free software; you can redistribute it and/or modify
1299+ * it under the terms of the GNU Lesser General Public License as published by
1300+ * the Free Software Foundation; version 3.
1301+ *
1302+ * This program is distributed in the hope that it will be useful,
1303+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1304+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1305+ * GNU Lesser General Public License for more details.
1306+ *
1307+ * You should have received a copy of the GNU Lesser General Public License
1308+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1309+ */
1310+
1311+#ifndef KOREANLANGUAGEFEATURES_H
1312+#define KOREANLANGUAGEFEATURES_H
1313+
1314+#include "abstractlanguagefeatures.h"
1315+#include <QObject>
1316+
1317+class KoreanLanguageFeatures : public QObject, public AbstractLanguageFeatures
1318+{
1319+ Q_OBJECT
1320+public:
1321+ explicit KoreanLanguageFeatures(QObject *parent = 0);
1322+ virtual ~KoreanLanguageFeatures();
1323+
1324+ virtual bool alwaysShowSuggestions() const;
1325+ virtual bool autoCapsAvailable() const;
1326+ virtual bool activateAutoCaps(const QString &preedit) const;
1327+ virtual QString appendixForReplacedPreedit(const QString &preedit) const;
1328+ virtual bool isSeparator(const QString &text) const;
1329+ virtual QString fullStopSequence() const { return QString("."); }
1330+ virtual bool isSymbol(const QString &text) const;
1331+ virtual bool ignoreSimilarity() const;
1332+ virtual bool wordEngineAvailable() const;
1333+};
1334+
1335+#endif // KOREANLANGUAGEFEATURES_H
1336
1337=== added file 'plugins/ko/src/koreanplugin.cpp'
1338--- plugins/ko/src/koreanplugin.cpp 1970-01-01 00:00:00 +0000
1339+++ plugins/ko/src/koreanplugin.cpp 2016-07-21 02:52:13 +0000
1340@@ -0,0 +1,102 @@
1341+#include "koreanplugin.h"
1342+#include "koreanlanguagefeatures.h"
1343+#include "spellpredictworker.h"
1344+
1345+#include <QDebug>
1346+
1347+KoreanPlugin::KoreanPlugin(QObject *parent) :
1348+ AbstractLanguagePlugin(parent)
1349+ , m_koreanLanguageFeatures(new KoreanLanguageFeatures)
1350+ , m_spellCheckEnabled(false)
1351+ , m_processingSpelling(false)
1352+{
1353+ m_spellPredictThread = new QThread();
1354+ m_spellPredictWorker = new SpellPredictWorker();
1355+ m_spellPredictWorker->moveToThread(m_spellPredictThread);
1356+
1357+ connect(m_spellPredictWorker, SIGNAL(newSpellingSuggestions(QString, QStringList)), this, SLOT(spellCheckFinishedProcessing(QString, QStringList)));
1358+ connect(m_spellPredictWorker, SIGNAL(newPredictionSuggestions(QString, QStringList)), this, SIGNAL(newPredictionSuggestions(QString, QStringList)));
1359+ connect(this, SIGNAL(newSpellCheckWord(QString)), m_spellPredictWorker, SLOT(newSpellCheckWord(QString)));
1360+ connect(this, SIGNAL(setSpellPredictLanguage(QString, QString)), m_spellPredictWorker, SLOT(setLanguage(QString, QString)));
1361+ connect(this, SIGNAL(setSpellCheckLimit(int)), m_spellPredictWorker, SLOT(setSpellCheckLimit(int)));
1362+ connect(this, SIGNAL(parsePredictionText(QString, QString)), m_spellPredictWorker, SLOT(parsePredictionText(QString, QString)));
1363+ connect(this, SIGNAL(addToUserWordList(QString)), m_spellPredictWorker, SLOT(addToUserWordList(QString)));
1364+ connect(this, SIGNAL(addOverride(QString, QString)), m_spellPredictWorker, SLOT(addOverride(QString, QString)));
1365+ m_spellPredictThread->start();
1366+
1367+}
1368+
1369+KoreanPlugin::~KoreanPlugin()
1370+{
1371+ m_spellPredictWorker->deleteLater();
1372+ m_spellPredictThread->quit();
1373+ m_spellPredictThread->wait();
1374+}
1375+
1376+AbstractLanguageFeatures* KoreanPlugin::languageFeature()
1377+{
1378+ return m_koreanLanguageFeatures;
1379+}
1380+
1381+void KoreanPlugin::predict(const QString& surroundingLeft, const QString& preedit)
1382+{
1383+ Q_EMIT parsePredictionText(surroundingLeft, preedit);
1384+}
1385+
1386+void KoreanPlugin::wordCandidateSelected(QString word)
1387+{
1388+ Q_UNUSED(word);
1389+}
1390+
1391+
1392+void KoreanPlugin::spellCheckerSuggest(const QString& word, int limit)
1393+{
1394+ m_nextSpellWord = word;
1395+ // Don't accept new words whilst we're processing, so we only process the
1396+ // most recent input once the current processing has completed
1397+ if (!m_processingSpelling) {
1398+ m_processingSpelling = true;
1399+ Q_EMIT setSpellCheckLimit(limit);
1400+ Q_EMIT newSpellCheckWord(word);
1401+ }
1402+}
1403+
1404+void KoreanPlugin::addToSpellCheckerUserWordList(const QString& word)
1405+{
1406+ Q_EMIT addToUserWordList(word);
1407+}
1408+
1409+bool KoreanPlugin::setLanguage(const QString& languageId, const QString& pluginPath)
1410+{
1411+ Q_EMIT setSpellPredictLanguage(languageId, pluginPath);
1412+ loadOverrides(pluginPath);
1413+ return true;
1414+}
1415+
1416+void KoreanPlugin::addSpellingOverride(const QString& orig, const QString& overriden)
1417+{
1418+ Q_EMIT addOverride(orig, overriden);
1419+}
1420+
1421+void KoreanPlugin::loadOverrides(const QString& pluginPath) {
1422+ QFile overrideFile(pluginPath + QDir::separator() + "overrides.csv");
1423+ if (overrideFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
1424+ QTextStream overrideStream(&overrideFile);
1425+ while (!overrideStream.atEnd()) {
1426+ QString line = overrideStream.readLine();
1427+ QStringList components = line.split(",");
1428+ if (components.length() == 2) {
1429+ addSpellingOverride(components.first(), components.last());
1430+ }
1431+ }
1432+ }
1433+}
1434+
1435+void KoreanPlugin::spellCheckFinishedProcessing(QString word, QStringList suggestions) {
1436+ Q_EMIT newSpellingSuggestions(word, suggestions);
1437+ if (word != m_nextSpellWord) {
1438+ Q_EMIT newSpellCheckWord(m_nextSpellWord);
1439+ } else {
1440+ m_processingSpelling = false;
1441+ }
1442+}
1443
1444=== added file 'plugins/ko/src/koreanplugin.h'
1445--- plugins/ko/src/koreanplugin.h 1970-01-01 00:00:00 +0000
1446+++ plugins/ko/src/koreanplugin.h 2016-07-21 02:52:13 +0000
1447@@ -0,0 +1,61 @@
1448+#ifndef KOREANPLUGIN_H
1449+#define KOREANPLUGIN_H
1450+
1451+#include <QObject>
1452+#include <QStringList>
1453+#include "languageplugininterface.h"
1454+#include "abstractlanguageplugin.h"
1455+#include "candidatescallback.h"
1456+#include "spellchecker.h"
1457+#include "spellpredictworker.h"
1458+
1459+#include <iostream>
1460+
1461+class KoreanLanguageFeatures;
1462+class CandidatesCallback;
1463+
1464+class KoreanPlugin : public AbstractLanguagePlugin
1465+{
1466+ Q_OBJECT
1467+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.KoreanPlugin" FILE "koreanplugin.json")
1468+ Q_INTERFACES(LanguagePluginInterface)
1469+
1470+public:
1471+ explicit KoreanPlugin(QObject *parent = 0);
1472+ virtual ~KoreanPlugin();
1473+
1474+ virtual void predict(const QString& surroundingLeft, const QString& preedit);
1475+ virtual void wordCandidateSelected(QString word);
1476+ virtual AbstractLanguageFeatures* languageFeature();
1477+
1478+ //! spell checker
1479+ virtual void spellCheckerSuggest(const QString& word, int limit);
1480+ virtual void addToSpellCheckerUserWordList(const QString& word);
1481+ virtual bool setLanguage(const QString& languageId, const QString& pluginPath);
1482+ virtual void addSpellingOverride(const QString& orig, const QString& overriden);
1483+ virtual void loadOverrides(const QString& pluginPath);
1484+
1485+signals:
1486+ void newSpellingSuggestions(QString word, QStringList suggestions);
1487+ void newPredictionSuggestions(QString word, QStringList suggestions);
1488+ void newSpellCheckWord(QString word);
1489+ void setSpellCheckLimit(int limit);
1490+ void setSpellPredictLanguage(QString language, QString pluginPath);
1491+ void parsePredictionText(QString surroundingLeft, QString preedit);
1492+ void setPredictionLanguage(QString language);
1493+ void addToUserWordList(const QString& word);
1494+ void addOverride(const QString& orig, const QString& overriden);
1495+
1496+public slots:
1497+ void spellCheckFinishedProcessing(QString word, QStringList suggestions);
1498+
1499+private:
1500+ KoreanLanguageFeatures* m_koreanLanguageFeatures;
1501+ SpellPredictWorker *m_spellPredictWorker;
1502+ QThread *m_spellPredictThread;
1503+ bool m_spellCheckEnabled;
1504+ QString m_nextSpellWord;
1505+ bool m_processingSpelling;
1506+};
1507+
1508+#endif // KOREANPLUGIN_H
1509
1510=== added file 'plugins/ko/src/koreanplugin.json'
1511--- plugins/ko/src/koreanplugin.json 1970-01-01 00:00:00 +0000
1512+++ plugins/ko/src/koreanplugin.json 2016-07-21 02:52:13 +0000
1513@@ -0,0 +1,7 @@
1514+{
1515+ "IID": "org.qt-project.Qt.Examples.KoreanPlugin",
1516+ "MetaData": {
1517+ },
1518+ "className": "KoreanPlugin",
1519+ "debug": true
1520+}
1521
1522=== added file 'plugins/ko/src/src.pro'
1523--- plugins/ko/src/src.pro 1970-01-01 00:00:00 +0000
1524+++ plugins/ko/src/src.pro 2016-07-21 02:52:13 +0000
1525@@ -0,0 +1,67 @@
1526+TOP_BUILDDIR = $$OUT_PWD/../../..
1527+TOP_SRCDIR = $$PWD/../../..
1528+
1529+include($${TOP_SRCDIR}/config.pri)
1530+
1531+TEMPLATE = lib
1532+CONFIG += plugin
1533+QT += widgets
1534+INCLUDEPATH += \
1535+ $${TOP_SRCDIR}/src/ \
1536+ $${TOP_SRCDIR}/src/lib/ \
1537+ $${TOP_SRCDIR}/src/lib/logic/ \
1538+ $${TOP_SRCDIR}/plugins/westernsupport/
1539+
1540+HEADERS = \
1541+ koreanplugin.h \
1542+ koreanlanguagefeatures.h \
1543+ $${TOP_SRCDIR}/src/lib/logic/abstractlanguageplugin.h \
1544+ $${TOP_SRCDIR}/plugins/westernsupport/spellchecker.h \
1545+ $${TOP_SRCDIR}/plugins/westernsupport/spellpredictworker.h \
1546+ $${TOP_SRCDIR}/plugins/westernsupport/candidatescallback.h \
1547+
1548+SOURCES = \
1549+ koreanplugin.cpp \
1550+ koreanlanguagefeatures.cpp \
1551+ $${TOP_SRCDIR}/src/lib/logic/abstractlanguageplugin.cpp \
1552+ $${TOP_SRCDIR}/plugins/westernsupport/spellchecker.cpp \
1553+ $${TOP_SRCDIR}/plugins/westernsupport/spellpredictworker.cpp \
1554+ $${TOP_SRCDIR}/plugins/westernsupport/candidatescallback.cpp \
1555+
1556+
1557+TARGET = $$qtLibraryTarget(koplugin)
1558+
1559+EXAMPLE_FILES = koreanplugin.json
1560+
1561+# generate database for presage:
1562+PLUGIN_INSTALL_PATH = $${UBUNTU_KEYBOARD_LIB_DIR}/ko/
1563+
1564+lang_db_ko.commands += \
1565+ rm -f $$PWD/database_ko.db && \
1566+ text2ngram -n 1 -l -f sqlite -o $$PWD/database_ko.db $$PWD/korean.txt && \
1567+ text2ngram -n 2 -l -f sqlite -o $$PWD/database_ko.db $$PWD/korean.txt && \
1568+ text2ngram -n 3 -l -f sqlite -o $$PWD/database_ko.db $$PWD/korean.txt
1569+lang_db_ko.files += $$PWD/database_ko.db
1570+
1571+lang_db_ko_install.files += $$PWD/database_ko.db
1572+lang_db_ko_install.path = $$PLUGIN_INSTALL_PATH
1573+
1574+QMAKE_EXTRA_TARGETS += lang_db_ko lang_db_ko_install
1575+
1576+target.path = $$PLUGIN_INSTALL_PATH
1577+INSTALLS += target lang_db_ko_install
1578+
1579+OTHER_FILES += \
1580+ koreanplugin.json \
1581+ korean.txt
1582+
1583+# hunspell
1584+CONFIG += link_pkgconfig
1585+PKGCONFIG += hunspell
1586+LIBS += -lhunspell
1587+DEFINES += HAVE_HUNSPELL
1588+
1589+# presage
1590+LIBS += -lpresage
1591+DEFINES += HUNSPELL_DICT_PATH=\\\"$$HUNSPELL_DICT_PATH\\\"
1592+
1593
1594=== modified file 'plugins/plugins.pro'
1595--- plugins/plugins.pro 2016-06-01 23:08:11 +0000
1596+++ plugins/plugins.pro 2016-07-21 02:52:13 +0000
1597@@ -23,6 +23,7 @@
1598 it \
1599 ja \
1600 lv \
1601+ ko \
1602 nb \
1603 nl \
1604 pl \
1605
1606=== modified file 'qml/keys/CharKey.qml'
1607--- qml/keys/CharKey.qml 2016-06-14 14:01:28 +0000
1608+++ qml/keys/CharKey.qml 2016-07-21 02:52:13 +0000
1609@@ -97,6 +97,11 @@
1610 // Allow action keys to override the standard key behaviour
1611 property bool overridePressArea: false
1612
1613+ // Allow to manipulate preedit if it need.
1614+ // if allowPreeditHandler is enabled should be assigned preeditHandler.
1615+ property bool allowPreeditHandler: false
1616+ property var preeditHandler: null
1617+
1618 // Don't detect swipe changes until the swipeTimer has expired to prevent
1619 // accidentally selecting something other than the default extended key
1620 property bool swipeReady: false
1621@@ -240,6 +245,12 @@
1622 if (switchBackFromSymbols && panel.state === "SYMBOLS") {
1623 panel.state = "CHARACTERS";
1624 }
1625+
1626+ if (allowPreeditHandler && preeditHandler) {
1627+ preeditHandler.onKeyReleased(keyToSend, action);
1628+ return;
1629+ }
1630+
1631 event_handler.onKeyReleased(keyToSend, action);
1632 } else if (action == "backspace") {
1633 // Send release from backspace if we're swiped out since
1634
1635=== modified file 'qml/keys/languages.js'
1636--- qml/keys/languages.js 2016-06-01 23:08:11 +0000
1637+++ qml/keys/languages.js 2016-07-21 02:52:13 +0000
1638@@ -37,6 +37,7 @@
1639 if (languageId == "it") return i18n.tr("Italian");
1640 if (languageId == "ja") return i18n.tr("Japanese");
1641 if (languageId == "lv") return i18n.tr("Latvian");
1642+ if (languageId == "ko") return i18n.tr("Korean");
1643 if (languageId == "nl") return i18n.tr("Dutch");
1644 if (languageId == "nb") return i18n.tr("Norwegian");
1645 if (languageId == "pl") return i18n.tr("Polish");
1646
1647=== modified file 'tests/autopilot/ubuntu_keyboard/emulators/keypad.py'
1648--- tests/autopilot/ubuntu_keyboard/emulators/keypad.py 2015-09-15 12:32:13 +0000
1649+++ tests/autopilot/ubuntu_keyboard/emulators/keypad.py 2016-07-21 02:52:13 +0000
1650@@ -74,5 +74,6 @@
1651 _iter_keys("LanguageKey", lambda x: x.action)
1652 _iter_keys("ReturnKey", lambda x: x.action)
1653 _iter_keys("CommitKey", lambda x: x.action)
1654+ _iter_keys("HCharKey", lambda x: x.label)
1655
1656 return (contained_keys, key_positions)
1657
1658=== modified file 'tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py'
1659--- tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py 2016-06-15 12:44:54 +0000
1660+++ tests/autopilot/ubuntu_keyboard/tests/test_keyboard.py 2016-07-21 02:52:13 +0000
1661@@ -1675,6 +1675,42 @@
1662 )
1663
1664
1665+class UbuntuKeyboardKoreanTests(UbuntuKeyboardTests):
1666+
1667+ def set_test_settings(self):
1668+ gsettings = Gio.Settings.new("com.canonical.keyboard.maliit")
1669+ gsettings.set_string("active-language", "ko")
1670+ gsettings.set_boolean("auto-capitalization", True)
1671+ gsettings.set_boolean("auto-completion", True)
1672+ gsettings.set_boolean("predictive-text", True)
1673+ gsettings.set_boolean("spell-checking", True)
1674+ gsettings.set_boolean("double-space-full-stop", True)
1675+
1676+ def test_korean_input(self):
1677+ """Test keys on Korean layout.
1678+
1679+ """
1680+ text_area = self.launch_test_input_area(
1681+ input_hints=['Qt.ImhNoPredictiveText'])
1682+ self.pointer.click_object(text_area)
1683+ keyboard = Keyboard()
1684+ self.assertThat(keyboard.is_available, Eventually(Equals(True)))
1685+
1686+ expected = "한글 "
1687+ keyboard.press_key('ㅎ')
1688+ keyboard.press_key('ㅏ')
1689+ keyboard.press_key('ㄴ')
1690+ keyboard.press_key('ㄱ')
1691+ keyboard.press_key('ㅡ')
1692+ keyboard.press_key('ㄹ')
1693+ keyboard.press_key(' ')
1694+
1695+ self.assertThat(
1696+ text_area.text,
1697+ Eventually(Equals(expected))
1698+ )
1699+
1700+
1701 def maliit_cleanup():
1702 presagedir = os.path.expanduser("~/.presage")
1703 if os.path.exists(presagedir + ".bak") and os.path.exists(presagedir):

Subscribers

People subscribed via source and target branches