~ubuntu-branches/ubuntu/trusty/presage/trusty-proposed

« back to all changes in this revision

Viewing changes to apps/python/prompter/prompter.py

  • Committer: Bazaar Package Importer
  • Author(s): Matteo Vescovi
  • Date: 2011-08-06 09:26:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110806092615-0wvhajaht9974ncx
Tags: upstream-0.8.6
ImportĀ upstreamĀ versionĀ 0.8.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
##########
 
2
#  Presage, an extensible predictive text entry system
 
3
#  ------------------------------------------------------
 
4
#
 
5
#  Copyright (C) 2008  Matteo Vescovi <matteo.vescovi@yahoo.co.uk>
 
6
#
 
7
#  This program is free software; you can redistribute it and/or modify
 
8
#  it under the terms of the GNU General Public License as published by
 
9
#  the Free Software Foundation; either version 2 of the License, or
 
10
#  (at your option) any later version.
 
11
#
 
12
#  This program is distributed in the hope that it will be useful,
 
13
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
#  GNU General Public License for more details.
 
16
#
 
17
#  You should have received a copy of the GNU General Public License along
 
18
#  with this program; if not, write to the Free Software Foundation, Inc.,
 
19
#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
20
 
 
21
import sys
 
22
 
 
23
try:
 
24
   import wx
 
25
   import wx.stc
 
26
except ImportError, ex:
 
27
   print '''
 
28
Error: failed to import module wxPython.
 
29
 
 
30
wxPython is a Python binding for the wxWidgets toolkit.
 
31
 
 
32
Check that wxPython is properly installed.
 
33
'''
 
34
   print ex
 
35
   sys.exit(1)
 
36
 
 
37
try:
 
38
   import presage
 
39
except ImportError, ex:
 
40
   print '''
 
41
Error: failed to import module presage.
 
42
 
 
43
Check that presage python binding is properly installed (if
 
44
installed in a non-standard location, please set PYTHONPATH
 
45
accordingly).
 
46
 
 
47
Check that presage library is properly installed (if installed in a
 
48
non-standard location, please set LD_LIBRARY_PATH (PATH, LIBPATH)
 
49
accordingly).
 
50
'''
 
51
   print ex
 
52
   sys.exit(1)
 
53
 
 
54
##########
 
55
# Prompter
 
56
#
 
57
class Prompter(wx.App):
 
58
   def __init__(self, version, config=None, suggestions=None, redirect=False):
 
59
      self.version = version # do this first, wx.App.__init__() calls OnInit()
 
60
      self.config = config
 
61
      self.suggestions = suggestions
 
62
      wx.App.__init__(self, redirect)
 
63
 
 
64
   def OnInit(self):
 
65
      self.frame = PrompterFrame(parent=None, id=-1, title='pyprompter', version=self.version, config=self.config, suggestions=self.suggestions)
 
66
      self.SetTopWindow(self.frame)
 
67
      return True
 
68
 
 
69
###############
 
70
# PrompterFrame
 
71
#
 
72
class PrompterFrame(wx.Frame):
 
73
   wildcard = "Text files (*.txt)|*.txt|"     \
 
74
       "All files (*.*)|*.*"
 
75
 
 
76
   def __init__(self, parent, id, title, version, config, suggestions):
 
77
      wx.Frame.__init__(self, parent, id, title)
 
78
      self.version = version
 
79
 
 
80
      self.editor = PrompterEditor(self, config, suggestions)
 
81
 
 
82
      self.MakeMenuBar()
 
83
      self.MakeToolBar()
 
84
      
 
85
      self.sizer = wx.BoxSizer(wx.HORIZONTAL)
 
86
      self.sizer.Add(self.editor, 1, wx.EXPAND)
 
87
      self.SetSizer(self.sizer)
 
88
      self.SetAutoLayout(True)
 
89
      self.Show()
 
90
 
 
91
   def MakeMenuBar(self):
 
92
      def BindMenu(item, handler):
 
93
         self.Bind(wx.EVT_MENU, handler, item)
 
94
 
 
95
      # file menu
 
96
      self.fileMenu = wx.Menu()
 
97
      BindMenu(self.fileMenu.Append(wx.ID_NEW, "&New\tCTRL+N"), self.OnFileMenuNew)
 
98
      BindMenu(self.fileMenu.Append(wx.ID_OPEN, "&Open\tCTRL+O"), self.OnFileMenuOpen)
 
99
      self.fileMenu.AppendSeparator()
 
100
      BindMenu(self.fileMenu.Append(wx.ID_SAVE, "&Save\tCTRL+S"), self.OnFileMenuSave)
 
101
      BindMenu(self.fileMenu.Append(wx.ID_SAVEAS, "Save &As\tSHIFT+CTRL+S"), self.OnFileMenuSaveAs)
 
102
      self.fileMenu.AppendSeparator()
 
103
      BindMenu(self.fileMenu.Append(wx.ID_CLOSE, "&Close\tCTRL+W"), self.OnFileMenuClose)
 
104
      BindMenu(self.fileMenu.Append(wx.ID_EXIT, "&Quit\tCTRL+Q"), self.OnFileMenuQuit)
 
105
 
 
106
      # edit menu
 
107
      self.editMenu = wx.Menu()
 
108
      BindMenu(self.editMenu.Append(wx.ID_UNDO, "&Undo\tCTRL+Z"), self.OnEditMenuUndo)
 
109
      BindMenu(self.editMenu.Append(wx.ID_REDO, "&Redo\tSHIFT+CTRL+Z"), self.OnEditMenuRedo)
 
110
      self.editMenu.AppendSeparator()
 
111
      BindMenu(self.editMenu.Append(wx.ID_CUT, "Cu&t\tCTRL+X"), self.OnEditMenuCut)
 
112
      BindMenu(self.editMenu.Append(wx.ID_COPY, "&Copy\tCTRL+C"), self.OnEditMenuCopy)
 
113
      BindMenu(self.editMenu.Append(wx.ID_PASTE, "&Paste\tCTRL+V"), self.OnEditMenuPaste)
 
114
      self.editMenu.AppendSeparator()
 
115
      BindMenu(self.editMenu.Append(wx.ID_SELECTALL, "Select &All\tCTRL+A"), self.OnEditMenuSelectAll)
 
116
 
 
117
      
 
118
      # view menu
 
119
      self.viewMenu = wx.Menu()
 
120
      self.ID_TOGGLE_WORD_WRAP = wx.NewId()
 
121
      # need to save wxMenuItem object returned by Append() to test if checked or not
 
122
      self.word_wrap = self.viewMenu.Append(self.ID_TOGGLE_WORD_WRAP,
 
123
                                            "&Word wrap",
 
124
                                            "Toggle word wrap mode",
 
125
                                            wx.ITEM_CHECK)
 
126
      BindMenu(self.word_wrap, self.OnViewMenuWordWrap)
 
127
      # turn text_wrap checked menu item on at start-up
 
128
      self.viewMenu.Check(self.ID_TOGGLE_WORD_WRAP, True)
 
129
      self.viewMenu.AppendSeparator()
 
130
      BindMenu(self.viewMenu.Append(wx.ID_ZOOM_IN, "L&arger text\tCTRL++"), self.OnViewMenuLargerText)
 
131
      BindMenu(self.viewMenu.Append(wx.ID_ZOOM_OUT, "S&maller text\tCTRL+-"), self.OnViewMenuSmallerText)
 
132
      self.viewMenu.AppendSeparator()      
 
133
      self.ID_SHOW_TOOLBAR = wx.NewId()
 
134
      self.show_toolbar_view_menu_item = self.viewMenu.Append(self.ID_SHOW_TOOLBAR,
 
135
                                                              "Show &Toolbar",
 
136
                                                              "Show Toolbar",
 
137
                                                              wx.ITEM_CHECK)
 
138
      BindMenu(self.show_toolbar_view_menu_item, self.OnViewMenuShowToolbar)
 
139
      self.viewMenu.Check(self.ID_SHOW_TOOLBAR, True)
 
140
 
 
141
      # presage menu
 
142
      self.presageMenu = wx.Menu()
 
143
      self.ID_PROMPT_ME = wx.NewId()
 
144
      BindMenu(self.presageMenu.Append(self.ID_PROMPT_ME, "&Prompt me\tCTRL+P"), self.OnPresageMenuPromptMe)
 
145
 
 
146
      learn_mode = self.editor.prsg.config(self.editor.learn_mode_config_var)
 
147
      if learn_mode.lower() == 'true':
 
148
         learn_mode = True
 
149
      elif learn_mode.lower() == 'false':
 
150
         learn_mode = False
 
151
      self.ID_TOGGLE_LEARN_MODE = wx.NewId()
 
152
      self.learn_presage_menu_item = self.presageMenu.Append(self.ID_TOGGLE_LEARN_MODE,
 
153
                                                             "&Learn mode\tCTRL+L",
 
154
                                                             "Toggle learn mode",
 
155
                                                             wx.ITEM_CHECK)
 
156
      self.presageMenu.Check(self.ID_TOGGLE_LEARN_MODE, learn_mode)
 
157
      BindMenu(self.learn_presage_menu_item, self.OnPresageMenuToggleLearnMode)
 
158
 
 
159
      self.ID_TOGGLE_FUNCTION_KEYS_MODE = wx.NewId()
 
160
      self.function_mode_presage_menu_item = self.presageMenu.Append(self.ID_TOGGLE_FUNCTION_KEYS_MODE,
 
161
                                                                     "&Function keys\tCTRL+F",
 
162
                                                                     "Toggle function keys mode",
 
163
                                                                     wx.ITEM_CHECK)
 
164
      self.presageMenu.Check(self.ID_TOGGLE_FUNCTION_KEYS_MODE, True)
 
165
      BindMenu(self.function_mode_presage_menu_item, self.OnPresageMenuToggleFunctionMode)
 
166
 
 
167
      self.ID_TOGGLE_AUTOPUNCTUATION_MODE = wx.NewId()
 
168
      self.autopunctuation_mode_presage_menu_item = self.presageMenu.Append(self.ID_TOGGLE_AUTOPUNCTUATION_MODE,
 
169
                                                                            "&Autopunctuation\tCTRL+A",
 
170
                                                                            "Toggle autopunctuation mode",
 
171
                                                                            wx.ITEM_CHECK)
 
172
      self.presageMenu.Check(self.ID_TOGGLE_AUTOPUNCTUATION_MODE, True)
 
173
      BindMenu(self.autopunctuation_mode_presage_menu_item, self.OnPresageMenuToggleAutopunctuationMode)
 
174
 
 
175
      # help menu
 
176
      self.helpMenu = wx.Menu()
 
177
      BindMenu(self.helpMenu.Append(wx.ID_HELP, "&Contents"), self.OnHelpMenuContents)
 
178
      self.helpMenu.AppendSeparator()
 
179
      BindMenu(self.helpMenu.Append(wx.ID_ABOUT, "&About"), self.OnHelpMenuAbout)
 
180
 
 
181
      # menu bar
 
182
      self.menuBar = wx.MenuBar()
 
183
      self.menuBar.Append(self.fileMenu, "&File")
 
184
      self.menuBar.Append(self.editMenu, "&Edit")
 
185
      self.menuBar.Append(self.viewMenu, "&View")
 
186
      self.menuBar.Append(self.presageMenu, "&Presage")
 
187
      self.menuBar.Append(self.helpMenu, "&Help")
 
188
      self.SetMenuBar(self.menuBar)
 
189
 
 
190
      # grey out menu items
 
191
      self.fileMenu.Enable(wx.ID_SAVE, False)
 
192
      self.fileMenu.Enable(wx.ID_SAVEAS, False)
 
193
 
 
194
      self.editMenu.Enable(wx.ID_UNDO, False)
 
195
      self.editMenu.Enable(wx.ID_REDO, False)
 
196
 
 
197
   def MakeToolBar(self):
 
198
      def BindTool(item, handler):
 
199
         self.Bind(wx.EVT_TOOL, handler, item)
 
200
         self.Bind(wx.EVT_TOOL_RCLICKED, handler, item)
 
201
 
 
202
      self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL
 
203
                                        | wx.NO_BORDER
 
204
                                        | wx.TB_FLAT)
 
205
 
 
206
      tsize = (16,16)
 
207
      new_bmp =  wx.ArtProvider.GetBitmap(wx.ART_NEW, wx.ART_TOOLBAR, tsize)
 
208
      open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize)
 
209
      save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize)
 
210
 
 
211
      undo_bmp = wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR, tsize)
 
212
      redo_bmp = wx.ArtProvider.GetBitmap(wx.ART_REDO, wx.ART_TOOLBAR, tsize)
 
213
 
 
214
      cut_bmp = wx.ArtProvider.GetBitmap(wx.ART_CUT, wx.ART_TOOLBAR, tsize)
 
215
      copy_bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_TOOLBAR, tsize)
 
216
      paste_bmp= wx.ArtProvider.GetBitmap(wx.ART_PASTE, wx.ART_TOOLBAR, tsize)
 
217
 
 
218
      prompt_me_bmp= wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_TOOLBAR, tsize)
 
219
 
 
220
      self.toolbar.SetToolBitmapSize(tsize)
 
221
      
 
222
      BindTool(self.toolbar.AddLabelTool(wx.ID_NEW, "New", new_bmp, shortHelp="New", longHelp="New file"),
 
223
               self.OnFileMenuNew)
 
224
      BindTool(self.toolbar.AddLabelTool(wx.ID_OPEN, "Open", open_bmp, shortHelp="Open", longHelp="Open file"),
 
225
               self.OnFileMenuOpen)
 
226
      BindTool(self.toolbar.AddLabelTool(wx.ID_SAVE, "Save", save_bmp, shortHelp="Save", longHelp="Save file"),
 
227
               self.OnFileMenuSave)
 
228
      self.toolbar.AddSeparator()
 
229
      BindTool(self.toolbar.AddLabelTool(wx.ID_UNDO, "Undo", undo_bmp, shortHelp="Undo", longHelp="Undo last action"),
 
230
               self.OnEditMenuUndo)
 
231
      BindTool(self.toolbar.AddLabelTool(wx.ID_REDO, "Redo", redo_bmp, shortHelp="Redo", longHelp="Redo last action"),
 
232
               self.OnEditMenuRedo)
 
233
      self.toolbar.AddSeparator()
 
234
      BindTool(self.toolbar.AddLabelTool(wx.ID_CUT, "Cut", cut_bmp, shortHelp="Cut", longHelp="Cut selection"),
 
235
               self.OnEditMenuCut)
 
236
      BindTool(self.toolbar.AddLabelTool(wx.ID_COPY, "Copy", copy_bmp, shortHelp="Copy", longHelp="Copy selection"),
 
237
               self.OnEditMenuCopy)
 
238
      BindTool(self.toolbar.AddLabelTool(wx.ID_PASTE, "Paste", paste_bmp, shortHelp="Paste", longHelp="Paste selection"),
 
239
               self.OnEditMenuPaste)
 
240
      self.toolbar.AddSeparator()
 
241
      BindTool(self.toolbar.AddLabelTool(self.ID_PROMPT_ME, "Prompt me", prompt_me_bmp, shortHelp="Prompt me", longHelp="Prompt me with a prediction"),
 
242
               self.OnPresageMenuPromptMe)
 
243
 
 
244
      self.toolbar.Realize()
 
245
 
 
246
   # menu handlers
 
247
   def OnFileMenuNew(self, event):
 
248
      self.editor.file = None
 
249
      self.editor.ClearAll()
 
250
      self.fileMenu.Enable(wx.ID_SAVE, False)
 
251
      self.fileMenu.Enable(wx.ID_SAVEAS, False)
 
252
 
 
253
   def OnFileMenuOpen(self, event):
 
254
      print "Opening a file.."
 
255
 
 
256
      # Create the dialog. In this case the current directory is forced as the starting
 
257
      # directory for the dialog, and no default file name is forced. This can easilly
 
258
      # be changed in your program. This is an 'open' dialog, and allows multitple
 
259
      # file selections as well.
 
260
      #
 
261
      # Finally, if the directory is changed in the process of getting files, this
 
262
      # dialog is set up to change the current working directory to the path chosen.
 
263
      dlg = wx.FileDialog(
 
264
          self, message="Choose a file", defaultDir="", 
 
265
          defaultFile="", wildcard=self.wildcard, style=wx.OPEN | wx.CHANGE_DIR
 
266
          )
 
267
      
 
268
      # Show the dialog and retrieve the user response. If it is the OK response, 
 
269
      # process the data.
 
270
      if dlg.ShowModal() == wx.ID_OK:
 
271
          # This returns a Python list of files that were selected.
 
272
          path = dlg.GetPath()
 
273
      
 
274
          print ("Opening %s\n" % path)
 
275
 
 
276
          try:
 
277
              fsock = open(path, 'r')
 
278
              contents = fsock.read()
 
279
              fsock.close()
 
280
              self.editor.SetText(contents)
 
281
              self.editor.file = path       # remember file we're editing
 
282
              self.fileMenu.Enable(wx.ID_SAVE, False)
 
283
              self.fileMenu.Enable(wx.ID_SAVEAS, True)
 
284
          except IOError:
 
285
              dialog = wx.MessageDialog(self, "Error opening file %s" % path,
 
286
                                        "Error opening file", wx.OK)
 
287
              dialog.ShowModal()
 
288
              dialog.Destroy()
 
289
              
 
290
      # Destroy the dialog. Don't do this until you are done with it!
 
291
      # BAD things can happen otherwise!
 
292
      dlg.Destroy()
 
293
 
 
294
   def OnFileMenuSave(self, event):
 
295
      print "Save file"
 
296
      if self.editor.file == None:
 
297
         self.OnFileMenuSaveAs(event)
 
298
      else:
 
299
         self.__SaveFile(self.editor.file)
 
300
         self.fileMenu.Enable(wx.ID_SAVE, False)
 
301
 
 
302
   def OnFileMenuSaveAs(self, event):
 
303
      print "Save file as"
 
304
 
 
305
      # Create the dialog. In this case the current directory is forced as the starting
 
306
      # directory for the dialog, and no default file name is forced. This can easilly
 
307
      # be changed in your program. This is an 'save' dialog.
 
308
      #
 
309
      # Unlike the 'open dialog' example found elsewhere, this example does NOT
 
310
      # force the current working directory to change if the user chooses a different
 
311
      # directory than the one initially set.
 
312
      dlg = wx.FileDialog(
 
313
          self, message="Save file as ...", defaultDir="", 
 
314
          defaultFile="", wildcard=self.wildcard, style=wx.SAVE
 
315
          )
 
316
 
 
317
      # This sets the default filter that the user will initially see. Otherwise,
 
318
      # the first filter in the list will be used by default.
 
319
      dlg.SetFilterIndex(2)
 
320
 
 
321
      # Show the dialog and retrieve the user response. If it is the OK response, 
 
322
      # process the data.
 
323
      if dlg.ShowModal() == wx.ID_OK:
 
324
          path = dlg.GetPath()
 
325
          self.editor.file = path  # remember file we're editing has changed
 
326
          self.__SaveFile(path)
 
327
          self.fileMenu.Enable(wx.ID_SAVE, False)
 
328
 
 
329
      # Destroy the dialog. Don't do this until you are done with it!
 
330
      # BAD things can happen otherwise!
 
331
      dlg.Destroy()
 
332
 
 
333
   def OnFileMenuClose(self, event):
 
334
      self.OnFileMenuNew(event)        # this will do for now
 
335
   
 
336
   def OnFileMenuQuit(self, event):
 
337
      print "This should first check that changes have been saved..."
 
338
      self.Close(True)
 
339
   
 
340
   def OnEditMenuUndo(self, event):
 
341
      if self.editor.CanUndo():
 
342
         self.editor.Undo()
 
343
         print "Undo last action"
 
344
 
 
345
   def OnEditMenuRedo(self, event):
 
346
      if self.editor.CanRedo():
 
347
         self.editor.Redo()
 
348
         print "Redo last action"
 
349
 
 
350
   def OnEditMenuCut(self, event):
 
351
      self.clip = self.editor.GetSelectedText()
 
352
      self.editor.ReplaceSelection('')
 
353
      print "Cut selected text: " + self.clip
 
354
   
 
355
   def OnEditMenuCopy(self, event):
 
356
      self.clip = self.editor.GetSelectedText()
 
357
      print "Stored selected text into clip: " + self.clip
 
358
   
 
359
   def OnEditMenuPaste(self, event):
 
360
      self.editor.ReplaceSelection(self.clip)
 
361
      print "Replace selection with: " + self.clip
 
362
 
 
363
   def OnEditMenuSelectAll(self, event):
 
364
      self.editor.SelectAll()
 
365
 
 
366
   def OnViewMenuWordWrap(self, event):
 
367
      self.editor.ToggleWordWrapMode()
 
368
 
 
369
   def OnViewMenuLargerText(self, event):
 
370
      self.editor.IncreaseTextSize()
 
371
 
 
372
   def OnViewMenuSmallerText(self, event):
 
373
      self.editor.DecreaseTextSize()
 
374
 
 
375
   def OnViewMenuShowToolbar(self, event):
 
376
      if event.Checked():
 
377
         self.toolbar.Show()
 
378
      else:
 
379
         self.toolbar.Hide()
 
380
      self.SendSizeEvent() # cause Frame to reevaluate childrens' positions
 
381
 
 
382
   def OnPresageMenuPromptMe(self, event):
 
383
      self.editor.ShowPrediction()
 
384
 
 
385
   def OnPresageMenuToggleLearnMode(self, event):
 
386
      self.editor.prsg.config(self.editor.learn_mode_config_var, str(event.Checked()))
 
387
      print "Learn mode switched to " + str(event.Checked())
 
388
 
 
389
   def OnPresageMenuToggleFunctionMode(self, event):
 
390
      self.editor.function_keys_enabled = event.Checked()
 
391
 
 
392
   def OnPresageMenuToggleAutopunctuationMode(self, event):
 
393
      self.editor.autopunctuation = event.Checked()
 
394
 
 
395
   def OnHelpMenuContents(self, event):
 
396
      message = "Sorry, help not written yet."
 
397
      dialog = wx.MessageDialog(self, message, "pyprompter help", wx.OK)
 
398
      dialog.ShowModal()
 
399
      dialog.Destroy()
 
400
 
 
401
   def OnHelpMenuAbout(self, event):
 
402
      self.__ShowAboutDialogBox()
 
403
 
 
404
   def __ShowAboutDialogBox(self):
 
405
      name = 'pyprompter'
 
406
      version = self.version
 
407
      copyright = '(C) 2008 Matteo Vescovi'
 
408
      description = '''This program is intended as a demonstration of Presage ONLY.
 
409
The Presage project aims to provide an intelligent predictive text entry platform. Its intent is NOT to provide a predictive text entry user interface.
 
410
 
 
411
Think of Presage as the predictive backend that sits behind a shiny user interface and does all the predictive heavy lifting.
 
412
'''
 
413
      website = 'http://presage.sourceforge.net/'
 
414
      devs = ["Matteo Vescovi"]
 
415
      license = '''This program is free software; you can redistribute it and/or modify
 
416
it under the terms of the GNU General Public License as published by
 
417
the Free Software Foundation; either version 2 of the License, or
 
418
(at your option) any later version.
 
419
 
 
420
This program is distributed in the hope that it will be useful,
 
421
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
422
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
423
GNU General Public License for more details.
 
424
 
 
425
You should have received a copy of the GNU General Public License along
 
426
with this program; if not, write to the Free Software Foundation, Inc.,
 
427
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
428
'''
 
429
      # AboutBox and AboutDialogInfo were introduced in wxPython 2.7.1.1
 
430
      if wx.VERSION > (2, 7, 1, 1):
 
431
         # build about dialog information
 
432
         info = wx.AboutDialogInfo()
 
433
         info.SetName(name)
 
434
         info.SetVersion(version)
 
435
         info.SetCopyright(copyright)
 
436
         info.SetDescription(description)
 
437
         info.SetWebSite(website)
 
438
         info.SetDevelopers(devs)
 
439
         info.SetDocWriters(devs)
 
440
         info.SetArtists(devs)
 
441
         #info.SetTranslators(devs)
 
442
         info.SetLicense(license)
 
443
         #info.SetIcon()
 
444
         
 
445
         # show about dialog box
 
446
         wx.AboutBox(info)
 
447
 
 
448
      else:
 
449
         message = name + ' ' + version + '\n' \
 
450
                   + '\n' \
 
451
                   + copyright + '\n' \
 
452
                   + '\n' \
 
453
                   + description + '\n' \
 
454
                   + website
 
455
         
 
456
         dialog = wx.MessageDialog(self, message, "About pyprompter", wx.OK)
 
457
         dialog.ShowModal()
 
458
         dialog.Destroy()
 
459
 
 
460
   def __SaveFile(self, path):
 
461
      try:
 
462
         fp = open(path, 'w') # Create file anew
 
463
         fp.write(self.editor.GetText())
 
464
         fp.close()
 
465
      except IOError:
 
466
         dialog = wx.MessageDialog(self, "Error saving file %s" % path,
 
467
                                   "Error saving file", wx.OK)
 
468
         dialog.ShowModal()
 
469
         dialog.Destroy()
 
470
 
 
471
################
 
472
# PrompterEditor
 
473
#
 
474
class PrompterEditor(wx.stc.StyledTextCtrl):
 
475
   def __init__(self, parent, config, suggestions):
 
476
      wx.stc.StyledTextCtrl.__init__(self, parent)
 
477
 
 
478
      self.parent = parent    # remember parent access frame menus
 
479
      self.file = None        # remember what file to save to
 
480
      self.append_whitespace_on_completion = True
 
481
      self.autopunctuation = True
 
482
      self.autopunctuation_whitespace = ' '
 
483
      self.autopunctuation_chars = ".,;:'?!$%&"
 
484
      self.function_keys_enabled = True
 
485
      self.learn_mode_config_var = "Presage.Predictors.SmoothedNgramPredictor.LEARN"
 
486
 
 
487
      self.Bind(wx.EVT_CHAR, self.OnChar)
 
488
      self.Bind(wx.stc.EVT_STC_USERLISTSELECTION, self.OnUserListSelection)
 
489
      self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModified)
 
490
 
 
491
      class PrompterPresageCallback(presage.PresageCallback):
 
492
         def __init__(self, edtr):
 
493
            presage.PresageCallback.__init__(self)
 
494
            self.editor = edtr
 
495
 
 
496
         def get_past_stream(self):
 
497
            result = self.editor.GetTextRange(0,
 
498
                                              self.editor.GetCurrentPos())
 
499
            return str(result)
 
500
 
 
501
         def get_future_stream(self):
 
502
            result = self.editor.GetTextRange(self.editor.GetCurrentPos(),
 
503
                                              self.editor.GetTextLength())
 
504
            return str(result)
 
505
 
 
506
      callback = PrompterPresageCallback(self).__disown__()
 
507
 
 
508
      if config:
 
509
         self.prsg = presage.Presage(callback, config)
 
510
      else:
 
511
         self.prsg = presage.Presage(callback)
 
512
 
 
513
      if suggestions:
 
514
         self.prsg.config('Presage.Selector.SUGGESTIONS', suggestions)
 
515
 
 
516
      self.SetCodePage(wx.stc.STC_CP_UTF8) # set unicode codepage
 
517
      self.SetWrapMode(wx.stc.STC_WRAP_WORD)
 
518
      self.AutoCompSetAutoHide(False)
 
519
      self.separator = ','
 
520
      self.AutoCompSetSeparator(44)
 
521
      #self.AutoCompSetIgnoreCase(1)
 
522
      self.AutoCompSetMaxHeight(int(self.prsg.config("Presage.Selector.SUGGESTIONS")))
 
523
 
 
524
      self.text_size = 10
 
525
      self.StyleSetSize(wx.stc.STC_STYLE_DEFAULT, self.text_size)
 
526
 
 
527
      # hide margings
 
528
      for i in range(5):
 
529
         self.SetMarginWidth(i, 0)
 
530
 
 
531
      # delaying the __ShowPrediction until after the parent frame and
 
532
      # the STC are shown.
 
533
      wx.CallAfter(self.__ShowPrediction)
 
534
      wx.CallAfter(self.SetSTCFocus, 1)
 
535
 
 
536
   def OnChar(self, event):
 
537
      print "------------ OnChar() handler"
 
538
 
 
539
      keycode = event.GetKeyCode()
 
540
      
 
541
      if self.__FunctionKey(keycode):
 
542
         self.__HandleFunctionKey(keycode)
 
543
      else:
 
544
         key = unichr(keycode)
 
545
 
 
546
         self.parent.fileMenu.Enable(wx.ID_SAVE, True)
 
547
         self.parent.fileMenu.Enable(wx.ID_SAVEAS, True)
 
548
 
 
549
         if self.__AutoPunctuation(key):
 
550
            # autopunctuation takes care of adding text and updating
 
551
            # prsg, nothing to do here.
 
552
            pass
 
553
         else:
 
554
            self.AddText(key)
 
555
 
 
556
         self.__ShowPrediction()
 
557
 
 
558
   def ShowPrediction(self, string = ''):
 
559
      self.__ShowPrediction(string)
 
560
 
 
561
   def __ShowPrediction(self, string = ''):
 
562
      print "------------ __ShowPrediction()"
 
563
      try:
 
564
         prefix = self.prsg.prefix()
 
565
         context = self.prsg.context()
 
566
         context_change = self.prsg.context_change()
 
567
 
 
568
         self.prediction = self.prsg.predict()
 
569
 
 
570
         if self.function_keys_enabled:
 
571
            self.prediction = self.__PrependFunctionLabel(self.prediction)
 
572
         self.suggestions = self.separator.join(self.prediction);
 
573
 
 
574
      except presage.PresageException, ex:
 
575
         print 'Caught exception %s' % (ex)
 
576
         print '  code: %d' % (ex.code())
 
577
         print '  what: %s' % (ex.what())
 
578
         
 
579
         if ex.code() == presage.PRESAGE_SQLITE_EXECUTE_SQL_ERROR:
 
580
            # switch off learning and update menu checkbox
 
581
            self.prsg.config(self.learn_mode_config_var, "false")
 
582
            self.parent.presageMenu.Check(self.parent.ID_TOGGLE_LEARN_MODE, False)
 
583
 
 
584
            # notify user
 
585
            dialog = wx.MessageDialog(self.parent, "The adaptive text learning component of the presage predictive text entry engine has detected an error. Learning has been switched off.", "Learning error in presage predictive text engine", wx.OK)
 
586
            dialog.ShowModal()
 
587
            dialog.Destroy()
 
588
 
 
589
      print "String:         " + string
 
590
      print "Prefix:         " + prefix
 
591
      print "Prefix len:     " + str(len(prefix))
 
592
      print "Context:        " + context
 
593
      print "Context change: " + str(context_change)
 
594
      print "Prediction:     " + self.suggestions
 
595
 
 
596
      if self.AutoCompActive():
 
597
         self.AutoCompCancel()
 
598
 
 
599
      # AutoCompShow() does not generate an event when autocompletion is
 
600
      # successful, hence it is not possible to notify presage that
 
601
      # the current token was completed.
 
602
      #self.AutoCompShow(len(prefix), suggestions)
 
603
 
 
604
      # UserListShow() throws an EVT_STC_USERLISTSELECTION event that we
 
605
      # can handle to notify presage that the token was automatically
 
606
      # completed.
 
607
      self.UserListShow(1, self.suggestions)
 
608
 
 
609
   def __AutoPunctuation(self, char):
 
610
      if self.autopunctuation:
 
611
         # autopunctuation is enabled
 
612
         if char in self.autopunctuation_chars:
 
613
            # char is an autopunctuation character
 
614
            curr_pos = self.GetCurrentPos()
 
615
            if curr_pos > 0:
 
616
               # previous character exists
 
617
               prev_pos = curr_pos - 1
 
618
               prev_char = chr(self.GetCharAt(prev_pos))
 
619
               if prev_char in self.autopunctuation_whitespace:
 
620
                  # previous character is an autopunctuation whitespace
 
621
 
 
622
                  # swap whitespace and current char
 
623
                  self.SetSelection(prev_pos, curr_pos)
 
624
                  self.ReplaceSelection(char + ' ')
 
625
 
 
626
                  return True
 
627
 
 
628
      return False
 
629
 
 
630
   def __FunctionKey(self, keycode):
 
631
      result = False
 
632
      if self.function_keys_enabled:
 
633
         self.function_keys = [wx.WXK_F1, wx.WXK_F2, wx.WXK_F3, wx.WXK_F4,  wx.WXK_F5,  wx.WXK_F6,
 
634
                               wx.WXK_F7, wx.WXK_F8, wx.WXK_F9, wx.WXK_F10, wx.WXK_F11, wx.WXK_F12]
 
635
         if keycode in self.function_keys:
 
636
            result = True
 
637
      return result
 
638
 
 
639
   def __HandleFunctionKey(self, key):
 
640
      print "Got function key " + str(key)
 
641
 
 
642
      try:
 
643
         idx = self.function_keys.index(key)
 
644
         print self.prediction[idx]
 
645
         if self.AutoCompActive():
 
646
            self.AutoCompCancel()
 
647
 
 
648
         self.UserListShow(1, self.suggestions)
 
649
         self.AutoCompSelect(self.prediction[idx])
 
650
         self.AutoCompComplete()
 
651
      except IndexError:
 
652
         print 'Key not in prediction completion list'
 
653
   
 
654
   def __PrependFunctionLabel(self, prediction):
 
655
      return ['F' + str(i + 1) + ' ' + prediction[i] for i in range(len(prediction))]
 
656
 
 
657
   def __RemoveFunctionLabel(self, completion):
 
658
      idx = completion.find(' ')
 
659
      if not idx == -1:
 
660
         completion = completion[idx+1:]
 
661
      return completion
 
662
 
 
663
   def OnUserListSelection(self, event):
 
664
      completion = unicode(event.GetText())
 
665
      if self.function_keys_enabled:
 
666
         completion = self.__RemoveFunctionLabel(completion)
 
667
      prefix_length = len(unicode(self.prsg.prefix()))
 
668
      
 
669
      print "----------- OnUserListSelection() handler"
 
670
      print "Completion:    " + completion
 
671
      print "Prefix length: " + str(prefix_length)
 
672
      print "To be added:   " + completion[prefix_length:]
 
673
 
 
674
      # no need to call complete, using callbacks
 
675
      #self.prsg.complete(completion.encode('utf-8'))
 
676
      self.AddText(completion[prefix_length:])
 
677
 
 
678
      if self.append_whitespace_on_completion:
 
679
         self.AddText(' ')
 
680
         # no need to update, using callbacks
 
681
         #self.prsg.update(' ')
 
682
 
 
683
      # schedule showing of prediction after current and pending events
 
684
      # are dealt with (thanks to Robin Dunn for pointing this out)
 
685
      wx.CallAfter(self.__ShowPrediction)
 
686
 
 
687
   def OnModified(self, event):
 
688
      # grey out or enable menu items
 
689
      self.parent.fileMenu.Enable(wx.ID_SAVE, True)
 
690
      self.parent.fileMenu.Enable(wx.ID_SAVEAS, True)
 
691
      
 
692
      if self.CanUndo():
 
693
         self.parent.editMenu.Enable(wx.ID_UNDO, True)
 
694
      else:
 
695
         self.parent.editMenu.Enable(wx.ID_UNDO, False)
 
696
      
 
697
      if self.CanRedo():
 
698
         self.parent.editMenu.Enable(wx.ID_REDO, True)
 
699
      else:
 
700
         self.parent.editMenu.Enable(wx.ID_REDO, False)
 
701
 
 
702
   def ToggleWordWrapMode(self):
 
703
      if self.parent.word_wrap.IsChecked():
 
704
         self.SetWrapMode(wx.stc.STC_WRAP_WORD)
 
705
      else:
 
706
         self.SetWrapMode(wx.stc.STC_WRAP_NONE)
 
707
 
 
708
   def IncreaseTextSize(self):
 
709
      self.text_size += 1
 
710
      self.StyleSetSize(wx.stc.STC_STYLE_DEFAULT, self.text_size)
 
711
 
 
712
   def DecreaseTextSize(self):
 
713
      if self.text_size > 1:
 
714
         self.text_size -= 1
 
715
         self.StyleSetSize(wx.stc.STC_STYLE_DEFAULT, self.text_size)