GtkConsole.py 7.53 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# -*- coding: utf-8 -*-
"""
$Id$

Copyright 2012 Lars Kruse <devel@sumpfralle.de>

This file is part of PyCAM.

PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PyCAM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PyCAM.  If not, see <http://www.gnu.org/licenses/>.
"""

sumpfralle's avatar
sumpfralle committed
23 24
import os
import sys
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
import code
# gtk is imported later
#import gtk
import StringIO

import pycam.Plugins
import pycam.Utils.log

_log = pycam.Utils.log.get_logger()


class GtkConsole(pycam.Plugins.PluginBase):

    UI_FILE = "gtk_console.ui"
    DEPENDS = ["Clipboard"]
    CATEGORIES = ["System"]

sumpfralle's avatar
sumpfralle committed
42 43 44 45
    # sys.ps1 and sys.ps2 don't seem to be available outside of the shell
    PROMPT_PS1 = ">>> "
    PROMPT_PS2 = "... "

46 47 48 49 50 51 52 53 54 55 56 57 58
    def setup(self):
        self._history = []
        self._history_position = None
        if self.gui:
            import gtk
            self._gtk = gtk
            self._console = code.InteractiveConsole(
                    locals=self.core.get_namespace(), filename="PyCAM")
            self._console_output = StringIO.StringIO()
            # TODO: clean up this stdin/stdout mess (maybe subclass "sys"?)
            code.sys.stdout = self._console_output
            code.sys.stdin = StringIO.StringIO()
            self._console_buffer = self.gui.get_object("ConsoleViewBuffer")
sumpfralle's avatar
sumpfralle committed
59 60
            def console_write(data):
                self._console_buffer.insert(
61
                        self._console_buffer.get_end_iter(), data)
sumpfralle's avatar
sumpfralle committed
62 63 64 65
                self._console_buffer.place_cursor(
                        self._console_buffer.get_end_iter())
            self._console.write  = console_write
            self._clear_console()
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
            console_action = self.gui.get_object("ToggleConsoleWindow")
            self.register_gtk_accelerator("console", console_action, None,
                    "ToggleConsoleWindow")
            self.core.register_ui("view_menu", "ToggleConsoleWindow",
                    console_action, 90)
            self._window = self.gui.get_object("ConsoleDialog")
            self._window_position = None
            self._gtk_handlers = []
            hide_window = lambda *args: self._toggle_window(value=False)
            for objname, signal, func in (
                    ("ConsoleExecuteButton", "clicked", self._execute_command),
                    ("CommandInput", "activate", self._execute_command),
                    ("CopyConsoleButton", "clicked", self._copy_to_clipboard),
                    ("WipeConsoleButton", "clicked", self._clear_console),
                    ("CommandInput", "key-press-event", self._scroll_history),
                    ("ToggleConsoleWindow", "toggled", self._toggle_window),
                    ("CloseConsoleButton", "clicked", hide_window),
83
                    ("ConsoleDialog", "delete-event", hide_window),
84 85 86 87 88 89 90 91 92 93 94 95 96
                    ("ConsoleDialog", "destroy", hide_window)):
                self._gtk_handlers.append((self.gui.get_object(objname),
                        signal, func))
            self.register_gtk_handlers(self._gtk_handlers)
        return True

    def teardown(self):
        if self.gui:
            self.unregister_gtk_handlers(self._gtk_handlers)

    def _hide_window(self, widget=None, event=None):
        self.gui.get_object("ConsoleDialog").hide()
        # don't close window (for "destroy" event)
sumpfralle's avatar
sumpfralle committed
97
        return True
98 99 100 101

    def _clear_console(self, widget=None):
        start, end = self._console_buffer.get_bounds()
        self._console_buffer.delete(start, end)
sumpfralle's avatar
sumpfralle committed
102
        self._console.write(self.PROMPT_PS1)
103 104 105 106 107 108 109

    def _execute_command(self, widget=None):
        input_control = self.gui.get_object("CommandInput")
        text = input_control.get_text()
        if not text:
            return
        input_control.set_text("")
sumpfralle's avatar
sumpfralle committed
110 111
        # add the command to the console window
        self._console.write(text + os.linesep)
112 113 114 115 116
        # execute command - check if it needs more input
        if not self._console.push(text):
            # append result to console view
            self._console_output.seek(0)
            for line in self._console_output.readlines():
sumpfralle's avatar
sumpfralle committed
117
                self._console.write(line)
118 119 120 121 122
            # scroll down console view to the end of the buffer
            view = self.gui.get_object("ConsoleView")
            view.scroll_mark_onscreen(self._console_buffer.get_insert())
            # clear the buffer
            self._console_output.truncate(0)
sumpfralle's avatar
sumpfralle committed
123 124 125 126 127
            # show the prompt again
            self._console.write(self.PROMPT_PS1)
        else:
            # show the "waiting for more" prompt
            self._console.write(self.PROMPT_PS2)
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
        # add to history
        if not self._history or (text != self._history[-1]):
            self._history.append(text)
        self._history_position = None

    def _copy_to_clipboard(self, widget=None):
        start, end = self._console_buffer.get_bounds()
        content = self._console_buffer.get_text(start, end)
        self.core.get("clipboard-set")(content)

    def _toggle_window(self, widget=None, value=None, action=None):
        toggle_checkbox = self.gui.get_object("ToggleConsoleWindow")
        checkbox_state = toggle_checkbox.get_active()
        if value is None:
            new_state = checkbox_state
        elif action is None:
            new_state = value
        else:
            new_state = action
        if new_state:
            if self._window_position:
                self._window.move(*self._window_position)
            self._window.show()
        else:
            self._window_position = self._window.get_position()
            self._window.hide()
        toggle_checkbox.set_active(new_state)
        return True

    def _scroll_history(self, widget=None, event=None):
        if event is None:
159
            return False
160 161 162 163
        try:
            keyval = getattr(event, "keyval")
            get_state = getattr(event, "get_state")
        except AttributeError:
164
            return False
165 166
        if get_state():
            # ignore, if any modifier is pressed
167
            return False
168 169 170 171 172 173 174 175 176 177 178
        input_control = self.gui.get_object("CommandInput")
        if (keyval == self._gtk.keysyms.Up):
            if self._history_position is None:
                # store the current (new) line for later
                self._history_lastline_backup = input_control.get_text()
                # start with the last item
                self._history_position = len(self._history) - 1
            elif self._history_position > 0:
                self._history_position -= 1
            else:
                # invalid -> no change
179
                return True
180 181
        elif (keyval == self._gtk.keysyms.Down):
            if self._history_position is None:
182
                return True
183 184 185
            self._history_position += 1
        else:
            # all other keys: ignore
186
            return False
187 188 189 190 191 192 193 194 195
        if self._history_position >= len(self._history):
            input_control.set_text(self._history_lastline_backup)
            # make sure that the backup can be stored again
            self._history_position = None
        else:
            input_control.set_text(self._history[self._history_position])
        # move the cursor to the end of the new text
        input_control.set_position(0)
        input_control.grab_focus()
196
        return True
197