OSDN Git Service

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