Fix build script for Tizen 4.0
[platform/core/uifw/ise-engine-sunpinyin.git] / wrapper / ibus / setup / main.py
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 Leo Zheng <zym361@gmail.com>, Kov Chai <tchaikov@gmail.com>
4 # *
5 # The contents of this file are subject to the terms of either the GNU Lesser
6 # General Public License Version 2.1 only ("LGPL") or the Common Development and
7 # Distribution License ("CDDL")(collectively, the "License"). You may not use this
8 # file except in compliance with the License. You can obtain a copy of the CDDL at
9 # http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
10 # http://www.opensource.org/licenses/lgpl-license.php. See the License for the 
11 # specific language governing permissions and limitations under the License. When
12 # distributing the software, include this License Header Notice in each file and
13 # include the full text of the License in the License file as well as the
14 # following notice:
15
16 # NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
17 # (CDDL)
18 # For Covered Software in this distribution, this License shall be governed by the
19 # laws of the State of California (excluding conflict-of-law provisions).
20 # Any litigation relating to this License shall be subject to the jurisdiction of
21 # the Federal Courts of the Northern District of California and the state courts
22 # of the State of California, with venue lying in Santa Clara County, California.
23
24 # Contributor(s):
25
26 # If you wish your version of this file to be governed by only the CDDL or only
27 # the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
28 # include this software in this distribution under the [CDDL or LGPL Version 2.1]
29 # license." If you don't indicate a single choice of license, a recipient has the
30 # option to distribute your version of this file under either the CDDL or the LGPL
31 # Version 2.1, or to extend the choice of license to its licensees as provided
32 # above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
33 # Version 2 license, then the option applies only if the new code is made subject
34 # to such option by the copyright holder. 
35 #
36
37 import sys
38 import os
39 from os import path
40 import gtk
41 import ibus
42 import gettext
43 import locale
44
45 GETTEXT_PACKAGE="ibus-sunpinyin"
46 _ = lambda msg: gettext.gettext(msg)
47
48 XML_FILE = path.join(path.dirname(__file__), "setup.xml")
49 SEPARATOR = "/"
50
51 class Logger:
52     @staticmethod
53     def pr(message):
54         print >> sys.stderr, message
55         
56 class Option(object):
57     """Option serves as an interface of ibus.config
58
59     it is used to synchronize the configuration with setting on user interface
60     """
61     config = ibus.Bus().get_config()
62     
63     def __init__(self, name, default):
64         self.name = name
65         self.default = default
66     
67     def read(self):
68         section, key = self.__get_config_name()
69         return self.config.get_value(section, key, self.default)
70
71     def write(self, v):
72         section, key = self.__get_config_name()
73         return self.config.set_value(section, key, type(self.default)(v))
74
75    
76     def __get_config_name(self):
77         keys = self.name.rsplit(SEPARATOR ,1)
78         if len(keys) == 2:
79             return SEPARATOR.join(("engine/SunPinyin", keys[0])), keys[1]
80         else:
81             assert len(keys) == 1
82             return "engine/SunPinyin", keys[0]
83
84 class TrivalOption(Option):
85     """option represented using a simple gtk widget
86     """
87     def __init__(self, name, default, owner):
88         super(TrivalOption, self).__init__(name, default)
89         self.xml = owner
90         self.widget = owner.get_object(name)
91         assert self.widget is not None, "%s not found in gtkbuilder" % name
92
93     def init_ui(self):
94         self.init()
95         self.read_config()
96
97     def init(self):
98         pass
99     
100     def read_config(self):
101         """update user inferface with ibus.config
102         """
103         self.v = self.read()
104         self.__set_value(self.v)
105
106     def write_config(self):
107         v = self.save_ui_setting()
108         self.write(v)
109         
110     def save_ui_setting(self):
111         """save user interface settings into self.v
112         """
113         self.v = self.__get_value()
114         return self.v
115     
116     def is_changed(self):
117         return self.v != self.__get_value()
118
119     def __get_value(self):
120         try:
121             return self.widget.get_value()
122         except:
123             return self.widget.get_active()
124
125     def __set_value(self, v):
126         try:
127             self.widget.set_value(v)
128         except:
129             self.widget.set_active(v)
130             
131 class CheckBoxOption(TrivalOption):
132     def __init__(self, name, default, owner):
133         super(CheckBoxOption, self).__init__(name, default, owner)
134
135 class ComboBoxOption(TrivalOption):
136     def __init__(self, name, default, options, owner):
137         try:
138             default = int(default)
139         except ValueError:
140             default = options.index(default)
141         super(ComboBoxOption, self).__init__(name, default, owner)
142         self.options = options
143         
144     def init(self):
145         model = gtk.ListStore(str)
146         for v in self.options:
147             model.append([str(v)])
148         self.widget.set_model(model)
149
150     def __get_value(self):
151         active = self.widget.get_active()
152         try:
153             # if the options are numbers, save the liternal of active option as
154             # a number
155             return int(self.options[active])
156         except ValueError:
157             # otherwise save its index
158             return active
159     
160     def __set_value(self, v):
161         try:
162             # if the options are just numbers, we treat 'self.v' as the literal
163             # of option
164             dummy = int(self.options[0])
165             active = self.options.index(v)
166         except ValueError:
167             active = v
168         self.widget.set_active(active)
169         
170 class RadioOption(Option):
171     """option represented using multiple Raidio buttons
172     """
173     def __init__(self, name, default, options, owner):
174         super(RadioOption, self).__init__(name, default)
175         self.options = options
176         self.xml = owner
177
178     def init_ui(self):
179         self.read_config()
180         
181     def read_config(self):
182         self.v = self.read()
183         name = SEPARATOR.join([self.name, self.v])
184         button = self.xml.get_object(name)
185         assert button is not None, "button: %r not found" % name
186         button.set_active(True)
187
188     def write_config(self):
189         active_opt = None
190         for opt in self.options:
191             radio_name = SEPARATOR.join([self.name, opt])
192             radio = self.xml.get_object(radio_name)
193             if radio.get_active():
194                 active_opt = opt
195                 break
196         assert active_opt is not None
197         self.write(active_opt)
198
199 class MappingInfo:
200     def __init__(self, name, mapping):
201         self.name = name
202         self.mapping = mapping
203         
204 class MappingOption(object):
205     """an option which presents some sort of mapping, e.g. fuzzy pinyin mapping
206
207     it is not directly related to a config option like TrivalOption does, but
208     we always have a checkbox in UI for each of it so user can change it easily.
209     """
210     def __init__(self, name, mappings, owner):
211         self.name = name
212         self.widget = owner.get_object(name)
213         self.mappings = mappings
214         
215     def get_mappings(self):
216         if self.widget.get_active():
217             return [':'.join(self.mappings)]
218         else:
219             return []
220
221     def set_active(self, enabled):
222         self.widget.set_active(enabled)
223
224     def get_active(self):
225         return self.widget.get_active()
226     
227     is_enabled = property(get_active, set_active)
228
229     def key(self):
230         return self.mappings[0]
231     
232 class MultiMappingOption(Option):
233     def __init__(self, name, options, default=[]):
234         Option.__init__(self, name, default)
235         self.options = options
236         self.saved_pairs = default
237         
238     def read_config(self):
239         if not self.saved_pairs:
240             self.saved_pairs = self.read()
241         keys = set([pair.split(':')[0] for pair in self.saved_pairs])
242         for opt in self.options:
243             opt.is_enabled = (opt.key() in keys)
244             # throw away unknown pair
245     
246     def write_config(self):
247         # ignore empty settings
248         if self.saved_pairs:
249             self.write(self.saved_pairs)
250         
251     def save_ui_setting(self):
252         self.saved_pairs = sum([opt.get_mappings() for opt in self.options
253                                 if opt.is_enabled], [])
254         return self.saved_pairs
255     
256     def set_active_all(self, enabled):
257         for opt in self.options:
258             opt.is_enabled = enabled
259             
260 class MultiCheckDialog (object):
261     """ a modal dialog box with 'choose all' and 'choose none' button
262     
263     TODO: another option is to use radio button
264     """
265     def __init__ (self, ui_name, config_name, mappings, option_klass=MappingOption):
266         self.ui_name = ui_name
267         self.config_name = config_name
268         self.mappings = mappings
269         self.option_klass = option_klass
270         self.saved_settings = []
271         self.mapping_options = None
272         
273     def get_setup_name(self):
274         """assuming the name of dialog looks like 'dlg_fuzzy_setup'
275         """
276         return '_'.join(['dlg', self.ui_name, 'setup'])
277     
278     def __init_ui(self):
279         dlg_name = self.get_setup_name()
280         self.__xml = gtk.Builder()
281         self.__xml.add_objects_from_file(XML_FILE, dlg_name)
282         self.__dlg = self.__xml.get_object(dlg_name)
283         assert self.__dlg is not None, "dialog %s not found in %s" % (dlg_name, XML_FILE)
284         handlers = {'_'.join(["on", self.ui_name, "select_all_clicked"]) : self.on_button_check_all_clicked,
285                     '_'.join(["on", self.ui_name, "unselect_all_clicked"]) : self.on_button_uncheck_all_clicked,
286                     '_'.join(["on", self.ui_name, "ok_clicked"]) : self.on_button_ok_clicked,
287                     '_'.join(["on", self.ui_name, "cancel_clicked"]) : self.on_button_cancel_clicked}
288         self.__xml.connect_signals(handlers)
289
290         options = [self.option_klass(m.name, m.mapping, self.__xml) 
291                    for m in self.mappings]
292         self.mapping_options = MultiMappingOption(self.config_name, options, self.saved_settings)
293
294     def dummy(self):
295         """a dummy func, i don't initialize myself upon other's request.
296         instead, i will do it by myself.
297         """
298         pass
299
300     init_ui = read_config = dummy
301     
302     def run(self):
303         self.__init_ui()
304         self.__read_config()
305         self.__dlg.run()
306         
307     def __read_config(self):
308         self.mapping_options.read_config()
309         
310     def __save_ui_settings(self):
311         """save to in-memory storage, will flush to config if not canceled in main_window
312         """
313         self.saved_settings = self.mapping_options.save_ui_setting()
314
315     def write_config(self):
316         if self.mapping_options is not None:
317             self.mapping_options.write_config()
318             
319     def on_button_check_all_clicked(self, button):
320         self.mapping_options.set_active_all(True)
321         
322     def on_button_uncheck_all_clicked(self, button):
323         self.mapping_options.set_active_all(False)
324     
325     def on_button_ok_clicked(self, button):
326         """update given options with settings in UI, these settings will be
327         written to config if user push 'OK' or 'Apply' in the main window
328         """
329         self.__save_ui_settings()
330         self.__dlg.destroy()
331         
332     def on_button_cancel_clicked(self, button):
333         self.__dlg.destroy()
334
335 class FuzzySetupDialog (MultiCheckDialog):
336     def __init__(self):
337         mappings = [MappingInfo('QuanPin/Fuzzy/ShiSi', ('sh','s')),
338                     MappingInfo('QuanPin/Fuzzy/ZhiZi', ('zh','z')),
339                     MappingInfo('QuanPin/Fuzzy/ChiCi', ('ch','c')),
340                     MappingInfo('QuanPin/Fuzzy/ShiSi', ('sh','s')),
341                     MappingInfo('QuanPin/Fuzzy/AnAng', ('an','ang')),
342                     MappingInfo('QuanPin/Fuzzy/OnOng', ('on','ong')),
343                     MappingInfo('QuanPin/Fuzzy/EnEng', ('en','eng')),
344                     MappingInfo('QuanPin/Fuzzy/InIng', ('in','ing')),
345                     MappingInfo('QuanPin/Fuzzy/EngOng', ('eng','ong')),
346                     MappingInfo('QuanPin/Fuzzy/IanIang', ('ian','iang')),
347                     MappingInfo('QuanPin/Fuzzy/UanUang', ('uan','uang')),
348                     MappingInfo('QuanPin/Fuzzy/NeLe', ('n','l')),
349                     MappingInfo('QuanPin/Fuzzy/FoHe', ('f','h')),
350                     MappingInfo('QuanPin/Fuzzy/LeRi', ('l','r')),
351                     MappingInfo('QuanPin/Fuzzy/KeGe', ('k','g'))]
352         MultiCheckDialog.__init__(self,
353                                   ui_name = 'fuzzy',
354                                   config_name = 'QuanPin/Fuzzy/Pinyins',
355                                   mappings = mappings)
356         
357 class CorrectionSetupDialog (MultiCheckDialog):
358     def __init__(self):
359         mappings = [MappingInfo('QuanPin/AutoCorrection/GnNg', ('gn','ng')),
360                     MappingInfo('QuanPin/AutoCorrection/UenUn', ('uen','un')),
361                     MappingInfo('QuanPin/AutoCorrection/ImgIng', ('img','ing')),
362                     MappingInfo('QuanPin/AutoCorrection/IouIu', ('iou','iu')),
363                     MappingInfo('QuanPin/AutoCorrection/UeiUi', ('uei','ui'))]
364         MultiCheckDialog.__init__(self,
365                                   ui_name = 'correction',
366                                   config_name = 'QuanPin/AutoCorrection/Pinyins',
367                                   mappings = mappings)
368
369 class PunctMapping(MappingOption):
370     def __init__(self, name, mappings, owner):
371         MappingOption.__init__(self, name, mappings, owner)
372         if mappings:
373             self.widget.set_sensitive(True)
374             self.init_keys_values(mappings)
375         else:
376             self.widget.set_sensitive(False)
377             
378     def init_keys_values(self, mappings):
379         self.keys = [m[0] for m in mappings]
380         values_with_closing = [v or k for k, v in mappings]
381         self.values = []
382         for v in values_with_closing:
383             try:
384                 self.values.append(v[0])
385             except:
386                 self.values.append(v)
387         self.keys.reverse()
388         self.values.reverse()
389
390     def get_mappings(self):
391         if self.widget.get_active():
392             pairs = []
393             for k,vs in self.mappings:
394                 try:
395                     for v in vs:
396                         pairs.append(':'.join([k,v]))
397                 except:
398                     v = vs
399                     if v is None:
400                         continue
401                     pairs.append(':'.join([k,v]))
402             return pairs
403         else:
404             return []
405
406     def set_active(self, enabled):
407         if not self.mappings: return
408         if enabled:
409             self.widget.set_label('\n'.join(self.values))
410         else:
411             self.widget.set_label('\n'.join(self.keys))
412         self.widget.set_active(enabled)
413
414     is_enabled = property(MappingOption.get_active, set_active)
415     
416     def key(self):
417         for k, v in self.mappings:
418             if v is not None:
419                 return k
420         else:
421             return None
422
423 class PunctMappingSetupDialog (MultiCheckDialog):
424     # TODO: the UI should looks like a virtual keyboard,
425     #       user are allowed to choose the mappings to all punctuation keys.
426     def __init__(self):
427         mappings = [MappingInfo('togglebutton1', [('`',None), ('~',u'~')]),
428                     MappingInfo('togglebutton2', []),
429                     MappingInfo('togglebutton3', [('2',None), ('@',u'@')]),
430                     MappingInfo('togglebutton4', [('3',None), ('#',u'#')]),
431                     MappingInfo('togglebutton5', [('4',None), ('$',u'¥' )]),
432                     MappingInfo('togglebutton6', [('5',None), ('%',u'%')]),
433                     MappingInfo('togglebutton7', [('6',None), ('^',u'…')]),
434                     MappingInfo('togglebutton8', [('7',None), ('&',u'&')]),
435                     MappingInfo('togglebutton9', [('8',None), ('*',u'*')]),
436                     MappingInfo('togglebutton10', [('9',None), ('*',u'(')]),
437                     MappingInfo('togglebutton11', [('0',None), ('*',u')')]),
438                     MappingInfo('togglebutton12', [('-',u'-'), ('_',u'——')]),
439                     MappingInfo('togglebutton13', [('=',u'='), ('+',u'+')]),
440                     MappingInfo('togglebutton14', [('\\',None), ('|',u'‖')]),
441                     MappingInfo('togglebutton27', [('[',u'〔'), ('{',u'{')]),
442                     MappingInfo('togglebutton28', [(']',u'〕'), ('}',u'}')]),
443                     MappingInfo('togglebutton39', []),
444                     MappingInfo('togglebutton40', []),
445                     MappingInfo('togglebutton50', [(',',None), ('<',u'〈')]),
446                     MappingInfo('togglebutton51', [('.',u'·'), ('>',u'〉')]),
447                     MappingInfo('togglebutton52', [('/',u'/'), ('?',None)])]
448                     #'\'',(u'‘',u'’'),
449         MultiCheckDialog.__init__(self, ui_name="punctmapping",
450                                   config_name="General/PunctMapping/Mappings",
451                                   mappings=mappings,
452                                   option_klass=PunctMapping)
453
454 class MainWindow():
455     SPECIAL_OBJECTS = [
456         'pymodel', 'memory_adjustment', 'candidate_adjustment',
457         'max_best_adjustment', 'max_tail_candidate_adjustment',
458         ]
459
460     def __init__ (self):
461         self.__bus = ibus.Bus()
462         self.__config = self.__bus.get_config()
463         
464         
465     def run(self):
466         self.__init_ui("main_window")
467         self.__read_config()
468         gtk.main()
469         
470     def __init_ui(self, name):
471         self.__init_gettext()
472         xml_file = path.join(path.dirname(__file__), XML_FILE)
473         self.__xml = gtk.Builder()
474         self.__xml.add_objects_from_file(xml_file, self.SPECIAL_OBJECTS)
475         self.__xml.add_objects_from_file(xml_file, [name])
476         self.__xml.connect_signals(self)
477         self.__init_options()
478         self.window = self.__xml.get_object(name)
479         self.window.show_all()
480
481     def __init_gettext(self):
482         locale.setlocale(locale.LC_ALL, "")
483         localedir = os.getenv("IBUS_LOCALEDIR")
484         gettext.bindtextdomain(GETTEXT_PACKAGE, localedir)
485         gettext.bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8")
486
487     def __init_options(self):
488         self.__fuzzy_setup = FuzzySetupDialog()
489         self.__correction_setup = CorrectionSetupDialog()
490         self.__punctmapping_setup = PunctMappingSetupDialog()
491         
492         self.__options = [
493             TrivalOption("General/MemoryPower", 3, self.__xml),
494             TrivalOption("General/PageSize", 10, self.__xml),
495             TrivalOption("General/MaxBest", 1, self.__xml),
496             TrivalOption("General/MaxTailCandidate", 0, self.__xml),
497             
498             RadioOption("General/InitialStatus/Mode", 'Chinese', ['Chinese', 'English'], self.__xml),
499             RadioOption("General/InitialStatus/Punct", 'Full', ['Full', 'Half'], self.__xml),
500             RadioOption("General/InitialStatus/Letter", 'Half', ['Full', 'Half'], self.__xml),
501             RadioOption("General/Charset", 'GBK', ['GB2312', 'GBK', 'GB18030'], self.__xml),
502             CheckBoxOption("General/PunctMapping/Enabled", False, self.__xml),
503                                 
504             RadioOption("Keyboard/ModeSwitch", 'Shift', ['Shift', 'Control'], self.__xml),
505             RadioOption("Keyboard/PunctSwitch", 'None', ['ControlComma',
506                                                          'ControlPeriod',
507                                                          'None'], self.__xml),
508             CheckBoxOption("Keyboard/Page/MinusEquals", False, self.__xml),
509             CheckBoxOption("Keyboard/Page/Brackets", False, self.__xml),
510             CheckBoxOption("Keyboard/Page/CommaPeriod", False, self.__xml),
511             CheckBoxOption("Keyboard/CancelBackspace", True, self.__xml),
512             CheckBoxOption("Keyboard/SmartPunct", True, self.__xml),
513             
514             RadioOption("Pinyin/Scheme", 'QuanPin', ['QuanPin', 'ShuangPin'], self.__xml),
515             ComboBoxOption("Pinyin/ShuangPinType", 'MS2003', ['MS2003',
516                                                               'ABC',
517                                                               'ZiRanMa',
518                                                               'Pinyin++',
519                                                               'ZiGuang',
520                                                               'XiaoHe'], self.__xml),
521             CheckBoxOption("QuanPin/Fuzzy/Enabled", False, self.__xml),
522             CheckBoxOption("QuanPin/AutoCorrection/Enabled", False, self.__xml),
523             CheckBoxOption("QuanPin/FuzzySegs/Enabled", False, self.__xml),
524             CheckBoxOption("QuanPin/InnerFuzzy/Enabled", False, self.__xml),
525             
526             self.__fuzzy_setup,
527             self.__correction_setup,
528             self.__punctmapping_setup,
529         ]
530
531     def __get_option(self, name):
532         for opt in self.__options:
533             if opt.name == name:
534                 return opt
535         else:
536             return None
537         
538     def __read_config(self):
539         for opt in self.__options:
540             opt.init_ui()
541             opt.read_config()
542         self.on_chk_fuzzy_enabled_toggled(None)
543         self.on_chk_correction_enabled_toggled(None)
544         self.on_chk_punctmapping_enabled_toggled(None)
545         self.on_radio_shuangpin_toggled(None)
546         
547     def __write_config(self):
548         for opt in self.__options:
549             opt.write_config()
550
551     def __update_enabling_button(self, checkbox_name, button_name):
552         """enable a setup button when checked, disable it otherwise
553         """
554         checkbox = self.__xml.get_object(checkbox_name)
555         assert checkbox is not None, "checkbox: %s not found" % checkbox_name
556         button = self.__xml.get_object(button_name)
557         assert button is not None, "button: %s not found" % button_name
558         button_enabled = checkbox.get_active()
559         button.set_sensitive(button_enabled)
560
561     def on_radio_shuangpin_toggled(self, button):
562         radio = self.__xml.get_object("Pinyin/Scheme/ShuangPin")
563         enabled = radio.get_active()
564         combo = self.__xml.get_object("Pinyin/ShuangPinType")
565         combo.set_sensitive(enabled)
566         
567     def on_chk_fuzzy_enabled_toggled(self, button):
568         self.__update_enabling_button("QuanPin/Fuzzy/Enabled",
569                                       "button_fuzzy_setup")
570         
571     def on_button_fuzzy_setup_clicked(self, button):
572         self.__fuzzy_setup.run()
573         
574     def on_chk_correction_enabled_toggled(self, button):
575         self.__update_enabling_button("QuanPin/AutoCorrection/Enabled",
576                                       "button_correction_setup")
577
578     def on_chk_smartseg_enabled_toggled(self, button):
579         self.__update_enabling_button("QuanPin/FuzzySegs/Enabled",
580                                       "QuanPin/InnerFuzzy/Enabled")
581
582     def on_button_correction_setup_clicked(self, button):
583         self.__correction_setup.run()
584         
585     def on_chk_punctmapping_enabled_toggled(self, button):
586         self.__update_enabling_button("General/PunctMapping/Enabled",
587                                       "button_punctmapping_setup")
588     
589     def on_button_punctmapping_setup_clicked(self, button):
590         self.__punctmapping_setup.run()
591     
592     def on_main_ok_clicked(self, button):
593         self.__write_config()
594         self.__quit()
595         
596     def on_main_apply_clicked(self, button):
597         self.__write_config()
598
599     def on_main_cancel_clicked(self, button):
600         self.__quit()
601
602     def __quit(self):
603         gtk.main_quit()
604     
605 if __name__ == "__main__":
606     MainWindow().run()