commitmsg: move the progress bar into the dock title
[git-cola.git] / cola / widgets / stash.py
blob4e0f89f1c817be4af1f103bd7b785da9d82eec2e
1 """Widgets for manipulating git stashes"""
2 from qtpy.QtCore import Qt
4 from ..i18n import N_
5 from ..interaction import Interaction
6 from ..models import stash
7 from ..qtutils import get
8 from .. import cmds
9 from .. import hotkeys
10 from .. import icons
11 from .. import qtutils
12 from . import defs
13 from . import diff
14 from . import standard
17 def view(context, show=True):
18 """Launches a stash dialog using the provided model + view"""
19 model = stash.StashModel(context)
20 stash_view = StashView(context, model, parent=qtutils.active_window())
21 if show:
22 stash_view.show()
23 stash_view.raise_()
24 return stash_view
27 class StashView(standard.Dialog):
28 def __init__(self, context, model, parent=None):
29 standard.Dialog.__init__(self, parent=parent)
30 self.context = context
31 self.model = model
32 self.stashes = []
33 self.revids = []
34 self.names = []
36 self.setWindowTitle(N_('Stash'))
37 if parent is not None:
38 self.setWindowModality(Qt.WindowModal)
40 self.stash_list = standard.ListWidget(parent=self)
41 self.stash_text = diff.DiffTextEdit(context, self)
43 self.button_rename = qtutils.create_button(
44 text=N_('Rename'),
45 tooltip=N_('Rename the selected stash'),
46 icon=icons.edit(),
49 self.button_apply = qtutils.create_button(
50 text=N_('Apply'), tooltip=N_('Apply the selected stash'), icon=icons.ok()
53 self.button_save = qtutils.create_button(
54 text=N_('Save'),
55 tooltip=N_('Save modified state to new stash'),
56 icon=icons.save(),
57 default=True,
60 self.button_drop = qtutils.create_button(
61 text=N_('Drop'), tooltip=N_('Drop the selected stash'), icon=icons.discard()
64 self.button_pop = qtutils.create_button(
65 text=N_('Pop'),
66 tooltip=N_('Apply and drop the selected stash (git stash pop)'),
67 icon=icons.discard(),
70 self.button_close = qtutils.close_button()
72 self.keep_index = qtutils.checkbox(
73 text=N_('Keep Index'),
74 checked=True,
75 tooltip=N_('Stash unstaged changes only, keeping staged changes'),
78 self.stash_index = qtutils.checkbox(
79 text=N_('Stash Index'), tooltip=N_('Stash staged changes only')
82 # Arrange layouts
83 self.splitter = qtutils.splitter(
84 Qt.Horizontal, self.stash_list, self.stash_text
86 self.splitter.setChildrenCollapsible(False)
88 self.btn_layt = qtutils.hbox(
89 defs.no_margin,
90 defs.button_spacing,
91 self.stash_index,
92 self.keep_index,
93 qtutils.STRETCH,
94 self.button_close,
95 self.button_save,
96 self.button_rename,
97 self.button_apply,
98 self.button_pop,
99 self.button_drop,
102 self.main_layt = qtutils.vbox(
103 defs.margin, defs.spacing, self.splitter, self.btn_layt
105 self.setLayout(self.main_layt)
106 self.splitter.setSizes([self.width() // 3, self.width() * 2 // 3])
108 # Apply stash with Ctrl+Enter
109 self.apply_action = qtutils.add_action(
110 self, N_('Apply'), self.stash_apply, hotkeys.APPLY
112 # Pop stash with Ctrl+Backspace
113 self.pop_action = qtutils.add_action(
114 self, N_('Pop'), self.stash_pop, hotkeys.DELETE_FILE_SECONDARY
116 # Drop stash with Ctrl+Shift+Backspace
117 self.drop_action = qtutils.add_action(
118 self, N_('Pop'), self.stash_drop, hotkeys.DELETE_FILE
121 # pylint: disable=no-member
122 self.stash_list.itemSelectionChanged.connect(self.item_selected)
124 qtutils.connect_button(self.button_save, self.stash_save)
125 qtutils.connect_button(self.button_rename, self.stash_rename)
126 qtutils.connect_button(self.button_apply, self.stash_apply)
127 qtutils.connect_button(self.button_pop, self.stash_pop)
128 qtutils.connect_button(self.button_drop, self.stash_drop)
129 qtutils.connect_button(self.button_close, self.close_and_rescan)
131 qtutils.connect_checkbox(self.stash_index, self.stash_index_clicked)
132 qtutils.connect_checkbox(self.keep_index, self.keep_index_clicked)
134 self.init_size(parent=parent)
136 self.update_from_model()
137 self.update_actions()
139 def close_and_rescan(self):
140 cmds.do(cmds.Rescan, self.context)
141 self.reject()
143 # "stash" and "keep" index are mutually disable, but we don't
144 # want a radio button because we'd have to add a 3rd "default" option.
145 def stash_index_clicked(self, clicked):
146 if clicked:
147 self.keep_index.setChecked(False)
149 def keep_index_clicked(self, clicked):
150 if clicked:
151 self.stash_index.setChecked(False)
153 def selected_stash(self):
154 """Returns the stash name of the currently selected stash"""
155 list_widget = self.stash_list
156 stash_list = self.revids
157 return qtutils.selected_item(list_widget, stash_list)
159 def selected_name(self):
160 list_widget = self.stash_list
161 stash_list = self.names
162 return qtutils.selected_item(list_widget, stash_list)
164 def item_selected(self):
165 """Shows the current stash in the main view."""
166 self.update_actions()
167 selection = self.selected_stash()
168 if not selection:
169 return
170 diff_text = self.model.stash_diff(selection)
171 self.stash_text.setPlainText(diff_text)
173 def update_actions(self):
174 is_staged = self.model.is_staged()
175 self.stash_index.setEnabled(is_staged)
177 is_changed = self.model.is_changed()
178 self.keep_index.setEnabled(is_changed)
179 self.button_save.setEnabled(is_changed)
181 is_selected = bool(self.selected_stash())
182 self.apply_action.setEnabled(is_selected)
183 self.drop_action.setEnabled(is_selected)
184 self.pop_action.setEnabled(is_selected)
185 self.button_rename.setEnabled(is_selected)
186 self.button_apply.setEnabled(is_selected)
187 self.button_drop.setEnabled(is_selected)
188 self.button_pop.setEnabled(is_selected)
190 def update_from_model(self):
191 """Initiates git queries on the model and updates the view"""
192 stashes, revids, author_dates, names = self.model.stash_info()
193 self.stashes = stashes
194 self.revids = revids
195 self.names = names
197 self.stash_list.clear()
198 self.stash_list.addItems(self.stashes)
199 if self.stash_list.count() > 0:
200 for i in range(self.stash_list.count()):
201 self.stash_list.item(i).setToolTip(author_dates[i])
202 item = self.stash_list.item(0)
203 self.stash_list.setCurrentItem(item)
205 # "Stash Index" depends on staged changes, so disable this option
206 # if there are no staged changes.
207 is_staged = self.model.is_staged()
208 if get(self.stash_index) and not is_staged:
209 self.stash_index.setChecked(False)
211 def stash_rename(self):
212 """Renames the currently selected stash"""
213 selection = self.selected_stash()
214 name = self.selected_name()
215 new_name, ok = qtutils.prompt(
216 N_('Enter a new name for the stash'),
217 text=name,
218 title=N_('Rename Stash'),
219 parent=self,
221 if not ok or not new_name:
222 return
223 if new_name == name:
224 Interaction.information(
225 N_('No change made'), N_('The stash has not been renamed')
227 return
228 context = self.context
229 cmds.do(stash.RenameStash, context, selection, new_name)
230 self.update_from_model()
232 def stash_pop(self):
233 self.stash_apply(pop=True)
235 def stash_apply(self, pop=False):
236 """Applies the currently selected stash"""
237 selection = self.selected_stash()
238 if not selection:
239 return
240 context = self.context
241 index = get(self.keep_index)
242 cmds.do(stash.ApplyStash, context, selection, index, pop)
243 self.update_from_model()
245 def stash_save(self):
246 """Saves the worktree in a stash
248 This prompts the user for a stash name and creates
249 a git stash named accordingly.
252 stash_name, ok = qtutils.prompt(
253 N_('Enter a name for the stash'), title=N_('Save Stash'), parent=self
255 if not ok or not stash_name:
256 return
257 context = self.context
258 keep_index = get(self.keep_index)
259 stash_index = get(self.stash_index)
260 if stash_index:
261 cmds.do(stash.StashIndex, context, stash_name)
262 else:
263 cmds.do(stash.SaveStash, context, stash_name, keep_index)
264 self.update_from_model()
266 def stash_drop(self):
267 """Drops the currently selected stash"""
268 selection = self.selected_stash()
269 name = self.selected_name()
270 if not selection:
271 return
272 if not Interaction.confirm(
273 N_('Drop Stash?'),
274 N_('Recovering a dropped stash is not possible.'),
275 N_('Drop the "%s" stash?') % name,
276 N_('Drop Stash'),
277 default=True,
278 icon=icons.discard(),
280 return
281 cmds.do(stash.DropStash, self.context, selection)
282 self.update_from_model()
283 self.stash_text.setPlainText('')
285 def export_state(self):
286 """Export persistent settings"""
287 state = super().export_state()
288 state['keep_index'] = get(self.keep_index)
289 state['stash_index'] = get(self.stash_index)
290 state['sizes'] = get(self.splitter)
291 return state
293 def apply_state(self, state):
294 """Apply persistent settings"""
295 result = super().apply_state(state)
296 keep_index = bool(state.get('keep_index', True))
297 stash_index = bool(state.get('stash_index', False))
298 self.keep_index.setChecked(keep_index)
299 self.stash_index.setChecked(stash_index)
300 try:
301 self.splitter.setSizes(state['sizes'])
302 except (KeyError, ValueError, AttributeError):
303 pass
304 return result