Ejemplo n.º 1
0
def zoom_height(top):
    geom = top.wm_geometry()
    m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
    if not m:
        top.bell()
        return
    width, height, x, y = map(int, m.groups())
    newheight = top.winfo_screenheight()
    if sys.platform == 'win32':
        newy = 0
        newheight = newheight - 72

    elif macosxSupport.runningAsOSXApp():
        # The '88' below is a magic number that avoids placing the bottom
        # of the window below the panel on my machine. I don't know how
        # to calculate the correct value for this with tkinter.
        newy = 22
        newheight = newheight - newy - 88

    else:
        #newy = 24
        newy = 0
        #newheight = newheight - 96
        newheight = newheight - 88
    if height >= newheight:
        newgeom = ""
    else:
        newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy)
    top.wm_geometry(newgeom)
Ejemplo n.º 2
0
def zoom_height(top):
    geom = top.wm_geometry()
    m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
    if not m:
        top.bell()
        return
    width, height, x, y = map(int, m.groups())
    newheight = top.winfo_screenheight()
    if sys.platform == 'win32':
        newy = 0
        newheight = newheight - 72

    elif macosxSupport.runningAsOSXApp():
        # The '88' below is a magic number that avoids placing the bottom
        # of the window below the panel on my machine. I don't know how
        # to calculate the correct value for this with tkinter.
        newy = 22
        newheight = newheight - newy - 88

    else:
        #newy = 24
        newy = 0
        #newheight = newheight - 96
        newheight = newheight - 88
    if height >= newheight:
        newgeom = ""
    else:
        newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy)
    top.wm_geometry(newgeom)
Ejemplo n.º 3
0
 def _create_statusbar(self):
     self.status_bar = MultiStatusBar(self.top)
     if macosxSupport.runningAsOSXApp():
         # Insert some padding to avoid obscuring some of the statusbar
         # by the resize widget.
         self.status_bar.set_label('_padding1', '    ', side=RIGHT)
     self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
     self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
     self.status_bar.pack(side=BOTTOM, fill=X)
Ejemplo n.º 4
0
 def _create_statusbar(self):
     self.status_bar = MultiStatusBar(self.top)
     if macosxSupport.runningAsOSXApp():
         # Insert some padding to avoid obscuring some of the statusbar
         # by the resize widget.
         self.status_bar.set_label('_padding1', '    ', side=RIGHT)
     self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
     self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
     self.status_bar.pack(side=BOTTOM, fill=X)
Ejemplo n.º 5
0
    def GetCurrentKeySet(self):
        result = self.GetKeySet(self.CurrentKeys())

        if macosxSupport.runningAsOSXApp():
            # We're using AquaTk, replace all keybingings that use the
            # Alt key by ones that use the Option key because the former
            # don't work reliably.
            for k, v in result.items():
                v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
                if v != v2:
                    result[k] = v2

        return result
Ejemplo n.º 6
0
    def GetCurrentKeySet(self):
        result = self.GetKeySet(self.CurrentKeys())

        if macosxSupport.runningAsOSXApp():
            # We're using AquaTk, replace all keybingings that use the
            # Alt key by ones that use the Option key because the former
            # don't work reliably.
            for k, v in result.items():
                v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
                if v != v2:
                    result[k] = v2

        return result
Ejemplo n.º 7
0
    def _setup_bindings(self):
        text = self.text

        def bind_them(to_bind, prefix='_%s'):
            for tb in to_bind:
                prefix_size = tb.count('<')
                method_name = tb[prefix_size:-prefix_size].replace('-', '_')
                text.bind(tb, getattr(self, prefix % method_name.lower()))

        actions = ('<<help>>', '<<python-docs>>', '<<about-idle>>',
                   '<<open-config-dialog>>', '<<open-module>>', '<<cut>>',
                   '<<copy>>', '<<paste>>', '<<select-all>>',
                   '<<remove-selection>>', '<<del-word-left>>',
                   '<<del-word-right>>', '<<beginning-of-line>>')
        events = ('<<find>>', '<<center-insert>>', '<<find-again>>',
                  '<<find-in-files>>', '<<find-selection>>', '<<replace>>',
                  '<<goto-line>>', '<<smart-backspace>>', '<<smart-indent>>',
                  '<<indent-region>>', '<<dedent-region>>',
                  '<<comment-region>>', '<<tabify-region>>',
                  '<<untabify-region>>', '<<toggle-tabs>>',
                  '<<change-indentwidth>>')
        parent_actions = ('<<new-tab>>', '<<next-tab>>', '<<prev-tab>>')

        bind_them(actions)
        bind_them(events, prefix="_%s_event")
        for action in parent_actions:
            prefix_size = action.count('<')
            method_name = action[prefix_size:-prefix_size].replace('-', '_')
            text.bind(action, getattr(self.editwin, method_name))

        text.bind('<<close-tab>>', self.close_tab)
        text.bind('<<newline-and-indent>>', self.newline_and_indent_event)
        text.bind("<<do-nothing>>", lambda event: "break")
        text.bind("<Left>", self._move_at_edge_if_selection(0))
        text.bind("<Right>", self._move_at_edge_if_selection(1))
        text.bind("<3>", self._right_menu)
        text.bind('<<set-line-and-column>>', self.editwin.set_line_and_column)
        text.event_add("<<set-line-and-column>>", "<KeyRelease>",
                       "<ButtonRelease>")

        if self.editwin.flist:
            text.bind("<<open-new-window>>",
                      utils.callback(self.editwin.new_callback, self))
            text.bind("<<close-all-windows>>",
                      self.editwin.flist.close_all_callback)
            text.bind("<<open-class-browser>>", self._open_class_browser)
            text.bind("<<open-path-browser>>", self._open_path_browser)

        if macosxSupport.runningAsOSXApp():
            # Command-W on editorwindows doesn't work without this.
            text.bind('<<close-window>>', self.editwin.close_event)
Ejemplo n.º 8
0
    def SetModifiersForPlatform(self):
        """Determine list of names of key modifiers for this platform.

        The names are used to build Tk bindings -- it doesn't matter if the
        keyboard has these keys, it matters if Tk understands them. The
        order is also important: key binding equality depends on it, so
        config-keys.def must use the same ordering.
        """
        import macosxSupport
        if macosxSupport.runningAsOSXApp():
            self.modifiers = ['Shift', 'Control', 'Option', 'Command']
        else:
            self.modifiers = ['Control', 'Alt', 'Shift']
        self.modifier_label = {'Control': 'Ctrl'} # short name
Ejemplo n.º 9
0
    def _setup_bindings(self):
        text = self.text
        def bind_them(to_bind, prefix='_%s'):
            for tb in to_bind:
                prefix_size = tb.count('<')
                method_name = tb[prefix_size:-prefix_size].replace('-', '_')
                text.bind(tb, getattr(self, prefix % method_name.lower()))
            
        actions = ('<<help>>', '<<python-docs>>',
            '<<about-idle>>', '<<open-config-dialog>>', '<<open-module>>',
            '<<cut>>', '<<copy>>', '<<paste>>', '<<select-all>>',
            '<<remove-selection>>', '<<del-word-left>>', '<<del-word-right>>',
            '<<beginning-of-line>>')
        events = ('<<find>>', '<<center-insert>>', '<<find-again>>',
            '<<find-in-files>>', '<<find-selection>>', '<<replace>>',
            '<<goto-line>>', '<<smart-backspace>>', '<<smart-indent>>',
            '<<indent-region>>', '<<dedent-region>>', '<<comment-region>>',
            '<<tabify-region>>', '<<untabify-region>>', '<<toggle-tabs>>',
            '<<change-indentwidth>>')
        parent_actions = ('<<new-tab>>', '<<next-tab>>', '<<prev-tab>>')

        bind_them(actions)
        bind_them(events, prefix="_%s_event")
        for action in parent_actions:
            prefix_size = action.count('<')
            method_name = action[prefix_size:-prefix_size].replace('-', '_')
            text.bind(action, getattr(self.editwin, method_name))

        text.bind('<<close-tab>>', self.close_tab)
        text.bind('<<newline-and-indent>>', self.newline_and_indent_event)
        text.bind("<<do-nothing>>", lambda event: "break")
        text.bind("<Left>", self._move_at_edge_if_selection(0))
        text.bind("<Right>", self._move_at_edge_if_selection(1))
        text.bind("<3>", self._right_menu)
        text.bind('<<set-line-and-column>>', self.editwin.set_line_and_column)
        text.event_add("<<set-line-and-column>>",
                                    "<KeyRelease>", "<ButtonRelease>")

        if self.editwin.flist:
            text.bind("<<open-new-window>>",
                utils.callback(self.editwin.new_callback, self))
            text.bind("<<close-all-windows>>",
                self.editwin.flist.close_all_callback)
            text.bind("<<open-class-browser>>", self._open_class_browser)
            text.bind("<<open-path-browser>>", self._open_path_browser)

        if macosxSupport.runningAsOSXApp():
            # Command-W on editorwindows doesn't work without this.
            text.bind('<<close-window>>', self.editwin.close_event)
Ejemplo n.º 10
0
    root.withdraw()
    flist = PyShellFileList(root)
    macosxSupport.setupApp(root, flist)

    if enable_edit:
        if not (cmd or script):
            for filename in args:
                flist.open(filename)
            if not args:
                flist.new()
    if enable_shell:
        shell = flist.open_shell()
        if not shell:
            return  # couldn't open shell

        if macosxSupport.runningAsOSXApp() and flist.dict:
            # On OSX: when the user has double-clicked on a file that causes
            # IDLE to be launched the shell window will open just in front of
            # the file she wants to see. Lower the interpreter window when
            # there are open files.
            shell.top.lower()

    shell = flist.pyshell
    # handle remaining options:
    if debug:
        shell.open_debugger()
    if startup:
        filename = os.environ.get("IDLESTARTUP") or \
                   os.environ.get("PYTHONSTARTUP")
        if filename and os.path.isfile(filename):
            shell.interp.execfile(filename)
Ejemplo n.º 11
0
import re
import Tkinter
import macosxSupport

# the event type constants, which define the meaning of mc_type
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
# the modifier state constants, which define the meaning of mc_state
MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
MC_OPTION = 1<<6; MC_COMMAND = 1<<7

# define the list of modifiers, to be used in complex event types.
if macosxSupport.runningAsOSXApp():
    _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
    _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
else:
    _modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M"))
    _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)

# a dictionary to map a modifier name into its number
_modifier_names = dict([(name, number)
                         for number in range(len(_modifiers))
                         for name in _modifiers[number]])

# A binder is a class which binds functions to one type of event. It has two
# methods: bind and unbind, which get a function and a parsed sequence, as
# returned by _parse_sequence(). There are two types of binders:
# _SimpleBinder handles event types with no modifiers and no detail.
Ejemplo n.º 12
0
    root.withdraw()
    flist = PyShellFileList(root)
    macosxSupport.setupApp(root, flist)

    if enable_edit:
        if not (cmd or script):
            for filename in args:
                flist.open(filename)
            if not args:
                flist.new()
    if enable_shell:
        shell = flist.open_shell()
        if not shell:
            return # couldn't open shell

        if macosxSupport.runningAsOSXApp() and flist.dict:
            # On OSX: when the user has double-clicked on a file that causes
            # IDLE to be launched the shell window will open just in front of
            # the file she wants to see. Lower the interpreter window when
            # there are open files.
            shell.top.lower()

    shell = flist.pyshell
    # handle remaining options:
    if debug:
        shell.open_debugger()
    if startup:
        filename = os.environ.get("IDLESTARTUP") or \
                   os.environ.get("PYTHONSTARTUP")
        if filename and os.path.isfile(filename):
            shell.interp.execfile(filename)
Ejemplo n.º 13
0
MC_MOTION = 17
MC_MOUSEWHEEL = 18
MC_PROPERTY = 19
MC_REPARENT = 20
MC_UNMAP = 21
MC_VISIBILITY = 22
# the modifier state constants, which define the meaning of mc_state
MC_SHIFT = 1 << 0
MC_CONTROL = 1 << 2
MC_ALT = 1 << 3
MC_META = 1 << 5
MC_OPTION = 1 << 6
MC_COMMAND = 1 << 7

# define the list of modifiers, to be used in complex event types.
if macosxSupport.runningAsOSXApp():
    _modifiers = (("Shift", ), ("Control", ), ("Option", ), ("Command", ))
    _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
else:
    _modifiers = (("Control", ), ("Alt", ), ("Shift", ), ("Meta", "M"))
    _modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)

# a dictionary to map a modifier name into its number
_modifier_names = dict([(name, number) for number in range(len(_modifiers))
                        for name in _modifiers[number]])

# A binder is a class which binds functions to one type of event. It has two
# methods: bind and unbind, which get a function and a parsed sequence, as
# returned by _parse_sequence(). There are two types of binders:
# _SimpleBinder handles event types with no modifiers and no detail.
# No Python functions are called when no events are binded.
Ejemplo n.º 14
0
class PyShell(OutputWindow):

    shell_title = "Python Shell"

    # Override classes
    ColorDelegator = ModifiedColorDelegator
    UndoDelegator = ModifiedUndoDelegator

    # Override menus
    menu_specs = [
        ("file", "_File"),
        ("edit", "_Edit"),
        ("debug", "_Debug"),
        ("options", "_Options"),
        ("windows", "_Windows"),
        ("help", "_Help"),
    ]

    if macosxSupport.runningAsOSXApp():
        del menu_specs[-3]
        menu_specs[-2] = ("windows", "_Window")

    # New classes
    from IdleHistory import History

    def __init__(self, flist=None):
        if use_subprocess:
            ms = self.menu_specs
            if ms[2][0] != "shell":
                ms.insert(2, ("shell", "She_ll"))
        self.interp = ModifiedInterpreter(self)
        if flist is None:
            root = Tk()
            fixwordbreaks(root)
            root.withdraw()
            flist = PyShellFileList(root)
        #
        OutputWindow.__init__(self, flist, None, None)
        #
        ##        self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
        self.usetabs = True
        # indentwidth must be 8 when using tabs.  See note in EditorWindow:
        self.indentwidth = 8
        self.context_use_ps1 = True
        #
        text = self.text
        text.configure(wrap="char")
        text.bind("<<newline-and-indent>>", self.enter_callback)
        text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
        text.bind("<<interrupt-execution>>", self.cancel_callback)
        text.bind("<<end-of-file>>", self.eof_callback)
        text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
        text.bind("<<toggle-debugger>>", self.toggle_debugger)
        text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
        if use_subprocess:
            text.bind("<<view-restart>>", self.view_restart_mark)
            text.bind("<<restart-shell>>", self.restart_shell)
        #
        self.save_stdout = sys.stdout
        self.save_stderr = sys.stderr
        self.save_stdin = sys.stdin
        import IOBinding
        self.stdout = PseudoFile(self, "stdout", IOBinding.encoding)
        self.stderr = PseudoFile(self, "stderr", IOBinding.encoding)
        self.console = PseudoFile(self, "console", IOBinding.encoding)
        if not use_subprocess:
            sys.stdout = self.stdout
            sys.stderr = self.stderr
            sys.stdin = self
        #
        self.history = self.History(self.text)
        #
        self.pollinterval = 50  # millisec

    def get_standard_extension_names(self):
        return idleConf.GetExtensions(shell_only=True)

    reading = False
    executing = False
    canceled = False
    endoffile = False
    closing = False

    def set_warning_stream(self, stream):
        global warning_stream
        warning_stream = stream

    def get_warning_stream(self):
        return warning_stream

    def toggle_debugger(self, event=None):
        if self.executing:
            tkMessageBox.showerror(
                "Don't debug now",
                "You can only toggle the debugger when idle",
                master=self.text)
            self.set_debugger_indicator()
            return "break"
        else:
            db = self.interp.getdebugger()
            if db:
                self.close_debugger()
            else:
                self.open_debugger()

    def set_debugger_indicator(self):
        db = self.interp.getdebugger()
        self.setvar("<<toggle-debugger>>", not not db)

    def toggle_jit_stack_viewer(self, event=None):
        pass  # All we need is the variable

    def close_debugger(self):
        db = self.interp.getdebugger()
        if db:
            self.interp.setdebugger(None)
            db.close()
            if self.interp.rpcclt:
                RemoteDebugger.close_remote_debugger(self.interp.rpcclt)
            self.resetoutput()
            self.console.write("[DEBUG OFF]\n")
            sys.ps1 = ">>> "
            self.showprompt()
        self.set_debugger_indicator()

    def open_debugger(self):
        if self.interp.rpcclt:
            dbg_gui = RemoteDebugger.start_remote_debugger(
                self.interp.rpcclt, self)
        else:
            dbg_gui = Debugger.Debugger(self)
        self.interp.setdebugger(dbg_gui)
        dbg_gui.load_breakpoints()
        sys.ps1 = "[DEBUG ON]\n>>> "
        self.showprompt()
        self.set_debugger_indicator()

    def beginexecuting(self):
        "Helper for ModifiedInterpreter"
        self.resetoutput()
        self.executing = 1

    def endexecuting(self):
        "Helper for ModifiedInterpreter"
        self.executing = 0
        self.canceled = 0
        self.showprompt()

    def close(self):
        "Extend EditorWindow.close()"
        if self.executing:
            response = tkMessageBox.askokcancel(
                "Kill?",
                "The program is still running!\n Do you want to kill it?",
                default="ok",
                parent=self.text)
            if response is False:
                return "cancel"
        if self.reading:
            self.top.quit()
        self.canceled = True
        self.closing = True
        # Wait for poll_subprocess() rescheduling to stop
        self.text.after(2 * self.pollinterval, self.close2)

    def close2(self):
        return EditorWindow.close(self)

    def _close(self):
        "Extend EditorWindow._close(), shut down debugger and execution server"
        self.close_debugger()
        if use_subprocess:
            self.interp.kill_subprocess()
        # Restore std streams
        sys.stdout = self.save_stdout
        sys.stderr = self.save_stderr
        sys.stdin = self.save_stdin
        # Break cycles
        self.interp = None
        self.console = None
        self.flist.pyshell = None
        self.history = None
        EditorWindow._close(self)

    def ispythonsource(self, filename):
        "Override EditorWindow method: never remove the colorizer"
        return True

    def short_title(self):
        return self.shell_title

    COPYRIGHT = \
          'Type "copyright", "credits" or "license()" for more information.'

    firewallmessage = """
    ****************************************************************
    Personal firewall software may warn about the connection IDLE
    makes to its subprocess using this computer's internal loopback
    interface.  This connection is not visible on any external
    interface and no data is sent to or received from the Internet.
    ****************************************************************
    """

    def begin(self):
        self.resetoutput()
        if use_subprocess:
            nosub = ''
            client = self.interp.start_subprocess()
            if not client:
                self.close()
                return False
        else:
            nosub = "==== No Subprocess ===="
        self.write("Python %s on %s\n%s\n%s\nIDLE %s      %s\n" %
                   (sys.version, sys.platform, self.COPYRIGHT,
                    self.firewallmessage, idlever.IDLE_VERSION, nosub))
        self.showprompt()
        import Tkinter
        Tkinter._default_root = None  # 03Jan04 KBK What's this?
        return True

    def readline(self):
        save = self.reading
        try:
            self.reading = 1
            self.top.mainloop()  # nested mainloop()
        finally:
            self.reading = save
        line = self.text.get("iomark", "end-1c")
        if len(line) == 0:  # may be EOF if we quit our mainloop with Ctrl-C
            line = "\n"
        if isinstance(line, unicode):
            import IOBinding
            try:
                line = line.encode(IOBinding.encoding)
            except UnicodeError:
                pass
        self.resetoutput()
        if self.canceled:
            self.canceled = 0
            if not use_subprocess:
                raise KeyboardInterrupt
        if self.endoffile:
            self.endoffile = 0
            line = ""
        return line

    def isatty(self):
        return True

    def cancel_callback(self, event=None):
        try:
            if self.text.compare("sel.first", "!=", "sel.last"):
                return  # Active selection -- always use default binding
        except:
            pass
        if not (self.executing or self.reading):
            self.resetoutput()
            self.interp.write("KeyboardInterrupt\n")
            self.showprompt()
            return "break"
        self.endoffile = 0
        self.canceled = 1
        if (self.executing and self.interp.rpcclt):
            if self.interp.getdebugger():
                self.interp.restart_subprocess()
            else:
                self.interp.interrupt_subprocess()
        if self.reading:
            self.top.quit()  # exit the nested mainloop() in readline()
        return "break"

    def eof_callback(self, event):
        if self.executing and not self.reading:
            return  # Let the default binding (delete next char) take over
        if not (self.text.compare("iomark", "==", "insert")
                and self.text.compare("insert", "==", "end-1c")):
            return  # Let the default binding (delete next char) take over
        if not self.executing:
            self.resetoutput()
            self.close()
        else:
            self.canceled = 0
            self.endoffile = 1
            self.top.quit()
        return "break"

    def linefeed_callback(self, event):
        # Insert a linefeed without entering anything (still autoindented)
        if self.reading:
            self.text.insert("insert", "\n")
            self.text.see("insert")
        else:
            self.newline_and_indent_event(event)
        return "break"

    def enter_callback(self, event):
        if self.executing and not self.reading:
            return  # Let the default binding (insert '\n') take over
        # If some text is selected, recall the selection
        # (but only if this before the I/O mark)
        try:
            sel = self.text.get("sel.first", "sel.last")
            if sel:
                if self.text.compare("sel.last", "<=", "iomark"):
                    self.recall(sel, event)
                    return "break"
        except:
            pass
        # If we're strictly before the line containing iomark, recall
        # the current line, less a leading prompt, less leading or
        # trailing whitespace
        if self.text.compare("insert", "<", "iomark linestart"):
            # Check if there's a relevant stdin range -- if so, use it
            prev = self.text.tag_prevrange("stdin", "insert")
            if prev and self.text.compare("insert", "<", prev[1]):
                self.recall(self.text.get(prev[0], prev[1]), event)
                return "break"
            next = self.text.tag_nextrange("stdin", "insert")
            if next and self.text.compare("insert lineend", ">=", next[0]):
                self.recall(self.text.get(next[0], next[1]), event)
                return "break"
            # No stdin mark -- just get the current line, less any prompt
            indices = self.text.tag_nextrange("console", "insert linestart")
            if indices and \
               self.text.compare(indices[0], "<=", "insert linestart"):
                self.recall(self.text.get(indices[1], "insert lineend"), event)
            else:
                self.recall(
                    self.text.get("insert linestart", "insert lineend"), event)
            return "break"
        # If we're between the beginning of the line and the iomark, i.e.
        # in the prompt area, move to the end of the prompt
        if self.text.compare("insert", "<", "iomark"):
            self.text.mark_set("insert", "iomark")
        # If we're in the current input and there's only whitespace
        # beyond the cursor, erase that whitespace first
        s = self.text.get("insert", "end-1c")
        if s and not s.strip():
            self.text.delete("insert", "end-1c")
        # If we're in the current input before its last line,
        # insert a newline right at the insert point
        if self.text.compare("insert", "<", "end-1c linestart"):
            self.newline_and_indent_event(event)
            return "break"
        # We're in the last line; append a newline and submit it
        self.text.mark_set("insert", "end-1c")
        if self.reading:
            self.text.insert("insert", "\n")
            self.text.see("insert")
        else:
            self.newline_and_indent_event(event)
        self.text.tag_add("stdin", "iomark", "end-1c")
        self.text.update_idletasks()
        if self.reading:
            self.top.quit()  # Break out of recursive mainloop() in raw_input()
        else:
            self.runit()
        return "break"

    def recall(self, s, event):
        # remove leading and trailing empty or whitespace lines
        s = re.sub(r'^\s*\n', '', s)
        s = re.sub(r'\n\s*$', '', s)
        lines = s.split('\n')
        self.text.undo_block_start()
        try:
            self.text.tag_remove("sel", "1.0", "end")
            self.text.mark_set("insert", "end-1c")
            prefix = self.text.get("insert linestart", "insert")
            if prefix.rstrip().endswith(':'):
                self.newline_and_indent_event(event)
                prefix = self.text.get("insert linestart", "insert")
            self.text.insert("insert", lines[0].strip())
            if len(lines) > 1:
                orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0)
                new_base_indent = re.search(r'^([ \t]*)', prefix).group(0)
                for line in lines[1:]:
                    if line.startswith(orig_base_indent):
                        # replace orig base indentation with new indentation
                        line = new_base_indent + line[len(orig_base_indent):]
                    self.text.insert('insert', '\n' + line.rstrip())
        finally:
            self.text.see("insert")
            self.text.undo_block_stop()

    def runit(self):
        line = self.text.get("iomark", "end-1c")
        # Strip off last newline and surrounding whitespace.
        # (To allow you to hit return twice to end a statement.)
        i = len(line)
        while i > 0 and line[i - 1] in " \t":
            i = i - 1
        if i > 0 and line[i - 1] == "\n":
            i = i - 1
        while i > 0 and line[i - 1] in " \t":
            i = i - 1
        line = line[:i]
        more = self.interp.runsource(line)

    def open_stack_viewer(self, event=None):
        if self.interp.rpcclt:
            return self.interp.remote_stack_viewer()
        try:
            sys.last_traceback
        except:
            tkMessageBox.showerror("No stack trace",
                                   "There is no stack trace yet.\n"
                                   "(sys.last_traceback is not defined)",
                                   master=self.text)
            return
        from StackViewer import StackBrowser
        sv = StackBrowser(self.root, self.flist)

    def view_restart_mark(self, event=None):
        self.text.see("iomark")
        self.text.see("restart")

    def restart_shell(self, event=None):
        self.interp.restart_subprocess()

    def showprompt(self):
        self.resetoutput()
        try:
            s = str(sys.ps1)
        except:
            s = ""
        self.console.write(s)
        self.text.mark_set("insert", "end-1c")
        self.set_line_and_column()
        self.io.reset_undo()

    def resetoutput(self):
        source = self.text.get("iomark", "end-1c")
        if self.history:
            self.history.history_store(source)
        if self.text.get("end-2c") != "\n":
            self.text.insert("end-1c", "\n")
        self.text.mark_set("iomark", "end-1c")
        self.set_line_and_column()
        sys.stdout.softspace = 0

    def write(self, s, tags=()):
        try:
            self.text.mark_gravity("iomark", "right")
            OutputWindow.write(self, s, tags, "iomark")
            self.text.mark_gravity("iomark", "left")
        except:
            pass
        if self.canceled:
            self.canceled = 0
            if not use_subprocess:
                raise KeyboardInterrupt
Ejemplo n.º 15
0
    def __init__(self, flist=None, filename=None, key=None, root=None,
       start_page=EditorPage):
        if EditorWindow.help_url is None:
            dochome =  os.path.join(sys.prefix, 'Doc', 'index.html')
            if sys.platform.count('linux'):
                # look for html docs in a couple of standard places
                pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
                if os.path.isdir('/var/www/html/python/'):  # "python2" rpm
                    dochome = '/var/www/html/python/index.html'
                else:
                    basepath = '/usr/share/doc/'  # standard location
                    dochome = os.path.join(basepath, pyver,
                                           'Doc', 'index.html')
            elif sys.platform[:3] == 'win':
                chmfile = os.path.join(sys.prefix, 'Doc',
                                       'Python%d%d.chm' % sys.version_info[:2])
                if os.path.isfile(chmfile):
                    dochome = chmfile

            elif macosxSupport.runningAsOSXApp():
                # documentation is stored inside the python framework
                dochome = os.path.join(sys.prefix,
                        'Resources/English.lproj/Documentation/index.html')

            dochome = os.path.normpath(dochome)
            if os.path.isfile(dochome):
                EditorWindow.help_url = dochome
                if sys.platform == 'darwin':
                    # Safari requires real file:-URLs
                    EditorWindow.help_url = 'file://' + EditorWindow.help_url
            else:
                EditorWindow.help_url = "http://www.python.org/doc/current"

        self.flist = flist
        root = root or flist.root
        self.root = root
        try:
            sys.ps1
        except AttributeError:
            sys.ps1 = '>>> '
        self.menubar = Menu(root)
        self.top = WindowList.ListedToplevel(root, menu=self.menubar)
        if flist:
            self.tkinter_vars = flist.vars
            # self.top.instance_dict makes flist.inversedict avalable to
            # configDialog.py so it can access all EditorWindow instaces
            self.top.instance_dict = flist.inversedict
        else:
            self.tkinter_vars = {}  # keys: Tkinter event names
                                    # values: Tkinter variable instances
            self.top.instance_dict = {}
        self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
            'recent-files.lst')

        if flist:
            flist.inversedict[self] = key
            if key:
                flist.dict[key] = self

        self.menudict = None

        # create a Notebook where the text pages for this EditorWindow will
        # reside
        self.text_notebook = TabbedPageSet(self.top)
        self.text_notebook.pack(fill=BOTH, expand=True)
        self.text_notebook.bind('<<NotebookTabChanged>>', self._update_controls)
        self.new_tab(filename=filename, load_ext=False, ptype=start_page)
        self.text = self.current_page.text # XXX
        self.top.focused_widget = self.text
        self.top.bind('<<tab-closed>>', self._post_tab_close)

        # The following "width" attribute is used by PyShell, so keep it here
        self.width = idleConf.GetOption('main', 'EditorPage', 'width')

        self.top.protocol("WM_DELETE_WINDOW", self.close)
        self.top.bind("<<close-window>>", self.close_event)

        self._create_statusbar()
        self.top.after_idle(self.set_line_and_column)

        # usetabs true  -> literal tab characters are used by indent and
        #                  dedent cmds, possibly mixed with spaces if
        #                  indentwidth is not a multiple of tabwidth,
        #                  which will cause Tabnanny to nag!
        #         false -> tab characters are converted to spaces by indent
        #                  and dedent cmds, and ditto TAB keystrokes
        # Although use-spaces=0 can be configured manually in config-main.def,
        # configuration of tabs v. spaces is not supported in the configuration
        # dialog.  IDLE promotes the preferred Python indentation: use spaces!
        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces',
            type='bool')
        self.usetabs = not usespaces

        # tabwidth is the display width of a literal tab character.
        # CAUTION:  telling Tk to use anything other than its default
        # tab setting causes it to use an entirely different tabbing algorithm,
        # treating tab stops as fixed distances from the left margin.
        # Nobody expects this, so for now tabwidth should never be changed.
        self.tabwidth = 8    # must remain 8 until Tk is fixed.

        # indentwidth is the number of screen characters per indent level.
        # The recommended Python indentation is four spaces.
        self.indentwidth = self.tabwidth
        self.set_notabs_indentwidth()

        # If context_use_ps1 is true, parsing searches back for a ps1 line;
        # else searches for a popular (if, def, ...) Python stmt.
        self.context_use_ps1 = False

        # When searching backwards for a reliable place to begin parsing,
        # first start num_context_lines[0] lines back, then
        # num_context_lines[1] lines back if that didn't work, and so on.
        # The last value should be huge (larger than the # of lines in a
        # conceivable file).
        # Making the initial values larger slows things down more often.
        self.num_context_lines = 50, 500, 5000000

        if hasattr(self, 'ispythonsource'): # PyShell
            self.set_indentation_params(self.ispythonsource(filename))
        else:
            self.set_indentation_params(
                self.current_page.ispythonsource(filename))

        self.extensions = {}
        self._load_extensions()

        menu = self.menudict.get('windows')
        if menu:
            end = menu.index("end")
            if end is None:
                end = -1
            if end >= 0:
                menu.add_separator()
                end = end + 1
            self.wmenu_end = end
            WindowList.register_callback(self.postwindowsmenu)

        # Some abstractions so IDLE extensions are cross-IDE
        self.askyesno = tkMessageBox.askyesno
        self.askinteger = tkSimpleDialog.askinteger
        self.showerror = tkMessageBox.showerror
Ejemplo n.º 16
0
class EditorWindow(object):
    from ColorDelegator import ColorDelegator  # overridden by PyShell
    from UndoDelegator import UndoDelegator  # overridden by PyShell

    help_url = None
    menu_specs = [
        ("file", "_File"),
        ("edit", "_Edit"),
        ("format", "F_ormat"),
        ("run", "_Run"),
        ("options", "_Options"),
        ("windows", "_Windows"),
        ("help", "_Help"),
    ]

    if macosxSupport.runningAsOSXApp():
        del menu_specs[-3]
        menu_specs[-2] = ("windows", "_Window")

    def __init__(self,
                 flist=None,
                 filename=None,
                 key=None,
                 root=None,
                 start_page=EditorPage):
        if EditorWindow.help_url is None:
            dochome = os.path.join(sys.prefix, 'Doc', 'index.html')
            if sys.platform.count('linux'):
                # look for html docs in a couple of standard places
                pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
                if os.path.isdir('/var/www/html/python/'):  # "python2" rpm
                    dochome = '/var/www/html/python/index.html'
                else:
                    basepath = '/usr/share/doc/'  # standard location
                    dochome = os.path.join(basepath, pyver, 'Doc',
                                           'index.html')
            elif sys.platform[:3] == 'win':
                chmfile = os.path.join(sys.prefix, 'Doc',
                                       'Python%d%d.chm' % sys.version_info[:2])
                if os.path.isfile(chmfile):
                    dochome = chmfile

            elif macosxSupport.runningAsOSXApp():
                # documentation is stored inside the python framework
                dochome = os.path.join(
                    sys.prefix,
                    'Resources/English.lproj/Documentation/index.html')

            dochome = os.path.normpath(dochome)
            if os.path.isfile(dochome):
                EditorWindow.help_url = dochome
                if sys.platform == 'darwin':
                    # Safari requires real file:-URLs
                    EditorWindow.help_url = 'file://' + EditorWindow.help_url
            else:
                EditorWindow.help_url = "http://www.python.org/doc/current"

        self.flist = flist
        root = root or flist.root
        self.root = root
        try:
            sys.ps1
        except AttributeError:
            sys.ps1 = '>>> '
        self.menubar = Menu(root)
        self.top = WindowList.ListedToplevel(root, menu=self.menubar)
        if flist:
            self.tkinter_vars = flist.vars
            # self.top.instance_dict makes flist.inversedict avalable to
            # configDialog.py so it can access all EditorWindow instaces
            self.top.instance_dict = flist.inversedict
        else:
            self.tkinter_vars = {}  # keys: Tkinter event names
            # values: Tkinter variable instances
            self.top.instance_dict = {}
        self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
                                              'recent-files.lst')

        if flist:
            flist.inversedict[self] = key
            if key:
                flist.dict[key] = self

        self.menudict = None

        # create a Notebook where the text pages for this EditorWindow will
        # reside
        self.text_notebook = TabbedPageSet(self.top)
        self.text_notebook.pack(fill=BOTH, expand=True)
        self.text_notebook.bind('<<NotebookTabChanged>>',
                                self._update_controls)
        self.new_tab(filename=filename, load_ext=False, ptype=start_page)
        self.text = self.current_page.text  # XXX
        self.top.focused_widget = self.text
        self.top.bind('<<tab-closed>>', self._post_tab_close)

        # The following "width" attribute is used by PyShell, so keep it here
        self.width = idleConf.GetOption('main', 'EditorPage', 'width')

        self.top.protocol("WM_DELETE_WINDOW", self.close)
        self.top.bind("<<close-window>>", self.close_event)

        self._create_statusbar()
        self.top.after_idle(self.set_line_and_column)

        # usetabs true  -> literal tab characters are used by indent and
        #                  dedent cmds, possibly mixed with spaces if
        #                  indentwidth is not a multiple of tabwidth,
        #                  which will cause Tabnanny to nag!
        #         false -> tab characters are converted to spaces by indent
        #                  and dedent cmds, and ditto TAB keystrokes
        # Although use-spaces=0 can be configured manually in config-main.def,
        # configuration of tabs v. spaces is not supported in the configuration
        # dialog.  IDLE promotes the preferred Python indentation: use spaces!
        usespaces = idleConf.GetOption('main',
                                       'Indent',
                                       'use-spaces',
                                       type='bool')
        self.usetabs = not usespaces

        # tabwidth is the display width of a literal tab character.
        # CAUTION:  telling Tk to use anything other than its default
        # tab setting causes it to use an entirely different tabbing algorithm,
        # treating tab stops as fixed distances from the left margin.
        # Nobody expects this, so for now tabwidth should never be changed.
        self.tabwidth = 8  # must remain 8 until Tk is fixed.

        # indentwidth is the number of screen characters per indent level.
        # The recommended Python indentation is four spaces.
        self.indentwidth = self.tabwidth
        self.set_notabs_indentwidth()

        # If context_use_ps1 is true, parsing searches back for a ps1 line;
        # else searches for a popular (if, def, ...) Python stmt.
        self.context_use_ps1 = False

        # When searching backwards for a reliable place to begin parsing,
        # first start num_context_lines[0] lines back, then
        # num_context_lines[1] lines back if that didn't work, and so on.
        # The last value should be huge (larger than the # of lines in a
        # conceivable file).
        # Making the initial values larger slows things down more often.
        self.num_context_lines = 50, 500, 5000000

        if hasattr(self, 'ispythonsource'):  # PyShell
            self.set_indentation_params(self.ispythonsource(filename))
        else:
            self.set_indentation_params(
                self.current_page.ispythonsource(filename))

        self.extensions = {}
        self._load_extensions()

        menu = self.menudict.get('windows')
        if menu:
            end = menu.index("end")
            if end is None:
                end = -1
            if end >= 0:
                menu.add_separator()
                end = end + 1
            self.wmenu_end = end
            WindowList.register_callback(self.postwindowsmenu)

        # Some abstractions so IDLE extensions are cross-IDE
        self.askyesno = tkMessageBox.askyesno
        self.askinteger = tkSimpleDialog.askinteger
        self.showerror = tkMessageBox.showerror

    @property
    def current_page(self):
        """Return the active EditorPage in EditorWindow."""
        if not self.text_notebook.pages:  # no pages available
            return None
        curr_tab = self.text_notebook.select()
        if not curr_tab:
            return None

        if TTK:
            page = self.text_notebook.pages[self.text_notebook.tab(curr_tab)
                                            ['text']].editpage
        else:
            page = self.text_notebook.pages[curr_tab].editpage
        return page

    def remove_tab_controls(self):
        """Remove tab area and most tab bindings from this window."""
        if TTK:
            self.text_notebook['style'] = 'PyShell.TNotebook'
            style = Style(self.top)
            style.layout('PyShell.TNotebook.Tab', [('null', '')])
        else:
            self.text_notebook._tab_set.grid_forget()

        # remove commands related to tab
        if 'file' in self.menudict:
            menu = self.menudict['file']
            curr_entry = None
            i = 0
            while True:
                last_entry, curr_entry = curr_entry, menu.entryconfigure(i)
                if last_entry == curr_entry:
                    # no more menu entries
                    break

                if 'label' in curr_entry and 'Tab' in curr_entry['label'][-1]:
                    if 'Close' not in ' '.join(curr_entry['label'][-1]):
                        menu.delete(i)
                i += 1

        self.current_page.text.unbind('<<new-tab>>')
        # close-tab is still available!

    def short_title(self):
        # overriden by PyShell
        return self.current_page.short_title()

    def next_tab(self, event):
        """Show next tab if not in the last tab already."""
        index = self.text_notebook.index(self.text_notebook.select())
        if index == len(self.text_notebook.tabs()) - 1:
            return
        self.text_notebook.select(index + 1)

    def prev_tab(self, event):
        """Show the previous tab if not in the first tab already."""
        index = self.text_notebook.index(self.text_notebook.select())
        if index == 0:
            return
        self.text_notebook.select(index - 1)

    def new_tab(self, event=None, filename=None, load_ext=True, ptype=None):
        """Create a new EditorPage and insert it into the notebook."""
        page_title = "#%d" % (len(self.text_notebook.pages) + 1)
        page = self.text_notebook.add_page(page_title)

        vbar = Scrollbar(page.frame, name='vbar')
        hbar = Scrollbar(page.frame, name='hbar', orient='horizontal')
        hbar.set(0, 0)
        vbar.set(0, 0)
        ptype = ptype or EditorPage
        page.editpage = ptype(page.frame,
                              self,
                              title=page_title,
                              name='text',
                              padx=5,
                              wrap='none')

        firstpage = False  # don't update window's title
        if self.menudict is None:
            # This EditorWindow is being created now, perform the following
            # tasks before.
            firstpage = True  # will cause window's title to be updated
            self.menudict = {}
            self._createmenubar(page.editpage.text)
            # Create the recent files submenu
            self.recent_files_menu = Menu(self.menubar)
            self.menudict['file'].insert_cascade(3,
                                                 label='Recent Files',
                                                 underline=0,
                                                 menu=self.recent_files_menu)
            self.update_recent_files_list()

        # pack widgets
        text = page.editpage.text
        vbar['command'] = text.yview
        hbar['command'] = text.xview
        text['yscrollcommand'] = vbar.set
        text['xscrollcommand'] = hbar.set
        vbar.pack(side=RIGHT, fill=Y)
        hbar.pack(side=BOTTOM, fill=X)
        fontWeight = 'normal'
        if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'):
            fontWeight = 'bold'
        text.config(
            font=(idleConf.GetOption('main', 'EditorPage', 'font'),
                  idleConf.GetOption('main', 'EditorPage', 'font-size'),
                  fontWeight))
        text.pack(side=TOP, fill=BOTH, expand=1)
        text.focus_set()

        self.apply_bindings(tab=page)
        if load_ext:
            self._load_extensions()

        # select the just created page
        self.text_notebook.select(len(self.text_notebook.pages) - 1)

        page.editpage.post_init(filename=filename,
                                update_window_title=firstpage)

        self.top.event_generate('<<tab-created>>')
        return "break"

    def new_callback(self, event, page):
        dirname, basename = page.io.defaultfilename()
        self.flist.new(dirname)
        return "break"

    def set_line_and_column(self, event=None):
        # Used by PyShell too
        curr_page = self.current_page
        if not curr_page:
            return

        line, column = curr_page.text.index(INSERT).split('.')
        self.status_bar.set_label('column', 'Col: %s' % column)
        self.status_bar.set_label('line', 'Ln: %s' % line)

    def postwindowsmenu(self):
        # Only called when Windows menu exists
        menu = self.menudict['windows']
        end = menu.index("end")
        if end is None:
            end = -1
        if end > self.wmenu_end:
            menu.delete(self.wmenu_end + 1, end)
        WindowList.add_windows_to_menu(menu)

    def newline_and_indent_event(self, event):
        """Call newline_and_indent_event on current EditorPage."""
        self.current_page.newline_and_indent_event(event)

    def get_selection_indices(self):
        """Call get_selection_indices on current EditorPage."""
        return self.current_page.get_selection_indices()

    def build_char_in_string_func(self, startindex):
        """Call build_char_in_string_func on current EditorPage."""
        return self.current_page.build_char_in_string_func(startindex)

    def gotoline(self, lineno):
        page = self.current_page
        text = page.text

        if lineno is not None and lineno > 0:
            text.mark_set("insert", "%d.0" % lineno)
            text.tag_remove("sel", "1.0", "end")
            text.tag_add("sel", "insert", "insert +1l")
            page.center()

    def close_hook(self):
        if self.flist:
            self.flist.unregister_maybe_terminate(self)
            self.flist = None

    def set_close_hook(self, close_hook):
        self.close_hook = close_hook

    def set_theme(self, ttkstyle):
        # called from configDialog.py
        ttkstyle.theme_use(idleConf.GetOption('main', 'Theme', 'displaytheme'))

    def ResetColorizer(self):
        "Update the colour theme"
        # Called from self.filename_change_hook and from configDialog.py
        for page in self.text_notebook.pages.itervalues():
            page.editpage.reset_colorizer()

    def ResetFont(self):
        "Update the text widgets' font if it is changed"
        # Called from configDialog.py
        fontWeight = 'normal'
        if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'):
            fontWeight = 'bold'

        for page in self.text_notebook.pages.itervalues():
            text = page.editpage.text
            text.config(
                font=(idleConf.GetOption('main', 'EditorPage', 'font'),
                      idleConf.GetOption('main', 'EditorPage', 'font-size'),
                      fontWeight))

    def RemoveKeybindings(self):
        "Remove the keybindings before they are changed."
        # Called from configDialog.py
        Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()

        for page in self.text_notebook.pages.itervalues():
            text = page.editpage.text
            for event, keylist in keydefs.items():
                text.event_delete(event, *keylist)

        for extensionName in self._get_standard_extension_names():
            xkeydefs = idleConf.GetExtensionBindings(extensionName)
            if xkeydefs:
                for page in self.text_notebook.pages.itervalues():
                    text = page.editpage.text
                    for event, keylist in xkeydefs.items():
                        text.event_delete(event, *keylist)

    def ApplyKeybindings(self):
        "Update the keybindings after they are changed"
        # Called from configDialog.py
        Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
        self.apply_bindings()
        for extensionName in self._get_standard_extension_names():
            xkeydefs = idleConf.GetExtensionBindings(extensionName)
            if xkeydefs:
                self.apply_bindings(xkeydefs)
        #update menu accelerators
        menuEventDict = {}
        for menu in Bindings.menudefs:
            menuEventDict[menu[0]] = {}
            for item in menu[1]:
                if item:
                    menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
        for menubarItem in self.menudict.keys():
            menu = self.menudict[menubarItem]
            end = menu.index(END) + 1
            for index in range(0, end):
                if menu.type(index) == 'command':
                    accel = menu.entrycget(index, 'accelerator')
                    if accel:
                        itemName = menu.entrycget(index, 'label')
                        event = ''
                        if menuEventDict.has_key(menubarItem):
                            if menuEventDict[menubarItem].has_key(itemName):
                                event = menuEventDict[menubarItem][itemName]
                        if event:
                            accel = get_accelerator(keydefs, event)
                            menu.entryconfig(index, accelerator=accel)

    def reset_help_menu_entries(self):
        "Update the additional help entries on the Help menu"
        help_list = idleConf.GetAllExtraHelpSourcesList()
        helpmenu = self.menudict['help']
        # first delete the extra help entries, if any
        helpmenu_length = helpmenu.index(END)
        if helpmenu_length > self.base_helpmenu_length:
            helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
        # then rebuild them
        if help_list:
            helpmenu.add_separator()
            for entry in help_list:
                cmd = self.__extra_help_callback(entry[1])
                helpmenu.add_command(label=entry[0], command=cmd)
        # and update the menu dictionary
        self.menudict['help'] = helpmenu

    def set_notabs_indentwidth(self):
        "Update the indentwidth if changed and not using tabs in this window"
        # Called from configDialog.py
        if not self.usetabs:
            self.indentwidth = idleConf.GetOption('main',
                                                  'Indent',
                                                  'num-spaces',
                                                  type='int')

    def update_recent_files_list(self, new_file=None):
        "Load and update the recent files list and menus"
        # IOBinding calls this
        rf_list = []
        if os.path.exists(self.recent_files_path):
            rf_list_file = open(self.recent_files_path, 'r')
            try:
                rf_list = rf_list_file.readlines()
            finally:
                rf_list_file.close()
        if new_file:
            new_file = os.path.abspath(new_file) + '\n'
            if new_file in rf_list:
                rf_list.remove(new_file)  # move to top
            rf_list.insert(0, new_file)
        # clean and save the recent files list
        bad_paths = []
        for path in rf_list:
            if '\0' in path or not os.path.exists(path[0:-1]):
                bad_paths.append(path)
        rf_list = [path for path in rf_list if path not in bad_paths]
        ulchars = "1234567890ABCDEFGHIJK"
        rf_list = rf_list[0:len(ulchars)]
        rf_file = open(self.recent_files_path, 'w')
        try:
            rf_file.writelines(rf_list)
        finally:
            rf_file.close()
        # for each edit window instance, construct the recent files menu
        for instance in self.top.instance_dict.keys():
            menu = instance.recent_files_menu
            menu.delete(1, END)  # clear, and rebuild:
            for i, file in enumerate(rf_list):
                file_name = file[0:-1]  # zap \n
                # make unicode string to display non-ASCII chars correctly
                ufile_name = filename_to_unicode(file_name)
                callback = instance.__recent_file_callback(file_name)
                menu.add_command(label=ulchars[i] + " " + ufile_name,
                                 command=callback,
                                 underline=0)

    def get_geometry(self):
        "Return (width, height, x, y)"
        geom = self.top.wm_geometry()
        m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
        tuple = (map(int, m.groups()))
        return tuple

    def close_event(self, event):
        self.close()

    def close(self):
        to_check = self.text_notebook.pages.copy()

        while to_check:
            curr_tab = self.text_notebook.select()
            if TTK:
                page_name = self.text_notebook.tab(curr_tab)['text']
            else:
                page_name = curr_tab
            page = to_check.pop(page_name)
            editpage = page.editpage
            reply = editpage.close_tab()
            if reply == "cancel":
                break

    def _close(self):
        WindowList.unregister_callback(self.postwindowsmenu)
        self._unload_extensions()
        self.tkinter_vars = None

        for page in self.text_notebook.pages.itervalues():
            page.editpage.close()

        self.top.destroy()
        if self.close_hook:
            # unless override: unregister from flist, terminate if last window
            self.close_hook()

    def apply_bindings(self, keydefs=None, tab=None):
        if keydefs is None:
            keydefs = Bindings.default_keydefs

        if tab:
            iter_over = [tab]
        else:
            iter_over = self.text_notebook.pages.itervalues()

        for page in iter_over:
            text = page.editpage.text
            text.keydefs = keydefs
            for event, keylist in keydefs.items():
                if keylist:
                    text.event_add(event, *keylist)

    def getvar(self, name):
        var = self.get_var_obj(name)
        if var:
            value = var.get()
            return value
        else:
            raise NameError, name

    def setvar(self, name, value, vartype=None):
        var = self.get_var_obj(name, vartype)
        if var:
            var.set(value)
        else:
            raise NameError, name

    def get_var_obj(self, name, vartype=None, text=None):
        var = self.tkinter_vars.get(name)
        if not var and vartype:
            # create a Tkinter variable object with self.text as master:
            self.tkinter_vars[name] = var = vartype(text or self.text)
        return var

    # Tk implementations of "virtual text methods" -- each platform
    # reusing IDLE's support code needs to define these for its GUI's
    # flavor of widget.

    # Return the text widget's current view of what a tab stop means
    # (equivalent width in spaces).
    def get_tabwidth(self):  # XXX depends on self.text
        current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
        return int(current)

    # Set the text widget's current view of what a tab stop means.
    def set_tabwidth(self, newtabwidth):  # XXX depends on self.text
        text = self.text
        if self.get_tabwidth() != newtabwidth:
            pixels = text.tk.call("font", "measure", text["font"],
                                  "-displayof", text.master, "n" * newtabwidth)
            text.configure(tabs=pixels)

    # If ispythonsource and guess are true, guess a good value for
    # indentwidth based on file content (if possible), and if
    # indentwidth != tabwidth set usetabs false.
    # In any case, adjust the Text widget's view of what a tab
    # character means.
    def set_indentation_params(self, ispythonsource, guess=True):
        if guess and ispythonsource:
            i = self.guess_indent()
            if 2 <= i <= 8:
                self.indentwidth = i
            if self.indentwidth != self.tabwidth:
                self.usetabs = False
        self.set_tabwidth(self.tabwidth)

    # Guess indentwidth from text content.
    # Return guessed indentwidth.  This should not be believed unless
    # it's in a reasonable range (e.g., it will be 0 if no indented
    # blocks are found).
    def guess_indent(self):  # XXX depends on self.text
        opener, indented = IndentSearcher(self.text, self.tabwidth).run()
        if opener and indented:
            raw, indentsmall = classifyws(opener, self.tabwidth)
            raw, indentlarge = classifyws(indented, self.tabwidth)
        else:
            indentsmall = indentlarge = 0
        return indentlarge - indentsmall

    # Private methods/attributes

    # extensions won't have more than one instance per window
    _unique_extensions = ['CodeContext', 'ScriptBinding', 'FormatParagraph']

    def _unload_extensions(self):
        for ins in self.extensions.values():
            if hasattr(ins, "close"):
                ins.close()
        self.extensions = {}

    def _load_extension(self, name, tab):
        ext_loaded = self.extensions.get(name)

        try:
            mod = __import__(name, globals(), locals(), [])
        except ImportError:
            print "\nFailed to import extension: ", name
            return

        keydefs = idleConf.GetExtensionBindings(name)

        if name not in self._unique_extensions or not ext_loaded:
            # create a new instance
            cls = getattr(mod, name)
            ins = cls(tab.editpage)
            self.extensions.setdefault(name, []).append(ins)
            if not ext_loaded:
                # create new items in menu only if this is the first time this
                # extension is being loaded in this window
                if hasattr(cls, "menudefs"):
                    self._fill_menus(cls.menudefs, keydefs)
        elif name in self._unique_extensions and ext_loaded:
            # get an existing instance
            ins = self.extensions[name][0]

        if keydefs:
            self.apply_bindings(keydefs, tab)
            for vevent in keydefs.keys():
                methodname = vevent.replace("-", "_")
                while methodname[:1] == '<':
                    methodname = methodname[1:]
                while methodname[-1:] == '>':
                    methodname = methodname[:-1]
                methodname = methodname + "_event"
                if hasattr(ins, methodname):
                    tab.editpage.text.bind(vevent, getattr(ins, methodname))

    def _load_extensions(self):
        self._load_standard_extensions(self.text_notebook.last_page())

    def _load_standard_extensions(self, tab):
        for name in self._get_standard_extension_names():
            try:
                self._load_extension(name, tab)
            except:
                print "Failed to load extension", repr(name)
                traceback.print_exc()

    def _get_standard_extension_names(self):
        return idleConf.GetExtensions(editor_only=True)

    def _post_tab_close(self, event):
        if not self.current_page:
            # no tabs now, close window
            self._close()
            return

    def _update_controls(self, event):
        curr_page = self.current_page
        if not curr_page:
            return

        self.text = curr_page.text
        curr_page.saved_change_hook(True, False)  # update window title
        curr_page.text.focus_set()
        self.set_line_and_column()

        # update references in extensions that are loaded only once
        for ext in self._unique_extensions:
            if ext not in self.extensions:
                continue
            ext = self.extensions[ext][0]
            ext.editpage = curr_page

    def _create_statusbar(self):
        self.status_bar = MultiStatusBar(self.top)
        if macosxSupport.runningAsOSXApp():
            # Insert some padding to avoid obscuring some of the statusbar
            # by the resize widget.
            self.status_bar.set_label('_padding1', '    ', side=RIGHT)
        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
        self.status_bar.pack(side=BOTTOM, fill=X)

    def _createmenubar(self, text):
        mbar = self.menubar
        menudict = self.menudict
        for name, label in self.menu_specs:
            underline, label = prepstr(label)
            menudict[name] = menu = Menu(mbar, name=name, tearoff=0)
            mbar.add_cascade(label=label, menu=menu, underline=underline)

        if sys.platform == 'darwin' and '.framework' in sys.executable:
            # Insert the application menu
            menudict['application'] = menu = Menu(mbar, name='apple')
            mbar.add_cascade(label='IDLE', menu=menu)

        self._fill_menus(text=text)
        self.base_helpmenu_length = self.menudict['help'].index(END)
        self.reset_help_menu_entries()

    def _fill_menus(self, menudefs=None, keydefs=None, text=None):
        """Add appropriate entries to the menus and submenus

        Menus that are absent or None in self.menudict are ignored.
        """
        if menudefs is None:
            menudefs = Bindings.menudefs
        if keydefs is None:
            keydefs = Bindings.default_keydefs
        menudict = self.menudict
        for mname, entrylist in menudefs:
            menu = menudict.get(mname)
            if not menu:
                continue
            for entry in entrylist:
                if not entry:
                    menu.add_separator()
                else:
                    label, eventname = entry
                    checkbutton = (label[:1] == '!')
                    if checkbutton:
                        label = label[1:]
                    underline, label = prepstr(label)
                    accelerator = get_accelerator(keydefs, eventname)

                    def command(eventname=eventname):
                        self.text.event_generate(eventname)

                    if checkbutton:
                        var = self.get_var_obj(eventname, BooleanVar, text)
                        menu.add_checkbutton(label=label,
                                             underline=underline,
                                             command=command,
                                             accelerator=accelerator,
                                             variable=var)
                    else:
                        menu.add_command(label=label,
                                         underline=underline,
                                         command=command,
                                         accelerator=accelerator)

    def __recent_file_callback(self, file_name):
        def open_recent_file(fn_closure=file_name):
            self.current_page.io.open(editFile=fn_closure)

        return open_recent_file

    def __extra_help_callback(self, helpfile):
        "Create a callback with the helpfile value frozen at definition time"

        def display_extra_help(helpfile=helpfile):
            if not helpfile.startswith(('www', 'http')):
                url = os.path.normpath(helpfile)
            if sys.platform[:3] == 'win':
                os.startfile(helpfile)
            else:
                webbrowser.open(helpfile)

        return display_extra_help
Ejemplo n.º 17
0
    def __init__(self,
                 flist=None,
                 filename=None,
                 key=None,
                 root=None,
                 start_page=EditorPage):
        if EditorWindow.help_url is None:
            dochome = os.path.join(sys.prefix, 'Doc', 'index.html')
            if sys.platform.count('linux'):
                # look for html docs in a couple of standard places
                pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
                if os.path.isdir('/var/www/html/python/'):  # "python2" rpm
                    dochome = '/var/www/html/python/index.html'
                else:
                    basepath = '/usr/share/doc/'  # standard location
                    dochome = os.path.join(basepath, pyver, 'Doc',
                                           'index.html')
            elif sys.platform[:3] == 'win':
                chmfile = os.path.join(sys.prefix, 'Doc',
                                       'Python%d%d.chm' % sys.version_info[:2])
                if os.path.isfile(chmfile):
                    dochome = chmfile

            elif macosxSupport.runningAsOSXApp():
                # documentation is stored inside the python framework
                dochome = os.path.join(
                    sys.prefix,
                    'Resources/English.lproj/Documentation/index.html')

            dochome = os.path.normpath(dochome)
            if os.path.isfile(dochome):
                EditorWindow.help_url = dochome
                if sys.platform == 'darwin':
                    # Safari requires real file:-URLs
                    EditorWindow.help_url = 'file://' + EditorWindow.help_url
            else:
                EditorWindow.help_url = "http://www.python.org/doc/current"

        self.flist = flist
        root = root or flist.root
        self.root = root
        try:
            sys.ps1
        except AttributeError:
            sys.ps1 = '>>> '
        self.menubar = Menu(root)
        self.top = WindowList.ListedToplevel(root, menu=self.menubar)
        if flist:
            self.tkinter_vars = flist.vars
            # self.top.instance_dict makes flist.inversedict avalable to
            # configDialog.py so it can access all EditorWindow instaces
            self.top.instance_dict = flist.inversedict
        else:
            self.tkinter_vars = {}  # keys: Tkinter event names
            # values: Tkinter variable instances
            self.top.instance_dict = {}
        self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
                                              'recent-files.lst')

        if flist:
            flist.inversedict[self] = key
            if key:
                flist.dict[key] = self

        self.menudict = None

        # create a Notebook where the text pages for this EditorWindow will
        # reside
        self.text_notebook = TabbedPageSet(self.top)
        self.text_notebook.pack(fill=BOTH, expand=True)
        self.text_notebook.bind('<<NotebookTabChanged>>',
                                self._update_controls)
        self.new_tab(filename=filename, load_ext=False, ptype=start_page)
        self.text = self.current_page.text  # XXX
        self.top.focused_widget = self.text
        self.top.bind('<<tab-closed>>', self._post_tab_close)

        # The following "width" attribute is used by PyShell, so keep it here
        self.width = idleConf.GetOption('main', 'EditorPage', 'width')

        self.top.protocol("WM_DELETE_WINDOW", self.close)
        self.top.bind("<<close-window>>", self.close_event)

        self._create_statusbar()
        self.top.after_idle(self.set_line_and_column)

        # usetabs true  -> literal tab characters are used by indent and
        #                  dedent cmds, possibly mixed with spaces if
        #                  indentwidth is not a multiple of tabwidth,
        #                  which will cause Tabnanny to nag!
        #         false -> tab characters are converted to spaces by indent
        #                  and dedent cmds, and ditto TAB keystrokes
        # Although use-spaces=0 can be configured manually in config-main.def,
        # configuration of tabs v. spaces is not supported in the configuration
        # dialog.  IDLE promotes the preferred Python indentation: use spaces!
        usespaces = idleConf.GetOption('main',
                                       'Indent',
                                       'use-spaces',
                                       type='bool')
        self.usetabs = not usespaces

        # tabwidth is the display width of a literal tab character.
        # CAUTION:  telling Tk to use anything other than its default
        # tab setting causes it to use an entirely different tabbing algorithm,
        # treating tab stops as fixed distances from the left margin.
        # Nobody expects this, so for now tabwidth should never be changed.
        self.tabwidth = 8  # must remain 8 until Tk is fixed.

        # indentwidth is the number of screen characters per indent level.
        # The recommended Python indentation is four spaces.
        self.indentwidth = self.tabwidth
        self.set_notabs_indentwidth()

        # If context_use_ps1 is true, parsing searches back for a ps1 line;
        # else searches for a popular (if, def, ...) Python stmt.
        self.context_use_ps1 = False

        # When searching backwards for a reliable place to begin parsing,
        # first start num_context_lines[0] lines back, then
        # num_context_lines[1] lines back if that didn't work, and so on.
        # The last value should be huge (larger than the # of lines in a
        # conceivable file).
        # Making the initial values larger slows things down more often.
        self.num_context_lines = 50, 500, 5000000

        if hasattr(self, 'ispythonsource'):  # PyShell
            self.set_indentation_params(self.ispythonsource(filename))
        else:
            self.set_indentation_params(
                self.current_page.ispythonsource(filename))

        self.extensions = {}
        self._load_extensions()

        menu = self.menudict.get('windows')
        if menu:
            end = menu.index("end")
            if end is None:
                end = -1
            if end >= 0:
                menu.add_separator()
                end = end + 1
            self.wmenu_end = end
            WindowList.register_callback(self.postwindowsmenu)

        # Some abstractions so IDLE extensions are cross-IDE
        self.askyesno = tkMessageBox.askyesno
        self.askinteger = tkSimpleDialog.askinteger
        self.showerror = tkMessageBox.showerror