Blob Blame History Raw
# -*- coding: utf-8 -*-
#
#  viewactivatable.py - Gedit View Activatable Implementation
#
#  Copyright (C) 2010 - Jesse van den Kieboom
#
#  This program 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 2 of the License, or
#  (at your option) any later version.
#
#  This program 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 this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor,
#  Boston, MA 02110-1301, USA.

from .signals import Signals
from gi.repository import GObject, Gtk, Gdk, Pango, Gedit

class TextSizeViewActivatable(GObject.Object, Gedit.ViewActivatable, Signals):

    view = GObject.Property(type=Gedit.View)

    def __init__(self):
        GObject.Object.__init__(self)
        Signals.__init__(self)

    def do_activate(self):
        self.connect_signal(self.view, 'scroll-event', self.on_scroll_event)
        self.connect_signal(self.view, 'button-press-event', self.on_button_press_event)

        self.view.textsize_view_activatable = self

        self._default_font = None
        self._last_font = None
        self._font_tags = {}

    def do_deactivate(self):
        if self._default_font:
            self.view.override_font(self._default_font)

        self.remove_font_tags()
        self.disconnect_signals(self.view)

        delattr(self.view, "textsize_view_activatable")

    def remove_font_tags(self):
        buf = self.view.get_buffer()
        table = buf.get_tag_table()

        # Remove all the font tags
        for size in self._font_tags:
            tag = self._font_tags[size]
            table.remove(tag)

        self._font_tags = {}

    def update_default_font(self):
        context = self.view.get_style_context()
        description = context.get_font(context.get_state()).copy()

        if not self._last_font or description.hash() != self._last_font.hash():
            self._default_font = description

    def get_font_tags(self, start, end):
        tags = set()

        # Check all the know font tags
        for size in self._font_tags:
            tag = self._font_tags[size]

            if start.has_tag(tag):
                tags.add(tag)
            else:
                cp = start.copy()

                if cp.forward_to_tag_toggle(tag) and cp.compare(end) < 0:
                    tags.add(tag)

        return list(tags)

    def set_font_size(self, amount):
        self.update_default_font()

        context = self.view.get_style_context()
        description = context.get_font(context.get_state()).copy()

        buf = self.view.get_buffer()
        bounds = buf.get_selection_bounds()
        size = description.get_size() / Pango.SCALE

        if not bounds:
            description.set_size(max(1, (size + amount)) * Pango.SCALE)

            self.view.override_font(description)
            self._last_font = description
        else:
            start = bounds[0]
            end = bounds[1]

            tags = self.get_font_tags(start, end)

            if not tags:
                # Simply use the overall font size as the base
                newsize = size + amount
            elif len(tags) == 1:
                newsize = tags[0].props.font_desc.get_size() / Pango.SCALE + amount
            else:
                newsize = 0

                for tag in tags:
                    newsize += tag.props.font_desc.get_size() / Pango.SCALE

                newsize = round(newsize / len(tags))

            newsize = int(max(1, newsize))

            if not newsize in self._font_tags:
                newtag = buf.create_tag(None)

                desc = description
                desc.set_size(newsize * Pango.SCALE)

                newtag.props.font_desc = desc
                self._font_tags[newsize] = newtag
            else:
                newtag = self._font_tags[newsize]

            # Remove all the previous mix of tags
            for tag in tags:
                buf.remove_tag(tag, start, end)

            buf.apply_tag(newtag, start, end)

    def larger_text(self):
        self.set_font_size(1)

    def smaller_text(self):
        self.set_font_size(-1)

    def normal_size(self):
        self.update_default_font()

        buf = self.view.get_buffer()
        bounds = buf.get_selection_bounds()

        if not bounds:
            self.remove_font_tags()

            self.view.override_font(self._default_font)
            self._last_font = self._default_font
        else:
            tags = self.get_font_tags(bounds[0], bounds[1])

            for tag in tags:
                buf.remove_tag(tag, bounds[0], bounds[1])

    def on_scroll_event(self, view, event):
        state = event.state & Gtk.accelerator_get_default_mod_mask()

        if state != Gdk.ModifierType.CONTROL_MASK:
            return False

        # Scroll to the keep the line in the centre of the screen anchored
        visible_rect = self.view.get_visible_rect()
        central_line_y = visible_rect.height / 2 + visible_rect.y
        central_line_iter, _ = self.view.get_line_at_y(central_line_y)
        self.view.scroll_to_iter(central_line_iter, 0, True, 0.5, 0.5)

        if event.direction == Gdk.ScrollDirection.UP:
            self.larger_text()
            return True
        elif event.direction == Gdk.ScrollDirection.DOWN:
            self.smaller_text()
            return True
        elif event.direction == Gdk.ScrollDirection.SMOOTH:
            if event.delta_y > 0:
                self.smaller_text()
            elif event.delta_y < 0:
                self.larger_text()

        return False

    def on_button_press_event(self, view, event):
        state = event.state & Gtk.accelerator_get_default_mod_mask()

        if state == Gdk.ModifierType.CONTROL_MASK and event.button == 2:
            self.normal_size()
            return True
        else:
            return False

# ex:ts=4:et: