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
30 from threadlistmodel import ThreadListModel
31 from BbsType import bbs_type_judge_uri
35 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
37 GLADE_FILENAME = "board_window.glade"
39 def open_board(uri, update=False):
41 raise ValueError, "parameter must not be empty"
43 winwrap = session.get_window(uri)
46 winwrap.window.present()
50 winwrap = WinWrap(uri)
51 session.window_created(uri, winwrap)
57 def __init__(self, uri):
59 self.bbs_type = bbs_type_judge_uri.get_type(uri)
60 self.bbs = self.bbs_type.bbs_type
61 self.host = self.bbs_type.host
62 self.board = self.bbs_type.board
63 self.uri = self.bbs_type.uri
65 glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
66 self.widget_tree = gtk.glade.XML(glade_path)
68 self.window = self.widget_tree.get_widget("board_window")
70 self.window.set_title(self.uri)
72 self.treeview = self.widget_tree.get_widget("treeview")
73 self.treeview.set_model(ThreadListModel())
75 self.popupmenu = self.widget_tree.get_widget("popup_menu")
76 self.toolbar = self.widget_tree.get_widget("toolbar")
77 self.toolbar.unset_style()
78 self.statusbar = self.widget_tree.get_widget("appbar")
80 renderer = gtk.CellRendererText()
82 self.treeviewcolumn = {}
83 for i in range(1, len(ThreadListModel.column_names)):
84 column_name = ThreadListModel.column_names[i]
85 self.treeviewcolumn[column_name] = gtk.TreeViewColumn(
86 column_name, renderer)
87 self.treeviewcolumn[column_name].set_resizable(True)
88 self.treeviewcolumn[column_name].set_reorderable(True)
89 self.treeviewcolumn[column_name].set_clickable(True)
90 self.treeviewcolumn[column_name].set_cell_data_func(
91 renderer, self.on_cell_data, column_name)
92 self.treeviewcolumn[column_name].connect(
93 "clicked", self.on_column_clicked, column_name)
94 self.treeview.append_column(self.treeviewcolumn[column_name])
96 self.treeviewcolumn["lastModified"].set_cell_data_func(
97 renderer, self.on_data_lastmodified)
99 sigdic = {"on_board_window_destroy": self.on_board_window_destroy,
100 "on_quit_activate": self.on_quit_activate,
101 "on_refresh_activate": self.on_refresh_activate,
102 "on_treeview_row_activated":
103 lambda w,p,v: self.on_open_thread(w),
104 "on_treeview_button_press_event":
105 self.on_treeview_button_press_event,
107 self.on_close_activate,
108 "on_toolbar_activate": self.on_toolbar_activate,
109 "on_statusbar_activate": self.on_statusbar_activate,
110 "on_board_window_delete_event":
111 self.on_board_window_delete_event,
112 "on_popup_menu_open_activate": self.on_open_thread}
113 self.widget_tree.signal_autoconnect(sigdic)
115 self.gconf_client = gconf.client_get_default()
116 self.gconf_key_base = "/apps/" + config.APPNAME.lower() + \
119 width = self.gconf_client.get_int(
120 self.gconf_key_base + "window_width")
121 height = self.gconf_client.get_int(
122 self.gconf_key_base + "window_height")
123 self.window.set_default_size(width, height)
125 if not self.gconf_client.get_bool(self.gconf_key_base + "toolbar"):
126 self.toolbar.parent.hide()
127 if not self.gconf_client.get_bool(self.gconf_key_base + "statusbar"):
128 self.statusbar.hide()
132 def on_toolbar_activate(self, widget):
133 if self.toolbar.parent.get_property("visible"):
134 self.toolbar.parent.hide()
135 self.gconf_client.set_bool(self.gconf_key_base + "toolbar", False)
137 self.toolbar.parent.show()
138 self.gconf_client.set_bool(self.gconf_key_base + "toolbar", True)
140 def on_statusbar_activate(self, widget):
141 if self.statusbar.get_property("visible"):
142 self.statusbar.hide()
143 self.gconf_client.set_bool(self.gconf_key_base+"statusbar", False)
145 self.statusbar.show()
146 self.gconf_client.set_bool(self.gconf_key_base + "statusbar", True)
148 def updated_thread_highlight(self, column, cell, model, iter):
150 def is_updated_thread():
151 res = model.get_value(
152 iter, ThreadListModel.column_names.index("res"))
153 linecount = model.get_value(
154 iter, ThreadListModel.column_names.index("lineCount"))
155 return res != 0 and linecount != 0 and res > linecount
157 if is_updated_thread():
158 cell.set_property("weight", 800)
160 cell.set_property("weight", 400)
162 def on_cell_data(self, column, cell, model, iter, column_name):
163 self.updated_thread_highlight(column, cell, model, iter)
164 column_num = ThreadListModel.column_names.index(column_name)
165 value = model.get_value(iter, column_num)
166 if model.get_column_type(column_num) == gobject.TYPE_INT:
168 cell.set_property("text", "")
170 cell.set_property("text", str(value))
172 cell.set_property("text", value)
174 def on_data_lastmodified(self, column, cell, model, iter, user_data=None):
175 self.updated_thread_highlight(column, cell, model, iter)
176 lastmod = model.get_value(
177 iter, ThreadListModel.column_names.index("lastModified"))
179 cell.set_property("text", "")
181 cell.set_property("text", time.strftime(
182 "%Y/%m/%d(%a) %H:%M:%S", time.localtime(lastmod)))
184 def on_board_window_delete_event(self, widget, event):
185 w, h = widget.get_size()
186 self.gconf_client.set_int(self.gconf_key_base + "window_width", w)
187 self.gconf_client.set_int(self.gconf_key_base + "window_height", h)
191 def on_board_window_destroy(self, widget):
194 def on_quit_activate(self, widget):
197 def on_close_activate(self, widget):
198 self.window.destroy()
200 def on_refresh_activate(self, widget):
201 t = board_data.GetRemote(
202 self.bbs, self.board, self.bbs_type.get_subject_txt_uri(),
203 self.update_datastore)
206 def on_column_clicked(self, treeviewcolumn, column_name):
207 model = self.treeview.get_model()
209 model.sort(column_name)
210 self.reset_sort_indicator()
212 def reset_sort_indicator(self):
213 model = self.treeview.get_model()
215 sort_column_name, sort_reverse = model.get_sort()
216 for name,column in self.treeviewcolumn.iteritems():
217 column.set_sort_indicator(False)
218 if sort_column_name != "num" or sort_reverse:
219 self.treeviewcolumn[sort_column_name].set_sort_indicator(True)
221 self.treeviewcolumn[sort_column_name].set_sort_order(
224 self.treeviewcolumn[sort_column_name].set_sort_order(
227 def on_open_thread(self, widget):
228 treeselection = self.treeview.get_selection()
229 model, iter = treeselection.get_selected()
233 thread = model.get_value(iter, ThreadListModel.column_names.index("id"))
234 title = model.get_value(
235 iter, ThreadListModel.column_names.index("title"))
236 print thread + ':"' + title + '"', "activated"
238 res = model.get_value(iter, ThreadListModel.column_names.index("res"))
239 lineCount = model.get_value(
240 iter, ThreadListModel.column_names.index("lineCount"))
242 update = res > lineCount
244 bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
245 uri_opener.open_uri(bbs_type_for_thread.get_thread_uri(), update)
247 def on_treeview_button_press_event(self, widget, event):
248 if event.button == 3:
252 pthinfo = widget.get_path_at_pos(x, y)
253 if pthinfo is not None:
254 path, col, cellx, celly = pthinfo
256 widget.set_cursor(path, col, 0)
257 self.popupmenu.popup(None, None, None, event.button, time)
260 def update_datastore(self, datalist, lastmod):
261 print "reflesh datastore"
264 lastmod = misc.httpdate_to_secs(lastmod)
268 time_start = time.time()
270 for id, dic in datalist.iteritems():
274 if lastmod == 0 or dic["num"] == 0:
279 # avoid the Last-Modified time of subject.txt and
280 # the build time of thread is equal (zero division)
281 dur = lastmod - start
283 dic["average"] = 999999
285 dic["average"] = int(res * 60 * 60 * 24 / dur)
288 httpdate = dic["lastModified"]
290 secs = misc.httpdate_to_secs(httpdate)
291 dic["lastModified"] = secs
293 dic["lastModified"] = 0
295 list_list.append(dic)
297 model = self.treeview.get_model()
298 model.set_list(list_list)
300 # redraw visible area after set list to model
301 self.treeview.queue_draw()
303 self.reset_sort_indicator()
306 time_end = time.time()
307 print time_end - time_start
309 def on_thread_idx_updated(self, thread_uri, idx_dic):
310 if not thread_uri or not idx_dic:
313 # nothing to do if thread_uri does not belong to this board.
314 bbs_type = bbs_type_judge_uri.get_type(thread_uri)
315 if bbs_type.bbs_type != self.bbs \
316 or bbs_type.board != self.board or not bbs_type.is_thread():
319 thread = bbs_type.thread
321 model = self.treeview.get_model()
323 idx_dic["id"] = thread
325 idx_dic["lastModified"] = misc.httpdate_to_secs(
326 idx_dic["lastModified"])
328 idx_dic["lastModified"] = 0
329 model.modify_row(idx_dic)
331 def load(self, update=False):
332 sbj_path = misc.get_board_subjecttxt_path(
333 self.bbs_type.bbs_type, self.bbs_type.board)
334 sbj_exists = os.path.exists(sbj_path)
336 if update or not sbj_exists:
337 t = board_data.GetRemote(
338 self.bbs, self.board, self.bbs_type.get_subject_txt_uri(),
339 self.update_datastore)
342 t = board_data.LoadLocal(
343 self.bbs, self.board, self.update_datastore)