~ubuntu-branches/debian/sid/guake/sid

« back to all changes in this revision

Viewing changes to src/guake

  • Committer: Package Import Robot
  • Author(s): Daniel Echeverry
  • Date: 2014-09-05 21:47:17 UTC
  • mfrom: (1.1.6)
  • mto: (1.1.7)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140905214717-o44j7cegdz6t44yl
Tags: upstream-0.5.0
ImportĀ upstreamĀ versionĀ 0.5.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
 
1
#!/usr/bin/env python2
2
2
# -*- coding: utf-8; -*-
3
3
"""
4
4
Copyright (C) 2007-2012 Lincoln de Sousa <lincoln@minaslivre.org>
16
16
 
17
17
You should have received a copy of the GNU General Public
18
18
License along with this program; if not, write to the
19
 
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
 
Boston, MA 02111-1307, USA.
 
19
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
20
Boston, MA 02110-1301 USA
21
21
"""
 
22
 
22
23
from __future__ import absolute_import
 
24
from __future__ import division
23
25
 
24
 
import pygtk
 
26
import dbus
 
27
import gconf
25
28
import gobject
26
 
pygtk.require('2.0')
27
 
gobject.threads_init()
28
 
 
29
29
import gtk
 
30
import logging
 
31
import os
 
32
import posix
 
33
import pygtk
 
34
import re
 
35
import signal
 
36
import subprocess
 
37
import sys
30
38
import vte
 
39
import xdg.Exceptions
 
40
 
31
41
from pango import FontDescription
32
 
import pynotify
33
 
import gconf
34
 
import dbus
35
 
from xdg.DesktopEntry import DesktopEntry
36
 
import xdg.Exceptions
37
 
 
38
 
import os
39
 
import sys
40
 
import signal
41
42
from thread import start_new_thread
42
43
from time import sleep
43
 
import posix
 
44
from urllib import quote_plus
44
45
from urllib import url2pathname
45
46
from urlparse import urlsplit
 
47
from xdg.DesktopEntry import DesktopEntry
 
48
from xml.sax.saxutils import escape as xml_escape
46
49
 
47
50
import guake.globalhotkeys
48
 
from guake.simplegladeapp import SimpleGladeApp, bindtextdomain
49
 
from guake.prefs import PrefsDialog, LKEY, GKEY
50
 
from guake.dbusiface import DbusManager, DBUS_NAME, DBUS_PATH
51
 
from guake.common import test_gconf, pixmapfile, gladefile, ShowableError, _
 
51
import guake.notifier
 
52
 
 
53
from guake.common import ShowableError
 
54
from guake.common import _
 
55
from guake.common import clamp
 
56
from guake.common import gladefile
 
57
from guake.common import pixmapfile
52
58
from guake.common import shell_quote
53
 
from guake.globals import NAME, VERSION, LOCALE_DIR, KEY, GCONF_PATH, \
54
 
    TERMINAL_MATCH_EXPRS, TERMINAL_MATCH_TAGS, \
55
 
    ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER
 
59
from guake.common import test_gconf
 
60
from guake.dbusiface import DBUS_NAME
 
61
from guake.dbusiface import DBUS_PATH
 
62
from guake.dbusiface import DbusManager
 
63
from guake.globals import ALIGN_BOTTOM
 
64
from guake.globals import ALIGN_CENTER
 
65
from guake.globals import ALIGN_LEFT
 
66
from guake.globals import ALIGN_RIGHT
 
67
from guake.globals import ALWAYS_ON_PRIMARY
 
68
from guake.globals import GCONF_PATH
 
69
from guake.globals import KEY
 
70
from guake.globals import LOCALE_DIR
 
71
from guake.globals import NAME
 
72
from guake.globals import QUICK_OPEN_MATCHERS
 
73
from guake.globals import TERMINAL_MATCH_EXPRS
 
74
from guake.globals import TERMINAL_MATCH_TAGS
 
75
from guake.globals import VERSION
 
76
from guake.prefs import GKEY
 
77
from guake.prefs import LKEY
 
78
from guake.prefs import PrefsDialog
 
79
from guake.simplegladeapp import SimpleGladeApp
 
80
from guake.simplegladeapp import bindtextdomain
56
81
 
57
 
pynotify.init('Guake!')
58
82
 
59
83
GNOME_FONT_PATH = '/desktop/gnome/interface/monospace_font_name'
60
84
 
 
85
pygtk.require('2.0')
 
86
gobject.threads_init()
 
87
 
61
88
# Loading translation
62
89
bindtextdomain(NAME, LOCALE_DIR)
63
90
 
120
147
        notify_add = client.notify_add
121
148
 
122
149
        # these keys does not need to be watched.
123
 
        #notify_add(KEY('/general/default_shell'), self.shell_changed)
124
 
        #notify_add(KEY('/general/use_login_shell'), self.login_shell_toggled)
125
 
        #notify_add(KEY('/general/use_popup'), self.popup_toggled)
126
 
        #notify_add(KEY('/general/window_losefocus'), self.losefocus_toggled)
 
150
        # notify_add(KEY('/general/default_shell'), self.shell_changed)
 
151
        # notify_add(KEY('/general/use_login_shell'), self.login_shell_toggled)
 
152
        # notify_add(KEY('/general/use_popup'), self.popup_toggled)
 
153
        # notify_add(KEY('/general/window_losefocus'), self.losefocus_toggled)
 
154
        # notify_add(KEY('/general/use_vte_titles'), self.use_vte_titles_changed)
 
155
        # notify_add(KEY('/general/quick_open_enable'), self.on_quick_open_enable_changed)
 
156
 
 
157
        # Notification is not required for mouse_display/display_n because
 
158
        # get_final_window_rect polls gconf and is called whenever Guake is
 
159
        # shown or resized
127
160
 
128
161
        notify_add(KEY('/general/show_resizer'), self.show_resizer_toggled)
129
162
 
131
164
        notify_add(KEY('/general/window_ontop'), self.ontop_toggled)
132
165
        notify_add(KEY('/general/window_tabbar'), self.tabbar_toggled)
133
166
        notify_add(KEY('/general/window_height'), self.size_changed)
 
167
        notify_add(KEY('/general/window_width'), self.size_changed)
 
168
        notify_add(KEY('/general/window_valignment'), self.alignment_changed)
 
169
        notify_add(KEY('/general/window_halignment'), self.alignment_changed)
 
170
        notify_add(KEY('/style/cursor_blink_mode'), self.cursor_blink_mode_changed)
 
171
        notify_add(KEY('/style/cursor_shape'), self.cursor_shape_changed)
134
172
 
135
173
        notify_add(KEY('/general/use_scrollbar'), self.scrollbar_toggled)
136
174
        notify_add(KEY('/general/history_size'), self.history_size_changed)
162
200
        """If the gconf var use_trayicon be changed, this method will
163
201
        be called and will show/hide the trayicon.
164
202
        """
165
 
        self.guake.tray_icon.set_visible(entry.value.get_bool())
 
203
        if hasattr(self.guake.tray_icon, 'set_status'):
 
204
            self.guake.tray_icon.set_status(entry.value.get_bool())
 
205
        else:
 
206
            self.guake.tray_icon.set_visible(entry.value.get_bool())
166
207
 
167
208
    def ontop_toggled(self, client, connection_id, entry, data):
168
209
        """If the gconf var window_ontop be changed, this method will
196
237
        self.guake.window.resize(window_rect.width, window_rect.height)
197
238
        self.guake.window.move(window_rect.x, window_rect.y)
198
239
 
 
240
    def cursor_blink_mode_changed(self, client, connection_id, entry, data):
 
241
        """Called when cursor blink mode settings has been changed
 
242
        """
 
243
        for term in self.guake.term_list:
 
244
            term.set_property("cursor-blink-mode", entry.value.get_int())
 
245
 
 
246
    def cursor_shape_changed(self, client, connection_id, entry, data):
 
247
        """Called when the cursor shape settings has been changed
 
248
        """
 
249
        for term in self.guake.term_list:
 
250
            term.set_property("cursor-shape", entry.value.get_int())
 
251
 
199
252
    def scrollbar_toggled(self, client, connection_id, entry, data):
200
253
        """If the gconf var use_scrollbar be changed, this method will
201
254
        be called and will show/hide scrollbars of all terminals open.
202
255
        """
203
 
        for i in self.guake.term_list:
 
256
        for term in self.guake.term_list:
204
257
            # There is an hbox in each tab of the main notebook and it
205
258
            # contains a Terminal and a Scrollbar. Since only have the
206
259
            # Terminal here, we're going to use this to get the
207
260
            # scrollbar and hide/show it.
208
 
            hbox = i.get_parent()
 
261
            hbox = term.get_parent()
209
262
            terminal, scrollbar = hbox.get_children()
210
263
            if entry.value.get_bool():
211
264
                scrollbar.show()
239
292
    def default_font_toggled(self, client, connection_id, entry, data):
240
293
        """If the gconf var use_default_font be changed, this method
241
294
        will be called and will change the font style to the gnome
242
 
        default or to the choosen font in style/font/style in all
 
295
        default or to the chosen font in style/font/style in all
243
296
        terminals open.
244
297
        """
245
298
        if entry.value.get_bool():
267
320
        """
268
321
        fgcolor = gtk.gdk.color_parse(entry.value.get_string())
269
322
        for i in self.guake.term_list:
270
 
            i.set_color_dim(fgcolor)
271
 
            i.set_color_foreground(fgcolor)
272
 
            i.set_color_bold(fgcolor)
 
323
            i.set_color_dim(i.custom_fgcolor or fgcolor)
 
324
            i.set_color_foreground(i.custom_fgcolor or fgcolor)
 
325
            i.set_color_bold(i.custom_fgcolor or fgcolor)
273
326
 
274
327
    def fpalette_changed(self, client, connection_id, entry, data):
275
328
        """If the gconf var style/font/palette be changed, this method
280
333
            client.get_string(KEY('/style/font/color')))
281
334
        bgcolor = gtk.gdk.color_parse(
282
335
            client.get_string(KEY('/style/background/color')))
283
 
        palette = [gtk.gdk.color_parse(color) for color in 
284
 
            entry.value.get_string().split(':')]
 
336
        palette = [gtk.gdk.color_parse(color) for color in
 
337
                   entry.value.get_string().split(':')]
285
338
        for i in self.guake.term_list:
286
339
            i.set_colors(fgcolor, bgcolor, palette)
287
340
 
292
345
        """
293
346
        bgcolor = gtk.gdk.color_parse(entry.value.get_string())
294
347
        for i in self.guake.term_list:
295
 
            i.set_color_background(bgcolor)
296
 
            i.set_background_tint_color(bgcolor)
 
348
            i.set_color_background(i.custom_bgcolor or bgcolor)
 
349
            i.set_background_tint_color(i.custom_bgcolor or bgcolor)
297
350
 
298
351
    def bgimage_changed(self, client, connection_id, entry, data):
299
352
        """If the gconf var style/background/image be changed, this
301
354
        will set the transparent flag to false if an image is set in
302
355
        all terminals open.
303
356
        """
304
 
        image = entry.value.get_string()
305
 
        for i in self.guake.term_list:
306
 
            if image and os.path.exists(image):
307
 
                i.set_background_image_file(image)
308
 
                i.set_background_transparent(False)
309
 
            else:
310
 
                """We need to clear the image if it's not set but there is
311
 
                a bug in vte python bidnings which doesn't allow None to be
312
 
                passed to set_background_image (C GTK function expects NULL).
313
 
                The user will need to restart Guake after clearing the image.
314
 
                i.set_background_image(None)
315
 
                """
316
 
                if self.guake.has_argb:
317
 
                    i.set_background_transparent(False)
318
 
                else:
319
 
                    i.set_background_transparent(True)
 
357
        self.guake.set_background_image(entry.value.get_string())
320
358
 
321
359
    def bgtransparency_changed(self, client, connection_id, entry, data):
322
360
        """If the gconf var style/background/transparency be changed, this
323
361
        method will be called and will set the saturation and transparency
324
362
        properties in all terminals open.
325
363
        """
326
 
        transparency = entry.value.get_int()
327
 
        for i in self.guake.term_list:
328
 
            i.set_background_saturation(transparency / 100.0)
329
 
            if self.guake.has_argb:
330
 
                i.set_opacity(int((100 - transparency) / 100.0 * 65535))
 
364
        self.guake.set_background_transparency(entry.value.get_int())
331
365
 
332
366
    def backspace_changed(self, client, connection_id, entry, data):
333
367
        """If the gconf var compat_backspace be changed, this method
354
388
        to be used in internal methods.
355
389
        """
356
390
        self.guake = guake
357
 
        self.accel_group = None # see reload_accelerators
 
391
        self.accel_group = None  # see reload_accelerators
358
392
        self.client = gconf.client_get_default()
359
393
 
360
394
        notify_add = self.client.notify_add
361
395
        notify_add(GKEY('show_hide'), self.reload_globals)
362
396
 
363
 
        keys = ['toggle_fullscreen', 'new_tab', 'close_tab', 'rename_tab',
 
397
        keys = ['toggle_fullscreen', 'new_tab', 'close_tab', 'rename_current_tab',
364
398
                'previous_tab', 'next_tab', 'clipboard_copy', 'clipboard_paste',
365
 
                'quit',
 
399
                'quit', 'zoom_in', 'zoom_out', 'increase_height', 'decrease_height', "search_on_web",
 
400
                'switch_tab1', 'switch_tab2', 'switch_tab3', 'switch_tab4', 'switch_tab5',
 
401
                'switch_tab6', 'switch_tab7', 'switch_tab8', 'switch_tab9', 'switch_tab10'
366
402
                ]
367
403
        for key in keys:
368
404
            notify_add(LKEY(key), self.reload_accelerators)
377
413
        key = entry.get_value().get_string()
378
414
        if not self.guake.hotkeys.bind(key, self.guake.show_hide):
379
415
            raise ShowableError(_('key binding error'),
380
 
                                _('Unable to bind global <b>%s</b> key') % key,
 
416
                                _('Unable to bind global <b>%s</b> key') % xml_escape(key),
381
417
                                -1)
382
418
 
383
419
    def reload_accelerators(self, *args):
395
431
        """Reads all gconf paths under /apps/guake/keybindings/local
396
432
        and adds to the main accel_group.
397
433
        """
398
 
        gets = lambda x:self.client.get_string(LKEY(x))
 
434
        gets = lambda x: self.client.get_string(LKEY(x))
399
435
        key, mask = gtk.accelerator_parse(gets('quit'))
400
436
        if key > 0:
401
437
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
422
458
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
423
459
                                           self.guake.accel_next)
424
460
 
425
 
        key, mask = gtk.accelerator_parse(gets('rename_tab'))
 
461
        key, mask = gtk.accelerator_parse(gets('rename_current_tab'))
426
462
        if key > 0:
427
463
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
428
 
                                           self.guake.accel_rename)
 
464
                                           self.guake.accel_rename_current_tab)
429
465
 
430
466
        key, mask = gtk.accelerator_parse(gets('clipboard_copy'))
431
467
        if key > 0:
442
478
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
443
479
                                           self.guake.accel_toggle_fullscreen)
444
480
 
 
481
        key, mask = gtk.accelerator_parse(gets('zoom_in'))
 
482
        if key > 0:
 
483
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
 
484
                                           self.guake.accel_zoom_in)
 
485
 
 
486
        key, mask = gtk.accelerator_parse(gets('zoom_in_alt'))
 
487
        if key > 0:
 
488
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
 
489
                                           self.guake.accel_zoom_in)
 
490
 
 
491
        key, mask = gtk.accelerator_parse(gets('zoom_out'))
 
492
        if key > 0:
 
493
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
 
494
                                           self.guake.accel_zoom_out)
 
495
 
 
496
        key, mask = gtk.accelerator_parse(gets('increase_height'))
 
497
        if key > 0:
 
498
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
 
499
                                           self.guake.accel_increase_height)
 
500
 
 
501
        key, mask = gtk.accelerator_parse(gets('decrease_height'))
 
502
        if key > 0:
 
503
            self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
 
504
                                           self.guake.accel_decrease_height)
 
505
 
 
506
        for tab in xrange(1, 11):
 
507
            key, mask = gtk.accelerator_parse(gets('switch_tab%d' % tab))
 
508
            if key > 0:
 
509
                self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
 
510
                                               self.guake.gen_accel_switch_tabN(tab - 1))
 
511
 
 
512
        try:
 
513
            key, mask = gtk.accelerator_parse(gets('search_on_web'))
 
514
            if key > 0:
 
515
                self.accel_group.connect_group(key, mask, gtk.ACCEL_VISIBLE,
 
516
                                               self.guake.search_on_web)
 
517
        except Exception as e:
 
518
            print "Exception occured: %s" % (str(e,))
 
519
 
 
520
 
445
521
class GuakeTerminal(vte.Terminal):
446
522
    """Just a vte.Terminal with some properties already set.
447
523
    """
 
524
 
448
525
    def __init__(self):
449
526
        super(GuakeTerminal, self).__init__()
450
527
        self.configure_terminal()
451
528
        self.add_matches()
452
529
        self.connect('button-press-event', self.button_press)
453
530
        self.matched_value = ''
 
531
        self.font_scale_index = 0
 
532
        self.pid = None
 
533
        self.custom_bgcolor = None
 
534
        self.custom_fgcolor = None
454
535
 
455
536
    def configure_terminal(self):
456
537
        """Sets all customized properties on the terminal
463
544
        self.set_sensitive(True)
464
545
        self.set_flags(gtk.CAN_DEFAULT)
465
546
        self.set_flags(gtk.CAN_FOCUS)
 
547
        cursor_blink_mode = client.get_int(KEY('/style/cursor_blink_mode'))
 
548
        client.set_int(KEY('/style/cursor_blink_mode'), cursor_blink_mode)
 
549
        cursor_shape = client.get_int(KEY('/style/cursor_shape'))
 
550
        client.set_int(KEY('/style/cursor_shape'), cursor_shape)
466
551
 
467
552
    def add_matches(self):
468
553
        """Adds all regular expressions declared in
473
558
            tag = self.match_add(expr)
474
559
            self.match_set_cursor_type(tag, gtk.gdk.HAND2)
475
560
 
 
561
        for _useless, match, _otheruseless in QUICK_OPEN_MATCHERS:
 
562
            tag = self.match_add(match)
 
563
            self.match_set_cursor_type(tag, gtk.gdk.HAND2)
 
564
 
 
565
    def get_current_directory(self):
 
566
        directory = os.path.expanduser('~')
 
567
        if self.pid is not None:
 
568
            cwd = os.readlink("/proc/%d/cwd" % self.pid)
 
569
            if os.path.exists(cwd):
 
570
                directory = cwd
 
571
        return directory
 
572
 
476
573
    def button_press(self, terminal, event):
477
574
        """Handles the button press event in the terminal widget. If
478
 
        any match string is caught, another aplication is open to
 
575
        any match string is caught, another application is open to
479
576
        handle the matched resource uri.
480
577
        """
481
578
        self.matched_value = ''
483
580
            int(event.x / self.get_char_width()),
484
581
            int(event.y / self.get_char_height()))
485
582
 
486
 
        if event.button == 1 \
487
 
                and event.get_state() & gtk.gdk.CONTROL_MASK \
488
 
                and matched_string:
 
583
        if (event.button == 1
 
584
                and event.get_state() & gtk.gdk.CONTROL_MASK
 
585
                and matched_string):
489
586
            value, tag = matched_string
490
 
 
491
 
            if TERMINAL_MATCH_TAGS[tag] == 'schema':
492
 
                # value here should not be changed, it is right and
493
 
                # ready to be used.
494
 
                pass
495
 
            elif TERMINAL_MATCH_TAGS[tag] == 'http':
496
 
                value = 'http://%s' % value
497
 
            elif TERMINAL_MATCH_TAGS[tag] == 'email':
498
 
                value = 'mailto:%s' % value
499
 
 
500
 
            gtk.show_uri(self.window.get_screen(), value,
501
 
                         gtk.gdk.x11_get_server_time(self.window))
 
587
            # First searching in additional matchers
 
588
            found = False
 
589
            client = gconf.client_get_default()
 
590
            use_quick_open = client.get_bool(KEY("/general/quick_open_enable"))
 
591
            cmdline = client.get_string(KEY("/general/quick_open_command_line"))
 
592
            if use_quick_open:
 
593
                for _useless, _otheruseless, extractor in QUICK_OPEN_MATCHERS:
 
594
                    g = re.compile(extractor).match(value)
 
595
                    if g and len(g.groups()) == 2:
 
596
                        filename = g.group(1)
 
597
                        line_number = g.group(2)
 
598
                        filepath = filename
 
599
                        curdir = self.get_current_directory()
 
600
                        filepath = os.path.join(curdir, filename)
 
601
                        if not os.path.exists(filepath):
 
602
                            logging.info("Cannot open file %s, it doesn't exists (current dir: %s)", filepath,
 
603
                                         os.path.curdir)
 
604
                            continue
 
605
                        logging.debug("Opening file %s at line %s", filepath, line_number)
 
606
                        resolved_cmdline = cmdline % {"file_path": filepath, "line_number": line_number}
 
607
                        logging.debug("Command line: %s", resolved_cmdline)
 
608
                        subprocess.call(resolved_cmdline, shell=True)
 
609
                        found = True
 
610
                        break
 
611
            if not found:
 
612
                if TERMINAL_MATCH_TAGS[tag] == 'schema':
 
613
                    # value here should not be changed, it is right and
 
614
                    # ready to be used.
 
615
                    pass
 
616
                elif TERMINAL_MATCH_TAGS[tag] == 'http':
 
617
                    value = 'http://%s' % value
 
618
                elif TERMINAL_MATCH_TAGS[tag] == 'email':
 
619
                    value = 'mailto:%s' % value
 
620
 
 
621
                gtk.show_uri(self.window.get_screen(), value,
 
622
                             gtk.gdk.x11_get_server_time(self.window))
502
623
        elif event.button == 3 and matched_string:
503
624
            self.matched_value = matched_string[0]
504
625
 
 
626
    def set_font(self, font):
 
627
        self.font = font
 
628
        self.set_font_scale_index(0)
 
629
 
 
630
    def set_font_scale_index(self, scale_index):
 
631
        self.font_scale_index = clamp(scale_index, -6, 12)
 
632
 
 
633
        font = FontDescription(self.font.to_string())
 
634
        scale_factor = 2 ** (self.font_scale_index / 6)
 
635
        new_size = int(scale_factor * font.get_size())
 
636
 
 
637
        if font.get_size_is_absolute():
 
638
            font.set_absolute_size(new_size)
 
639
        else:
 
640
            font.set_size(new_size)
 
641
 
 
642
        super(GuakeTerminal, self).set_font(font)
 
643
 
 
644
    font_scale = property(
 
645
        fset=set_font_scale_index,
 
646
        fget=lambda self: self.font_scale_index
 
647
    )
 
648
 
 
649
    def increase_font_size(self):
 
650
        self.font_scale += 1
 
651
 
 
652
    def decrease_font_size(self):
 
653
        self.font_scale -= 1
 
654
 
 
655
 
505
656
class GuakeTerminalBox(gtk.HBox):
506
657
    """A box to group the terminal and a scrollbar.
507
658
    """
536
687
        # setting global hotkey and showing a pretty notification =)
537
688
        guake.globalhotkeys.init()
538
689
 
 
690
        # Cannot use "getattr(gtk.Window().get_style(), "base")[int(gtk.STATE_SELECTED)]"
 
691
        # since theme has not been applied before first show_all
 
692
        self.selected_color = None
 
693
 
 
694
        self.isPromptQuitDialogOpened = False
 
695
 
539
696
        # trayicon!
540
 
        img = pixmapfile('guake-tray.png')
541
 
        self.tray_icon = gtk.status_icon_new_from_file(img)
542
 
        self.tray_icon.set_tooltip(_('Guake Terminal'))
543
 
        self.tray_icon.connect('popup-menu', self.show_menu)
544
 
        self.tray_icon.connect('activate', self.show_hide)
 
697
        try:
 
698
            import appindicator
 
699
        except ImportError:
 
700
            img = pixmapfile('guake-tray.png')
 
701
            self.tray_icon = gtk.status_icon_new_from_file(img)
 
702
            self.tray_icon.set_tooltip(_('Guake Terminal'))
 
703
            self.tray_icon.connect('popup-menu', self.show_menu)
 
704
            self.tray_icon.connect('activate', self.show_hide)
 
705
        else:
 
706
            self.tray_icon = appindicator.Indicator(_("guake-indicator"), _("guake-tray"), appindicator.CATEGORY_OTHER)
 
707
            self.tray_icon.set_icon("guake-tray")
 
708
            self.tray_icon.set_status(appindicator.STATUS_ACTIVE)
 
709
            menu = self.get_widget('tray-menu')
 
710
            show = gtk.MenuItem(_('Show'))
 
711
            show.set_sensitive(True)
 
712
            show.connect('activate', self.show_hide)
 
713
            show.show()
 
714
            menu.prepend(show)
 
715
            self.tray_icon.set_menu(menu)
545
716
 
546
717
        # adding images from a different path.
547
718
        ipath = pixmapfile('guake.png')
560
731
        # check and set ARGB for real transparency
561
732
        screen = self.window.get_screen()
562
733
        colormap = screen.get_rgba_colormap()
563
 
        if colormap != None and screen.is_composited():
 
734
        if colormap == None:
 
735
            self.has_argb = False
 
736
        else:
564
737
            self.window.set_colormap(colormap)
565
 
            self.has_argb = True
566
 
        else:
567
 
            self.has_argb = False
 
738
            self.has_argb = self.window.get_screen().is_composited()
 
739
 
 
740
            def composited_changed(screen):
 
741
                self.has_argb = screen.is_composited()
 
742
                self.set_background_transparency(
 
743
                    self.client.get_int(KEY('/style/background/transparency')))
 
744
                self.set_background_image(
 
745
                    self.client.get_string(KEY('/style/background/image')))
 
746
 
 
747
            self.window.get_screen().connect("composited-changed",
 
748
                                             composited_changed)
568
749
 
569
750
        # List of vte.Terminal widgets, it will be useful when needed
570
751
        # to get a widget by the current page in self.notebook
585
766
        # holds the timestamp of the losefocus event
586
767
        self.losefocus_time = 0
587
768
 
 
769
        # holds the timestamp of the previous show/hide action
 
770
        self.prev_showhide_time = 0
 
771
 
588
772
        # double click stuff
589
773
        def double_click(hbox, event):
590
774
            """Handles double clicks on tabs area and when receive
598
782
        # Flag to prevent guake hide when window_losefocus is true and
599
783
        # user tries to use the context menu.
600
784
        self.showing_context_menu = False
 
785
 
601
786
        def hide_context_menu(menu):
602
787
            """Turn context menu flag off to make sure it is not being
603
788
            shown.
614
799
            return True
615
800
        self.window.connect('delete-event', destroy)
616
801
 
617
 
        # Flag to completly disable losefocus hiding
 
802
        # Flag to completely disable losefocus hiding
618
803
        self.disable_losefocus_hiding = False
619
804
 
620
805
        # this line is important to resize the main window and make it
621
806
        # smaller.
622
807
        self.window.set_geometry_hints(min_width=1, min_height=1)
623
808
 
 
809
        # special trick to avoid the "lost guake on Ubuntu 'Show Desktop'" problem.
 
810
        # DOCK makes the window foundable after having being "lost" after "Show Desktop"
 
811
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
 
812
        # Restore back to normal behavior
 
813
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
 
814
 
624
815
        # resizer stuff
625
816
        self.resizer.connect('motion-notify-event', self.on_resizer_drag)
626
817
 
639
830
        filename = pixmapfile('guake-notification.png')
640
831
 
641
832
        if self.client.get_bool(KEY('/general/start_fullscreen')):
642
 
             self.fullscreen()
 
833
            self.fullscreen()
643
834
 
644
835
        if not self.hotkeys.bind(key, self.show_hide):
645
 
            notification = pynotify.Notification(
 
836
            guake.notifier.show_message(
646
837
                _('Guake!'),
647
838
                _('A problem happened when binding <b>%s</b> key.\n'
648
839
                  'Please use Guake Preferences dialog to choose another '
649
 
                  'key (The trayicon was enabled)') % label, filename)
 
840
                  'key') % xml_escape(label), filename)
650
841
            self.client.set_bool(KEY('/general/use_trayicon'), True)
651
 
            notification.show()
652
842
 
653
843
        elif self.client.get_bool(KEY('/general/use_popup')):
654
844
            # Pop-up that shows that guake is working properly (if not
655
845
            # unset in the preferences windows)
656
 
            notification = pynotify.Notification(
 
846
            guake.notifier.show_message(
657
847
                _('Guake!'),
658
848
                _('Guake is now running,\n'
659
 
                  'press <b>%s</b> to use it.') % label, filename)
660
 
            notification.show()
 
849
                  'press <b>%s</b> to use it.') % xml_escape(label), filename)
 
850
 
 
851
    def set_background_transparency(self, transparency):
 
852
        for i in self.term_list:
 
853
            i.set_background_saturation(transparency / 100.0)
 
854
            if self.has_argb:
 
855
                i.set_opacity(int((100 - transparency) / 100.0 * 65535))
 
856
 
 
857
    def set_background_image(self, image):
 
858
        for i in self.term_list:
 
859
            if image and os.path.exists(image):
 
860
                i.set_background_image_file(image)
 
861
                i.set_background_transparent(False)
 
862
            else:
 
863
                """We need to clear the image if it's not set but there is
 
864
                a bug in vte python bindings which doesn't allow None to be
 
865
                passed to set_background_image (C GTK function expects NULL).
 
866
                The user will need to restart Guake after clearing the image.
 
867
                i.set_background_image(None)
 
868
                """
 
869
                if self.has_argb:
 
870
                    i.set_background_transparent(False)
 
871
                else:
 
872
                    i.set_background_transparent(True)
 
873
 
 
874
    def set_bgcolor(self, bgcolor, tab=None):
 
875
        """Set the background color of `tab' or the current tab to `bgcolor'."""
 
876
        if not self.term_list:
 
877
            self.add_tab()
 
878
        index = tab or self.notebook.get_current_page()
 
879
        self.term_list[index].custom_bgcolor = gtk.gdk.color_parse(bgcolor)
 
880
 
 
881
    def set_fgcolor(self, fgcolor, tab=None):
 
882
        """Set the foreground color of `tab' or the current tab to `fgcolor'."""
 
883
        if not self.term_list:
 
884
            self.add_tab()
 
885
        index = tab or self.notebook.get_current_page()
 
886
        self.term_list[index].custom_fgcolor = gtk.gdk.color_parse(fgcolor)
661
887
 
662
888
    def execute_command(self, command, tab=None):
663
889
        """Execute the `command' in the `tab'. If tab is None, the
683
909
        if not 'GDK_BUTTON1_MASK' in mod.value_names:
684
910
            return
685
911
 
686
 
        max_height = self.window.get_screen().get_height()
 
912
        screen = self.window.get_screen()
 
913
        x, y, _ = screen.get_root_window().get_pointer()
 
914
        screen_no = screen.get_monitor_at_point(x, y)
 
915
 
 
916
        max_height = screen.get_monitor_geometry(screen_no).height
687
917
        percent = y / (max_height / 100)
688
918
 
689
919
        if percent < 1:
698
928
        if self.disable_losefocus_hiding or self.showing_context_menu:
699
929
            return
700
930
 
 
931
        if self.isPromptQuitDialogOpened:
 
932
            return
 
933
 
701
934
        value = self.client.get_bool(KEY('/general/window_losefocus'))
702
935
        visible = window.get_property('visible')
703
936
        if value and visible:
726
959
            self.get_widget('context_paste').set_sensitive(False)
727
960
        else:
728
961
            self.get_widget('context_paste').set_sensitive(True)
 
962
 
 
963
        current_selection = ''
 
964
        current_term = self.term_list[self.notebook.get_current_page()]
 
965
        if current_term.get_has_selection():
 
966
            current_term.copy_clipboard()
 
967
            guake_clipboard = gtk.clipboard_get()
 
968
            current_selection = guake_clipboard.wait_for_text().rstrip()
 
969
 
 
970
            if len(current_selection) > 20:
 
971
                current_selection = current_selection[:17] + "..."
 
972
 
 
973
        if current_selection:
 
974
            self.get_widget('context_search_on_web').set_label(_("Search on Web: '%s'") % current_selection)
 
975
            self.get_widget('context_search_on_web').set_sensitive(True)
 
976
        else:
 
977
            self.get_widget('context_search_on_web').set_label(_("Search on Web (no selection)"))
 
978
            self.get_widget('context_search_on_web').set_sensitive(False)
 
979
 
729
980
        context_menu = self.get_widget('context-menu')
730
981
        context_menu.popup(None, None, None, 3, gtk.get_current_event_time())
731
982
        return True
732
983
 
733
 
    def show_rename_dialog(self, target, event):
 
984
    def show_rename_current_tab_dialog(self, target, event):
734
985
        """On double-click over a tab, show the rename dialog.
735
986
        """
736
987
        if event.button == 1:
737
988
            if event.type == gtk.gdk._2BUTTON_PRESS:
738
 
                self.accel_rename()
 
989
                self.accel_rename_current_tab()
739
990
                self.set_terminal_focus()
740
991
                self.selected_tab.pressed()
741
992
                return
776
1027
            self.losefocus_time = 0
777
1028
            return
778
1029
 
 
1030
        # limit rate at which the visibility can be toggled.
 
1031
        if self.prev_showhide_time and \
 
1032
                (event_time - self.prev_showhide_time) < 65:
 
1033
            return
 
1034
        self.prev_showhide_time = event_time
 
1035
 
779
1036
        if not self.window.get_property('visible'):
780
1037
            self.show()
781
1038
            self.set_terminal_focus()
 
1039
        elif (self.client.get_bool(KEY('/general/focus_if_open')) and
 
1040
                self.window.window and
 
1041
                not self.window.window.get_state()):
 
1042
            self.window.window.focus()
782
1043
        else:
783
1044
            self.hide()
784
 
 
 
1045
 
785
1046
    def show(self):
786
1047
        """Shows the main window and grabs the focus on it.
787
1048
        """
788
1049
        # setting window in all desktops
 
1050
        window_rect = self.get_final_window_rect()
789
1051
        self.get_widget('window-root').stick()
790
1052
 
791
1053
        # add tab must be called before window.show to avoid a
793
1055
        if not self.term_list:
794
1056
            self.add_tab()
795
1057
 
 
1058
        self.window.set_keep_below(False)
796
1059
        self.window.show_all()
 
1060
 
 
1061
        if self.selected_color is None:
 
1062
            self.selected_color = getattr(self.window.get_style(), "light")[int(gtk.STATE_SELECTED)]
 
1063
 
 
1064
            # Reapply the tab color to all button in the tab list, since at least one don't have the select color
 
1065
            # set. This needs to happen AFTER the first show_all, since before the gtk has not loaded the right
 
1066
            # colors yet.
 
1067
            for tab in self.tabs.get_children():
 
1068
                if isinstance(tab, gtk.RadioButton):
 
1069
                    tab.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.Color(str(self.selected_color)))
 
1070
 
 
1071
        # this work arround an issue in fluxbox
 
1072
        self.window.move(window_rect.x, window_rect.y)
 
1073
 
797
1074
        self.client.notify(KEY('/general/window_height'))
798
1075
 
799
1076
        try:
814
1091
        """Hides the main window of the terminal and sets the visible
815
1092
        flag to False.
816
1093
        """
817
 
        self.window.hide() # Don't use hide_all here!
 
1094
        self.window.set_keep_below(True)
 
1095
        self.window.hide()  # Don't use hide_all here!
 
1096
 
 
1097
    def get_final_window_monitor(self):
 
1098
        """Gets the final screen number for the main window of guake.
 
1099
        """
 
1100
 
 
1101
        screen = self.window.get_screen()
 
1102
 
 
1103
        # fetch settings
 
1104
        use_mouse = self.client.get_bool(KEY('/general/mouse_display'))
 
1105
        dest_screen = self.client.get_int(KEY('/general/display_n'))
 
1106
 
 
1107
        if use_mouse:
 
1108
            x, y, _ = screen.get_root_window().get_pointer()
 
1109
            dest_screen = screen.get_monitor_at_point(x, y)
 
1110
 
 
1111
        # If Guake is configured to use a screen that is not currently attached,
 
1112
        # default to 'primary display' option.
 
1113
        n_screens = screen.get_n_monitors()
 
1114
        if dest_screen > n_screens - 1:
 
1115
            self.client.set_bool(KEY('/general/mouse_display'), False)
 
1116
            self.client.set_int(KEY('/general/display_n'), dest_screen)
 
1117
            dest_screen = screen.get_primary_monitor()
 
1118
 
 
1119
        # Use primary display if configured
 
1120
        if dest_screen == ALWAYS_ON_PRIMARY:
 
1121
            dest_screen = screen.get_primary_monitor()
 
1122
 
 
1123
        return dest_screen
818
1124
 
819
1125
    def get_final_window_rect(self):
820
1126
        """Gets the final size of the main window of guake. The height
821
1127
        is the window_height property, width is window_width and the
822
1128
        horizontal alignment is given by window_alignment.
823
1129
        """
824
 
        screen = self.window.get_screen()
825
 
        height = self.client.get_int(KEY('/general/window_height'))
826
 
        width = 100
 
1130
 
 
1131
        # fetch settings
 
1132
        try:
 
1133
            height_percents = self.client.get_float(KEY('/general/window_height'))
 
1134
        except:
 
1135
            height_percents = self.client.get_int(KEY('/general/window_height'))
 
1136
        try:
 
1137
            width_percents = self.client.get_float(KEY('/general/window_width'))
 
1138
        except:
 
1139
            width_percents = self.client.get_int(KEY('/general/window_width'))
827
1140
        halignment = self.client.get_int(KEY('/general/window_halignment'))
828
 
 
829
 
        # get the rectangle just from the first/default monitor in the
830
 
        # future we might create a field to select which monitor you
831
 
        # wanna use
832
 
        window_rect = screen.get_monitor_geometry(0)
 
1141
        valignment = self.client.get_int(KEY('/general/window_valignment'))
 
1142
 
 
1143
        # get the rectangle just from the destination monitor
 
1144
        screen = self.window.get_screen()
 
1145
        monitor = self.get_final_window_monitor()
 
1146
        window_rect = screen.get_monitor_geometry(monitor)
 
1147
 
 
1148
        if os.environ.get('DESKTOP_SESSION') == "ubuntu":
 
1149
            unity_hide = self.client.get_int(KEY('/apps/compiz-1/plugins/'
 
1150
                                                 'unityshell/screen0/options/launcher_hide_mode'))
 
1151
            # launcher_hide_mode = 1 => autohide
 
1152
            if unity_hide != 1:
 
1153
                # Size of the icons for Unity in Ubuntu <= 12.04
 
1154
                # TODO Ubuntu 12.10 use dconf :
 
1155
                # /org/compiz/profiles/unity/plugins/unityshell/icon-size
 
1156
                unity_icon_size = self.client.get_int(KEY('/apps/compiz-1/'
 
1157
                                                          'plugins/unityshell/screen0/options/icon_size'))
 
1158
                unity_dock = unity_icon_size + 17
 
1159
                window_rect.width = window_rect.width - unity_dock
 
1160
 
833
1161
        total_width = window_rect.width
834
 
        window_rect.height = window_rect.height * height / 100
835
 
        window_rect.width = window_rect.width * width / 100
836
 
 
837
 
        if width < total_width:
 
1162
        total_height = window_rect.height
 
1163
 
 
1164
        window_rect.height = window_rect.height * height_percents / 100
 
1165
        window_rect.width = window_rect.width * width_percents / 100
 
1166
 
 
1167
        if window_rect.width < total_width:
838
1168
            if halignment == ALIGN_CENTER:
839
 
                window_rect.x = (total_width - window_rect.width) / 2
 
1169
                window_rect.x += (total_width - window_rect.width) / 2
840
1170
            elif halignment == ALIGN_LEFT:
841
 
                window_rect.x = 0
 
1171
                window_rect.x += 0
842
1172
            elif halignment == ALIGN_RIGHT:
843
 
                window_rect.x = total_width - window_rect.width
844
 
        window_rect.y = 0
 
1173
                window_rect.x += total_width - window_rect.width
 
1174
        if window_rect.height < total_height:
 
1175
            if valignment == ALIGN_BOTTOM:
 
1176
                window_rect.y += (total_height - window_rect.height)
 
1177
 
845
1178
        return window_rect
846
1179
 
847
1180
    def get_running_fg_processes(self):
855
1188
            term_pid = self.pid_list[term_idx]
856
1189
            fgpid = posix.tcgetpgrp(fdpty)
857
1190
            if not (fgpid == -1 or fgpid == term_pid):
858
 
                total_procs += 1
 
1191
                total_procs += 1
859
1192
            term_idx += 1
860
1193
        return total_procs
861
1194
 
867
1200
        self.client.notify(KEY('/general/use_trayicon'))
868
1201
        self.client.notify(KEY('/general/prompt_on_quit'))
869
1202
        self.client.notify(KEY('/general/window_tabbar'))
 
1203
        self.client.notify(KEY('/general/mouse_display'))
 
1204
        self.client.notify(KEY('/general/display_n'))
870
1205
        self.client.notify(KEY('/general/window_ontop'))
871
1206
        self.client.notify(KEY('/general/window_height'))
 
1207
        self.client.notify(KEY('/general/window_width'))
872
1208
        self.client.notify(KEY('/general/use_scrollbar'))
873
1209
        self.client.notify(KEY('/general/history_size'))
874
1210
        self.client.notify(KEY('/general/show_resizer'))
875
1211
        self.client.notify(KEY('/general/use_vte_titles'))
 
1212
        self.client.notify(KEY('/general/quick_open_enable'))
 
1213
        self.client.notify(KEY('/general/quick_open_command_line'))
 
1214
        self.client.notify(KEY('/style/cursor_shape'))
876
1215
        self.client.notify(KEY('/style/font/style'))
877
1216
        self.client.notify(KEY('/style/font/color'))
878
1217
        self.client.notify(KEY('/style/font/palette'))
889
1228
        if self.client.get_bool(KEY('/general/prompt_on_quit')):
890
1229
            procs = self.get_running_fg_processes()
891
1230
            if procs >= 1:
 
1231
                self.isPromptQuitDialogOpened = True
892
1232
                dialog = PromptQuitDialog(self.window, procs)
893
1233
                response = dialog.run() == gtk.RESPONSE_YES
894
1234
                dialog.destroy()
 
1235
                self.isPromptQuitDialogOpened = False
895
1236
                if response:
896
1237
                    gtk.main_quit()
897
1238
            else:
899
1240
        else:
900
1241
            gtk.main_quit()
901
1242
 
 
1243
    def accel_zoom_in(self, *args):
 
1244
        """Callback to zoom in.
 
1245
        """
 
1246
        for term in self.term_list:
 
1247
            term.increase_font_size()
 
1248
        return True
 
1249
 
 
1250
    def accel_zoom_out(self, *args):
 
1251
        """Callback to zoom out.
 
1252
        """
 
1253
        for term in self.term_list:
 
1254
            term.decrease_font_size()
 
1255
        return True
 
1256
 
 
1257
    def accel_increase_height(self, *args):
 
1258
        """Callback to increase height.
 
1259
        """
 
1260
        try:
 
1261
            height = self.client.get_float(KEY('/general/window_height'))
 
1262
        except:
 
1263
            height = self.client.get_int(KEY('/general/window_height'))
 
1264
 
 
1265
        self.client.set_int(KEY('/general/window_height'), int(height) + 2)
 
1266
        return True
 
1267
 
 
1268
    def accel_decrease_height(self, *args):
 
1269
        """Callback to decrease height.
 
1270
        """
 
1271
        try:
 
1272
            height = self.client.get_float(KEY('/general/window_height'))
 
1273
        except:
 
1274
            height = self.client.get_int(KEY('/general/window_height'))
 
1275
 
 
1276
        self.client.set_int(KEY('/general/window_height'), int(height) - 2)
 
1277
        return True
 
1278
 
902
1279
    def accel_add(self, *args):
903
1280
        """Callback to add a new tab. Called by the accel key.
904
1281
        """
909
1286
        """Callback to go to the previous tab. Called by the accel key.
910
1287
        """
911
1288
        if self.notebook.get_current_page() == 0:
912
 
            self.notebook.set_current_page(self.notebook.get_n_pages()-1)
 
1289
            self.notebook.set_current_page(self.notebook.get_n_pages() - 1)
913
1290
        else:
914
1291
            self.notebook.prev_page()
915
1292
        return True
917
1294
    def accel_next(self, *args):
918
1295
        """Callback to go to the next tab. Called by the accel key.
919
1296
        """
920
 
        if self.notebook.get_current_page()+1 == self.notebook.get_n_pages():
 
1297
        if self.notebook.get_current_page() + 1 == self.notebook.get_n_pages():
921
1298
            self.notebook.set_current_page(0)
922
1299
        else:
923
1300
            self.notebook.next_page()
924
1301
        return True
925
1302
 
926
 
    def accel_rename(self, *args):
 
1303
    def gen_accel_switch_tabN(self, N):
 
1304
        """Generates callback (which called by accel key) to go to the Nth tab.
 
1305
        """
 
1306
        def callback(*args):
 
1307
            if N >= 0 and N < self.notebook.get_n_pages():
 
1308
                self.notebook.set_current_page(N)
 
1309
            return True
 
1310
 
 
1311
        return callback
 
1312
 
 
1313
    def accel_rename_current_tab(self, *args):
927
1314
        """Callback to show the rename tab dialog. Called by the accel
928
1315
        key.
929
1316
        """
930
1317
        pagepos = self.notebook.get_current_page()
931
1318
        self.selected_tab = self.tabs.get_children()[pagepos]
932
 
        self.on_rename_activate()
 
1319
        self.on_rename_current_tab_activate()
933
1320
        return True
934
1321
 
935
1322
    def accel_copy_clipboard(self, *args):
976
1363
            self.toolbar.hide()
977
1364
 
978
1365
    def unfullscreen(self):
 
1366
        window_rect = self.get_final_window_rect()
979
1367
        self.window.unfullscreen()
980
1368
        self.is_fullscreen = False
 
1369
        self.window.resize(window_rect.width, window_rect.height)
 
1370
        self.window.move(window_rect.x, window_rect.y)
981
1371
 
982
1372
        # making sure that tabbar and resizer will come back to
983
1373
        # their default state.
1002
1392
        if not use_them:
1003
1393
            return
1004
1394
        page = self.notebook.page_num(box)
1005
 
        self.tabs.get_children()[page].set_label(vte.get_window_title())
 
1395
        tab = self.tabs.get_children()[page]
 
1396
        # if tab has been renamed by user, don't override.
 
1397
        if not getattr(tab, 'custom_label_set', False):
 
1398
            tab.set_label(vte.get_window_title())
1006
1399
 
1007
 
    def on_rename_activate(self, *args):
 
1400
    def on_rename_current_tab_activate(self, *args):
1008
1401
        """Shows a dialog to rename the current tab.
1009
1402
        """
1010
1403
        entry = gtk.Entry()
1035
1428
        self.disable_losefocus_hiding = False
1036
1429
 
1037
1430
        if response == gtk.RESPONSE_ACCEPT:
1038
 
            self.selected_tab.set_label(entry.get_text())
 
1431
            new_text = entry.get_text()
 
1432
            self.selected_tab.set_label(new_text)
 
1433
            # if user sets empty name, consider he wants default behavior.
 
1434
            setattr(self.selected_tab, 'custom_label_set', bool(new_text))
 
1435
            # trigger titling handler in case that custom label has been reset
 
1436
            current_vte = self.term_list[self.notebook.get_current_page()]
 
1437
            current_vte.emit('window-title-changed')
1039
1438
 
1040
1439
        dialog.destroy()
1041
1440
        self.set_terminal_focus()
1061
1460
        for uri in droppeduris:
1062
1461
            scheme, _, path, _, _ = urlsplit(uri)
1063
1462
 
1064
 
            if scheme!="file":
 
1463
            if scheme != "file":
1065
1464
                pathlist.append(uri)
1066
1465
            else:
1067
1466
                filename = url2pathname(path)
1073
1472
                    pathlist.append(filename)
1074
1473
                    continue
1075
1474
 
1076
 
                if desktopentry.getType()=='Link':
 
1475
                if desktopentry.getType() == 'Link':
1077
1476
                    pathlist.append(desktopentry.getURL())
1078
1477
 
1079
 
                if desktopentry.getType()=='Application':
 
1478
                if desktopentry.getType() == 'Application':
1080
1479
                    app = desktopentry.getExec()
1081
1480
 
1082
 
        if app and len(droppeduris)==1:
 
1481
        if app and len(droppeduris) == 1:
1083
1482
            text = app
1084
1483
        else:
1085
 
            text = str.join("", (shell_quote(path)+" " for path in pathlist))
 
1484
            text = str.join("", (shell_quote(path) + " " for path in pathlist))
1086
1485
 
1087
1486
        box.terminal.feed_child(text)
1088
1487
        return True
1095
1494
        pagepos = self.notebook.get_current_page()
1096
1495
        self.delete_tab(pagepos)
1097
1496
 
 
1497
    def rename_tab(self, tab_index, new_text):
 
1498
        """Rename an already added tab by its index.
 
1499
        """
 
1500
        try:
 
1501
            tab = self.tabs.get_children()[tab_index]
 
1502
        except IndexError:
 
1503
            pass
 
1504
        else:
 
1505
            tab.set_label(new_text)
 
1506
            setattr(tab, 'custom_label_set', new_text != "-")
 
1507
            current_vte = self.term_list[tab_index]
 
1508
            current_vte.emit('window-title-changed')
 
1509
 
1098
1510
    def rename_current_tab(self, new_text):
1099
1511
        """Sets the `self.selected_tab' var with the selected radio
1100
1512
        button and change its label to `new_text'.
1103
1515
        self.selected_tab = self.tabs.get_children()[pagepos]
1104
1516
        self.selected_tab.set_label(new_text)
1105
1517
 
 
1518
        # it's hard to pass an empty string as a command line argument,
 
1519
        # so we'll interpret single dash "-" as a "reset custom title" request
 
1520
        setattr(self.selected_tab, 'custom_label_set', new_text != "-")
 
1521
 
 
1522
        # trigger titling handler in case that custom label has been reset
 
1523
        current_vte = self.term_list[self.notebook.get_current_page()]
 
1524
        current_vte.emit('window-title-changed')
 
1525
 
1106
1526
    def get_current_dir(self):
1107
1527
        """Gets the working directory of the current tab to create a
1108
1528
        new one in the same dir.
1168
1588
                    proxy + 'authentication_user')
1169
1589
                auth_pass = self.client.get_string(
1170
1590
                    proxy + 'authentication_password')
 
1591
                auth_pass = quote_plus(auth_pass, '')
1171
1592
                os.environ['http_proxy'] = 'http://%s:%s@%s:%d' % (
1172
1593
                    auth_user, auth_pass, host, port)
1173
1594
                os.environ['https_proxy'] = 'http://%s:%s@%s:%d' % (
1185
1606
        box.terminal.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
1186
1607
                                   gtk.DEST_DEFAULT_DROP |
1187
1608
                                   gtk.DEST_DEFAULT_HIGHLIGHT,
1188
 
                                  [('text/uri-list', gtk.TARGET_OTHER_APP, 0)],
1189
 
                                  gtk.gdk.ACTION_COPY
1190
 
                                 )
 
1609
                                   [('text/uri-list', gtk.TARGET_OTHER_APP, 0)],
 
1610
                                   gtk.gdk.ACTION_COPY
 
1611
                                   )
1191
1612
        box.terminal.connect('button-press-event', self.show_context_menu)
1192
1613
        box.terminal.connect('child-exited', self.on_terminal_exited, box)
1193
1614
        box.terminal.connect('window-title-changed',
1195
1616
        box.terminal.connect('drag-data-received',
1196
1617
                             self.on_drag_data_received,
1197
1618
                             box)
 
1619
 
 
1620
        #-- Ubuntu has a patch to libvte which disables mouse scrolling in apps
 
1621
        #-- like vim and less by default. If this is the case, enable it back.
 
1622
        if hasattr(box.terminal, "set_alternate_screen_scroll"):
 
1623
            box.terminal.set_alternate_screen_scroll(True)
 
1624
 
1198
1625
        box.show()
1199
1626
 
1200
1627
        self.term_list.append(box.terminal)
1209
1636
 
1210
1637
        final_params = self.get_fork_params(default_params)
1211
1638
        pid = box.terminal.fork_command(**final_params)
 
1639
        box.terminal.pid = pid
1212
1640
        self.pid_list.append(pid)
1213
1641
 
1214
1642
        # Adding a new radio button to the tabbar
1220
1648
        bnt.set_property('can-focus', False)
1221
1649
        bnt.set_property('draw-indicator', False)
1222
1650
        bnt.connect('button-press-event', self.show_tab_menu)
1223
 
        bnt.connect('button-press-event', self.show_rename_dialog)
 
1651
        bnt.connect('button-press-event', self.show_rename_current_tab_dialog)
1224
1652
        bnt.connect('clicked',
1225
1653
                    lambda *x: self.notebook.set_current_page(
1226
1654
                        self.notebook.page_num(box)
1227
1655
                    ))
 
1656
        if self.selected_color is not None:
 
1657
            bnt.modify_bg(gtk.STATE_ACTIVE, gtk.gdk.Color(str(self.selected_color)))
 
1658
        drag_drop_type = ("text/plain", gtk.TARGET_SAME_APP, 80)
 
1659
        bnt.drag_dest_set(gtk.DEST_DEFAULT_ALL, [drag_drop_type], gtk.gdk.ACTION_MOVE)
 
1660
        bnt.connect("drag_data_received", self.on_drop_tab)
 
1661
        bnt.drag_source_set(gtk.gdk.BUTTON1_MASK, [drag_drop_type], gtk.gdk.ACTION_MOVE)
 
1662
        bnt.connect("drag_data_get", self.on_drag_tab)
1228
1663
        bnt.show()
1229
1664
 
1230
1665
        self.tabs.pack_start(bnt, expand=False, padding=1)
1234
1669
        box.terminal.grab_focus()
1235
1670
        self.load_config()
1236
1671
 
 
1672
        if self.is_fullscreen:
 
1673
            self.fullscreen()
 
1674
 
 
1675
    def on_drag_tab(self, widget, context, selection, targetType, eventTime):
 
1676
        tab_pos = self.tabs.get_children().index(widget)
 
1677
        selection.set(selection.target, 32, str(tab_pos))
 
1678
 
 
1679
    def on_drop_tab(self, widget, context, x, y, selection, targetType, data):
 
1680
        old_tab_pos = int(selection.get_text())
 
1681
        new_tab_pos = self.tabs.get_children().index(widget)
 
1682
        self.move_tab(old_tab_pos, new_tab_pos)
 
1683
 
 
1684
    def move_tab(self, old_tab_pos, new_tab_pos):
 
1685
        self.notebook.reorder_child(self.notebook.get_nth_page(old_tab_pos), new_tab_pos)
 
1686
        self.pid_list.insert(new_tab_pos, self.pid_list.pop(old_tab_pos))
 
1687
        self.term_list.insert(new_tab_pos, self.term_list.pop(old_tab_pos))
 
1688
        self.tabs.reorder_child(self.tabs.get_children()[old_tab_pos], new_tab_pos)
 
1689
 
1237
1690
    def delete_tab(self, pagepos, kill=True):
1238
1691
        """This function will destroy the notebook page, terminal and
1239
1692
        tab widgets and will call the function to kill interpreter
1259
1712
        call this in another thread. This doesn't change any thing in
1260
1713
        UI, so you can use python's start_new_thread.
1261
1714
        """
1262
 
        os.kill(pid, signal.SIGTERM)
 
1715
        try:
 
1716
            os.kill(pid, signal.SIGHUP)
 
1717
        except OSError:
 
1718
            pass
1263
1719
        num_tries = 30
1264
1720
 
1265
1721
        while num_tries > 0:
1312
1768
        self.selected_tab = self.tabs.get_children()[pagepos]
1313
1769
        return pagepos
1314
1770
 
 
1771
    def search_on_web(self, *args):
 
1772
        """search on web the selected text
 
1773
        """
 
1774
        current_term = self.term_list[self.notebook.get_current_page()]
 
1775
 
 
1776
        if current_term.get_has_selection():
 
1777
            current_term.copy_clipboard()
 
1778
            guake_clipboard = gtk.clipboard_get()
 
1779
            search_query = guake_clipboard.wait_for_text()
 
1780
            search_query = quote_plus(search_query)
 
1781
            if search_query:
 
1782
                search_url = "https://www.google.com/#q=%s&safe=off" % (search_query,)
 
1783
                gtk.show_uri(current_term.window.get_screen(), search_url,
 
1784
                             gtk.gdk.x11_get_server_time(current_term.window))
 
1785
        return True
 
1786
 
 
1787
 
1315
1788
def main():
1316
1789
    """Parses the command line parameters and decide if dbus methods
1317
1790
    should be called or not. If there is already a guake instance
1321
1794
    from optparse import OptionParser
1322
1795
    parser = OptionParser()
1323
1796
    parser.add_option('-f', '--fullscreen', dest='fullscreen',
1324
 
            action='store_true', default=False,
1325
 
            help=_('Put Guake in fullscreen mode'))
 
1797
                      action='store_true', default=False,
 
1798
                      help=_('Put Guake in fullscreen mode'))
1326
1799
 
1327
1800
    parser.add_option('-t', '--toggle-visibility', dest='show_hide',
1328
 
            action='store_true', default=False,
1329
 
            help=_('Toggles the visibility of the terminal window'))
 
1801
                      action='store_true', default=False,
 
1802
                      help=_('Toggles the visibility of the terminal window'))
 
1803
 
 
1804
    parser.add_option('--show', dest="show",
 
1805
                      action='store_true', default=False,
 
1806
                      help=_('Shows Guake main window'))
 
1807
 
 
1808
    parser.add_option('--hide', dest='hide',
 
1809
                      action='store_true', default=False,
 
1810
                      help=_('Hides Guake main window'))
1330
1811
 
1331
1812
    parser.add_option('-p', '--preferences', dest='show_preferences',
1332
 
            action='store_true', default=False,
1333
 
            help=_('Shows Guake preference window'))
 
1813
                      action='store_true', default=False,
 
1814
                      help=_('Shows Guake preference window'))
1334
1815
 
1335
1816
    parser.add_option('-a', '--about', dest='show_about',
1336
 
            action='store_true', default=False,
1337
 
            help=_('Shows Guake\'s about info'))
 
1817
                      action='store_true', default=False,
 
1818
                      help=_('Shows Guake\'s about info'))
1338
1819
 
1339
1820
    parser.add_option('-n', '--new-tab', dest='new_tab',
1340
 
            action='store', default='',
1341
 
            help=_('Add a new tab'))
 
1821
                      action='store', default='',
 
1822
                      help=_('Add a new tab (with current directory set to NEW_TAB)'))
1342
1823
 
1343
1824
    parser.add_option('-s', '--select-tab', dest='select_tab',
1344
 
            action='store', default='',
1345
 
            help=_('Select a tab'))
 
1825
                      action='store', default='',
 
1826
                      help=_('Select a tab (SELECT_TAB is the index of the tab)'))
1346
1827
 
1347
1828
    parser.add_option('-g', '--selected-tab', dest='selected_tab',
1348
 
            action='store_true', default=False,
1349
 
            help=_('Return the selectd tab index.'))
 
1829
                      action='store_true', default=False,
 
1830
                      help=_('Return the selected tab index.'))
1350
1831
 
1351
1832
    parser.add_option('-e', '--execute-command', dest='command',
1352
 
            action='store', default='',
1353
 
            help=_('Execute an arbitrary command in the selected tab.'))
1354
 
 
1355
 
    parser.add_option('-r', '--rename-tab', dest='rename_tab',
1356
 
            action='store', default='',
1357
 
            help=_('Rename the selected tab.'))
 
1833
                      action='store', default='',
 
1834
                      help=_('Execute an arbitrary command in the selected tab.'))
 
1835
 
 
1836
    parser.add_option('-i', '--tab-index', dest='tab_index',
 
1837
                      action='store', default='0',
 
1838
                      help=_('Specify the tab to rename. Default is 0.'))
 
1839
 
 
1840
    parser.add_option('--bgcolor', dest='bgcolor',
 
1841
                      action='store', default='',
 
1842
                      help=_('Set the hexadecimal (#rrggbb) background color of the selected tab.'))
 
1843
 
 
1844
    parser.add_option('--fgcolor', dest='fgcolor',
 
1845
                      action='store', default='',
 
1846
                      help=_('Set the hexadecimal (#rrggbb) foreground color of the selected tab.'))
 
1847
 
 
1848
    parser.add_option('--rename-tab', dest='rename_tab',
 
1849
                      metavar='TITLE',
 
1850
                      action='store', default='',
 
1851
                      help=_('Rename the specified tab. Reset to default if TITLE is a single dash "-".'))
 
1852
 
 
1853
    parser.add_option('-r', '--rename-current-tab', dest='rename_current_tab',
 
1854
                      metavar='TITLE',
 
1855
                      action='store', default='',
 
1856
                      help=_('Rename the current tab. Reset to default if TITLE is a single dash "-".'))
1358
1857
 
1359
1858
    parser.add_option('-q', '--quit', dest='quit',
1360
 
            action='store_true', default=False,
1361
 
            help=_('Says to Guake go away =('))
 
1859
                      action='store_true', default=False,
 
1860
                      help=_('Says to Guake go away =('))
1362
1861
 
1363
1862
    options = parser.parse_args()[0]
1364
1863
 
1379
1878
    if options.fullscreen:
1380
1879
        instance.fullscreen()
1381
1880
 
 
1881
    if options.show:
 
1882
        remote_object.show()
 
1883
 
 
1884
    if options.hide:
 
1885
        remote_object.hide()
 
1886
 
1382
1887
    if options.show_preferences:
1383
1888
        remote_object.show_prefs()
1384
1889
        only_show_hide = False
1401
1906
        remote_object.execute_command(options.command)
1402
1907
        only_show_hide = False
1403
1908
 
1404
 
    if options.rename_tab:
1405
 
        remote_object.rename_current_tab(options.rename_tab)
 
1909
    if options.tab_index and options.rename_tab:
 
1910
        remote_object.rename_tab(int(options.tab_index), options.rename_tab)
 
1911
 
 
1912
    if options.bgcolor:
 
1913
        remote_object.set_bgcolor(options.bgcolor)
 
1914
        only_show_hide = False
 
1915
 
 
1916
    if options.fgcolor:
 
1917
        remote_object.set_fgcolor(options.fgcolor)
 
1918
        only_show_hide = False
 
1919
 
 
1920
    if options.rename_current_tab:
 
1921
        remote_object.rename_current_tab(options.rename_current_tab)
1406
1922
        only_show_hide = False
1407
1923
 
1408
1924
    if options.show_about:
1418
1934
        # it is already running, so, lets toggle its visibility.
1419
1935
        remote_object.show_hide()
1420
1936
 
 
1937
    if not already_running:
 
1938
        startup_script = instance.client.get_string(KEY("/general/startup_script"))
 
1939
        if startup_script:
 
1940
            print "Calling startup script: ", startup_script
 
1941
            pid = subprocess.Popen([startup_script], shell=True, stdin=None, stdout=None,
 
1942
                                   stderr=None, close_fds=True)
 
1943
            print "Script started with pid", pid
 
1944
            # Please ensure this is the last line !!!!
1421
1945
    return already_running
1422
1946
 
1423
1947
if __name__ == '__main__':
1424
1948
    if not test_gconf():
1425
1949
        raise ShowableError(_('Guake can not init!'),
1426
 
            _('Gconf Error.\n'
1427
 
              'Have you installed <b>guake.schemas</b> properly?'))
 
1950
                            _('Gconf Error.\n'
 
1951
                              'Have you installed <b>guake.schemas</b> properly?'))
1428
1952
 
1429
1953
    if not main():
1430
1954
        gtk.main()