OSDN Git Service

Enable to get dictionaries in the data store.
[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 import sys
28 import itertools
29
30 import board_data
31 import uri_opener
32 import misc
33 from threadlistmodel import ThreadListModel
34 from BbsType import bbs_type_judge_uri
35 import config
36 import session
37 import winwrapbase
38 from misc import ThreadInvoker
39 import bookmark_list
40 import bookmark_window
41 import board_plugins
42 import board_column
43 import board_states
44
45 GLADE_FILENAME = "board_window.glade"
46
47 def open_board(uri, update=False):
48     if not uri:
49         raise ValueError, "parameter must not be empty"
50
51     bbs_type = bbs_type_judge_uri.get_type(uri)
52     uri = bbs_type.get_uri_base()  # use strict uri
53
54     winwrap = session.get_window(uri)
55     if winwrap:
56         # already opened
57         winwrap.window.present()
58         if update:
59             winwrap.load(update)
60     else:
61         winwrap = WinWrap(bbs_type.uri)  # pass the original uri
62         winwrap.load(update)
63
64
65 class WinWrap(winwrapbase.WinWrapBase, board_data.BoardData):
66
67     def __init__(self, uri):
68
69         self.bbs_type = bbs_type_judge_uri.get_type(uri)
70         board_data.BoardData.__init__(self, self.bbs_type)
71
72         glade_path = os.path.join(config.glade_dir, GLADE_FILENAME)
73         self.widget_tree = gtk.glade.XML(glade_path)
74         self._get_widgets()
75
76         self.widget_tree.signal_autoconnect(self)
77
78         self.window.set_title(self.bbs_type.uri)
79         self.treeview.set_model(ThreadListModel())
80         self.toolbar.unset_style()
81
82         renderer = gtk.CellRendererText()
83
84         for column_class in board_column.tree_view_column_list:
85             column_class(renderer, self.treeview)
86
87         self.treeview.set_fixed_height_mode(True)
88
89         # menu plugins
90         board_plugins.load(self.widget_tree)
91
92         self.restore()
93         self.window.show()
94
95         self.created()
96
97     def _get_widgets(self):
98         self.window = self.widget_tree.get_widget("window_board")
99         self.treeview = self.widget_tree.get_widget("treeview")
100         self.popupmenu = self.widget_tree.get_widget("popup_treeview_menu")
101         self.toolbar = self.widget_tree.get_widget("toolbar")
102         self.statusbar = self.widget_tree.get_widget("appbar")
103         self.progress = self.statusbar.get_progress()
104         self.filterbar = self.widget_tree.get_widget(
105             "bonobodockitem_filterbar")
106         self.entry_filterbar = self.widget_tree.get_widget("entry_filterbar")
107
108     def set_status(self, text):
109         self.statusbar.set_status(text)
110
111     def set_fraction(self, fraction):
112         self.progress.set_fraction(fraction)
113
114     def destroy(self):
115         self.save()
116         self.window.destroy()
117
118     def get_uri(self):
119         return self.bbs_type.get_uri_base()
120
121     def open_thread(self):
122         treeselection = self.treeview.get_selection()
123         model, iter = treeselection.get_selected()
124         if not iter:
125             return
126
127         dic = model.get_dict(iter)
128         thread = dic["id"]
129         title = dic["title"]
130         print thread + ':"' + title + '"', "activated"
131
132         res = dic["res"]
133         lineCount = dic["lineCount"]
134
135         update = res > lineCount
136
137         bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
138         uri_opener.open_uri(bbs_type_for_thread.get_thread_uri(), update)
139
140     def update_datastore(self, new_list):
141         print "reflesh datastore"
142
143         self.treeview.get_model().set_list(new_list)
144
145         # redraw visible area after set list to model
146         self.treeview.queue_draw()
147
148         print "end"
149
150     def on_thread_idx_updated(self, thread_uri, idx_dic):
151         if not thread_uri or not idx_dic:
152             return
153
154         # nothing to do if thread_uri does not belong to this board.
155         bbs_type = bbs_type_judge_uri.get_type(thread_uri)
156         if not bbs_type.is_thread() \
157                or not bbs_type.is_same_board(self.bbs_type):
158             return
159
160         thread = bbs_type.thread
161
162         model = self.treeview.get_model()
163         if model:
164             idx_dic["id"] = thread
165             try:
166                 idx_dic["lastModified"] =  misc.httpdate_to_secs(
167                     idx_dic["lastModified"])
168             except ValueError:
169                 idx_dic["lastModified"] = 0
170             model.modify_row(idx_dic)
171
172     def load(self, update=False):
173
174         def set_id(thread_id, item_dict):
175             item_dict["id"] = thread_id
176             return item_dict
177
178         def conv_dictdict_to_listdict(dictdict):
179             key_iter = dictdict.iterkeys()
180             value_iter = dictdict.itervalues()
181             iterable = itertools.imap(set_id, key_iter, value_iter)
182             return [item_dict for item_dict in iterable]
183
184         def load_local():
185             datalist = self.load_idxfiles()
186             self.merge_local_subjecttxt(datalist)
187             new_list = conv_dictdict_to_listdict(datalist)
188             gobject.idle_add(self.update_datastore, new_list)
189
190         def get_remote(datalist):
191             print "start get subject.txt"
192             self.merge_remote_subjecttxt(datalist)
193             new_list = conv_dictdict_to_listdict(datalist)
194             gobject.idle_add(self.update_datastore, new_list)
195
196         def on_end():
197             def reset_progress():
198                 self.progress.set_fraction(0.0)
199                 self.progress.hide()
200             gobject.idle_add(reset_progress)
201
202         def deep_copy():
203             def init_some(dic):
204                 dic["num"] = 0
205                 dic["res"] = 0
206                 dic["average"] = 0
207                 return dic
208
209             model = self.treeview.get_model()
210             if model:
211                 iterable = model.original_list
212
213                 # unmerge subject.txt
214                 iterable = itertools.ifilter(
215                     lambda dic: dic["lineCount"] > 0, iterable)
216                 iterable = itertools.imap(lambda dic: dic.copy(), iterable)
217                 iterable = itertools.imap(init_some, iterable)
218
219                 new_dict = dict([(dic["id"], dic) for dic in iterable])
220             else:
221                 new_dict = {}
222             return new_dict
223
224         sbj_path = misc.get_board_subjecttxt_path(self.bbs_type)
225         sbj_exists = os.path.exists(sbj_path)
226
227         self.progress.show()
228
229         if update or not sbj_exists:
230             new_dict = deep_copy()
231             t = ThreadInvoker(on_end, lambda *x: get_remote(new_dict))
232             t.start()
233         else:
234             t = ThreadInvoker(on_end, load_local)
235             t.start()
236
237     def save(self):
238         try:
239             states_path = misc.get_board_states_path(self.bbs_type)
240             dirname = os.path.dirname(states_path)
241
242             # save only if board dir exists.
243             if os.path.exists(dirname):
244                 window_width, window_height = self.window.get_size()
245                 toolbar_visible = self.toolbar.parent.get_property("visible")
246                 statusbar_visible = self.statusbar.get_property("visible")
247                 filterbar_visible = self.filterbar.get_property("visible")
248
249                 columns = self.treeview.get_columns()
250                 order = ""
251                 width = ""
252                 for column in columns:
253                     if order:
254                         order += ","
255                     order += column.id
256                     if width:
257                         width += ","
258                     width += str(column.get_width())
259
260                 sort_column_name, sort_reverse = \
261                                   self.treeview.get_model().get_sort()
262                 sort_reverse = str(sort_reverse)
263
264                 f = file(states_path, "w")
265
266                 f.write("columns=" + order + "\n")
267                 f.write("widths=" + width + "\n")
268                 f.write("sort_column=" + sort_column_name + "\n")
269                 f.write("sort_reverse=" + sort_reverse + "\n")
270                 f.write("window_width=" + str(window_width) + "\n")
271                 f.write("window_height=" + str(window_height) + "\n")
272                 f.write("toolbar_visible=" + str(toolbar_visible) + "\n")
273                 f.write("statusbar_visible=" + str(statusbar_visible) + "\n")
274                 f.write("filterbar_visible=" + str(filterbar_visible) + "\n")
275
276                 f.close()
277         except:
278             traceback.print_exc()
279
280     def restore(self):
281         try:
282             states_dict = board_states.states_file_to_dict(self.bbs_type)
283
284             self.treeview.get_model().sort(
285                 states_dict["sort_column"], True, states_dict["sort_reverse"])
286
287             # set column order before set column width
288             treeviewcolumn = dict(
289                 [(cln.id, cln) for cln in self.treeview.get_columns()])
290             base_column = None
291             for column_name in states_dict["columns"]:
292                 if column_name in treeviewcolumn:
293                     column = treeviewcolumn[column_name]
294                     self.treeview.move_column_after(column, base_column)
295                     base_column = column
296
297             # set column width afeter set column order
298             for width, column in itertools.izip(
299                 states_dict["widths"], self.treeview.get_columns()):
300                 if width:
301                     column.set_fixed_width(width)
302
303             self.window.set_default_size(
304                 states_dict["window_width"], states_dict["window_height"])
305
306             if not states_dict["toolbar_visible"]:
307                 gobject.idle_add(self.toolbar.parent.hide,
308                                  priority=gobject.PRIORITY_HIGH)
309             if not states_dict["statusbar_visible"]:
310                 gobject.idle_add(self.statusbar.hide,
311                                  priority=gobject.PRIORITY_HIGH)
312             if not states_dict["filterbar_visible"]:
313                 gobject.idle_add(self.filterbar.hide,
314                                  priority=gobject.PRIORITY_HIGH)
315         except:
316             traceback.print_exc()
317
318     def on_menu_toolbar_activate(self, widget):
319         if self.toolbar.parent.get_property("visible"):
320             self.toolbar.parent.hide()
321         else:
322             self.toolbar.parent.show()
323
324     def on_menu_statusbar_activate(self, widget):
325         if self.statusbar.get_property("visible"):
326             self.statusbar.hide()
327         else:
328             self.statusbar.show()
329
330
331     # signal handlers
332
333     # menu and toolbutton
334
335     def on_menu_add_bookmark_activate(self, widget):
336         bookmark_list.bookmark_list.add_bookmark_with_edit(
337             uri=self.bbs_type.uri)
338
339     def on_menu_close_activate(self, widget):
340         self.destroy()
341
342     def on_menu_delete_activate(self, widget):
343         selection = self.treeview.get_selection()
344         model, iter = selection.get_selected()
345         if not iter:
346             return
347         thread = model.get_dict(iter)["id"]
348         
349         bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
350
351         dat_path = misc.get_thread_dat_path(bbs_type_for_thread)
352         try:
353             os.remove(dat_path)
354         except OSError:
355             traceback.print_exc()
356         idx_path = misc.get_thread_idx_path(bbs_type_for_thread)
357         try:
358             os.remove(idx_path)
359         except OSError:
360             traceback.print_exc()
361         states_path = misc.get_thread_states_path(bbs_type_for_thread)
362         try:
363             os.remove(states_path)
364         except OSError:
365             traceback.print_exc()
366
367     def on_menu_filter_activate(self, widget):
368         self.filterbar.show()
369         self.entry_filterbar.grab_focus()
370
371     def on_menu_manage_bookmarks_activate(self, widget):
372         bookmark_window.open()
373
374     def on_menu_open_activate(self, widget):
375         self.open_thread()
376
377     def on_menu_quit_activate(self, widget):
378         session.main_quit()
379
380     def on_menu_refresh_activate(self, widget):
381         self.load(True)
382
383
384     # window
385
386     def on_window_board_delete_event(self, widget, event):
387         self.save()
388         return False
389
390     def on_window_board_destroy(self, widget):
391         self.popupmenu.destroy()
392         for column in self.treeview.get_columns():
393             column.destroy()
394         self.destroyed()
395
396
397     # treeview
398
399     def on_treeview_row_activated(self, widget, path, view_column):
400         self.open_thread()
401
402     def on_treeview_button_press_event(self, widget, event):
403         if event.button == 3:
404             x = int(event.x)
405             y = int(event.y)
406             time = event.time
407             pthinfo = widget.get_path_at_pos(x, y)
408             if pthinfo is not None:
409                 path, col, cellx, celly = pthinfo
410                 widget.grab_focus()
411                 widget.set_cursor(path, col, 0)
412                 self.popupmenu.popup(None, None, None, event.button, time)
413             return 1
414
415
416     # filterbar
417
418     def on_entry_filterbar_activate(self, widget):
419         text = widget.get_text()
420
421         def func(model, item):
422             try:
423                 item["title"].index(text)
424             except ValueError:
425                 return False
426             else:
427                 return True
428
429         model = self.treeview.get_model()
430         if model:
431             if text:
432                 filter_func = func
433             else:
434                 filter_func = None
435             model.refilter(filter_func)
436
437     def on_toolbutton_filterbar_close_clicked(self, widget):
438         self.filterbar.hide()
439
440     def on_button_filterbar_clear_clicked(self, widget):
441         self.entry_filterbar.set_text("")
442         model = self.treeview.get_model()
443         if model:
444             model.refilter(None)