Source

codeaide / codeaide / modes / sidebar.py

Full commit
from codeaide.base import *



class SidebarWidget(QWidget):


    def __init__(self, textedit):
        QWidget.__init__(self, textedit)
        self.setContentsMargins(0, 0, 0, 0)
        self.textedit = textedit
        palette = QPalette()
        palette.setColor(self.backgroundRole(), QColor("lightgrey"))
        self.setPalette(palette)
        self.hblayout = QHBoxLayout(self)
        self.hblayout.setContentsMargins(0, 0, 0, 0)
        self.hblayout.setSpacing(0)
        self.setLayout(self.hblayout)


    def add_widget(self, widget):
        widget.setContentsMargins(0, 0, 0, 0)
        self.layout().addWidget(widget)        
        width = self.sizeHint().width()
        self.setFixedWidth(width)
        self.textedit.setViewportMargins(width, 0, 0, 0)




class Sidebar(ModeBase):

    
    def init(self, SidebarWidgetClass=SidebarWidget):
        self.widget = SidebarWidgetClass(self.textedit)
        self.textedit.connect(
            self.textedit, SIGNAL("visible_blocks_changed()"), 
                self.on_visible_blocks_changed)


    def on_visible_blocks_changed(self):
        self.widget.emit(SIGNAL("updateSidebar()"))


    def resize_hook(self, event):
        cr = self.textedit.contentsRect()
        self.widget.setGeometry(
            cr.left(), cr.top(), self.widget.width(), cr.height())
        self.widget.emit(SIGNAL("updateSidebar()"))



class SidebarModeBase(ModeBase):

    requires = [Sidebar]


    def init(self):
        self.sidebar = self.textedit.find_mode(Sidebar)
        self.sidebar_widget = self.sidebar.widget
        self.init_sidebar()


    def init_sidebar(self):
        pass





class LineNumbersWidget(QWidget):

    
    def __init__(self, mode):
        self.textedit = mode.textedit
        QWidget.__init__(self, self.textedit)
        self.sidebar = mode.sidebar
        mode.sidebar_widget.connect(
            mode.sidebar_widget, SIGNAL("updateSidebar()"), self.update)
        self.text_pen = QPen(QColor(self.textedit.scheme.line_number_color))
        self.back_brush = QBrush(
            QColor(self.textedit.scheme.line_number_background))


    def sizeHint(self):
        return QSize(self.parent().fontMetrics().width("W") * 4, 0)


    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(event.rect(), self.back_brush)
        painter.setPen(self.text_pen)
        w = self.width() - 2
        for vb in self.textedit.visible_blocks: 
            painter.drawText(
                    0, vb.top, w, vb.height, Qt.AlignRight, str(vb.row))
        return QWidget.paintEvent(self, event)




class LineNumbers(SidebarModeBase):


    def init_sidebar(self):
        self.widget = LineNumbersWidget(self)    
        self.sidebar_widget.add_widget(self.widget)




class LineStatusWidget(QWidget):


    def __init__(self, mode):
        self.sidebar = mode.sidebar
        self.sidebar_widget = mode.sidebar_widget
        self.textedit = mode.textedit
        QWidget.__init__(self, self.textedit)
        self.modified_pen = QPen(QColor("red"))
        self.connect(self.textedit, SIGNAL("newText()"),
                              self.on_new_text)
        self.connect(self.textedit.document(), SIGNAL("contentsChange(int,int,int)"),
                     self.on_contents_change)
        self.setMouseTracking(True)
        self.sidebar_widget.connect(
            self.sidebar_widget, SIGNAL("updateSidebar()"), self.update)


    def on_new_text(self):
        block = self.textedit.document().begin()
        while block and block.isValid():
            user_data = self.textedit.user_data(block)
            user_data["status"] = 0
            user_data["revision"] = 0
            user_data["backup"] = block.text()
            block = block.next()


    def mouseMoveEvent(self, event):
        pos = event.pos()
        y = pos.y()
        for vb in self.textedit.visible_blocks:
            left, top, width, height =  vb.rect
            if y > top and y < top+height:
                user_data = vb.block.userData()
                if not user_data:
                    return
                backup = user_data.get("backup")
                if backup is None:
                    return
                QToolTip.showText(
                    self.mapToGlobal(pos), backup, self)



    def on_contents_change(self, position, chars_removed, chars_added):
        if chars_removed == 0 and chars_added == 0:
            # style changes (highlighting, ..)
            return
        textedit = self.textedit
        doc = textedit.document()
        start_block = doc.findBlock(position - chars_removed)
        end_block = doc.findBlock(position + chars_added)
        block = start_block
        update = False
        while block and block.isValid():
            user_data = textedit.user_data(block)
            if user_data.get("backup") != block.text():
                if user_data.get("status", 0) != 1:
                    user_data["status"] = 1
                    user_data["revision"] = user_data.get("revision", 0) + 1
                    update = True
            if block == end_block:
                break
            block = block.next()
        # XXX: updating only changed area might be slower
        # because of more Python calls?
        if update:
            self.update()


    def sizeHint(self):
        return QSize(2, 0)


    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(self.modified_pen)
        for vb in self.textedit.visible_blocks:
            user_data = vb.block.userData()
            if not user_data or user_data["status"] == 1:
                left, top, right, height = vb.rect
                # XXX: check if in event.rect
                painter.drawLine(0, top, 0, top+height)
                painter.drawLine(1, top, 1, top+height)
        return QWidget.paintEvent(self, event)




class LineStatus(SidebarModeBase):
    
    
    def init_sidebar(self):
        self.widget = LineStatusWidget(self)
        self.sidebar.widget.add_widget(self.widget)