prompt.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. """!
  2. @package prompt.py
  3. @brief wxGUI prompt
  4. Classes:
  5. - GPrompt
  6. - PromptListCtrl
  7. - TextCtrlAutoComplete
  8. @todo: fix TextCtrlAutoComplete to work also on Macs (missing
  9. wx.PopupWindow())
  10. (C) 2009 by the GRASS Development Team
  11. This program is free software under the GNU General Public
  12. License (>=v2). Read the file COPYING that comes with GRASS
  13. for details.
  14. @author Martin Landa <landa.martin gmail.com>
  15. """
  16. import os
  17. import sys
  18. import shlex
  19. import wx
  20. import wx.lib.mixins.listctrl as listmix
  21. from grass.script import core as grass
  22. import globalvar
  23. import utils
  24. import menuform
  25. import menudata
  26. class GPrompt:
  27. """!Interactive GRASS prompt"""
  28. def __init__(self, parent):
  29. self.parent = parent # GMFrame
  30. # dictionary of modules (description, keywords, ...)
  31. self.modules = self.parent.menudata.GetModules()
  32. self.panel, self.input = self.__create()
  33. def __create(self):
  34. """!Create widget"""
  35. cmdprompt = wx.Panel(self.parent)
  36. #
  37. # search
  38. #
  39. searchTxt = wx.StaticText(parent = cmdprompt, id = wx.ID_ANY,
  40. label = _("Find module:"))
  41. self.searchBy = wx.Choice(parent = cmdprompt, id = wx.ID_ANY,
  42. choices = [_("description"),
  43. _("keywords")])
  44. winHeight = self.searchBy.GetSize()[1]
  45. self.search = wx.TextCtrl(parent = cmdprompt, id = wx.ID_ANY,
  46. value = "", size = (-1, 25))
  47. label = wx.Button(parent = cmdprompt, id = wx.ID_ANY,
  48. label = _("&Cmd >"), size = (-1, winHeight))
  49. label.SetToolTipString(_("Click for erasing command prompt"))
  50. ### todo: fix TextCtrlAutoComplete to work also on Macs
  51. ### reason: missing wx.PopupWindow()
  52. try:
  53. cmdinput = TextCtrlAutoComplete(parent = cmdprompt, id = wx.ID_ANY,
  54. value = "",
  55. style = wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
  56. size = (-1, winHeight),
  57. statusbar = self.parent.statusbar)
  58. except NotImplementedError:
  59. # wx.PopupWindow may be not available in wxMac
  60. # see http://trac.wxwidgets.org/ticket/9377
  61. cmdinput = wx.TextCtrl(parent = cmdprompt, id = wx.ID_ANY,
  62. value = "",
  63. style=wx.TE_LINEWRAP | wx.TE_PROCESS_ENTER,
  64. size = (-1, 25))
  65. self.searchBy.Enable(False)
  66. self.search.Enable(False)
  67. cmdinput.SetFont(wx.Font(10, wx.FONTFAMILY_MODERN, wx.NORMAL, wx.NORMAL, 0, ''))
  68. wx.CallAfter(cmdinput.SetInsertionPoint, 0)
  69. # bidnings
  70. label.Bind(wx.EVT_BUTTON, self.OnCmdErase)
  71. cmdinput.Bind(wx.EVT_TEXT_ENTER, self.OnRunCmd)
  72. cmdinput.Bind(wx.EVT_TEXT, self.OnUpdateStatusBar)
  73. self.search.Bind(wx.EVT_TEXT, self.OnSearchModule)
  74. # layout
  75. sizer = wx.GridBagSizer(hgap=5, vgap=5)
  76. sizer.AddGrowableRow(1)
  77. sizer.AddGrowableCol(2)
  78. sizer.Add(item = searchTxt,
  79. flag = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL,
  80. pos = (0, 0))
  81. sizer.Add(item = self.searchBy,
  82. flag = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER,
  83. pos = (0, 1))
  84. sizer.Add(item = self.search,
  85. flag = wx.EXPAND | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER,
  86. border = 5,
  87. pos = (0, 2))
  88. sizer.Add(item = label,
  89. flag = wx.LEFT | wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER,
  90. border = 5,
  91. pos = (1, 0))
  92. sizer.Add(item = cmdinput,
  93. flag = wx.EXPAND | wx.RIGHT,
  94. border = 5,
  95. pos = (1, 1), span = (1, 2))
  96. cmdprompt.SetSizer(sizer)
  97. sizer.Fit(cmdprompt)
  98. cmdprompt.Layout()
  99. return cmdprompt, cmdinput
  100. def __checkKey(self, text, keywords):
  101. """!Check if text is in keywords"""
  102. found = 0
  103. keys = text.split(',')
  104. if len(keys) > 1: # -> multiple keys
  105. for k in keys[:-1]:
  106. k = k.strip()
  107. for key in keywords:
  108. if k == key: # full match
  109. found += 1
  110. break
  111. k = keys[-1].strip()
  112. for key in keywords:
  113. if k in key: # partial match
  114. found +=1
  115. break
  116. else:
  117. for key in keywords:
  118. if text in key: # partial match
  119. found +=1
  120. break
  121. if found == len(keys):
  122. return True
  123. return False
  124. def GetPanel(self):
  125. """!Get main widget panel"""
  126. return self.panel
  127. def GetInput(self):
  128. """!Get main prompt widget"""
  129. return self.input
  130. def OnCmdErase(self, event):
  131. """!Erase command prompt"""
  132. self.input.SetValue('')
  133. def OnRunCmd(self, event):
  134. """!Run command"""
  135. cmdString = event.GetString()
  136. if self.parent.GetName() != "LayerManager":
  137. return
  138. if cmdString[:2] == 'd.' and not self.parent.curr_page:
  139. self.parent.NewDisplay(show=True)
  140. cmd = shlex.split(str(cmdString))
  141. if len(cmd) > 1:
  142. self.parent.goutput.RunCmd(cmd, switchPage = True)
  143. else:
  144. self.parent.goutput.RunCmd(cmd, switchPage = False)
  145. self.OnUpdateStatusBar(None)
  146. def OnUpdateStatusBar(self, event):
  147. """!Update Layer Manager status bar"""
  148. if self.parent.GetName() != "LayerManager":
  149. return
  150. if event is None:
  151. self.parent.statusbar.SetStatusText("")
  152. else:
  153. self.parent.statusbar.SetStatusText(_("Type GRASS command and run by pressing ENTER"))
  154. event.Skip()
  155. def OnSearchModule(self, event):
  156. """!Search module by metadata"""
  157. text = event.GetString()
  158. if not text:
  159. self.input.SetChoices(globalvar.grassCmd['all'])
  160. return
  161. modules = []
  162. for module, data in self.modules.iteritems():
  163. if self.searchBy.GetSelection() == 0: # -> description
  164. if text in data['desc']:
  165. modules.append(module)
  166. else: # -> keywords
  167. if self.__checkKey(text, data['keywords']):
  168. modules.append(module)
  169. self.parent.statusbar.SetStatusText(_("%d modules found") % len(modules))
  170. self.input.SetChoices(modules)
  171. class PromptListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
  172. def __init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition,
  173. size = wx.DefaultSize, style = 0):
  174. wx.ListCtrl.__init__(self, parent, id, pos, size, style)
  175. listmix.ListCtrlAutoWidthMixin.__init__(self)
  176. class TextCtrlAutoComplete(wx.ComboBox, listmix.ColumnSorterMixin):
  177. def __init__ (self, parent, statusbar,
  178. id = wx.ID_ANY, choices = [], **kwargs):
  179. """!Constructor works just like wx.TextCtrl except you can pass in a
  180. list of choices. You can also change the choice list at any time
  181. by calling setChoices.
  182. Inspired by http://wiki.wxpython.org/TextCtrlAutoComplete
  183. """
  184. self.statusbar = statusbar
  185. if kwargs.has_key('style'):
  186. kwargs['style'] = wx.TE_PROCESS_ENTER | kwargs['style']
  187. else:
  188. kwargs['style'] = wx.TE_PROCESS_ENTER
  189. wx.ComboBox.__init__(self, parent, id, **kwargs)
  190. # some variables
  191. self._choices = choices
  192. self._hideOnNoMatch = True
  193. self._module = None # currently selected module
  194. self._choiceType = None # type of choice (module, params, flags, raster, vector ...)
  195. self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
  196. self._historyItem = 0 # last item
  197. # sort variable needed by listmix
  198. self.itemDataMap = dict()
  199. # widgets
  200. try:
  201. self.dropdown = wx.PopupWindow(self)
  202. except NotImplementedError:
  203. self.Destroy()
  204. raise NotImplementedError
  205. # create the list and bind the events
  206. self.dropdownlistbox = PromptListCtrl(parent = self.dropdown,
  207. style = wx.LC_REPORT | wx.LC_SINGLE_SEL | \
  208. wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER,
  209. pos = wx.Point(0, 0))
  210. listmix.ColumnSorterMixin.__init__(self, 1)
  211. # set choices (list of GRASS modules)
  212. self._choicesCmd = globalvar.grassCmd['all']
  213. self._choicesMap = dict()
  214. for type in ('raster', 'vector'):
  215. self._choicesMap[type] = grass.list_strings(type = type[:4])
  216. # first search for GRASS module
  217. self.SetChoices(self._choicesCmd)
  218. self.SetMinSize(self.GetSize())
  219. # read history
  220. self.SetHistoryItems()
  221. # bindings...
  222. self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged)
  223. self.Bind(wx.EVT_TEXT, self.OnEnteredText)
  224. self.Bind(wx.EVT_KEY_DOWN , self.OnKeyDown)
  225. ### self.Bind(wx.EVT_LEFT_DOWN, self.OnClick)
  226. # if need drop down on left click
  227. self.dropdown.Bind(wx.EVT_LISTBOX , self.OnListItemSelected, self.dropdownlistbox)
  228. self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.OnListClick)
  229. self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.OnListDClick)
  230. self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.OnListColClick)
  231. self.Bind(wx.EVT_COMBOBOX, self.OnCommandSelect)
  232. def _updateDataList(self, choices):
  233. """!Update data list"""
  234. # delete, if need, all the previous data
  235. if self.dropdownlistbox.GetColumnCount() != 0:
  236. self.dropdownlistbox.DeleteAllColumns()
  237. self.dropdownlistbox.DeleteAllItems()
  238. # and update the dict
  239. if choices:
  240. for numVal, data in enumerate(choices):
  241. self.itemDataMap[numVal] = data
  242. else:
  243. numVal = 0
  244. self.SetColumnCount(numVal)
  245. def _setListSize(self):
  246. """!Set list size"""
  247. choices = self._choices
  248. longest = 0
  249. for choice in choices:
  250. longest = max(len(choice), longest)
  251. longest += 3
  252. itemcount = min(len( choices ), 7) + 2
  253. charheight = self.dropdownlistbox.GetCharHeight()
  254. charwidth = self.dropdownlistbox.GetCharWidth()
  255. self.popupsize = wx.Size(charwidth*longest, charheight*itemcount)
  256. self.dropdownlistbox.SetSize(self.popupsize)
  257. self.dropdown.SetClientSize(self.popupsize)
  258. def _showDropDown(self, show = True):
  259. """!Either display the drop down list (show = True) or hide it
  260. (show = False).
  261. """
  262. if show:
  263. size = self.dropdown.GetSize()
  264. width, height = self.GetSizeTuple()
  265. x, y = self.ClientToScreenXY(0, height)
  266. if size.GetWidth() != width:
  267. size.SetWidth(width)
  268. self.dropdown.SetSize(size)
  269. self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
  270. if (y + size.GetHeight()) < self._screenheight:
  271. self.dropdown.SetPosition(wx.Point(x, y))
  272. else:
  273. self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight()))
  274. self.dropdown.Show(show)
  275. def _listItemVisible(self):
  276. """!Moves the selected item to the top of the list ensuring it is
  277. always visible.
  278. """
  279. toSel = self.dropdownlistbox.GetFirstSelected()
  280. if toSel == -1:
  281. return
  282. self.dropdownlistbox.EnsureVisible(toSel)
  283. def _setModule(self, name):
  284. """!Set module's choices (flags, parameters)"""
  285. # get module's description
  286. if name in self._choicesCmd and not self._module:
  287. try:
  288. self._module = menuform.GUI().ParseInterface(cmd = [name])
  289. except IOError:
  290. self._module = None
  291. # set choices (flags)
  292. self._choicesMap['flag'] = self._module.get_list_flags()
  293. for idx in range(len(self._choicesMap['flag'])):
  294. item = self._choicesMap['flag'][idx]
  295. desc = self._module.get_flag(item)['label']
  296. if not desc:
  297. desc = self._module.get_flag(item)['description']
  298. self._choicesMap['flag'][idx] = '%s (%s)' % (item, desc)
  299. # set choices (parameters)
  300. self._choicesMap['param'] = self._module.get_list_params()
  301. for idx in range(len(self._choicesMap['param'])):
  302. item = self._choicesMap['param'][idx]
  303. desc = self._module.get_param(item)['label']
  304. if not desc:
  305. desc = self._module.get_param(item)['description']
  306. self._choicesMap['param'][idx] = '%s (%s)' % (item, desc)
  307. def _setValueFromSelected(self):
  308. """!Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
  309. Will do nothing if no item is selected in the wx.ListCtrl.
  310. """
  311. sel = self.dropdownlistbox.GetFirstSelected()
  312. if sel < 0:
  313. return
  314. if self._colFetch != -1:
  315. col = self._colFetch
  316. else:
  317. col = self._colSearch
  318. itemtext = self.dropdownlistbox.GetItem(sel, col).GetText()
  319. cmd = shlex.split(str(self.GetValue()))
  320. if len(cmd) > 0 and cmd[0] in self._choicesCmd:
  321. # -> append text (skip last item)
  322. if self._choiceType == 'param':
  323. itemtext = itemtext.split(' ')[0]
  324. self.SetValue(' '.join(cmd) + ' ' + itemtext + '=')
  325. optType = self._module.get_param(itemtext)['prompt']
  326. if optType in ('raster', 'vector'):
  327. # -> raster/vector map
  328. self.SetChoices(self._choicesMap[optType], optType)
  329. elif self._choiceType == 'flag':
  330. itemtext = itemtext.split(' ')[0]
  331. if len(itemtext) > 1:
  332. prefix = '--'
  333. else:
  334. prefix = '-'
  335. self.SetValue(' '.join(cmd[:-1]) + ' ' + prefix + itemtext)
  336. elif self._choiceType in ('raster', 'vector'):
  337. self.SetValue(' '.join(cmd[:-1]) + ' ' + cmd[-1].split('=', 1)[0] + '=' + itemtext)
  338. else:
  339. # -> reset text
  340. self.SetValue(itemtext + ' ')
  341. # define module
  342. self._setModule(itemtext)
  343. # use parameters as default choices
  344. self._choiceType = 'param'
  345. self.SetChoices(self._choicesMap['param'], type = 'param')
  346. self.SetInsertionPointEnd()
  347. self._showDropDown(False)
  348. def GetListCtrl(self):
  349. """!Method required by listmix.ColumnSorterMixin"""
  350. return self.dropdownlistbox
  351. def SetHistoryItems(self):
  352. """!Read history file and update combobox items"""
  353. env = grass.gisenv()
  354. try:
  355. fileHistory = open(os.path.join(env['GISDBASE'],
  356. env['LOCATION_NAME'],
  357. env['MAPSET'],
  358. '.bash_history'), 'r')
  359. except IOError:
  360. self.SetItems([])
  361. return
  362. try:
  363. hist = []
  364. for line in fileHistory.readlines():
  365. hist.append(line.replace('\n', ''))
  366. self.SetItems(hist)
  367. finally:
  368. fileHistory.close()
  369. return
  370. self.SetItems([])
  371. def SetChoices(self, choices, type = 'module'):
  372. """!Sets the choices available in the popup wx.ListBox.
  373. The items will be sorted case insensitively.
  374. @param choices list of choices
  375. @param type type of choices (module, param, flag, raster, vector)
  376. """
  377. self._choices = choices
  378. self._choiceType = type
  379. self.dropdownlistbox.SetWindowStyleFlag(wx.LC_REPORT | wx.LC_SINGLE_SEL |
  380. wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER)
  381. if not isinstance(choices, list):
  382. self._choices = [ x for x in choices ]
  383. if self._choiceType not in ('raster', 'vector'):
  384. # do not sort raster/vector maps
  385. utils.ListSortLower(self._choices)
  386. self._updateDataList(self._choices)
  387. self.dropdownlistbox.InsertColumn(0, "")
  388. for num, colVal in enumerate(self._choices):
  389. index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)
  390. self.dropdownlistbox.SetStringItem(index, 0, colVal)
  391. self.dropdownlistbox.SetItemData(index, num)
  392. self._setListSize()
  393. # there is only one choice for both search and fetch if setting a single column:
  394. self._colSearch = 0
  395. self._colFetch = -1
  396. def OnClick(self, event):
  397. """Left mouse button pressed"""
  398. sel = self.dropdownlistbox.GetFirstSelected()
  399. if not self.dropdown.IsShown():
  400. if sel > -1:
  401. self.dropdownlistbox.Select(sel)
  402. else:
  403. self.dropdownlistbox.Select(0)
  404. self._listItemVisible()
  405. self._showDropDown()
  406. else:
  407. self.dropdown.Hide()
  408. def OnCommandSelect(self, event):
  409. """!Command selected from history"""
  410. self._historyItem = event.GetSelection() - len(self.GetItems())
  411. self.SetFocus()
  412. def OnListClick(self, evt):
  413. """!Left mouse button pressed"""
  414. toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() )
  415. #no values on poition, return
  416. if toSel == -1: return
  417. self.dropdownlistbox.Select(toSel)
  418. def OnListDClick(self, evt):
  419. """!Mouse button double click"""
  420. self._setValueFromSelected()
  421. def OnListColClick(self, evt):
  422. """!Left mouse button pressed on column"""
  423. col = evt.GetColumn()
  424. # reverse the sort
  425. if col == self._colSearch:
  426. self._ascending = not self._ascending
  427. self.SortListItems( evt.GetColumn(), ascending=self._ascending )
  428. self._colSearch = evt.GetColumn()
  429. evt.Skip()
  430. def OnListItemSelected(self, event):
  431. """!Item selected"""
  432. self._setValueFromSelected()
  433. event.Skip()
  434. def OnEnteredText(self, event):
  435. """!Text entered"""
  436. text = event.GetString()
  437. if not text:
  438. # control is empty; hide dropdown if shown:
  439. if self.dropdown.IsShown():
  440. self._showDropDown(False)
  441. event.Skip()
  442. return
  443. try:
  444. cmd = shlex.split(str(text))
  445. except ValueError, e:
  446. self.statusbar.SetStatusText(str(e))
  447. cmd = text.split(' ')
  448. pattern = str(text)
  449. if len(cmd) > 0 and cmd[0] in self._choicesCmd and not self._module:
  450. self._setModule(cmd[0])
  451. elif len(cmd) > 1 and cmd[0] in self._choicesCmd:
  452. if self._module:
  453. if len(cmd[-1].split('=', 1)) == 1:
  454. # new option
  455. if cmd[-1][0] == '-':
  456. # -> flags
  457. self.SetChoices(self._choicesMap['flag'], type = 'flag')
  458. pattern = cmd[-1].lstrip('-')
  459. else:
  460. # -> options
  461. self.SetChoices(self._choicesMap['param'], type = 'param')
  462. pattern = cmd[-1]
  463. else:
  464. # value
  465. pattern = cmd[-1].split('=', 1)[1]
  466. else:
  467. # search for GRASS modules
  468. if self._module:
  469. # -> switch back to GRASS modules list
  470. self.SetChoices(self._choicesCmd)
  471. self._module = None
  472. self._choiceType = None
  473. self._choiceType
  474. self._choicesMap
  475. found = False
  476. choices = self._choices
  477. for numCh, choice in enumerate(choices):
  478. if choice.lower().startswith(pattern):
  479. found = True
  480. if found:
  481. self._showDropDown(True)
  482. item = self.dropdownlistbox.GetItem(numCh)
  483. toSel = item.GetId()
  484. self.dropdownlistbox.Select(toSel)
  485. break
  486. if not found:
  487. self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False)
  488. if self._hideOnNoMatch:
  489. self._showDropDown(False)
  490. if self._module and '=' not in cmd[-1]:
  491. message = ''
  492. if cmd[-1][0] == '-': # flag
  493. message = _("Warning: flag <%s> not found in '%s'") % \
  494. (cmd[-1][1:], self._module.name)
  495. else: # option
  496. message = _("Warning: option <%s> not found in '%s'") % \
  497. (cmd[-1], self._module.name)
  498. self.statusbar.SetStatusText(message)
  499. if self._module and len(cmd[-1]) == 2 and cmd[-1][-2] == '=':
  500. optType = self._module.get_param(cmd[-1][:-2])['prompt']
  501. if optType in ('raster', 'vector'):
  502. # -> raster/vector map
  503. self.SetChoices(self._choicesMap[optType], optType)
  504. self._listItemVisible()
  505. event.Skip()
  506. def OnKeyDown (self, event):
  507. """!Do some work when the user press on the keys: up and down:
  508. move the cursor left and right: move the search
  509. """
  510. skip = True
  511. sel = self.dropdownlistbox.GetFirstSelected()
  512. visible = self.dropdown.IsShown()
  513. KC = event.GetKeyCode()
  514. if KC == wx.WXK_RIGHT:
  515. # right -> show choices
  516. if sel < (self.dropdownlistbox.GetItemCount() - 1):
  517. self.dropdownlistbox.Select(sel + 1)
  518. self._listItemVisible()
  519. self._showDropDown()
  520. skip = False
  521. elif KC == wx.WXK_UP:
  522. if visible:
  523. if sel > 0:
  524. self.dropdownlistbox.Select(sel - 1)
  525. self._listItemVisible()
  526. self._showDropDown()
  527. skip = False
  528. else:
  529. self._historyItem -= 1
  530. try:
  531. self.SetValue(self.GetItems()[self._historyItem])
  532. except IndexError:
  533. self._historyItem += 1
  534. elif KC == wx.WXK_DOWN:
  535. if visible:
  536. if sel < (self.dropdownlistbox.GetItemCount() - 1):
  537. self.dropdownlistbox.Select(sel + 1)
  538. self._listItemVisible()
  539. self._showDropDown()
  540. skip = False
  541. else:
  542. if self._historyItem < -1:
  543. self._historyItem += 1
  544. self.SetValue(self.GetItems()[self._historyItem])
  545. if visible:
  546. if event.GetKeyCode() == wx.WXK_RETURN:
  547. self._setValueFromSelected()
  548. skip = False
  549. if event.GetKeyCode() == wx.WXK_ESCAPE:
  550. self._showDropDown(False)
  551. skip = False
  552. if skip:
  553. event.Skip()
  554. def OnControlChanged(self, event):
  555. """!Control changed"""
  556. if self.IsShown():
  557. self._showDropDown(False)
  558. event.Skip()