OSDN Git Service

Highlight updated threads
[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_rules_hint(True)
46
47         self.popupmenu = self.mainwin.get_widget("popup_menu")
48
49         renderer = gtk.CellRendererText()
50
51         self.treeviewcolumn = {}
52         for i in range(1, len(ThreadListModel.column_names)):
53             column_name = ThreadListModel.column_names[i]
54             self.treeviewcolumn[column_name] = gtk.TreeViewColumn(
55                 column_name, renderer)
56             self.treeviewcolumn[column_name].set_resizable(True)
57             self.treeviewcolumn[column_name].set_reorderable(True)
58             self.treeviewcolumn[column_name].set_clickable(True)
59             self.treeviewcolumn[column_name].set_cell_data_func(
60                 renderer, self.on_cell_data, column_name)
61             self.treeviewcolumn[column_name].connect(
62                 "clicked", self.on_column_clicked, column_name)
63             self.treeview.append_column(self.treeviewcolumn[column_name])
64
65         self.treeviewcolumn["lastModified"].set_cell_data_func(
66             renderer, self.on_data_lastmodified)
67
68         sigdic = {"on_board_window_destroy": self.on_board_window_destroy,
69                   "on_quit_activate": self.on_quit_activate,
70                   "on_load_local_activate": self.on_load_local_activate,
71                   "on_get_remote_activate": self.on_get_remote_activate,
72                   "on_treeview_row_activated":
73                   lambda w,p,v: self.on_open_thread(w),
74                   "on_treeview_button_press_event":
75                   self.on_treeview_button_press_event,
76                   "on_popup_menu_open_activate": self.on_open_thread}
77         self.mainwin.signal_autoconnect(sigdic)
78
79     def updated_thread_highlight(self, column, cell, model, iter):
80
81         def is_updated_thread():
82             res = model.get_value(
83                 iter, ThreadListModel.column_names.index("res"))
84             linecount = model.get_value(
85                 iter, ThreadListModel.column_names.index("lineCount"))
86             return res != 0 and linecount != 0 and res > linecount
87
88         if is_updated_thread():
89             cell.set_property("weight", 800)
90         else:
91             cell.set_property("weight", 400)
92
93     def on_cell_data(self, column, cell, model, iter, column_name):
94         self.updated_thread_highlight(column, cell, model, iter)
95         column_num = ThreadListModel.column_names.index(column_name)
96         value = model.get_value(iter, column_num)
97         if model.get_column_type(column_num) == gobject.TYPE_INT:
98             if value == 0:
99                 cell.set_property("text", "")
100             else:
101                 cell.set_property("text", str(value))
102         else:
103             cell.set_property("text", value)
104
105     def on_data_lastmodified(self, column, cell, model, iter, user_data=None):
106         self.updated_thread_highlight(column, cell, model, iter)
107         lastmod = model.get_value(
108             iter, ThreadListModel.column_names.index("lastModified"))
109         if lastmod == 0:
110             cell.set_property("text", "")
111         else:
112             cell.set_property("text", time.strftime(
113                 "%Y/%m/%d(%a) %H:%M:%S", time.localtime(lastmod)))
114
115     def on_board_window_destroy(self, widget):
116         gtk.main_quit()
117
118     def on_quit_activate(self, widget):
119         gtk.main_quit()
120
121     def on_load_local_activate(self, widget):
122         t = board_data.LoadLocal(self.bbs, self.board, self.update_datastore)
123         t.start()
124
125     def on_get_remote_activate(self, widget):
126         t = board_data.GetRemote(self.bbs, self.board, self.update_datastore)
127         t.start()
128
129     def on_column_clicked(self, treeviewcolumn, column_name):
130         model = self.treeview.get_model()
131         if model:
132             model.sort(column_name)
133             self.reset_sort_indicator()
134
135     def reset_sort_indicator(self):
136         model = self.treeview.get_model()
137         if model:
138             sort_column_name, sort_reverse = model.get_sort()
139             for name,column in self.treeviewcolumn.iteritems():
140                 column.set_sort_indicator(False)
141             if sort_column_name != "num" or sort_reverse:
142                 self.treeviewcolumn[sort_column_name].set_sort_indicator(True)
143                 if sort_reverse:
144                     self.treeviewcolumn[sort_column_name].set_sort_order(
145                         gtk.SORT_DESCENDING)
146                 else:
147                     self.treeviewcolumn[sort_column_name].set_sort_order(
148                         gtk.SORT_ASCENDING)
149         
150     def on_open_thread(self, widget):
151         treeselection = self.treeview.get_selection()
152         model, iter = treeselection.get_selected()
153         if not iter:
154             return
155
156         thread = model.get_value(iter, ThreadListModel.column_names.index("id"))
157         title = model.get_value(
158             iter, ThreadListModel.column_names.index("title"))
159         print thread + ':"' + title + '"', "activated"
160
161         thread_window.WinWrap(self.bbs, self.board, thread)
162
163     def on_treeview_button_press_event(self, widget, event):
164         if event.button == 3:
165             x = int(event.x)
166             y = int(event.y)
167             time = event.time
168             pthinfo = widget.get_path_at_pos(x, y)
169             if pthinfo is not None:
170                 path, col, cellx, celly = pthinfo
171                 widget.grab_focus()
172                 widget.set_cursor(path, col, 0)
173                 self.popupmenu.popup(None, None, None, event.button, time)
174             return 1
175
176     def update_datastore(self, datalist, lastmod):
177         print "reflesh datastore"
178
179         try:
180             lastmod = misc.httpdate_to_secs(lastmod)
181         except:
182             lastmod = 0
183
184         time_start = time.time()
185         list_list = []
186         for id, dic in datalist.iteritems():
187             dic["id"] = id
188
189             # average
190             if lastmod == 0 or dic["num"] == 0:
191                 dic["average"] = 0
192             else:
193                 res = dic["res"]
194                 start = int(id)
195                 dic["average"] = int(res * 60 * 60 * 24 / (lastmod - start))
196
197             # lastModified
198             httpdate = dic["lastModified"]
199             try:
200                 secs = misc.httpdate_to_secs(httpdate)
201                 dic["lastModified"] = secs
202             except ValueError:
203                 dic["lastModified"] = 0
204             
205             list_list.append(dic)
206
207         model = self.treeview.get_model()
208         if model:
209             sort_column_name, sort_reverse = model.get_sort()
210             model = ThreadListModel(list_list, sort_column_name, sort_reverse)
211         else:
212             model = ThreadListModel(list_list)
213         self.treeview.set_model(model)
214
215         self.reset_sort_indicator()
216
217         print "end"
218         time_end = time.time()
219         print time_end - time_start
220
221
222 class ThreadListModel(gtk.GenericTreeModel):
223     column_types = (str, int, str, int, int, int, int)
224     column_names = ["id", "num", "title", "res", "lineCount",
225                     "lastModified", "average"]
226
227     def __init__(self, list, sort_column_name="num", sort_reverse=False):
228         gtk.GenericTreeModel.__init__(self)
229         self.list = list
230         self.sort_column_name = sort_column_name
231         self.sort_reverse = sort_reverse
232         self.do_sort(self.sort_column_name, self.sort_reverse)
233
234     def build_order_dict(self):
235         # key: thread id, value: index in self.list
236         self.order_dict = dict(
237             [(item["id"], index) for index, item in enumerate(self.list)])
238  
239     def compare(self, v1, v2, reverse):
240         if not reverse:
241             if v1 == 0 and v2 == 0:
242                 return 0
243             elif v1 == 0:
244                 return 1
245             elif v2 == 0:
246                 return -1
247         return v1 - v2
248
249     def get_sort(self):
250         return self.sort_column_name, self.sort_reverse
251
252     def do_sort(self, column_name, reverse):
253         if self.column_types[self.column_names.index(column_name)] == str:
254             self.list.sort(None, lambda dic: dic[column_name], reverse)
255         else:
256             h = lambda x, y: self.compare(x, y, reverse)
257             self.list.sort(h, lambda dic: dic[column_name], reverse)
258         self.build_order_dict()
259
260     def sort(self, column_name):
261         old_order_dict = self.order_dict
262
263         if column_name == self.sort_column_name:
264             # sort reverse
265             self.sort_reverse = not self.sort_reverse
266         else:
267             self.sort_column_name = column_name
268             self.sort_reverse = False
269
270         self.do_sort(self.sort_column_name, self.sort_reverse)
271
272         neworder = [old_order_dict[item["id"]] for item in self.list]
273         self.rows_reordered(None, None, neworder)
274
275     def on_get_flags(self):
276         return gtk.TREE_MODEL_LIST_ONLY
277
278     def on_get_n_columns(self):
279         return len(self.column_types)
280
281     def on_get_column_type(self, n):
282         return self.column_types[n]
283
284     def on_get_iter(self, path):
285         return self.list[path[0]]
286
287     def on_get_path(self, rowref):
288         return self.order_dict[rowref["id"]]
289
290     def on_get_value(self, rowref, column):
291         return rowref[self.column_names[column]]
292
293     def on_iter_next(self, rowref):
294         try:
295             i = self.order_dict[rowref["id"]] + 1
296             return self.list[i]
297         except IndexError:
298             return None
299
300     def on_iter_children(self, rowref):
301         if rowref:
302             return None
303         return self.list[0]
304
305     def on_iter_has_child(self, rowref):
306         return False
307
308     def on_iter_n_children(self, rowref):
309         if rowref:
310             return 0
311         return len(self.list)
312
313     def on_iter_nth_child(self, rowref, n):
314         if rowref:
315             return None
316         try:
317             return self.list[n]
318         except IndexError:
319             return None
320
321     def on_iter_parent(self, child):
322         return None