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.fm = self.textedit.fontMetrics()
        self.blocks_changed(0)
        self.textedit.connect(
            self.textedit, SIGNAL("blockCountChanged(int)"), 
                self.blocks_changed)
        self.textedit.connect(self.textedit.verticalScrollBar(),
                   SIGNAL("valueChanged(int)"),
                   self.blocks_changed)


    def blocks_changed(self, num):
        lines = []
        te = self.textedit
        block = te.firstVisibleBlock()
        row = block.blockNumber() + 1
        width = self.widget.width()
        w  = width -2
        h = self.fm.height()

        bbgeom = te.blockBoundingGeometry(block)
        top = bbgeom.translated(te.contentOffset()).top()
        bottom = top + bbgeom.height()
        ebottom_top = 0 #event.rect().top()
        ebottom_bottom = self.widget.height() #event.rect().bottom()
        while block.isValid() and top <= ebottom_bottom:
            if block.isVisible() and bottom >= ebottom_top:
                lines.append(
                   ((row, block), (0, top, w, h))
                   )
            block = block.next()
            row += 1
            top = bottom
            bottom = top + te.blockBoundingRect(block).height()
        self.lines = lines
        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):
        QWidget.__init__(self, mode.textedit)
        self.sidebar = mode.sidebar
        mode.sidebar_widget.connect(
            mode.sidebar_widget, SIGNAL("updateSidebar()"), self.update)
        self.textpen = QPen(QColor(0x80, 0x80, 0x80))


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


    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(self.textpen)
        w = self.width()
        for (row, block), (left, top, width, height) in self.sidebar.lines: 
            painter.drawText(
                    0, top, w, height, Qt.AlignRight, str(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.textedit.connect(self.textedit, SIGNAL("textChanged()"), 
            self.on_text_changed)
        self.setMouseTracking(True)
        self.sidebar_widget.connect(
            self.sidebar_widget, SIGNAL("updateSidebar()"), self.update)


    def mouseMoveEvent(self, event):
        pos = event.pos()
        y = pos.y()
        for (row, block), (left, top, width, height) in self.sidebar.lines:
            if y > top and y < top+height:
                user_data = 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_text_changed(self):
        # XXX: does not fully work with selecions
        cursor = self.textedit.textCursor()
        block = cursor.block()
        previous = block.previous()
        if previous and previous.isValid():
            pud = self.textedit.user_data(previous)
            if pud.get("backup") != previous.text():
                pud["status"] = 1
        user_data = self.textedit.user_data(block)
        user_data["status"] = 1
        self.update()   


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


    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(self.modified_pen)
        for (row, block), (left, top, width, height) in self.sidebar.lines:
            user_data = block.userData()
            if not user_data or user_data["status"] == 1:
                painter.drawLine(left, top, left, top+height)
                painter.drawLine(left+1, top, left+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)