class PickBranchesDialog(Dialog): def __init__(self, repo, parent=None): super(PickBranchesDialog, self).__init__(parent=parent) self.repo = repo self.branch_model = BranchModel(repo, checkable=True, parent=self) self.filter_model = FilterModel(parent=self) self.filter_model.setSourceModel(self.branch_model) self.branch_list.setModel(self.filter_model) self.filter_text.textEdited.connect(self.filter_text_edited) self.branch_model.dataChanged.connect(self.branch_toggled) self.dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(False) @property def selected_branches(self): return self.branch_model.checked_branches def filter_text_edited(self, text): if text: self.filter_model.filters += self.filter_branch else: self.filter_model.filters -= self.filter_branch def branch_toggled(self, top_left, bottom_right): self.dialog_buttons.button(QDialogButtonBox.Ok).setEnabled(bool(self.branch_model.checked_branches)) def filter_branch(self, branch): return self.filter_text.text() in str(branch)
class FileView(View): preferred_dock_area = Qt.RightDockWidgetArea def create_ui(self): super(FileView, self).create_ui() self.toggle_deep_button.setIcon(QApplication.style().standardIcon( QStyle.SP_DirIcon)) self.toggle_deep_button.toggled.connect(self.deep_toggled) self.toggle_unmodified_button.setIcon(apply_status_to_icon( QApplication.style().standardIcon(QStyle.SP_FileIcon), git_api.UNMODIFIED, git_api.UNMODIFIED)) self.toggle_unmodified_button.toggled.connect(self.toggle_filter( exclude_unmodified)) self.toggle_untracked_button.setIcon(apply_status_to_icon( QApplication.style().standardIcon(QStyle.SP_FileIcon), git_api.UNTRACKED, git_api.UNTRACKED)) self.toggle_untracked_button.toggled.connect(self.toggle_filter( exclude_untracked)) self.toggle_ignored_button.setIcon(apply_status_to_icon( QApplication.style().standardIcon(QStyle.SP_FileIcon), git_api.IGNORED, git_api.IGNORED)) self.toggle_ignored_button.toggled.connect(self.toggle_filter( exclude_ignored)) self.filter_text.textEdited.connect(self.filter_text_edited) workspace = self.app.workspace self._directory = None self._file_lister = shallow_file_list self.file_model = FileModel(workspace, parent=self) self.file_model.file_source = lambda: (self._file_lister(self._directory) if self._directory else None) self.filter_model = FilterModel(parent=self) self.filter_model.setSourceModel(self.file_model) self.file_list.setModel(self.filter_model) # We must assign the selection model to a variable, to avoid triggering # a segfault bug (temporary PyObject* destroyed prematurely) # see https://bugreports.qt-project.org/browse/PYSIDE-79 selection_model = self.file_list.selectionModel() selection_model.currentChanged.connect(self.current_item_changed) selection_model.selectionChanged.connect(self.item_selection_changed) self.app.view_focus_out[self] += self.lost_focus self.app.view_focus_in[self] += self.gained_focus def on_added_to_window(self): super(FileView, self).on_added_to_window() if not self.parent().file_view: self.parent().file_view = self def on_removed_from_window(self): super(FileView, self).on_removed_from_window() if self.parent().file_view is self: self.parent().file_view = None @property def directory(self): return self._directory @directory.setter def directory(self, new_dir): self._directory = new_dir self.file_model.refresh() @property def selected_files(self): return tuple(model_item(index) for index in self.file_list.selectionModel().selectedRows()) def deep_toggled(self, deep): if deep: self._file_lister = deep_file_list else: self._file_lister = shallow_file_list self.file_model.refresh() def toggle_filter(self, filter_func): def handler(include): if include: self.filter_model.filters -= filter_func else: self.filter_model.filters += filter_func return handler def filter_text_edited(self, text): if text: self.filter_model.filters += self.exclude_by_path else: self.filter_model.filters -= self.exclude_by_path def exclude_by_path(self, item): return self.filter_text.text() in item.name def current_item_changed(self, index, old_index): self.parent().current_item_changed.emit(model_item(index)) def item_selection_changed(self, selected, deselected): self.parent().item_selection_changed.emit(self.selected_files) def lost_focus(self, old, now): self.parent().current_item_changed.emit(None) self.parent().item_selection_changed.emit([]) def gained_focus(self, old, now): self.parent().current_item_changed.emit(model_item( self.file_list.currentIndex())) self.parent().item_selection_changed.emit(self.selected_files)
class LogFilter(QFrame): def __init__(self, parent=None): super(LogFilter, self).__init__(parent=parent) setup_ui(self) self.filter_model = FilterModel(parent=self) self._viewer = None self.revs_menu = QMenu(parent=self) self.revs_separator = None self.rev_actions = QActionGroup(self) self.rev_actions.addAction(self.action_all_refs) self.select_revs_button.setMenu(self.revs_menu) self.action_all_refs.revs = None self.action_all_refs.triggered.connect(self.show_all_refs) self.action_select_branches.triggered.connect(self.pick_branches) self.filter_text.textEdited.connect(self.filter_text_edited) @property def source_model(self): return self.filter_model.sourceModel() @source_model.setter def source_model(self, model): self.filter_model.setSourceModel(model) self.revs_menu.clear() for action in self.rev_actions.actions(): if action is not self.action_all_refs: self.rev_actions.removeAction(action) self.revs_menu.addAction(self.action_all_refs) if not model.repo.head_ref: self.revs_menu.addAction(self.create_rev_action(model.repo, 'HEAD')) for branch in model.repo.branches: self.revs_menu.addAction(self.create_rev_action(model.repo, branch)) if model.all: self._select_action(self.action_all_refs) else: selected_revs = model.revs if not selected_revs: if model.repo.head_ref: selected_revs = (model.repo.head_ref,) else: selected_revs = ('HEAD',) action = self._find_rev_action(selected_revs) if not action: action = self.create_rev_action(model.repo, model.revs) self.revs_menu.addAction(action) self._select_action(action) self.revs_separator = self.revs_menu.addSeparator() self.revs_menu.addAction(self.action_select_branches) @property def viewer(self): return self._viewer @viewer.setter def viewer(self, new_viewer): self._viewer = new_viewer self._viewer.setModel(self.filter_model) def create_rev_action(self, repo, *revs): action = QAction(self.revs_menu) action.revs = revs action.setText(', '.join(str(rev) for rev in revs)) action.setCheckable(True) action.setActionGroup(self.rev_actions) action.triggered.connect(self._revs_action_triggered) return action def show_all_refs(self): self._select_action(self.action_all_refs) with busy_cursor(): self.source_model.revs = () self.source_model.all = True self.source_model.refresh() def pick_branches(self): dialog = PickBranchesDialog(repo=self.source_model.repo, parent=self) if dialog.exec_() == dialog.Accepted: self.show_revs(*dialog.selected_branches) def _select_action(self, action): action.setChecked(True) self.select_revs_button.setText(action.text()) def _revs_action_triggered(self): self._select_action(self.sender()) with busy_cursor(): self.source_model.revs = self.sender().revs self.source_model.all = False self.source_model.refresh() def _find_rev_action(self, revs): return next((action for action in self.rev_actions.actions() if action.revs == revs), None) def show_revs(self, *revs): action = self._find_rev_action(revs) if not action: action = self.create_rev_action(self.source_model.repo, *revs) self.revs_menu.insertAction(self.revs_separator, action) self._select_action(action) with busy_cursor(): self.source_model.revs = revs self.source_model.all = False self.source_model.refresh() def filter_text_edited(self, text): if text: self.viewer.hideColumn(0) self.filter_model.filters += self.filter_graph_row else: self.filter_model.filters -= self.filter_graph_row self.viewer.showColumn(0) def filter_graph_row(self, graph_row): return commit_matches_text(graph_row.log_entry, self.filter_text.text())