main: use the correct command class when loading commit messages
[git-cola.git] / cola / main / view.py
blob7cb6a682c3b9ce8575375b830e6a81ef22bd9c07
1 """This view provides the main git-cola user interface.
2 """
3 import os
5 from PyQt4 import QtCore
6 from PyQt4 import QtGui
7 from PyQt4.QtCore import Qt
8 from PyQt4.QtCore import SIGNAL
10 from cola import cmds
11 from cola import core
12 from cola import gitcmds
13 from cola import guicmds
14 from cola import merge
15 from cola import gitcfg
16 from cola import prefs
17 from cola import qtutils
18 from cola import resources
19 from cola import settings
20 from cola import stash
21 from cola import utils
22 from cola import version
23 from cola.bookmarks import manage_bookmarks
24 from cola.classic import cola_classic
25 from cola.classic import classic_widget
26 from cola.dag import git_dag
27 from cola.git import git
28 from cola.git import STDOUT
29 from cola.i18n import N_
30 from cola.interaction import Interaction
31 from cola.qt import create_dock
32 from cola.qt import create_menu
33 from cola.qt import create_toolbutton
34 from cola.qtutils import add_action
35 from cola.qtutils import add_action_bool
36 from cola.qtutils import connect_action
37 from cola.qtutils import connect_action_bool
38 from cola.qtutils import options_icon
39 from cola.widgets import action
40 from cola.widgets import cfgactions
41 from cola.widgets import editremotes
42 from cola.widgets import remote
43 from cola.widgets.about import launch_about_dialog
44 from cola.widgets.about import show_shortcuts
45 from cola.widgets.archive import GitArchiveDialog
46 from cola.widgets.commitmsg import CommitMessageEditor
47 from cola.widgets.compare import compare_branches
48 from cola.widgets.createtag import create_tag
49 from cola.widgets.createbranch import create_new_branch
50 from cola.widgets.diff import DiffEditor
51 from cola.widgets.log import LogWidget
52 from cola.widgets.recent import browse_recent
53 from cola.widgets.status import StatusWidget
54 from cola.widgets.search import search
55 from cola.widgets.standard import MainWindow
58 class MainView(MainWindow):
60 def __init__(self, model, parent=None):
61 MainWindow.__init__(self, parent)
62 # Default size; this is thrown out when save/restore is used
63 self.resize(987, 610)
64 self.model = model
65 self.prefs_model = prefs_model = prefs.PreferencesModel()
67 # The widget version is used by import/export_state().
68 # Change this whenever dockwidgets are removed.
69 self.widget_version = 2
71 # Keeps track of merge messages we've seen
72 self.merge_message_hash = ''
74 self.setAcceptDrops(True)
75 self.setAttribute(Qt.WA_MacMetalStyle)
77 cfg = gitcfg.instance()
78 self.classic_dockable = (cfg.get('cola.browserdockable') or
79 cfg.get('cola.classicdockable'))
80 if self.classic_dockable:
81 self.classicdockwidget = create_dock(N_('Browser'), self)
82 self.classicwidget = classic_widget(self)
83 self.classicdockwidget.setWidget(self.classicwidget)
85 # "Actions" widget
86 self.actionsdockwidget = create_dock(N_('Action'), self)
87 self.actionsdockwidgetcontents = action.ActionButtons(self)
88 self.actionsdockwidget.setWidget(self.actionsdockwidgetcontents)
89 self.actionsdockwidget.toggleViewAction().setChecked(False)
90 self.actionsdockwidget.hide()
92 # "Repository Status" widget
93 self.statuswidget = StatusWidget(self)
94 self.statusdockwidget = create_dock(N_('Status'), self)
95 self.statusdockwidget.setWidget(self.statuswidget)
97 # "Commit Message Editor" widget
98 self.position_label = QtGui.QLabel()
99 font = qtutils.default_monospace_font()
100 font.setPointSize(int(font.pointSize() * 0.8))
101 self.position_label.setFont(font)
102 self.commitdockwidget = create_dock(N_('Commit'), self)
103 titlebar = self.commitdockwidget.titleBarWidget()
104 titlebar.add_corner_widget(self.position_label)
106 self.commitmsgeditor = CommitMessageEditor(model, self)
107 self.commitdockwidget.setWidget(self.commitmsgeditor)
109 # "Console" widget
110 self.logwidget = LogWidget()
111 self.logdockwidget = create_dock(N_('Console'), self)
112 self.logdockwidget.setWidget(self.logwidget)
113 self.logdockwidget.toggleViewAction().setChecked(False)
114 self.logdockwidget.hide()
116 # "Diff Viewer" widget
117 self.diffdockwidget = create_dock(N_('Diff'), self)
118 self.diffeditor = DiffEditor(self.diffdockwidget)
119 self.diffdockwidget.setWidget(self.diffeditor)
121 # "Diff Options" tool menu
122 self.diff_ignore_space_at_eol_action = add_action(self,
123 N_('Ignore changes in whitespace at EOL'),
124 self._update_diff_opts)
125 self.diff_ignore_space_at_eol_action.setCheckable(True)
127 self.diff_ignore_space_change_action = add_action(self,
128 N_('Ignore changes in amount of whitespace'),
129 self._update_diff_opts)
130 self.diff_ignore_space_change_action.setCheckable(True)
132 self.diff_ignore_all_space_action = add_action(self,
133 N_('Ignore all whitespace'),
134 self._update_diff_opts)
135 self.diff_ignore_all_space_action.setCheckable(True)
137 self.diff_function_context_action = add_action(self,
138 N_('Show whole surrounding functions of changes'),
139 self._update_diff_opts)
140 self.diff_function_context_action.setCheckable(True)
142 self.diffopts_button = create_toolbutton(text=N_('Options'),
143 icon=options_icon(),
144 tooltip=N_('Diff Options'))
145 self.diffopts_menu = create_menu(N_('Diff Options'),
146 self.diffopts_button)
148 self.diffopts_menu.addAction(self.diff_ignore_space_at_eol_action)
149 self.diffopts_menu.addAction(self.diff_ignore_space_change_action)
150 self.diffopts_menu.addAction(self.diff_ignore_all_space_action)
151 self.diffopts_menu.addAction(self.diff_function_context_action)
152 self.diffopts_button.setMenu(self.diffopts_menu)
153 self.diffopts_button.setPopupMode(QtGui.QToolButton.InstantPopup)
155 titlebar = self.diffdockwidget.titleBarWidget()
156 titlebar.add_corner_widget(self.diffopts_button)
158 # All Actions
159 self.menu_unstage_all = add_action(self,
160 N_('Unstage All'), cmds.run(cmds.UnstageAll))
161 self.menu_unstage_all.setIcon(qtutils.icon('remove.svg'))
163 self.menu_unstage_selected = add_action(self,
164 N_('Unstage From Commit'), cmds.run(cmds.UnstageSelected))
165 self.menu_unstage_selected.setIcon(qtutils.icon('remove.svg'))
167 self.menu_show_diffstat = add_action(self,
168 N_('Diffstat'), cmds.run(cmds.Diffstat), 'Alt+D')
170 self.menu_stage_modified = add_action(self,
171 N_('Stage Changed Files To Commit'),
172 cmds.run(cmds.StageModified), 'Alt+A')
173 self.menu_stage_modified.setIcon(qtutils.icon('add.svg'))
175 self.menu_stage_untracked = add_action(self,
176 N_('Stage All Untracked'),
177 cmds.run(cmds.StageUntracked), 'Alt+U')
178 self.menu_stage_untracked.setIcon(qtutils.icon('add.svg'))
180 self.menu_export_patches = add_action(self,
181 N_('Export Patches...'), guicmds.export_patches, 'Alt+E')
183 self.new_repository = add_action(self,
184 N_('New Repository...'), guicmds.open_new_repo)
185 self.new_repository.setIcon(qtutils.new_icon())
187 self.menu_preferences = add_action(self,
188 N_('Preferences'), self.preferences,
189 QtGui.QKeySequence.Preferences, 'Ctrl+O')
191 self.menu_edit_remotes = add_action(self,
192 N_('Edit Remotes...'), lambda: editremotes.edit().exec_())
193 self.menu_rescan = add_action(self,
194 cmds.Refresh.name(),
195 cmds.run(cmds.Refresh),
196 cmds.Refresh.SHORTCUT)
197 self.menu_rescan.setIcon(qtutils.reload_icon())
199 self.menu_browse_recent = add_action(self,
200 N_('Recently Modified Files...'),
201 browse_recent, 'Shift+Ctrl+E')
203 self.menu_cherry_pick = add_action(self,
204 N_('Cherry-Pick...'),
205 guicmds.cherry_pick, 'Ctrl+P')
207 self.menu_load_commitmsg = add_action(self,
208 N_('Load Commit Message...'), guicmds.load_commitmsg)
210 self.menu_save_tarball = add_action(self,
211 N_('Save As Tarball/Zip...'), self.save_archive)
213 self.menu_quit = add_action(self,
214 N_('Quit'), self.close, 'Ctrl+Q')
215 self.menu_manage_bookmarks = add_action(self,
216 N_('Bookmarks...'), manage_bookmarks)
217 self.menu_grep = add_action(self,
218 N_('Grep'), guicmds.grep, 'Ctrl+G')
219 self.menu_merge_local = add_action(self,
220 N_('Merge...'), merge.local_merge)
222 self.menu_merge_abort = add_action(self,
223 N_('Abort Merge...'), merge.abort_merge)
225 self.menu_fetch = add_action(self,
226 N_('Fetch...'), remote.fetch)
227 self.menu_push = add_action(self,
228 N_('Push...'), remote.push)
229 self.menu_pull = add_action(self,
230 N_('Pull...'), remote.pull)
232 self.menu_open_repo = add_action(self,
233 N_('Open...'), guicmds.open_repo)
234 self.menu_open_repo.setIcon(qtutils.open_icon())
236 self.menu_stash = add_action(self,
237 N_('Stash...'), stash.stash, 'Alt+Shift+S')
239 self.menu_clone_repo = add_action(self,
240 N_('Clone...'), guicmds.clone_repo)
241 self.menu_clone_repo.setIcon(qtutils.git_icon())
243 self.menu_help_docs = add_action(self,
244 N_('Documentation'), resources.show_html_docs,
245 QtGui.QKeySequence.HelpContents)
247 self.menu_help_shortcuts = add_action(self,
248 N_('Keyboard Shortcuts'),
249 show_shortcuts,
250 QtCore.Qt.Key_Question)
252 self.menu_visualize_current = add_action(self,
253 N_('Visualize Current Branch...'),
254 cmds.run(cmds.VisualizeCurrent))
255 self.menu_visualize_all = add_action(self,
256 N_('Visualize All Branches...'),
257 cmds.run(cmds.VisualizeAll))
258 self.menu_search_commits = add_action(self,
259 N_('Search...'), search)
260 self.menu_browse_branch = add_action(self,
261 N_('Browse Current Branch...'), guicmds.browse_current)
262 self.menu_browse_other_branch = add_action(self,
263 N_('Browse Other Branch...'), guicmds.browse_other)
264 self.menu_load_commitmsg_template = add_action(self,
265 N_('Get Commit Message Template'),
266 cmds.run(cmds.LoadCommitMessageFromTemplate))
267 self.menu_help_about = add_action(self,
268 N_('About'), launch_about_dialog)
270 self.menu_diff_expression = add_action(self,
271 N_('Expression...'), guicmds.diff_expression)
272 self.menu_branch_compare = add_action(self,
273 N_('Branches...'), compare_branches)
275 self.menu_create_tag = add_action(self,
276 N_('Create Tag...'), create_tag)
278 self.menu_create_branch = add_action(self,
279 N_('Create...'), create_new_branch, 'Ctrl+B')
281 self.menu_delete_branch = add_action(self,
282 N_('Delete...'), guicmds.delete_branch)
284 self.menu_delete_remote_branch = add_action(self,
285 N_('Delete Remote Branch...'), guicmds.delete_remote_branch)
287 self.menu_checkout_branch = add_action(self,
288 N_('Checkout...'), guicmds.checkout_branch, 'Alt+B')
289 self.menu_branch_review = add_action(self,
290 N_('Review...'), guicmds.review_branch)
292 self.menu_classic = add_action(self,
293 N_('Browser...'), cola_classic)
294 self.menu_classic.setIcon(qtutils.git_icon())
296 self.menu_dag = add_action(self,
297 N_('DAG...'), lambda: git_dag(self.model).show())
298 self.menu_dag.setIcon(qtutils.git_icon())
300 self.rebase_start_action = add_action(self,
301 N_('Start Interactive Rebase...'), self.rebase_start)
303 self.rebase_edit_todo_action = add_action(self,
304 N_('Edit...'), self.rebase_edit_todo)
306 self.rebase_continue_action = add_action(self,
307 N_('Continue'), self.rebase_continue)
309 self.rebase_skip_action = add_action(self,
310 N_('Skip Current Patch'), self.rebase_skip)
312 self.rebase_abort_action = add_action(self,
313 N_('Abort'), self.rebase_abort)
315 # Relayed actions
316 if not self.classic_dockable:
317 # These shortcuts conflict with those from the
318 # 'Browser' widget so don't register them when
319 # the browser is a dockable tool.
320 status_tree = self.statusdockwidget.widget().tree
321 self.addAction(status_tree.up)
322 self.addAction(status_tree.down)
323 self.addAction(status_tree.process_selection)
325 self.lock_layout_action = add_action_bool(self,
326 N_('Lock Layout'), self.set_lock_layout, False)
328 # Create the application menu
329 self.menubar = QtGui.QMenuBar(self)
331 # File Menu
332 self.file_menu = create_menu(N_('File'), self.menubar)
333 self.file_menu.addAction(self.new_repository)
334 self.file_menu.addAction(self.menu_open_repo)
335 self.menu_open_recent = self.file_menu.addMenu(N_('Open Recent'))
336 self.file_menu.addSeparator()
337 self.file_menu.addAction(self.menu_clone_repo)
338 self.file_menu.addAction(self.menu_manage_bookmarks)
339 self.file_menu.addSeparator()
340 self.file_menu.addAction(self.menu_edit_remotes)
341 self.file_menu.addAction(self.menu_rescan)
342 self.file_menu.addSeparator()
343 self.file_menu.addAction(self.menu_browse_recent)
344 self.file_menu.addSeparator()
345 self.file_menu.addAction(self.menu_load_commitmsg)
346 self.file_menu.addAction(self.menu_load_commitmsg_template)
347 self.file_menu.addSeparator()
348 self.file_menu.addAction(self.menu_save_tarball)
349 self.file_menu.addAction(self.menu_export_patches)
350 self.file_menu.addSeparator()
351 self.file_menu.addAction(self.menu_preferences)
352 self.file_menu.addAction(self.menu_quit)
353 self.menubar.addAction(self.file_menu.menuAction())
355 # Actions menu
356 self.actions_menu = create_menu(N_('Actions'), self.menubar)
357 self.actions_menu.addAction(self.menu_fetch)
358 self.actions_menu.addAction(self.menu_push)
359 self.actions_menu.addAction(self.menu_pull)
360 self.actions_menu.addAction(self.menu_stash)
361 self.actions_menu.addSeparator()
362 self.actions_menu.addAction(self.menu_create_tag)
363 self.actions_menu.addAction(self.menu_cherry_pick)
364 self.actions_menu.addAction(self.menu_merge_local)
365 self.actions_menu.addAction(self.menu_merge_abort)
366 self.actions_menu.addSeparator()
367 self.actions_menu.addAction(self.menu_grep)
368 self.actions_menu.addAction(self.menu_search_commits)
369 self.menubar.addAction(self.actions_menu.menuAction())
371 # Index Menu
372 self.commit_menu = create_menu(N_('Index'), self.menubar)
373 self.commit_menu.setTitle(N_('Index'))
374 self.commit_menu.addAction(self.menu_stage_modified)
375 self.commit_menu.addAction(self.menu_stage_untracked)
376 self.commit_menu.addSeparator()
377 self.commit_menu.addAction(self.menu_unstage_all)
378 self.commit_menu.addAction(self.menu_unstage_selected)
379 self.menubar.addAction(self.commit_menu.menuAction())
381 # Diff Menu
382 self.diff_menu = create_menu(N_('Diff'), self.menubar)
383 self.diff_menu.addAction(self.menu_diff_expression)
384 self.diff_menu.addAction(self.menu_branch_compare)
385 self.diff_menu.addSeparator()
386 self.diff_menu.addAction(self.menu_show_diffstat)
387 self.menubar.addAction(self.diff_menu.menuAction())
389 # Branch Menu
390 self.branch_menu = create_menu(N_('Branch'), self.menubar)
391 self.branch_menu.addAction(self.menu_branch_review)
392 self.branch_menu.addSeparator()
393 self.branch_menu.addAction(self.menu_create_branch)
394 self.branch_menu.addAction(self.menu_checkout_branch)
395 self.branch_menu.addAction(self.menu_delete_branch)
396 self.branch_menu.addAction(self.menu_delete_remote_branch)
397 self.branch_menu.addSeparator()
398 self.branch_menu.addAction(self.menu_browse_branch)
399 self.branch_menu.addAction(self.menu_browse_other_branch)
400 self.branch_menu.addSeparator()
401 self.branch_menu.addAction(self.menu_visualize_current)
402 self.branch_menu.addAction(self.menu_visualize_all)
403 self.menubar.addAction(self.branch_menu.menuAction())
405 # Rebase menu
406 self.rebase_menu = create_menu(N_('Rebase'), self.actions_menu)
407 self.rebase_menu.addAction(self.rebase_start_action)
408 self.rebase_menu.addAction(self.rebase_edit_todo_action)
409 self.rebase_menu.addSeparator()
410 self.rebase_menu.addAction(self.rebase_continue_action)
411 self.rebase_menu.addAction(self.rebase_skip_action)
412 self.rebase_menu.addSeparator()
413 self.rebase_menu.addAction(self.rebase_abort_action)
414 self.menubar.addAction(self.rebase_menu.menuAction())
416 # View Menu
417 self.view_menu = create_menu(N_('View'), self.menubar)
418 self.view_menu.addAction(self.menu_classic)
419 self.view_menu.addAction(self.menu_dag)
420 self.view_menu.addSeparator()
421 if self.classic_dockable:
422 self.view_menu.addAction(self.classicdockwidget.toggleViewAction())
424 self.setup_dockwidget_view_menu()
425 self.view_menu.addSeparator()
426 self.view_menu.addAction(self.lock_layout_action)
427 self.menubar.addAction(self.view_menu.menuAction())
429 # Help Menu
430 self.help_menu = create_menu(N_('Help'), self.menubar)
431 self.help_menu.addAction(self.menu_help_docs)
432 self.help_menu.addAction(self.menu_help_shortcuts)
433 self.help_menu.addAction(self.menu_help_about)
434 self.menubar.addAction(self.help_menu.menuAction())
436 # Set main menu
437 self.setMenuBar(self.menubar)
439 # Arrange dock widgets
440 left = Qt.LeftDockWidgetArea
441 right = Qt.RightDockWidgetArea
442 bottom = Qt.BottomDockWidgetArea
444 self.addDockWidget(left, self.commitdockwidget)
445 if self.classic_dockable:
446 self.addDockWidget(left, self.classicdockwidget)
447 self.tabifyDockWidget(self.classicdockwidget, self.commitdockwidget)
448 self.addDockWidget(left, self.diffdockwidget)
449 self.addDockWidget(bottom, self.actionsdockwidget)
450 self.addDockWidget(bottom, self.logdockwidget)
451 self.tabifyDockWidget(self.actionsdockwidget, self.logdockwidget)
453 self.addDockWidget(right, self.statusdockwidget)
455 # Listen for model notifications
456 model.add_observer(model.message_updated, self._update_view)
458 prefs_model.add_observer(prefs_model.message_config_updated,
459 self._config_updated)
461 # Set a default value
462 self.show_cursor_position(1, 0)
464 self.connect(self.menu_open_recent, SIGNAL('aboutToShow()'),
465 self.build_recent_menu)
467 self.connect(self.commitmsgeditor, SIGNAL('cursorPosition(int,int)'),
468 self.show_cursor_position)
469 self.connect(self, SIGNAL('update'), self._update_callback)
470 self.connect(self, SIGNAL('install_config_actions'),
471 self._install_config_actions)
473 # Install .git-config-defined actions
474 self._config_task = None
475 self.install_config_actions()
477 # Restore saved settings
478 if not qtutils.apply_state(self):
479 self.set_initial_size()
481 self.statusdockwidget.widget().setFocus()
483 # Route command output here
484 Interaction.log_status = self.logwidget.log_status
485 Interaction.log = self.logwidget.log
486 Interaction.log(version.git_version_str() + '\n' +
487 N_('git cola version %s') % version.version())
489 def set_initial_size(self):
490 self.statuswidget.set_initial_size()
491 self.commitmsgeditor.set_initial_size()
493 # Qt overrides
494 def closeEvent(self, event):
495 """Save state in the settings manager."""
496 commit_msg = self.commitmsgeditor.commit_message(raw=True)
497 self.model.save_commitmsg(commit_msg)
498 MainWindow.closeEvent(self, event)
500 def build_recent_menu(self):
501 recent = settings.Settings().recent
502 menu = self.menu_open_recent
503 menu.clear()
504 for r in recent:
505 name = os.path.basename(r)
506 directory = os.path.dirname(r)
507 text = '%s %s %s' % (name, unichr(0x2192), directory)
508 menu.addAction(text, cmds.run(cmds.OpenRepo, r))
510 # Accessors
511 mode = property(lambda self: self.model.mode)
513 def _config_updated(self, source, config, value):
514 if config == 'cola.fontdiff':
515 # The diff font
516 font = QtGui.QFont()
517 if not font.fromString(value):
518 return
519 self.logwidget.setFont(font)
520 self.diffeditor.setFont(font)
521 self.commitmsgeditor.setFont(font)
523 elif config == 'cola.tabwidth':
524 # variable-tab-width setting
525 self.diffeditor.set_tabwidth(value)
526 self.commitmsgeditor.set_tabwidth(value)
528 elif config == 'cola.linebreak':
529 # enables automatic line breaks
530 self.commitmsgeditor.set_linebreak(value)
532 elif config == 'cola.textwidth':
533 # text width used for line wrapping
534 self.commitmsgeditor.set_textwidth(value)
536 def install_config_actions(self):
537 """Install .gitconfig-defined actions"""
538 self._config_task = self._start_config_actions_task()
540 def _start_config_actions_task(self):
541 """Do the expensive "get_config_actions()" call in the background"""
542 class ConfigActionsTask(QtCore.QRunnable):
543 def __init__(self, sender):
544 QtCore.QRunnable.__init__(self)
545 self._sender = sender
546 def run(self):
547 names = cfgactions.get_config_actions()
548 self._sender.emit(SIGNAL('install_config_actions'), names)
550 task = ConfigActionsTask(self)
551 QtCore.QThreadPool.globalInstance().start(task)
552 return task
554 def _install_config_actions(self, names):
555 """Install .gitconfig-defined actions"""
556 if not names:
557 return
558 menu = self.actions_menu
559 menu.addSeparator()
560 for name in names:
561 menu.addAction(name, cmds.run(cmds.RunConfigAction, name))
563 def _update_view(self):
564 self.emit(SIGNAL('update'))
566 def _update_callback(self):
567 """Update the title with the current branch and directory name."""
568 branch = self.model.currentbranch
569 curdir = core.getcwd()
570 is_rebasing = self.model.is_rebasing
572 msg = N_('Repository: %s') % curdir
573 msg += '\n'
574 msg += N_('Branch: %s') % branch
575 if is_rebasing:
576 msg += '\n\n'
577 msg += N_('This repository is currently being rebased.\n'
578 'Resolve conflicts, commit changes, and run:\n'
579 ' Rebase > Continue')
580 self.commitdockwidget.setToolTip(msg)
582 alerts = []
583 if is_rebasing:
584 alerts.append(N_('Rebasing').upper())
585 if self.mode == self.model.mode_amend:
586 alerts.append(N_('Amending').upper())
588 l = unichr(0xab)
589 r = unichr(0xbb)
590 title = ('%s: %s %s%s' % (
591 self.model.project,
592 branch,
593 alerts and ((r+' %s '+l+' ') % ', '.join(alerts)) or '',
594 self.model.git.worktree()))
595 self.setWindowTitle(title)
596 self.commitmsgeditor.set_mode(self.mode)
597 self.update_rebase_actions(is_rebasing)
599 if not self.model.amending():
600 # Check if there's a message file in .git/
601 merge_msg_path = gitcmds.merge_message_path()
602 if merge_msg_path is None:
603 return
604 merge_msg_hash = utils.checksum(merge_msg_path)
605 if merge_msg_hash == self.merge_message_hash:
606 return
607 self.merge_message_hash = merge_msg_hash
608 cmds.do(cmds.LoadCommitMessageFromFile, merge_msg_path)
610 def update_rebase_actions(self, is_rebasing):
611 can_rebase = not is_rebasing
612 self.rebase_start_action.setEnabled(can_rebase)
613 self.rebase_edit_todo_action.setEnabled(is_rebasing)
614 self.rebase_continue_action.setEnabled(is_rebasing)
615 self.rebase_skip_action.setEnabled(is_rebasing)
616 self.rebase_abort_action.setEnabled(is_rebasing)
618 def apply_state(self, state):
619 """Imports data for save/restore"""
620 result = MainWindow.apply_state(self, state)
621 self.lock_layout_action.setChecked(state.get('lock_layout', False))
622 return result
624 def setup_dockwidget_view_menu(self):
625 # Hotkeys for toggling the dock widgets
626 if utils.is_darwin():
627 optkey = 'Meta'
628 else:
629 optkey = 'Ctrl'
630 dockwidgets = (
631 (optkey + '+0', self.logdockwidget),
632 (optkey + '+1', self.commitdockwidget),
633 (optkey + '+2', self.statusdockwidget),
634 (optkey + '+3', self.diffdockwidget),
635 (optkey + '+4', self.actionsdockwidget),
637 for shortcut, dockwidget in dockwidgets:
638 # Associate the action with the shortcut
639 toggleview = dockwidget.toggleViewAction()
640 toggleview.setShortcut(shortcut)
641 self.view_menu.addAction(toggleview)
642 def showdock(show, dockwidget=dockwidget):
643 if show:
644 dockwidget.raise_()
645 dockwidget.widget().setFocus()
646 else:
647 self.setFocus()
648 self.addAction(toggleview)
649 connect_action_bool(toggleview, showdock)
651 # Create a new shortcut Shift+<shortcut> that gives focus
652 toggleview = QtGui.QAction(self)
653 toggleview.setShortcut('Shift+' + shortcut)
654 def focusdock(dockwidget=dockwidget, showdock=showdock):
655 if dockwidget.toggleViewAction().isChecked():
656 showdock(True)
657 else:
658 dockwidget.toggleViewAction().trigger()
659 self.addAction(toggleview)
660 connect_action(toggleview, focusdock)
662 def _update_diff_opts(self):
663 space_at_eol = self.diff_ignore_space_at_eol_action.isChecked()
664 space_change = self.diff_ignore_space_change_action.isChecked()
665 all_space = self.diff_ignore_all_space_action.isChecked()
666 function_context = self.diff_function_context_action.isChecked()
668 gitcmds.update_diff_overrides(space_at_eol,
669 space_change,
670 all_space,
671 function_context)
672 self.statuswidget.refresh()
674 def preferences(self):
675 return prefs.preferences(model=self.prefs_model, parent=self)
677 def save_archive(self):
678 ref = git.rev_parse('HEAD')[STDOUT]
679 shortref = ref[:7]
680 GitArchiveDialog.save(ref, shortref, self)
682 def dragEnterEvent(self, event):
683 """Accepts drops"""
684 MainWindow.dragEnterEvent(self, event)
685 event.acceptProposedAction()
687 def dropEvent(self, event):
688 """Apply dropped patches with git-am"""
689 event.accept()
690 urls = event.mimeData().urls()
691 if not urls:
692 return
693 paths = map(lambda x: unicode(x.path()), urls)
694 patches = [p for p in paths if p.endswith('.patch')]
695 dirs = [p for p in paths if os.path.isdir(p)]
696 dirs.sort()
697 for d in dirs:
698 patches.extend(self._gather_patches(d))
699 cmds.do(cmds.ApplyPatches, patches)
701 def _gather_patches(self, path):
702 """Find patches in a subdirectory"""
703 patches = []
704 for root, subdirs, files in os.walk(path):
705 for name in [f for f in files if f.endswith('.patch')]:
706 patches.append(os.path.join(root, name))
707 return patches
709 def show_cursor_position(self, rows, cols):
710 display = '&nbsp;%02d:%02d&nbsp;' % (rows, cols)
711 if cols > 78:
712 display = ('<span style="color: white; '
713 ' background-color: red;"'
714 '>%s</span>' % display)
715 elif cols > 72:
716 display = ('<span style="color: black; '
717 ' background-color: orange;"'
718 '>%s</span>' % display)
719 elif cols > 64:
720 display = ('<span style="color: black; '
721 ' background-color: yellow;"'
722 '>%s</span>' % display)
723 else:
724 display = ('<span style="color: grey;">%s</span>' % display)
726 self.position_label.setText(display)
728 def rebase_start(self):
729 branch = guicmds.choose_ref(N_('Select New Upstream'),
730 N_('Interactive Rebase'))
731 if not branch:
732 return None
733 self.model.is_rebasing = True
734 self._update_callback()
735 cmds.do(cmds.Rebase, branch)
737 def rebase_edit_todo(self):
738 cmds.do(cmds.RebaseEditTodo)
740 def rebase_continue(self):
741 cmds.do(cmds.RebaseContinue)
743 def rebase_skip(self):
744 cmds.do(cmds.RebaseSkip)
746 def rebase_abort(self):
747 cmds.do(cmds.RebaseAbort)