~ubuntu-branches/ubuntu/lucid/wxwidgets2.8/lucid

« back to all changes in this revision

Viewing changes to wxPython/wx/tools/Editra/src/ed_cmdbar.py

  • Committer: Bazaar Package Importer
  • Author(s): Devid Antonio Filoni
  • Date: 2009-08-11 14:33:24 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20090811143324-gw6dj432dbe8lw3w
Tags: 2.8.10.1-0ubuntu1
* New upstream release (LP: #387424).
* Update debian/patches/tcl_tk.tcl.dpatch patch.
* Update debian/patches/editra_pixmaps.dpatch patch.
* Remove debian/patches/colours.dpatch patch, merged upstream.
* Remove useless debian/patches/WX_CONFIG.dpatch file.
* debian/control.in: modify *-dbg Section field to debug.
* debian/control.in: unfold the Build-Depends, Suggests, Replaces and
  Depends fields.
* Override ancient-libtool and outdated-autotools-helper-file lintian
  warnings and ancient-autotools-helper-file lintian errors.
* Fix debian-watch-file-should-use-sf-redirector lintian warning.
* Fix binary-control-field-duplicates-source lintian infos.
* Fix duplicate-short-description lintian info.
* Move debian/*.desktop files to debian/desktop_files directory.
* Move debian/*.1 files to debian/manpages directory.
* Remove useless debian/README.HowToBuild.txt file.
* Remove debian/build_all file, it isn't used.
* Add debian/patches/CVE-2009-2369.dpatch patch taken from Debian unstable,
  fix an integer overflow in the wxImage::Create function (CVE-2009-2369).
* Add debian/patches/svn61009_fix_ftbfs.dpatch patch taken from upstream
  SVN rev 61009, fix FTBFS caused by new versions of glib.
* debian/rules: fix FTBFS caused by some changes introduced in
  2.8.9.1-0ubuntu5.
* Add debian/patches/{wxversion_path, wx_pth_dbg}.dpatch patches with
  changes introduced directly to source in 2.8.9.1-0ubuntu5.
  PLEASE REMEMBER, THERE IS NOTHING WRONG WITH USING DPATCH!
* Do not provide python-wxaddons package, upstream doesn't ship wxaddons
  anymore.
* debian/control.in: add Homepage field.
* Fix description-possibly-contains-homepage lintian infos.
* Bump Standards-Version to 3.8.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
"""
19
19
 
20
20
__author__ = "Cody Precord <cprecord@editra.org>"
21
 
__svnid__ = "$Id: ed_cmdbar.py 55379 2008-08-30 17:28:46Z CJP $"
22
 
__revision__ = "$Revision: 55379 $"
 
21
__svnid__ = "$Id: ed_cmdbar.py 60508 2009-05-03 21:54:15Z CJP $"
 
22
__revision__ = "$Revision: 60508 $"
23
23
 
24
24
#--------------------------------------------------------------------------#
25
25
# Imports
26
26
import os
27
27
import sys
28
 
import cStringIO, zlib
29
28
import glob
30
29
import re
31
30
import wx
36
35
import ed_search
37
36
import ed_event
38
37
import ed_msg
39
 
import eclib.platebtn as platebtn
40
 
import eclib.finddlg as finddlg
 
38
import ebmlib
 
39
import eclib
41
40
 
42
41
_ = wx.GetTranslation
43
42
#--------------------------------------------------------------------------#
60
59
#-----------------------------------------------------------------------------#
61
60
# Globals
62
61
ID_CLOSE_BUTTON = wx.NewId()
63
 
ID_SEARCH_CTRL = wx.NewId()
64
62
ID_SEARCH_NEXT = wx.NewId()
65
63
ID_SEARCH_PRE = wx.NewId()
66
64
ID_MATCH_CASE = wx.NewId()
67
 
ID_LINE_CTRL = wx.NewId()
68
 
ID_CMD_CTRL = wx.NewId()
 
65
ID_REGEX = wx.NewId()
69
66
 
70
67
#-----------------------------------------------------------------------------#
71
68
 
72
 
class CommandBar(wx.Panel):
73
 
    """The command bar is a slim panel that is used to hold various small
74
 
    controls for searching jumping to line, ect...
75
 
    @todo: make a plugin interface and a management system for showing and
76
 
           hiding the various conrols
77
 
 
78
 
    """
79
 
    def __init__(self, parent, id_, size=(-1, 24), style=wx.TAB_TRAVERSAL):
80
 
        """Initializes the bar and its default widgets
81
 
        @postcondition: commandbar is created
82
 
 
83
 
        """
84
 
        wx.Panel.__init__(self, parent, id_, size=size, style=style)
 
69
class CommandBarBase(eclib.ControlBar):
 
70
    """Base class for control bars"""
 
71
    def __init__(self, parent):
 
72
        eclib.ControlBar.__init__(self, parent,
 
73
                                    style=eclib.CTRLBAR_STYLE_GRADIENT)
 
74
 
 
75
        if wx.Platform == '__WXGTK__':
 
76
            self.SetWindowStyle(eclib.CTRLBAR_STYLE_DEFAULT)
 
77
 
 
78
        self.SetVMargin(2, 2)
85
79
 
86
80
        # Attributes
87
81
        self._parent = parent
88
 
        self._installed = False
89
 
        self._sizers = dict(psizer=parent.GetSizer(),
90
 
                            h_sizer=wx.BoxSizer(wx.HORIZONTAL),
91
 
                            goto=wx.BoxSizer(),
92
 
                            search=wx.BoxSizer(),
93
 
                            cmd=wx.BoxSizer())
94
 
 
95
 
        # Install Controls
96
 
        v_sizer = wx.BoxSizer(wx.VERTICAL)
97
 
        self._sizers['h_sizer'].Add((8, 8))
98
 
        bstyle = wx.BU_EXACTFIT
99
 
        if wx.Platform == '__WXGTK__':
100
 
            bstyle = wx.NO_BORDER
101
 
 
102
 
        self.close_b = wx.BitmapButton(self, ID_CLOSE_BUTTON, \
103
 
                                       XButton.GetBitmap(), style=bstyle)
104
 
        self._sizers['h_sizer'].Add(self.close_b, 0, wx.ALIGN_CENTER_VERTICAL)
105
 
        self._sizers['h_sizer'].Add((12, 12))
106
 
        v_sizer.Add((2, 2))
107
 
        self._sizers['h_sizer'].Add(v_sizer)
108
 
        self.SetSizer(self._sizers['h_sizer'])
109
 
        self.SetAutoLayout(True)
110
 
 
111
 
        # Bind Events
112
 
        ed_msg.Subscribe(self.OnThemeChange, ed_msg.EDMSG_THEME_CHANGED)
113
 
 
114
 
        # Don't paint the gradient on gtk due to transparency
115
 
        # issues with some controls
116
 
        if wx.Platform != '__WXGTK__':
117
 
            self.Bind(wx.EVT_PAINT, self.OnPaint)
118
 
        self.Bind(wx.EVT_BUTTON, self.OnButton)
119
 
        self.Bind(wx.EVT_CHECKBOX, self.OnCheck)
 
82
        self.ctrl = None
 
83
        self.close_b = eclib.PlateButton(self, ID_CLOSE_BUTTON,
 
84
                                         bmp=XButton.GetBitmap(),
 
85
                                         style=eclib.PB_STYLE_NOBG)
 
86
 
 
87
        # Setup
 
88
        self.AddControl(self.close_b, wx.ALIGN_LEFT)
 
89
 
 
90
        # Event Handlers
 
91
        self.Bind(wx.EVT_BUTTON, self.OnClose, self.close_b)
 
92
 
 
93
    def OnClose(self, evt):
 
94
        """Handles events from the buttons on the bar
 
95
        @param evt: Event that called this handler
 
96
 
 
97
        """
 
98
        e_id = evt.GetId()
 
99
        if e_id == ID_CLOSE_BUTTON:
 
100
            self.Hide()
 
101
        else:
 
102
            evt.Skip()
120
103
 
121
104
    def Hide(self):
122
105
        """Hides the control and notifies the parent
123
106
        @postcondition: commandbar is hidden
 
107
        @todo: dont reference nb directly here
124
108
 
125
109
        """
126
 
        wx.Panel.Hide(self)
127
 
        if self._sizers['psizer'] != None:
128
 
            self._sizers['psizer'].Layout()
 
110
        super(CommandBarBase, self).Hide()
129
111
        self._parent.SendSizeEvent()
130
112
        self._parent.nb.GetCurrentCtrl().SetFocus()
131
113
        return True
132
114
 
133
 
    def InstallCtrl(self, id_):
134
 
        """Installs a control into the bar by ID
135
 
        @postcondition: control is installed
136
 
        @return: requested control or None
137
 
 
138
 
        """
139
 
        if id_ == ID_SEARCH_CTRL:
140
 
            ctrl = self.InstallSearchCtrl()
141
 
        elif id_ == ID_LINE_CTRL:
142
 
            ctrl = self.InstallLineCtrl()
143
 
        elif id_ == ID_CMD_CTRL:
144
 
            ctrl = self.InstallCommandCtrl()
145
 
        else:
146
 
            ctrl = None
147
 
        return ctrl
148
 
 
149
 
    def InstallLineCtrl(self):
150
 
        """Installs the go to line control into the panel.
151
 
        @postcondition: GotoLine control is installed in bar.
152
 
 
153
 
        """
154
 
        h_sizer = wx.BoxSizer(wx.HORIZONTAL)
155
 
        v_sizer = wx.BoxSizer(wx.VERTICAL)
156
 
        v_sizer.Add((5, 5))
157
 
        linectrl = LineCtrl(self, ID_LINE_CTRL, self._parent.nb.GetCurrentCtrl,
158
 
                            size=(100, 20))
159
 
        v_sizer.Add(linectrl, 0, wx.ALIGN_CENTER_VERTICAL)
160
 
        v_sizer.Add((4, 4))
161
 
        go_lbl = wx.StaticText(self, label=_("Goto Line") + ": ")
162
 
        if wx.Platform == '__WXMAC__':
163
 
            go_lbl.SetFont(wx.SMALL_FONT)
164
 
        h_sizer.AddMany([(go_lbl, 0, wx.ALIGN_CENTER_VERTICAL),
165
 
                         ((5, 5)), (v_sizer)])
166
 
        h_sizer.Layout()
167
 
        self._sizers['goto'] = h_sizer
168
 
        self._sizers['h_sizer'].Add(h_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)
169
 
        self._sizers['h_sizer'].Layout()
170
 
        return linectrl
171
 
 
172
 
    def InstallCommandCtrl(self):
173
 
        """Install the sizer containing the command executer control
174
 
        into the bar.
175
 
        @return: the command control instance
176
 
 
177
 
        """
178
 
        h_sizer = wx.BoxSizer(wx.HORIZONTAL)
179
 
        v_sizer = wx.BoxSizer(wx.VERTICAL)
180
 
        v_sizer.Add((5, 5))
181
 
        cmdctrl = CommandExecuter(self, ID_CMD_CTRL, size=(150, 20))
182
 
        v_sizer.Add(cmdctrl, 0, wx.ALIGN_CENTER_VERTICAL)
183
 
        v_sizer.Add((4, 4))
184
 
        cmd_lbl = wx.StaticText(self, label=_("Command") + ": ")
185
 
        if wx.Platform == '__WXMAC__':
186
 
            cmd_lbl.SetFont(wx.SMALL_FONT)
187
 
        h_sizer.AddMany([(cmd_lbl, 0, wx.ALIGN_CENTER_VERTICAL),
188
 
                         ((5, 5)), (v_sizer)])
189
 
        h_sizer.Layout()
190
 
        self._sizers['cmd'] = h_sizer
191
 
        self._sizers['h_sizer'].Add(h_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)
192
 
        self._sizers['h_sizer'].Layout()
193
 
        return cmdctrl
194
 
 
195
 
    def InstallSearchCtrl(self):
196
 
        """Installs the search context controls into the panel.
197
 
        Other controls should be removed from the panel before calling
198
 
        this method.
199
 
        @postcondition: search control is installed in bar
200
 
 
201
 
        """
202
 
        h_sizer = wx.BoxSizer(wx.HORIZONTAL)
203
 
        v_sizer = wx.BoxSizer(wx.VERTICAL)
204
 
        t_sizer = wx.BoxSizer(wx.VERTICAL)
205
 
 
206
 
        spacer = (6, 6)
207
 
        if wx.Platform == '__WXGTK__':
208
 
            spacer = (4, 4)
209
 
        v_sizer.Add(spacer)
210
 
        search = ed_search.EdSearchCtrl(self, ID_SEARCH_CTRL,
211
 
                                        menulen=5, size=(180, 20))
212
 
        v_sizer.Add(search, 0, wx.ALIGN_CENTER_VERTICAL)
213
 
        v_sizer.Add((4, 4))
 
115
    def SetControl(self, ctrl):
 
116
        """Set the main control of this command bar
 
117
        @param ctrl: window
 
118
 
 
119
        """
 
120
        self.ctrl = ctrl
 
121
 
 
122
    def SetFocus(self):
 
123
        """Set the focus to the bar and its main control"""
 
124
        super(CommandBarBase, self).SetFocus()
 
125
        if self.ctrl is not None:
 
126
            self.ctrl.SetFocus()
 
127
 
 
128
#-----------------------------------------------------------------------------#
 
129
 
 
130
class SearchBar(CommandBarBase):
 
131
    """Commandbar for searching text in the current buffer."""
 
132
    def __init__(self, parent):
 
133
        CommandBarBase.__init__(self, parent)
 
134
 
 
135
        # Attributes
 
136
        self.SetControl(ed_search.EdSearchCtrl(self, wx.ID_ANY,
 
137
                                               menulen=5, size=(180, -1)))
 
138
        self._sctrl = self.ctrl.GetSearchController()
 
139
 
 
140
        # Setup
214
141
        f_lbl = wx.StaticText(self, label=_("Find") + u": ")
215
 
        ctrl_sizer = wx.BoxSizer(wx.HORIZONTAL)
216
142
        t_bmp = wx.ArtProvider.GetBitmap(str(ed_glob.ID_DOWN), wx.ART_MENU)
217
 
        next_btn = platebtn.PlateButton(self, ID_SEARCH_NEXT, _("Next"),
218
 
                                        t_bmp, style=platebtn.PB_STYLE_NOBG)
 
143
        next_btn = eclib.PlateButton(self, ID_SEARCH_NEXT, _("Next"),
 
144
                                     t_bmp, style=eclib.PB_STYLE_NOBG)
 
145
        self.AddControl(f_lbl, wx.ALIGN_LEFT)
 
146
        self.AddControl(self.ctrl, wx.ALIGN_LEFT)
 
147
        self.AddControl(next_btn, wx.ALIGN_LEFT)
219
148
 
220
149
        t_bmp = wx.ArtProvider.GetBitmap(str(ed_glob.ID_UP), wx.ART_MENU)
221
 
        pre_btn = platebtn.PlateButton(self, ID_SEARCH_PRE, _("Previous"),
222
 
                                       t_bmp, style=platebtn.PB_STYLE_NOBG)
 
150
        pre_btn = eclib.PlateButton(self, ID_SEARCH_PRE, _("Previous"),
 
151
                                    t_bmp, style=eclib.PB_STYLE_NOBG)
 
152
        self.AddControl(pre_btn, wx.ALIGN_LEFT)
223
153
 
224
154
        match_case = wx.CheckBox(self, ID_MATCH_CASE, _("Match Case"))
225
 
        match_case.SetValue(search.IsMatchCase())
 
155
        match_case.SetValue(self.ctrl.IsMatchCase())
 
156
        self.AddControl(match_case, wx.ALIGN_LEFT)
 
157
 
 
158
        regex_cb = wx.CheckBox(self, ID_REGEX, _("Regular Expression"))
 
159
        regex_cb.SetValue(self.ctrl.IsRegEx())
 
160
        self.AddControl(regex_cb, wx.ALIGN_LEFT)
 
161
 
 
162
        # HACK: workaround bug in mac control that resets size to
 
163
        #       that of the default variant after any text has been
 
164
        #       typed in it. Note it reports the best size as the default
 
165
        #       variant and causes layout issues. wxBUG
226
166
        if wx.Platform == '__WXMAC__':
227
 
            t_sizer.Add((5, 5))
228
 
            for win in [f_lbl, match_case, next_btn, pre_btn]:
229
 
                win.SetFont(wx.SMALL_FONT)
230
 
 
231
 
        ctrl_sizer.AddMany([(10, 0), (next_btn, 0, wx.ALIGN_CENTER_VERTICAL),
232
 
                            ((5, 0)), (pre_btn, 0, wx.ALIGN_CENTER_VERTICAL),
233
 
                            ((8, 0)),
234
 
                            (match_case, 0, wx.ALIGN_CENTER_VERTICAL)])
235
 
 
236
 
        t_sizer.Add(ctrl_sizer, 0, wx.ALIGN_CENTER_VERTICAL)
237
 
 
238
 
        h_sizer.AddMany([(f_lbl, 0, wx.ALIGN_CENTER_VERTICAL),
239
 
                         ((5, 5)), (v_sizer, 0, wx.ALIGN_CENTER_VERTICAL),
240
 
                         (t_sizer, 0, wx.ALIGN_CENTER_VERTICAL)])
241
 
        self._sizers['search'] = h_sizer
242
 
        self._sizers['h_sizer'].Add(h_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)
243
 
        self._sizers['h_sizer'].Layout()
244
 
        return search
 
167
            self.ctrl.SetSizeHints(180, 16, 180, 16)
 
168
 
 
169
        # Event Handlers
 
170
        self.Bind(wx.EVT_BUTTON, self.OnButton)
 
171
        self.Bind(wx.EVT_CHECKBOX, self.OnCheck)
 
172
        ed_msg.Subscribe(self.OnThemeChange, ed_msg.EDMSG_THEME_CHANGED)
 
173
        self._sctrl.RegisterClient(self)
 
174
 
 
175
    def __del__(self):
 
176
        ed_msg.Unsubscribe(self.OnThemeChange)
 
177
        self._sctrl.RemoveClient(self)
 
178
 
 
179
    def OnButton(self, evt):
 
180
        """Handle button clicks for the next/previous buttons
 
181
        @param evt: wx.CommandEvent
 
182
 
 
183
        """
 
184
        e_id = evt.GetId()
 
185
        if e_id in [ID_SEARCH_NEXT, ID_SEARCH_PRE]:
 
186
            self.ctrl.DoSearch(e_id == ID_SEARCH_NEXT)
 
187
        else:
 
188
            evt.Skip()
245
189
 
246
190
    def OnCheck(self, evt):
247
 
        """Check box event handler
248
 
        @param evt: Event that called this handler
249
 
        @type evt: wx.EVT_CHECKBOX
 
191
        """Set search options for match case, regex, ect...
 
192
        @param evt: wx.CommandEvent
250
193
 
251
194
        """
252
195
        e_id = evt.GetId()
253
 
        if e_id == ID_MATCH_CASE:
 
196
        if e_id in (ID_MATCH_CASE, ID_REGEX):
254
197
            ctrl = self.FindWindowById(e_id)
255
198
            if ctrl != None:
256
 
                search = self.FindWindowById(ID_SEARCH_CTRL)
257
 
                if search != None:
 
199
                if e_id == ID_MATCH_CASE:
 
200
                    flag = eclib.AFR_MATCHCASE
 
201
                else:
 
202
                    flag = eclib.AFR_REGEX
 
203
 
 
204
                if self.ctrl != None:
258
205
                    if ctrl.GetValue():
259
 
                        search.SetSearchFlag(finddlg.AFR_MATCHCASE)
 
206
                        self.ctrl.SetSearchFlag(flag)
260
207
                    else:
261
 
                        search.ClearSearchFlag(finddlg.AFR_MATCHCASE)
262
 
        else:
263
 
            evt.Skip()
264
 
 
265
 
    def OnButton(self, evt):
266
 
        """Handles events from the buttons on the bar
267
 
        @param evt: Event that called this handler
268
 
 
269
 
        """
270
 
        e_id = evt.GetId()
271
 
        if e_id == ID_CLOSE_BUTTON:
272
 
            self.Hide()
273
 
        elif e_id in [ID_SEARCH_NEXT, ID_SEARCH_PRE]:
274
 
            search = self.FindWindowById(ID_SEARCH_CTRL)
275
 
            if search != None:
276
 
                search.DoSearch(e_id == ID_SEARCH_NEXT)
277
 
        else:
278
 
            evt.Skip()
279
 
 
280
 
    def OnPaint(self, evt):
281
 
        """Paints the background of the bar with a nice gradient.
282
 
        @param evt: Event that called this handler
283
 
        @type evt: wx.PaintEvent
284
 
 
285
 
        """
286
 
        dc = wx.PaintDC(self)
287
 
        gc = wx.GraphicsContext.Create(dc)
288
 
        col1 = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)
289
 
        col2 = util.AdjustColour(col1, 40)
290
 
        col1 = util.AdjustColour(col1, -15)
291
 
        grad = gc.CreateLinearGradientBrush(0, 1, 0, 29, col2, col1)
292
 
        rect = self.GetClientRect()
293
 
 
294
 
        pen_col = tuple([min(190, x) for x in util.AdjustColour(col1, 20)])
295
 
        gc.SetPen(gc.CreatePen(wx.Pen(pen_col, 1)))
296
 
        gc.SetBrush(grad)
297
 
        gc.DrawRectangle(0, 1, rect.width - 0.5, rect.height - 0.5)
298
 
 
299
 
        evt.Skip()
 
208
                        self.ctrl.ClearSearchFlag(flag)
 
209
        else:
 
210
            evt.Skip()
 
211
 
 
212
    def NotifyOptionChanged(self, evt):
 
213
        """Callback for L{ed_search.SearchController} to notify of update
 
214
        to the find options.
 
215
        @param evt: eclib.finddlg.FindEvent
 
216
 
 
217
        """
 
218
        self.FindWindowById(ID_MATCH_CASE).SetValue(evt.IsMatchCase())
 
219
        self.FindWindowById(ID_REGEX).SetValue(evt.IsRegEx())
300
220
 
301
221
    def OnThemeChange(self, msg):
302
222
        """Update icons when the theme has changed
303
223
        @param msg: Message Object
304
224
 
305
225
        """
306
 
        self.UpdateIcons()
307
 
 
308
 
    def Show(self, id_=0):
309
 
        """Shows the control and installs it in the parents
310
 
        sizer if not installed already.
311
 
        @param id_: Id of control to show in bar
312
 
 
313
 
        """
314
 
        # Install self in parent
315
 
        if not self._installed and self._sizers['psizer'] != None:
316
 
            self._installed = True
317
 
            self._sizers['psizer'].Add(self, 0, wx.EXPAND)
318
 
            self._sizers['psizer'].Layout()
319
 
            self._parent.SendSizeEvent()
320
 
        wx.Panel.Show(self)
321
 
 
322
 
        # HACK YUCK, come back and try again when my brain is working
323
 
        # Show specified control
324
 
        if id_:
325
 
            ctrl = self.FindWindowById(id_)
326
 
            if ctrl is None:
327
 
                ctrl = self.InstallCtrl(id_)
328
 
 
329
 
            # First Hide everything
330
 
            for kid in (list(self._sizers['search'].GetChildren()) +
331
 
                        list(self._sizers['goto'].GetChildren()) +
332
 
                        list(self._sizers['cmd'].GetChildren())):
333
 
                kid.Show(False)
334
 
 
335
 
            if id_ == ID_SEARCH_CTRL:
336
 
                for kid in self._sizers['search'].GetChildren():
337
 
                    kid.Show(True)
338
 
                self.FindWindowById(ID_SEARCH_CTRL).AutoSetQuery()
339
 
                self._sizers['search'].Layout()
340
 
            elif id_ == ID_LINE_CTRL:
341
 
                for kid in self._sizers['goto'].GetChildren():
342
 
                    kid.Show(True)
343
 
            elif id_ == ID_CMD_CTRL:
344
 
                for kid in self._sizers['cmd'].GetChildren():
345
 
                    kid.Show(True)
346
 
 
347
 
            self.GetSizer().Layout()
348
 
            if ctrl != None:
349
 
                ctrl.SetFocus()
350
 
                ctrl.SelectAll()
351
 
 
352
 
    def UpdateIcons(self):
353
 
        """Refresh icons to current theme settings
354
 
        @postcondition: all icons are updated
355
 
 
356
 
        """
357
226
        next = self.FindWindowById(ID_SEARCH_NEXT)
358
227
        if next:
359
228
            t_bmp = wx.ArtProvider.GetBitmap(str(ed_glob.ID_DOWN), wx.ART_MENU)
372
241
 
373
242
#-----------------------------------------------------------------------------#
374
243
 
375
 
class CommandExecuter(wx.SearchCtrl):
 
244
class CommandEntryBar(CommandBarBase):
 
245
    """Commandbar for editor command entry and execution."""
 
246
    def __init__(self, parent):
 
247
        CommandBarBase.__init__(self, parent)
 
248
 
 
249
        # Attributes
 
250
        self.SetControl(CommandExecuter(self, wx.ID_ANY, size=(150, -1)))
 
251
 
 
252
        # Setup
 
253
        cmd_lbl = wx.StaticText(self, label=_("Command") + ": ")
 
254
        self.AddControl(cmd_lbl, wx.ALIGN_LEFT)
 
255
        self.AddControl(self.ctrl, wx.ALIGN_LEFT)
 
256
 
 
257
        # HACK: workaround bug in mac control that resets size to
 
258
        #       that of the default variant after any text has been
 
259
        #       typed in it. Note it reports the best size as the default
 
260
        #       variant and causes layout issues. wxBUG
 
261
        if wx.Platform == '__WXMAC__':
 
262
            self.ctrl.SetSizeHints(150, 16, 150, 16)
 
263
 
 
264
#-----------------------------------------------------------------------------#
 
265
 
 
266
class GotoLineBar(CommandBarBase):
 
267
    """Commandbar for Goto Line function"""
 
268
    def __init__(self, parent):
 
269
        CommandBarBase.__init__(self, parent)
 
270
 
 
271
        # Attributes
 
272
        self.SetControl(LineCtrl(self, wx.ID_ANY,
 
273
                                 self._parent.nb.GetCurrentCtrl,
 
274
                                 size=(100, -1)))
 
275
 
 
276
        # Setup
 
277
        go_lbl = wx.StaticText(self, label=_("Goto Line") + ": ")
 
278
        self.AddControl(go_lbl, wx.ALIGN_LEFT)
 
279
        self.AddControl(self.ctrl, wx.ALIGN_LEFT)
 
280
 
 
281
        # HACK: workaround bug in mac control that resets size to
 
282
        #       that of the default variant after any text has been
 
283
        #       typed in it. Note it reports the best size as the default
 
284
        #       variant and causes layout issues. wxBUG
 
285
        if wx.Platform == '__WXMAC__':
 
286
            self.ctrl.SetSizeHints(100, 16, 100, 16)
 
287
 
 
288
#-----------------------------------------------------------------------------#
 
289
 
 
290
class CommandExecuter(eclib.CommandEntryBase):
376
291
    """Part of the Vi emulation, opens a minibuffer to execute EX commands.
377
292
    @note: based on search ctrl so we get the nice roudned edges on wxmac.
378
293
 
384
299
 
385
300
    def __init__(self, parent, id_, size=wx.DefaultSize):
386
301
        """Initializes the CommandExecuter"""
387
 
        wx.SearchCtrl.__init__(self, parent, id_, size=size,
388
 
                               style=wx.TE_PROCESS_ENTER|wx.WANTS_CHARS)
 
302
        eclib.CommandEntryBase.__init__(self, parent, id_, size=size,
 
303
                                           style=wx.TE_PROCESS_ENTER|wx.WANTS_CHARS)
389
304
 
390
305
        # Attributes
391
306
        self._history = dict(cmds=[''], index=-1, lastval='')
401
316
        else:
402
317
            self._popup = PopupWinList(self)
403
318
 
404
 
        # Hide the search button and text
405
 
        self.ShowSearchButton(False)
406
 
        self.ShowCancelButton(False)
407
 
        self.SetDescriptiveText(wx.EmptyString)
408
 
 
409
 
        # Event Handlers
410
 
        # HACK, needed on Windows to get any key events and for
411
 
        # GTK to get key down events
412
 
        if wx.Platform in ['__WXGTK__', '__WXMSW__']:
413
 
            for child in self.GetChildren():
414
 
                if isinstance(child, wx.TextCtrl):
415
 
                    child.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
416
 
                    child.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
417
 
                    break
418
 
        else:
419
 
            self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
420
 
            self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
421
 
 
422
 
        self.Bind(wx.EVT_TEXT_ENTER, self.OnEnter)
423
319
        self.Bind(ed_event.EVT_NOTIFY, self.OnPopupNotify)
424
320
        ed_msg.Subscribe(self._UpdateCwd, ed_msg.EDMSG_UI_NB_CHANGED)
425
321
        ed_msg.Subscribe(self._UpdateCwd, ed_msg.EDMSG_FILE_SAVED)
527
423
        if os.path.exists(cmd):
528
424
            frame.DoOpen(ed_glob.ID_COMMAND_LINE_OPEN, cmd)
529
425
        else:
530
 
            frame.nb.OpenPage(util.GetPathName(cmd), util.GetFileName(cmd))
 
426
            frame.nb.OpenPage(ebmlib.GetPathName(cmd), ebmlib.GetFileName(cmd))
531
427
 
532
428
    def ExecuteCommand(self, cmd_str):
533
429
        """Interprets and executes a command then hides the control
569
465
            return
570
466
 
571
467
        self.CommandPush(cmd_str)
572
 
        self._histidx = -1
573
468
        self.GetParent().Hide()
574
469
 
575
470
    def GetHistCommand(self, pre=True):
663
558
        @keyword files: Get list of files too
664
559
 
665
560
        """
666
 
        replace = 0
667
 
        if path.startswith("~/") or path.startswith("~\\"):
668
 
            prefix = wx.GetHomeDir()
669
 
            replace = len(prefix) + 1
670
 
            path = os.path.join(prefix, path[2:])
671
 
        elif not path.startswith(os.sep):
672
 
            prefix = self._curdir
673
 
            replace = len(prefix)
674
 
            path = os.path.join(prefix, path)
675
 
        else:
676
 
            pass
677
 
 
678
 
        paths = []
679
 
        for atom in glob.glob(path + "*"):
680
 
            if os.path.isdir(atom) or files:
681
 
                if replace > 0:
682
 
                    atom = atom[replace:]
683
 
                if os.path.isdir(atom) and atom[-1] != os.sep:
684
 
                    atom += os.sep
685
 
                paths.append(atom)
686
 
 
687
 
        return sorted(list(set(paths)))
 
561
        def append_slash(path):
 
562
            """Helper function that appends a slash to the path
 
563
            if it's a directory.
 
564
 
 
565
            """
 
566
            if os.path.isdir(path) and not path.endswith(os.sep):
 
567
                return path + os.sep
 
568
            return path
 
569
 
 
570
        curdir = self._curdir
 
571
        head, tail = os.path.split(path)
 
572
        head = os.path.expanduser(head)
 
573
        head = os.path.expandvars(head)
 
574
        head = os.path.join(curdir, head)
 
575
        if not os.path.isdir(head):
 
576
            return []
 
577
 
 
578
        # We expanded head, so trim the suggestion list of its head
 
579
        # so we can add the tail of the suggestion back to the original head
 
580
        candidates = [os.path.basename(p) for p in os.listdir(head)
 
581
                      if p.startswith(tail)]
 
582
        candidates = [append_slash(os.path.join(os.path.dirname(path), cand))
 
583
                      for cand in candidates]
 
584
        if not files:
 
585
            candidates = [cand for cand in candidates if os.path.isdir(cand)]
 
586
 
 
587
        return sorted(list(set(candidates)))
688
588
 
689
589
    def ListDir(self):
690
 
        """List the next directory from the current cmd path
691
 
 
692
 
        """
 
590
        """List the next directory from the current cmd path"""
693
591
        cmd = self.GetValue()
694
592
        if cmd.startswith('cd '):
695
593
            cstr = 'cd '
702
600
        paths = self.GetPaths(cmd, cstr == 'e ')
703
601
        self._popup.SetChoices(paths)
704
602
        if len(paths):
705
 
            pos = self.GetScreenPosition().Get()
706
 
            extent = self.GetTextExtent(cstr)
707
 
            self._popup.SetPosition((pos[0] + extent[0], pos[1] + extent[1] + 7))
708
 
            self._popup.SetBestSelection(cmd)
 
603
            self._popup.SetupPosition(self)
709
604
            if not self._popup.IsShown():
710
605
                self._popup.Show()
711
 
        else:
712
 
            self._popup.Hide()
 
606
 
713
607
        self.SetInsertionPoint(self.GetLastPosition())
714
608
 
715
609
    def OnEnter(self, evt):
717
611
        @postcondition: ctrl is cleared and command is executed
718
612
 
719
613
        """
720
 
        if self._popup.IsShown():
 
614
        if self._popup.HasSuggestions() and self._popup.HasSelection():
721
615
            psel = self._popup.GetSelection()
722
616
            if self.GetValue().split(' ', 1)[-1].strip() != psel:
723
617
                self._AdjustValue(psel)
 
618
                self._popup.Hide()
724
619
                return
725
620
 
726
621
        cmd = self.GetValue()
737
632
        """
738
633
        e_key = evt.GetKeyCode()
739
634
        cmd = self.GetValue()
 
635
 
740
636
        if e_key == wx.WXK_UP:
741
 
            if self._popup.IsShown():
 
637
            if self._popup.HasSuggestions():
742
638
                self._popup.AdvanceSelection(False)
743
639
            else:
744
640
                self.GetHistCommand(pre=True)
745
641
        elif e_key == wx.WXK_DOWN:
746
 
            if self._popup.IsShown():
 
642
            if self._popup.HasSuggestions():
747
643
                self._popup.AdvanceSelection(True)
748
644
            else:
749
645
                self.GetHistCommand(pre=False)
753
649
        elif e_key == wx.WXK_TAB:
754
650
            # Provide Tab Completion or swallow key
755
651
            if cmd.startswith('cd ') or cmd.startswith('e '):
756
 
                if self._popup.IsShown():
 
652
                if self._popup.HasSuggestions():
757
653
                    self._AdjustValue(self._popup.GetSelection())
758
654
                self.ListDir()
759
655
            else:
760
656
                pass
761
657
        elif e_key == wx.WXK_ESCAPE:
762
 
            self.Clear()
763
 
            self.GetParent().Hide()
 
658
            if self._popup.IsShown():
 
659
                self._popup.Hide()
 
660
            else:
 
661
                self.Clear()
 
662
                self.GetParent().Hide()
764
663
        else:
765
664
            evt.Skip()
766
665
 
770
669
 
771
670
        """
772
671
        val = self.GetValue()
773
 
        if self._popup.IsShown() and \
774
 
           evt.GetKeyCode() not in [wx.WXK_DOWN, wx.WXK_UP]:
 
672
        if self._popup.IsShown():
775
673
            if not len(val):
776
674
                self._popup.Hide()
777
675
            else:
825
723
        @param value: string to set value of control to
826
724
 
827
725
        """
828
 
        wx.SearchCtrl.SetValue(self, value)
 
726
        super(CommandExecuter, self).SetValue(value)
829
727
        self._AdjustSize()
830
728
 
831
729
    def UpdateAutoComp(self):
 
730
        """Update the autocomp list for paths that best match current value"""
832
731
        self.ListDir()
833
 
        val = self.GetValue().split(' ', 1)[-1]
834
 
        self._popup.SetBestSelection(val)
835
732
 
836
733
    def WriteCommand(self, cstr):
837
734
        """Perform a file write related command
844
741
 
845
742
#-----------------------------------------------------------------------------#
846
743
 
847
 
class LineCtrl(wx.SearchCtrl):
 
744
class LineCtrl(eclib.CommandEntryBase):
848
745
    """A custom int control for providing a go To line control
849
746
    for the Command Bar.
850
 
    @note: The control is subclassed from SearchCtrl so that it gets
851
 
           the nice rounded edges on wxMac.
852
747
 
853
748
    """
854
749
    def __init__(self, parent, id_, get_doc, size=wx.DefaultSize):
857
752
                        current document.
858
753
 
859
754
        """
860
 
        wx.SearchCtrl.__init__(self, parent, id_, "", size=size,
861
 
                             style=wx.TE_PROCESS_ENTER,
862
 
                             validator=util.IntValidator(0, 65535))
 
755
        eclib.CommandEntryBase.__init__(self, parent, id_, "", size=size,
 
756
                                        style=wx.TE_PROCESS_ENTER,
 
757
                                        validator=util.IntValidator(0, 65535))
863
758
 
864
759
        # Attributes
865
760
        self._last = 0
866
761
        self.GetDoc = get_doc
867
762
 
868
 
        # Hide the search button and text
869
 
        self.ShowSearchButton(False)
870
 
        self.ShowCancelButton(False)
871
 
        self.SetDescriptiveText(wx.EmptyString)
872
 
 
873
 
        # MSW/GTK HACK
874
 
        if wx.Platform in ['__WXGTK__', '__WXMSW__']:
875
 
            for child in self.GetChildren():
876
 
                if isinstance(child, wx.TextCtrl):
877
 
                    child.SetValidator(util.IntValidator(0, 65535))
878
 
                    child.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
879
 
                    break
880
 
        else:
881
 
            self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
882
 
 
883
 
        # Event management
884
 
        self.Bind(wx.EVT_TEXT_ENTER, self.OnInput)
885
 
 
886
 
    def OnInput(self, evt):
 
763
    def OnEnter(self, evt):
887
764
        """Processes the entered line number
888
765
        @param evt: Event that called this handler
889
766
        @type evt: wx.EVT_TEXT_ENTER
920
797
# TODO: merge the common parts of these two classes into a single base class
921
798
 
922
799
class PopupList(wx.MiniFrame):
 
800
    """Popup window with a listbox in it"""
923
801
    def __init__(self, parent, choices=list(), pos=wx.DefaultPosition):
924
802
 
925
803
        style = wx.FRAME_NO_TASKBAR | wx.FRAME_FLOAT_ON_PARENT
999
877
        return self._list.GetStrings()
1000
878
 
1001
879
    def GetListCtrl(self):
 
880
        """Get the ListBox control of the popupwindow"""
1002
881
        return self._list
1003
882
 
1004
883
    def GetSelection(self):
1008
887
        """
1009
888
        return self._list.GetStringSelection()
1010
889
 
 
890
    def HasSelection(self):
 
891
        """Tells whether anything in the list is selected"""
 
892
        return self._list.GetSelection() != wx.NOT_FOUND
 
893
 
 
894
    def HasSuggestions(self):
 
895
        """Tell whether the list is showing suggestions"""
 
896
        return self.IsShown() and self.ListCount() > 0
 
897
 
 
898
    def ListCount(self):
 
899
        """return the number of elements in the popup list"""
 
900
        return self._list.GetCount()
 
901
 
1011
902
    def OnFocus(self, evt):
1012
903
        """Raise and reset the focus to the parent window whenever
1013
904
        we get focus.
1037
928
        self.__PostEvent()
1038
929
 
1039
930
    def OnSize(self, evt):
 
931
        """Resize the listbox"""
1040
932
        csz = self.GetClientSize()
1041
933
        csz.SetWidth(csz.x + wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X))
1042
934
        self._list.SetSize(csz)
1047
939
        @keyword show: Should the window be shown or not
1048
940
 
1049
941
        """
1050
 
        res = wx.Frame.Show(self, show)
 
942
        res = super(PopupList, self).Show(show)
1051
943
 
1052
944
        if res and show:
1053
945
            self.ActivateParent()
1062
954
        @param choices: list of strings
1063
955
 
1064
956
        """
 
957
        selection = self._list.GetSelection()
1065
958
        self._list.SetItems(choices)
 
959
        count = self._list.GetCount()
 
960
        if selection == wx.NOT_FOUND or selection >= count:
 
961
            selection = 0
 
962
        if count > 0:
 
963
            self._list.SetSelection(selection)
1066
964
 
1067
965
    def SetSelection(self, index):
1068
966
        """Set the selection in the list by index
1078
976
        """
1079
977
        self._list.SetStringSelection(text)
1080
978
 
1081
 
    def SetBestSelection(self, prefix):
1082
 
        """Set the selection to the one that bests matches the
1083
 
        given string.
1084
 
        @param prefix: prefix to set selection of
1085
 
        @note: searches for a match recursively, if no partial match is found
1086
 
               then the first item in the list is selected.
 
979
    def SetupPosition(self, cmd_ex):
 
980
        """Sets size and position of widget
 
981
        @param cmd_ex: CommandExecuter window
1087
982
 
1088
983
        """
1089
 
        if not len(prefix):
1090
 
            if len(self._list.GetStrings()):
1091
 
                self._list.SetSelection(0)
1092
 
                self.ActivateParent()
1093
 
        else:
1094
 
            matches = [item for item in self._list.GetItems()
1095
 
                       if item.startswith(prefix) ]
1096
 
            if len(matches):
1097
 
                self._list.SetStringSelection(sorted(matches)[0])
1098
 
                self.ActivateParent()
1099
 
            else:
1100
 
                self.SetBestSelection(prefix[:-1])
 
984
        cmd = cmd_ex.GetValue()
 
985
        cmd = cmd.split(u' ', 1)[0]
 
986
        xpos = cmd_ex.GetTextExtent(cmd + u' ')[0]
 
987
        pos = cmd_ex.GetScreenPosition().Get()
 
988
        csize = cmd_ex.GetSize()
 
989
        self.SetPosition((pos[0] + xpos, pos[1] + csize[1]))
 
990
        self.ActivateParent()
1101
991
 
 
992
#----------------------------------------------------------------------------#
1102
993
 
1103
994
class PopupWinList(wx.PopupWindow):
1104
995
    """Popuplist for Windows/GTK"""
1109
1000
        # Attributes
1110
1001
        self._list = wx.ListBox(self, choices=choices, pos=(0, 0),
1111
1002
                                style=wx.LC_REPORT | wx.LC_SINGLE_SEL |
1112
 
                                      wx.LC_NO_HEADER | wx.NO_BORDER)
 
1003
                                      wx.LC_NO_HEADER)
1113
1004
 
1114
1005
        # Layout
1115
1006
        sizer = wx.BoxSizer(wx.HORIZONTAL)
1127
1018
        @keyword down: move selection down or up
1128
1019
 
1129
1020
        """
 
1021
        item_count = self._list.GetCount()
 
1022
        if item_count == 0:
 
1023
            return
1130
1024
        csel = self._list.GetSelection()
1131
 
        if csel != wx.NOT_FOUND:
 
1025
        if csel == wx.NOT_FOUND:
 
1026
            if down:
 
1027
                csel = 0
 
1028
            else:
 
1029
                csel = -1
 
1030
        else:
1132
1031
            if down:
1133
1032
                csel += 1
1134
1033
            else:
1135
1034
                csel -= 1
1136
 
                csel = max(csel, 0)
1137
 
 
1138
 
            if csel < len(self._list.GetItems()):
1139
 
                self._list.SetSelection(csel)
1140
 
                self._list.EnsureVisible(csel)
 
1035
 
 
1036
        # If it's -1 actually, but just in case something
 
1037
        # crazy happens and it drops even below -1
 
1038
        if csel < 0:
 
1039
            csel = item_count - 1
 
1040
        if csel >= item_count:
 
1041
            csel = 0
 
1042
        self._list.SetSelection(csel)
 
1043
        self._list.EnsureVisible(csel)
 
1044
 
 
1045
    def GetListCtrl(self):
 
1046
        """Get the ListBox control of the popupwindow"""
 
1047
        return self._list
1141
1048
 
1142
1049
    def GetSelection(self):
1143
1050
        """Get the string that is currently selected in the list
1146
1053
        """
1147
1054
        return self._list.GetStringSelection()
1148
1055
 
 
1056
    def HasSelection(self):
 
1057
        """Tells whether anything in the list is selected"""
 
1058
        return self._list.GetSelection() != wx.NOT_FOUND
 
1059
 
 
1060
    def HasSuggestions(self):
 
1061
        """Tell whether the list is showing suggestions"""
 
1062
        return self.IsShown() and self.ListCount() > 0
 
1063
 
 
1064
    def ListCount(self):
 
1065
        """return the number of elements in the popup list"""
 
1066
        return self._list.GetCount()
 
1067
 
1149
1068
    def OnSize(self, evt):
 
1069
        """Resize the list box to the correct size to fit."""
1150
1070
        csz = self.GetClientSize()
1151
1071
        csz.SetWidth(csz.x + wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X))
1152
1072
        self._list.SetSize(csz)
1153
1073
        evt.Skip()
1154
1074
 
1155
 
    def SetBestSelection(self, prefix):
1156
 
        """Set the selection to the one that bests matches the
1157
 
        given string.
1158
 
        @param prefix: prefix to set selection of
1159
 
        @note: searches for a match recursively, if no partial match is found
1160
 
               then the first item in the list is selected.
 
1075
    def SetupPosition(self, cmd_ex):
 
1076
        """Sets size and position of widget
 
1077
        @param cmd_ex: CommandExecuter window
1161
1078
 
1162
1079
        """
1163
 
        if not len(prefix):
1164
 
            if len(self._list.GetStrings()):
1165
 
                self._list.SetSelection(0)
1166
 
        else:
1167
 
            matches = [item for item in self._list.GetItems()
1168
 
                       if item.startswith(prefix) ]
1169
 
            if len(matches):
1170
 
                self._list.SetStringSelection(sorted(matches)[0])
1171
 
            else:
1172
 
                self.SetBestSelection(prefix[:-1])
1173
 
 
 
1080
        cmd = cmd_ex.GetValue()
 
1081
        cmd = cmd.split(u' ', 1)[0]
 
1082
        pos = cmd_ex.GetScreenPosition().Get()
 
1083
        csize = cmd_ex.GetSize()
 
1084
        xpos = cmd_ex.GetTextExtent(cmd)[0]
1174
1085
        self._list.SetInitialSize()
1175
1086
        self.SetInitialSize()
 
1087
        self.SetPosition((pos[0] + xpos, pos[1] + csize[1]))
1176
1088
 
1177
1089
    def SetChoices(self, choices):
1178
1090
        """Set the available choices that are shown in the list
1179
1091
        @param choices: list of strings
1180
1092
 
1181
1093
        """
 
1094
        selection = self._list.GetSelection()
1182
1095
        self._list.SetItems(choices)
 
1096
        count = self._list.GetCount()
 
1097
        if selection == wx.NOT_FOUND or selection >= count:
 
1098
            selection = 0
 
1099
        if count > 0:
 
1100
            self._list.SetSelection(selection)
1183
1101
 
1184
1102
    def Show(self, show=True):
1185
1103
        """Adjust size of popup and then show it
1186
1104
        @keyword show: Should the window be shown or not
1187
1105
 
1188
1106
        """
1189
 
        res = wx.PopupWindow.Show(self, show)
 
1107
        res = super(PopupWinList, self).Show(show)
1190
1108
 
1191
1109
        self._list.Show()
1192
1110
        self._list.SetInitialSize()