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