OSDN Git Service

Fix zero division.
[fukui-no-namari/fukui-no-namari.git] / src / Hage1 / board_window.py
1 # Copyright (C) 2006 by Aiwota Programmer
2 # aiwotaprog@tetteke.tk
3 #
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.
8 #
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.
13 #
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
17
18 import pygtk
19 pygtk.require('2.0')
20 import gtk
21 import gtk.glade
22 import os
23 import time
24 import gobject
25
26 import board_data
27 import thread_window
28 import misc
29
30 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
31                          "..", "data")
32 GLADE_FILENAME = "board_window.glade"
33
34
35 class WinWrap:
36
37     def __init__(self, bbs, board):
38         self.bbs = bbs
39         self.board = board
40
41         glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
42         self.mainwin = gtk.glade.XML(glade_path)
43
44         self.treeview = self.mainwin.get_widget("treeview")
45         self.treeview.set_model(ThreadListModel())
46         self.treeview.set_rules_hint(True)
47
48         self.popupmenu = self.mainwin.get_widget("popup_menu")
49
50         renderer = gtk.CellRendererText()
51
52         self.treeviewcolumn = {}
53         for i in range(1, len(ThreadListModel.column_names)):
54             column_name = ThreadListModel.column_names[i]
55             self.treeviewcolumn[column_name] = gtk.TreeViewColumn(
56                 column_name, renderer)
57             self.treeviewcolumn[column_name].set_resizable(True)
58             self.treeviewcolumn[column_name].set_reorderable(True)
59             self.treeviewcolumn[column_name].set_clickable(True)
60             self.treeviewcolumn[column_name].set_cell_data_func(
61                 renderer, self.on_cell_data, column_name)
62             self.treeviewcolumn[column_name].connect(
63                 "clicked", self.on_column_clicked, column_name)
64             self.treeview.append_column(self.treeviewcolumn[column_name])
65
66         self.treeviewcolumn["lastModified"].set_cell_data_func(
67             renderer, self.on_data_lastmodified)
68
69         sigdic = {"on_board_window_destroy": self.on_board_window_destroy,
70                   "on_quit_activate": self.on_quit_activate,
71                   "on_load_local_activate": self.on_load_local_activate,
72                   "on_get_remote_activate": self.on_get_remote_activate,
73                   "on_treeview_row_activated":
74                   lambda w,p,v: self.on_open_thread(w),
75                   "on_treeview_button_press_event":
76                   self.on_treeview_button_press_event,
77                   "on_popup_menu_open_activate": self.on_open_thread}
78         self.mainwin.signal_autoconnect(sigdic)
79
80     def updated_thread_highlight(self, column, cell, model, iter):
81
82         def is_updated_thread():
83             res = model.get_value(
84                 iter, ThreadListModel.column_names.index("res"))
85             linecount = model.get_value(
86                 iter, ThreadListModel.column_names.index("lineCount"))
87             return res != 0 and linecount != 0 and res > linecount
88
89         if is_updated_thread():
90             cell.set_property("weight", 800)
91         else:
92             cell.set_property("weight", 400)
93
94     def on_cell_data(self, column, cell, model, iter, column_name):
95         self.updated_thread_highlight(column, cell, model, iter)
96         column_num = ThreadListModel.column_names.index(column_name)
97         value = model.get_value(iter, column_num)
98         if model.get_column_type(column_num) == gobject.TYPE_INT:
99             if value == 0:
100                 cell.set_property("text", "")
101             else:
102                 cell.set_property("text", str(value))
103         else:
104             cell.set_property("text", value)
105
106     def on_data_lastmodified(self, column, cell, model, iter, user_data=None):
107         self.updated_thread_highlight(column, cell, model, iter)
108         lastmod = model.get_value(
109             iter, ThreadListModel.column_names.index("lastModified"))
110         if lastmod == 0:
111             cell.set_property("text", "")
112         else:
113             cell.set_property("text", time.strftime(
114                 "%Y/%m/%d(%a) %H:%M:%S", time.localtime(lastmod)))
115
116     def on_board_window_destroy(self, widget):
117         gtk.main_quit()
118
119     def on_quit_activate(self, widget):
120         gtk.main_quit()
121
122     def on_load_local_activate(self, widget):
123         t = board_data.LoadLocal(self.bbs, self.board, self.update_datastore)
124         t.start()
125
126     def on_get_remote_activate(self, widget):
127         t = board_data.GetRemote(self.bbs, self.board, self.update_datastore)
128         t.start()
129
130     def on_column_clicked(self, treeviewcolumn, column_name):
131         model = self.treeview.get_model()
132         if model:
133             model.sort(column_name)
134             self.reset_sort_indicator()
135
136     def reset_sort_indicator(self):
137         model = self.treeview.get_model()
138         if model:
139             sort_column_name, sort_reverse = model.get_sort()
140             for name,column in self.treeviewcolumn.iteritems():
141                 column.set_sort_indicator(False)
142             if sort_column_name != "num" or sort_reverse:
143                 self.treeviewcolumn[sort_column_name].set_sort_indicator(True)
144                 if sort_reverse:
145                     self.treeviewcolumn[sort_column_name].set_sort_order(
146                         gtk.SORT_DESCENDING)
147                 else:
148                     self.treeviewcolumn[sort_column_name].set_sort_order(
149                         gtk.SORT_ASCENDING)
150         
151     def on_open_thread(self, widget):
152         treeselection = self.treeview.get_selection()
153         model, iter = treeselection.get_selected()
154         if not iter:
155             return
156
157         thread = model.get_value(iter, ThreadListModel.column_names.index("id"))
158         title = model.get_value(
159             iter, ThreadListModel.column_names.index("title"))
160         print thread + ':"' + title + '"', "activated"
161
162         thread_window.WinWrap(self.bbs, self.board, thread)
163
164     def on_treeview_button_press_event(self, widget, event):
165         if event.button == 3:
166             x = int(event.x)
167             y = int(event.y)
168             time = event.time
169             pthinfo = widget.get_path_at_pos(x, y)
170             if pthinfo is not None:
171                 path, col, cellx, celly = pthinfo
172                 widget.grab_focus()
173                 widget.set_cursor(path, col, 0)
174                 self.popupmenu.popup(None, None, None, event.button, time)
175             return 1
176
177     def update_datastore(self, datalist, lastmod):
178         print "reflesh datastore"
179
180         try:
181             lastmod = misc.httpdate_to_secs(lastmod)
182         except:
183             lastmod = 0
184
185         time_start = time.time()
186         list_list = []
187         for id, dic in datalist.iteritems():
188             dic["id"] = id
189
190             # average
191             if lastmod == 0 or dic["num"] == 0:
192                 dic["average"] = 0
193             else:
194                 res = dic["res"]
195                 start = int(id)
196                 # avoid the Last-Modified time of subject.txt and
197                 # the build time of thread is equal (zero division)
198                 dur = lastmod - start
199                 if dur == 0:
200                     dic["average"] = 999999
201                 else:
202                     dic["average"] = int(res * 60 * 60 * 24 / dur)
203
204             # lastModified
205             httpdate = dic["lastModified"]
206             try:
207                 secs = misc.httpdate_to_secs(httpdate)
208                 dic["lastModified"] = secs
209             except ValueError:
210                 dic["lastModified"] = 0
211             
212             list_list.append(dic)
213
214         model = self.treeview.get_model()
215         model.set_list(list_list)
216         self.reset_sort_indicator()
217
218         print "end"
219         time_end = time.time()
220         print time_end - time_start
221
222
223 class ThreadListModel(gtk.GenericTreeModel):
224     column_types = (str, int, str, int, int, int, int)
225     column_names = ["id", "num", "title", "res", "lineCount",
226                     "lastModified", "average"]
227
228     def __init__(self, list=None, sort_column_name="num", sort_reverse=False,
229                  filter_func=None):
230         gtk.GenericTreeModel.__init__(self)
231         if list:
232             self.original_list = list[:]
233         else:
234             self.original_list = []
235
236         self.sort_column_name = sort_column_name
237         self.sort_reverse = sort_reverse
238         self.filter_func = filter_func
239
240         self.do_filter()
241         self.do_sort(self.sort_column_name, self.sort_reverse)
242
243     def refilter(self):
244         before_size = len(self.list)
245         self.do_filter()
246         after_size = len(self.list)
247         self.do_sort(self.sort_column_name, self.sort_reverse)
248         self.list_modified(before_size, after_size)
249         
250     def set_list(self, newlist):
251         self.original_list = newlist[:]
252         self.refilter()
253
254     def set_filter_func(func):
255         self.filter_func = func
256
257     def build_order_dict(self):
258         # key: thread id, value: index in self.list
259         self.order_dict = dict(
260             [(item["id"], index) for index, item in enumerate(self.list)])
261
262     def list_modified(self, before_size, after_size):
263         if before_size > after_size:
264             print "remove", before_size-after_size, "threads"
265             for i in range(before_size - after_size):
266                 self.row_deleted(after_size)
267         elif after_size > before_size:
268             print "insert", after_size-before_size, "threads"
269             for i in range(before_size, after_size):
270                 self.row_inserted(i, self.get_iter(i))
271         elif after_size != 0:
272             self.row_changed(0, self.get_iter(0))
273
274     def do_filter(self):
275         if not self.filter_func:
276             self.list = self.original_list[:]
277             return
278
279         self.list = []
280         for item in self.original_list:
281             if self.filter_func(self, item):
282                 self.list.append(item)
283
284     def compare(self, v1, v2, reverse):
285         if not reverse:
286             if v1 == 0 and v2 == 0:
287                 return 0
288             elif v1 == 0:
289                 return 1
290             elif v2 == 0:
291                 return -1
292         return v1 - v2
293
294     def get_sort(self):
295         return self.sort_column_name, self.sort_reverse
296
297     def do_sort(self, column_name, reverse):
298         if self.column_types[self.column_names.index(column_name)] == str:
299             self.list.sort(None, lambda dic: dic[column_name], reverse)
300         else:
301             h = lambda x, y: self.compare(x, y, reverse)
302             self.list.sort(h, lambda dic: dic[column_name], reverse)
303         self.build_order_dict()
304
305     def sort(self, column_name):
306         old_order_dict = self.order_dict
307
308         if column_name == self.sort_column_name:
309             # sort reverse
310             self.sort_reverse = not self.sort_reverse
311         else:
312             self.sort_column_name = column_name
313             self.sort_reverse = False
314
315         self.do_sort(self.sort_column_name, self.sort_reverse)
316
317         neworder = [old_order_dict[item["id"]] for item in self.list]
318         self.rows_reordered(None, None, neworder)
319
320     def on_get_flags(self):
321         return gtk.TREE_MODEL_LIST_ONLY
322
323     def on_get_n_columns(self):
324         return len(self.column_types)
325
326     def on_get_column_type(self, n):
327         return self.column_types[n]
328
329     def on_get_iter(self, path):
330         try:
331             return self.list[path[0]]
332         except IndexError:
333             return None
334
335     def on_get_path(self, rowref):
336         return self.order_dict[rowref["id"]]
337
338     def on_get_value(self, rowref, column):
339         return rowref[self.column_names[column]]
340
341     def on_iter_next(self, rowref):
342         try:
343             i = self.order_dict[rowref["id"]] + 1
344             return self.list[i]
345         except IndexError:
346             return None
347
348     def on_iter_children(self, rowref):
349         if rowref:
350             return None
351         return self.list[0]
352
353     def on_iter_has_child(self, rowref):
354         return False
355
356     def on_iter_n_children(self, rowref):
357         if rowref:
358             return 0
359         return len(self.list)
360
361     def on_iter_nth_child(self, rowref, n):
362         if rowref:
363             return None
364         try:
365             return self.list[n]
366         except IndexError:
367             return None
368
369     def on_iter_parent(self, child):
370         return None