~ubuntu-branches/ubuntu/precise/pida/precise

« back to all changes in this revision

Viewing changes to pida/services/terminal.py

  • Committer: Bazaar Package Importer
  • Author(s): Jan Luebbe
  • Date: 2007-04-17 16:08:06 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070417160806-3ttlb6igf94x9i03
Tags: 0.4.4-1
* New upstream release (closes: #419129)
* Add dependency on python-glade2 (closes: #418716)
* Update copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
 
27
27
import gtk
28
28
import gobject
 
29
import pango
29
30
 
30
31
import pida.core.base as base
31
32
import pida.core.service as service
33
34
import pida.pidagtk.contentview as contentview
34
35
 
35
36
defs = service.definitions
36
 
types = service.types
 
37
 
 
38
from pida.model import attrtypes as types
37
39
 
38
40
class terminal_view(contentview.content_view):
39
41
 
43
45
        terminal, kw = make_terminal(term_type, **kw)
44
46
        self.widget.pack_start(terminal.widget)
45
47
        self.set_long_title(' '.join(command_args))
46
 
        terminal.configure(self.service.opt('fonts_and_colours',
47
 
                                            'foreground_colour'),
48
 
                           self.service.opt('fonts_and_colours',
49
 
                                            'background_colour'),
50
 
                           self.service.opt('fonts_and_colours',
51
 
                                            'font'))
 
48
        opts = self.service.options.fonts_and_colours
 
49
        
 
50
        terminal.configure(opts.foreground_colour, opts.background_colour,
 
51
                           opts.font, opts.use_bold)
 
52
 
 
53
        terminal.service = self.service
52
54
        terminal.connect_child_exit(self.cb_exited)
53
55
        terminal.connect_title(self.set_long_title)
54
56
        terminal.execute(command_args, **kw)
55
57
        self.grab_focus = terminal.widget.grab_focus
 
58
        self.config = terminal.configure
56
59
 
57
60
    def cb_exited(self):
58
61
        self.close()
59
 
        
 
62
 
60
63
view_location_map = {'View Pane':'view',
61
64
                     'Quick Pane':'content',
62
65
                     'Detached':'ext'}
63
66
 
64
 
class terminal_manager(service.service):
65
 
 
66
 
    display_name = 'Terminals'
67
 
 
68
 
    multi_view_type = terminal_view
69
 
 
70
 
    def get_multi_view_book_type(self):
71
 
        opt = self.opt('general', 'terminal_location')
72
 
        return view_location_map[opt]
73
 
    multi_view_book = property(get_multi_view_book_type)
74
 
 
 
67
 
 
68
class TerminalConfig:
 
69
    __order__ = ['general', 'shell', 'fonts_and_colours']
75
70
    class shell(defs.optiongroup):
76
 
        """Shell options."""
 
71
        """Options relating to the shell run in terminals"""
 
72
        __order__ = ['command']
 
73
        label = 'Shell Options'
77
74
        class command(defs.option):
78
 
            """The command used for the shell."""
79
 
            default = os.environ['SHELL'] or 'bash'
 
75
            """The command used for the shell"""
 
76
            default = os.environ.get('SHELL', '')
80
77
            rtype = types.string
81
 
 
 
78
            label = 'Shell command'
82
79
    class general(defs.optiongroup):
83
 
        """Terminal options."""
 
80
        """General options relating to the terminal emulator"""
 
81
        __order__ = ['terminal_type', 'terminal_location']
 
82
        label = 'General Options'
84
83
        class terminal_type(defs.option):
85
 
            """The default terminal type used."""
 
84
            """The default terminal type used"""
86
85
            default = 'Vte'
87
 
            rtype = types.stringlist('Vte')#, 'Moo')
 
86
            rtype = types.stringlist('Vte', 'Moo')
 
87
            label = 'Terminal Type'
88
88
        class terminal_location(defs.option):
89
 
            """Where newly started terminals will appear by default"""
 
89
            """The pane where newly started terminals will appear by default"""
90
90
            rtype = types.stringlist(*view_location_map.keys())
91
91
            default = 'View Pane'
92
 
 
 
92
            label = 'Terminal Location'
93
93
    class fonts_and_colours(defs.optiongroup):
94
 
        """Fonts and colours for the terminal"""
 
94
        """Font and colour options for the terminal emulator"""
 
95
        label = 'Fonts & Colours'
 
96
        __order__  = ['background_colour', 'foreground_colour', 'font',
 
97
        'use_bold']
95
98
        class background_colour(defs.option):
96
99
            """The background colour to be used"""
97
100
            default = '#000000'
98
101
            rtype = types.color
 
102
            label = 'Background colour'
99
103
        class foreground_colour(defs.option):
100
 
            """The background colour to be used"""
 
104
            """The foreground colour to be used"""
101
105
            default = '#c0c0c0'
102
106
            rtype = types.color
 
107
            label = 'Foreground colour'
103
108
        class font(defs.option):
104
109
            """The font to be used in terminals"""
105
110
            default = 'Monospace 8'
106
111
            rtype = types.font
 
112
            label = 'Font'
 
113
        class use_bold(defs.option):
 
114
            """Whether the terminal will display bold characters"""
 
115
            rtype = types.boolean
 
116
            default = True
 
117
            label = 'Allow bold characters'
 
118
    __markup__ = lambda self: 'Terminal Emulator'
 
119
 
 
120
 
 
121
class terminal_manager(service.service):
 
122
 
 
123
    config_definition = TerminalConfig
 
124
 
 
125
    class TerminalView(defs.View):
 
126
        view_type = terminal_view
 
127
        book_name = 'view'
 
128
 
 
129
    def init(self):
 
130
        self.views = []
 
131
 
 
132
    def bind(self):
 
133
        fmanager = self.boss.get_service("filemanager")
 
134
        self._termact = gtk.Action('Terminal', 'Terminal',
 
135
                            'Open a terminal in this directory',
 
136
                            'gtk-terminal')
 
137
        self._termact.connect('activate', self.on_act_terminal)
 
138
 
 
139
        fmanager.cmd_register_toolbar_action(self._termact)
 
140
        
 
141
 
 
142
    def on_act_terminal(self, action):
 
143
        cwd = self.boss.call_command('filemanager', 'get_current_directory')
 
144
        self.cmd_execute_shell(kwdict={'directory': cwd})
 
145
        
 
146
    def cb_fonts_and_colours__foreground_colour(self, val):
 
147
        self._update_view_config()
 
148
 
 
149
    def cb_fonts_and_colours__background_colour(self, val):
 
150
        self._update_view_config()
 
151
 
 
152
    def cb_fonts_and_colours__font(self, val):
 
153
        self._update_view_config()
 
154
 
 
155
    def cb_fonts_and_colours__use_bold(self, val):
 
156
        self._update_view_config()
 
157
 
 
158
    views = []
 
159
 
 
160
    def _update_view_config(self):
 
161
        for view in self.views:
 
162
            view.config(self.opts.fonts_and_colours__foreground_colour,
 
163
                        self.opts.fonts_and_colours__background_colour,
 
164
                        self.opts.fonts_and_colours__font,
 
165
                        self.opts.fonts_and_colours__use_bold)
 
166
 
 
167
    def get_multi_view_book_type(self):
 
168
        opt = self.opts.general__terminal_location
 
169
        return view_location_map[opt]
 
170
    multi_view_book = property(get_multi_view_book_type)
107
171
 
108
172
    def cmd_execute(self, command_args=[], command_line='',
109
173
                    term_type=None, icon_name='terminal',
110
174
                    short_title='Terminal', kwdict={}):
111
175
        if term_type == None:
112
 
            term_type = self.opt('general', 'terminal_type').lower()
113
 
        self.create_multi_view(term_type=term_type,
114
 
                               command_args=command_args,
115
 
                               icon_name=icon_name,
116
 
                               short_title=short_title,
117
 
                               **kwdict)
 
176
            term_type = self.opts.general__terminal_type.lower()
 
177
        view = self.create_view('TerminalView',
 
178
                                term_type=term_type,
 
179
                                command_args=command_args,
 
180
                                icon_name=icon_name,
 
181
                                short_title=short_title,
 
182
                                **kwdict)
 
183
        self.show_view(view=view)
 
184
        self.views.append(view)
118
185
 
119
186
    def cmd_execute_shell(self, term_type=None, kwdict={}):
120
 
        shellcommand = self.opt('shell', 'command')
121
 
        self.call('execute', command_args=[shellcommand],
 
187
        shellcommand = self.opts.shell__command
 
188
        self.cmd_execute(command_args=[shellcommand],
122
189
                  term_type=term_type, kwdict=kwdict)
123
190
 
124
191
    @actions.action(stock_id='gtk-terminal',
129
196
        proj = self.boss.call_command('projectmanager',
130
197
                                      'get_current_project')
131
198
        if proj is not None:
132
 
            directory = proj.source_directory
133
 
        self.call('execute_shell', kwdict={'directory': directory})
134
 
        
 
199
            directory = proj.source__directory
 
200
        self.cmd_execute_shell(kwdict={'directory': directory})
 
201
 
 
202
    def view_closed(self, view):
 
203
        self.views.remove(view)
135
204
 
136
205
    def get_menu_definition(self):
137
206
        return """
154
223
                <placeholder name="VcToolbar">
155
224
                </placeholder>
156
225
                <placeholder name="ToolsToolbar">
157
 
            <separator />
158
226
            <toolitem  name="terminal" action="terminal+terminal" />
159
 
            <separator />
160
227
                </placeholder>
161
228
                </toolbar>
162
229
            """
180
247
        return None
181
248
    widget = property(get_widget)
182
249
 
183
 
    def configure(self, fg, bg, font):
 
250
    def configure(self, fg, bg, font, use_bold):
184
251
        pass
185
252
 
186
253
class hidden_terminal(pida_terminal):
201
268
        self.__term = vte.Terminal()
202
269
        self.__term.connect('child-exited', self.cb_exited)
203
270
 
204
 
    def configure(self, fg, bg, font):
 
271
    def configure(self, fg, bg, font, use_bold):
205
272
        bgcol = gtk.gdk.color_parse(bg)
206
273
        fgcol = gtk.gdk.color_parse(fg)
207
274
        self.__term.set_colors(fgcol, bgcol, [])
208
275
        self.__term.set_font_from_string(font)
209
 
        self.__term.set_size(30, 10)
210
276
        self.__term.set_size_request(1, 50)
 
277
        self.__term.set_allow_bold(use_bold)
211
278
 
212
279
    def get_widget(self):
213
280
        return self.__term
217
284
        command = command_args[0]
218
285
        self.__term.fork_command(command, command_args, **kw)
219
286
 
220
 
 
221
287
    def feed(self, text, color=None):
222
 
        """ Feed text to the terminal, optionally coloured."""
 
288
        """
 
289
        Feed text to the terminal, optionally coloured.
 
290
        """
223
291
        if color is not None:
224
292
            text = '\x1b[%sm%s\x1b[0m' % (color, text)
225
293
        self.__term.feed(text)
243
311
            title = term.get_window_title()
244
312
            callback(title)
245
313
        self.__term.connect('window-title-changed', title_changed)
246
 
        
 
314
 
 
315
import re
 
316
 
 
317
fre = re.compile(r'(/.+):([0-9]+):')
 
318
pre = re.compile(r'File \"(/.+)\"\, line ([0-9]+)\,')
247
319
 
248
320
class moo_terminal(pida_terminal):
249
321
 
250
322
    def init(self):
251
323
        import moo
252
324
        self.__term = moo.term.Term()
 
325
        self.__term.connect('child-died', self.cb_exited)
 
326
        
 
327
        self.it = self.__term.create_tag('a')
 
328
        self.it.set_attributes(moo.term.TEXT_FOREGROUND, moo.term.BLUE)
 
329
        self.pt = self.__term.create_tag('b')
 
330
        self.pt.set_attributes(moo.term.TEXT_FOREGROUND, moo.term.RED)
 
331
            
 
332
        self.__term.connect('new-line', self.cb_newline)
 
333
            
 
334
        self.__term.connect('populate-popup', self.cb_popup)
 
335
 
 
336
    def cb_popup(self, t, menu):
 
337
        if not t.get_selection():
 
338
            menu.get_children()[0].set_sensitive(False)
 
339
        x, y = t.window_to_buffer_coords(*t.get_pointer())
 
340
        i = t.get_iter_at_location(x, y)
 
341
        if i.has_tag(self.it) or i.has_tag(self.pt):
 
342
            e = i.copy()
 
343
            if i.has_tag(self.it):
 
344
                i.get_tag_start(self.it)
 
345
                e.get_tag_end(self.it)
 
346
                string = i.get_text(e)
 
347
                filename, ln, empty = string.split(':')
 
348
            elif i.has_tag(self.pt):
 
349
                i.get_tag_start(self.pt)
 
350
                e.get_tag_end(self.pt)
 
351
                string = i.get_text(e)
 
352
                filename, ln = pre.search(string).groups()
 
353
                
 
354
                
 
355
            menu.add(gtk.SeparatorMenuItem())
 
356
            a = gtk.Action('goto-line', 'Goto line', 'Goto the line',
 
357
                           gtk.STOCK_OK)
 
358
            mi = a.create_menu_item()
 
359
            menu.add(mi)
 
360
            menu.show_all()
 
361
            a.connect('activate', self.act_gotoline, filename, int(ln) - 1)
 
362
 
 
363
    def cb_newline(self, t):    
 
364
        cursor_i = t.get_iter_at_cursor()
 
365
        line_n = cursor_i.row - 1
 
366
        startline_i = t.get_iter_at_line(line_n)
 
367
        endline_i = startline_i.copy()
 
368
        endline_i.forward_to_line_end()
 
369
        line = startline_i.get_text(endline_i)
 
370
        self._line_received(line, line_n)
 
371
 
 
372
    def _line_received(self, line, line_n):
 
373
        for r, tag in [(fre, self.it), (pre, self.pt)]:
 
374
            match = r.search(line)
 
375
            if match:
 
376
                t = self.__term
 
377
                msl = t.get_iter_at_line_offset(line_n, match.start())
 
378
                esl = t.get_iter_at_line_offset(line_n, match.end())
 
379
                t.apply_tag(tag, msl, esl)
 
380
 
 
381
    def act_gotoline(self, action, file, line):
 
382
        self.service.boss.call_command('buffermanager', 'open_file_line',
 
383
            filename=file, linenumber=line)
 
384
 
 
385
    def get_line_at_click(self, x, y):
 
386
        pass
 
387
 
 
388
    def get_word_at_click(self, x, y):
 
389
        pass
 
390
 
 
391
    def cb_exited(self, term):
 
392
        self.feed('Child exited\r\n', '1;34')
 
393
        self.feed('Press any key to close.')
 
394
        self.__term.add_events(gtk.gdk.KEY_PRESS_MASK)
 
395
        self.__term.connect('key-press-event', self.cb_press_any_key)
 
396
 
 
397
    def feed(self, text, color=None):
 
398
        """ Feed text to the terminal, optionally coloured."""
 
399
        if color is not None:
 
400
            text = '\x1b[%sm%s\x1b[0m' % (color, text)
 
401
        self.__term.feed(text)
 
402
 
 
403
    def cb_press_any_key(self, term, event):
 
404
        self.exited()
253
405
 
254
406
    def execute(self, command_args, **kw):
255
407
        self.__term.fork_argv(command_args, **kw)
256
408
 
 
409
    def configure(self, fg, bg, font, use_bold):
 
410
        self.__term.set_font_from_string(font)
 
411
        self.__term.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse(fg))
 
412
        self.__term.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(bg))
 
413
 
257
414
    def get_widget(self):
258
415
        return self.__term
259
416
    widget = property(get_widget)
260
417
 
261
 
    def connect_child_exit(self):
262
 
        pass
263
 
 
264
418
    def translate_kwargs(self, **kw):
265
419
        kwdict = {}
266
420
        if 'directory' in kw:
268
422
        return kwdict
269
423
 
270
424
    def connect_title(self, callback):
271
 
        pass
 
425
        def title_changed(term, title):
 
426
            callback(title)
 
427
        self.__term.connect('set-window-title', title_changed)
 
428
 
272
429
 
273
430
class dumb_terminal(pida_terminal):
274
431
 
276
433
        import gtk
277
434
        self.__sw = gtk.ScrolledWindow()
278
435
        self.__sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
279
 
        self.__view = gtk.TextView()
 
436
        try:
 
437
            import moo
 
438
            self.__view = moo.edit.TextView()
 
439
            self.__is_moo = True
 
440
        except:
 
441
            self.__view = gtk.TextView()
 
442
            self.__is_moo = False
280
443
        self.__sw.add(self.__view)
281
444
 
282
 
    def configure(self, fg, bg, font):
 
445
    def configure(self, fg, bg, font, use_bold):
283
446
        model = self.__view.get_buffer()
284
 
        self.__tag = model.create_tag('fixed', editable=False,
285
 
                                      font=font)
286
 
                                       #, background=bg,
287
 
                                      #foreground=fg)
 
447
        self.__view.modify_font(pango.FontDescription(font))
288
448
        #bgcol = gtk.gdk.color_parse(bg)
289
449
        #self.__view.modify_bg(gtk.STATE_NORMAL, bgcol)
290
450
        #self.__view.modify_base(gtk.STATE_NORMAL, bgcol)
291
451
 
292
 
 
293
452
    def translate_kwargs(self, **kw):
294
453
        kwdict = {}
295
454
        if 'directory' in kw:
296
455
            kwdict['cwd'] = kw['directory']
 
456
        if 'lang' in kw:
 
457
            kwdict['lang'] = kw['lang']
 
458
            del kwdict['lang']
297
459
        return kwdict
298
460
 
299
461
    def execute(self, command_args, **kw):
 
462
        if kw.has_key('lang'):
 
463
            lang = kw['lang']
 
464
        else:
 
465
            lang = None
 
466
        if self.__is_moo:
 
467
            self.__view.set_lang_by_id(lang)
300
468
        proc = popen(command_args, self.cb_completed, kw)
301
469
 
302
470
    def get_widget(self):
306
474
        model = self.__view.get_buffer()
307
475
        start = model.get_start_iter()
308
476
        data = '%s\nChild exited' % data
309
 
        model.insert_with_tags(start, data, self.__tag)
310
 
        self.__view.scroll_to_iter(model.get_end_iter(), 0.0, False)
311
 
 
 
477
        model.insert_at_cursor(data)
 
478
        self.__view.scroll_to_mark(model.get_insert(), 0.0, False)
312
479
 
313
480
    def connect_title(self, callback):
314
481
        pass
338
505
 
339
506
 
340
507
class popen(object):
341
 
    
342
508
    def __init__(self, cmdargs, callback, kwargs):
343
509
        self.__running = False
344
510
        self.__readbuf = []
362
528
        return True
363
529
 
364
530
    def cb_hup(self, fd, cond):
 
531
        while True:
 
532
            data = os.read(fd.fileno(), 1024)
 
533
            if data == '':
 
534
                break
 
535
            self.__readbuf.append(data)
365
536
        self.__callback(''.join(self.__readbuf))
366
537
        self.__running = False
367
538
        gobject.source_remove(self.__readtag)
368
539
        return False
369
540
 
370
 
def test_terminal(terminal_type_names, command_line, **kw):
371
 
    import gtk
372
 
    w = gtk.Window()
373
 
    b = gtk.VButtonBox()
374
 
    w.add(b)
375
 
    for tt in terminal_type_names:
376
 
        t, kw = make_terminal(tt, **kw)
377
 
        if t.widget is not None:
378
 
            t.widget.set_size_request(200, 200)
379
 
            b.pack_start(t.widget, expand=True)
380
 
        else:
381
 
            b.pack_start(gtk.Label('no widget for %s' % tt))
382
 
        t.execute(command_line, **kw)
383
 
    w.show_all()
384
 
    w.connect('destroy', lambda *a: gtk.main_quit())
385
 
    gtk.main()
386
 
 
387
541
 
388
542
 
389
543