Exemplo n.º 1
0
    def __init__(self,
                 arg_provider,
                 parent=None,
                 complete=False,
                 encoding=None,
                 filter_options=None,
                 ui_mode=True,
                 allow_refresh=True):

        title = [gettext("Diff"), gettext("Loading...")]
        QBzrWindow.__init__(self, title, parent, ui_mode=ui_mode)
        self.restoreSize("diff", (780, 580))

        self.trees = None
        self.encoding = encoding
        self.arg_provider = arg_provider
        self.filter_options = filter_options
        if filter_options is None:
            self.filter_options = FilterOptions(all_enable=True)
        self.complete = complete
        self.ignore_whitespace = False
        self.delayed_signal_connections = []

        self.diffview = SidebySideDiffView(self)
        self.sdiffview = SimpleDiffView(self)
        self.views = (self.diffview, self.sdiffview)
        for view in self.views:
            view.set_complete(complete)

        self.stack = QtGui.QStackedWidget(self.centralwidget)
        self.stack.addWidget(self.diffview)
        self.stack.addWidget(self.sdiffview)
        vbox = QtGui.QVBoxLayout(self.centralwidget)
        vbox.addWidget(self.stack)

        # Don't use a custom tab width by default
        # Indices are left side, right side and unidiff
        # respectively
        self.custom_tab_widths = [-1, -1, -1]

        for browser in self.diffview.browsers:
            browser.installEventFilter(self)

        self.create_main_toolbar(allow_refresh)
        self.addToolBarBreak()
        self.find_toolbar = FindToolbar(self, self.diffview.browsers,
                                        self.show_find)
        self.find_toolbar.hide()
        self.addToolBar(self.find_toolbar)
        setup_guidebar_for_find(self.sdiffview, self.find_toolbar, 1)
        for gb in self.diffview.guidebar_panels:
            setup_guidebar_for_find(gb, self.find_toolbar, 1)
        self.diff_context = ExtDiffContext(self)
Exemplo n.º 2
0
    def __init__(self,
                 branch,
                 working_tree,
                 annotate_tree,
                 path,
                 fileId,
                 encoding=None,
                 parent=None,
                 ui_mode=True,
                 no_graph=False,
                 loader=None,
                 loader_args=None,
                 activate_line=None):
        QBzrWindow.__init__(
            self,
            [gettext("Annotate"), gettext("Loading...")],
            parent,
            ui_mode=ui_mode)
        self.restoreSize("annotate", (780, 680))

        self.activate_line_after_load = activate_line

        self.windows = []

        self.branch = branch
        self.annotate_tree = annotate_tree
        self.working_tree = working_tree
        if (self.working_tree is None
                and isinstance(annotate_tree, WorkingTree)):
            self.working_tree = annotate_tree

        self.no_graph = no_graph
        self.old_lines = None

        self.fileId = fileId
        self.path = path
        self.encoding = encoding
        self.loader_func = loader
        self.loader_args = loader_args

        self.throbber = ToolBarThrobberWidget(self)

        self.text_edit_frame = AnnotateEditerFrameBase(self)
        self.text_edit = AnnotatedTextEdit(self)
        self.text_edit.setFrameStyle(QtGui.QFrame.NoFrame)
        self.text_edit.setTextInteractionFlags(
            QtCore.Qt.TextSelectableByMouse
            | QtCore.Qt.TextSelectableByKeyboard)
        self.text_edit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap)

        self.text_edit.document().setDefaultFont(get_monospace_font())

        self.guidebar_panel = GuideBarPanel(self.text_edit, parent=self)
        self.guidebar_panel.add_entry('annotate', QtGui.QColor(255, 160, 180))
        self.annotate_bar = AnnotateBar(self.text_edit, self, self.get_revno)
        annotate_spliter = QtGui.QSplitter(QtCore.Qt.Horizontal, self)
        annotate_spliter.addWidget(self.annotate_bar)
        annotate_spliter.addWidget(self.guidebar_panel)
        self.annotate_bar.splitter = annotate_spliter
        self.text_edit_frame.hbox.addWidget(annotate_spliter)

        self.connect(self.text_edit, QtCore.SIGNAL("cursorPositionChanged()"),
                     self.edit_cursorPositionChanged)
        self.connect(self.annotate_bar,
                     QtCore.SIGNAL("cursorPositionChanged()"),
                     self.edit_cursorPositionChanged)
        self.connect(self.text_edit, QtCore.SIGNAL("documentChangeFinished()"),
                     self.edit_documentChangeFinished)

        self.log_list = AnnotateLogList(self.processEvents, self.throbber,
                                        self)
        self.log_list.header().hideSection(logmodel.COL_DATE)
        self.log_list.parent_annotate_window = self
        self.log_branch_loaded = False

        self.connect(
            self.log_list.selectionModel(),
            QtCore.SIGNAL("selectionChanged(QItemSelection, QItemSelection)"),
            self.log_list_selectionChanged)

        self.message = LogListRevisionMessageBrowser(self.log_list, self)

        self.encoding_selector = EncodingMenuSelector(
            self.encoding, gettext("Encoding"), self._on_encoding_changed)

        hsplitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
        hsplitter.addWidget(self.log_list)
        hsplitter.addWidget(self.message)

        hsplitter.setStretchFactor(0, 2)
        hsplitter.setStretchFactor(1, 2)

        splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
        splitter.addWidget(self.text_edit_frame)
        splitter.addWidget(hsplitter)

        splitter.setStretchFactor(0, 5)
        splitter.setStretchFactor(1, 2)

        vbox = QtGui.QVBoxLayout(self.centralwidget)
        #vbox.addWidget(self.toolbar)
        vbox.addWidget(splitter)
        self.text_edit.setFocus()

        self.show_find = QtGui.QAction(get_icon("edit-find"), gettext("Find"),
                                       self)
        self.show_find.setShortcuts(QtGui.QKeySequence.Find)
        self.show_find.setCheckable(True)

        self.show_goto_line = QtGui.QAction(get_icon("go-jump"),
                                            gettext("Goto Line"), self)
        self.show_goto_line.setShortcuts((QtCore.Qt.CTRL + QtCore.Qt.Key_L, ))
        self.show_goto_line.setCheckable(True)

        show_view_menu = QtGui.QAction(get_icon("document-properties"),
                                       gettext("&View Options"), self)
        view_menu = QtGui.QMenu(gettext('View Options'), self)
        show_view_menu.setMenu(view_menu)

        word_wrap = QtGui.QAction(gettext("Word Wrap"), self)
        word_wrap.setCheckable(True)
        self.connect(word_wrap, QtCore.SIGNAL("toggled (bool)"),
                     self.word_wrap_toggle)

        def setTabStopWidth(tw):
            self.text_edit.setTabStopWidth(
                get_tab_width_pixels(tab_width_chars=tw))
            get_set_tab_width_chars(branch=self.branch, tab_width_chars=tw)

        self.tab_width_selector = TabWidthMenuSelector(
            get_set_tab_width_chars(branch=branch), gettext("Tab Width"),
            setTabStopWidth)

        view_menu.addMenu(self.tab_width_selector)
        view_menu.addMenu(self.encoding_selector)
        view_menu.addAction(word_wrap)

        toolbar = self.addToolBar(gettext("Annotate"))
        toolbar.setMovable(False)
        toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)

        #self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
        toolbar.addAction(self.show_find)
        toolbar.addAction(self.show_goto_line)
        toolbar.addAction(show_view_menu)
        toolbar.widgetForAction(show_view_menu).setPopupMode(
            QtGui.QToolButton.InstantPopup)

        spacer = QtGui.QWidget()
        spacer.setSizePolicy(QtGui.QSizePolicy.Expanding,
                             QtGui.QSizePolicy.Expanding)
        toolbar.addWidget(spacer)
        toolbar.addWidget(self.throbber)

        self.addToolBarBreak()

        self.find_toolbar = FindToolbar(self, self.text_edit, self.show_find)
        self.find_toolbar.hide()
        self.addToolBar(self.find_toolbar)
        self.connect(self.show_find, QtCore.SIGNAL("toggled (bool)"),
                     self.show_find_toggle)
        setup_guidebar_for_find(self.guidebar_panel,
                                self.find_toolbar,
                                index=1)

        self.goto_line_toolbar = GotoLineToolbar(self, self.show_goto_line)
        self.goto_line_toolbar.hide()
        self.addToolBar(self.goto_line_toolbar)

        self.connect(self.show_goto_line, QtCore.SIGNAL("toggled (bool)"),
                     self.show_goto_line_toggle)

        self.__hashes = {}
Exemplo n.º 3
0
class AnnotateWindow(QBzrWindow):
    def __init__(self,
                 branch,
                 working_tree,
                 annotate_tree,
                 path,
                 fileId,
                 encoding=None,
                 parent=None,
                 ui_mode=True,
                 no_graph=False,
                 loader=None,
                 loader_args=None,
                 activate_line=None):
        QBzrWindow.__init__(
            self,
            [gettext("Annotate"), gettext("Loading...")],
            parent,
            ui_mode=ui_mode)
        self.restoreSize("annotate", (780, 680))

        self.activate_line_after_load = activate_line

        self.windows = []

        self.branch = branch
        self.annotate_tree = annotate_tree
        self.working_tree = working_tree
        if (self.working_tree is None
                and isinstance(annotate_tree, WorkingTree)):
            self.working_tree = annotate_tree

        self.no_graph = no_graph
        self.old_lines = None

        self.fileId = fileId
        self.path = path
        self.encoding = encoding
        self.loader_func = loader
        self.loader_args = loader_args

        self.throbber = ToolBarThrobberWidget(self)

        self.text_edit_frame = AnnotateEditerFrameBase(self)
        self.text_edit = AnnotatedTextEdit(self)
        self.text_edit.setFrameStyle(QtGui.QFrame.NoFrame)
        self.text_edit.setTextInteractionFlags(
            QtCore.Qt.TextSelectableByMouse
            | QtCore.Qt.TextSelectableByKeyboard)
        self.text_edit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap)

        self.text_edit.document().setDefaultFont(get_monospace_font())

        self.guidebar_panel = GuideBarPanel(self.text_edit, parent=self)
        self.guidebar_panel.add_entry('annotate', QtGui.QColor(255, 160, 180))
        self.annotate_bar = AnnotateBar(self.text_edit, self, self.get_revno)
        annotate_spliter = QtGui.QSplitter(QtCore.Qt.Horizontal, self)
        annotate_spliter.addWidget(self.annotate_bar)
        annotate_spliter.addWidget(self.guidebar_panel)
        self.annotate_bar.splitter = annotate_spliter
        self.text_edit_frame.hbox.addWidget(annotate_spliter)

        self.connect(self.text_edit, QtCore.SIGNAL("cursorPositionChanged()"),
                     self.edit_cursorPositionChanged)
        self.connect(self.annotate_bar,
                     QtCore.SIGNAL("cursorPositionChanged()"),
                     self.edit_cursorPositionChanged)
        self.connect(self.text_edit, QtCore.SIGNAL("documentChangeFinished()"),
                     self.edit_documentChangeFinished)

        self.log_list = AnnotateLogList(self.processEvents, self.throbber,
                                        self)
        self.log_list.header().hideSection(logmodel.COL_DATE)
        self.log_list.parent_annotate_window = self
        self.log_branch_loaded = False

        self.connect(
            self.log_list.selectionModel(),
            QtCore.SIGNAL("selectionChanged(QItemSelection, QItemSelection)"),
            self.log_list_selectionChanged)

        self.message = LogListRevisionMessageBrowser(self.log_list, self)

        self.encoding_selector = EncodingMenuSelector(
            self.encoding, gettext("Encoding"), self._on_encoding_changed)

        hsplitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
        hsplitter.addWidget(self.log_list)
        hsplitter.addWidget(self.message)

        hsplitter.setStretchFactor(0, 2)
        hsplitter.setStretchFactor(1, 2)

        splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
        splitter.addWidget(self.text_edit_frame)
        splitter.addWidget(hsplitter)

        splitter.setStretchFactor(0, 5)
        splitter.setStretchFactor(1, 2)

        vbox = QtGui.QVBoxLayout(self.centralwidget)
        #vbox.addWidget(self.toolbar)
        vbox.addWidget(splitter)
        self.text_edit.setFocus()

        self.show_find = QtGui.QAction(get_icon("edit-find"), gettext("Find"),
                                       self)
        self.show_find.setShortcuts(QtGui.QKeySequence.Find)
        self.show_find.setCheckable(True)

        self.show_goto_line = QtGui.QAction(get_icon("go-jump"),
                                            gettext("Goto Line"), self)
        self.show_goto_line.setShortcuts((QtCore.Qt.CTRL + QtCore.Qt.Key_L, ))
        self.show_goto_line.setCheckable(True)

        show_view_menu = QtGui.QAction(get_icon("document-properties"),
                                       gettext("&View Options"), self)
        view_menu = QtGui.QMenu(gettext('View Options'), self)
        show_view_menu.setMenu(view_menu)

        word_wrap = QtGui.QAction(gettext("Word Wrap"), self)
        word_wrap.setCheckable(True)
        self.connect(word_wrap, QtCore.SIGNAL("toggled (bool)"),
                     self.word_wrap_toggle)

        def setTabStopWidth(tw):
            self.text_edit.setTabStopWidth(
                get_tab_width_pixels(tab_width_chars=tw))
            get_set_tab_width_chars(branch=self.branch, tab_width_chars=tw)

        self.tab_width_selector = TabWidthMenuSelector(
            get_set_tab_width_chars(branch=branch), gettext("Tab Width"),
            setTabStopWidth)

        view_menu.addMenu(self.tab_width_selector)
        view_menu.addMenu(self.encoding_selector)
        view_menu.addAction(word_wrap)

        toolbar = self.addToolBar(gettext("Annotate"))
        toolbar.setMovable(False)
        toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)

        #self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
        toolbar.addAction(self.show_find)
        toolbar.addAction(self.show_goto_line)
        toolbar.addAction(show_view_menu)
        toolbar.widgetForAction(show_view_menu).setPopupMode(
            QtGui.QToolButton.InstantPopup)

        spacer = QtGui.QWidget()
        spacer.setSizePolicy(QtGui.QSizePolicy.Expanding,
                             QtGui.QSizePolicy.Expanding)
        toolbar.addWidget(spacer)
        toolbar.addWidget(self.throbber)

        self.addToolBarBreak()

        self.find_toolbar = FindToolbar(self, self.text_edit, self.show_find)
        self.find_toolbar.hide()
        self.addToolBar(self.find_toolbar)
        self.connect(self.show_find, QtCore.SIGNAL("toggled (bool)"),
                     self.show_find_toggle)
        setup_guidebar_for_find(self.guidebar_panel,
                                self.find_toolbar,
                                index=1)

        self.goto_line_toolbar = GotoLineToolbar(self, self.show_goto_line)
        self.goto_line_toolbar.hide()
        self.addToolBar(self.goto_line_toolbar)

        self.connect(self.show_goto_line, QtCore.SIGNAL("toggled (bool)"),
                     self.show_goto_line_toggle)

        self.__hashes = {}

    def show(self):
        QBzrWindow.show(self)
        QtCore.QTimer.singleShot(0, self.initial_load)

    @runs_in_loading_queue
    @ui_current_widget
    @reports_exception()
    def initial_load(self):
        """Called to perform the initial load of the form.  Enables a
        throbber window, then loads the branches etc if they weren't specified
        in our constructor.
        """
        self.throbber.show()
        try:
            if self.loader_func is not None:
                (self.branch, self.annotate_tree, self.working_tree, self.path,
                 self.fileId) = self.loader_func(*self.loader_args)
                self.loader_func = self.loader_args = None  # kill extra refs...
                QtCore.QCoreApplication.processEvents()
            self.encoding = get_set_encoding(self.encoding, self.branch)
            self.encoding_selector.encoding = self.encoding
            self.branch.lock_read()
            try:
                self.set_annotate_title()
                self.annotate(self.annotate_tree, self.fileId, self.path)
            finally:
                self.branch.unlock()
        finally:
            self.throbber.hide()
        QtCore.QTimer.singleShot(1, self._activate_line)

    def _activate_line(self):
        # [bialix 2012/09/14] I have to use QtCore.QTimer.singleShot because
        # otherwise the line with uncommitted changes is not highlighted
        # properly: nothing appears in the revision message browser at all.
        # I didn't find the reason behind it, shame on me.
        n = self.activate_line_after_load
        self.activate_line_after_load = None  # clear the line number just in case
        if n:
            if n == 1:
                # [bialix 2012/09/13] actually after loading annotation
                # document implictly sets current position to the first line.
                # So we're unable to change position to 1st line.
                # Make a hack: select 2nd line, and then actually select 1st one.
                self.go_to_line(2)
            self.go_to_line(n)

    def set_annotate_title(self):
        # and update the title to show we are done.
        if isinstance(self.annotate_tree, RevisionTree):
            revno = self.get_revno(self.annotate_tree.get_revision_id())
            if revno:
                self.set_title_and_icon([
                    gettext("Annotate"), self.path,
                    gettext("Revision %s") % revno
                ])
                return

        self.set_title_and_icon([gettext("Annotate"), self.path])

    def get_revno(self, revid):
        gv = self.log_list.log_model.graph_viz
        if (gv and revid in gv.revid_rev):
            return gv.revid_rev[revid].revno_str
        return ""

    def annotate(self, annotate_tree, fileId, path):
        self.now = time.time()
        self.rev_indexes = {}
        last_revid = None
        lines = []
        annotate = []
        ordered_revids = []

        self.processEvents()
        for revid, text in annotate_tree.annotate_iter(fileId):
            if revid == CURRENT_REVISION:
                revid = CURRENT_REVISION + annotate_tree.basedir

            text = text.decode(self.encoding, 'replace')

            lines.append(text)

            text = text.rstrip()
            if revid not in self.rev_indexes:
                self.rev_indexes[revid] = []
                ordered_revids.append(revid)
            self.rev_indexes[revid].append(len(annotate))

            is_top = last_revid != revid
            last_revid = revid

            annotate.append((revid, is_top))
            if len(annotate) % 100 == 0:
                self.processEvents()
        annotate.append((None, False))  # because the view has one more line

        new_positions = None
        if self.old_lines:
            # Try keep the scroll, and selection stable.
            old_positions, lines_to_center = self.text_edit.get_positions()
            new_positions = self.translate_positions(self.old_lines, lines,
                                                     old_positions)

        self.text_edit.annotate = None
        self.text_edit.setPlainText("".join(lines))
        if new_positions:
            self.text_edit.set_positions(new_positions, lines_to_center)

        self.old_lines = lines
        self.annotate_bar.adjustWidth(len(lines), 999)
        self.annotate_bar.annotate = annotate
        self.text_edit.annotate = annotate
        self.annotate_bar.show_current_line = False

        self.text_edit.emit(QtCore.SIGNAL("documentChangeFinished()"))

        self.processEvents()

        just_loaded_log = False
        if not self.log_branch_loaded:
            self.log_branch_loaded = True
            bi = BranchInfo('', self.working_tree, self.branch)
            self.log_list.load((bi, ), bi, [self.fileId], self.no_graph,
                               logmodel.WithWorkingTreeGraphVizLoader)

            gv = self.log_list.log_model.graph_viz
            self.annotate_bar.adjustWidth(len(lines),
                                          gv.revisions[0].revno_sequence[0])

            just_loaded_log = True

            # Show the revisions the we know about from the annotate.
            filter = self.log_list.log_model.file_id_filter
            changed_revs = []
            for revid in self.rev_indexes.keys():
                rev = gv.revid_rev[revid]
                filter.filter_file_id[rev.index] = True
                changed_revs.append(rev)
            filter.filter_changed_callback(changed_revs, last_call=True)

        self.processEvents()
        highlight_document(self.text_edit, path)
        self.processEvents()
        load_revisions(ordered_revids,
                       self.branch.repository,
                       revisions_loaded=self.revisions_loaded,
                       pass_prev_loaded_rev=True)
        self.processEvents()

        if just_loaded_log:
            # Check for any other revisions we don't know about

            filter = self.log_list.log_model.file_id_filter
            revids = [
                rev.revid for rev in gv.revisions
                if rev.revid not in self.rev_indexes
            ]
            filter.load(revids)

    def translate_positions(self, old_lines, new_lines, old_positions):
        sm = SequenceMatcher(None, old_lines, new_lines)
        opcodes = sm.get_opcodes()
        new_positions = [None for x in range(len(old_positions))]
        opcode_len = lambda start, end, lines: sum(
            [len(l) for l in lines[start:end]])
        for i, old_pos in enumerate(old_positions):
            old_char_start = 0
            new_char_start = 0
            for code, old_start, old_end, new_start, new_end in opcodes:
                old_len = opcode_len(old_start, old_end, old_lines)
                new_len = opcode_len(new_start, new_end, new_lines)
                if (old_pos >= old_char_start
                        and old_pos < old_char_start + old_len):
                    if code == 'delete':
                        new_pos = new_char_start
                    elif (code == 'replace' and len(opcodes) > 1):
                        # XXX This should cache the opcodes if we do the same
                        # block more than once.
                        new_inner_pos = self.translate_positions(
                            ''.join(old_lines[old_start:old_end]),
                            ''.join(new_lines[new_start:new_end]),
                            [old_pos - old_char_start])[0]
                        new_pos = new_char_start + new_inner_pos
                    else:
                        new_pos = new_char_start + (old_pos - old_char_start)
                    new_positions[i] = new_pos
                    break
                old_char_start += old_len
                new_char_start += new_len
        return new_positions

    def revisions_loaded(self, revisions, last_call):
        for rev in revisions.itervalues():
            authors = rev.get_apparent_authors()
            emails = map(self._maybe_extract_email, authors)
            author_id = ';'.join(emails)

            if rev.timestamp is None:
                days = sys.maxint
            elif self.now < rev.timestamp:
                days = 0
            else:
                days = (self.now - rev.timestamp) / (24 * 60 * 60)

            alpha = 0.5 / ((days / 50) + 1)
            h_sh = self._get_hash(author_id)
            hue = 1 - float(h_sh) / sys.maxint
            color = QtGui.QColor.fromHsvF(hue, 1, 1, alpha)
            brush = QtGui.QBrush(color)

            self.annotate_bar.rev_colors[rev.revision_id] = brush
            self.text_edit.rev_colors[rev.revision_id] = brush

        self.annotate_bar.update()
        self.text_edit.update()

    def _maybe_extract_email(self, author):
        name, email = parse_username(author)
        if email:
            return email
        return author

    def _get_hash(self, author_id):
        h = self.__hashes.get(author_id)
        if h is None:
            h = abs(hash(author_id))
            #h = abs(hash(hashlib.md5(author_id).hexdigest())) # maybe increase enthropy via md5?
            # XXX [bialix 2012/03/30]
            # I don't really like how we create colors for annotation.
            # Also, abs(hash) loses 1 bit of the hash result.
            # I think we can improve our algorithm to use more distinctive colors
            # maybe we should use md5 as more enthropy source for hash,
            # or maybe we can use some sort of predefined set of colors
            # and each next author will use color from that set,
            # but in this case we lose consistency of colors for the same author
            # between different annotations
            # I have no clever idea yet.
            self.__hashes[author_id] = h
        return h

    def edit_cursorPositionChanged(self):
        current_line = self.text_edit.document().findBlock(
            self.text_edit.textCursor().position()).blockNumber()
        if self.text_edit.annotate:
            rev_id, is_top = self.text_edit.annotate[current_line]
            self.log_list.select_revid(rev_id)

    def edit_documentChangeFinished(self):
        self.annotate_bar.update_highlight_lines()
        self.guidebar_panel.update_data(
            annotate=self.annotate_bar.highlight_lines)

    @runs_in_loading_queue
    def set_annotate_revision(self):
        self.throbber.show()
        try:
            self.branch.lock_read()
            try:
                revid = str(self.log_list.currentIndex().data(
                    logmodel.RevIdRole).toString())
                if revid.startswith(CURRENT_REVISION):
                    rev = cached_revisions[revid]
                    self.annotate_tree = self.working_tree
                else:
                    self.annotate_tree = self.branch.repository.revision_tree(
                        revid)
                self.path = self.annotate_tree.id2path(self.fileId)
                self.set_annotate_title()
                self.processEvents()
                self.annotate(self.annotate_tree, self.fileId, self.path)
            finally:
                self.branch.unlock()
        finally:
            self.throbber.hide()

    @runs_in_loading_queue
    def _on_encoding_changed(self, encoding):
        self.encoding = encoding
        get_set_encoding(encoding, self.branch)
        self.throbber.show()
        try:
            self.branch.lock_read()
            try:
                self.annotate(self.annotate_tree, self.fileId, self.path)
            finally:
                self.branch.unlock()
        finally:
            self.throbber.hide()

    def log_list_selectionChanged(self, selected, deselected):
        revids = self.log_list.get_selection_and_merged_revids()
        self.annotate_bar.highlight_revids = revids
        self.guidebar_panel.update_data(
            annotate=self.annotate_bar.highlight_lines)
        self.annotate_bar.update()

    def show_find_toggle(self, state):
        if state:
            self.show_goto_line.setChecked(False)

    def show_goto_line_toggle(self, state):
        self.goto_line_toolbar.setVisible(state)
        if state:
            self.goto_line_toolbar.line_edit.setFocus()
            self.show_find.setChecked(False)
        else:
            self.goto_line_toolbar.line_edit.setText('')

    def word_wrap_toggle(self, state):
        if state:
            self.text_edit.setLineWrapMode(QtGui.QPlainTextEdit.WidgetWidth)
        else:
            self.text_edit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap)

    def go_to_line(self, line):
        doc = self.text_edit.document()
        cursor = QtGui.QTextCursor(doc)
        cursor.setPosition(doc.findBlockByNumber(line - 1).position())
        self.text_edit.setTextCursor(cursor)
        self.text_edit.centerCursor()
Exemplo n.º 4
0
    def __init__(self,
                 file_list=None,
                 directory=None,
                 complete=False,
                 encoding=None,
                 splitters=None,
                 parent=None,
                 select_all=False,
                 init_msg=None):
        ToolbarPanel.__init__(self, slender=False, icon_size=22, parent=parent)

        self.revision = None
        self.file_list = file_list
        self.directory = directory
        self.message = None

        self.initial_encoding = encoding
        self.select_all = select_all

        self.current_layout = -1
        self.load_settings()

        self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical, self)
        pal = QtGui.QPalette()
        pal.setColor(QtGui.QPalette.Window, QtGui.QColor(0, 0, 0, 0))
        self.splitter.setPalette(pal)

        self.splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal, self)
        self.splitter2 = QtGui.QSplitter(QtCore.Qt.Horizontal, self)
        self.splitter.addWidget(self.splitter1)
        self.splitter.addWidget(self.splitter2)

        message_groupbox = QtGui.QGroupBox(gettext("Message"), self)
        message_layout = QtGui.QVBoxLayout(message_groupbox)
        self.splitter1.addWidget(message_groupbox)

        language = get_global_config().get_user_option(
            'spellcheck_language') or 'en'
        spell_checker = SpellChecker(language)

        self.message = TextEdit(spell_checker,
                                message_groupbox,
                                main_window=self)
        self.message.setToolTip(gettext("Enter the shelve message"))
        self.connect(self.message, QtCore.SIGNAL("messageEntered()"),
                     self.do_shelve)
        self.completer = QtGui.QCompleter()
        self.completer_model = QtGui.QStringListModel(self.completer)
        self.completer.setModel(self.completer_model)
        self.message.setCompleter(self.completer)
        self.message.setAcceptRichText(False)
        if init_msg is not None:
            self.message.setText(init_msg)
        SpellCheckHighlighter(self.message.document(), spell_checker)

        message_layout.addWidget(self.message)

        self.file_view = QtGui.QTreeWidget(self)
        self.file_view.setHeaderLabels(
            [gettext("File Name"),
             gettext("Status"),
             gettext("Hunks")])
        header = self.file_view.header()
        header.setStretchLastSection(False)
        header.setResizeMode(0, QtGui.QHeaderView.Stretch)
        header.setResizeMode(1, QtGui.QHeaderView.ResizeToContents)
        header.setResizeMode(2, QtGui.QHeaderView.ResizeToContents)

        self.splitter1.addWidget(self.file_view)

        hunk_panel = ToolbarPanel(parent=self)
        self.hunk_view = HunkView(complete=complete)

        self.splitter2.addWidget(hunk_panel)

        # Build hunk panel toolbar
        show_find = hunk_panel.add_toolbar_button(
            N_("Find"),
            icon_name="edit-find",
            checkable=True,
            shortcut=QtGui.QKeySequence.Find)
        hunk_panel.add_separator()

        view_menu = QtGui.QMenu(gettext('View Options'), self)
        view_menu.addAction(
            hunk_panel.create_button(N_("Complete"),
                                     icon_name="complete",
                                     onclick=self.hunk_view.set_complete,
                                     checkable=True,
                                     checked=complete))
        self.tabwidth_selector = \
                TabWidthMenuSelector(label_text=gettext("Tab width"),
                    onChanged=self.on_tabwidth_changed)
        view_menu.addMenu(self.tabwidth_selector)

        self.encoding_selector = EncodingMenuSelector(encoding,
                                                      gettext("Encoding"),
                                                      self.encoding_changed)
        self.encoding_selector.setIcon(get_icon("format-text-bold", 16))
        view_menu.addMenu(self.encoding_selector)
        hunk_panel.add_toolbar_menu(N_("&View Options"),
                                    view_menu,
                                    icon_name="document-properties",
                                    shortcut="Alt+V")

        hunk_panel.add_separator()
        hunk_panel.add_toolbar_button(N_("Previous hunk"),
                                      icon_name="go-up",
                                      onclick=self.hunk_view.move_previous,
                                      shortcut="Alt+Up")
        hunk_panel.add_toolbar_button(N_("Next hunk"),
                                      icon_name="go-down",
                                      onclick=self.hunk_view.move_next,
                                      shortcut="Alt+Down")

        self.editor_button = hunk_panel.add_toolbar_button(
            N_("Use editor"),
            icon_name="accessories-text-editor",
            enabled=False,
            onclick=self.use_editor,
            shortcut="Ctrl+E")
        find_toolbar = FindToolbar(self, self.hunk_view.browser, show_find)
        hunk_panel.add_widget(find_toolbar)
        hunk_panel.add_widget(self.hunk_view)
        find_toolbar.hide()

        setup_guidebar_for_find(self.hunk_view.guidebar, find_toolbar, index=1)
        self.find_toolbar = find_toolbar

        layout = QtGui.QVBoxLayout()
        layout.setMargin(10)
        layout.addWidget(self.splitter)
        self.add_layout(layout)

        shelve_menu = QtGui.QMenu(gettext("Shelve"), self)
        shelve_menu.addAction(
            self.create_button(N_("Destroy"),
                               onclick=lambda: self.do_shelve(destroy=True)))

        self.add_toolbar_button(N_('Shelve'),
                                icon_name='shelve',
                                shortcut=QtGui.QKeySequence.Save,
                                onclick=self.do_shelve,
                                menu=shelve_menu)

        self.add_separator()

        self.add_toolbar_button(N_('Select all'),
                                icon_name='select-all',
                                onclick=lambda: self.check_all(True))

        self.add_toolbar_button(N_('Unselect all'),
                                icon_name='unselect-all',
                                onclick=lambda: self.check_all(False))

        layout_selector = \
                LayoutSelector(num=3, onchanged=self.set_layout, parent=self,
                                initial_no=self.current_layout)

        self.add_toolbar_menu(N_("&Layout"),
                              layout_selector,
                              icon_name="internet-news-reader",
                              shortcut="Alt+L")

        self.add_toolbar_button(N_('&Refresh'),
                                icon_name='view-refresh',
                                shortcut="Ctrl+R",
                                onclick=self.refresh)

        self.connect(self.file_view, QtCore.SIGNAL("itemSelectionChanged()"),
                     self.selected_file_changed)

        self.connect(self.file_view,
                     QtCore.SIGNAL("itemChanged(QTreeWidgetItem *, int)"),
                     self.file_checked)

        self.connect(self.hunk_view, QtCore.SIGNAL("selectionChanged()"),
                     self.selected_hunk_changed)

        self.set_layout()

        if splitters:
            splitters.add("shelve_splitter", self.splitter)
            splitters.add("shelve_splitter1", self.splitter1)
            splitters.add("shelve_splitter2", self.splitter2)
        for sp in (self.splitter, self.splitter1, self.splitter2):
            sp.setChildrenCollapsible(False)
            sp.setStretchFactor(0, 3)
            sp.setStretchFactor(1, 7)

        self.brushes = {
            'add file': QtGui.QBrush(QtCore.Qt.blue),
            'delete file': QtGui.QBrush(QtCore.Qt.red),
            'rename': QtGui.QBrush(QtGui.QColor(160, 32, 240)),  # purple
        }

        self.loaded = False
Exemplo n.º 5
0
    def __init__(self,
                 directory=None,
                 complete=False,
                 ignore_whitespace=False,
                 encoding=None,
                 splitters=None,
                 parent=None):
        ToolbarPanel.__init__(self, slender=False, icon_size=22, parent=parent)

        self.initial_encoding = encoding
        self.directory = directory

        self.current_diffs = []
        self.complete = complete
        self.ignore_whitespace = ignore_whitespace
        self.show_files = False
        self.load_settings()

        # build main widgets
        self.shelve_view = QtGui.QTreeWidget(self)
        self.shelve_view.setHeaderLabels([gettext("Id"), gettext("Message")])
        header = self.shelve_view.header()
        header.setResizeMode(0, QtGui.QHeaderView.ResizeToContents)

        self.file_view = QtGui.QTreeWidget(self)
        self.file_view.setHeaderLabels(
            [gettext("File Name"), gettext("Status")])
        self.file_view.setSelectionMode(
            QtGui.QAbstractItemView.ExtendedSelection)
        header = self.file_view.header()
        header.setStretchLastSection(False)
        header.setResizeMode(0, QtGui.QHeaderView.Stretch)
        header.setResizeMode(1, QtGui.QHeaderView.ResizeToContents)
        self.stack = QtGui.QStackedWidget(self)
        self.diffviews = (SidebySideDiffView(self), SimpleDiffView(self))
        for view in self.diffviews:
            self.stack.addWidget(view)
        for browser in self.diffviews[0].browsers:
            browser.installEventFilter(self)

        diff_panel = ToolbarPanel(self)

        # build diffpanel toolbar
        show_find = diff_panel.add_toolbar_button(
            N_("Find"),
            icon_name="edit-find",
            checkable=True,
            shortcut=QtGui.QKeySequence.Find)
        diff_panel.add_separator()
        diff_panel.add_toolbar_button(N_("Unidiff"),
                                      icon_name="unidiff",
                                      checkable=True,
                                      shortcut="Ctrl+U",
                                      onclick=self.unidiff_toggled)

        view_menu = QtGui.QMenu(gettext('View Options'), self)
        view_menu.addAction(
            diff_panel.create_button(N_("&Complete"),
                                     icon_name="complete",
                                     checkable=True,
                                     checked=complete,
                                     onclick=self.complete_toggled))
        view_menu.addAction(
            diff_panel.create_button(N_("Ignore whitespace"),
                                     icon_name="whitespace",
                                     checkable=True,
                                     checked=ignore_whitespace,
                                     onclick=self.whitespace_toggled))
        self.tabwidth_selector = TabWidthMenuSelector(
            label_text=gettext("Tab width"),
            onChanged=self.on_tabwidth_changed)
        view_menu.addMenu(self.tabwidth_selector)
        self.encoding_selector = EncodingMenuSelector(encoding,
                                                      gettext("Encoding"),
                                                      self.encoding_changed)
        self.encoding_selector.setIcon(get_icon("format-text-bold", 16))
        view_menu.addMenu(self.encoding_selector)
        diff_panel.add_toolbar_menu(N_("&View Options"),
                                    view_menu,
                                    icon_name="document-properties",
                                    shortcut="Alt+V")

        self.find_toolbar = FindToolbar(self, self.diffviews[0].browsers,
                                        show_find)
        diff_panel.add_widget(self.find_toolbar)
        diff_panel.add_widget(self.stack)
        self.find_toolbar.hide()
        for gb in self.diffviews[0].guidebar_panels:
            setup_guidebar_for_find(gb, self.find_toolbar, 1)
        setup_guidebar_for_find(self.diffviews[1], self.find_toolbar, 1)

        # Layout widgets
        self.splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        self.splitter1.addWidget(self.shelve_view)

        self.splitter2 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        self.splitter2.addWidget(self.file_view)
        self.splitter2.addWidget(diff_panel)

        self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
        self.splitter.addWidget(self.splitter1)
        self.splitter.addWidget(self.splitter2)
        self.splitter.setStretchFactor(0, 1)

        if splitters:
            splitters.add("shelvelist_splitter", self.splitter)
            splitters.add("shelvelist_splitter1", self.splitter1)
            splitters.add("shelvelist_splitter2", self.splitter2)

        for sp in (self.splitter, self.splitter1, self.splitter2):
            sp.setChildrenCollapsible(False)
            sp.setStretchFactor(0, 3)
            sp.setStretchFactor(1, 7)

        pal = QtGui.QPalette()
        pal.setColor(QtGui.QPalette.Window, QtGui.QColor(0, 0, 0, 0))
        self.splitter.setPalette(pal)

        layout = QtGui.QVBoxLayout()
        layout.setMargin(10)
        layout.addWidget(self.splitter)
        self.add_layout(layout)

        # build main toolbar
        unshelve_menu = QtGui.QMenu(gettext("Unshelve"), self)
        unshelve_menu.addAction(
            self.create_button(N_("Dry run"),
                               onclick=lambda: self.do_unshelve('dry-run')))
        unshelve_menu.addAction(
            self.create_button(N_("Keep"),
                               onclick=lambda: self.do_unshelve('keep')))
        unshelve_menu.addAction(
            self.create_button(
                N_("Delete"), onclick=lambda: self.do_unshelve('delete-only')))

        self.unshelve_button = self.add_toolbar_button(
            N_("Unshelve"),
            icon_name="unshelve",
            enabled=False,
            onclick=lambda: self.do_unshelve('apply'),
            menu=unshelve_menu)
        self.add_separator()

        layout_selector = \
                LayoutSelector(num=3, onchanged=lambda val:self.set_layout(type=val),
                    parent=self, initial_no=self.current_layout)

        layout_selector.addSeparator()
        layout_selector.addAction(
            self.create_button(
                gettext("Show filelist"),
                icon_name="file",
                icon_size=16,
                checkable=True,
                checked=self.show_files,
                shortcut="Ctrl+L",
                onclick=lambda val: self.set_layout(show_files=val)))

        self.add_toolbar_menu(N_("&Layout"),
                              layout_selector,
                              icon_name="internet-news-reader",
                              shortcut="Alt+L")

        self.add_toolbar_button(N_("&Refresh"),
                                icon_name="view-refresh",
                                shortcut="Ctrl+R",
                                onclick=self.refresh)

        self.shelf_id = None

        self.set_layout()

        # set signals
        self.connect(self.shelve_view, QtCore.SIGNAL("itemSelectionChanged()"),
                     self.selected_shelve_changed)
        self.connect(self.file_view, QtCore.SIGNAL("itemSelectionChanged()"),
                     self.selected_files_changed)

        self.loaded = False
        self._interrupt_switch = False
        self._need_refresh = False
        self._selecting_all_files = False
        self.brushes = {
            'added': QtGui.QBrush(QtCore.Qt.blue),
            'removed': QtGui.QBrush(QtCore.Qt.red),
            'renamed': QtGui.QBrush(QtGui.QColor(160, 32, 240)),  # purple
            'renamed and modified': QtGui.QBrush(QtGui.QColor(160, 32, 240)),
        }
Exemplo n.º 6
0
class ShelveListWidget(ToolbarPanel):
    def __init__(self,
                 directory=None,
                 complete=False,
                 ignore_whitespace=False,
                 encoding=None,
                 splitters=None,
                 parent=None):
        ToolbarPanel.__init__(self, slender=False, icon_size=22, parent=parent)

        self.initial_encoding = encoding
        self.directory = directory

        self.current_diffs = []
        self.complete = complete
        self.ignore_whitespace = ignore_whitespace
        self.show_files = False
        self.load_settings()

        # build main widgets
        self.shelve_view = QtGui.QTreeWidget(self)
        self.shelve_view.setHeaderLabels([gettext("Id"), gettext("Message")])
        header = self.shelve_view.header()
        header.setResizeMode(0, QtGui.QHeaderView.ResizeToContents)

        self.file_view = QtGui.QTreeWidget(self)
        self.file_view.setHeaderLabels(
            [gettext("File Name"), gettext("Status")])
        self.file_view.setSelectionMode(
            QtGui.QAbstractItemView.ExtendedSelection)
        header = self.file_view.header()
        header.setStretchLastSection(False)
        header.setResizeMode(0, QtGui.QHeaderView.Stretch)
        header.setResizeMode(1, QtGui.QHeaderView.ResizeToContents)
        self.stack = QtGui.QStackedWidget(self)
        self.diffviews = (SidebySideDiffView(self), SimpleDiffView(self))
        for view in self.diffviews:
            self.stack.addWidget(view)
        for browser in self.diffviews[0].browsers:
            browser.installEventFilter(self)

        diff_panel = ToolbarPanel(self)

        # build diffpanel toolbar
        show_find = diff_panel.add_toolbar_button(
            N_("Find"),
            icon_name="edit-find",
            checkable=True,
            shortcut=QtGui.QKeySequence.Find)
        diff_panel.add_separator()
        diff_panel.add_toolbar_button(N_("Unidiff"),
                                      icon_name="unidiff",
                                      checkable=True,
                                      shortcut="Ctrl+U",
                                      onclick=self.unidiff_toggled)

        view_menu = QtGui.QMenu(gettext('View Options'), self)
        view_menu.addAction(
            diff_panel.create_button(N_("&Complete"),
                                     icon_name="complete",
                                     checkable=True,
                                     checked=complete,
                                     onclick=self.complete_toggled))
        view_menu.addAction(
            diff_panel.create_button(N_("Ignore whitespace"),
                                     icon_name="whitespace",
                                     checkable=True,
                                     checked=ignore_whitespace,
                                     onclick=self.whitespace_toggled))
        self.tabwidth_selector = TabWidthMenuSelector(
            label_text=gettext("Tab width"),
            onChanged=self.on_tabwidth_changed)
        view_menu.addMenu(self.tabwidth_selector)
        self.encoding_selector = EncodingMenuSelector(encoding,
                                                      gettext("Encoding"),
                                                      self.encoding_changed)
        self.encoding_selector.setIcon(get_icon("format-text-bold", 16))
        view_menu.addMenu(self.encoding_selector)
        diff_panel.add_toolbar_menu(N_("&View Options"),
                                    view_menu,
                                    icon_name="document-properties",
                                    shortcut="Alt+V")

        self.find_toolbar = FindToolbar(self, self.diffviews[0].browsers,
                                        show_find)
        diff_panel.add_widget(self.find_toolbar)
        diff_panel.add_widget(self.stack)
        self.find_toolbar.hide()
        for gb in self.diffviews[0].guidebar_panels:
            setup_guidebar_for_find(gb, self.find_toolbar, 1)
        setup_guidebar_for_find(self.diffviews[1], self.find_toolbar, 1)

        # Layout widgets
        self.splitter1 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        self.splitter1.addWidget(self.shelve_view)

        self.splitter2 = QtGui.QSplitter(QtCore.Qt.Horizontal)
        self.splitter2.addWidget(self.file_view)
        self.splitter2.addWidget(diff_panel)

        self.splitter = QtGui.QSplitter(QtCore.Qt.Vertical)
        self.splitter.addWidget(self.splitter1)
        self.splitter.addWidget(self.splitter2)
        self.splitter.setStretchFactor(0, 1)

        if splitters:
            splitters.add("shelvelist_splitter", self.splitter)
            splitters.add("shelvelist_splitter1", self.splitter1)
            splitters.add("shelvelist_splitter2", self.splitter2)

        for sp in (self.splitter, self.splitter1, self.splitter2):
            sp.setChildrenCollapsible(False)
            sp.setStretchFactor(0, 3)
            sp.setStretchFactor(1, 7)

        pal = QtGui.QPalette()
        pal.setColor(QtGui.QPalette.Window, QtGui.QColor(0, 0, 0, 0))
        self.splitter.setPalette(pal)

        layout = QtGui.QVBoxLayout()
        layout.setMargin(10)
        layout.addWidget(self.splitter)
        self.add_layout(layout)

        # build main toolbar
        unshelve_menu = QtGui.QMenu(gettext("Unshelve"), self)
        unshelve_menu.addAction(
            self.create_button(N_("Dry run"),
                               onclick=lambda: self.do_unshelve('dry-run')))
        unshelve_menu.addAction(
            self.create_button(N_("Keep"),
                               onclick=lambda: self.do_unshelve('keep')))
        unshelve_menu.addAction(
            self.create_button(
                N_("Delete"), onclick=lambda: self.do_unshelve('delete-only')))

        self.unshelve_button = self.add_toolbar_button(
            N_("Unshelve"),
            icon_name="unshelve",
            enabled=False,
            onclick=lambda: self.do_unshelve('apply'),
            menu=unshelve_menu)
        self.add_separator()

        layout_selector = \
                LayoutSelector(num=3, onchanged=lambda val:self.set_layout(type=val),
                    parent=self, initial_no=self.current_layout)

        layout_selector.addSeparator()
        layout_selector.addAction(
            self.create_button(
                gettext("Show filelist"),
                icon_name="file",
                icon_size=16,
                checkable=True,
                checked=self.show_files,
                shortcut="Ctrl+L",
                onclick=lambda val: self.set_layout(show_files=val)))

        self.add_toolbar_menu(N_("&Layout"),
                              layout_selector,
                              icon_name="internet-news-reader",
                              shortcut="Alt+L")

        self.add_toolbar_button(N_("&Refresh"),
                                icon_name="view-refresh",
                                shortcut="Ctrl+R",
                                onclick=self.refresh)

        self.shelf_id = None

        self.set_layout()

        # set signals
        self.connect(self.shelve_view, QtCore.SIGNAL("itemSelectionChanged()"),
                     self.selected_shelve_changed)
        self.connect(self.file_view, QtCore.SIGNAL("itemSelectionChanged()"),
                     self.selected_files_changed)

        self.loaded = False
        self._interrupt_switch = False
        self._need_refresh = False
        self._selecting_all_files = False
        self.brushes = {
            'added': QtGui.QBrush(QtCore.Qt.blue),
            'removed': QtGui.QBrush(QtCore.Qt.red),
            'renamed': QtGui.QBrush(QtGui.QColor(160, 32, 240)),  # purple
            'renamed and modified': QtGui.QBrush(QtGui.QColor(160, 32, 240)),
        }

    def set_layout(self, type=None, show_files=None):
        if type is not None:
            self.current_layout = type
        if show_files is not None:
            self.show_files = show_files

        self.file_view.setParent(None)
        if self.current_layout == 1:
            self.splitter.setOrientation(QtCore.Qt.Vertical)
            self.splitter1.setOrientation(QtCore.Qt.Horizontal)
            if self.show_files:
                self.splitter1.insertWidget(1, self.file_view)
        elif self.current_layout == 2:
            self.splitter.setOrientation(QtCore.Qt.Horizontal)
            self.splitter1.setOrientation(QtCore.Qt.Vertical)
            if self.show_files:
                self.splitter1.insertWidget(1, self.file_view)
        else:
            self.splitter.setOrientation(QtCore.Qt.Vertical)
            self.splitter2.setOrientation(QtCore.Qt.Horizontal)
            if self.show_files:
                self.splitter2.insertWidget(0, self.file_view)

        if type is not None:
            # Reset splitter pos after changing type.
            for sp in (self.splitter, self.splitter1, self.splitter2):
                if sp.count() != 2:
                    continue
                size = sum(sp.sizes())
                if size > 0:
                    size1 = int(size * 0.3)
                    sp.setSizes((size1, size - size1))

        if show_files == False:
            # When filelist is hidden, select all files always.
            self.select_all_files()

    def on_tabwidth_changed(self, width):
        get_set_tab_width_chars(self.tree.branch, tab_width_chars=width)
        self._on_tabwidth_changed(width)

    def _on_tabwidth_changed(self, width):
        pixels = get_tab_width_pixels(tab_width_chars=width)
        self.diffviews[0].setTabStopWidths((pixels, pixels))
        self.diffviews[1].setTabStopWidth(pixels)

    def refresh(self):
        self.loaded = False
        self.clear()
        tree = WorkingTree.open_containing(self.directory)[0]
        tree.lock_read()
        try:
            manager = tree.get_shelf_manager()
            shelves = manager.active_shelves()
            for shelf_id in reversed(shelves):
                message = manager.get_metadata(shelf_id).get('message')
                item = QtGui.QTreeWidgetItem()
                item.setText(0, unicode(shelf_id))
                item.setText(1, message or gettext('<no message>'))
                item.setIcon(0, get_icon("folder", 16))
                item.shelf_id = shelf_id
                self.shelve_view.addTopLevelItem(item)
            self.tree = tree
            self.manager = manager

            branch = tree.branch
            if self.initial_encoding is None:
                encoding = get_set_encoding(None, branch)
                self.initial_encoding = encoding  # save real encoding for the next time
                self.encoding_selector.encoding = encoding  # set encoding selector
            tabwidth = get_set_tab_width_chars(branch)
            self.tabwidth_selector.setTabWidth(tabwidth)
            self._on_tabwidth_changed(tabwidth)

        finally:
            tree.unlock()
        self.update()
        self.loaded = True

    def update(self):
        for view in (self.shelve_view.viewport(),
                     self.file_view.viewport()) + self.diffviews:
            view.update()

    def clear(self):
        self.shelve_view.clear()
        self.manager = None

    def show_changes(self, shelf_id):
        cleanup = []
        shelf_file = self.manager.read_shelf(shelf_id)
        cleanup.append(shelf_file.close)
        try:
            records = Unshelver.iter_records(shelf_file)
            revid = Unshelver.parse_metadata(records)['revision_id']
            try:
                base_tree = self.tree.revision_tree(revid)
            except NoSuchRevisionInTree:
                base_tree = self.tree.branch.repository.revision_tree(revid)
            preview = transform.TransformPreview(base_tree)
            cleanup.append(preview.finalize)
            preview.deserialize(records)

            tabwidth = get_tab_width_pixels(self.tree.branch)
            self.diffviews[0].setTabStopWidths((tabwidth, tabwidth))
            self.diffviews[1].setTabStopWidth(tabwidth)

            self.load_diff(preview.get_preview_tree(), base_tree)

        finally:
            for func in cleanup:
                func()

    def load_diff(self, tree, base_tree):
        self.file_view.clear()

        for di in DiffItem.iter_items((base_tree, tree), lock_trees=False):
            di.load()

            old_path, new_path = di.paths
            if di.versioned == (True, False):
                text = old_path
            elif di.versioned == (False, True):
                text = new_path
            elif di.paths[0] != di.paths[1]:
                text = u'%s => %s' % (old_path, new_path)
            else:
                text = old_path

            item = QtGui.QTreeWidgetItem()
            item.setText(0, text)
            item.setText(1, gettext(di.status))
            if (di.kind[1] or di.kind[0]) == 'directory':
                item.setIcon(0, get_icon("folder", 16))
            else:
                item.setIcon(0, get_icon("file", 16))
            item.diffitem = di
            brush = self.brushes.get(di.status)
            if brush:
                item.setForeground(0, brush)
                item.setForeground(1, brush)
            self.file_view.addTopLevelItem(item)

    @lazy_call(100, per_instance=True)
    @runs_in_loading_queue
    def _show_selected_diff(self):
        if sip.isdeleted(self):
            return
        self._interrupt_switch = False
        try:
            refresh = self._need_refresh
            self._need_refresh = False

            diffs = [x.diffitem for x in self.file_view.selectedItems()]
            diffs.sort(key=lambda x: x.paths[0] or x.paths[1])

            cur_len = len(self.current_diffs)
            if not refresh and cur_len <= len(
                    diffs) and self.current_diffs == diffs[0:cur_len]:
                appends = diffs[cur_len:]
            else:
                for view in self.diffviews:
                    view.set_complete(self.complete)
                    view.clear()
                self.current_diffs = []
                appends = diffs
            for d in appends:
                lines = d.lines
                groups = d.groups(self.complete, self.ignore_whitespace)
                dates = d.dates[:]  # dates will be changed in append_diff
                ulines = d.get_unicode_lines((self.encoding_selector.encoding,
                                              self.encoding_selector.encoding))
                data = [''.join(l) for l in ulines]
                for view in self.diffviews:
                    view.append_diff(list(d.paths), d.file_id, d.kind,
                                     d.status, dates, d.versioned, d.binary,
                                     ulines, groups, data,
                                     d.properties_changed)
                self.current_diffs.append(d)
                if self._interrupt_switch:
                    # Interrupted
                    break
        finally:
            self._interrupt_switch = False
            for view in self.diffviews[0].browsers + (self.diffviews[1], ):
                view.emit(QtCore.SIGNAL("documentChangeFinished()"))

    def selected_shelve_changed(self):
        self._change_current_shelve()

    @lazy_call(100, per_instance=True)
    @runs_in_loading_queue
    def _change_current_shelve(self):
        if sip.isdeleted(self):
            return
        items = self.shelve_view.selectedItems()
        if len(items) != 1:
            self.shelf_id = None
            self.unshelve_button.setEnabled(False)
            self.file_view.clear()
        else:
            self.shelf_id = items[0].shelf_id
            self.unshelve_button.setEnabled(True)
            self.show_changes(self.shelf_id)
            if self.show_files:
                self.select_first_file()
            else:
                self.select_all_files()
        self.file_view.viewport().update()

    def selected_files_changed(self):
        if not self._selecting_all_files:
            self.show_selected_diff()

    def select_all_files(self):
        try:
            self._selecting_all_files = True
            for i in range(0, self.file_view.topLevelItemCount()):
                self.file_view.topLevelItem(i).setSelected(True)
        finally:
            self._selecting_all_files = False
            self.selected_files_changed()

    def select_first_file(self):
        if self.file_view.topLevelItemCount() > 0:
            self.file_view.topLevelItem(0).setSelected(True)

    def show_selected_diff(self, refresh=False):
        self._need_refresh = refresh or self._need_refresh
        self._show_selected_diff()

    def unidiff_toggled(self, state):
        index = 1 if state else 0
        self.diffviews[index].rewind()
        if index == 0:
            self.find_toolbar.set_text_edits(self.diffviews[0].browsers)
        else:
            self.find_toolbar.set_text_edits([self.diffviews[1].view])
        self.stack.setCurrentIndex(index)

    def complete_toggled(self, state):
        self.complete = state
        self.show_selected_diff(refresh=True)

    def whitespace_toggled(self, state):
        self.ignore_whitespace = state
        self.show_selected_diff(refresh=True)

    def prompt_bool(self, prompt, warning=False):
        if warning:
            func = QtGui.QMessageBox.warning
        else:
            func = QtGui.QMessageBox.question
        ret = func(self, gettext('Shelve'), gettext(prompt),
                   QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
        return (ret == QtGui.QMessageBox.Ok)

    prompts = {
        "apply":
        N_("Apply changes in shelf[%(id)d], and remove from the shelf"),
        "dry-run":
        N_("Simulate to apply changes in shelf[%(id)d] without changing working tree"
           ),
        "keep":
        N_("Apply changes in shelf[%(id)d], but keep it shelved"),
        "delete-only":
        N_("Remove shelf[%(id)d] without applying"),
    }

    def do_unshelve(self, action):
        if not self.shelf_id:
            return

        prompt = gettext(self.prompts[action]) % {"id": self.shelf_id}
        if action != "dry-run":
            if not self.prompt_bool(prompt, warning=(action == "delete-only")):
                return
        self.unshelve(self.shelf_id, prompt, action)
        self.refresh()

    def unshelve(self, id, desc, action):
        args = ["unshelve", str(id), '--' + action]
        window = SimpleSubProcessDialog(gettext("Shelve Manager"),
                                        desc=gettext(desc),
                                        args=args,
                                        dir=self.directory,
                                        immediate=True,
                                        parent=self.window())

        def finished(result):
            if result:
                self.emit(QtCore.SIGNAL("unshelved(int, QString*)"),
                          self.shelf_id, action)

        self.connect(window, QtCore.SIGNAL("subprocessFinished(bool)"),
                     finished)

        window.exec_()
        self.refresh()

    def encoding_changed(self, encoding):
        self.show_selected_diff(refresh=True)

    def eventFilter(self, object, event):
        if event.type() == QtCore.QEvent.FocusIn:
            if object in self.diffviews[0].browsers:
                self.find_toolbar.set_text_edit(object)
        return ToolbarPanel.eventFilter(self, object, event)

    def load_settings(self):
        config = get_qbzr_config()
        layout = config.get_option("shelvelist_layout")
        if layout not in ("2", "3"):
            layout = "1"
        self.current_layout = int(layout)
        self.show_files = not not config.get_option_as_bool(
            "shelvelist_show_filelist")

    def save_settings(self):
        config = get_qbzr_config()
        config.set_option("shelvelist_layout", str(self.current_layout))
        config.set_option("shelvelist_show_filelist", str(self.show_files))
        config.save()

    def hideEvent(self, event):
        self.save_settings()
Exemplo n.º 7
0
class DiffWindow(QBzrWindow):
    def __init__(self,
                 arg_provider,
                 parent=None,
                 complete=False,
                 encoding=None,
                 filter_options=None,
                 ui_mode=True,
                 allow_refresh=True):

        title = [gettext("Diff"), gettext("Loading...")]
        QBzrWindow.__init__(self, title, parent, ui_mode=ui_mode)
        self.restoreSize("diff", (780, 580))

        self.trees = None
        self.encoding = encoding
        self.arg_provider = arg_provider
        self.filter_options = filter_options
        if filter_options is None:
            self.filter_options = FilterOptions(all_enable=True)
        self.complete = complete
        self.ignore_whitespace = False
        self.delayed_signal_connections = []

        self.diffview = SidebySideDiffView(self)
        self.sdiffview = SimpleDiffView(self)
        self.views = (self.diffview, self.sdiffview)
        for view in self.views:
            view.set_complete(complete)

        self.stack = QtGui.QStackedWidget(self.centralwidget)
        self.stack.addWidget(self.diffview)
        self.stack.addWidget(self.sdiffview)
        vbox = QtGui.QVBoxLayout(self.centralwidget)
        vbox.addWidget(self.stack)

        # Don't use a custom tab width by default
        # Indices are left side, right side and unidiff
        # respectively
        self.custom_tab_widths = [-1, -1, -1]

        for browser in self.diffview.browsers:
            browser.installEventFilter(self)

        self.create_main_toolbar(allow_refresh)
        self.addToolBarBreak()
        self.find_toolbar = FindToolbar(self, self.diffview.browsers,
                                        self.show_find)
        self.find_toolbar.hide()
        self.addToolBar(self.find_toolbar)
        setup_guidebar_for_find(self.sdiffview, self.find_toolbar, 1)
        for gb in self.diffview.guidebar_panels:
            setup_guidebar_for_find(gb, self.find_toolbar, 1)
        self.diff_context = ExtDiffContext(self)

    def connect_later(self, *args, **kwargs):
        """Schedules a signal to be connected after loading CLI arguments.
        
        Accepts the same arguments as QObject.connect method.
        """
        self.delayed_signal_connections.append((args, kwargs))

    def process_delayed_connections(self):
        for (args, kwargs) in self.delayed_signal_connections:
            self.connect(*args, **kwargs)

    def create_main_toolbar(self, allow_refresh=True):
        toolbar = self.addToolBar(gettext("Diff"))
        toolbar.setMovable(False)
        toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)

        self.show_find = self.create_find_action()
        toolbar.addAction(self.show_find)
        toolbar.addAction(self.create_toggle_view_mode())
        self.view_refresh = self.create_refresh_action(allow_refresh)
        if allow_refresh:
            toolbar.addAction(self.view_refresh)

        if has_ext_diff():
            show_ext_diff_menu = self.create_ext_diff_action()
            toolbar.addAction(show_ext_diff_menu)
            widget = toolbar.widgetForAction(show_ext_diff_menu)
            widget.setPopupMode(QtGui.QToolButton.InstantPopup)
            widget.setShortcut("Alt+E")
            show_shortcut_hint(widget)

        show_view_menu = self.create_view_menu()
        toolbar.addAction(show_view_menu)
        widget = toolbar.widgetForAction(show_view_menu)
        widget.setPopupMode(QtGui.QToolButton.InstantPopup)
        widget.setShortcut("Alt+V")
        show_shortcut_hint(widget)

        spacer = QtGui.QWidget()
        spacer.setSizePolicy(QtGui.QSizePolicy.Expanding,
                             QtGui.QSizePolicy.Expanding)
        toolbar.addWidget(spacer)

        self.throbber = ToolBarThrobberWidget(self)
        toolbar.addWidget(self.throbber)
        return toolbar

    def create_find_action(self):
        action = QtGui.QAction(get_icon("edit-find"), gettext("&Find"), self)
        action.setShortcut(QtGui.QKeySequence.Find)
        action.setToolTip(gettext("Find on active panel"))
        show_shortcut_hint(action)
        action.setCheckable(True)
        return action

    def create_toggle_view_mode(self):
        action = QtGui.QAction(get_icon("view-split-left-right"),
                               gettext("Unidiff"), self)
        action.setToolTip(
            gettext("Toggle between Side by side and Unidiff view modes"))
        action.setShortcut("Ctrl+U")
        show_shortcut_hint(action)
        action.setCheckable(True)
        action.setChecked(False)
        self.connect(action, QtCore.SIGNAL("toggled (bool)"),
                     self.click_toggle_view_mode)
        return action

    def create_refresh_action(self, allow_refresh=True):
        action = QtGui.QAction(get_icon("view-refresh"), gettext("&Refresh"),
                               self)
        action.setShortcut("Ctrl+R")
        show_shortcut_hint(action)
        self.connect(action, QtCore.SIGNAL("triggered (bool)"),
                     self.click_refresh)
        action.setEnabled(allow_refresh)
        return action

    def create_ext_diff_action(self):
        action = QtGui.QAction(get_icon("system-run"),
                               gettext("&External Diff"), self)
        action.setToolTip(gettext("Launch an external diff application"))
        ext_diff_menu = ExtDiffMenu(parent=self, include_builtin=False)
        action.setMenu(ext_diff_menu)
        self.connect(ext_diff_menu, QtCore.SIGNAL("triggered(QString)"),
                     self.ext_diff_triggered)
        return action

    def create_view_menu(self):
        show_view_menu = QtGui.QAction(get_icon("document-properties"),
                                       gettext("&View Options"), self)
        view_menu = QtGui.QMenu(gettext('View Options'), self)
        show_view_menu.setMenu(view_menu)

        view_complete = QtGui.QAction(gettext("&Complete"), self)
        view_complete.setCheckable(True)
        self.connect(view_complete, QtCore.SIGNAL("toggled (bool)"),
                     self.click_complete)
        view_menu.addAction(view_complete)

        self.ignore_whitespace_action = self.create_ignore_ws_action()
        view_menu.addAction(self.ignore_whitespace_action)

        def on_unidiff_tab_width_changed(tabwidth):
            if self.branches:
                get_set_tab_width_chars(branch=self.branches[0],
                                        tab_width_chars=tabwidth)
            self.custom_tab_widths[2] = tabwidth
            self.setup_tab_width()

        self.tab_width_selector_unidiff = TabWidthMenuSelector(
            label_text=gettext("Tab width"),
            onChanged=on_unidiff_tab_width_changed)
        view_menu.addMenu(self.tab_width_selector_unidiff)

        def on_left_tab_width_changed(tabwidth):
            if self.branches:
                get_set_tab_width_chars(branch=self.branches[0],
                                        tab_width_chars=tabwidth)
            self.custom_tab_widths[0] = tabwidth
            self.setup_tab_width()

        self.tab_width_selector_left = TabWidthMenuSelector(
            label_text=gettext("Left side tab width"),
            onChanged=on_left_tab_width_changed)
        view_menu.addMenu(self.tab_width_selector_left)

        def on_right_tab_width_changed(tabwidth):
            if self.branches:
                get_set_tab_width_chars(branch=self.branches[1],
                                        tab_width_chars=tabwidth)
            self.custom_tab_widths[1] = tabwidth
            self.setup_tab_width()

        self.tab_width_selector_right = TabWidthMenuSelector(
            label_text=gettext("Right side tab width"),
            onChanged=on_right_tab_width_changed)
        view_menu.addMenu(self.tab_width_selector_right)

        if self.stack.currentWidget() == self.diffview:
            self.tab_width_selector_unidiff.menuAction().setVisible(False)
        else:
            self.tab_width_selector_left.menuAction().setVisible(False)
            self.tab_width_selector_right.menuAction().setVisible(False)

        def on_left_encoding_changed(encoding):
            if self.branches:
                get_set_encoding(encoding, self.branches[0])
            self.click_refresh()

        self.encoding_selector_left = EncodingMenuSelector(
            self.encoding, gettext("Left side encoding"),
            on_left_encoding_changed)
        view_menu.addMenu(self.encoding_selector_left)

        def on_right_encoding_changed(encoding):
            if self.branches:
                get_set_encoding(encoding, self.branches[1])
            self.click_refresh()

        self.encoding_selector_right = EncodingMenuSelector(
            self.encoding, gettext("Right side encoding"),
            on_right_encoding_changed)
        view_menu.addMenu(self.encoding_selector_right)
        return show_view_menu

    def create_ignore_ws_action(self):
        action = QtGui.QAction(gettext("&Ignore whitespace changes"), self)
        action.setCheckable(True)
        action.setChecked(self.ignore_whitespace)
        self.connect_later(action, QtCore.SIGNAL("toggled (bool)"),
                           self.click_ignore_whitespace)
        return action

    def eventFilter(self, object, event):
        if event.type() == QtCore.QEvent.FocusIn:
            if object in self.diffview.browsers:
                self.find_toolbar.set_text_edit(object)
        return QBzrWindow.eventFilter(self, object, event)
        # Why doesn't this work?
        #return super(DiffWindow, self).eventFilter(object, event)

    def show(self):
        QBzrWindow.show(self)
        QtCore.QTimer.singleShot(1, self.initial_load)

    @runs_in_loading_queue
    @ui_current_widget
    @reports_exception()
    def initial_load(self):
        """Called to perform the initial load of the form.  Enables a
        throbber window, then loads the branches etc if they weren't specified
        in our constructor.
        """
        op = cleanup.OperationWithCleanups(self._initial_load)
        self.throbber.show()
        op.add_cleanup(self.throbber.hide)
        op.run()

    def _initial_load(self, op):
        args = self.arg_provider.get_diff_window_args(self.processEvents,
                                                      op.add_cleanup)

        self.trees = (args["old_tree"], args["new_tree"])
        self.branches = (args.get("old_branch",
                                  None), args.get("new_branch", None))
        self.specific_files = args.get("specific_files", None)
        self.ignore_whitespace = args.get("ignore_whitespace", False)
        self.ignore_whitespace_action.setChecked(self.ignore_whitespace)

        self.process_delayed_connections()
        self.load_branch_info()
        self.setup_tab_width()
        self.load_diff()

    def load_branch_info(self):
        self.set_diff_title()
        self.encoding_selector_left.encoding = get_set_encoding(
            self.encoding, self.branches[0])
        self.encoding_selector_right.encoding = get_set_encoding(
            self.encoding, self.branches[1])
        self.processEvents()

    def set_diff_title(self):
        rev1_title = get_title_for_tree(self.trees[0], self.branches[0],
                                        self.branches[1])
        rev2_title = get_title_for_tree(self.trees[1], self.branches[1],
                                        self.branches[0])

        title = [gettext("Diff"), "%s..%s" % (rev1_title, rev2_title)]

        if self.specific_files:
            nfiles = len(self.specific_files)
            if nfiles > 2:
                title.append(ngettext("%d file", "%d files", nfiles) % nfiles)
            else:
                title.append(", ".join(self.specific_files))
        else:
            if self.filter_options and not self.filter_options.is_all_enable():
                title.append(self.filter_options.to_str())

        self.set_title_and_icon(title)
        self.processEvents()

    def setup_tab_width(self):
        tabWidths = self.custom_tab_widths
        if tabWidths[0] < 0:
            tabWidths[0] = get_set_tab_width_chars(branch=self.branches[0])
            self.tab_width_selector_left.setTabWidth(tabWidths[0])
        if tabWidths[1] < 0:
            tabWidths[1] = get_set_tab_width_chars(branch=self.branches[1])
            self.tab_width_selector_right.setTabWidth(tabWidths[1])
        if tabWidths[2] < 0:
            tabWidths[2] = get_set_tab_width_chars(branch=self.branches[0])
            self.tab_width_selector_unidiff.setTabWidth(tabWidths[2])
        tabWidthsPixels = [
            get_tab_width_pixels(tab_width_chars=i) for i in tabWidths
        ]
        self.diffview.setTabStopWidths(tabWidthsPixels)
        self.sdiffview.setTabStopWidth(tabWidthsPixels[2])

    def load_diff(self):
        self.view_refresh.setEnabled(False)
        self.processEvents()

        try:
            no_changes = True  # if there is no changes found we need to inform the user
            for di in DiffItem.iter_items(self.trees,
                                          specific_files=self.specific_files,
                                          filter=self.filter_options.check,
                                          lock_trees=True):
                lines = di.lines
                self.processEvents()
                groups = di.groups(self.complete, self.ignore_whitespace)
                self.processEvents()
                ulines = di.get_unicode_lines(
                    (self.encoding_selector_left.encoding,
                     self.encoding_selector_right.encoding))
                data = [''.join(l) for l in ulines]

                for view in self.views:
                    view.append_diff(list(di.paths), di.file_id, di.kind,
                                     di.status, di.dates, di.versioned,
                                     di.binary, ulines, groups, data,
                                     di.properties_changed)
                    self.processEvents()
                no_changes = False
        except PathsNotVersionedError, e:
            QtGui.QMessageBox.critical(
                self, gettext('Diff'),
                gettext(u'File %s is not versioned.\n'
                        'Operation aborted.') % e.paths_as_string,
                gettext('&Close'))
            self.close()

        if no_changes:
            QtGui.QMessageBox.information(self, gettext('Diff'),
                                          gettext('No changes found.'),
                                          gettext('&OK'))
        for t in self.views[0].browsers + (self.views[1], ):
            t.emit(QtCore.SIGNAL("documentChangeFinished()"))
        self.view_refresh.setEnabled(self.can_refresh())