git-cola v4.3.1
[git-cola.git] / cola / models / selection.py
blobb656378ba9178f0825c2e95ab967ffce504c8f9a
1 """Provides a selection model to handle selection."""
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 import collections
5 from qtpy import QtCore
6 from qtpy.QtCore import Signal
8 State = collections.namedtuple('State', 'staged unmerged modified untracked')
11 def create():
12 """Create a SelectionModel"""
13 return SelectionModel()
16 def pick(selection):
17 """Choose the first list from stage, unmerged, modified, untracked"""
18 if selection.staged:
19 files = selection.staged
20 elif selection.unmerged:
21 files = selection.unmerged
22 elif selection.modified:
23 files = selection.modified
24 elif selection.untracked:
25 files = selection.untracked
26 else:
27 files = []
28 return files
31 def union(selection):
32 """Return the union of all selected items in a sorted list"""
33 values = set(
34 selection.staged + selection.unmerged + selection.modified + selection.untracked
36 return list(sorted(values))
39 def _filter(values, remove):
40 """Filter a list in-place by removing items"""
41 remove_set = set(remove)
42 values_copy = list(values)
43 last = len(values_copy) - 1
44 for idx, value in enumerate(reversed(values)):
45 if value not in remove_set:
46 values.pop(last - idx)
49 class SelectionModel(QtCore.QObject):
50 """Provides information about selected file paths."""
52 selection_changed = Signal()
54 # These properties wrap the individual selection items
55 # to provide higher-level pseudo-selections.
56 unstaged = property(lambda self: self.unmerged + self.modified + self.untracked)
58 def __init__(self):
59 super(SelectionModel, self).__init__()
60 self.staged = []
61 self.unmerged = []
62 self.modified = []
63 self.untracked = []
64 self.line_number = None
66 def reset(self):
67 self.staged = []
68 self.unmerged = []
69 self.modified = []
70 self.untracked = []
71 self.line_number = None
73 def is_empty(self):
74 return not (
75 bool(self.staged or self.unmerged or self.modified or self.untracked)
78 def set_selection(self, s):
79 """Set the new selection."""
80 self.staged = s.staged
81 self.unmerged = s.unmerged
82 self.modified = s.modified
83 self.untracked = s.untracked
84 self.selection_changed.emit()
86 def update(self, other):
87 _filter(self.staged, other.staged)
88 _filter(self.unmerged, other.unmerged)
89 _filter(self.modified, other.modified)
90 _filter(self.untracked, other.untracked)
91 self.selection_changed.emit()
93 def selection(self):
94 return State(self.staged, self.unmerged, self.modified, self.untracked)
96 def single_selection(self):
97 """Scan across staged, modified, etc. and return a single item."""
98 staged = None
99 modified = None
100 unmerged = None
101 untracked = None
102 if self.staged:
103 staged = self.staged[0]
104 elif self.modified:
105 modified = self.modified[0]
106 elif self.unmerged:
107 unmerged = self.unmerged[0]
108 elif self.untracked:
109 untracked = self.untracked[0]
110 return State(staged, unmerged, modified, untracked)
112 def filename(self):
113 paths = [path for path in self.single_selection() if path is not None]
114 if paths:
115 filename = paths[0]
116 else:
117 filename = None
118 return filename
120 def group(self):
121 """A list of selected files in various states of being"""
122 return pick(self.selection())
124 def union(self):
125 """Return the union of all selected items in a sorted list"""
126 return union(self)