1 """This controller handles the search dialog."""
7 from PyQt4
import QtGui
10 from cola
import gitcmds
11 from cola
import qtutils
12 from cola
.qobserver
import QObserver
13 from cola
.models
.search
import SearchModel
14 from cola
.views
.search
import SearchView
16 # Modes for this controller.
17 # Note: names correspond to radio button names for convenience
18 REVISION_ID
= 'radio_revision'
19 REVISION_RANGE
= 'radio_range'
21 MESSAGE
= 'radio_message'
23 AUTHOR
= 'radio_author'
24 COMMITTER
= 'radio_committer'
25 DATE_RANGE
= 'radio_daterange'
28 def search(searchtype
, browse
=False):
29 """Return a callback to handle various search actions."""
31 search_commits(cola
.model(),
32 QtGui
.QApplication
.instance().activeWindow(),
38 class SearchEngine(object):
39 def __init__(self
, model
):
43 max_count
= self
.model
.max_results
46 'max-count': max_count
,
47 'pretty': 'format:%H %aN - %s - %ar',
50 def common_args(self
):
51 return (self
.model
.query
, self
.rev_args())
54 if not self
.validate():
59 return len(self
.model
.query
) > 1
61 def revisions(self
, *args
, **kwargs
):
62 revlist
= self
.model
.git
.log(*args
, **kwargs
)
63 return gitcmds
.parse_rev_list(revlist
)
68 class RevisionSearch(SearchEngine
):
70 query
, args
= self
.common_args()
71 expr
= re
.compile(query
)
72 revs
= self
.revisions(all
=True, **args
)
73 return [ r
for r
in revs
if expr
.match(r
[0]) ]
75 class RevisionRangeSearch(SearchEngine
):
76 def __init__(self
, model
):
77 SearchEngine
.__init
__(self
, model
)
78 self
.RE
= re
.compile(r
'[^.]*\.\..*')
80 return bool(self
.RE
.match(self
.model
.query
))
82 query
, kwargs
= self
.common_args()
83 return self
.revisions(query
, **kwargs
)
85 class PathSearch(SearchEngine
):
87 query
, args
= self
.common_args()
88 paths
= ['--'] + query
.split(':')
89 return self
.revisions(all
=True, *paths
, **args
)
91 class MessageSearch(SearchEngine
):
93 query
, kwargs
= self
.common_args()
94 return self
.revisions(all
=True, grep
=query
, **kwargs
)
96 class AuthorSearch(SearchEngine
):
98 query
, kwargs
= self
.common_args()
99 return self
.revisions(all
=True, author
=query
, **kwargs
)
101 class CommitterSearch(SearchEngine
):
103 query
, kwargs
= self
.common_args()
104 return self
.revisions(all
=True, committer
=query
, **kwargs
)
106 class DiffSearch(SearchEngine
):
108 query
, kwargs
= self
.common_args()
109 return gitcmds
.parse_rev_list(
110 self
.model
.git
.log('-S'+query
, all
=True, **kwargs
))
112 class DateRangeSearch(SearchEngine
):
116 kwargs
= self
.rev_args()
117 start_date
= self
.model
.start_date
118 end_date
= self
.model
.end_date
119 return self
.revisions(date
='iso',
125 # Each search type is handled by a distinct SearchEngine subclass
127 REVISION_ID
: RevisionSearch
,
128 REVISION_RANGE
: RevisionRangeSearch
,
130 MESSAGE
: MessageSearch
,
132 AUTHOR
: AuthorSearch
,
133 COMMITTER
: CommitterSearch
,
134 DATE_RANGE
: DateRangeSearch
,
137 class SearchController(QObserver
):
138 def __init__(self
, model
, view
):
139 QObserver
.__init
__(self
, model
, view
)
140 self
.add_observables('query',
144 self
.add_actions(max_results
= self
.search_callback
,
145 start_date
= self
.search_callback
,
146 end_date
= self
.search_callback
)
149 button_search
= self
.search_callback
,
150 button_browse
= self
.browse_callback
,
151 commit_list
= self
.display_callback
,
152 button_export
= self
.export_patch
,
153 button_cherrypick
= self
.cherry_pick
,
155 # Radio buttons trigger a search
156 radio_revision
= self
.search_callback
,
157 radio_range
= self
.search_callback
,
158 radio_message
= self
.search_callback
,
159 radio_path
= self
.search_callback
,
160 radio_diff
= self
.search_callback
,
161 radio_author
= self
.search_callback
,
162 radio_committer
= self
.search_callback
,
163 radio_daterange
= self
.search_callback
,
167 def update_fonts(self
):
168 font
= self
.model
.cola_config('fontdiff')
170 qfont
= QtGui
.QFont()
171 qfont
.fromString(font
)
172 self
.view
.commit_text
.setFont(qfont
)
174 def set_mode(self
, mode
):
175 radio
= getattr(self
.view
, mode
)
176 radio
.setChecked(True)
178 def radio_to_mode(self
, radio_button
):
179 return str(radio_button
.objectName())
182 for name
in SEARCH_ENGINES
:
183 radiobutton
= getattr(self
.view
, name
)
184 if radiobutton
.isChecked():
187 def search_callback(self
, *args
):
188 engineclass
= SEARCH_ENGINES
.get(self
.mode())
190 print "mode: '%s' is currently unimplemented" % self
.mode()
192 self
.results
= engineclass(self
.model
).search()
194 self
.display_results()
196 self
.view
.commit_list
.clear()
197 self
.view
.commit_text
.setText('')
199 def browse_callback(self
):
200 paths
= QtGui
.QFileDialog
.getOpenFileNames(self
.view
,
201 self
.tr("Choose Path(s)"))
205 lenprefix
= len(os
.getcwd()) + 1
206 for path
in map(lambda x
: unicode(x
), paths
):
207 if not path
.startswith(os
.getcwd()):
209 filepaths
.append(path
[lenprefix
:])
210 query
= ':'.join(filepaths
)
211 self
.model
.set_query('')
213 self
.model
.set_query(query
)
215 def display_results(self
):
216 commit_list
= map(lambda x
: x
[1], self
.results
)
217 self
.model
.set_commit_list(commit_list
)
218 qtutils
.set_listwidget_strings(self
.view
.commit_list
, commit_list
)
220 def display_callback(self
, *args
):
221 widget
= self
.view
.commit_list
222 row
, selected
= qtutils
.selected_row(widget
)
223 if not selected
or len(self
.results
) < row
:
225 revision
= self
.results
[row
][0]
226 qtutils
.set_clipboard(revision
)
227 diff
= gitcmds
.commit_diff(revision
)
228 self
.view
.commit_text
.setText(diff
)
230 def export_patch(self
):
231 widget
= self
.view
.commit_list
232 row
, selected
= qtutils
.selected_row(widget
)
233 if not selected
or len(self
.results
) < row
:
235 revision
= self
.results
[row
][0]
236 qtutils
.log(*self
.model
.export_patchset(revision
, revision
))
238 def cherry_pick(self
):
239 widget
= self
.view
.commit_list
240 row
, selected
= qtutils
.selected_row(widget
)
241 if not selected
or len(self
.results
) < row
:
243 revision
= self
.results
[row
][0]
244 qtutils
.log(*self
.model
.git
.cherry_pick(revision
,
248 def search_commits(model
, parent
, mode
, browse
):
250 return '%04d-%02d-%02d' % time
.localtime(timespec
)[:3]
252 # TODO subclass model for search only
253 model
= SearchModel(cwd
=model
.git
.worktree())
254 view
= SearchView(parent
)
255 ctl
= SearchController(model
, view
)
257 model
.set_start_date(date(time
.time()-(87640*7)))
258 model
.set_end_date(date(time
.time()+87640))
261 ctl
.browse_callback()