OSDN Git Service

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