OSDN Git Service

Add an update option to uri_opener.
[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 uri_opener
28 import misc
29 from threadlistmodel import ThreadListModel
30 from BbsType import bbs_type_judge_uri
31
32 import session
33
34 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
35                          "..", "data")
36 GLADE_FILENAME = "board_window.glade"
37
38 def open_board(uri, update=False):
39     if not uri:
40         raise ValueError, "parameter must not be empty"
41
42     winwrap = session.get_window(uri)
43     if winwrap:
44         # already opened
45         winwrap.window.present()
46         if update:
47             winwrap.load(update)
48     else:
49         winwrap = WinWrap(uri)
50         session.window_created(uri, winwrap)
51         winwrap.load(update)
52
53
54 class WinWrap:
55
56     def __init__(self, uri):
57
58         self.bbs_type = bbs_type_judge_uri.get_type(uri)
59         self.bbs = self.bbs_type.bbs_type
60         self.host = self.bbs_type.host
61         self.board = self.bbs_type.board
62         self.uri = self.bbs_type.uri
63         
64         glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
65         self.widget_tree = gtk.glade.XML(glade_path)
66
67         self.window = self.widget_tree.get_widget("board_window")
68
69         self.window.set_title(self.uri)
70
71         self.treeview = self.widget_tree.get_widget("treeview")
72         self.treeview.set_model(ThreadListModel())
73
74         self.popupmenu = self.widget_tree.get_widget("popup_menu")
75
76         renderer = gtk.CellRendererText()
77
78         self.treeviewcolumn = {}
79         for i in range(1, len(ThreadListModel.column_names)):
80             column_name = ThreadListModel.column_names[i]
81             self.treeviewcolumn[column_name] = gtk.TreeViewColumn(
82                 column_name, renderer)
83             self.treeviewcolumn[column_name].set_resizable(True)
84             self.treeviewcolumn[column_name].set_reorderable(True)
85             self.treeviewcolumn[column_name].set_clickable(True)
86             self.treeviewcolumn[column_name].set_cell_data_func(
87                 renderer, self.on_cell_data, column_name)
88             self.treeviewcolumn[column_name].connect(
89                 "clicked", self.on_column_clicked, column_name)
90             self.treeview.append_column(self.treeviewcolumn[column_name])
91
92         self.treeviewcolumn["lastModified"].set_cell_data_func(
93             renderer, self.on_data_lastmodified)
94
95         sigdic = {"on_board_window_destroy": self.on_board_window_destroy,
96                   "on_quit_activate": self.on_quit_activate,
97                   "on_load_local_activate": self.on_load_local_activate,
98                   "on_get_remote_activate": self.on_get_remote_activate,
99                   "on_treeview_row_activated":
100                   lambda w,p,v: self.on_open_thread(w),
101                   "on_treeview_button_press_event":
102                   self.on_treeview_button_press_event,
103                   "on_close_activate":
104                   self.on_close_activate,
105                   "on_popup_menu_open_activate": self.on_open_thread}
106         self.widget_tree.signal_autoconnect(sigdic)
107
108     def updated_thread_highlight(self, column, cell, model, iter):
109
110         def is_updated_thread():
111             res = model.get_value(
112                 iter, ThreadListModel.column_names.index("res"))
113             linecount = model.get_value(
114                 iter, ThreadListModel.column_names.index("lineCount"))
115             return res != 0 and linecount != 0 and res > linecount
116
117         if is_updated_thread():
118             cell.set_property("weight", 800)
119         else:
120             cell.set_property("weight", 400)
121
122     def on_cell_data(self, column, cell, model, iter, column_name):
123         self.updated_thread_highlight(column, cell, model, iter)
124         column_num = ThreadListModel.column_names.index(column_name)
125         value = model.get_value(iter, column_num)
126         if model.get_column_type(column_num) == gobject.TYPE_INT:
127             if value == 0:
128                 cell.set_property("text", "")
129             else:
130                 cell.set_property("text", str(value))
131         else:
132             cell.set_property("text", value)
133
134     def on_data_lastmodified(self, column, cell, model, iter, user_data=None):
135         self.updated_thread_highlight(column, cell, model, iter)
136         lastmod = model.get_value(
137             iter, ThreadListModel.column_names.index("lastModified"))
138         if lastmod == 0:
139             cell.set_property("text", "")
140         else:
141             cell.set_property("text", time.strftime(
142                 "%Y/%m/%d(%a) %H:%M:%S", time.localtime(lastmod)))
143
144     def on_board_window_destroy(self, widget):
145         pass
146
147     def on_quit_activate(self, widget):
148         session.main_quit()
149
150     def on_close_activate(self, widget):
151         self.window.destroy()
152
153     def on_load_local_activate(self, widget):
154         t = board_data.LoadLocal(self.bbs, self.board, self.update_datastore)
155         t.start()
156
157     def on_get_remote_activate(self, widget):
158         t = board_data.GetRemote(
159             self.bbs, self.board, self.bbs_type.get_subject_txt_uri(),
160             self.update_datastore)
161         t.start()
162
163     def on_column_clicked(self, treeviewcolumn, column_name):
164         model = self.treeview.get_model()
165         if model:
166             model.sort(column_name)
167             self.reset_sort_indicator()
168
169     def reset_sort_indicator(self):
170         model = self.treeview.get_model()
171         if model:
172             sort_column_name, sort_reverse = model.get_sort()
173             for name,column in self.treeviewcolumn.iteritems():
174                 column.set_sort_indicator(False)
175             if sort_column_name != "num" or sort_reverse:
176                 self.treeviewcolumn[sort_column_name].set_sort_indicator(True)
177                 if sort_reverse:
178                     self.treeviewcolumn[sort_column_name].set_sort_order(
179                         gtk.SORT_DESCENDING)
180                 else:
181                     self.treeviewcolumn[sort_column_name].set_sort_order(
182                         gtk.SORT_ASCENDING)
183         
184     def on_open_thread(self, widget):
185         treeselection = self.treeview.get_selection()
186         model, iter = treeselection.get_selected()
187         if not iter:
188             return
189
190         thread = model.get_value(iter, ThreadListModel.column_names.index("id"))
191         title = model.get_value(
192             iter, ThreadListModel.column_names.index("title"))
193         print thread + ':"' + title + '"', "activated"
194
195         res = model.get_value(iter, ThreadListModel.column_names.index("res"))
196         lineCount = model.get_value(
197             iter, ThreadListModel.column_names.index("lineCount"))
198
199         update = res > lineCount
200
201         bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
202         uri_opener.open_uri(bbs_type_for_thread.get_thread_uri(), update)
203
204     def on_treeview_button_press_event(self, widget, event):
205         if event.button == 3:
206             x = int(event.x)
207             y = int(event.y)
208             time = event.time
209             pthinfo = widget.get_path_at_pos(x, y)
210             if pthinfo is not None:
211                 path, col, cellx, celly = pthinfo
212                 widget.grab_focus()
213                 widget.set_cursor(path, col, 0)
214                 self.popupmenu.popup(None, None, None, event.button, time)
215             return 1
216
217     def update_datastore(self, datalist, lastmod):
218         print "reflesh datastore"
219
220         try:
221             lastmod = misc.httpdate_to_secs(lastmod)
222         except:
223             lastmod = 0
224
225         time_start = time.time()
226         list_list = []
227         for id, dic in datalist.iteritems():
228             dic["id"] = id
229
230             # average
231             if lastmod == 0 or dic["num"] == 0:
232                 dic["average"] = 0
233             else:
234                 res = dic["res"]
235                 start = int(id)
236                 # avoid the Last-Modified time of subject.txt and
237                 # the build time of thread is equal (zero division)
238                 dur = lastmod - start
239                 if dur == 0:
240                     dic["average"] = 999999
241                 else:
242                     dic["average"] = int(res * 60 * 60 * 24 / dur)
243
244             # lastModified
245             httpdate = dic["lastModified"]
246             try:
247                 secs = misc.httpdate_to_secs(httpdate)
248                 dic["lastModified"] = secs
249             except ValueError:
250                 dic["lastModified"] = 0
251             
252             list_list.append(dic)
253
254         model = self.treeview.get_model()
255         model.set_list(list_list)
256
257         # redraw visible area after set list to model
258         self.treeview.queue_draw()
259
260         self.reset_sort_indicator()
261
262         print "end"
263         time_end = time.time()
264         print time_end - time_start
265
266     def on_thread_idx_updated(self, thread_uri, idx_dic):
267         if not thread_uri or not idx_dic:
268             return
269
270         # nothing to do if thread_uri does not belong to this board.
271         bbs_type = bbs_type_judge_uri.get_type(thread_uri)
272         if bbs_type.bbs_type != self.bbs \
273            or bbs_type.board != self.board or not bbs_type.is_thread():
274             return
275
276         thread = bbs_type.thread
277
278         model = self.treeview.get_model()
279         if model:
280             idx_dic["id"] = thread
281             try:
282                 idx_dic["lastModified"] =  misc.httpdate_to_secs(
283                     idx_dic["lastModified"])
284             except ValueError:
285                 idx_dic["lastModified"] = 0
286             model.modify_row(idx_dic)
287
288     def load(self, update=False):
289         sbj_path = misc.get_board_subjecttxt_path(
290             self.bbs_type.bbs_type, self.bbs_type.board)
291         sbj_exists = os.path.exists(sbj_path)
292
293         if update or not sbj_exists:
294             t = board_data.GetRemote(
295                 self.bbs, self.board, self.bbs_type.get_subject_txt_uri(),
296                 self.update_datastore)
297             t.start()
298         else:
299             t = board_data.LoadLocal(
300                 self.bbs, self.board, self.update_datastore)
301             t.start()