OSDN Git Service

Print decoded form data.
[fukui-no-namari/fukui-no-namari.git] / src / FukuiNoNamari / 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 import traceback
27
28 import board_data
29 import uri_opener
30 import misc
31 from threadlistmodel import ThreadListModel
32 from BbsType import bbs_type_judge_uri
33 import config
34 import session
35 import winwrapbase
36 from misc import ThreadInvoker
37
38 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
39                          "..", "data")
40 GLADE_FILENAME = "board_window.glade"
41
42 def open_board(uri, update=False):
43     if not uri:
44         raise ValueError, "parameter must not be empty"
45
46     winwrap = session.get_window(uri)
47     if winwrap:
48         # already opened
49         winwrap.window.present()
50         if update:
51             winwrap.load(update)
52     else:
53         winwrap = WinWrap(uri)
54         winwrap.load(update)
55
56
57 class WinWrap(winwrapbase.WinWrapBase, board_data.BoardData):
58
59     def __init__(self, uri):
60
61         self.bbs_type = bbs_type_judge_uri.get_type(uri)
62         board_data.BoardData.__init__(self, self.bbs_type)
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.bbs_type.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         self.toolbar = self.widget_tree.get_widget("toolbar")
76         self.toolbar.unset_style()
77         self.statusbar = self.widget_tree.get_widget("appbar")
78         self.filterbar = self.widget_tree.get_widget("bonobodockitem_filterbar")
79         self.entry_filterbar = self.widget_tree.get_widget("entry_filterbar")
80
81         renderer = gtk.CellRendererText()
82
83         self.treeviewcolumn = {}
84         for i in range(1, len(ThreadListModel.column_names)):
85             column_name = ThreadListModel.column_names[i]
86             self.treeviewcolumn[column_name] = gtk.TreeViewColumn(
87                 column_name, renderer)
88             self.treeviewcolumn[column_name].set_resizable(True)
89             self.treeviewcolumn[column_name].set_reorderable(True)
90             self.treeviewcolumn[column_name].set_clickable(True)
91             self.treeviewcolumn[column_name].set_cell_data_func(
92                 renderer, self.on_cell_data, column_name)
93             self.treeviewcolumn[column_name].connect(
94                 "clicked", self.on_column_clicked, column_name)
95             self.treeviewcolumn[column_name].set_sizing(
96                 gtk.TREE_VIEW_COLUMN_FIXED)
97             self.treeviewcolumn[column_name].set_min_width(20)
98             self.treeviewcolumn[column_name].set_fixed_width(
99                 ThreadListModel.column_width[i])
100             self.treeview.append_column(self.treeviewcolumn[column_name])
101
102         self.treeviewcolumn["lastModified"].set_cell_data_func(
103             renderer, self.on_data_lastmodified)
104
105         self.treeview.set_fixed_height_mode(True)
106
107         sigdic = {"on_board_window_destroy": self.on_board_window_destroy,
108                   "on_quit_activate": self.on_quit_activate,
109                   "on_refresh_activate": self.on_refresh_activate,
110                   "on_treeview_row_activated":
111                   lambda w,p,v: self.on_open_thread(w),
112                   "on_treeview_button_press_event":
113                   self.on_treeview_button_press_event,
114                   "on_close_activate":
115                   self.on_close_activate,
116                   "on_toolbar_activate": self.on_toolbar_activate,
117                   "on_statusbar_activate": self.on_statusbar_activate,
118                   "on_board_window_delete_event":
119                   self.on_board_window_delete_event,
120                   "on_entry_filterbar_activate": self.on_entry_filterbar_activate,
121                   "on_filter_activate": self.on_filter_activate,
122                   "on_toolbutton_filterbar_close_clicked":
123                   self.on_toolbutton_filterbar_close_clicked,
124                   "on_popup_menu_open_activate": self.on_open_thread}
125         self.widget_tree.signal_autoconnect(sigdic)
126
127         self.restore()
128         self.window.show()
129
130         self.created()
131
132     def set_status(self, text):
133         self.statusbar.set_status(text)
134
135     def destroy(self):
136         self.save()
137         self.window.destroy()
138
139     def get_uri(self):
140         return self.bbs_type.get_uri_base()
141
142     def on_toolbar_activate(self, widget):
143         if self.toolbar.parent.get_property("visible"):
144             self.toolbar.parent.hide()
145         else:
146             self.toolbar.parent.show()
147
148     def on_statusbar_activate(self, widget):
149         if self.statusbar.get_property("visible"):
150             self.statusbar.hide()
151         else:
152             self.statusbar.show()
153
154     def on_filter_activate(self, widget):
155         self.filterbar.show()
156         self.entry_filterbar.grab_focus()
157
158     def on_entry_filterbar_activate(self, widget):
159         text = widget.get_text()
160
161         def func(model, item):
162             try:
163                 item["title"].index(text)
164             except ValueError:
165                 return False
166             else:
167                 return True
168
169         model = self.treeview.get_model()
170         if model:
171             if text:
172                 model.set_filter_func(func)
173             else:
174                 model.set_filter_func(None)
175             model.refilter()
176
177     def on_toolbutton_filterbar_close_clicked(self, widget):
178         self.filterbar.hide()
179
180     def updated_thread_highlight(self, column, cell, model, iter):
181
182         def is_updated_thread():
183             res = model.get_value(
184                 iter, ThreadListModel.column_names.index("res"))
185             linecount = model.get_value(
186                 iter, ThreadListModel.column_names.index("lineCount"))
187             return res != 0 and linecount != 0 and res > linecount
188
189         if is_updated_thread():
190             cell.set_property("weight", 800)
191         else:
192             cell.set_property("weight", 400)
193
194     def on_cell_data(self, column, cell, model, iter, column_name):
195         self.updated_thread_highlight(column, cell, model, iter)
196         column_num = ThreadListModel.column_names.index(column_name)
197         value = model.get_value(iter, column_num)
198         if model.get_column_type(column_num) \
199            in (gobject.TYPE_INT, gobject.TYPE_DOUBLE):
200             if value == 0:
201                 cell.set_property("text", "")
202             else:
203                 cell.set_property("text", str(value))
204         else:
205             cell.set_property("text", value)
206
207     def on_data_lastmodified(self, column, cell, model, iter, user_data=None):
208         self.updated_thread_highlight(column, cell, model, iter)
209         lastmod = model.get_value(
210             iter, ThreadListModel.column_names.index("lastModified"))
211         if lastmod == 0:
212             cell.set_property("text", "")
213         else:
214             cell.set_property("text", time.strftime(
215                 "%Y/%m/%d(%a) %H:%M:%S", time.localtime(lastmod)))
216
217     def on_board_window_delete_event(self, widget, event):
218         self.save()
219         return False
220
221     def on_board_window_destroy(self, widget):
222         self.destroyed()
223
224     def on_quit_activate(self, widget):
225         session.main_quit()
226
227     def on_close_activate(self, widget):
228         self.destroy()
229
230     def on_refresh_activate(self, widget):
231         self.load(True)
232
233     def on_column_clicked(self, treeviewcolumn, column_name):
234         model = self.treeview.get_model()
235         if model:
236             model.sort(column_name)
237             self.reset_sort_indicator()
238
239     def reset_sort_indicator(self):
240         model = self.treeview.get_model()
241         if model:
242             sort_column_name, sort_reverse = model.get_sort()
243             for name,column in self.treeviewcolumn.iteritems():
244                 column.set_sort_indicator(False)
245             if sort_column_name != "num" or sort_reverse:
246                 self.treeviewcolumn[sort_column_name].set_sort_indicator(True)
247                 if sort_reverse:
248                     self.treeviewcolumn[sort_column_name].set_sort_order(
249                         gtk.SORT_DESCENDING)
250                 else:
251                     self.treeviewcolumn[sort_column_name].set_sort_order(
252                         gtk.SORT_ASCENDING)
253         
254     def on_open_thread(self, widget):
255         treeselection = self.treeview.get_selection()
256         model, iter = treeselection.get_selected()
257         if not iter:
258             return
259
260         thread = model.get_value(iter, ThreadListModel.column_names.index("id"))
261         title = model.get_value(
262             iter, ThreadListModel.column_names.index("title"))
263         print thread + ':"' + title + '"', "activated"
264
265         res = model.get_value(iter, ThreadListModel.column_names.index("res"))
266         lineCount = model.get_value(
267             iter, ThreadListModel.column_names.index("lineCount"))
268
269         update = res > lineCount
270
271         bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
272         uri_opener.open_uri(bbs_type_for_thread.get_thread_uri(), update)
273
274     def on_treeview_button_press_event(self, widget, event):
275         if event.button == 3:
276             x = int(event.x)
277             y = int(event.y)
278             time = event.time
279             pthinfo = widget.get_path_at_pos(x, y)
280             if pthinfo is not None:
281                 path, col, cellx, celly = pthinfo
282                 widget.grab_focus()
283                 widget.set_cursor(path, col, 0)
284                 self.popupmenu.popup(None, None, None, event.button, time)
285             return 1
286
287     def update_datastore(self, datalist):
288         print "reflesh datastore"
289
290         list_list = []
291         for id, dic in datalist.iteritems():
292             dic["id"] = id
293
294             # lastModified
295             httpdate = dic["lastModified"]
296             try:
297                 secs = misc.httpdate_to_secs(httpdate)
298                 dic["lastModified"] = secs
299             except ValueError:
300                 dic["lastModified"] = 0
301
302             list_list.append(dic)
303
304         model = self.treeview.get_model()
305         model.set_list(list_list)
306
307         # redraw visible area after set list to model
308         self.treeview.queue_draw()
309
310         self.reset_sort_indicator()
311
312         print "end"
313
314     def on_thread_idx_updated(self, thread_uri, idx_dic):
315         if not thread_uri or not idx_dic:
316             return
317
318         # nothing to do if thread_uri does not belong to this board.
319         bbs_type = bbs_type_judge_uri.get_type(thread_uri)
320         if not bbs_type.is_thread() \
321                or not bbs_type.is_same_board(self.bbs_type):
322             return
323
324         thread = bbs_type.thread
325
326         model = self.treeview.get_model()
327         if model:
328             idx_dic["id"] = thread
329             try:
330                 idx_dic["lastModified"] =  misc.httpdate_to_secs(
331                     idx_dic["lastModified"])
332             except ValueError:
333                 idx_dic["lastModified"] = 0
334             model.modify_row(idx_dic)
335
336     def load(self, update=False):
337
338         def load_local():
339             datalist = self.load_idxfiles()
340             self.merge_local_subjecttxt(datalist)
341             gobject.idle_add(self.update_datastore, datalist)
342
343         def get_remote():
344             print "start get subject.txt"
345             datalist = self.load_idxfiles()
346             self.merge_remote_subjecttxt(datalist)
347             gobject.idle_add(self.update_datastore, datalist)
348
349         sbj_path = misc.get_board_subjecttxt_path(self.bbs_type)
350         sbj_exists = os.path.exists(sbj_path)
351
352         if update or not sbj_exists:
353             t = ThreadInvoker(lambda *args: -1, get_remote)
354             t.start()
355         else:
356             t = ThreadInvoker(lambda *args: -1, load_local)
357             t.start()
358
359     def save(self):
360         try:
361             states_path = misc.get_board_states_path(self.bbs_type)
362             dirname = os.path.dirname(states_path)
363
364             # save only if board dir exists.
365             if os.path.exists(dirname):
366                 window_width, window_height = self.window.get_size()
367                 toolbar_visible = self.toolbar.parent.get_property("visible")
368                 statusbar_visible = self.statusbar.get_property("visible")
369                 filterbar_visible = self.filterbar.get_property("visible")
370
371                 columns = self.treeview.get_columns()
372                 order = ""
373                 width = ""
374                 for column in columns:
375                     for name, kolumn in self.treeviewcolumn.iteritems():
376                         if column == kolumn:
377                             if order:
378                                 order += ","
379                             order += name
380                             if width:
381                                 width += ","
382                             width += str(column.get_width())
383                             break
384                 sort_column_name, sort_reverse = \
385                                   self.treeview.get_model().get_sort()
386                 sort_reverse = str(sort_reverse)
387
388                 f = file(states_path, "w")
389
390                 f.write("columns=" + order + "\n")
391                 f.write("widths=" + width + "\n")
392                 f.write("sort_column=" + sort_column_name + "\n")
393                 f.write("sort_reverse=" + sort_reverse + "\n")
394                 f.write("window_width=" + str(window_width) + "\n")
395                 f.write("window_height=" + str(window_height) + "\n")
396                 f.write("toolbar_visible=" + str(toolbar_visible) + "\n")
397                 f.write("statusbar_visible=" + str(statusbar_visible) + "\n")
398                 f.write("filterbar_visible=" + str(filterbar_visible) + "\n")
399
400                 f.close()
401         except:
402             traceback.print_exc()
403
404     def restore(self):
405         try:
406             window_height = 600
407             window_width = 600
408             toolbar_visible = True
409             statusbar_visible = True
410             filterbar_visible = False
411
412             try:
413                 key_base = config.gconf_app_key_base() + "/board_states"
414                 gconf_client = gconf.client_get_default()
415                 width = gconf_client.get_int(key_base + "/window_width")
416                 height = gconf_client.get_int(key_base + "/window_height")
417                 toolbar_visible = gconf_client.get_bool(key_base + "/toolbar")
418                 statusbar_visible = gconf_client.get_bool(key_base + "/statusbar")
419                 filterbar_visible = gconf_client.get_bool(key_base + "/filterbar")
420
421                 if width != 0:
422                     window_width = width
423                 if height != 0:
424                     window_height = height
425             except:
426                 traceback.print_exc()
427
428             states_path = misc.get_board_states_path(self.bbs_type)
429             if os.path.exists(states_path):
430                 sort_column_name = "num"
431                 sort_reverse = False
432                 for line in file(states_path):
433                     if line.startswith("columns="):
434                         line = line[len("columns="):].rstrip("\n")
435                         base_column = None
436                         for name in line.split(","):
437                             if name in self.treeviewcolumn:
438                                 column = self.treeviewcolumn[name]
439                                 self.treeview.move_column_after(
440                                     column, base_column)
441                                 base_column = column
442                     elif line.startswith("widths="):
443                         line = line[len("widths="):].rstrip("\n")
444                         columns = self.treeview.get_columns()
445                         for i, width in enumerate(line.split(",")):
446                             try:
447                                 width = int(width)
448                             except:
449                                 pass
450                             else:
451                                 if i < len(columns):
452                                     columns[i].set_fixed_width(width)
453                     elif line.startswith("sort_column="):
454                         kolumn_name = line[len("sort_column="):].rstrip("\n")
455                         if kolumn_name in ThreadListModel.column_names:
456                             sort_column_name = kolumn_name
457                     elif line.startswith("sort_reverse="):
458                         reverse = line[len("sort_reverse="):].rstrip("\n")
459                         sort_reverse = reverse == "True"
460                     elif line.startswith("window_height="):
461                         height = window_height
462                         try:
463                             height = int(
464                                 line[len("window_height="):].rstrip("\n"))
465                         except:
466                             pass
467                         else:
468                             window_height = height
469                     elif line.startswith("window_width="):
470                         width = window_width
471                         try:
472                             width = int(
473                                 line[len("window_width="):].rstrip("\n"))
474                         except:
475                             pass
476                         else:
477                             window_width = width
478                     elif line.startswith("toolbar_visible="):
479                         tbar = line[len("toolbar_visible="):].rstrip("\n")
480                         toolbar_visible = tbar == "True"
481                     elif line.startswith("statusbar_visible="):
482                         sbar = line[len("statusbar_visible="):].rstrip("\n")
483                         statusbar_visible = sbar == "True"
484                     elif line.startswith("filterbar_visible="):
485                         fbar = line[len("filterbar_visible="):].rstrip("\n")
486                         filterbar_visible = fbar == "True"
487
488                 self.treeview.get_model().sort(
489                     sort_column_name, True, sort_reverse)
490
491             self.window.set_default_size(window_width, window_height)
492
493             if not toolbar_visible:
494                 gobject.idle_add(self.toolbar.parent.hide,
495                                  priority=gobject.PRIORITY_HIGH)
496             if not statusbar_visible:
497                 gobject.idle_add(self.statusbar.hide,
498                                  priority=gobject.PRIORITY_HIGH)
499             if not filterbar_visible:
500                 gobject.idle_add(self.filterbar.hide,
501                                  priority=gobject.PRIORITY_HIGH)
502         except:
503             traceback.print_exc()