3 Module for searching items.
5 __copyright__
= "Copyright (c) 2002-2005 Free Software Foundation, Inc."
6 __license__
= """ GNU General Public License
8 Straw is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2 of the License, or (at your option) any later
13 Straw is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along with
18 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 Place - Suite 330, Boston, MA 02111-1307, USA. """
33 from ItemList
import ItemsView
, ItemsPresenter
43 class FindLimit(object):
44 __slots__
= ('text', 'start', 'end')
46 def __init__(self
, text
, start
= None, end
= None):
51 def is_subset(self
, fl
):
52 if self
.text
.find(fl
.text
) == -1:
54 if fl
.end
and (self
.end
or self
.end
> fl
.end
):
56 if fl
.start
and (self
.start
or self
.start
< fl
.start
):
61 return (self
.text
== fl
.text
and
62 self
.end
== fl
.end
and
63 self
.start
== fl
.start
)
65 def time_contains(self
, item
):
67 if item
.pub_date
: # pub_date hasn't been initialized in older versions of straw
68 if self
.start
and self
.start
> item
.pub_date
:
70 if self
.end
and self
.end
< item
.pub_date
:
77 self
._feedlist
= feeds
.get_instance()
79 def find_matching(self
, limit
, items
):
81 for feed
in self
._feedlist
:
83 for item
in feed
.items
:
84 if limit
.time_contains(item
) and item
.match(limit
.text
):
88 feed
.unload_contents()
89 while gtk
.events_pending(): gtk
.main_iteration(False)
93 error
.log("ITEMS IS NOT NONE.!",items
)
94 # we expect this to be quicker than the above since the items are
95 # already loaded in the memory.
97 if limit
.time_contains(item
) and item
.match(limit
.text
):
102 def find(self
, limit
):
103 if len(limit
.text
) < 1:
108 if limit
.is_subset(self
._stack
[-1][0]):
109 # the new search string is longer than the previous
110 if len(self
._stack
[-1][1]):
111 # return existing items
112 items
= self
._stack
[-1][1]
115 length
= len(self
._stack
)
119 if limit
.equals(self
._stack
[length
][0]):
122 if limit
.is_subset(self
._stack
[length
][0]):
124 self
._stack
= self
._stack
[:length
+1]
125 if foundprev
or len(self
._stack
):
126 items
= self
._stack
[-1][1]
127 return (limit
, items
)
129 def append_matches(self
, limit
, matches
):
131 self
._stack
.append(lm
)
134 class FindResultListView(ItemsView
):
137 Model: Result Liststore
138 Presenter: ItemListPresenter
140 def _initialize(self
):
141 ItemsView
._initialize
(self
)
142 self
._widget
.set_rules_hint(True)
143 self
._create
_columns
()
144 selection
= self
._widget
.get_selection()
145 selection
.connect("changed", self
._item
_selection
_changed
)
148 def _model_set(self
):
149 self
._widget
.set_model(self
._model
)
151 def _create_columns(self
):
152 renderer
= gtk
.CellRendererText()
153 column
= gtk
.TreeViewColumn('Title', renderer
, text
=Column
.TITLE
,
155 self
._widget
.append_column(column
)
156 renderer
= gtk
.CellRendererText()
157 column
= gtk
.TreeViewColumn('Feed', renderer
, text
=Column
.FEED_TITLE
,
159 self
._widget
.append_column(column
)
163 self
._widget
.get_model().clear()
165 class FindResultPresenter(ItemsPresenter
):
166 def _initialize(self
):
168 model
= gtk
.ListStore(gobject
.TYPE_STRING
, gobject
.TYPE_STRING
,
169 gobject
.TYPE_PYOBJECT
, gobject
.TYPE_INT
)
171 self
._finder
= Finder()
172 self
._interrupted
= False
175 def _mark_item(self
, item
, index
=None):
177 index
= self
._matches
.index(item
)
178 self
._set
_column
_weight
(item
, index
)
180 def find(self
, limit
):
181 self
._clear
_matches
()
183 limit_items
= self
._finder
.find(limit
)
184 if not len(limit_items
):
186 limit
, items
= limit_items
187 for item
in self
._finder
.find_matching(limit
, items
):
188 if self
._interrupted
:
189 self
._interrupted
= True
191 treeiter
= self
._model
.append()
192 weight
= pango
.WEIGHT_NORMAL
194 weight
= pango
.WEIGHT_BOLD
195 self
._matches
.append(item
)
196 self
._model
.set(treeiter
, Column
.TITLE
, item
.title
,
197 Column
.FEED_TITLE
, item
.feed
.title
,
201 error
.log("MATCHES: %d %s %s" %(len(self
._matches
),limit
.text
,items
))
202 self
._finder
.append_matches(limit
, self
._matches
)
205 def stop(self
, signal
):
206 error
.log("stopping...")
207 self
._interrupted
= True
211 self
._interrupted
= False
213 self
._clear
_matches
()
216 def _clear_matches(self
):
217 for feed
in dict.fromkeys((item
.feed
for item
in self
._matches
)).keys():
218 feed
.unload_contents()
221 class FindView(MVP
.WidgetView
):
224 def _initialize(self
):
225 self
._widget
_tree
= gtk
.glade
.get_widget_tree(self
._widget
)
226 self
._text
_entry
= self
._widget
_tree
.get_widget("find_text_entry")
228 def _on_find_text_entry_insert_text(self
, *args
):
229 gobject
.timeout_add(700, self
._after
_change
)
232 def _on_find_text_entry_delete_text(self
, *args
):
233 gobject
.timeout_add(700, self
._after
_change
)
236 def _on_find_start_time_limit_check_toggled(self
, button
, *args
):
237 state
= button
.get_active()
238 start_date_widget
= self
._widget
_tree
.get_widget('find_start_date_edit')
239 start_date_widget
.set_sensitive(state
)
240 stime
= self
._determine
_time
(state
, start_date_widget
.get_time())
241 self
._presenter
.start_date(stime
)
244 def _on_find_end_time_limit_check_toggled(self
, button
, *args
):
245 state
= button
.get_active()
246 end_date_widget
= self
._widget
_tree
.get_widget('find_end_date_edit')
247 end_date_widget
.set_sensitive(state
)
248 etime
= self
._determine
_time
(state
, end_date_widget
.get_time() + self
.SECS_DAY
)
249 self
._presenter
.end_date(etime
)
252 def _on_find_start_date_edit_date_changed(self
, widget
):
254 Precondition: start_date_toggle is toggled (ON) or else this signal
255 won't be triggered in the first place
257 stime
= self
._determine
_time
(True, widget
.get_time())
258 self
._presenter
.start_date(stime
)
261 def _on_find_end_date_edit_date_changed(self
, widget
):
263 Precondition: end_date_toggle is toggled (ON) or else this signal
264 won't be triggered in the first place
266 etime
= self
._determine
_time
(True, widget
.get_time() + self
.SECS_DAY
)
267 self
._presenter
.end_date(etime
)
270 def _after_change(self
):
271 newtext
= self
._text
_entry
.get_text()
272 self
._presenter
.text_changed(newtext
)
275 def _determine_time(self
, togglestate
, dtime
):
278 xtime
= time
.gmtime(dtime
)
281 def _get_text(self
): return self
._text
282 text
= property(_get_text
)
284 def get_widget(self
):
288 self
._text
_entry
.delete_text(0,-1)
291 class FindPresenter(MVP
.BasicPresenter
):
292 def _initialize(self
):
293 self
.initialize_slots(Event
.FindInterruptSignal
)
294 widget_tree
= gtk
.glade
.get_widget_tree(self
._view
.get_widget())
295 self
._find
_result
_pres
= FindResultPresenter(view
=
296 FindResultListView(widget_tree
.get_widget("find_results_treeview")))
297 self
.signal_connect(Event
.FindInterruptSignal
,
298 self
._find
_result
_pres
.stop
)
299 self
._resultscount
= widget_tree
.get_widget("find_results_count_display")
300 self
._rendering
= False
302 self
._start
_date
= None
303 self
._end
_date
= None
305 def start_date(self
, stime
):
306 self
._start
_date
= stime
307 if stime
and stime
is not self
._start
_date
:
308 limit
= FindLimit(self
._text
,
311 self
._find
_items
(limit
)
314 def end_date(self
, etime
):
315 self
._end
_date
= etime
316 if etime
and etime
is not self
._end
_date
:
317 limit
= FindLimit(self
._text
,
320 self
._find
_items
(limit
)
323 def text_changed(self
, newtext
):
324 # terminate an existing result rendering in progress
326 self
.emit_signal(Event
.FindInterruptSignal(self
))
327 changed
= newtext
is not self
._text
330 self
._rendering
= False
331 if changed
and not self
._rendering
:
332 self
._rendering
= True
333 limit
= FindLimit(self
._text
,
336 self
._find
_items
(limit
)
337 self
._rendering
= False
340 def _find_items(self
, limit
):
341 matches
= self
._find
_result
_pres
.find(limit
)
342 nmatches
= len(matches
)
343 if nmatches
<= 0: self
._find
_result
_pres
.clear()
344 self
._resultscount
.set_text(_("%d items found") % nmatches
or 0)
348 self
._find
_result
_pres
.clear()
351 def _find_result_presenter(self
): return self
._find
_result
_pres
352 item_list
= property(_find_result_presenter
)