1 """Widgets for manipulating git stashes"""
2 from __future__
import division
, absolute_import
, unicode_literals
4 from qtpy
import QtCore
5 from qtpy
.QtCore
import Qt
8 from ..interaction
import Interaction
9 from ..models
import stash
10 from ..qtutils
import get
12 from .. import hotkeys
14 from .. import qtutils
18 from . import standard
21 def view(context
, show
=True):
22 """Launches a stash dialog using the provided model + view"""
23 model
= stash
.StashModel(context
)
24 stash_view
= StashView(context
, model
, parent
=qtutils
.active_window())
31 class StashView(standard
.Dialog
):
32 def __init__(self
, context
, model
, parent
=None):
33 standard
.Dialog
.__init
__(self
, parent
=parent
)
34 self
.context
= context
40 self
.setWindowTitle(N_('Stash'))
41 if parent
is not None:
42 self
.setWindowModality(Qt
.WindowModal
)
44 self
.stash_list
= standard
.ListWidget(parent
=self
)
45 self
.stash_text
= diff
.DiffTextEdit(context
, self
)
47 self
.button_apply
= qtutils
.create_button(
48 text
=N_('Apply'), tooltip
=N_('Apply the selected stash'), icon
=icons
.ok()
51 self
.button_save
= qtutils
.create_button(
53 tooltip
=N_('Save modified state to new stash'),
58 self
.button_drop
= qtutils
.create_button(
59 text
=N_('Drop'), tooltip
=N_('Drop the selected stash'), icon
=icons
.discard()
62 self
.button_pop
= qtutils
.create_button(
64 tooltip
=N_('Apply and drop the selected stash (git stash pop)'),
68 self
.button_close
= qtutils
.close_button()
70 self
.keep_index
= qtutils
.checkbox(
71 text
=N_('Keep Index'),
73 tooltip
=N_('Stash unstaged changes only, keeping staged changes'),
76 self
.stash_index
= qtutils
.checkbox(
77 text
=N_('Stash Index'), tooltip
=N_('Stash staged changes only')
81 self
.splitter
= qtutils
.splitter(
82 Qt
.Horizontal
, self
.stash_list
, self
.stash_text
84 self
.splitter
.setChildrenCollapsible(False)
86 self
.btn_layt
= qtutils
.hbox(
99 self
.main_layt
= qtutils
.vbox(
100 defs
.margin
, defs
.spacing
, self
.splitter
, self
.btn_layt
102 self
.setLayout(self
.main_layt
)
103 self
.splitter
.setSizes([self
.width() // 3, self
.width() * 2 // 3])
105 # Apply stash with Ctrl+Enter
106 self
.apply_action
= qtutils
.add_action(
107 self
, N_('Apply'), self
.stash_apply
, hotkeys
.APPLY
109 # Pop stash with Ctrl+Backspace
110 self
.pop_action
= qtutils
.add_action(
111 self
, N_('Pop'), self
.stash_pop
, hotkeys
.DELETE_FILE_SECONDARY
113 # Drop stash with Ctrl+Shift+Backspace
114 self
.drop_action
= qtutils
.add_action(
115 self
, N_('Pop'), self
.stash_drop
, hotkeys
.DELETE_FILE
118 # pylint: disable=no-member
119 self
.stash_list
.itemSelectionChanged
.connect(self
.item_selected
)
121 qtutils
.connect_button(self
.button_save
, self
.stash_save
)
122 qtutils
.connect_button(self
.button_apply
, self
.stash_apply
)
123 qtutils
.connect_button(self
.button_pop
, self
.stash_pop
)
124 qtutils
.connect_button(self
.button_drop
, self
.stash_drop
)
125 qtutils
.connect_button(self
.button_close
, self
.close_and_rescan
)
127 qtutils
.connect_checkbox(self
.stash_index
, self
.stash_index_clicked
)
128 qtutils
.connect_checkbox(self
.keep_index
, self
.keep_index_clicked
)
130 self
.init_size(parent
=parent
)
132 self
.update_from_model()
133 self
.update_actions()
135 def close_and_rescan(self
):
136 cmds
.do(cmds
.Rescan
, self
.context
)
139 # "stash" and "keep" index are mutually disable, but we don't
140 # want a radio button because we'd have to add a 3rd "default" option.
141 def stash_index_clicked(self
, clicked
):
143 self
.keep_index
.setChecked(False)
145 def keep_index_clicked(self
, clicked
):
147 self
.stash_index
.setChecked(False)
149 def selected_stash(self
):
150 """Returns the stash name of the currently selected stash"""
151 list_widget
= self
.stash_list
152 stash_list
= self
.revids
153 return qtutils
.selected_item(list_widget
, stash_list
)
155 def selected_name(self
):
156 list_widget
= self
.stash_list
157 stash_list
= self
.names
158 return qtutils
.selected_item(list_widget
, stash_list
)
160 def item_selected(self
):
161 """Shows the current stash in the main view."""
162 self
.update_actions()
163 selection
= self
.selected_stash()
166 diff_text
= self
.model
.stash_diff(selection
)
167 self
.stash_text
.setPlainText(diff_text
)
169 def update_actions(self
):
170 is_staged
= self
.model
.is_staged()
171 self
.stash_index
.setEnabled(is_staged
)
173 is_changed
= self
.model
.is_changed()
174 self
.keep_index
.setEnabled(is_changed
)
175 self
.button_save
.setEnabled(is_changed
)
177 is_selected
= bool(self
.selected_stash())
178 self
.apply_action
.setEnabled(is_selected
)
179 self
.drop_action
.setEnabled(is_selected
)
180 self
.pop_action
.setEnabled(is_selected
)
181 self
.button_apply
.setEnabled(is_selected
)
182 self
.button_drop
.setEnabled(is_selected
)
183 self
.button_pop
.setEnabled(is_selected
)
185 def update_from_model(self
):
186 """Initiates git queries on the model and updates the view"""
187 stashes
, revids
, author_dates
, names
= self
.model
.stash_info()
188 self
.stashes
= stashes
192 self
.stash_list
.clear()
193 self
.stash_list
.addItems(self
.stashes
)
194 if self
.stash_list
.count() > 0:
195 for i
in range(self
.stash_list
.count()):
196 self
.stash_list
.item(i
).setToolTip(author_dates
[i
])
197 item
= self
.stash_list
.item(0)
198 self
.stash_list
.setCurrentItem(item
)
200 # "Stash Index" depends on staged changes, so disable this option
201 # if there are no staged changes.
202 is_staged
= self
.model
.is_staged()
203 if get(self
.stash_index
) and not is_staged
:
204 self
.stash_index
.setChecked(False)
207 self
.stash_apply(pop
=True)
209 def stash_apply(self
, pop
=False):
210 """Applies the currently selected stash"""
211 selection
= self
.selected_stash()
214 context
= self
.context
215 index
= get(self
.keep_index
)
216 cmds
.do(stash
.ApplyStash
, context
, selection
, index
, pop
)
217 QtCore
.QTimer
.singleShot(1, self
.accept
)
219 def stash_save(self
):
220 """Saves the worktree in a stash
222 This prompts the user for a stash name and creates
223 a git stash named accordingly.
226 stash_name
, ok
= qtutils
.prompt(
227 N_('Enter a name for the stash'), title
=N_('Save Stash'), parent
=self
229 if not ok
or not stash_name
:
231 # Sanitize the stash name
232 stash_name
= utils
.sanitize(stash_name
)
233 if stash_name
in self
.names
:
234 Interaction
.critical(
235 N_('Error: Stash exists'),
236 N_('A stash named "%s" already exists') % stash_name
,
239 context
= self
.context
240 keep_index
= get(self
.keep_index
)
241 stash_index
= get(self
.stash_index
)
243 cmds
.do(stash
.StashIndex
, context
, stash_name
)
245 cmds
.do(stash
.SaveStash
, context
, stash_name
, keep_index
)
246 QtCore
.QTimer
.singleShot(1, self
.accept
)
248 def stash_drop(self
):
249 """Drops the currently selected stash"""
250 selection
= self
.selected_stash()
251 name
= self
.selected_name()
254 if not Interaction
.confirm(
256 N_('Recovering a dropped stash is not possible.'),
257 N_('Drop the "%s" stash?') % name
,
260 icon
=icons
.discard(),
263 cmds
.do(stash
.DropStash
, self
.context
, selection
)
264 self
.update_from_model()
265 self
.stash_text
.setPlainText('')
267 def export_state(self
):
268 """Export persistent settings"""
269 state
= super(StashView
, self
).export_state()
270 state
['keep_index'] = get(self
.keep_index
)
271 state
['stash_index'] = get(self
.stash_index
)
272 state
['sizes'] = get(self
.splitter
)
275 def apply_state(self
, state
):
276 """Apply persistent settings"""
277 result
= super(StashView
, self
).apply_state(state
)
278 keep_index
= bool(state
.get('keep_index', True))
279 stash_index
= bool(state
.get('stash_index', False))
280 self
.keep_index
.setChecked(keep_index
)
281 self
.stash_index
.setChecked(stash_index
)
283 self
.splitter
.setSizes(state
['sizes'])
284 except (KeyError, ValueError, AttributeError):