1 # Copyright (C) 2006 by Aiwota Programmer
2 # aiwotaprog@tetteke.tk
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 from threadlistmodel import ThreadListModel
32 from BbsType import bbs_type_judge_uri
36 from misc import ThreadInvoker
38 import bookmark_window
40 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
42 GLADE_FILENAME = "board_window.glade"
44 def open_board(uri, update=False):
46 raise ValueError, "parameter must not be empty"
48 winwrap = session.get_window(uri)
51 winwrap.window.present()
55 winwrap = WinWrap(uri)
59 class WinWrap(winwrapbase.WinWrapBase, board_data.BoardData):
61 def __init__(self, uri):
63 self.bbs_type = bbs_type_judge_uri.get_type(uri)
64 board_data.BoardData.__init__(self, self.bbs_type)
66 glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
67 self.widget_tree = gtk.glade.XML(glade_path)
69 self.window = self.widget_tree.get_widget("board_window")
71 self.window.set_title(self.bbs_type.uri)
73 self.treeview = self.widget_tree.get_widget("treeview")
74 self.treeview.set_model(ThreadListModel())
76 self.popupmenu = self.widget_tree.get_widget("popup_menu")
77 self.toolbar = self.widget_tree.get_widget("toolbar")
78 self.toolbar.unset_style()
79 self.statusbar = self.widget_tree.get_widget("appbar")
80 self.filterbar = self.widget_tree.get_widget("bonobodockitem_filterbar")
81 self.entry_filterbar = self.widget_tree.get_widget("entry_filterbar")
83 renderer = gtk.CellRendererText()
85 self.treeviewcolumn = {}
86 for i in range(1, len(ThreadListModel.column_names)):
87 column_name = ThreadListModel.column_names[i]
88 self.treeviewcolumn[column_name] = gtk.TreeViewColumn(
89 column_name, renderer)
90 self.treeviewcolumn[column_name].set_resizable(True)
91 self.treeviewcolumn[column_name].set_reorderable(True)
92 self.treeviewcolumn[column_name].set_clickable(True)
93 self.treeviewcolumn[column_name].set_cell_data_func(
94 renderer, self.on_cell_data, column_name)
95 self.treeviewcolumn[column_name].connect(
96 "clicked", self.on_column_clicked, column_name)
97 self.treeviewcolumn[column_name].set_sizing(
98 gtk.TREE_VIEW_COLUMN_FIXED)
99 self.treeviewcolumn[column_name].set_min_width(20)
100 self.treeviewcolumn[column_name].set_fixed_width(
101 ThreadListModel.column_width[i])
102 self.treeview.append_column(self.treeviewcolumn[column_name])
104 self.treeviewcolumn["lastModified"].set_cell_data_func(
105 renderer, self.on_data_lastmodified)
107 self.treeview.set_fixed_height_mode(True)
109 sigdic = {"on_board_window_destroy": self.on_board_window_destroy,
110 "on_quit_activate": self.on_quit_activate,
111 "on_refresh_activate": self.on_refresh_activate,
112 "on_treeview_row_activated":
113 lambda w,p,v: self.on_open_thread(w),
114 "on_treeview_button_press_event":
115 self.on_treeview_button_press_event,
117 self.on_close_activate,
118 "on_toolbar_activate": self.on_toolbar_activate,
119 "on_statusbar_activate": self.on_statusbar_activate,
120 "on_board_window_delete_event":
121 self.on_board_window_delete_event,
122 "on_entry_filterbar_activate": self.on_entry_filterbar_activate,
123 "on_filter_activate": self.on_filter_activate,
124 "on_toolbutton_filterbar_close_clicked":
125 self.on_toolbutton_filterbar_close_clicked,
126 "on_button_filterbar_clear_clicked":
127 self.on_button_filterbar_clear_clicked,
128 "on_delete_activate": self.on_delete_activate,
129 "on_manage_bookmarks_activate": \
130 self.on_manage_bookmarks_activate,
131 "on_add_bookmark_activate": self.on_add_bookmark_activate,
132 "on_popup_menu_open_activate": self.on_open_thread}
133 self.widget_tree.signal_autoconnect(sigdic)
140 def set_status(self, text):
141 self.statusbar.set_status(text)
145 self.window.destroy()
148 return self.bbs_type.get_uri_base()
150 def on_toolbar_activate(self, widget):
151 if self.toolbar.parent.get_property("visible"):
152 self.toolbar.parent.hide()
154 self.toolbar.parent.show()
156 def on_statusbar_activate(self, widget):
157 if self.statusbar.get_property("visible"):
158 self.statusbar.hide()
160 self.statusbar.show()
162 def on_filter_activate(self, widget):
163 self.filterbar.show()
164 self.entry_filterbar.grab_focus()
166 def on_entry_filterbar_activate(self, widget):
167 text = widget.get_text()
169 def func(model, item):
171 item["title"].index(text)
177 model = self.treeview.get_model()
180 model.set_filter_func(func)
182 model.set_filter_func(None)
185 def on_toolbutton_filterbar_close_clicked(self, widget):
186 self.filterbar.hide()
188 def on_button_filterbar_clear_clicked(self, widget):
189 self.entry_filterbar.set_text("")
190 model = self.treeview.get_model()
192 model.set_filter_func(None)
195 def updated_thread_highlight(self, column, cell, model, iter):
197 def is_updated_thread():
198 res = model.get_value(
199 iter, ThreadListModel.column_names.index("res"))
200 linecount = model.get_value(
201 iter, ThreadListModel.column_names.index("lineCount"))
202 return res != 0 and linecount != 0 and res > linecount
204 if is_updated_thread():
205 cell.set_property("weight", 800)
207 cell.set_property("weight", 400)
209 def on_cell_data(self, column, cell, model, iter, column_name):
210 self.updated_thread_highlight(column, cell, model, iter)
211 column_num = ThreadListModel.column_names.index(column_name)
212 value = model.get_value(iter, column_num)
213 if model.get_column_type(column_num) \
214 in (gobject.TYPE_INT, gobject.TYPE_DOUBLE):
216 cell.set_property("text", "")
218 cell.set_property("text", str(value))
220 cell.set_property("text", value)
222 def on_data_lastmodified(self, column, cell, model, iter, user_data=None):
223 self.updated_thread_highlight(column, cell, model, iter)
224 lastmod = model.get_value(
225 iter, ThreadListModel.column_names.index("lastModified"))
227 cell.set_property("text", "")
229 cell.set_property("text", time.strftime(
230 "%Y/%m/%d(%a) %H:%M:%S", time.localtime(lastmod)))
232 def on_board_window_delete_event(self, widget, event):
236 def on_board_window_destroy(self, widget):
239 def on_quit_activate(self, widget):
242 def on_close_activate(self, widget):
245 def on_refresh_activate(self, widget):
248 def on_column_clicked(self, treeviewcolumn, column_name):
249 model = self.treeview.get_model()
251 model.sort(column_name)
252 self.reset_sort_indicator()
254 def reset_sort_indicator(self):
255 model = self.treeview.get_model()
257 sort_column_name, sort_reverse = model.get_sort()
258 for name,column in self.treeviewcolumn.iteritems():
259 column.set_sort_indicator(False)
260 if sort_column_name != "num" or sort_reverse:
261 self.treeviewcolumn[sort_column_name].set_sort_indicator(True)
263 self.treeviewcolumn[sort_column_name].set_sort_order(
266 self.treeviewcolumn[sort_column_name].set_sort_order(
269 def on_open_thread(self, widget):
270 treeselection = self.treeview.get_selection()
271 model, iter = treeselection.get_selected()
275 thread = model.get_value(iter, ThreadListModel.column_names.index("id"))
276 title = model.get_value(
277 iter, ThreadListModel.column_names.index("title"))
278 print thread + ':"' + title + '"', "activated"
280 res = model.get_value(iter, ThreadListModel.column_names.index("res"))
281 lineCount = model.get_value(
282 iter, ThreadListModel.column_names.index("lineCount"))
284 update = res > lineCount
286 bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
287 uri_opener.open_uri(bbs_type_for_thread.get_thread_uri(), update)
289 def on_add_bookmark_activate(self, widget):
290 bookmark_list.bookmark_list.add_bookmark_with_edit(
291 uri=self.bbs_type.uri)
293 def on_manage_bookmarks_activate(self, widget):
294 bookmark_window.open()
296 def on_delete_activate(self, widget):
297 selection = self.treeview.get_selection()
298 model, iter = selection.get_selected()
301 thread = model.get_value(
302 iter, ThreadListModel.column_names.index("id"))
304 bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
306 dat_path = misc.get_thread_dat_path(bbs_type_for_thread)
310 traceback.print_exc()
311 idx_path = misc.get_thread_idx_path(bbs_type_for_thread)
315 traceback.print_exc()
316 states_path = misc.get_thread_states_path(bbs_type_for_thread)
318 os.remove(states_path)
320 traceback.print_exc()
322 def on_treeview_button_press_event(self, widget, event):
323 if event.button == 3:
327 pthinfo = widget.get_path_at_pos(x, y)
328 if pthinfo is not None:
329 path, col, cellx, celly = pthinfo
331 widget.set_cursor(path, col, 0)
332 self.popupmenu.popup(None, None, None, event.button, time)
335 def update_datastore(self, datalist):
336 print "reflesh datastore"
339 for id, dic in datalist.iteritems():
343 httpdate = dic["lastModified"]
345 secs = misc.httpdate_to_secs(httpdate)
346 dic["lastModified"] = secs
348 dic["lastModified"] = 0
350 list_list.append(dic)
352 model = self.treeview.get_model()
353 model.set_list(list_list)
355 # redraw visible area after set list to model
356 self.treeview.queue_draw()
358 self.reset_sort_indicator()
362 def on_thread_idx_updated(self, thread_uri, idx_dic):
363 if not thread_uri or not idx_dic:
366 # nothing to do if thread_uri does not belong to this board.
367 bbs_type = bbs_type_judge_uri.get_type(thread_uri)
368 if not bbs_type.is_thread() \
369 or not bbs_type.is_same_board(self.bbs_type):
372 thread = bbs_type.thread
374 model = self.treeview.get_model()
376 idx_dic["id"] = thread
378 idx_dic["lastModified"] = misc.httpdate_to_secs(
379 idx_dic["lastModified"])
381 idx_dic["lastModified"] = 0
382 model.modify_row(idx_dic)
384 def load(self, update=False):
387 datalist = self.load_idxfiles()
388 self.merge_local_subjecttxt(datalist)
389 gobject.idle_add(self.update_datastore, datalist)
392 print "start get subject.txt"
393 datalist = self.load_idxfiles()
394 self.merge_remote_subjecttxt(datalist)
395 gobject.idle_add(self.update_datastore, datalist)
397 sbj_path = misc.get_board_subjecttxt_path(self.bbs_type)
398 sbj_exists = os.path.exists(sbj_path)
400 if update or not sbj_exists:
401 t = ThreadInvoker(lambda *args: -1, get_remote)
404 t = ThreadInvoker(lambda *args: -1, load_local)
409 states_path = misc.get_board_states_path(self.bbs_type)
410 dirname = os.path.dirname(states_path)
412 # save only if board dir exists.
413 if os.path.exists(dirname):
414 window_width, window_height = self.window.get_size()
415 toolbar_visible = self.toolbar.parent.get_property("visible")
416 statusbar_visible = self.statusbar.get_property("visible")
417 filterbar_visible = self.filterbar.get_property("visible")
419 columns = self.treeview.get_columns()
422 for column in columns:
423 for name, kolumn in self.treeviewcolumn.iteritems():
430 width += str(column.get_width())
432 sort_column_name, sort_reverse = \
433 self.treeview.get_model().get_sort()
434 sort_reverse = str(sort_reverse)
436 f = file(states_path, "w")
438 f.write("columns=" + order + "\n")
439 f.write("widths=" + width + "\n")
440 f.write("sort_column=" + sort_column_name + "\n")
441 f.write("sort_reverse=" + sort_reverse + "\n")
442 f.write("window_width=" + str(window_width) + "\n")
443 f.write("window_height=" + str(window_height) + "\n")
444 f.write("toolbar_visible=" + str(toolbar_visible) + "\n")
445 f.write("statusbar_visible=" + str(statusbar_visible) + "\n")
446 f.write("filterbar_visible=" + str(filterbar_visible) + "\n")
450 traceback.print_exc()
456 toolbar_visible = True
457 statusbar_visible = True
458 filterbar_visible = False
461 key_base = config.gconf_app_key_base() + "/board_states"
462 gconf_client = gconf.client_get_default()
463 width = gconf_client.get_int(key_base + "/window_width")
464 height = gconf_client.get_int(key_base + "/window_height")
465 toolbar_visible = gconf_client.get_bool(key_base + "/toolbar")
466 statusbar_visible = gconf_client.get_bool(key_base + "/statusbar")
467 filterbar_visible = gconf_client.get_bool(key_base + "/filterbar")
472 window_height = height
474 traceback.print_exc()
476 states_path = misc.get_board_states_path(self.bbs_type)
477 if os.path.exists(states_path):
478 sort_column_name = "num"
480 for line in file(states_path):
481 if line.startswith("columns="):
482 line = line[len("columns="):].rstrip("\n")
484 for name in line.split(","):
485 if name in self.treeviewcolumn:
486 column = self.treeviewcolumn[name]
487 self.treeview.move_column_after(
490 elif line.startswith("widths="):
491 line = line[len("widths="):].rstrip("\n")
492 columns = self.treeview.get_columns()
493 for i, width in enumerate(line.split(",")):
500 columns[i].set_fixed_width(width)
501 elif line.startswith("sort_column="):
502 kolumn_name = line[len("sort_column="):].rstrip("\n")
503 if kolumn_name in ThreadListModel.column_names:
504 sort_column_name = kolumn_name
505 elif line.startswith("sort_reverse="):
506 reverse = line[len("sort_reverse="):].rstrip("\n")
507 sort_reverse = reverse == "True"
508 elif line.startswith("window_height="):
509 height = window_height
512 line[len("window_height="):].rstrip("\n"))
516 window_height = height
517 elif line.startswith("window_width="):
521 line[len("window_width="):].rstrip("\n"))
526 elif line.startswith("toolbar_visible="):
527 tbar = line[len("toolbar_visible="):].rstrip("\n")
528 toolbar_visible = tbar == "True"
529 elif line.startswith("statusbar_visible="):
530 sbar = line[len("statusbar_visible="):].rstrip("\n")
531 statusbar_visible = sbar == "True"
532 elif line.startswith("filterbar_visible="):
533 fbar = line[len("filterbar_visible="):].rstrip("\n")
534 filterbar_visible = fbar == "True"
536 self.treeview.get_model().sort(
537 sort_column_name, True, sort_reverse)
539 self.window.set_default_size(window_width, window_height)
541 if not toolbar_visible:
542 gobject.idle_add(self.toolbar.parent.hide,
543 priority=gobject.PRIORITY_HIGH)
544 if not statusbar_visible:
545 gobject.idle_add(self.statusbar.hide,
546 priority=gobject.PRIORITY_HIGH)
547 if not filterbar_visible:
548 gobject.idle_add(self.filterbar.hide,
549 priority=gobject.PRIORITY_HIGH)
551 traceback.print_exc()