1 """Widgets for manipulating git stashes"""
2 from qtpy
.QtCore
import Qt
5 from ..interaction
import Interaction
6 from ..models
import stash
7 from ..qtutils
import get
11 from .. import qtutils
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())
27 class StashView(standard
.Dialog
):
28 def __init__(self
, context
, model
, parent
=None):
29 standard
.Dialog
.__init
__(self
, parent
=parent
)
30 self
.context
= context
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(
45 tooltip
=N_('Rename the selected stash'),
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(
55 tooltip
=N_('Save modified state to new stash'),
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(
66 tooltip
=N_('Apply and drop the selected stash (git stash pop)'),
70 self
.button_close
= qtutils
.close_button()
72 self
.keep_index
= qtutils
.checkbox(
73 text
=N_('Keep Index'),
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')
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(
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
)
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
):
147 self
.keep_index
.setChecked(False)
149 def keep_index_clicked(self
, 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()
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
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'),
218 title
=N_('Rename Stash'),
221 if not ok
or not new_name
:
224 Interaction
.information(
225 N_('No change made'), N_('The stash has not been renamed')
228 context
= self
.context
229 cmds
.do(stash
.RenameStash
, context
, selection
, new_name
)
230 self
.update_from_model()
233 self
.stash_apply(pop
=True)
235 def stash_apply(self
, pop
=False):
236 """Applies the currently selected stash"""
237 selection
= self
.selected_stash()
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
:
257 context
= self
.context
258 keep_index
= get(self
.keep_index
)
259 stash_index
= get(self
.stash_index
)
261 cmds
.do(stash
.StashIndex
, context
, stash_name
)
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()
272 if not Interaction
.confirm(
274 N_('Recovering a dropped stash is not possible.'),
275 N_('Drop the "%s" stash?') % name
,
278 icon
=icons
.discard(),
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
)
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
)
301 self
.splitter
.setSizes(state
['sizes'])
302 except (KeyError, ValueError, AttributeError):