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 __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 = {}
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()
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
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)), }
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()
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())