# -*- coding: utf-8 -*-
#
# This file is part of NINJA-IDE (http://ninja-ide.org).
#
# NINJA-IDE 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
# any later version.
#
# NINJA-IDE 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 NINJA-IDE; If not, see <http://www.gnu.org/licenses/>.
import sys
from PyQt4.QtGui import QFrame
from PyQt4.QtGui import QPlainTextEdit
from PyQt4.QtGui import QTextOption
from PyQt4.QtGui import QGraphicsOpacityEffect
from PyQt4.QtGui import QFontMetrics
from PyQt4.QtGui import QPainter
from PyQt4.QtGui import QColor
from PyQt4.QtGui import QPen
from PyQt4.QtGui import QBrush
from PyQt4.QtCore import Qt
from PyQt4.QtCore import SIGNAL
from PyQt4.QtCore import QPropertyAnimation
from ninja_ide import resources
from ninja_ide.core import settings
#QGraphicsOpacityEffect doesn't work in mac cause a Qt Issue: QTBUG-15367
ACTIVATE_OPACITY = True if sys.platform != 'darwin' else False
class MiniMap(QPlainTextEdit):
def __init__(self, parent):
super(MiniMap, self).__init__(parent)
self.setWordWrapMode(QTextOption.NoWrap)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setReadOnly(True)
self.setCenterOnScroll(True)
self.setMouseTracking(True)
self.viewport().setCursor(Qt.PointingHandCursor)
self.setTextInteractionFlags(Qt.NoTextInteraction)
self._parent = parent
self.highlighter = None
self.lines_count = 0
self.connect(self._parent, SIGNAL("updateRequest(const QRect&, int)"),
self.update_visible_area)
if ACTIVATE_OPACITY:
self.goe = QGraphicsOpacityEffect()
self.setGraphicsEffect(self.goe)
self.goe.setOpacity(settings.MINIMAP_MIN_OPACITY)
self.animation = QPropertyAnimation(self.goe, "opacity")
self.slider = SliderArea(self)
self.slider.show()
def __calculate_max(self):
line_height = self._parent.cursorRect().height()
if line_height > 0:
self.lines_count = self._parent.viewport().height() / line_height
self.slider.update_position()
self.update_visible_area()
def set_code(self, source):
self.setPlainText(source)
self.__calculate_max()
def adjust_to_parent(self):
self.setFixedHeight(self._parent.height())
self.setFixedWidth(self._parent.width() * settings.SIZE_PROPORTION)
x = self._parent.width() - self.width()
self.move(x, 0)
fontsize = int(self.width() / settings.MARGIN_LINE)
if fontsize < 1:
fontsize = 1
font = self.document().defaultFont()
font.setPointSize(fontsize)
self.setFont(font)
self.__calculate_max()
def update_visible_area(self):
if not self.slider.pressed:
line_number = self._parent.firstVisibleBlock().blockNumber()
block = self.document().findBlockByLineNumber(line_number)
cursor = self.textCursor()
cursor.setPosition(block.position())
rect = self.cursorRect(cursor)
self.setTextCursor(cursor)
self.slider.move_slider(rect.y())
def enterEvent(self, event):
if ACTIVATE_OPACITY:
self.animation.setDuration(300)
self.animation.setStartValue(settings.MINIMAP_MIN_OPACITY)
self.animation.setEndValue(settings.MINIMAP_MAX_OPACITY)
self.animation.start()
def leaveEvent(self, event):
if ACTIVATE_OPACITY:
self.animation.setDuration(300)
self.animation.setStartValue(settings.MINIMAP_MAX_OPACITY)
self.animation.setEndValue(settings.MINIMAP_MIN_OPACITY)
self.animation.start()
def mousePressEvent(self, event):
super(MiniMap, self).mousePressEvent(event)
cursor = self.cursorForPosition(event.pos())
self._parent.jump_to_line(cursor.blockNumber())
def resizeEvent(self, event):
super(MiniMap, self).resizeEvent(event)
self.slider.update_position()
def scroll_area(self, pos_parent, pos_slider):
pos_parent.setY(pos_parent.y() - pos_slider.y())
cursor = self.cursorForPosition(pos_parent)
self._parent.verticalScrollBar().setValue(cursor.blockNumber())
def wheelEvent(self, event):
super(MiniMap, self).wheelEvent(event)
self._parent.wheelEvent(event)
class SliderArea(QFrame):
def __init__(self, parent):
super(SliderArea, self).__init__(parent)
self._parent = parent
self.setMouseTracking(True)
self.setCursor(Qt.OpenHandCursor)
color = resources.CUSTOM_SCHEME.get('current-line',
resources.COLOR_SCHEME['current-line'])
if ACTIVATE_OPACITY:
self.setStyleSheet("background: %s;" % color)
self.goe = QGraphicsOpacityEffect()
self.setGraphicsEffect(self.goe)
self.goe.setOpacity(settings.MINIMAP_MAX_OPACITY / 2)
else:
self.setStyleSheet("background: transparent;")
self.pressed = False
self.__scroll_margins = None
def paintEvent(self, event):
"""Paint over the widget to overlay its content."""
if not ACTIVATE_OPACITY:
painter = QPainter()
painter.begin(self)
painter.setRenderHint(QPainter.TextAntialiasing, True)
painter.setRenderHint(QPainter.Antialiasing, True)
painter.fillRect(event.rect(), QBrush(
QColor(255, 255, 255, 80)))
painter.setPen(QPen(Qt.NoPen))
painter.end()
super(SliderArea, self).paintEvent(event)
def update_position(self):
font_size = QFontMetrics(self._parent.font()).height()
height = self._parent.lines_count * font_size
self.setFixedHeight(height)
self.setFixedWidth(self._parent.width())
self.__scroll_margins = (height, self._parent.height() - height)
def move_slider(self, y):
self.move(0, y)
def mousePressEvent(self, event):
super(SliderArea, self).mousePressEvent(event)
self.pressed = True
self.setCursor(Qt.ClosedHandCursor)
def mouseReleaseEvent(self, event):
super(SliderArea, self).mouseReleaseEvent(event)
self.pressed = False
self.setCursor(Qt.OpenHandCursor)
def mouseMoveEvent(self, event):
super(SliderArea, self).mouseMoveEvent(event)
if self.pressed:
pos = self.mapToParent(event.pos())
y = pos.y() - (self.height() / 2)
if y < 0:
y = 0
if y < self.__scroll_margins[0]:
self._parent.verticalScrollBar().setSliderPosition(
self._parent.verticalScrollBar().sliderPosition() - 2)
elif y > self.__scroll_margins[1]:
self._parent.verticalScrollBar().setSliderPosition(
self._parent.verticalScrollBar().sliderPosition() + 2)
self.move(0, y)
self._parent.scroll_area(pos, event.pos())