# A quake terminal for Sugar # v0.06 by 31d1 31d1@31d1.com # with some code ganked from terminal.xo, isforinsects, and supersat # Released under the MIT license, shown below. # # Copyright (c) 2008 31d1 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # How to install: # # put this file at: # /usr/share/sugar/shell/view/quake.py # # Now we need to edit keyhandler.py in the same folder. # Make a backup first. Any errors or typos will keep X from starting, # and you will have to switch to a virtual terminal and revert. # # Open keyhandler.py in an editor. # First we import quake.py as a module. add: # # import quake # # to the list of modules at the top. # # Next, we add our hotkey. Add a pair to the _actions_table. # Before the line: # # 'F1' : 'zoom_mesh', # # add the line: # # 'Down' : 'quake_term', # # (or whatever hotkey you want). # # Finally, we add a function to actually handle the hotkey. # Above the _change_volume() function, add this function: # # def handle_quake_term(self): # quake_term = quake.get_quake() # quake_term.toggle_visible() # # Save changes and restart X, and if it starts: # # Ctrl-Down: will toggle a fullscreen terminal. # Ctrl-t: open a new tab # Ctrl-Left/Right: switch between tabs # Ctrl-Shift-v/Right Click: paste # TODO # better tab titles import pygtk pygtk.require('2.0') import gtk import os.path import ConfigParser import vte import pango from gtk.gdk import CONTROL_MASK, keyval_name from sugar import env class Quake: def __init__(self): self.term_opts() self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.connect('key_press_event', self.key_press_cb) self.window.connect('delete_event', lambda a, b: True) self.notebook = gtk.Notebook() self.notebook.connect('switch_page',self.switch_page_cb) self.add_tab() self.notebook.set_show_tabs(False) self.window.add(self.notebook) self.page = self.notebook.get_current_page() def term_opts(self): self.conf = ConfigParser.ConfigParser() self.conf.read(os.path.join(env.get_profile_path(), 'terminalrc')) defaults = { 'font' : 'Monospace 8', 'fg_color' : '#000000', 'bg_color' : '#FFFFFF', 'allow_bold' : 'True', 'cursor_blink' : 'False', 'scrollback_lines' : '1000', 'scroll_on_keystroke' : 'False', 'scroll_on_output' : 'False', 'emulation' : 'xterm', 'bell' : 'False', 'visible_bell' : 'False' } if not self.conf.has_section('terminal'): self.conf.add_section('terminal') for option in defaults: if not self.conf.has_option('terminal', option): self.conf.set('terminal', option, defaults[option]) def key_press_cb(self, widget, event): if event.state & CONTROL_MASK: lastpage = self.notebook.get_n_pages() - 1 if keyval_name(event.keyval) == "t": self.add_tab() self.window.show_all() self.notebook.next_page() return True elif keyval_name(event.keyval) == "V": self.paste() return True elif keyval_name(event.keyval) == "Left": if self.notebook.get_current_page() == 0: self.notebook.set_current_page(lastpage) else: self.notebook.prev_page() return True elif keyval_name(event.keyval) == "Right": if self.notebook.get_current_page() == lastpage: self.notebook.set_current_page(0) else: self.notebook.next_page() return True def button_press_cb(self, widget, event): if event.button == 3: self.paste() return True def switch_page_cb(self, notebook, pg, n): old = notebook.get_nth_page(notebook.get_current_page()) new = notebook.get_nth_page(n) notebook.set_tab_label_text(old,'') notebook.set_tab_label_text(new,'[ ' + str(n + 1) + ' ]') return True def paste(self): cb = gtk.clipboard_get(selection = "PRIMARY").wait_for_text() if cb: self.page = self.notebook.get_current_page() self.notebook.get_nth_page(self.page).feed_child(cb) return True def rm_tab(self, child): if self.notebook.get_n_pages() > 2: child.destroy() elif self.notebook.get_n_pages() == 2: child.destroy() self.notebook.set_show_tabs(False) else: child.fork_command() def add_tab(self): terminal = vte.Terminal() terminal.connect("child-exited", self.rm_tab) terminal.connect("button-press-event", self.button_press_cb) terminal.set_allow_bold( self.conf.getboolean('terminal', 'allow_bold')) terminal.set_cursor_blinks( self.conf.getboolean('terminal', 'cursor_blink')) terminal.set_scrollback_lines( self.conf.getint('terminal', 'scrollback_lines')) terminal.set_scroll_on_keystroke( self.conf.getboolean('terminal', 'scroll_on_keystroke')) terminal.set_scroll_on_output( self.conf.getboolean('terminal', 'scroll_on_output')) terminal.set_emulation( self.conf.get('terminal', 'emulation')) terminal.set_audible_bell( self.conf.getboolean('terminal', 'bell')) terminal.set_visible_bell( self.conf.getboolean('terminal', 'visible_bell')) terminal.set_font( pango.FontDescription(self.conf.get('terminal', 'font'))) terminal.set_colors( gtk.gdk.color_parse(self.conf.get('terminal', 'fg_color')), gtk.gdk.color_parse(self.conf.get('terminal', 'bg_color')), []) child_pid = terminal.fork_command() self.notebook.append_page(terminal, gtk.Label('')) self.notebook.set_tab_label_packing( terminal, True, True, gtk.PACK_START) self.notebook.set_show_tabs(True) def toggle_visible(self): if self.window.get_property('visible'): self.page = self.notebook.get_current_page() self.window.hide_all() else: self.window.show_all() self.notebook.set_current_page(self.page) pg = self.notebook.get_nth_page(self.notebook.get_current_page()) pg.grab_focus() return True def main(self): gtk.main() def get_quake(): return _quake _quake = Quake()