26 import cPickle |
26 import cPickle |
27 |
27 |
28 MAX_ITEM_COUNT = 10 |
28 MAX_ITEM_COUNT = 10 |
29 MAX_ITEM_SHOWN = 6 |
29 MAX_ITEM_SHOWN = 6 |
30 if wx.Platform == '__WXMSW__': |
30 if wx.Platform == '__WXMSW__': |
31 ITEM_INTERVAL_HEIGHT = 3 |
31 LISTBOX_BORDER_HEIGHT = 2 |
|
32 LISTBOX_INTERVAL_HEIGHT = 0 |
32 else: |
33 else: |
33 ITEM_INTERVAL_HEIGHT = 6 |
34 LISTBOX_BORDER_HEIGHT = 4 |
34 |
35 LISTBOX_INTERVAL_HEIGHT = 6 |
35 if wx.Platform == '__WXMSW__': |
36 |
36 popupclass = wx.PopupTransientWindow |
37 class PopupWithListbox(wx.PopupWindow): |
37 else: |
|
38 popupclass = wx.PopupWindow |
|
39 |
|
40 class PopupWithListbox(popupclass): |
|
41 |
38 |
42 def __init__(self, parent, choices=[]): |
39 def __init__(self, parent, choices=[]): |
43 popupclass.__init__(self, parent, wx.SIMPLE_BORDER) |
40 wx.PopupWindow.__init__(self, parent, wx.BORDER_SIMPLE) |
44 |
41 |
45 self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT) |
42 self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT) |
46 if not wx.Platform == '__WXMSW__': |
43 |
47 self.ListBox.Bind(wx.EVT_LISTBOX, self.OnListBoxClick) |
|
48 self.ListBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnListBoxClick) |
|
49 |
|
50 self.SetChoices(choices) |
44 self.SetChoices(choices) |
51 |
45 |
52 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) |
46 self.ListBox.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) |
|
47 self.ListBox.Bind(wx.EVT_MOTION, self.OnMotion) |
53 |
48 |
54 def SetChoices(self, choices): |
49 def SetChoices(self, choices): |
55 max_text_width = 0 |
50 max_text_width = 0 |
56 max_text_height = 0 |
51 max_text_height = 0 |
57 |
52 |
62 max_text_width = max(max_text_width, w) |
57 max_text_width = max(max_text_width, w) |
63 max_text_height = max(max_text_height, h) |
58 max_text_height = max(max_text_height, h) |
64 |
59 |
65 itemcount = min(len(choices), MAX_ITEM_SHOWN) |
60 itemcount = min(len(choices), MAX_ITEM_SHOWN) |
66 width = self.Parent.GetSize()[0] |
61 width = self.Parent.GetSize()[0] |
67 height = max_text_height * itemcount + ITEM_INTERVAL_HEIGHT * (itemcount + 1) |
62 height = max_text_height * itemcount + \ |
|
63 LISTBOX_INTERVAL_HEIGHT * max(0, itemcount - 1) + \ |
|
64 2 * LISTBOX_BORDER_HEIGHT |
68 if max_text_width + 10 > width: |
65 if max_text_width + 10 > width: |
69 height += 15 |
66 height += 15 |
70 size = wx.Size(width, height) |
67 size = wx.Size(width, height) |
|
68 if wx.Platform == '__WXMSW__': |
|
69 size.width -= 2 |
71 self.ListBox.SetSize(size) |
70 self.ListBox.SetSize(size) |
72 self.SetClientSize(size) |
71 self.SetClientSize(size) |
73 |
72 |
74 def MoveSelection(self, direction): |
73 def MoveSelection(self, direction): |
75 selected = self.ListBox.GetSelection() |
74 selected = self.ListBox.GetSelection() |
85 self.ListBox.SetSelection(selected) |
84 self.ListBox.SetSelection(selected) |
86 |
85 |
87 def GetSelection(self): |
86 def GetSelection(self): |
88 return self.ListBox.GetStringSelection() |
87 return self.ListBox.GetStringSelection() |
89 |
88 |
90 def ProcessLeftDown(self, event): |
89 def OnLeftDown(self, event): |
91 selected = self.ListBox.HitTest(wx.Point(event.m_x, event.m_y)) |
90 selected = self.ListBox.HitTest(wx.Point(event.m_x, event.m_y)) |
|
91 parent_size = self.Parent.GetSize() |
|
92 parent_rect = wx.Rect(0, -parent_size[1], parent_size[0], parent_size[1]) |
92 if selected != wx.NOT_FOUND: |
93 if selected != wx.NOT_FOUND: |
93 wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) |
94 wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) |
94 return False |
95 elif parent_rect.InsideXY(event.m_x, event.m_y): |
95 |
96 result, x, y = self.Parent.HitTest(wx.Point(event.m_x, event.m_y + parent_size[1])) |
96 def OnListBoxClick(self, event): |
97 if result != wx.TE_HT_UNKNOWN: |
97 selected = event.GetSelection() |
98 self.Parent.SetInsertionPoint(self.Parent.XYToPosition(x, y)) |
98 if selected != wx.NOT_FOUND: |
99 else: |
99 wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) |
100 wx.CallAfter(self.Parent.DismissListBox) |
100 event.Skip() |
101 event.Skip() |
101 |
102 |
102 def OnKeyDown(self, event): |
103 def OnMotion(self, event): |
103 self.Parent.ProcessEvent(event) |
104 self.ListBox.SetSelection( |
104 |
105 self.ListBox.HitTest(wx.Point(event.m_x, event.m_y))) |
105 def OnDismiss(self): |
106 event.Skip() |
106 self.Parent.listbox = None |
|
107 wx.CallAfter(self.Parent.DismissListBox) |
|
108 |
107 |
109 class TextCtrlAutoComplete(wx.TextCtrl): |
108 class TextCtrlAutoComplete(wx.TextCtrl): |
110 |
109 |
111 def __init__ (self, parent, appframe, choices=None, dropDownClick=True, |
110 def __init__ (self, parent, choices=None, dropDownClick=True, |
112 element_path=None, **therest): |
111 element_path=None, **therest): |
113 """ |
112 """ |
114 Constructor works just like wx.TextCtrl except you can pass in a |
113 Constructor works just like wx.TextCtrl except you can pass in a |
115 list of choices. You can also change the choice list at any time |
114 list of choices. You can also change the choice list at any time |
116 by calling setChoices. |
115 by calling setChoices. |
117 """ |
116 """ |
118 |
117 |
119 therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) |
118 therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) |
120 |
119 |
121 wx.TextCtrl.__init__(self, parent, **therest) |
120 wx.TextCtrl.__init__(self, parent, **therest) |
122 self.AppFrame = appframe |
|
123 |
121 |
124 #Some variables |
122 #Some variables |
125 self._dropDownClick = dropDownClick |
123 self._dropDownClick = dropDownClick |
126 self._lastinsertionpoint = None |
124 self._lastinsertionpoint = None |
|
125 self._hasfocus = False |
127 |
126 |
128 self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) |
127 self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) |
129 self.element_path = element_path |
128 self.element_path = element_path |
130 |
129 |
131 self.listbox = None |
130 self.listbox = None |
169 if keycode == wx.WXK_DOWN: |
165 if keycode == wx.WXK_DOWN: |
170 self.listbox.MoveSelection(1) |
166 self.listbox.MoveSelection(1) |
171 else: |
167 else: |
172 self.listbox.MoveSelection(-1) |
168 self.listbox.MoveSelection(-1) |
173 elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and self.listbox is not None: |
169 elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and self.listbox is not None: |
174 self.SetValueFromSelected(self.listbox.GetSelection()) |
170 selected = self.listbox.GetSelection() |
|
171 if selected != "": |
|
172 self.SetValueFromSelected(selected) |
|
173 else: |
|
174 event.Skip() |
175 elif event.GetKeyCode() == wx.WXK_ESCAPE: |
175 elif event.GetKeyCode() == wx.WXK_ESCAPE: |
176 self.DismissListBox() |
176 self.DismissListBox() |
177 else: |
177 else: |
178 event.Skip() |
178 event.Skip() |
179 |
179 |
180 def OnClickToggleDown(self, event): |
180 def OnClickToggleDown(self, event): |
181 self._lastinsertionpoint = self.GetInsertionPoint() |
181 self._lastinsertionpoint = self.GetInsertionPoint() |
182 event.Skip() |
182 event.Skip() |
183 |
183 |
184 def OnClickToggleUp(self, event): |
184 def OnClickToggleUp(self, event): |
185 if self.GetInsertionPoint() == self._lastinsertionpoint: |
185 if not self._hasfocus: |
|
186 self._hasfocus = True |
|
187 elif self.GetInsertionPoint() == self._lastinsertionpoint: |
186 wx.CallAfter(self.PopupListBox) |
188 wx.CallAfter(self.PopupListBox) |
187 self._lastinsertionpoint = None |
189 self._lastinsertionpoint = None |
188 event.Skip() |
190 event.Skip() |
189 |
191 |
190 def OnControlChanged(self, event): |
192 def OnControlChanged(self, event): |
195 listentries = (listentries + [res])[-MAX_ITEM_COUNT:] |
197 listentries = (listentries + [res])[-MAX_ITEM_COUNT:] |
196 config.Write(self.element_path, cPickle.dumps(listentries)) |
198 config.Write(self.element_path, cPickle.dumps(listentries)) |
197 config.Flush() |
199 config.Flush() |
198 self.SetChoices(listentries) |
200 self.SetChoices(listentries) |
199 self.DismissListBox() |
201 self.DismissListBox() |
|
202 self._hasfocus = False |
200 event.Skip() |
203 event.Skip() |
201 |
204 |
202 def SetChoices(self, choices): |
205 def SetChoices(self, choices): |
203 self._choices = choices |
206 self._choices = choices |
204 self.RefreshListBoxChoices() |
207 self.RefreshListBoxChoices() |
205 |
208 |
206 def GetChoices(self): |
209 def GetChoices(self): |
207 return self._choices |
210 return self._choices |
208 |
211 |
209 def SetValueFromSelected(self, selected): |
212 def SetValueFromSelected(self, selected): |
210 """ |
213 """ |
211 Sets the wx.TextCtrl value from the selected wx.ListCtrl item. |
214 Sets the wx.TextCtrl value from the selected wx.ListCtrl item. |
212 Will do nothing if no item is selected in the wx.ListCtrl. |
215 Will do nothing if no item is selected in the wx.ListCtrl. |
213 """ |
216 """ |
214 if selected != "": |
217 if selected != "": |
215 self.SetValue(selected) |
218 self.SetValue(selected) |
216 self.DismissListBox() |
219 self.DismissListBox() |
217 |
220 |
218 def RefreshListBoxChoices(self): |
221 def RefreshListBoxChoices(self): |
219 if self.listbox is not None: |
222 if self.listbox is not None: |
220 text = self.GetValue() |
223 text = self.GetValue() |
221 choices = [choice for choice in self._choices if choice.startswith(text)] |
224 choices = [choice for choice in self._choices if choice.startswith(text)] |
227 |
230 |
228 # Show the popup right below or above the button |
231 # Show the popup right below or above the button |
229 # depending on available screen space... |
232 # depending on available screen space... |
230 pos = self.ClientToScreen((0, 0)) |
233 pos = self.ClientToScreen((0, 0)) |
231 sz = self.GetSize() |
234 sz = self.GetSize() |
|
235 if wx.Platform == '__WXMSW__': |
|
236 pos.x -= 2 |
|
237 pos.y -= 2 |
232 self.listbox.Position(pos, (0, sz[1])) |
238 self.listbox.Position(pos, (0, sz[1])) |
233 |
239 |
234 self.RefreshListBoxChoices() |
240 self.RefreshListBoxChoices() |
235 |
241 |
236 if wx.Platform == '__WXMSW__': |
242 self.listbox.Show() |
237 self.listbox.Popup() |
|
238 else: |
|
239 self.listbox.Show() |
|
240 self.AppFrame.EnableScrolling(False) |
|
241 |
243 |
242 def DismissListBox(self): |
244 def DismissListBox(self): |
243 if self.listbox is not None: |
245 if self.listbox is not None: |
244 if wx.Platform == '__WXMSW__': |
246 if self.listbox.ListBox.HasCapture(): |
245 self.listbox.Dismiss() |
247 self.listbox.ListBox.ReleaseMouse() |
246 else: |
248 self.listbox.Destroy() |
247 self.listbox.Destroy() |
|
248 self.listbox = None |
249 self.listbox = None |
249 self.AppFrame.EnableScrolling(True) |
|
250 |
|