controllers: add an "edit diff" launcher
[git-cola.git] / ugit / controllers / __init__.py
blob2b78e30373921920c339da86bca12cb141f70d2f
1 #!/usr/bin/env python
2 import os
3 import sys
4 import time
6 from PyQt4 import QtCore
7 from PyQt4 import QtGui
8 from PyQt4.QtGui import QDialog
9 from PyQt4.QtGui import QMessageBox
10 from PyQt4.QtGui import QMenu
11 from PyQt4.QtGui import QFont
13 from ugit import utils
14 from ugit import qtutils
15 from ugit import defaults
16 from ugit.qobserver import QObserver
18 # controllers namespace
19 import search
20 from util import logger
21 from push import push_branches
22 from util import choose_branch
23 from util import select_commits
24 from util import update_options
25 from repobrowser import browse_git_branch
26 from createbranch import create_new_branch
27 from search import search_commits
28 from merge import local_merge
29 from merge import abort_merge
30 from bookmark import save_bookmark
31 from bookmark import manage_bookmarks
32 from stash import stash
35 class Controller(QObserver):
36 """Manages the interaction between models and views."""
38 def init(self, model, view):
39 # parent-less log window
40 qtutils.LOGGER = logger()
42 # Avoids inotify floods from e.g. make
43 self.__last_inotify_event = time.time()
45 # The unstaged list context menu
46 self.__unstaged_menu = None
48 # The diff-display context menu
49 self.__diff_menu = None
50 self.__staged_diff_in_view = True
51 self.__diffgui_enabled = True
53 # Unstaged changes context menu
54 view.unstaged.contextMenuEvent = self.unstaged_context_menu_event
56 # Diff display context menu
57 view.display_text.contextMenuEvent = self.diff_context_menu_event
59 # Binds model params to their equivalent view widget
60 self.add_observables('commitmsg', 'staged', 'unstaged')
62 # When a model attribute changes, this runs a specific action
63 self.add_actions(staged = self.action_staged)
64 self.add_actions(unstaged = self.action_unstaged)
65 self.add_actions(global_ugit_fontdiff = self.update_diff_font)
66 self.add_actions(global_ugit_fontui = self.update_ui_font)
68 self.add_callbacks(
69 # Push Buttons
70 signoff_button = self.model.add_signoff,
71 stage_button = self.stage_selected,
72 commit_button = self.commit,
73 push_button = self.push,
74 # List Widgets
75 staged = self.diff_staged,
76 unstaged = self.diff_unstaged,
77 # Checkboxes
78 untracked_checkbox = self.rescan,
80 # File Menu
81 menu_quit = self.quit_app,
82 menu_open_repo = self.open_repo,
83 menu_manage_bookmarks = manage_bookmarks,
84 menu_save_bookmark = save_bookmark,
86 # Edit Menu
87 menu_options = self.options,
88 menu_cut = self.view.action_cut,
89 menu_copy = self.view.action_copy,
90 menu_paste = self.view.action_paste,
91 menu_delete = self.view.action_delete,
92 menu_select_all = self.view.action_select_all,
93 menu_undo = self.view.action_undo,
94 menu_redo = self.view.action_redo,
96 # Search Menu
97 menu_search_grep = self.grep,
98 menu_search_revision =
99 self.gen_search( search.REVISION_ID ),
100 menu_search_revision_range =
101 self.gen_search( search.REVISION_RANGE ),
102 menu_search_message =
103 self.gen_search( search.MESSAGE ),
104 menu_search_path =
105 self.gen_search( search.PATH, True ),
106 menu_search_date_range =
107 self.gen_search( search.DATE_RANGE ),
108 menu_search_diff =
109 self.gen_search( search.DIFF ),
110 menu_search_author =
111 self.gen_search( search.AUTHOR ),
112 menu_search_committer =
113 self.gen_search( search.COMMITTER ),
115 # Merge Menu
116 menu_merge_local =
117 lambda: local_merge( self.model, self.view ),
118 menu_merge_abort =
119 lambda: abort_merge( self.model, self.view ),
121 # Repository Menu
122 menu_visualize_current = self.viz_current,
123 menu_visualize_all = self.viz_all,
124 menu_browse_commits = self.browse_commits,
125 menu_browse_branch = self.browse_current,
126 menu_browse_other_branch = self.browse_other,
128 # Commit Menu
129 menu_rescan = self.rescan,
130 menu_create_branch = self.branch_create,
131 menu_delete_branch = self.branch_delete,
132 menu_checkout_branch = self.checkout_branch,
133 menu_rebase_branch = self.rebase,
134 menu_commit = self.commit,
135 menu_stage_selected = self.stage_selected,
136 menu_unstage_selected = self.unstage_selected,
137 menu_show_diffstat = self.show_diffstat,
138 menu_show_index = self.show_index,
139 menu_export_patches = self.export_patches,
140 menu_stash =
141 lambda: stash( self.model, self.view ),
142 menu_load_commitmsg = self.load_commitmsg,
143 menu_cherry_pick = self.cherry_pick,
144 menu_get_prev_commitmsg = model.get_prev_commitmsg,
145 menu_stage_modified =
146 lambda: self.log(self.model.stage_modified()),
147 menu_stage_untracked =
148 lambda: self.log(self.model.stage_untracked()),
149 menu_unstage_all =
150 lambda: self.log(self.model.unstage_all()),
153 # Delegate window events here
154 view.moveEvent = self.move_event
155 view.resizeEvent = self.resize_event
156 view.closeEvent = self.quit_app
157 view.staged.mousePressEvent = self.click_staged
158 view.unstaged.mousePressEvent = self.click_unstaged
160 # These are vanilla signal/slots since QObserver
161 # is already handling these signals.
162 self.connect(view.unstaged,
163 'itemDoubleClicked(QListWidgetItem*)',
164 self.stage_selected)
165 self.connect(view.staged,
166 'itemDoubleClicked(QListWidgetItem*)',
167 self.unstage_selected)
169 # Toolbar log button
170 self.connect(view.toolbar_show_log,
171 'triggered()', self.show_log)
173 self.connect(view.diff_dock,
174 'topLevelChanged(bool)',
175 lambda(b): self.setwindow(view.diff_dock, b))
177 self.connect(view.editor_dock,
178 'topLevelChanged(bool)',
179 lambda(b): self.setwindow(view.editor_dock, b))
181 self.connect(view.status_dock,
182 'topLevelChanged(bool)',
183 lambda(b): self.setwindow(view.status_dock, b))
185 self.init_log_window()
186 self.load_gui_settings()
187 self.rescan()
188 self.refresh_view(
189 'global_ugit_fontdiff',
190 'global_ugit_fontui',
192 self.start_inotify_thread()
194 def setwindow(self, dock, isfloating):
195 if isfloating:
196 flags = ( QtCore.Qt.Window
197 | QtCore.Qt.FramelessWindowHint )
198 dock.setWindowFlags( flags )
199 dock.show()
201 #####################################################################
202 # handle when the listitem icons are clicked
203 def click_event(self, widget, action_callback, event):
204 result = QtGui.QListWidget.mousePressEvent(widget, event)
205 xpos = event.pos().x()
206 if xpos > 5 and xpos < 20:
207 action_callback()
208 return result
210 def click_staged(self, event):
211 return self.click_event(
212 self.view.staged,
213 self.unstage_selected,
214 event)
216 def click_unstaged(self, event):
217 return self.click_event(
218 self.view.unstaged,
219 self.stage_selected,
220 event)
223 #####################################################################
224 # event() is called in response to messages from the inotify thread
225 def event(self, msg):
226 if msg.type() == defaults.INOTIFY_EVENT:
227 self.rescan()
228 return True
229 else:
230 return False
232 #####################################################################
233 # Actions triggered during model updates
235 def action_staged(self, widget):
236 qtutils.update_listwidget(widget,
237 self.model.get_staged(), staged=True)
238 self.view.editor_dock.raise_()
240 def action_unstaged(self, widget):
241 qtutils.update_listwidget(widget,
242 self.model.get_modified(), staged=False)
244 if self.view.untracked_checkbox.isChecked():
245 qtutils.update_listwidget(widget,
246 self.model.get_untracked(),
247 staged=False,
248 append=True,
249 untracked=True)
251 #####################################################################
252 # Qt callbacks
253 def gen_search(self, searchtype, browse=False):
254 def search_handler():
255 search_commits(self.model, searchtype, browse)
256 return search_handler
258 def grep(self):
259 txt, ok = qtutils.input("grep")
260 if not ok:
261 return
262 stuff = self.model.grep(txt)
263 self.view.display_text.setText(stuff)
264 self.view.diff_dock.raise_()
266 def show_log(self, *rest):
267 qtutils.toggle_log_window()
269 def options(self):
270 update_options(self.model, self.view)
272 def branch_create(self):
273 if create_new_branch(self.model, self.view):
274 self.rescan()
276 def branch_delete(self):
277 branch = choose_branch('Delete Branch',
278 self.view, self.model.get_local_branches())
279 if not branch: return
280 self.log(self.model.delete_branch(branch))
282 def browse_current(self):
283 branch = self.model.get_currentbranch()
284 browse_git_branch(self.model, self.view, branch)
286 def browse_other(self):
287 # Prompt for a branch to browse
288 branch = choose_branch('Browse Branch Files',
289 self.view, self.model.get_all_branches())
290 if not branch: return
291 # Launch the repobrowser
292 browse_git_branch(self.model, self.view, branch)
294 def checkout_branch(self):
295 branch = choose_branch('Checkout Branch',
296 self.view, self.model.get_local_branches())
297 if not branch: return
298 self.log(self.model.checkout(branch))
300 def browse_commits(self):
301 self.select_commits_gui(self.tr('Browse Commits'),
302 *self.model.log_helper(all=True))
304 def cherry_pick(self):
305 commits = self.select_commits_gui(self.tr('Cherry-Pick Commits'),
306 *self.model.log_helper(all=True))
307 if not commits: return
308 self.log(self.model.cherry_pick_list(commits))
310 def commit(self):
311 msg = self.model.get_commitmsg()
312 if not msg:
313 error_msg = self.tr(""
314 + "Please supply a commit message.\n"
315 + "\n"
316 + "A good commit message has the following format:\n"
317 + "\n"
318 + "- First line: Describe in one sentence what you did.\n"
319 + "- Second line: Blank\n"
320 + "- Remaining lines: Describe why this change is good.\n")
321 self.log(error_msg)
322 return
324 files = self.model.get_staged()
325 if not files and not self.view.amend_radio.isChecked():
326 error_msg = self.tr(""
327 + "No changes to commit.\n"
328 + "\n"
329 + "You must stage at least 1 file before you can commit.\n")
330 self.log(error_msg)
331 return
333 # Perform the commit
334 output = self.model.commit_with_msg(
335 msg, amend=self.view.amend_radio.isChecked())
337 # Reset state
338 self.view.new_commit_radio.setChecked(True)
339 self.view.amend_radio.setChecked(False)
340 self.model.set_commitmsg('')
341 self.log(output)
343 def view_diff(self, staged=True):
344 self.__staged_diff_in_view = staged
345 if self.__staged_diff_in_view:
346 widget = self.view.staged
347 else:
348 widget = self.view.unstaged
349 row, selected = qtutils.get_selected_row(widget)
350 if not selected:
351 self.view.reset_display()
352 self.__diffgui_enabled = False
353 return
354 (diff,
355 status) = self.model.get_diff_and_status(row, staged=staged)
357 self.view.set_display(diff)
358 self.view.set_info(self.tr(status))
359 self.view.diff_dock.raise_()
360 self.__diffgui_enabled = True
362 def edit_diff(self, staged=True):
363 self.__staged_diff_in_view = staged
364 if self.__staged_diff_in_view:
365 widget = self.view.staged
366 else:
367 widget = self.view.unstaged
368 row, selected = qtutils.get_selected_row(widget)
369 diff, status = self.model.get_diff_and_status(row, staged=staged)
370 if not selected:
371 return
373 if staged:
374 basename = self.model.get_staged()[row]
375 else:
376 basename = self.model.get_unstaged()[row]
378 contents = self.model.show("HEAD:"+basename, with_raw_output=True)
379 tmpfile = self.model.get_tmp_filename()
380 fh = open(tmpfile, 'w')
381 fh.write(contents)
382 fh.close()
384 pid = os.fork()
385 if pid:
386 return
387 else:
388 utils.run_cmd(self.model.get_diffeditor(), basename, tmpfile)
389 os.unlink(tmpfile)
390 sys.exit(0)
392 # use *rest to handle being called from different signals
393 def diff_staged(self, *rest):
394 self.view_diff(staged=True)
396 # use *rest to handle being called from different signals
397 def diff_unstaged(self, *rest):
398 self.view_diff(staged=False)
400 def export_patches(self):
401 (revs, summaries) = self.model.log_helper()
402 to_export = self.select_commits_gui(self.tr('Export Patches'),
403 revs, summaries)
404 if not to_export:
405 return
406 to_export.reverse()
407 revs.reverse()
408 self.log(self.model.format_patch_helper(
409 to_export, revs, output='patches'))
411 def open_repo(self):
412 """Spawns a new ugit session"""
413 dirname = qtutils.opendir_dialog(self.view,
414 'Open Git Repository...',
415 os.getcwd())
416 if dirname:
417 utils.fork(sys.argv[0], dirname)
419 def quit_app(self, *args):
420 """Save config settings and cleanup any inotify threads."""
422 if self.model.save_at_exit():
423 self.model.save_gui_settings()
424 qtutils.close_log_window()
426 if (self.inotify_thread and
427 self.inotify_thread.isRunning()):
429 self.inotify_thread.abort = True
430 self.inotify_thread.wait()
432 self.view.close()
434 def load_commitmsg(self):
435 file = qtutils.open_dialog(self.view,
436 'Load Commit Message...', defaults.DIRECTORY)
438 if file:
439 defaults.DIRECTORY = os.path.dirname(file)
440 slushy = utils.slurp(file)
441 if slushy: self.model.set_commitmsg(slushy)
443 def rebase(self):
444 branch = choose_branch('Rebase Branch',
445 self.view, self.model.get_local_branches())
446 if not branch: return
447 self.log(self.model.rebase(branch))
449 # use *rest to handle being called from the checkbox signal
450 def rescan(self, *rest):
451 '''Populates view widgets with results from "git status."'''
453 # save entire selection
454 unstaged = qtutils.get_selection_list(
455 self.view.unstaged,
456 self.model.get_unstaged())
457 staged = qtutils.get_selection_list(
458 self.view.staged,
459 self.model.get_staged())
461 scrollbar = self.view.display_text.verticalScrollBar()
462 scrollvalue = scrollbar.value()
464 # save selected item
465 unstageditem = qtutils.get_selected_item(
466 self.view.unstaged,
467 self.model.get_unstaged())
469 stageditem = qtutils.get_selected_item(
470 self.view.staged,
471 self.model.get_staged())
473 # get new values
474 self.model.update_status()
476 # restore selection
477 update_staged = False
478 update_unstaged = False
479 updated_unstaged = self.model.get_unstaged()
480 updated_staged = self.model.get_staged()
482 for item in unstaged:
483 if item in updated_unstaged:
484 idx = updated_unstaged.index(item)
485 listitem = self.view.unstaged.item(idx)
486 if listitem:
487 listitem.setSelected(True)
488 self.view.unstaged\
489 .setItemSelected(listitem, True)
490 update_unstaged = True
491 self.view.unstaged.update()
492 for item in staged:
493 if item in updated_staged:
494 idx = updated_staged.index(item)
495 listitem = self.view.staged.item(idx)
496 if listitem:
497 listitem.setSelected(True)
498 self.view.staged\
499 .setItemSelected(listitem, True)
500 update_staged = True
502 # restore selected item
503 if update_staged and stageditem:
504 idx = updated_staged.index(stageditem)
505 item = self.view.staged.item(idx)
506 self.view.staged.setCurrentItem(item)
507 self.view_diff(True)
508 scrollbar.setValue(scrollvalue)
510 elif update_unstaged and unstageditem:
511 idx = updated_unstaged.index(unstageditem)
512 item = self.view.unstaged.item(idx)
513 self.view.unstaged.setCurrentItem(item)
514 self.view_diff(False)
515 scrollbar.setValue(scrollvalue)
517 # Update the title with the current branch
518 self.view.setWindowTitle('%s [%s]' % (
519 self.model.get_project(),
520 self.model.get_currentbranch()))
522 # Check if there's a message file in .git/
523 merge_msg_path = self.model.get_merge_message_path()
524 if merge_msg_path is None:
525 return
527 # A merge message file exists.
528 set_msg = False
529 if self.model.get_commitmsg():
530 # The commit message editor contains data.
531 # Prompt before overwriting the commit message
532 # with the contents of the merge message.
533 answer = qtutils.question(self.view,
534 self.tr('Import Commit Message?'),
535 self.tr('A commit message from an in-progress'
536 + ' merge was found.\nImport it?'))
538 if answer:
539 set_msg = True
540 else:
541 set_msg = True
542 # Set the new commit message
543 if set_msg:
544 self.model.load_commitmsg(merge_msg_path)
545 self.view.editor_dock.raise_()
547 def push(self):
548 push_branches(self.model, self.view)
550 def show_diffstat(self):
551 """Show the diffstat from the latest commit."""
552 self.__diffgui_enabled = False
553 self.view.set_info(self.tr('Diffstat'))
554 self.view.set_display(self.model.diffstat())
556 def show_index(self):
557 self.__diffgui_enabled = False
558 self.view.set_info(self.tr('Index'))
559 self.view.set_display(self.model.diffindex())
561 #####################################################################
562 # diff gui
563 def process_diff_selection(self, items, widget,
564 cached=True, selected=False, reverse=True, noop=False):
566 filename = qtutils.get_selected_item(widget, items)
567 if not filename: return
568 parser = utils.DiffParser(self.model, filename=filename,
569 cached=cached)
570 offset, selection = self.view.diff_selection()
571 parser.process_diff_selection(selected, offset, selection)
572 self.rescan()
574 def stage_hunk(self):
575 self.process_diff_selection(
576 self.model.get_unstaged(),
577 self.view.unstaged,
578 cached=False)
580 def stage_hunk_selection(self):
581 self.process_diff_selection(
582 self.model.get_unstaged(),
583 self.view.unstaged,
584 cached=False,
585 selected=True)
587 def unstage_hunk(self, cached=True):
588 self.process_diff_selection(
589 self.model.get_staged(),
590 self.view.staged,
591 cached=True)
593 def unstage_hunk_selection(self):
594 self.process_diff_selection(
595 self.model.get_staged(),
596 self.view.staged,
597 cached=True,
598 selected=True)
600 # #######################################################################
601 # end diff gui
603 # *rest handles being called from different signals
604 def stage_selected(self,*rest):
605 """Use "git add" to add items to the git index.
606 This is a thin wrapper around map_to_listwidget."""
607 command = self.model.add_or_remove
608 widget = self.view.unstaged
609 items = self.model.get_unstaged()
610 self.map_to_listwidget(command, widget, items)
612 # *rest handles being called from different signals
613 def unstage_selected(self, *rest):
614 """Use "git reset" to remove items from the git index.
615 This is a thin wrapper around map_to_listwidget."""
616 command = self.model.reset_helper
617 widget = self.view.staged
618 items = self.model.get_staged()
619 self.map_to_listwidget(command, widget, items)
621 def undo_changes(self):
622 """Reverts local changes back to whatever's in HEAD."""
623 widget = self.view.unstaged
624 items = self.model.get_unstaged()
625 potential_items = qtutils.get_selection_list(widget, items)
626 items_to_undo = []
627 untracked = self.model.get_untracked()
628 for item in potential_items:
629 if item not in untracked:
630 items_to_undo.append(item)
631 if items_to_undo:
632 answer = qtutils.question(self.view,
633 self.tr('Destroy Local Changes?'),
634 self.tr('This operation will drop all '
635 + ' uncommitted changes. Continue?'),
636 default=False)
638 if not answer: return
640 output = self.model.checkout('HEAD', '--',
641 *items_to_undo)
642 self.log('git checkout HEAD -- '
643 + ' '.join(items_to_undo)
644 + '\n' + output)
645 else:
646 msg = 'No files selected for checkout from HEAD.'
647 self.log(self.tr(msg))
649 def viz_all(self):
650 """Visualizes the entire git history using gitk."""
651 browser = self.model.get_global_ugit_historybrowser()
652 utils.fork(browser,'--all')
654 def viz_current(self):
655 """Visualizes the current branch's history using gitk."""
656 browser = self.model.get_global_ugit_historybrowser()
657 utils.fork(browser, self.model.get_currentbranch())
659 def move_event(self, event):
660 defaults.X = event.pos().x()
661 defaults.Y = event.pos().y()
663 def resize_event(self, event):
664 defaults.WIDTH = event.size().width()
665 defaults.HEIGHT = event.size().height()
667 def load_gui_settings(self):
668 if not self.model.remember_gui_settings():
669 return
670 (w,h,x,y,
671 st0,st1,
672 sb0,sb1) = self.model.get_window_geom()
673 self.view.resize(w,h)
674 self.view.move(x,y)
676 def log(self, output, rescan=True, quiet=False):
677 """Logs output and optionally rescans for changes."""
678 qtutils.log(output, quiet=quiet, doraise=False)
679 if rescan: self.rescan()
681 def map_to_listwidget(self, command, widget, items):
682 """This is a helper method that retrieves the current
683 selection list, applies a command to that list,
684 displays a dialog showing the output of that command,
685 and calls rescan to pickup changes."""
686 apply_items = qtutils.get_selection_list(widget, items)
687 output = command(*apply_items)
688 self.log(output, quiet=True)
690 def unstaged_context_menu_event(self, event):
691 self.unstaged_context_menu_setup()
692 unstaged = self.view.unstaged
693 self.__unstaged_menu.exec_(unstaged.mapToGlobal(event.pos()))
695 def unstaged_context_menu_setup(self):
696 if self.__unstaged_menu: return
698 menu = self.__unstaged_menu = QMenu(self.view)
699 self.__stage_selected_action = menu.addAction(
700 self.tr('Stage Selected'), self.stage_selected)
701 self.__undo_changes_action = menu.addAction(
702 self.tr('Undo Local Changes'), self.undo_changes)
703 self.__edit_diff = menu.addAction(
704 self.tr('Launch Diff Editor'), lambda: self.edit_diff(staged=False))
706 self.connect(self.__unstaged_menu, 'aboutToShow()',
707 self.unstaged_context_menu_about_to_show)
709 def unstaged_context_menu_about_to_show(self):
710 unstaged_item = qtutils.get_selected_item(
711 self.view.unstaged,
712 self.model.get_unstaged())
714 is_tracked = unstaged_item not in self.model.get_untracked()
716 enable_staging = bool(self.__diffgui_enabled
717 and unstaged_item)
718 enable_undo = enable_staging and is_tracked
720 self.__stage_selected_action.setEnabled(enable_staging)
721 self.__undo_changes_action.setEnabled(enable_undo)
723 def diff_context_menu_about_to_show(self):
724 unstaged_item = qtutils.get_selected_item(
725 self.view.unstaged,
726 self.model.get_unstaged())
728 is_tracked= unstaged_item not in self.model.get_untracked()
730 enable_staged= (
731 self.__diffgui_enabled
732 and unstaged_item
733 and not self.__staged_diff_in_view
734 and is_tracked)
736 enable_unstaged= (
737 self.__diffgui_enabled
738 and self.__staged_diff_in_view
739 and qtutils.get_selected_item(
740 self.view.staged,
741 self.model.get_staged()))
743 self.__stage_hunk_action.setEnabled(bool(enable_staged))
744 self.__stage_hunk_selection_action.setEnabled(bool(enable_staged))
746 self.__unstage_hunk_action.setEnabled(bool(enable_unstaged))
747 self.__unstage_hunk_selection_action.setEnabled(bool(enable_unstaged))
749 def diff_context_menu_event(self, event):
750 self.diff_context_menu_setup()
751 textedit = self.view.display_text
752 self.__diff_menu.exec_(textedit.mapToGlobal(event.pos()))
754 def diff_context_menu_setup(self):
755 if self.__diff_menu: return
757 menu = self.__diff_menu = QMenu(self.view)
758 self.__stage_hunk_action = menu.addAction(
759 self.tr('Stage Hunk For Commit'), self.stage_hunk)
761 self.__stage_hunk_selection_action = menu.addAction(
762 self.tr('Stage Selected Lines'),
763 self.stage_hunk_selection)
765 self.__unstage_hunk_action = menu.addAction(
766 self.tr('Unstage Hunk From Commit'),
767 self.unstage_hunk)
769 self.__unstage_hunk_selection_action = menu.addAction(
770 self.tr('Unstage Selected Lines'),
771 self.unstage_hunk_selection)
773 self.__copy_action = menu.addAction(
774 self.tr('Copy'), self.view.copy_display)
776 self.connect(self.__diff_menu, 'aboutToShow()',
777 self.diff_context_menu_about_to_show)
779 def select_commits_gui(self, title, revs, summaries):
780 return select_commits(self.model, self.view, title, revs, summaries)
782 def update_diff_font(self):
783 font = self.model.get_global_ugit_fontdiff()
784 if not font: return
785 qfont = QFont()
786 qfont.fromString(font)
787 self.view.display_text.setFont(qfont)
788 self.view.commitmsg.setFont(qfont)
790 def update_ui_font(self):
791 font = self.model.get_global_ugit_fontui()
792 if not font: return
793 qfont = QFont()
794 qfont.fromString(font)
795 QtGui.qApp.setFont(qfont)
797 def init_log_window(self):
798 branch = self.model.get_currentbranch()
799 version = defaults.VERSION
800 qtutils.log(self.model.get_git_version()
801 + '\nugit version '+ version
802 + '\nCurrent Branch: '+ branch)
804 def start_inotify_thread(self):
805 # Do we have inotify? If not, return.
806 # Recommend installing inotify if we're on Linux.
807 self.inotify_thread = None
808 try:
809 from ugit.inotify import GitNotifier
810 qtutils.log(self.tr('inotify support: enabled'))
811 except ImportError:
812 import platform
813 if platform.system() == 'Linux':
815 msg = self.tr(
816 'inotify: disabled\n'
817 'Note: To enable inotify, '
818 'install python-pyinotify.\n')
820 plat = platform.platform().lower()
821 if 'debian' in plat or 'ubuntu' in plat:
822 msg += self.tr(
823 'On Debian or Ubuntu systems, '
824 'try: sudo apt-get install '
825 'python-pyinotify')
826 qtutils.log(msg)
828 return
830 # Start the notification thread
831 self.inotify_thread = GitNotifier(self, os.getcwd())
832 self.inotify_thread.start()