1 """This controller handles the repository file browser."""
6 from PyQt4
import QtGui
9 from cola
import gitcmd
10 from cola
import utils
11 from cola
import resources
12 from cola
import qtutils
13 from cola
.models
import browser
14 from cola
.views
.selectcommits
import SelectCommitsView
15 from cola
.qobserver
import QObserver
17 git
= gitcmd
.instance()
20 def select_file_from_repo():
21 """Launche a dialog to selecting a filename from a branch."""
22 model
= cola
.model().clone()
23 parent
= QtGui
.QApplication
.instance().activeWindow()
24 view
= SelectCommitsView(parent
, syntax
=False)
25 controller
= RepoBrowserController(model
, view
,
29 if view
.exec_() == QtGui
.QDialog
.Accepted
:
30 return controller
.filename
34 def browse_git_branch(branch
):
35 """Launch a dialog to browse files in a specific branch."""
38 # Clone the model to allow opening multiple browsers
39 # with different sets of data
40 model
= browser
.BrowserModel(branch
)
41 parent
= QtGui
.QApplication
.instance().activeWindow()
42 view
= SelectCommitsView(parent
, syntax
=False)
43 controller
= RepoBrowserController(model
, view
)
45 return view
.exec_() == QtGui
.QDialog
.Accepted
47 class RepoBrowserController(QObserver
):
48 """Provides control to the Repository Browser."""
49 def __init__(self
, model
, view
,
50 title
='File Browser', get_file
=False):
51 QObserver
.__init
__(self
, model
, view
)
53 self
.get_file
= get_file
54 """Whether we should returns a selected file"""
57 """The last selected filename"""
59 view
.setWindowTitle(title
)
60 self
.add_signals('itemSelectionChanged()', view
.commit_list
,)
61 self
.add_actions(directory
= self
.action_directory_changed
)
62 self
.add_callbacks(commit_list
= self
.item_changed
)
63 view
.commit_list
.contextMenuEvent
= self
.context_menu_event
64 # Start at the root of the tree
65 model
.set_directory('')
68 def context_menu_event(self
, event
):
69 """Generate a context menu for the repository browser."""
70 menu
= QtGui
.QMenu(self
.view
);
71 menu
.addAction(self
.tr('Blame'), self
.blame
)
72 menu
.exec_(self
.view
.commit_list
.mapToGlobal(event
.pos()))
75 """Show git-blame output for a file path."""
76 current
= self
.view
.commit_list
.currentRow()
77 item
= self
.view
.commit_list
.item(current
)
78 if item
is None or not item
.isSelected():
80 directories
= self
.model
.directories
81 directory_entries
= self
.model
.directory_entries
82 if current
< len(directories
):
85 idx
= current
- len(directories
)
86 if idx
>= len(self
.model
.subtree_sha1s
):
88 objtype
, sha1
, name
= self
.model
.subtree_node(idx
)
89 curdir
= self
.model
.directory
91 filename
= os
.path
.join(curdir
, name
)
94 blame
= git
.blame(self
.model
.currentbranch
, filename
)
95 self
.view
.commit_text
.setText(blame
)
97 ######################################################################
99 def action_directory_changed(self
):
100 """Called in response to a change in the model's directory."""
101 self
.model
.init_browser_data()
102 self
._display
_items
()
104 ######################################################################
106 def item_changed(self
,*rest
):
107 """Called when the current item changes"""
108 current
= self
.view
.commit_list
.currentRow()
109 item
= self
.view
.commit_list
.item(current
)
110 if item
is None or not item
.isSelected():
111 self
.view
.revision
.setText('')
112 self
.view
.commit_text
.setText('')
114 directories
= self
.model
.directories
115 directory_entries
= self
.model
.directory_entries
116 if current
< len(directories
):
117 # This is a directory...
119 dirent
= directories
[current
]
121 # This is a real directory for which
122 # we have child entries
123 entries
= directory_entries
[dirent
]
125 # This is '..' which is a special case
126 # since it doesn't really exist
128 self
.view
.commit_text
.setText('\n'.join(entries
))
129 self
.view
.revision
.setText('')
131 # This is a file entry. The current row is absolute,
132 # so get a relative index by subtracting the number
133 # of directory entries
134 idx
= current
- len(directories
)
135 if idx
>= len(self
.model
.subtree_sha1s
):
136 # This can happen when changing directories
139 objtype
, sha1
, name
= self
.model
.subtree_node(idx
)
141 curdir
= self
.model
.directory
143 self
.filename
= os
.path
.join(curdir
, name
)
147 catguts
= git
.cat_file(objtype
, sha1
, with_raw_output
=True)
148 self
.view
.commit_text
.setText(catguts
)
150 self
.view
.revision
.setText(sha1
)
151 self
.view
.revision
.selectAll()
153 # Copy the sha1 into the clipboard
154 qtutils
.set_clipboard(sha1
)
156 # automatically called by qobserver
157 def commit_list_doubleclick(self
,*rest
):
159 Called when an entry is double-clicked.
161 This callback changes the model's directory when
162 invoked on a directory item. When invoked on a file
163 it allows the file to be saved.
166 current
= self
.view
.commit_list
.currentRow()
167 directories
= self
.model
.directories
169 # A file item was double-clicked.
170 # Create a save-as dialog and export the file,
171 # or if in get_file mode, grab the filename and finish the dialog.
172 if current
>= len(directories
):
173 idx
= current
- len(directories
)
175 objtype
, sha1
, name
= self
.model
.subtree_node(idx
)
178 if self
.model
.directory
:
179 curdir
= self
.model
.directory
180 self
.filename
= os
.path
.join(curdir
, name
)
186 nameguess
= os
.path
.join(self
.model
.directory
, name
)
187 filename
= qtutils
.save_dialog(self
.view
, 'Save', nameguess
)
190 self
.model
.set_directory(os
.path
.dirname(filename
))
191 contents
= git
.cat_file(objtype
, sha1
, with_raw_output
=True)
192 utils
.write(filename
, contents
)
195 dirent
= directories
[current
]
196 curdir
= self
.model
.directory
198 # "change directories"
199 # '..' is a special case--it doesn't really exist...
201 newdir
= os
.path
.dirname(os
.path
.dirname(curdir
))
203 self
.model
.set_directory(newdir
)
205 self
.model
.set_directory(newdir
+ os
.sep
)
207 self
.model
.set_directory(curdir
+ dirent
)
209 def _display_items(self
):
211 Populate the commit_list with the current directories and items
213 Directories are always listed first.
217 # Clear commit/revision fields
218 self
.view
.commit_text
.setText('')
219 self
.view
.revision
.setText('')
221 dir_icon
= resources
.icon('dir.png')
222 file_icon
= resources
.icon('generic.png')
223 # Factory method for creating items
224 creator
= qtutils
.create_listwidget_item
226 # First the directories,
227 qtutils
.set_items(self
.view
.commit_list
,
228 map(lambda d
: creator(d
, dir_icon
),
229 self
.model
.directories
))
230 # and now the filenames
231 qtutils
.add_items(self
.view
.commit_list
,
232 map(lambda s
: creator(s
, file_icon
),
233 self
.model
.subtree_names
))