8 from PyQt4
import QtCore
9 from PyQt4
import QtGui
10 from PyQt4
.QtGui
import QDialog
11 from PyQt4
.QtGui
import QMessageBox
12 from PyQt4
.QtGui
import QMenu
13 from PyQt4
.QtGui
import QFont
15 from cola
import utils
16 from cola
import qtutils
17 from cola
import defaults
18 from cola
.qobserver
import QObserver
20 # controllers namespace
22 from util
import logger
23 from remote
import remote_action
24 from util
import choose_from_list
25 from util
import choose_from_combo
26 from util
import select_commits
27 from util
import update_options
28 from repobrowser
import browse_git_branch
29 from createbranch
import create_new_branch
30 from search
import search_commits
31 from merge
import local_merge
32 from merge
import abort_merge
33 from bookmark
import save_bookmark
34 from bookmark
import manage_bookmarks
35 from stash
import stash
38 class Controller(QObserver
):
39 """Manages the interaction between models and views."""
46 def init(self
, model
, view
):
50 none -> do nothing, disables most context menus
51 branch -> diff against another branch and selectively choose changes
52 worktree -> selectively add working changes to the index
53 index -> selectively remove changes from the index
55 self
.mode
= Controller
.MODE_NONE
57 # parent-less log window
58 qtutils
.LOGGER
= logger()
60 # Unstaged changes context menu
61 view
.unstaged
.contextMenuEvent
= self
.unstaged_context_menu_event
63 # Diff display context menu
64 view
.display_text
.contextMenuEvent
= self
.diff_context_menu_event
66 # Binds model params to their equivalent view widget
67 self
.add_observables('commitmsg', 'staged', 'unstaged')
69 # When a model attribute changes, this runs a specific action
70 self
.add_actions(staged
= self
.action_staged
)
71 self
.add_actions(unstaged
= self
.action_unstaged
)
72 self
.add_actions(global_cola_fontdiff
= self
.update_diff_font
)
73 self
.add_actions(global_cola_fontui
= self
.update_ui_font
)
77 signoff_button
= self
.model
.add_signoff
,
78 stage_button
= self
.stage_selected
,
79 commit_button
= self
.commit
,
80 fetch_button
= self
.fetch
,
81 push_button
= self
.push
,
82 pull_button
= self
.pull
,
84 staged
= self
.diff_staged
,
85 unstaged
= self
.diff_unstaged
,
87 untracked_checkbox
= self
.rescan
,
90 menu_quit
= self
.quit_app
,
91 menu_open_repo
= self
.open_repo
,
92 menu_manage_bookmarks
= manage_bookmarks
,
93 menu_save_bookmark
= save_bookmark
,
96 menu_options
= self
.options
,
97 menu_cut
= self
.view
.action_cut
,
98 menu_copy
= self
.view
.action_copy
,
99 menu_paste
= self
.view
.action_paste
,
100 menu_delete
= self
.view
.action_delete
,
101 menu_select_all
= self
.view
.action_select_all
,
102 menu_undo
= self
.view
.action_undo
,
103 menu_redo
= self
.view
.action_redo
,
106 menu_search_grep
= self
.grep
,
107 menu_search_revision
=
108 self
.gen_search( search
.REVISION_ID
),
109 menu_search_revision_range
=
110 self
.gen_search( search
.REVISION_RANGE
),
111 menu_search_message
=
112 self
.gen_search( search
.MESSAGE
),
114 self
.gen_search( search
.PATH
, True ),
115 menu_search_date_range
=
116 self
.gen_search( search
.DATE_RANGE
),
118 self
.gen_search( search
.DIFF
),
120 self
.gen_search( search
.AUTHOR
),
121 menu_search_committer
=
122 self
.gen_search( search
.COMMITTER
),
126 lambda: local_merge( self
.model
, self
.view
),
128 lambda: abort_merge( self
.model
, self
.view
),
131 menu_visualize_current
= self
.viz_current
,
132 menu_visualize_all
= self
.viz_all
,
133 menu_browse_commits
= self
.browse_commits
,
134 menu_browse_branch
= self
.browse_current
,
135 menu_browse_other_branch
= self
.browse_other
,
138 menu_create_branch
= self
.branch_create
,
139 menu_checkout_branch
= self
.checkout_branch
,
140 menu_diff_branch
= self
.diff_branch
,
141 menu_diffedit_branch
= self
.diffedit_branch
,
144 menu_rescan
= self
.rescan
,
145 menu_delete_branch
= self
.branch_delete
,
146 menu_rebase_branch
= self
.rebase
,
147 menu_commit
= self
.commit
,
148 menu_stage_selected
= self
.stage_selected
,
149 menu_unstage_selected
= self
.unstage_selected
,
150 menu_show_diffstat
= self
.show_diffstat
,
151 menu_show_index
= self
.show_index
,
152 menu_export_patches
= self
.export_patches
,
154 lambda: stash( self
.model
, self
.view
),
155 menu_load_commitmsg
= self
.load_commitmsg
,
156 menu_cherry_pick
= self
.cherry_pick
,
157 menu_get_prev_commitmsg
= model
.get_prev_commitmsg
,
158 menu_stage_modified
=
159 lambda: self
.log(self
.model
.stage_modified()),
160 menu_stage_untracked
=
161 lambda: self
.log(self
.model
.stage_untracked()),
163 lambda: self
.log(self
.model
.unstage_all()),
166 # Delegate window events here
167 view
.moveEvent
= self
.move_event
168 view
.resizeEvent
= self
.resize_event
169 view
.closeEvent
= self
.quit_app
170 view
.staged
.mousePressEvent
= self
.click_staged
171 view
.unstaged
.mousePressEvent
= self
.click_unstaged
173 # These are vanilla signal/slots since QObserver
174 # is already handling these signals.
175 self
.connect(view
.unstaged
,
176 'itemDoubleClicked(QListWidgetItem*)',
178 self
.connect(view
.staged
,
179 'itemDoubleClicked(QListWidgetItem*)',
180 self
.unstage_selected
)
183 self
.connect(view
.toolbar_show_log
,
184 'triggered()', self
.show_log
)
186 self
.connect(view
.diff_dock
,
187 'topLevelChanged(bool)',
188 lambda(b
): self
.setwindow(view
.diff_dock
, b
))
190 self
.connect(view
.editor_dock
,
191 'topLevelChanged(bool)',
192 lambda(b
): self
.setwindow(view
.editor_dock
, b
))
194 self
.connect(view
.status_dock
,
195 'topLevelChanged(bool)',
196 lambda(b
): self
.setwindow(view
.status_dock
, b
))
198 self
.init_log_window()
199 self
.load_gui_settings()
201 self
.refresh_view('global_cola_fontdiff', 'global_cola_fontui')
202 self
.start_inotify_thread()
204 def setwindow(self
, dock
, isfloating
):
206 if platform
.system() != 'Windows' and 'Macintosh' not in platform
.platform():
207 flags
= ( QtCore
.Qt
.Window
208 | QtCore
.Qt
.FramelessWindowHint
)
209 dock
.setWindowFlags( flags
)
212 #####################################################################
213 # handle when the listitem icons are clicked
214 def click_event(self
, widget
, action_callback
, event
):
215 result
= QtGui
.QListWidget
.mousePressEvent(widget
, event
)
216 xpos
= event
.pos().x()
217 if xpos
> 5 and xpos
< 20:
221 def click_staged(self
, event
):
222 return self
.click_event(self
.view
.staged
,
223 self
.unstage_selected
, event
)
225 def click_unstaged(self
, event
):
226 return self
.click_event(self
.view
.unstaged
,
227 self
.stage_selected
, event
)
229 #####################################################################
230 # event() is called in response to messages from the inotify thread
231 def event(self
, msg
):
232 if msg
.type() == defaults
.INOTIFY_EVENT
:
238 #####################################################################
239 # Actions triggered during model updates
241 def action_staged(self
, widget
):
242 qtutils
.update_listwidget(widget
,
243 self
.model
.get_staged(),
245 self
.view
.editor_dock
.raise_()
247 def action_unstaged(self
, widget
):
248 qtutils
.update_listwidget(widget
,
249 self
.model
.get_modified(),
251 if self
.view
.untracked_checkbox
.isChecked():
252 qtutils
.update_listwidget(widget
,
253 self
.model
.get_untracked(),
258 #####################################################################
260 def gen_search(self
, searchtype
, browse
=False):
261 def search_handler():
262 search_commits(self
.model
, searchtype
, browse
)
263 return search_handler
266 txt
, ok
= qtutils
.input("grep")
269 stuff
= self
.model
.grep(txt
)
270 self
.view
.display_text
.setText(stuff
)
271 self
.view
.diff_dock
.raise_()
273 def show_log(self
, *rest
):
274 qtutils
.toggle_log_window()
277 update_options(self
.model
, self
.view
)
279 def branch_create(self
):
280 if create_new_branch(self
.model
, self
.view
):
283 def branch_delete(self
):
284 branch
= choose_from_combo('Delete Branch',
286 self
.model
.get_local_branches())
289 self
.log(self
.model
.delete_branch(branch
))
291 def browse_current(self
):
292 branch
= self
.model
.get_currentbranch()
293 browse_git_branch(self
.model
, self
.view
, branch
)
295 def browse_other(self
):
296 # Prompt for a branch to browse
297 branch
= choose_from_combo('Browse Branch Files',
299 self
.model
.get_all_branches())
302 # Launch the repobrowser
303 browse_git_branch(self
.model
, self
.view
, branch
)
305 def checkout_branch(self
):
306 branch
= choose_from_combo('Checkout Branch',
308 self
.model
.get_local_branches())
311 self
.log(self
.model
.checkout(branch
))
313 def browse_commits(self
):
314 self
.select_commits_gui(self
.tr('Browse Commits'),
315 *self
.model
.log_helper(all
=True))
317 def cherry_pick(self
):
318 commits
= self
.select_commits_gui(self
.tr('Cherry-Pick Commits'),
319 *self
.model
.log_helper(all
=True))
322 self
.log(self
.model
.cherry_pick_list(commits
))
325 msg
= self
.model
.get_commitmsg()
327 error_msg
= self
.tr(""
328 + "Please supply a commit message.\n"
330 + "A good commit message has the following format:\n"
332 + "- First line: Describe in one sentence what you did.\n"
333 + "- Second line: Blank\n"
334 + "- Remaining lines: Describe why this change is good.\n")
338 files
= self
.model
.get_staged()
339 if not files
and not self
.view
.amend_radio
.isChecked():
340 error_msg
= self
.tr(""
341 + "No changes to commit.\n"
343 + "You must stage at least 1 file before you can commit.\n")
348 amend
= self
.view
.amend_radio
.isChecked()
349 output
= self
.model
.commit_with_msg(msg
, amend
=amend
)
352 self
.view
.new_commit_radio
.setChecked(True)
353 self
.view
.amend_radio
.setChecked(False)
354 self
.model
.set_commitmsg('')
357 def view_diff(self
, staged
=True):
359 self
.mode
= Controller
.MODE_INDEX
360 widget
= self
.view
.staged
362 self
.mode
= Controller
.MODE_WORKTREE
363 widget
= self
.view
.unstaged
364 row
, selected
= qtutils
.get_selected_row(widget
)
366 self
.mode
= Controller
.MODE_NONE
367 self
.view
.reset_display()
369 diff
, status
, filename
= self
.model
.get_diff_details(row
, staged
=staged
)
370 self
.view
.set_display(diff
)
371 self
.view
.set_info(self
.tr(status
))
372 self
.view
.diff_dock
.raise_()
373 qtutils
.set_clipboard(filename
)
375 def edit_file(self
, staged
=True):
377 widget
= self
.view
.staged
379 widget
= self
.view
.unstaged
380 row
, selected
= qtutils
.get_selected_row(widget
)
384 filename
= self
.model
.get_staged()[row
]
386 filename
= self
.model
.get_unstaged()[row
]
387 utils
.fork(self
.model
.get_editor(), filename
)
389 def launch_diffeditor(self
, filename
, tmpfile
):
390 if self
.model
.get_cola_config('editdiffreverse'):
391 utils
.fork(self
.model
.get_diffeditor(), tmpfile
, filename
)
393 utils
.fork(self
.model
.get_diffeditor(), filename
, tmpfile
)
395 def edit_diff(self
, staged
=True):
397 widget
= self
.view
.staged
399 widget
= self
.view
.unstaged
400 row
, selected
= qtutils
.get_selected_row(widget
)
401 diff
, status
, filename
= self
.model
.get_diff_details(row
, staged
=staged
)
404 contents
= self
.model
.show("HEAD:"+filename
, with_raw_output
=True)
405 tmpfile
= self
.model
.get_tmp_filename(filename
)
406 fh
= open(tmpfile
, 'w')
409 self
.launch_diffeditor(filename
, tmpfile
)
411 # use *rest to handle being called from different signals
412 def diff_staged(self
, *rest
):
413 self
.view_diff(staged
=True)
415 # use *rest to handle being called from different signals
416 def diff_unstaged(self
, *rest
):
417 self
.view_diff(staged
=False)
419 def export_patches(self
):
420 (revs
, summaries
) = self
.model
.log_helper()
421 to_export
= self
.select_commits_gui(self
.tr('Export Patches'),
427 self
.log(self
.model
.format_patch_helper(to_export
,
432 """Spawns a new cola session"""
433 dirname
= qtutils
.opendir_dialog(self
.view
,
434 'Open Git Repository...',
437 utils
.fork(sys
.argv
[0], dirname
)
439 def quit_app(self
, *args
):
440 """Save config settings and cleanup any inotify threads."""
442 if self
.model
.save_at_exit():
443 self
.model
.save_gui_settings()
444 qtutils
.close_log_window()
445 pattern
= self
.model
.get_tmp_file_pattern()
446 for filename
in glob
.glob(pattern
):
448 if self
.inotify_thread
and self
.inotify_thread
.isRunning():
449 self
.inotify_thread
.abort
= True
450 self
.inotify_thread
.wait()
453 def load_commitmsg(self
):
454 file = qtutils
.open_dialog(self
.view
,
455 'Load Commit Message...',
458 defaults
.DIRECTORY
= os
.path
.dirname(file)
459 slushy
= utils
.slurp(file)
461 self
.model
.set_commitmsg(slushy
)
464 branch
= choose_from_combo('Rebase Branch',
466 self
.model
.get_local_branches())
469 self
.log(self
.model
.rebase(branch
))
471 # use *rest to handle being called from the checkbox signal
472 def rescan(self
, *rest
):
473 """Populates view widgets with results from 'git status.'"""
475 # save entire selection
476 unstaged
= qtutils
.get_selection_list(self
.view
.unstaged
,
477 self
.model
.get_unstaged())
478 staged
= qtutils
.get_selection_list(self
.view
.staged
,
479 self
.model
.get_staged())
481 scrollbar
= self
.view
.display_text
.verticalScrollBar()
482 scrollvalue
= scrollbar
.value()
485 unstageditem
= qtutils
.get_selected_item(self
.view
.unstaged
,
486 self
.model
.get_unstaged())
488 stageditem
= qtutils
.get_selected_item(self
.view
.staged
,
489 self
.model
.get_staged())
492 self
.model
.update_status()
495 update_staged
= False
496 update_unstaged
= False
497 updated_unstaged
= self
.model
.get_unstaged()
498 updated_staged
= self
.model
.get_staged()
500 for item
in unstaged
:
501 if item
in updated_unstaged
:
502 idx
= updated_unstaged
.index(item
)
503 listitem
= self
.view
.unstaged
.item(idx
)
505 listitem
.setSelected(True)
506 self
.view
.unstaged
.setItemSelected(listitem
, True)
507 update_unstaged
= True
508 self
.view
.unstaged
.update()
510 if item
in updated_staged
:
511 idx
= updated_staged
.index(item
)
512 listitem
= self
.view
.staged
.item(idx
)
514 listitem
.setSelected(True)
515 self
.view
.staged
.setItemSelected(listitem
, True)
518 # restore selected item
519 if update_unstaged
and unstageditem
:
520 idx
= updated_unstaged
.index(unstageditem
)
521 item
= self
.view
.unstaged
.item(idx
)
522 self
.view
.unstaged
.setCurrentItem(item
)
523 self
.view_diff(False)
524 scrollbar
.setValue(scrollvalue
)
525 elif update_staged
and stageditem
:
526 idx
= updated_staged
.index(stageditem
)
527 item
= self
.view
.staged
.item(idx
)
528 self
.view
.staged
.setCurrentItem(item
)
530 scrollbar
.setValue(scrollvalue
)
532 # Update the title with the current branch
533 self
.view
.setWindowTitle('%s [%s]' % (
534 self
.model
.get_project(),
535 self
.model
.get_currentbranch()))
537 # Check if there's a message file in .git/
538 merge_msg_path
= self
.model
.get_merge_message_path()
539 if merge_msg_path
is None:
542 # A merge message file exists.
544 if self
.model
.get_commitmsg():
545 # The commit message editor contains data.
546 # Prompt before overwriting the commit message
547 # with the contents of the merge message.
548 answer
= qtutils
.question(self
.view
,
549 self
.tr('Import Commit Message?'),
550 self
.tr('A commit message from an in-progress'
551 +' merge was found.\nImport it?'))
556 # Set the new commit message
558 self
.model
.load_commitmsg(merge_msg_path
)
559 self
.view
.editor_dock
.raise_()
562 remote_action(self
.model
, self
.view
, "Fetch")
565 remote_action(self
.model
, self
.view
, "Push")
568 remote_action(self
.model
, self
.view
, "Pull")
570 def show_diffstat(self
):
571 """Show the diffstat from the latest commit."""
572 self
.mode
= Controller
.MODE_NONE
573 self
.view
.set_info(self
.tr('Diffstat'))
574 self
.view
.set_display(self
.model
.diffstat())
576 def show_index(self
):
577 self
.mode
= Controller
.MODE_NONE
578 self
.view
.set_info(self
.tr('Index'))
579 self
.view
.set_display(self
.model
.diffindex())
581 #####################################################################
582 def diffedit_branch(self
):
583 branch
= choose_from_combo('Select Branch',
585 self
.model
.get_all_branches())
588 zfiles_str
= self
.model
.diff(branch
, name_only
=True, z
=True)
589 files
= zfiles_str
.split('\0')
590 filename
= choose_from_list('Select File', self
.view
, files
)
593 contents
= self
.model
.show('%s:%s' % (branch
, filename
), with_raw_output
=True)
594 tmpfile
= self
.model
.get_tmp_filename(filename
)
595 fh
= open(tmpfile
, 'w')
598 self
.launch_diffeditor(filename
, tmpfile
)
600 # Set state machine to branch mode
601 self
.mode
= Controller
.MODE_NONE
603 #####################################################################
605 def diff_branch(self
):
606 branch
= choose_from_combo('Select Branch',
608 self
.model
.get_all_branches())
611 zfiles_str
= self
.model
.diff(branch
, name_only
=True, z
=True)
612 files
= zfiles_str
.split('\0')
613 filename
= choose_from_list('Select File', self
.view
, files
)
616 status
= ('Diff of "%s" between the work tree and %s'
617 % (filename
, branch
))
619 diff
= self
.model
.diff_helper(filename
=filename
,
623 self
.view
.set_display(diff
)
624 self
.view
.set_info(self
.tr(status
))
625 self
.view
.diff_dock
.raise_()
627 # Set state machine to branch mode
628 self
.mode
= Controller
.MODE_BRANCH
630 self
.filename
= filename
632 def process_diff_selection(self
, items
, widget
,
633 cached
=True, selected
=False, reverse
=True):
635 if self
.mode
== Controller
.MODE_BRANCH
:
637 filename
= self
.filename
638 parser
= utils
.DiffParser(self
.model
,
642 offset
, selection
= self
.view
.diff_selection()
643 parser
.process_diff_selection(selected
, offset
, selection
,
647 filename
= qtutils
.get_selected_item(widget
, items
)
650 parser
= utils
.DiffParser(self
.model
,
653 offset
, selection
= self
.view
.diff_selection()
654 parser
.process_diff_selection(selected
, offset
, selection
)
657 def stage_hunk(self
):
658 self
.process_diff_selection(self
.model
.get_unstaged(),
662 def stage_hunk_selection(self
):
663 self
.process_diff_selection(self
.model
.get_unstaged(),
668 def unstage_hunk(self
, cached
=True):
669 self
.process_diff_selection(self
.model
.get_staged(),
673 def unstage_hunk_selection(self
):
674 self
.process_diff_selection(self
.model
.get_staged(),
679 # #######################################################################
682 # *rest handles being called from different signals
683 def stage_selected(self
,*rest
):
684 """Use "git add" to add items to the git index.
685 This is a thin wrapper around map_to_listwidget."""
686 command
= self
.model
.add_or_remove
687 widget
= self
.view
.unstaged
688 items
= self
.model
.get_unstaged()
689 self
.map_to_listwidget(command
, widget
, items
)
691 # *rest handles being called from different signals
692 def unstage_selected(self
, *rest
):
693 """Use "git reset" to remove items from the git index.
694 This is a thin wrapper around map_to_listwidget."""
695 command
= self
.model
.reset_helper
696 widget
= self
.view
.staged
697 items
= self
.model
.get_staged()
698 self
.map_to_listwidget(command
, widget
, items
)
700 def undo_changes(self
):
701 """Reverts local changes back to whatever's in HEAD."""
702 widget
= self
.view
.unstaged
703 items
= self
.model
.get_unstaged()
704 potential_items
= qtutils
.get_selection_list(widget
, items
)
706 untracked
= self
.model
.get_untracked()
707 for item
in potential_items
:
708 if item
not in untracked
:
709 items_to_undo
.append(item
)
711 if not qtutils
.question(self
.view
,
712 self
.tr('Destroy Local Changes?'),
713 self
.tr('This operation will drop all '
714 +'uncommitted changes. '
719 output
= self
.model
.checkout('HEAD', '--', *items_to_undo
)
720 self
.log('git checkout HEAD -- '
721 +' '.join(items_to_undo
)
724 msg
= 'No files selected for checkout from HEAD.'
725 self
.log(self
.tr(msg
))
728 """Visualizes the entire git history using gitk."""
729 browser
= self
.model
.get_history_browser()
730 utils
.fork(browser
,'--all')
732 def viz_current(self
):
733 """Visualizes the current branch's history using gitk."""
734 browser
= self
.model
.get_history_browser()
735 utils
.fork(browser
, self
.model
.get_currentbranch())
737 def move_event(self
, event
):
738 defaults
.X
= event
.pos().x()
739 defaults
.Y
= event
.pos().y()
741 def resize_event(self
, event
):
742 defaults
.WIDTH
= event
.size().width()
743 defaults
.HEIGHT
= event
.size().height()
745 def load_gui_settings(self
):
746 if not self
.model
.remember_gui_settings():
748 (w
,h
,x
,y
,st0
,st1
,sb0
,sb1
) = self
.model
.get_window_geom()
749 self
.view
.resize(w
,h
)
752 def log(self
, output
, rescan
=True, quiet
=False):
753 """Logs output and optionally rescans for changes."""
754 qtutils
.log(output
, quiet
=quiet
, doraise
=False)
758 def map_to_listwidget(self
, command
, widget
, items
):
759 """This is a helper method that retrieves the current
760 selection list, applies a command to that list,
761 displays a dialog showing the output of that command,
762 and calls rescan to pickup changes."""
763 apply_items
= qtutils
.get_selection_list(widget
, items
)
764 output
= command(*apply_items
)
765 self
.log(output
, quiet
=True)
767 def unstaged_context_menu_event(self
, event
):
768 menu
= self
.unstaged_context_menu_setup()
769 unstaged
= self
.view
.unstaged
770 menu
.exec_(unstaged
.mapToGlobal(event
.pos()))
772 def unstaged_context_menu_setup(self
):
773 unstaged_item
= qtutils
.get_selected_item(self
.view
.unstaged
,
774 self
.model
.get_unstaged())
775 is_tracked
= unstaged_item
not in self
.model
.get_untracked()
776 enable_staging
= self
.mode
== Controller
.MODE_WORKTREE
777 enable_undo
= enable_staging
and is_tracked
779 menu
= QMenu(self
.view
)
782 menu
.addAction(self
.tr('Stage Selected'), self
.stage_selected
)
784 menu
.addAction(self
.tr('Undo Local Changes'), self
.undo_changes
)
785 menu
.addAction(self
.tr('Launch Editor'),
786 lambda: self
.edit_file(staged
=False))
788 menu
.addAction(self
.tr('Launch Diff Editor'),
789 lambda: self
.edit_diff(staged
=False))
792 def diff_context_menu_event(self
, event
):
793 menu
= self
.diff_context_menu_setup()
794 textedit
= self
.view
.display_text
795 menu
.exec_(textedit
.mapToGlobal(event
.pos()))
797 def diff_context_menu_setup(self
):
798 menu
= QMenu(self
.view
)
799 if self
.mode
== Controller
.MODE_WORKTREE
:
800 unstaged_item
= qtutils
.get_selected_item(self
.view
.unstaged
,
801 self
.model
.get_unstaged())
802 is_tracked
= (unstaged_item
803 and unstaged_item
not in self
.model
.get_untracked())
805 menu
.addAction(self
.tr('Stage Hunk For Commit'),
807 menu
.addAction(self
.tr('Stage Selected Lines'),
808 self
.stage_hunk_selection
)
810 elif self
.mode
== Controller
.MODE_INDEX
:
811 menu
.addAction(self
.tr('Unstage Hunk From Commit'),
813 menu
.addAction(self
.tr('Unstage Selected Lines'),
814 self
.unstage_hunk_selection
)
816 elif self
.mode
== Controller
.MODE_BRANCH
:
817 menu
.addAction(self
.tr('Apply Diff To Work Tree'),
819 menu
.addAction(self
.tr('Apply Diff Selection To Work Tree'),
820 self
.stage_hunk_selection
)
822 menu
.addAction(self
.tr('Copy'),
823 self
.view
.copy_display
)
826 def select_commits_gui(self
, title
, revs
, summaries
):
827 return select_commits(self
.model
, self
.view
, title
, revs
, summaries
)
829 def update_diff_font(self
):
830 font
= self
.model
.get_global_cola_fontdiff()
834 qfont
.fromString(font
)
835 self
.view
.display_text
.setFont(qfont
)
836 self
.view
.commitmsg
.setFont(qfont
)
838 def update_ui_font(self
):
839 font
= self
.model
.get_global_cola_fontui()
843 qfont
.fromString(font
)
844 QtGui
.qApp
.setFont(qfont
)
846 def init_log_window(self
):
847 branch
= self
.model
.get_currentbranch()
848 version
= defaults
.VERSION
849 qtutils
.log(self
.model
.get_git_version()
850 +'\ncola version '+ version
851 +'\nCurrent Branch: '+ branch
)
853 def start_inotify_thread(self
):
854 # Do we have inotify? If not, return.
855 # Recommend installing inotify if we're on Linux.
856 self
.inotify_thread
= None
858 from cola
.inotify
import GitNotifier
859 qtutils
.log(self
.tr('inotify support: enabled'))
862 if platform
.system() == 'Linux':
863 msg
= self
.tr('inotify: disabled\n'
864 'Note: To enable inotify, '
865 'install python-pyinotify.\n')
867 plat
= platform
.platform().lower()
868 if 'debian' in plat
or 'ubuntu' in plat
:
869 msg
+= self
.tr('On Debian or Ubuntu systems, '
870 'try: sudo apt-get install '
875 # Start the notification thread
876 self
.inotify_thread
= GitNotifier(self
, os
.getcwd())
877 self
.inotify_thread
.start()