OSDN Git Service

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