import gobject
import gconf
import traceback
+import sys
+import itertools
import board_data
import uri_opener
import session
import winwrapbase
from misc import ThreadInvoker
+import bookmark_list
+import bookmark_window
+import board_plugins
+import board_column
-GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
- "..", "data")
GLADE_FILENAME = "board_window.glade"
def open_board(uri, update=False):
if not uri:
raise ValueError, "parameter must not be empty"
+ bbs_type = bbs_type_judge_uri.get_type(uri)
+ uri = bbs_type.get_uri_base() # use strict uri
+
winwrap = session.get_window(uri)
if winwrap:
# already opened
if update:
winwrap.load(update)
else:
- winwrap = WinWrap(uri)
+ winwrap = WinWrap(bbs_type.uri) # pass the original uri
winwrap.load(update)
self.bbs_type = bbs_type_judge_uri.get_type(uri)
board_data.BoardData.__init__(self, self.bbs_type)
-
- glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
+
+ glade_path = os.path.join(config.glade_dir, GLADE_FILENAME)
self.widget_tree = gtk.glade.XML(glade_path)
+ self._get_widgets()
- self.window = self.widget_tree.get_widget("board_window")
+ self.widget_tree.signal_autoconnect(self)
self.window.set_title(self.bbs_type.uri)
-
- self.treeview = self.widget_tree.get_widget("treeview")
self.treeview.set_model(ThreadListModel())
-
- self.popupmenu = self.widget_tree.get_widget("popup_menu")
- self.toolbar = self.widget_tree.get_widget("toolbar")
self.toolbar.unset_style()
- self.statusbar = self.widget_tree.get_widget("appbar")
- self.filterbar = self.widget_tree.get_widget("bonobodockitem_filterbar")
- self.entry_filterbar = self.widget_tree.get_widget("entry_filterbar")
renderer = gtk.CellRendererText()
- self.treeviewcolumn = {}
- for i in range(1, len(ThreadListModel.column_names)):
- column_name = ThreadListModel.column_names[i]
- self.treeviewcolumn[column_name] = gtk.TreeViewColumn(
- column_name, renderer)
- self.treeviewcolumn[column_name].set_resizable(True)
- self.treeviewcolumn[column_name].set_reorderable(True)
- self.treeviewcolumn[column_name].set_clickable(True)
- self.treeviewcolumn[column_name].set_cell_data_func(
- renderer, self.on_cell_data, column_name)
- self.treeviewcolumn[column_name].connect(
- "clicked", self.on_column_clicked, column_name)
- self.treeviewcolumn[column_name].set_sizing(
- gtk.TREE_VIEW_COLUMN_FIXED)
- self.treeviewcolumn[column_name].set_min_width(20)
- self.treeviewcolumn[column_name].set_fixed_width(
- ThreadListModel.column_width[i])
- self.treeview.append_column(self.treeviewcolumn[column_name])
-
- self.treeviewcolumn["lastModified"].set_cell_data_func(
- renderer, self.on_data_lastmodified)
+ for column_class in board_column.tree_view_column_list:
+ column_class(renderer, self.treeview)
self.treeview.set_fixed_height_mode(True)
- sigdic = {"on_board_window_destroy": self.on_board_window_destroy,
- "on_quit_activate": self.on_quit_activate,
- "on_refresh_activate": self.on_refresh_activate,
- "on_treeview_row_activated":
- lambda w,p,v: self.on_open_thread(w),
- "on_treeview_button_press_event":
- self.on_treeview_button_press_event,
- "on_close_activate":
- self.on_close_activate,
- "on_toolbar_activate": self.on_toolbar_activate,
- "on_statusbar_activate": self.on_statusbar_activate,
- "on_board_window_delete_event":
- self.on_board_window_delete_event,
- "on_entry_filterbar_activate": self.on_entry_filterbar_activate,
- "on_filter_activate": self.on_filter_activate,
- "on_toolbutton_filterbar_close_clicked":
- self.on_toolbutton_filterbar_close_clicked,
- "on_button_filterbar_clear_clicked":
- self.on_button_filterbar_clear_clicked,
- "on_delete_activate": self.on_delete_activate,
- "on_popup_menu_open_activate": self.on_open_thread}
- self.widget_tree.signal_autoconnect(sigdic)
+ # menu plugins
+ board_plugins.load(self.treeview, self.menu_edit)
self.restore()
self.window.show()
self.created()
+ def _get_widgets(self):
+ self.window = self.widget_tree.get_widget("window_board")
+ self.treeview = self.widget_tree.get_widget("treeview")
+ self.popupmenu = self.widget_tree.get_widget("popup_treeview_menu")
+ self.toolbar = self.widget_tree.get_widget("toolbar")
+ self.statusbar = self.widget_tree.get_widget("appbar")
+ self.filterbar = self.widget_tree.get_widget(
+ "bonobodockitem_filterbar")
+ self.entry_filterbar = self.widget_tree.get_widget("entry_filterbar")
+ self.menu_edit = self.widget_tree.get_widget("menu_edit").get_submenu()
+
def set_status(self, text):
self.statusbar.set_status(text)
def get_uri(self):
return self.bbs_type.get_uri_base()
- def on_toolbar_activate(self, widget):
- if self.toolbar.parent.get_property("visible"):
- self.toolbar.parent.hide()
- else:
- self.toolbar.parent.show()
-
- def on_statusbar_activate(self, widget):
- if self.statusbar.get_property("visible"):
- self.statusbar.hide()
- else:
- self.statusbar.show()
-
- def on_filter_activate(self, widget):
- self.filterbar.show()
- self.entry_filterbar.grab_focus()
-
- def on_entry_filterbar_activate(self, widget):
- text = widget.get_text()
-
- def func(model, item):
- try:
- item["title"].index(text)
- except ValueError:
- return False
- else:
- return True
-
- model = self.treeview.get_model()
- if model:
- if text:
- model.set_filter_func(func)
- else:
- model.set_filter_func(None)
- model.refilter()
-
- def on_toolbutton_filterbar_close_clicked(self, widget):
- self.filterbar.hide()
-
- def on_button_filterbar_clear_clicked(self, widget):
- self.entry_filterbar.set_text("")
- model = self.treeview.get_model()
- if model:
- model.set_filter_func(None)
- model.refilter()
-
- def updated_thread_highlight(self, column, cell, model, iter):
-
- def is_updated_thread():
- res = model.get_value(
- iter, ThreadListModel.column_names.index("res"))
- linecount = model.get_value(
- iter, ThreadListModel.column_names.index("lineCount"))
- return res != 0 and linecount != 0 and res > linecount
-
- if is_updated_thread():
- cell.set_property("weight", 800)
- else:
- cell.set_property("weight", 400)
-
- def on_cell_data(self, column, cell, model, iter, column_name):
- self.updated_thread_highlight(column, cell, model, iter)
- column_num = ThreadListModel.column_names.index(column_name)
- value = model.get_value(iter, column_num)
- if model.get_column_type(column_num) \
- in (gobject.TYPE_INT, gobject.TYPE_DOUBLE):
- if value == 0:
- cell.set_property("text", "")
- else:
- cell.set_property("text", str(value))
- else:
- cell.set_property("text", value)
-
- def on_data_lastmodified(self, column, cell, model, iter, user_data=None):
- self.updated_thread_highlight(column, cell, model, iter)
- lastmod = model.get_value(
- iter, ThreadListModel.column_names.index("lastModified"))
- if lastmod == 0:
- cell.set_property("text", "")
- else:
- cell.set_property("text", time.strftime(
- "%Y/%m/%d(%a) %H:%M:%S", time.localtime(lastmod)))
-
- def on_board_window_delete_event(self, widget, event):
- self.save()
- return False
-
- def on_board_window_destroy(self, widget):
- self.destroyed()
-
- def on_quit_activate(self, widget):
- session.main_quit()
-
- def on_close_activate(self, widget):
- self.destroy()
-
- def on_refresh_activate(self, widget):
- self.load(True)
-
- def on_column_clicked(self, treeviewcolumn, column_name):
- model = self.treeview.get_model()
- if model:
- model.sort(column_name)
- self.reset_sort_indicator()
-
- def reset_sort_indicator(self):
- model = self.treeview.get_model()
- if model:
- sort_column_name, sort_reverse = model.get_sort()
- for name,column in self.treeviewcolumn.iteritems():
- column.set_sort_indicator(False)
- if sort_column_name != "num" or sort_reverse:
- self.treeviewcolumn[sort_column_name].set_sort_indicator(True)
- if sort_reverse:
- self.treeviewcolumn[sort_column_name].set_sort_order(
- gtk.SORT_DESCENDING)
- else:
- self.treeviewcolumn[sort_column_name].set_sort_order(
- gtk.SORT_ASCENDING)
-
- def on_open_thread(self, widget):
+ def open_thread(self):
treeselection = self.treeview.get_selection()
model, iter = treeselection.get_selected()
if not iter:
bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
uri_opener.open_uri(bbs_type_for_thread.get_thread_uri(), update)
- def on_delete_activate(self, widget):
- selection = self.treeview.get_selection()
- model, iter = selection.get_selected()
- if not iter:
- return
- thread = model.get_value(
- iter, ThreadListModel.column_names.index("id"))
-
- bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
-
- dat_path = misc.get_thread_dat_path(bbs_type_for_thread)
- try:
- os.remove(dat_path)
- except OSError:
- traceback.print_exc()
- idx_path = misc.get_thread_idx_path(bbs_type_for_thread)
- try:
- os.remove(idx_path)
- except OSError:
- traceback.print_exc()
- states_path = misc.get_thread_states_path(bbs_type_for_thread)
- try:
- os.remove(states_path)
- except OSError:
- traceback.print_exc()
-
- def on_treeview_button_press_event(self, widget, event):
- if event.button == 3:
- x = int(event.x)
- y = int(event.y)
- time = event.time
- pthinfo = widget.get_path_at_pos(x, y)
- if pthinfo is not None:
- path, col, cellx, celly = pthinfo
- widget.grab_focus()
- widget.set_cursor(path, col, 0)
- self.popupmenu.popup(None, None, None, event.button, time)
- return 1
-
- def update_datastore(self, datalist):
+ def update_datastore(self, new_list):
print "reflesh datastore"
- list_list = []
- for id, dic in datalist.iteritems():
- dic["id"] = id
-
- # lastModified
- httpdate = dic["lastModified"]
- try:
- secs = misc.httpdate_to_secs(httpdate)
- dic["lastModified"] = secs
- except ValueError:
- dic["lastModified"] = 0
-
- list_list.append(dic)
-
- model = self.treeview.get_model()
- model.set_list(list_list)
+ self.treeview.get_model().set_list(new_list)
# redraw visible area after set list to model
self.treeview.queue_draw()
- self.reset_sort_indicator()
-
print "end"
def on_thread_idx_updated(self, thread_uri, idx_dic):
def load(self, update=False):
+ def modify_dict(item_dict):
+ # lastModified, httpdate to second
+ httpdate = item_dict["lastModified"]
+ try:
+ secs = misc.httpdate_to_secs(httpdate)
+ except ValueError:
+ item_dict["lastModified"] = 0
+ else:
+ item_dict["lastModified"] = secs
+ return item_dict
+
+ def set_id(thread_id, item_dict):
+ item_dict["id"] = thread_id
+ return item_dict
+
+ def conv_dictdict_to_listdict(dictdict):
+ key_iter = dictdict.iterkeys()
+ value_iter = dictdict.itervalues()
+ iterable = itertools.imap(set_id, key_iter, value_iter)
+ iterable = itertools.imap(modify_dict, iterable)
+ return [item_dict for item_dict in iterable]
+
def load_local():
datalist = self.load_idxfiles()
self.merge_local_subjecttxt(datalist)
- gobject.idle_add(self.update_datastore, datalist)
+ new_list = conv_dictdict_to_listdict(datalist)
+ gobject.idle_add(self.update_datastore, new_list)
def get_remote():
print "start get subject.txt"
datalist = self.load_idxfiles()
self.merge_remote_subjecttxt(datalist)
- gobject.idle_add(self.update_datastore, datalist)
+ new_list = conv_dictdict_to_listdict(datalist)
+ gobject.idle_add(self.update_datastore, new_list)
sbj_path = misc.get_board_subjecttxt_path(self.bbs_type)
sbj_exists = os.path.exists(sbj_path)
order = ""
width = ""
for column in columns:
- for name, kolumn in self.treeviewcolumn.iteritems():
- if column == kolumn:
- if order:
- order += ","
- order += name
- if width:
- width += ","
- width += str(column.get_width())
- break
+ if order:
+ order += ","
+ order += column.id
+ if width:
+ width += ","
+ width += str(column.get_width())
+
sort_column_name, sort_reverse = \
self.treeview.get_model().get_sort()
sort_reverse = str(sort_reverse)
traceback.print_exc()
def restore(self):
- try:
- window_height = 600
- window_width = 600
- toolbar_visible = True
- statusbar_visible = True
- filterbar_visible = False
+ states_dict = dict([
+ ("columns", ()), ("widths", ()), ("sort_column", "num"),
+ ("sort_reverse", False), ("window_height", 600),
+ ("window_width", 600), ("toolbar_visible", True),
+ ("statusbar_visible", True), ("filterbar_visible", False)])
+
+ interest_list = [
+ "columns", "widths", "sort_column", "sort_reverse",
+ "window_height", "window_width", "toolbar_visible",
+ "statusbar_visible", "filterbar_visible"]
+
+ interest_type_list = (
+ (list, str), (list, int), (str, None), (bool, None), (int, None),
+ (int, None), (bool, None), (bool, None), (bool, None))
+
+ def key_value_generator():
+
+ def generate_pair():
+ states_path = misc.get_board_states_path(self.bbs_type)
+ try:
+ for line in file(states_path):
+ key_equal_value = line.rstrip()
+ try:
+ index = key_equal_value.index("=")
+ except ValueError:
+ pass
+ else:
+ key = key_equal_value[:index]
+ if key in interest_list:
+ value = key_equal_value[index+1:]
+ yield key, value
+ except IOError:
+ traceback.print_exc()
+
+ def to_int(value):
+ try:
+ return int(value)
+ except:
+ return 0
+
+ for key, value in generate_pair():
+ list_index = interest_list.index(key)
+ key_type, key_type_extra = interest_type_list[list_index]
+ if key_type == str:
+ yield key, value
+ elif key_type == int:
+ yield key, to_int(value)
+ elif key_type == bool:
+ value = value == "True"
+ yield key, value
+ elif key_type == list:
+ if key_type_extra == str:
+ yield key, value.split(",")
+ elif key_type_extra == int:
+ yield key, [i for i in itertools.imap(
+ to_int, value.split(","))]
+ else:
+ print key, "not supported",
+ key_type, key_type_extra, value
+ else:
+ print key, "not supported", key_type, value
+
+ def load_gconf():
+ key_base = config.gconf_app_key_base() + "/board_states"
+ gconf_client = gconf.client_get_default()
+ width = gconf_client.get_int(key_base + "/window_width")
+ height = gconf_client.get_int(key_base + "/window_height")
+ states_dict["toolbar_visible"] = gconf_client.get_bool(
+ key_base + "/toolbar")
+ states_dict["statusbar_visible"] = gconf_client.get_bool(
+ key_base + "/statusbar")
+ states_dict["filterbar_visible"] = gconf_client.get_bool(
+ key_base + "/filterbar")
+
+ if width != 0:
+ states_dict["window_width"] = width
+ if height != 0:
+ states_dict["window_height"] = height
+ try:
try:
- key_base = config.gconf_app_key_base() + "/board_states"
- gconf_client = gconf.client_get_default()
- width = gconf_client.get_int(key_base + "/window_width")
- height = gconf_client.get_int(key_base + "/window_height")
- toolbar_visible = gconf_client.get_bool(key_base + "/toolbar")
- statusbar_visible = gconf_client.get_bool(key_base + "/statusbar")
- filterbar_visible = gconf_client.get_bool(key_base + "/filterbar")
-
- if width != 0:
- window_width = width
- if height != 0:
- window_height = height
+ load_gconf()
except:
traceback.print_exc()
- states_path = misc.get_board_states_path(self.bbs_type)
- if os.path.exists(states_path):
- sort_column_name = "num"
- sort_reverse = False
- for line in file(states_path):
- if line.startswith("columns="):
- line = line[len("columns="):].rstrip("\n")
- base_column = None
- for name in line.split(","):
- if name in self.treeviewcolumn:
- column = self.treeviewcolumn[name]
- self.treeview.move_column_after(
- column, base_column)
- base_column = column
- elif line.startswith("widths="):
- line = line[len("widths="):].rstrip("\n")
- columns = self.treeview.get_columns()
- for i, width in enumerate(line.split(",")):
- try:
- width = int(width)
- except:
- pass
- else:
- if i < len(columns):
- columns[i].set_fixed_width(width)
- elif line.startswith("sort_column="):
- kolumn_name = line[len("sort_column="):].rstrip("\n")
- if kolumn_name in ThreadListModel.column_names:
- sort_column_name = kolumn_name
- elif line.startswith("sort_reverse="):
- reverse = line[len("sort_reverse="):].rstrip("\n")
- sort_reverse = reverse == "True"
- elif line.startswith("window_height="):
- height = window_height
- try:
- height = int(
- line[len("window_height="):].rstrip("\n"))
- except:
- pass
- else:
- window_height = height
- elif line.startswith("window_width="):
- width = window_width
- try:
- width = int(
- line[len("window_width="):].rstrip("\n"))
- except:
- pass
- else:
- window_width = width
- elif line.startswith("toolbar_visible="):
- tbar = line[len("toolbar_visible="):].rstrip("\n")
- toolbar_visible = tbar == "True"
- elif line.startswith("statusbar_visible="):
- sbar = line[len("statusbar_visible="):].rstrip("\n")
- statusbar_visible = sbar == "True"
- elif line.startswith("filterbar_visible="):
- fbar = line[len("filterbar_visible="):].rstrip("\n")
- filterbar_visible = fbar == "True"
-
- self.treeview.get_model().sort(
- sort_column_name, True, sort_reverse)
-
- self.window.set_default_size(window_width, window_height)
-
- if not toolbar_visible:
+ for key, value in key_value_generator():
+ states_dict[key] = value
+
+ self.treeview.get_model().sort(
+ states_dict["sort_column"], True, states_dict["sort_reverse"])
+
+ # set column order before set column width
+ treeviewcolumn = dict(
+ [(cln.id, cln) for cln in self.treeview.get_columns()])
+ base_column = None
+ for column_name in states_dict["columns"]:
+ if column_name in treeviewcolumn:
+ column = treeviewcolumn[column_name]
+ self.treeview.move_column_after(column, base_column)
+ base_column = column
+
+ # set column width afeter set column order
+ for width, column in itertools.izip(
+ states_dict["widths"], self.treeview.get_columns()):
+ if width:
+ column.set_fixed_width(width)
+
+ self.window.set_default_size(
+ states_dict["window_width"], states_dict["window_height"])
+
+ if not states_dict["toolbar_visible"]:
gobject.idle_add(self.toolbar.parent.hide,
priority=gobject.PRIORITY_HIGH)
- if not statusbar_visible:
+ if not states_dict["statusbar_visible"]:
gobject.idle_add(self.statusbar.hide,
priority=gobject.PRIORITY_HIGH)
- if not filterbar_visible:
+ if not states_dict["filterbar_visible"]:
gobject.idle_add(self.filterbar.hide,
priority=gobject.PRIORITY_HIGH)
except:
traceback.print_exc()
+
+ def on_menu_toolbar_activate(self, widget):
+ if self.toolbar.parent.get_property("visible"):
+ self.toolbar.parent.hide()
+ else:
+ self.toolbar.parent.show()
+
+ def on_menu_statusbar_activate(self, widget):
+ if self.statusbar.get_property("visible"):
+ self.statusbar.hide()
+ else:
+ self.statusbar.show()
+
+
+ # signal handlers
+
+ # menu and toolbutton
+
+ def on_menu_add_bookmark_activate(self, widget):
+ bookmark_list.bookmark_list.add_bookmark_with_edit(
+ uri=self.bbs_type.uri)
+
+ def on_menu_close_activate(self, widget):
+ self.destroy()
+
+ def on_menu_delete_activate(self, widget):
+ selection = self.treeview.get_selection()
+ model, iter = selection.get_selected()
+ if not iter:
+ return
+ thread = model.get_value(
+ iter, ThreadListModel.column_names.index("id"))
+
+ bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
+
+ dat_path = misc.get_thread_dat_path(bbs_type_for_thread)
+ try:
+ os.remove(dat_path)
+ except OSError:
+ traceback.print_exc()
+ idx_path = misc.get_thread_idx_path(bbs_type_for_thread)
+ try:
+ os.remove(idx_path)
+ except OSError:
+ traceback.print_exc()
+ states_path = misc.get_thread_states_path(bbs_type_for_thread)
+ try:
+ os.remove(states_path)
+ except OSError:
+ traceback.print_exc()
+
+ def on_menu_filter_activate(self, widget):
+ self.filterbar.show()
+ self.entry_filterbar.grab_focus()
+
+ def on_menu_manage_bookmarks_activate(self, widget):
+ bookmark_window.open()
+
+ def on_menu_open_activate(self, widget):
+ self.open_thread()
+
+ def on_menu_quit_activate(self, widget):
+ session.main_quit()
+
+ def on_menu_refresh_activate(self, widget):
+ self.load(True)
+
+
+ # window
+
+ def on_window_board_delete_event(self, widget, event):
+ self.save()
+ return False
+
+ def on_window_board_destroy(self, widget):
+ self.destroyed()
+
+
+ # treeview
+
+ def on_treeview_row_activated(self, widget, path, view_column):
+ self.open_thread()
+
+ def on_treeview_button_press_event(self, widget, event):
+ if event.button == 3:
+ x = int(event.x)
+ y = int(event.y)
+ time = event.time
+ pthinfo = widget.get_path_at_pos(x, y)
+ if pthinfo is not None:
+ path, col, cellx, celly = pthinfo
+ widget.grab_focus()
+ widget.set_cursor(path, col, 0)
+ self.popupmenu.popup(None, None, None, event.button, time)
+ return 1
+
+
+ # filterbar
+
+ def on_entry_filterbar_activate(self, widget):
+ text = widget.get_text()
+
+ def func(model, item):
+ try:
+ item["title"].index(text)
+ except ValueError:
+ return False
+ else:
+ return True
+
+ model = self.treeview.get_model()
+ if model:
+ if text:
+ filter_func = func
+ else:
+ filter_func = None
+ model.refilter(filter_func)
+
+ def on_toolbutton_filterbar_close_clicked(self, widget):
+ self.filterbar.hide()
+
+ def on_button_filterbar_clear_clicked(self, widget):
+ self.entry_filterbar.set_text("")
+ model = self.treeview.get_model()
+ if model:
+ model.refilter(None)