示例#1
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        settings.get_section('General').connect('pygments_style',
                                                self._set_style,
                                                run_now=True)

        def on_destroy(event):
            settings.get_section('General').disconnect('pygments_style',
                                                       self._set_style)

        self.bind('<Destroy>', on_destroy, add=True)
示例#2
0
    def save(self):
        """Save the file to the current :attr:`path`.

        This calls :meth:`save_as` if :attr:`path` is None, and returns
        False if the user cancels the save as dialog. None is returned
        on errors, and True is returned in all other cases. In other
        words, this returns True if saving succeeded.

        .. seealso:: The :virtevt:`Save` event.
        """
        if self.path is None:
            return self.save_as()

        self.event_generate('<<Save>>')

        encoding = settings.get_section('General')['encoding']
        try:
            with utils.backup_open(self.path, 'w', encoding=encoding) as f:
                for chunk in self.textwidget.iter_chunks():
                    f.write(chunk)
        except (OSError, UnicodeError) as e:
            log.exception("saving '%s' failed", self.path)
            utils.errordialog(type(e).__name__, "Saving failed!",
                              traceback.format_exc())
            return None

        self.mark_saved()
        return True
示例#3
0
    def on_wheel(self, direction):
        config = settings.get_section('General')
        if direction == 'reset':
            config.reset('font_size')
            return

        try:
            config['font_size'] += (1 if direction == 'up' else -1)
        except settings.InvalidValue:
            pass
示例#4
0
    def _get_hash(self):
        # superstitious omg-optimization
        config = settings.get_section('General')
        encoding = config['encoding']

        result = hashlib.md5()
        for chunk in self.textwidget.iter_chunks():
            chunk = chunk.encode(encoding, errors='replace')
            result.update(chunk)

        # hash objects don't define an __eq__ so we need to use a string
        # representation of the hash
        return result.hexdigest()
示例#5
0
    def open_file(cls, manager, path):
        """Read a file and return a new FileTab object.

        Use this constructor if you want to open an existing file from a
        path and let the user edit it.

        :exc:`UnicodeError` or :exc:`OSError` is raised if reading the
        file fails.
        """
        config = settings.get_section('General')
        with open(path, 'r', encoding=config['encoding']) as file:
            content = file.read()
        return cls(manager, content, path)
示例#6
0
from rbtk import get_main_window, settings

config = settings.get_section('General')
config.add_option('default_geometry', '650x500', reset=False)


def save_geometry(event):
    config['default_geometry'] = event.widget.geometry()


def setup():
    get_main_window().geometry(config['default_geometry'])
    get_main_window().bind('<<RBTKQuit>>', save_geometry, add=True)
示例#7
0
 def on_destroy(event):
     settings.get_section('General').disconnect('pygments_style',
                                                self._set_style)
示例#8
0
def setup_actions():
    def new_file():
        _tab_manager.add_tab(tabs.FileTab(_tab_manager))

    def open_files():
        for path in _dialogs.open_files():
            try:
                tab = tabs.FileTab.open_file(_tab_manager, path)
            except (UnicodeError, OSError) as e:
                log.exception("opening '%s' failed", path)
                utils.errordialog(
                    type(e).__name__, "Opening failed!",
                    traceback.format_exc())
                continue

            _tab_manager.add_tab(tab)

    def close_current_tab():
        if _tab_manager.current_tab.can_be_closed():
            _tab_manager.close_tab(_tab_manager.current_tab)

    add_action(new_file, "File/New File", ("Ctrl+N", '<Control-n>'))
    add_action(open_files, "File/Open", ("Ctrl+O", '<Control-o>'))
    add_action((lambda: _tab_manager.current_tab.save()),
               "File/Save", ("Ctrl+S", '<Control-s>'),
               tabtypes=[tabs.FileTab])
    add_action((lambda: _tab_manager.current_tab.save_as()),
               "File/Save As...", ("Ctrl+Shift+S", '<Control-S>'),
               tabtypes=[tabs.FileTab])
    menubar.get_menu("File").add_separator()

    # TODO: disable File/Quit when there are tabs, it's too easy to hit
    # Ctrl+Q accidentally
    add_action(close_current_tab,
               "File/Close", ("Ctrl+W", '<Control-w>'),
               tabtypes=[tabs.Tab])
    add_action(quit, "File/Quit", ("Ctrl+Q", '<Control-q>'))

    def textmethod(attribute):
        def result():
            textwidget = _tab_manager.current_tab.textwidget
            method = getattr(textwidget, attribute)
            method()

        return result

    # FIXME: bind these in a text widget only, not globally
    add_action(textmethod('undo'),
               "Edit/Undo", ("Ctrl+Z", '<Control-z>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('redo'),
               "Edit/Redo", ("Ctrl+Y", '<Control-y>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('cut'),
               "Edit/Cut", ("Ctrl+X", '<Control-x>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('copy'),
               "Edit/Copy", ("Ctrl+C", '<Control-c>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('paste'),
               "Edit/Paste", ("Ctrl+V", '<Control-v>'),
               tabtypes=[tabs.FileTab])
    add_action(textmethod('select_all'),
               "Edit/Select All", ("Ctrl+A", '<Control-a>'),
               tabtypes=[tabs.FileTab])
    menubar.get_menu("Edit").add_separator()

    # FIXME: this separator thing is a mess :(
    menubar.get_menu("Edit").add_separator()
    add_action(functools.partial(settings.show_dialog, rbtk.get_main_window()),
               "Edit/RBTK Settings...")

    # the font size stuff are bound by the textwidget itself, that's why
    # there are Nones everywhere
    add_action((lambda: _tab_manager.current_tab.textwidget.on_wheel('up')),
               "View/Bigger Font", ("Ctrl+Plus", None),
               tabtypes=[tabs.FileTab])
    add_action((lambda: _tab_manager.current_tab.textwidget.on_wheel('down')),
               "View/Smaller Font", ("Ctrl+Minus", None),
               tabtypes=[tabs.FileTab])
    add_action((lambda: _tab_manager.current_tab.textwidget.on_wheel('reset')),
               "View/Reset Font Size", ("Ctrl+Zero", None),
               tabtypes=[tabs.FileTab])
    menubar.get_menu("View").add_separator()

    def add_link(menupath, url):
        add_action(functools.partial(webbrowser.open, url), menupath)

    # TODO: an about dialog that shows porcupine version, Python version
    #       and where porcupine is installed
    # TODO: porcupine starring button

    ## FIX LINKS
    add_link("Help/Free help chat",
             "http://webchat.freenode.net/?channels=%23%23learnpython")
    add_link("Help/My Python tutorial",
             "https://github.com/Akuli/python-tutorial/blob/master/README.md")
    add_link("Help/Official Python documentation", "https://docs.python.org/")
    menubar.get_menu("Help").add_separator()
    add_link("Help/Porcupine Wiki", "https://github.com/Akuli/porcupine/wiki")
    add_link("Help/Report a problem or request a feature",
             "https://github.com/Akuli/porcupine/issues/new")
    add_link("Help/Read Porcupine's code on GitHub",
             "https://github.com/Akuli/porcupine/tree/master/porcupine")

    # TODO: loading the styles takes a long time on startup... try to
    # make it asynchronous without writing too complicated code?
    config = settings.get_section('General')
    for name in sorted(pygments.styles.get_all_styles()):
        if name.islower():
            label = name.replace('-', ' ').replace('_', ' ').title()
        else:
            label = name

        options = {
            'label': label,
            'value': name,
            'variable': config.get_var('pygments_style'),
        }

        style = pygments.styles.get_style_by_name(name)
        bg = style.background_color

        # styles have a style_for_token() method, but only iterating
        # is documented :( http://pygments.org/docs/formatterdevelopment/
        # i'm using iter() to make sure that dict() really treats
        # the style as an iterable of pairs instead of some other
        # metaprogramming fanciness
        fg = None
        style_infos = dict(iter(style))
        for token in [pygments.token.String, pygments.token.Text]:
            if style_infos[token]['color'] is not None:
                fg = '#' + style_infos[token]['color']
                break
        if fg is None:
            # do like textwidget.ThemedText._set_style does
            fg = (getattr(style, 'default_style', '')
                  or utils.invert_color(bg))

        options['foreground'] = options['activebackground'] = fg
        options['background'] = options['activeforeground'] = bg