4 from PyQt4
import QtGui
5 from PyQt4
import QtCore
6 from PyQt4
.QtGui
import QDialog
7 from PyQt4
.QtGui
import QMessageBox
8 from PyQt4
.QtGui
import QMenu
9 from qobserver
import QObserver
14 from views
import GitPushDialog
15 from views
import GitBranchDialog
16 from views
import GitCreateBranchDialog
17 from views
import GitCommitBrowser
18 from repobrowsercontroller
import GitRepoBrowserController
19 from createbranchcontroller
import GitCreateBranchController
20 from pushcontroller
import GitPushController
22 class GitController(QObserver
):
23 '''The controller is a mediator between the model and view.
24 It allows for a clean decoupling between view and model classes.'''
26 def __init__(self
, model
, view
):
27 QObserver
.__init
__(self
, model
, view
)
29 # The diff-display context menu
31 self
.__staged
_diff
_in
_view
= True
33 # Diff display context menu
34 view
.displayText
.controller
= self
35 view
.displayText
.contextMenuEvent
= self
.__menu
_event
37 # Default to creating a new commit(i.e. not an amend commit)
38 view
.newCommitRadio
.setChecked(True)
40 # Binds a specific model attribute to a view widget,
42 self
.model_to_view('commitmsg', 'commitText')
43 self
.model_to_view('staged', 'stagedList')
44 self
.model_to_view('all_unstaged', 'unstagedList')
46 # When a model attribute changes, this runs a specific action
47 self
.add_actions('staged', self
.action_staged
)
48 self
.add_actions('all_unstaged', self
.action_all_unstaged
)
50 # Routes signals for multiple widgets to our callbacks
52 self
.add_signals('textChanged()', view
.commitText
)
53 self
.add_signals('stateChanged(int)', view
.untrackedCheckBox
)
55 self
.add_signals('released()',
56 view
.stageButton
, view
.commitButton
,
57 view
.pushButton
, view
.signOffButton
,)
59 self
.add_signals('triggered()',
61 view
.createBranch
, view
.checkoutBranch
,
62 view
.rebaseBranch
, view
.deleteBranch
,
63 view
.setCommitMessage
, view
.commit
,
64 view
.stageChanged
, view
.stageUntracked
,
65 view
.stageSelected
, view
.unstageAll
,
68 view
.browseBranch
, view
.browseOtherBranch
,
69 view
.visualizeAll
, view
.visualizeCurrent
,
70 view
.exportPatches
, view
.cherryPick
,
72 view
.cut
, view
.copy
, view
.paste
, view
.delete
,
73 view
.selectAll
, view
.undo
, view
.redo
,)
75 self
.add_signals('itemClicked(QListWidgetItem *)',
76 view
.stagedList
, view
.unstagedList
,)
78 self
.add_signals('itemSelectionChanged()',
79 view
.stagedList
, view
.unstagedList
,)
81 self
.add_signals('splitterMoved(int,int)',
82 view
.splitter_top
, view
.splitter_bottom
)
85 self
.connect(QtGui
.qApp
, 'lastWindowClosed()',
86 self
.last_window_closed
)
88 # These callbacks are called in response to the signals
89 # defined above. One property of the QObserver callback
90 # mechanism is that the model is passed in as the first
91 # argument to the callback. This allows for a single
92 # controller to manage multiple models, though this
93 # isn't used at the moment.
95 # Actions that delegate directly to the model
96 'signOffButton': model
.add_signoff
,
97 'setCommitMessage': model
.get_prev_commitmsg
,
99 'stageButton': self
.stage_selected
,
100 'commitButton': self
.commit
,
101 'pushButton': self
.push
,
103 'stagedList': self
.diff_staged
,
104 'unstagedList': self
.diff_unstaged
,
106 'untrackedCheckBox': self
.rescan
,
108 'rescan': self
.rescan
,
109 'createBranch': self
.branch_create
,
110 'deleteBranch': self
.branch_delete
,
111 'checkoutBranch': self
.checkout_branch
,
112 'rebaseBranch': self
.rebase
,
113 'commit': self
.commit
,
114 'stageChanged': self
.stage_changed
,
115 'stageUntracked': self
.stage_untracked
,
116 'stageSelected': self
.stage_selected
,
117 'unstageAll': self
.unstage_all
,
118 'unstageSelected': self
.unstage_selected
,
119 'showDiffstat': self
.show_diffstat
,
120 'browseBranch': self
.browse_current
,
121 'browseOtherBranch': self
.browse_other
,
122 'visualizeCurrent': self
.viz_current
,
123 'visualizeAll': self
.viz_all
,
124 'exportPatches': self
.export_patches
,
125 'cherryPick': self
.cherry_pick
,
126 'loadCommitMsg': self
.load_commitmsg
,
130 'delete': self
.delete
,
131 'selectAll': self
.select_all
,
132 'undo': self
.view
.commitText
.undo
,
135 'splitter_top': self
.splitter_top_event
,
136 'splitter_bottom': self
.splitter_bottom_event
,
139 # Handle double-clicks in the staged/unstaged lists.
140 # These are vanilla signal/slots since the qobserver
141 # signal routing is already handling these lists' signals.
142 self
.connect(view
.unstagedList
,
143 'itemDoubleClicked(QListWidgetItem*)',
146 self
.connect(view
.stagedList
,
147 'itemDoubleClicked(QListWidgetItem*)',
148 self
.unstage_selected
)
150 # Delegate window move events here
151 self
.view
.moveEvent
= self
.move_event
152 self
.view
.resizeEvent
= self
.resize_event
155 self
.__read
_config
_settings
()
158 # Setup the inotify watchdog
159 self
.__start
_inotify
_thread
()
161 #####################################################################
162 # Actions triggered during model updates
164 def action_staged(self
, widget
):
165 self
.__update
_listwidget
(widget
,
166 self
.model
.get_staged(), staged
=True)
168 def action_all_unstaged(self
, widget
):
169 self
.__update
_listwidget
(widget
,
170 self
.model
.get_unstaged(), staged
=False)
172 if self
.view
.untrackedCheckBox
.isChecked():
173 self
.__update
_listwidget
(widget
,
174 self
.model
.get_untracked(),
179 #####################################################################
182 def branch_create(self
):
183 view
= GitCreateBranchDialog(self
.view
)
184 controller
= GitCreateBranchController(self
.model
, view
)
186 result
= view
.exec_()
187 if result
== QDialog
.Accepted
:
190 def branch_delete(self
):
191 dlg
= GitBranchDialog(self
.view
, branches
=cmds
.git_branch())
192 branch
= dlg
.getSelectedBranch()
193 if not branch
: return
194 qtutils
.show_command(self
.view
,
195 cmds
.git_branch(name
=branch
, delete
=True))
197 def browse_current(self
):
198 self
.__browse
_branch
(cmds
.git_current_branch())
200 def browse_other(self
):
201 # Prompt for a branch to browse
202 branches
= self
.model
.all_branches()
203 dialog
= GitBranchDialog(self
.view
, branches
=branches
)
205 # Launch the repobrowser
206 self
.__browse
_branch
(dialog
.getSelectedBranch())
208 def checkout_branch(self
):
209 dlg
= GitBranchDialog(self
.view
, cmds
.git_branch())
210 branch
= dlg
.getSelectedBranch()
211 if not branch
: return
212 qtutils
.show_command(self
.view
, cmds
.git_checkout(branch
))
215 def cherry_pick(self
):
216 '''Starts a cherry-picking session.'''
217 (revs
, summaries
) = cmds
.git_log(all
=True)
218 selection
, idxs
= self
.__select
_commits
(revs
, summaries
)
219 output
= cmds
.git_cherry_pick(selection
)
220 self
.__show
_command
(self
.tr(output
))
223 '''Sets up data and calls cmds.commit.'''
224 msg
= self
.model
.get_commitmsg()
226 error_msg
= self
.tr(""
227 + "Please supply a commit message.\n"
229 + "A good commit message has the following format:\n"
231 + "- First line: Describe in one sentence what you did.\n"
232 + "- Second line: Blank\n"
233 + "- Remaining lines: Describe why this change is good.\n")
235 self
.__show
_command
(error_msg
)
238 files
= self
.model
.get_staged()
241 + "No changes to commit.\n"
243 + "You must stage at least 1 file before you can commit.\n")
244 self
.__show
_command
(errmsg
)
248 output
= cmds
.git_commit(msg
,
249 amend
=self
.view
.amendRadio
.isChecked())
252 self
.view
.newCommitRadio
.setChecked(True)
253 self
.view
.amendRadio
.setChecked(False)
254 self
.model
.set_commitmsg('')
255 self
.__show
_command
(output
)
257 def commit_sha1_selected(self
, browser
, revs
):
258 '''This callback is called when a commit browser's
259 item is selected. This callback puts the current
260 revision sha1 into the commitText field.
261 This callback also puts shows the commit in the
262 browser's commit textedit and copies it into
263 the global clipboard/selection.'''
264 current
= browser
.commitList
.currentRow()
265 item
= browser
.commitList
.item(current
)
266 if not item
.isSelected():
267 browser
.commitText
.setText('')
268 browser
.revisionLine
.setText('')
271 # Get the commit's sha1 and put it in the revision line
273 browser
.revisionLine
.setText(sha1
)
274 browser
.revisionLine
.selectAll()
276 # Lookup the info for that sha1 and display it
277 commit_diff
= cmds
.git_show(sha1
)
278 browser
.commitText
.setText(commit_diff
)
280 # Copy the sha1 into the clipboard
281 qtutils
.set_clipboard(sha1
)
283 # use *rest to handle being called from different signals
284 def diff_staged(self
, *rest
):
285 self
.__staged
_diff
_in
_view
= True
286 widget
= self
.view
.stagedList
287 row
, selected
= qtutils
.get_selected_row(widget
)
290 self
.__reset
_display
()
293 filename
= self
.model
.get_staged()[row
]
294 diff
= cmds
.git_diff(filename
, cached
=True)
296 if os
.path
.exists(filename
):
297 self
.__set
_info
(self
.tr('Staged for commit'))
299 self
.__set
_info
(self
.tr('Staged for removal'))
301 self
.view
.displayText
.setText(diff
)
303 # use *rest to handle being called from different signals
304 def diff_unstaged(self
,*rest
):
305 self
.__staged
_diff
_in
_view
= False
306 widget
= self
.view
.unstagedList
308 row
, selected
= qtutils
.get_selected_row(widget
)
310 self
.__reset
_display
()
313 filename
=(self
.model
.get_unstaged()
314 + self
.model
.get_untracked())[row
]
315 if os
.path
.isdir(filename
):
316 self
.__set
_info
(self
.tr('Untracked directory'))
317 cmd
= 'ls -la %s' % utils
.shell_quote(filename
)
318 output
= commands
.getoutput(cmd
)
319 self
.view
.displayText
.setText(output
)
322 if filename
in self
.model
.get_unstaged():
323 diff
= cmds
.git_diff(filename
, cached
=False)
325 self
.__set
_info
(self
.tr('Modified, not staged'))
328 cmd
= 'file -b %s' % utils
.shell_quote(filename
)
329 file_type
= commands
.getoutput(cmd
)
331 if 'binary' in file_type
or 'data' in file_type
:
332 sq_filename
= utils
.shell_quote(filename
)
333 cmd
= 'hexdump -C %s' % sq_filename
334 contents
= commands
.getoutput(cmd
)
336 if os
.path
.exists(filename
):
337 file = open(filename
, 'r')
338 contents
= file.read()
343 self
.__set
_info
(self
.tr('Untracked, not staged')
347 self
.view
.displayText
.setText(msg
)
349 def copy_display(self
):
350 cursor
= self
.view
.displayText
.textCursor()
351 selection
= cursor
.selection().toPlainText()
352 qtutils
.set_clipboard(selection
)
354 def export_patches(self
):
355 '''Launches the commit browser and exports the selected
358 (revs
, summaries
) = cmds
.git_log()
359 selection
, idxs
= self
.__select
_commits
(revs
, summaries
)
360 if not selection
: return
362 # now get the selected indices to determine whether
363 # a range of consecutive commits were selected
364 selected_range
= range(idxs
[0], idxs
[-1] + 1)
365 export_range
= len(idxs
) > 1 and idxs
== selected_range
367 output
= cmds
.git_format_patch(selection
, export_range
)
368 self
.__show
_command
(output
)
370 def get_commit_msg(self
):
371 self
.model
.retrieve_latest_commitmsg()
373 def last_window_closed(self
):
374 '''Save config settings and cleanup the any inotify threads.'''
376 self
.__save
_config
_settings
()
378 if not self
.inotify_thread
: return
379 if not self
.inotify_thread
.isRunning(): return
381 self
.inotify_thread
.abort
= True
382 self
.inotify_thread
.quit()
383 self
.inotify_thread
.wait()
385 def load_commitmsg(self
):
386 file = qtutils
.open_dialog(self
.view
,
387 self
.tr('Load Commit Message...'),
391 defaults
.DIRECTORY
= os
.path
.dirname(file)
392 slushy
= utils
.slurp(file)
393 self
.model
.set_commitmsg(slushy
)
397 dlg
= GitBranchDialog(self
.view
, cmds
.git_branch())
398 dlg
.setWindowTitle("Select the current branch's new root")
399 branch
= dlg
.getSelectedBranch()
400 if not branch
: return
401 qtutils
.show_command(self
.view
, cmds
.git_rebase(branch
))
403 # use *rest to handle being called from the checkbox signal
404 def rescan(self
, *rest
):
405 '''Populates view widgets with results from "git status."'''
407 self
.view
.statusBar().showMessage(
408 self
.tr('Scanning for modified files ...'))
410 # Rescan for repo updates
411 self
.model
.update_status()
413 # Scan for branch changes
414 self
.__set
_branch
_ui
_items
()
416 if not self
.model
.has_squash_msg(): return
418 if self
.model
.get_commitmsg():
419 answer
= qtutils
.question(self
.view
,
420 self
.tr('Import Commit Message?'),
421 self
.tr('A commit message from an in-progress'
422 + ' merge was found.\nImport it?'))
424 if not answer
: return
426 # Set the new commit message
427 self
.model
.set_commitmsg(self
.model
.get_squash_msg())
430 model
= self
.model
.clone()
431 view
= GitPushDialog(self
.view
)
432 controller
= GitPushController(model
,view
)
441 cursor
= self
.view
.commitText
.textCursor()
442 selection
= cursor
.selection().toPlainText()
443 qtutils
.set_clipboard(selection
)
445 def paste(self
): self
.view
.commitText
.paste()
446 def undo(self
): self
.view
.commitText
.undo()
447 def redo(self
): self
.view
.commitText
.redo()
448 def select_all(self
): self
.view
.commitText
.selectAll()
450 self
.view
.commitText
.textCursor().removeSelectedText()
452 def show_diffstat(self
):
453 '''Show the diffstat from the latest commit.'''
454 self
.__show
_command
(cmds
.git_diff_stat(), rescan
=False)
457 #####################################################################
460 def __diff_selection(self
):
461 cursor
= self
.view
.displayText
.textCursor()
462 offset
= cursor
.position()
463 selection
= cursor
.selection().toPlainText()
464 num_selected_lines
= selection
.count(os
.linesep
)
465 return offset
, selection
467 def process_diff_selection(self
,
473 filename
= qtutils
.get_selected_item(widget
, items
)
474 if not filename
: return
476 header
, diff
= cmds
.git_diff(filename
,
477 with_diff_header
=True,
481 parser
= utils
.DiffParser(header
,diff
)
483 header_fwd
, diff_fwd
= cmds
.git_diff(filename
,
484 with_diff_header
=True,
488 offset
, selection
= self
.__diff
_selection
()
490 start
= diff_fwd
.index(selection
)
491 end
= start
+ len(selection
)
492 parser
.set_diffs_to_range(start
, end
)
494 parser
.set_diff_to_offset(offset
)
497 if not parser
.diffs
: return
500 for diff
in parser
.selected
:
501 contents
= parser
.get_diff_subset(diff
, start
, end
)
503 tmpfile
= utils
.get_tmp_filename()
504 utils
.write(tmpfile
, contents
)
507 for idx
, diff
in enumerate(parser
.diffs
):
508 tmpfile
= utils
.get_tmp_filename()
509 if parser
.write_diff(tmpfile
,idx
):
510 self
.model
.apply_diff(tmpfile
)
514 def stage_hunk(self
):
515 self
.process_diff_selection(
516 self
.model
.get_unstaged(),
517 self
.view
.unstagedList
,
520 def stage_hunks(self
):
521 self
.process_diff_selection(
522 self
.model
.get_unstaged(),
523 self
.view
.unstagedList
,
527 def unstage_hunk(self
, cached
=True):
528 self
.process_diff_selection(
529 self
.model
.get_staged(),
530 self
.view
.stagedList
,
533 def unstage_hunks(self
):
534 self
.process_diff_selection(
535 self
.model
.get_staged(),
536 self
.view
.stagedList
,
540 # #######################################################################
543 def stage_changed(self
):
544 '''Stage all changed files for commit.'''
545 output
= cmds
.git_add(self
.model
.get_unstaged())
546 self
.__show
_command
(output
)
548 def stage_untracked(self
):
549 '''Stage all untracked files for commit.'''
550 output
= cmds
.git_add(self
.model
.get_untracked())
551 self
.__show
_command
(output
)
553 # use *rest to handle being called from different signals
554 def stage_selected(self
,*rest
):
555 '''Use "git add" to add items to the git index.
556 This is a thin wrapper around __apply_to_list.'''
557 command
= cmds
.git_add_or_remove
558 widget
= self
.view
.unstagedList
559 items
= self
.model
.get_all_unstaged()
560 self
.__show
_command
(self
.__apply
_to
_list
(command
,widget
,items
))
562 # use *rest to handle being called from different signals
563 def unstage_selected(self
, *rest
):
564 '''Use "git reset" to remove items from the git index.
565 This is a thin wrapper around __apply_to_list.'''
566 command
= cmds
.git_reset
567 widget
= self
.view
.stagedList
568 items
= self
.model
.get_staged()
569 self
.__show
_command
(self
.__apply
_to
_list
(command
, widget
, items
))
571 def unstage_all(self
):
572 '''Use "git reset" to remove all items from the git index.'''
573 output
= cmds
.git_reset(self
.model
.get_staged())
574 self
.__show
_command
(output
)
577 '''Visualizes the entire git history using gitk.'''
578 utils
.fork('gitk','--all')
580 def viz_current(self
):
581 '''Visualizes the current branch's history using gitk.'''
582 utils
.fork('gitk', cmds
.git_current_branch())
584 # These actions monitor window resizes, splitter changes, etc.
585 def move_event(self
, event
):
586 defaults
.X
= event
.pos().x()
587 defaults
.Y
= event
.pos().y()
589 def resize_event(self
, event
):
590 defaults
.WIDTH
= event
.size().width()
591 defaults
.HEIGHT
= event
.size().height()
593 def splitter_top_event(self
,*rest
):
594 sizes
= self
.view
.splitter_top
.sizes()
595 defaults
.SPLITTER_TOP_0
= sizes
[0]
596 defaults
.SPLITTER_TOP_1
= sizes
[1]
598 def splitter_bottom_event(self
,*rest
):
599 sizes
= self
.view
.splitter_bottom
.sizes()
600 defaults
.SPLITTER_BOTTOM_0
= sizes
[0]
601 defaults
.SPLITTER_BOTTOM_1
= sizes
[1]
603 #####################################################################
606 def __apply_to_list(self
, command
, widget
, items
):
607 '''This is a helper method that retrieves the current
608 selection list, applies a command to that list,
609 displays a dialog showing the output of that command,
610 and calls rescan to pickup changes.'''
611 apply_items
= qtutils
.get_selection_list(widget
, items
)
612 output
= command(apply_items
)
616 def __browse_branch(self
, branch
):
617 if not branch
: return
618 # Clone the model to allow opening multiple browsers
619 # with different sets of data
620 model
= self
.model
.clone()
621 model
.set_branch(branch
)
622 view
= GitCommitBrowser()
623 controller
= GitRepoBrowserController(model
, view
)
627 def __menu_about_to_show(self
):
629 unstaged_item
= qtutils
.get_selected_item(
630 self
.view
.unstagedList
,
631 self
.model
.get_all_unstaged())
633 is_tracked
= unstaged_item
not in self
.model
.get_untracked()
637 not self
.__staged
_diff
_in
_view
641 self
.__staged
_diff
_in
_view
642 and qtutils
.get_selected_item(
643 self
.view
.stagedList
,
644 self
.model
.get_staged()))
646 self
.__stage
_hunk
_action
.setEnabled(bool(enable_staged
))
647 self
.__stage
_hunks
_action
.setEnabled(bool(enable_staged
))
649 self
.__unstage
_hunk
_action
.setEnabled(bool(enable_unstaged
))
650 self
.__unstage
_hunks
_action
.setEnabled(bool(enable_unstaged
))
652 def __menu_event(self
, event
):
654 textedit
= self
.view
.displayText
655 self
.__menu
.exec_(textedit
.mapToGlobal(event
.pos()))
657 def __menu_setup(self
):
658 if self
.__menu
: return
660 menu
= self
.__menu
= QMenu(self
.view
)
661 self
.__stage
_hunk
_action
= menu
.addAction(
662 self
.tr('Stage Hunk For Commit'),
665 self
.__stage
_hunks
_action
= menu
.addAction(
666 self
.tr('Stage Selected Lines'),
669 self
.__unstage
_hunk
_action
= menu
.addAction(
670 self
.tr('Unstage Hunk From Commit'),
673 self
.__unstage
_hunks
_action
= menu
.addAction(
674 self
.tr('Unstage Selected Lines'),
677 self
.__copy
_action
= menu
.addAction(
681 self
.connect(self
.__menu
, 'aboutToShow()', self
.__menu
_about
_to
_show
)
683 def __file_to_widget_item(self
, filename
, staged
, untracked
=False):
684 '''Given a filename, return a QListWidgetItem suitable
685 for adding to a QListWidget. "staged" controls whether
686 to use icons for the staged or unstaged list widget.'''
689 icon_file
= utils
.get_staged_icon(filename
)
691 icon_file
= utils
.get_untracked_icon()
693 icon_file
= utils
.get_icon(filename
)
695 return qtutils
.create_listwidget_item(filename
, icon_file
)
697 def __read_config_settings(self
):
700 sb0
,sb1
) = utils
.parse_geom(cmds
.git_config('ugit.geometry'))
701 self
.view
.resize(w
,h
)
703 self
.view
.splitter_top
.setSizes([st0
,st1
])
704 self
.view
.splitter_bottom
.setSizes([sb0
,sb1
])
706 def __save_config_settings(self
):
707 cmds
.git_config('ugit.geometry', utils
.get_geom())
709 def __select_commits(self
, revs
, summaries
):
710 '''Use the GitCommitBrowser to select commits from a list.'''
712 msg
= self
.tr('ERROR: No commits exist in this branch.')
713 self
.__show
_command
(msg
)
716 browser
= GitCommitBrowser(self
.view
)
717 self
.connect(browser
.commitList
,
718 'itemSelectionChanged()',
719 lambda: self
.commit_sha1_selected(
722 qtutils
.set_items(browser
.commitList
, summaries
)
725 result
= browser
.exec_()
726 if result
!= QDialog
.Accepted
:
729 list_widget
= browser
.commitList
730 selection
= qtutils
.get_selection_list(list_widget
, revs
)
731 if not selection
: return([],[])
733 # also return the selected index numbers
734 index_nums
= range(len(revs
))
735 idxs
= qtutils
.get_selection_list(list_widget
, index_nums
)
737 return(selection
, idxs
)
739 def __set_branch_ui_items(self
):
740 '''Sets up items that mention the current branch name.'''
741 branch
= cmds
.git_current_branch()
743 status_text
= self
.tr('Current Branch:') + ' ' + branch
744 self
.view
.statusBar().showMessage(status_text
)
746 project
= self
.model
.get_project()
747 title
= '%s [%s]' % ( project
, branch
)
749 self
.view
.setWindowTitle(title
)
751 def __reset_display(self
):
752 self
.view
.displayText
.setText('')
755 def __set_info(self
,text
):
756 self
.view
.displayLabel
.setText(text
)
758 def __start_inotify_thread(self
):
759 # Do we have inotify? If not, return.
760 # Recommend installing inotify if we're on Linux.
761 self
.inotify_thread
= None
763 from inotify
import GitNotifier
766 if platform
.system() == 'Linux':
767 msg
=(self
.tr('Unable import pyinotify.\n'
768 + 'inotify support has been'
772 plat
= platform
.platform().lower()
773 if 'debian' in plat
or 'ubuntu' in plat
:
774 msg
+= (self
.tr('Hint:')
775 + 'sudo apt-get install'
776 + ' python-pyinotify')
778 qtutils
.information(self
.view
,
779 self
.tr('inotify disabled'), msg
)
782 self
.inotify_thread
= GitNotifier(os
.getcwd())
783 self
.connect(self
.inotify_thread
,
784 'timeForRescan()', self
.rescan
)
786 # Start the notification thread
787 self
.inotify_thread
.start()
789 def __show_command(self
, output
, rescan
=True):
790 '''Shows output and optionally rescans for changes.'''
791 qtutils
.show_command(self
.view
, output
)
792 if rescan
: self
.rescan()
794 def __update_listwidget(self
, widget
, items
,
795 staged
, untracked
=False, append
=False):
796 '''Populate a QListWidget with the custom icon items.'''
797 if not append
: widget
.clear()
798 qtutils
.add_items( widget
,
799 [ self
.__file
_to
_widget
_item
(i
, staged
, untracked
)