OSDN Git Service

Add bookmark.
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Fri, 15 Sep 2006 00:07:47 +0000 (09:07 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Fri, 15 Sep 2006 00:24:03 +0000 (09:24 +0900)
12 files changed:
src/FukuiNoNamari/board_window.py
src/FukuiNoNamari/bookmark_core.py [new file with mode: 0644]
src/FukuiNoNamari/bookmark_editwindow.py [new file with mode: 0644]
src/FukuiNoNamari/bookmark_list.py [new file with mode: 0644]
src/FukuiNoNamari/bookmark_window.py [new file with mode: 0644]
src/FukuiNoNamari/thread_window.py
src/FukuiNoNamari/uri_opener.py
src/data/board_window.glade
src/data/bookmark_editwindow.glade [new file with mode: 0644]
src/data/bookmark_window.glade [new file with mode: 0644]
src/data/thread_window.glade
src/fukui-no-namari

index 962d074..42097be 100644 (file)
@@ -34,6 +34,8 @@ import config
 import session
 import winwrapbase
 from misc import ThreadInvoker
 import session
 import winwrapbase
 from misc import ThreadInvoker
+import bookmark_list
+import bookmark_window
 
 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                          "..", "data")
 
 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                          "..", "data")
@@ -124,6 +126,9 @@ class WinWrap(winwrapbase.WinWrapBase, board_data.BoardData):
                   "on_button_filterbar_clear_clicked":
                   self.on_button_filterbar_clear_clicked,
                   "on_delete_activate": self.on_delete_activate,
                   "on_button_filterbar_clear_clicked":
                   self.on_button_filterbar_clear_clicked,
                   "on_delete_activate": self.on_delete_activate,
+                  "on_manage_bookmarks_activate": \
+                  self.on_manage_bookmarks_activate,
+                  "on_add_bookmark_activate": self.on_add_bookmark_activate,
                   "on_popup_menu_open_activate": self.on_open_thread}
         self.widget_tree.signal_autoconnect(sigdic)
 
                   "on_popup_menu_open_activate": self.on_open_thread}
         self.widget_tree.signal_autoconnect(sigdic)
 
@@ -281,6 +286,13 @@ class WinWrap(winwrapbase.WinWrapBase, board_data.BoardData):
         bbs_type_for_thread = self.bbs_type.clone_with_thread(thread)
         uri_opener.open_uri(bbs_type_for_thread.get_thread_uri(), update)
 
         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_add_bookmark_activate(self, widget):
+        bookmark_list.bookmark_list.add_bookmark_with_edit(
+            uri=self.bbs_type.uri)
+
+    def on_manage_bookmarks_activate(self, widget):
+        bookmark_window.open()
+
     def on_delete_activate(self, widget):
         selection = self.treeview.get_selection()
         model, iter = selection.get_selected()
     def on_delete_activate(self, widget):
         selection = self.treeview.get_selection()
         model, iter = selection.get_selected()
diff --git a/src/FukuiNoNamari/bookmark_core.py b/src/FukuiNoNamari/bookmark_core.py
new file mode 100644 (file)
index 0000000..a707048
--- /dev/null
@@ -0,0 +1,88 @@
+# Copyright (C) 2006 by Aiwota Programmer
+# aiwotaprog@tetteke.tk
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+class BookmarkFormatError(Exception):
+
+    def __init__(self, value):
+        self.value = value
+
+    def __str__(self):
+        return self.value
+
+
+class BaseBookmark(object):
+
+    def __init__(self, uri, name, *categories):
+        self.uri = uri
+        self.name = name
+        self.categories = [cat for cat in categories]
+
+
+class Bookmark(BaseBookmark):
+
+    def __init__(self, uri=None, name=None, categories=None, formatted=None):
+        if uri and name:
+            pass
+        elif formatted:
+            uri, name, categories = self._parse_text(formatted)
+        else:
+            assert(False), "uri and name, or formatted must be set"
+        if not uri:
+            raise BookmarkFormatError, "uri must not be empty"
+        if not name:
+            raise BookmarkFormatError, "name must not be empty"
+        super(Bookmark, self).__init__(uri, name, *categories)
+
+    def _parse_text(self, formatted):
+        formatted = formatted.rstrip("\n")
+        datalist = formatted.split("\t")
+
+        name = None
+        uri = None
+        categories = []
+
+        for keyvalue in datalist:
+            if keyvalue.startswith("name="):
+                name = keyvalue[len("name="):]
+            elif keyvalue.startswith("uri="):
+                uri = keyvalue[len("uri="):]
+            elif keyvalue.startswith("category="):
+                category = keyvalue[len("category="):]
+                if category and category not in categories:
+                    categories.append(category)
+
+        return uri, name, categories
+
+    def tabbed_text(self):
+        def append_tab(text):
+            if text:
+                text += "\t"
+            return text
+
+        text = ""
+        if self.name:
+            text = append_tab(text)
+            text += "name=%s" % self.name
+        if self.uri:
+            text = append_tab(text)
+            text += "uri=%s" % self.uri
+        for category in self.categories:
+            if category:
+                text = append_tab(text)
+                text += "category=%s" % category
+        return text
diff --git a/src/FukuiNoNamari/bookmark_editwindow.py b/src/FukuiNoNamari/bookmark_editwindow.py
new file mode 100644 (file)
index 0000000..95e0bc4
--- /dev/null
@@ -0,0 +1,126 @@
+# Copyright (C) 2006 by Aiwota Programmer
+# aiwotaprog@tetteke.tk
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import pygtk
+pygtk.require("2.0")
+import gtk
+import gtk.glade
+import os.path
+
+from bookmark_core import Bookmark
+
+GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                         "..", "data")
+GLADE_FILENAME = "bookmark_editwindow.glade"
+
+
+class BookmarkEditWindow:
+
+    def __init__(self, on_newbookmark_complete, bookmark=None):
+        glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
+        self.widget_tree = gtk.glade.XML(glade_path)
+        self.widget_tree.signal_autoconnect(self)
+        self.window = self.widget_tree.get_widget("window_editbookmark")
+        self.entry_name = self.widget_tree.get_widget("entry_name")
+        self.entry_uri = self.widget_tree.get_widget("entry_uri")
+        self.button_ok = self.widget_tree.get_widget("button_ok")
+        self.button_cancel = self.widget_tree.get_widget("button_cancel")
+        self.button_category_add = self.widget_tree.get_widget(
+            "button_category_add")
+        self.button_category_edit = self.widget_tree.get_widget(
+            "button_category_edit")
+        self.button_category_delete = self.widget_tree.get_widget(
+            "button_category_delete")
+        self.treeview_categories = self.widget_tree.get_widget(
+            "treeview_categories")
+
+        cell = gtk.CellRendererText()
+        cell.set_property("editable", True)
+        cell.connect("edited", self.on_cell_edited)
+        treeviewcolumn = gtk.TreeViewColumn(None, cell, text=0)
+        treeviewcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        self.treeview_categories.append_column(treeviewcolumn)
+        self.treeview_categories.set_fixed_height_mode(True)
+        self.treeview_categories.set_model(gtk.ListStore(str))
+
+        self.on_newbookmark_complete = on_newbookmark_complete
+        self.bookmark = bookmark
+        if bookmark:
+            self.entry_name.set_text(bookmark.name)
+            self.entry_uri.set_text(bookmark.uri)
+            model = self.treeview_categories.get_model()
+            for category in bookmark.categories:
+                model.append([category])
+
+    def on_button_ok_clicked(self, widget):
+        bookmark_name = self.entry_name.get_text()
+        bookmark_uri = self.entry_uri.get_text()
+        if not bookmark_name or not bookmark_uri:
+            dialog = gtk.Dialog(
+                "Confirmation", self.window,
+                gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+            label = gtk.Label("NAME and URL must be set")
+            dialog.vbox.pack_start(label, True, True, 0)
+            label.show()
+            dialog.run()
+            dialog.destroy()
+        else:
+            bookmark_categories = []
+            for row in self.treeview_categories.get_model():
+                category = row.model.get_value(row.iter, 0)
+                if category and category not in bookmark_categories:
+                    bookmark_categories.append(category)
+            bookmark = Bookmark(uri=bookmark_uri, name=bookmark_name,
+                                categories=bookmark_categories)
+            self.on_newbookmark_complete(bookmark)
+            self.window.destroy()
+
+    def on_button_cancel_clicked(self, widget):
+        self.window.destroy()
+
+    def on_button_category_add_clicked(self, widget):
+        dialog = gtk.Dialog(
+            "Input Category Name", self.window,
+            gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+            (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+             gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+        dialog.set_default_response(gtk.RESPONSE_ACCEPT)
+        entry = gtk.Entry()
+        entry.set_property("activates-default", True)
+        dialog.vbox.pack_start(entry, True, True, 0)
+        entry.show()
+        response = dialog.run()
+        if response == gtk.RESPONSE_ACCEPT:
+            text = entry.get_text()
+            model = self.treeview_categories.get_model()
+            if model and text:
+                model.append([text])
+        dialog.destroy()
+
+    def on_button_category_delete_clicked(self, widget):
+        selection = self.treeview_categories.get_selection()
+        model, iter = selection.get_selected()
+        if iter:
+            model.remove(iter)
+
+    def on_cell_edited(self, cell, path, new_text):
+        model = self.treeview_categories.get_model()
+        if model:
+            iter = model.get_iter(path)
+            if iter:
+                model.set_value(iter, 0, new_text)
diff --git a/src/FukuiNoNamari/bookmark_list.py b/src/FukuiNoNamari/bookmark_list.py
new file mode 100644 (file)
index 0000000..de833ff
--- /dev/null
@@ -0,0 +1,192 @@
+# Copyright (C) 2006 by Aiwota Programmer
+# aiwotaprog@tetteke.tk
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import re
+import urllib
+import codecs
+import traceback
+import os
+import os.path
+
+from bookmark_core import BookmarkFormatError, Bookmark
+from bookmark_editwindow import BookmarkEditWindow
+
+bookmark_list = None
+
+def init(bookmarks_path, bbsmenu_uri):
+    global bookmark_list
+
+    bookmark_list = BookmarkList(bookmarks_path, bbsmenu_uri)
+    bookmark_list.load()
+
+
+class BookmarkList(object):
+
+    def __init__(self, bookmarks_path, bbsmenu_uri):
+        self.__bookmarks_path = bookmarks_path
+        self.__bbsmenu_uri = bbsmenu_uri
+        self.__bookmark_list = []
+        self.__view = None
+
+    def set_view(self, view):
+        self.__view = view
+
+    def load(self):
+        bookmark_list = []
+        try:
+            for line in file(self.__bookmarks_path):
+                line = line.rstrip("\n")
+                try:
+                    bookmark = Bookmark(formatted=line)
+                except BookmarkFormatError:
+                    traceback.print_exc()
+                else:
+                    bookmark_list.append(bookmark)
+        except IOError:
+            traceback.print_exc()
+        else:
+            self.__bookmark_list = bookmark_list
+
+    def save(self):
+        try:
+            basedir = os.path.dirname(self.__bookmarks_path)
+            if not os.path.isdir(basedir):
+                os.makedirs(basedir)
+            f = file(self.__bookmarks_path, "w")
+            for bookmark in self.__bookmark_list:
+                line = bookmark.tabbed_text() + "\n"
+                f.write(line)
+        except:
+            traceback.print_exc()
+        else:
+            f.close()
+
+    def update_bbsmenu(self):
+        reg = re.compile(
+            "<A HREF=(?P<uri>http://[^\s]+).*>(?P<name>[^<]+)</A>")
+        category_reg = re.compile(
+            "(?:<BR>){1,2}<B>(?P<category>[^<]+)</B><BR>")
+
+        new_bookmark_list = []
+        bookmark_uri_dict = {}
+        encoding = "cp932"
+
+        current_category = ""
+        try:
+            for line in urllib.urlopen(self.__bbsmenu_uri):
+                line = line.decode(encoding, "replace").strip()
+                if line:
+                    if not current_category:
+                        m = category_reg.match(line)
+                        if m:
+                            current_category = m.group("category")
+                            continue
+                    m = reg.match(line)
+                    if m:
+                        name = m.group("name")
+                        uri = m.group("uri")
+                        category = current_category
+                        if uri and uri in bookmark_uri_dict:
+                            bookmark = bookmark_uri_dict[uri]
+                            if category and \
+                               category not in bookmark.categories:
+                                bookmark.categories.append(category)
+                        elif name and uri:
+                            categories = ["bbsmenu"]
+                            if category:
+                                categories.append(category)
+                            bookmark = Bookmark(uri=uri, name=name,
+                                                categories=categories)
+                            new_bookmark_list.append(bookmark)
+                            bookmark_uri_dict[uri] = bookmark
+                else:
+                    current_category = ""
+        except:
+            traceback.print_exc()
+        else:
+            self._merge_bbsmenu(new_bookmark_list)
+
+    def _merge_bbsmenu(self, new_bbsmenu_list):
+        old_list = []
+        for bookmark in self.__bookmark_list:
+            if "bbsmenu" not in bookmark.categories:
+                old_list.append(bookmark)
+        new_bbsmenu_list += old_list
+        self.__bookmark_list = new_bbsmenu_list
+        self.save()
+        category_list = []
+        for bookmark in self.__bookmark_list:
+            for category in bookmark.categories:
+                if category and category not in category_list:
+                    category_list.append(category)
+
+    def get_category_list(self):
+        categories = []
+        for bookmark in self.__bookmark_list:
+            for category in bookmark.categories:
+                if category and category not in categories:
+                    # avoid empty and duplicate
+                    categories.append(category)
+        return categories
+
+    def get_bookmark_list_in_category(self, category):
+        bookmarks = []
+        for bookmark in self.__bookmark_list:
+            if category in bookmark.categories:
+                bookmarks.append(bookmark)
+        return bookmarks
+
+    def get_non_category_bookmark(self):
+        bookmarks = []
+        for bookmark in self.__bookmark_list:
+            have_category = False
+            for category in bookmark.categories:
+                if category:
+                    have_category = True
+                    break
+            if not have_category:
+                bookmarks.append(bookmark)
+        return bookmarks
+
+    def add_new_bookmark(self, bookmark):
+        self.__bookmark_list.append(bookmark)
+        self.save()
+
+    def delete_bookmark(self, bookmark):
+        try:
+            index = self.__bookmark_list.index(bookmark)
+        except ValueError:
+            pass
+        else:
+            del self.__bookmark_list[index]
+            self.save()
+
+    def add_bookmark_with_edit(self, name="New Bookmark", uri=None,
+                               categories=None):
+
+        def on_edit_complete(new_bookmark):
+            self.add_new_bookmark(new_bookmark)
+            self.new_bookmark_added()
+
+        if not categories:
+            categories = []
+        bookmark = Bookmark(name=name, uri=uri, categories=categories)
+        BookmarkEditWindow(on_edit_complete, bookmark)
+
+    def new_bookmark_added(self):
+        if self.__view:
+            self.__view.update_categories()
diff --git a/src/FukuiNoNamari/bookmark_window.py b/src/FukuiNoNamari/bookmark_window.py
new file mode 100644 (file)
index 0000000..77f52ef
--- /dev/null
@@ -0,0 +1,284 @@
+# Copyright (C) 2006 by Aiwota Programmer
+# aiwotaprog@tetteke.tk
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+import pygtk
+pygtk.require("2.0")
+import gtk
+import gtk.glade
+import traceback
+import gnome
+import os.path
+import itertools
+
+from bookmark_core import Bookmark
+from bookmark_editwindow import BookmarkEditWindow
+from BbsType import bbs_type_exception
+import bookmark_list
+import uri_opener
+import winwrapbase
+import session
+
+GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                         "..", "data")
+GLADE_FILENAME = "bookmark_window.glade"
+
+def open():
+    winwrap = session.get_window(WinWrap.uri)
+    if winwrap:
+        # already opened
+        winwrap.window.present()
+    else:
+        winwrap = WinWrap()
+
+
+class WinWrap(winwrapbase.WinWrapBase):
+
+    uri = "namari://bookmarks-manager"
+
+    def __init__(self):
+        self.bookmarklist = bookmark_list.bookmark_list
+        self.bookmarklist.set_view(self)
+
+        glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
+        self.widget_tree = gtk.glade.XML(glade_path)
+        self.window = self.widget_tree.get_widget("window_bookmark")
+        self.window.connect("destroy", self.on_destroy)
+        self.treeview_categories = self.widget_tree.get_widget(
+            "treeview_categories")
+        self.treeview_categories.connect(
+            "cursor-changed", self.on_treeview_categories_cursor_changed)
+        self.treeview_bookmarks = self.widget_tree.get_widget(
+            "treeview_bookmarks")
+        self.treeview_bookmarks.connect(
+            "row-activated", self.on_treeview_bookmarks_row_activated)
+        self.treeview_bookmarks.connect(
+            "cursor-changed", self.on_treeview_bookmarks_cursor_changed)
+        self.treeview_bookmarks.connect(
+            "button-press-event",
+            self.on_treeview_bookmarks_button_press_event)
+        self.statusbar = self.widget_tree.get_widget("statusbar")
+        self.statusbar.push(
+            self.statusbar.get_context_id("bookmarks"), "Ready.")
+        self.menu_bookmark = self.widget_tree.get_widget("menu_bookmark")
+
+        self.widget_tree.signal_autoconnect(self)
+
+        renderer = gtk.CellRendererText()
+
+        treeviewcolumn = gtk.TreeViewColumn("category", renderer, text=0)
+        treeviewcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        self.treeview_categories.append_column(treeviewcolumn)
+
+        treeviewcolumn = gtk.TreeViewColumn("bookmark", renderer, text=0)
+        treeviewcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+        self.treeview_bookmarks.append_column(treeviewcolumn)
+
+        self.treeview_categories.set_fixed_height_mode(True)
+        self.treeview_bookmarks.set_fixed_height_mode(True)
+
+        self.treeview_categories.set_model(gtk.ListStore(str, int))
+        self.treeview_bookmarks.set_model(gtk.ListStore(str, object))
+
+        self.current_category = ""
+        self.current_category_type = 0
+
+        self.update_categories()
+
+        self.created()
+
+    def on_destroy(self, widget):
+        self.destroyed()
+
+    def get_uri(self):
+        return self.uri
+
+    def destroyed(self):
+        self.bookmarklist.set_view(None)
+        winwrapbase.WinWrapBase.destroyed(self)
+
+    def destroy(self):
+        self.window.destroy()
+
+    def on_treeview_categories_cursor_changed(self, widget):
+        selection = self.treeview_categories.get_selection()
+        model, iter = selection.get_selected()
+        if model and iter:
+            category = model.get_value(iter, 0)
+            category_type = model.get_value(iter, 1)
+            if category and category != self.current_category:
+                self.update_bookmarks(category, category_type)
+
+    def _get_selected_bookmark(self):
+        selection = self.treeview_bookmarks.get_selection()
+        model, iter = selection.get_selected()
+        if model and iter:
+            return model.get_value(iter, 1)
+
+    def on_treeview_bookmarks_cursor_changed(self, widget):
+        bookmark = self._get_selected_bookmark()
+        if bookmark:
+            self.statusbar.pop(self.statusbar.get_context_id("bookmarks"))
+            self.statusbar.push(
+                self.statusbar.get_context_id("bookmarks"), bookmark.uri)
+
+    def on_treeview_bookmarks_row_activated(self, widget, path, column):
+        model = self.treeview_bookmarks.get_model()
+        if model:
+            iter = model.get_iter(path)
+            if iter:
+                self._open_bookmark(model.get_value(iter, 1))
+
+    def on_treeview_bookmarks_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.menu_bookmark.popup(None, None, None, event.button, time)
+            return True
+
+    def on_update_bbsmenu(self, widget):
+        self.bookmarklist.update_bbsmenu()
+        self.update_categories()
+
+    def _open_bookmark(self, bookmark):
+        try:
+            uri_opener.open_uri(bookmark.uri)
+        except bbs_type_exception.BbsTypeError:
+            self._open_bookmark_with_web_browser(bookmark)
+
+    def _open_bookmark_with_web_browser(self, bookmark):
+        gnome.url_show(bookmark.uri)
+
+    def update_bookmarks(self, category, category_type):
+        if category_type == 0:
+            bookmarks = self.bookmarklist.get_bookmark_list_in_category(
+                category)
+        else:
+            bookmarks = self.bookmarklist.get_non_category_bookmark()
+
+        model = self.treeview_bookmarks.get_model()
+        model.clear()
+
+        for bookmark in bookmarks:
+            model.append([bookmark.name, bookmark])
+        self.treeview_bookmarks.set_model(model)
+
+        self.current_category = category
+        self.current_category_type = category_type
+
+        category_model = self.treeview_categories.get_model()
+        if category_model:
+            for row in category_model:
+                text = row.model.get_value(row.iter, 0)
+                cat_type = row.model.get_value(row.iter, 1)
+                if text == category and cat_type == category_type:
+                    selection = self.treeview_categories.get_selection()
+                    selection.select_iter(row.iter)
+                    self.treeview_categories.scroll_to_cell(row.path)
+                    break
+
+        self.statusbar.pop(self.statusbar.get_context_id("bookmarks"))
+
+    def update_categories(self):
+        model = self.treeview_categories.get_model()
+        model.clear()
+        categories = self.bookmarklist.get_category_list()
+        model.append(["Non Category", 1])
+        for category in categories:
+            model.append([category, 0])
+        self.treeview_categories.set_model(model)
+
+        if self.current_category:
+            self.update_bookmarks(self.current_category,
+                                  self.current_category_type)
+        else:
+            self.update_bookmarks("", 0)
+
+    def on_editbookmark_complete(self, old_bookmark, new_bookmark):
+        changed = False
+        changed = changed or old_bookmark.name != new_bookmark.name
+        changed = changed or old_bookmark.uri != new_bookmark.uri
+        changed = changed or \
+                  len(old_bookmark.categories) != len(new_bookmark.categories)
+        if not changed:
+            for cat, kat in itertools.izip(old_bookmark.categories,
+                                           new_bookmark.categories):
+                if cat not in new_bookmark.categories:
+                    changed = True
+                    break
+                if kat not in old_bookmark.categories:
+                    changed = True
+                    break
+        if changed:
+            old_bookmark.name = new_bookmark.name
+            old_bookmark.uri = new_bookmark.uri
+            old_bookmark.categories = new_bookmark.categories
+            self.update_categories()
+            self.bookmarklist.save()
+        else:
+            print "not changed"
+        
+    def on_newbookmark_complete(self, bookmark):
+        self.bookmarklist.add_new_bookmark(bookmark)
+        self.update_categories()
+
+    # menu handler
+
+    def on_new_activate(self, widget):
+        BookmarkEditWindow(self.on_newbookmark_complete)
+
+    def on_open_activate(self, widget):
+        bookmark = self._get_selected_bookmark()
+        if bookmark:
+            self._open_bookmark(bookmark)
+
+    def on_open_web_activate(self, widget):
+        bookmark = self._get_selected_bookmark()
+        if bookmark:
+            self._open_bookmark_with_web_browser(bookmark)
+
+    def on_properties_activate(self, widget):
+        bookmark = self._get_selected_bookmark()
+        if bookmark:
+            BookmarkEditWindow(
+                lambda new_bookmark: \
+                self.on_editbookmark_complete(bookmark, new_bookmark),
+                bookmark)
+
+    def on_save_activate(self, widget):
+        self.bookmarklist.save()
+
+    def on_close_activate(self, widget):
+        self.destroy()
+
+    def on_quit_activate(self, widget):
+        session.main_quit()
+
+    def on_delete_activate(self, widget):
+        bookmark = self._get_selected_bookmark()
+        if bookmark:
+            self.bookmarklist.delete_bookmark(bookmark)
+            self.update_categories()
+
+    def on_refresh_bbsmenu_activate(self, widget):
+        self.bookmarklist.update_bbsmenu()
+        self.update_categories()
index 65fcfe3..6a14b33 100644 (file)
@@ -45,6 +45,8 @@ from BbsType import bbs_type_judge_uri
 from BbsType import bbs_type_exception
 import config
 import winwrapbase
 from BbsType import bbs_type_exception
 import config
 import winwrapbase
+import bookmark_list
+import bookmark_window
 
 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                          "..", "data")
 
 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                          "..", "data")
@@ -116,6 +118,9 @@ class WinWrap(winwrapbase.WinWrapBase):
                   "on_delete_activate": self.on_delete_activate,
                   "on_thread_window_delete_event":
                   self.on_thread_window_delete_event,
                   "on_delete_activate": self.on_delete_activate,
                   "on_thread_window_delete_event":
                   self.on_thread_window_delete_event,
+                  "on_add_bookmark_activate": self.on_add_bookmark_activate,
+                  "on_manage_bookmarks_activate": \
+                  self.on_manage_bookmarks_activate,
                   "on_thread_window_destroy": self.on_thread_window_destroy}
         self.widget_tree.signal_autoconnect(sigdic)
 
                   "on_thread_window_destroy": self.on_thread_window_destroy}
         self.widget_tree.signal_autoconnect(sigdic)
 
@@ -247,6 +252,13 @@ class WinWrap(winwrapbase.WinWrapBase):
     def on_quit_activate(self, widget):
         session.main_quit()
 
     def on_quit_activate(self, widget):
         session.main_quit()
 
+    def on_add_bookmark_activate(self, widget):
+        bookmark_list.bookmark_list.add_bookmark_with_edit(
+            name=self.title, uri=self.bbs_type.uri)
+
+    def on_manage_bookmarks_activate(self, widget):
+        bookmark_window.open()
+
     def on_show_board_activate(self, widget):
         board_window.open_board(self.bbs_type.get_uri_base())
 
     def on_show_board_activate(self, widget):
         board_window.open_board(self.bbs_type.get_uri_base())
 
index a279bc8..ee6719b 100644 (file)
 
 import board_window
 import thread_window
 
 import board_window
 import thread_window
+import bookmark_window
 from BbsType import bbs_type_judge_uri
 
 def open_uri(uri, update=False):
 from BbsType import bbs_type_judge_uri
 
 def open_uri(uri, update=False):
-    bbs_type = bbs_type_judge_uri.get_type(uri)
-    if bbs_type.is_board():
-        board_window.open_board(uri, update)
-    elif bbs_type.is_thread():
-        thread_window.open_thread(uri, update)
+    if uri == bookmark_window.WinWrap.uri:
+        bookmark_window.open()
     else:
     else:
-        print "the uri represents neither board nor thread: ", uri
+        bbs_type = bbs_type_judge_uri.get_type(uri)
+        if bbs_type.is_board():
+            board_window.open_board(uri, update)
+        elif bbs_type.is_thread():
+            thread_window.open_thread(uri, update)
+        else:
+            print "the uri represents neither board nor thread: ", uri
index 4fa5f9d..9d9d97c 100644 (file)
                          <signal name="activate" handler="on_delete_activate" last_modification_time="Thu, 14 Sep 2006 07:21:17 GMT"/>
 
                          <child internal-child="image">
                          <signal name="activate" handler="on_delete_activate" last_modification_time="Thu, 14 Sep 2006 07:21:17 GMT"/>
 
                          <child internal-child="image">
-                           <widget class="GtkImage" id="image20">
+                           <widget class="GtkImage" id="image24">
                              <property name="visible">True</property>
                              <property name="stock">gtk-delete</property>
                              <property name="icon_size">1</property>
                              <property name="visible">True</property>
                              <property name="stock">gtk-delete</property>
                              <property name="icon_size">1</property>
                          <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
                          <child internal-child="image">
                          <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
                          <child internal-child="image">
-                           <widget class="GtkImage" id="image21">
+                           <widget class="GtkImage" id="image25">
                              <property name="visible">True</property>
                              <property name="stock">gtk-refresh</property>
                              <property name="icon_size">1</property>
                              <property name="visible">True</property>
                              <property name="stock">gtk-refresh</property>
                              <property name="icon_size">1</property>
              </child>
 
              <child>
              </child>
 
              <child>
+               <widget class="GtkMenuItem" id="bookmarks1">
+                 <property name="visible">True</property>
+                 <property name="label" translatable="yes">_Bookmarks</property>
+                 <property name="use_underline">True</property>
+
+                 <child>
+                   <widget class="GtkMenu" id="bookmarks1_menu">
+
+                     <child>
+                       <widget class="GtkMenuItem" id="add_bookmark">
+                         <property name="visible">True</property>
+                         <property name="label" translatable="yes">Bookmark This Page</property>
+                         <property name="use_underline">True</property>
+                         <signal name="activate" handler="on_add_bookmark_activate" last_modification_time="Thu, 14 Sep 2006 12:21:26 GMT"/>
+                       </widget>
+                     </child>
+
+                     <child>
+                       <widget class="GtkMenuItem" id="manage_bookmarks">
+                         <property name="visible">True</property>
+                         <property name="label" translatable="yes">Show Bookmarks</property>
+                         <property name="use_underline">True</property>
+                         <signal name="activate" handler="on_manage_bookmarks_activate" last_modification_time="Thu, 14 Sep 2006 12:21:26 GMT"/>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+
+             <child>
                <widget class="GtkMenuItem" id="help1">
                  <property name="visible">True</property>
                  <property name="stock_item">GNOMEUIINFO_MENU_HELP_TREE</property>
                <widget class="GtkMenuItem" id="help1">
                  <property name="visible">True</property>
                  <property name="stock_item">GNOMEUIINFO_MENU_HELP_TREE</property>
diff --git a/src/data/bookmark_editwindow.glade b/src/data/bookmark_editwindow.glade
new file mode 100644 (file)
index 0000000..a2a95e9
--- /dev/null
@@ -0,0 +1,317 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window_editbookmark">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Edit Bookmark</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="default_width">300</property>
+  <property name="default_height">300</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+  <child>
+    <widget class="GtkVBox" id="vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+       <widget class="GtkTable" id="table1">
+         <property name="visible">True</property>
+         <property name="n_rows">2</property>
+         <property name="n_columns">2</property>
+         <property name="homogeneous">False</property>
+         <property name="row_spacing">0</property>
+         <property name="column_spacing">0</property>
+
+         <child>
+           <widget class="GtkLabel" id="label1">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">NAME:</property>
+             <property name="use_underline">False</property>
+             <property name="use_markup">False</property>
+             <property name="justify">GTK_JUSTIFY_LEFT</property>
+             <property name="wrap">False</property>
+             <property name="selectable">False</property>
+             <property name="xalign">0</property>
+             <property name="yalign">0.5</property>
+             <property name="xpad">0</property>
+             <property name="ypad">0</property>
+           </widget>
+           <packing>
+             <property name="left_attach">0</property>
+             <property name="right_attach">1</property>
+             <property name="top_attach">0</property>
+             <property name="bottom_attach">1</property>
+             <property name="x_options">fill</property>
+             <property name="y_options"></property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkLabel" id="label2">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">URL:</property>
+             <property name="use_underline">False</property>
+             <property name="use_markup">False</property>
+             <property name="justify">GTK_JUSTIFY_LEFT</property>
+             <property name="wrap">False</property>
+             <property name="selectable">False</property>
+             <property name="xalign">0</property>
+             <property name="yalign">0.5</property>
+             <property name="xpad">0</property>
+             <property name="ypad">0</property>
+           </widget>
+           <packing>
+             <property name="left_attach">0</property>
+             <property name="right_attach">1</property>
+             <property name="top_attach">1</property>
+             <property name="bottom_attach">2</property>
+             <property name="x_options">fill</property>
+             <property name="y_options"></property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkEntry" id="entry_name">
+             <property name="visible">True</property>
+             <property name="can_focus">True</property>
+             <property name="editable">True</property>
+             <property name="visibility">True</property>
+             <property name="max_length">0</property>
+             <property name="text" translatable="yes"></property>
+             <property name="has_frame">True</property>
+             <property name="invisible_char">*</property>
+             <property name="activates_default">True</property>
+           </widget>
+           <packing>
+             <property name="left_attach">1</property>
+             <property name="right_attach">2</property>
+             <property name="top_attach">0</property>
+             <property name="bottom_attach">1</property>
+             <property name="y_options"></property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkEntry" id="entry_uri">
+             <property name="visible">True</property>
+             <property name="can_focus">True</property>
+             <property name="editable">True</property>
+             <property name="visibility">True</property>
+             <property name="max_length">0</property>
+             <property name="text" translatable="yes"></property>
+             <property name="has_frame">True</property>
+             <property name="invisible_char">*</property>
+             <property name="activates_default">True</property>
+           </widget>
+           <packing>
+             <property name="left_attach">1</property>
+             <property name="right_attach">2</property>
+             <property name="top_attach">1</property>
+             <property name="bottom_attach">2</property>
+             <property name="y_options"></property>
+           </packing>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">False</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkFrame" id="frame1">
+         <property name="visible">True</property>
+         <property name="label_xalign">0</property>
+         <property name="label_yalign">0.5</property>
+         <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+         <child>
+           <widget class="GtkAlignment" id="alignment1">
+             <property name="visible">True</property>
+             <property name="xalign">0.5</property>
+             <property name="yalign">0.5</property>
+             <property name="xscale">1</property>
+             <property name="yscale">1</property>
+             <property name="top_padding">0</property>
+             <property name="bottom_padding">0</property>
+             <property name="left_padding">12</property>
+             <property name="right_padding">0</property>
+
+             <child>
+               <widget class="GtkHBox" id="hbox1">
+                 <property name="visible">True</property>
+                 <property name="homogeneous">False</property>
+                 <property name="spacing">0</property>
+
+                 <child>
+                   <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                     <property name="visible">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                     <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                     <property name="shadow_type">GTK_SHADOW_IN</property>
+                     <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+                     <child>
+                       <widget class="GtkTreeView" id="treeview_categories">
+                         <property name="visible">True</property>
+                         <property name="can_focus">True</property>
+                         <property name="headers_visible">False</property>
+                         <property name="rules_hint">False</property>
+                         <property name="reorderable">False</property>
+                         <property name="enable_search">False</property>
+                       </widget>
+                     </child>
+                   </widget>
+                   <packing>
+                     <property name="padding">0</property>
+                     <property name="expand">True</property>
+                     <property name="fill">True</property>
+                   </packing>
+                 </child>
+
+                 <child>
+                   <widget class="GtkVButtonBox" id="vbuttonbox1">
+                     <property name="border_width">5</property>
+                     <property name="visible">True</property>
+                     <property name="layout_style">GTK_BUTTONBOX_START</property>
+                     <property name="spacing">0</property>
+
+                     <child>
+                       <widget class="GtkButton" id="button_category_add">
+                         <property name="border_width">5</property>
+                         <property name="visible">True</property>
+                         <property name="can_default">True</property>
+                         <property name="can_focus">True</property>
+                         <property name="label">gtk-add</property>
+                         <property name="use_stock">True</property>
+                         <property name="relief">GTK_RELIEF_NORMAL</property>
+                         <property name="focus_on_click">True</property>
+                         <signal name="clicked" handler="on_button_category_add_clicked" last_modification_time="Wed, 13 Sep 2006 05:35:45 GMT"/>
+                       </widget>
+                     </child>
+
+                     <child>
+                       <widget class="GtkButton" id="button_category_edit">
+                         <property name="border_width">5</property>
+                         <property name="visible">True</property>
+                         <property name="can_default">True</property>
+                         <property name="can_focus">True</property>
+                         <property name="label">gtk-edit</property>
+                         <property name="use_stock">True</property>
+                         <property name="relief">GTK_RELIEF_NORMAL</property>
+                         <property name="focus_on_click">True</property>
+                         <signal name="clicked" handler="on_button_category_edit_clicked" last_modification_time="Wed, 13 Sep 2006 05:35:49 GMT"/>
+                       </widget>
+                     </child>
+
+                     <child>
+                       <widget class="GtkButton" id="button_category_delete">
+                         <property name="border_width">5</property>
+                         <property name="visible">True</property>
+                         <property name="can_default">True</property>
+                         <property name="can_focus">True</property>
+                         <property name="label">gtk-delete</property>
+                         <property name="use_stock">True</property>
+                         <property name="relief">GTK_RELIEF_NORMAL</property>
+                         <property name="focus_on_click">True</property>
+                         <signal name="clicked" handler="on_button_category_delete_clicked" last_modification_time="Wed, 13 Sep 2006 05:35:53 GMT"/>
+                       </widget>
+                     </child>
+                   </widget>
+                   <packing>
+                     <property name="padding">0</property>
+                     <property name="expand">False</property>
+                     <property name="fill">False</property>
+                   </packing>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkLabel" id="label4">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">CATEGORIES:</property>
+             <property name="use_underline">False</property>
+             <property name="use_markup">True</property>
+             <property name="justify">GTK_JUSTIFY_LEFT</property>
+             <property name="wrap">False</property>
+             <property name="selectable">False</property>
+             <property name="xalign">0.5</property>
+             <property name="yalign">0.5</property>
+             <property name="xpad">0</property>
+             <property name="ypad">0</property>
+           </widget>
+           <packing>
+             <property name="type">label_item</property>
+           </packing>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">True</property>
+         <property name="fill">True</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkHButtonBox" id="hbuttonbox1">
+         <property name="visible">True</property>
+         <property name="layout_style">GTK_BUTTONBOX_END</property>
+         <property name="spacing">0</property>
+
+         <child>
+           <widget class="GtkButton" id="button_cancel">
+             <property name="border_width">5</property>
+             <property name="visible">True</property>
+             <property name="can_default">True</property>
+             <property name="can_focus">True</property>
+             <property name="label">gtk-cancel</property>
+             <property name="use_stock">True</property>
+             <property name="relief">GTK_RELIEF_NORMAL</property>
+             <property name="focus_on_click">True</property>
+             <signal name="clicked" handler="on_button_cancel_clicked" last_modification_time="Wed, 13 Sep 2006 05:36:00 GMT"/>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkButton" id="button_ok">
+             <property name="border_width">5</property>
+             <property name="visible">True</property>
+             <property name="can_default">True</property>
+             <property name="has_default">True</property>
+             <property name="can_focus">True</property>
+             <property name="label">gtk-ok</property>
+             <property name="use_stock">True</property>
+             <property name="relief">GTK_RELIEF_NORMAL</property>
+             <property name="focus_on_click">True</property>
+             <signal name="clicked" handler="on_button_ok_clicked" last_modification_time="Wed, 13 Sep 2006 05:36:05 GMT"/>
+           </widget>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">False</property>
+       </packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff --git a/src/data/bookmark_window.glade b/src/data/bookmark_window.glade
new file mode 100644 (file)
index 0000000..6984cf1
--- /dev/null
@@ -0,0 +1,385 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window_bookmark">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Bookmarks</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="default_width">300</property>
+  <property name="default_height">300</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+  <child>
+    <widget class="GtkVBox" id="vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+       <widget class="GtkMenuBar" id="menubar1">
+         <property name="visible">True</property>
+
+         <child>
+           <widget class="GtkMenuItem" id="menuitem1">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">_File</property>
+             <property name="use_underline">True</property>
+
+             <child>
+               <widget class="GtkMenu" id="menuitem1_menu">
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="new">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_New Bookmark</property>
+                     <property name="use_underline">True</property>
+                     <signal name="activate" handler="on_new_activate" last_modification_time="Thu, 14 Sep 2006 08:25:45 GMT"/>
+                     <accelerator key="N" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image36">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-new</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="open">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Open Bookmark</property>
+                     <property name="use_underline">True</property>
+                     <signal name="activate" handler="on_open_activate" last_modification_time="Thu, 14 Sep 2006 08:25:45 GMT"/>
+                     <accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image37">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-open</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="open_web">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Open with _WebBrowser</property>
+                     <property name="use_underline">True</property>
+                     <signal name="activate" handler="on_open_web_activate" last_modification_time="Thu, 14 Sep 2006 11:49:28 GMT"/>
+                     <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image38">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-open</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="properties">
+                     <property name="visible">True</property>
+                     <property name="label">gtk-properties</property>
+                     <property name="use_stock">True</property>
+                     <signal name="activate" handler="on_properties_activate" last_modification_time="Thu, 14 Sep 2006 08:26:58 GMT"/>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator3">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="save">
+                     <property name="visible">True</property>
+                     <property name="label">gtk-save</property>
+                     <property name="use_stock">True</property>
+                     <signal name="activate" handler="on_save_activate" last_modification_time="Thu, 14 Sep 2006 08:25:45 GMT"/>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="close">
+                     <property name="visible">True</property>
+                     <property name="label">gtk-close</property>
+                     <property name="use_stock">True</property>
+                     <signal name="activate" handler="on_close_activate" last_modification_time="Thu, 14 Sep 2006 12:15:20 GMT"/>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="quit">
+                     <property name="visible">True</property>
+                     <property name="label">gtk-quit</property>
+                     <property name="use_stock">True</property>
+                     <signal name="activate" handler="on_quit_activate" last_modification_time="Thu, 14 Sep 2006 08:25:45 GMT"/>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkMenuItem" id="menuitem2">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">_Edit</property>
+             <property name="use_underline">True</property>
+
+             <child>
+               <widget class="GtkMenu" id="menuitem2_menu">
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="delete">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Delete Bookmark</property>
+                     <property name="use_underline">True</property>
+                     <signal name="activate" handler="on_delete_activate" last_modification_time="Thu, 14 Sep 2006 08:25:45 GMT"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image39">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-delete</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkMenuItem" id="menuitem3">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">_View</property>
+             <property name="use_underline">True</property>
+
+             <child>
+               <widget class="GtkMenu" id="menuitem3_menu">
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="refresh_bbsmenu">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Refresh BBS Menu</property>
+                     <property name="use_underline">True</property>
+                     <signal name="activate" handler="on_refresh_bbsmenu_activate" last_modification_time="Thu, 14 Sep 2006 09:52:59 GMT"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image40">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-refresh</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkMenuItem" id="menuitem4">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">_Help</property>
+             <property name="use_underline">True</property>
+           </widget>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">False</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkHPaned" id="hpaned1">
+         <property name="visible">True</property>
+         <property name="can_focus">True</property>
+         <property name="position">100</property>
+
+         <child>
+           <widget class="GtkScrolledWindow" id="scrolledwindow1">
+             <property name="visible">True</property>
+             <property name="can_focus">True</property>
+             <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+             <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+             <property name="shadow_type">GTK_SHADOW_IN</property>
+             <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+             <child>
+               <widget class="GtkTreeView" id="treeview_categories">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="headers_visible">True</property>
+                 <property name="rules_hint">False</property>
+                 <property name="reorderable">False</property>
+                 <property name="enable_search">True</property>
+               </widget>
+             </child>
+           </widget>
+           <packing>
+             <property name="shrink">True</property>
+             <property name="resize">False</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkScrolledWindow" id="scrolledwindow2">
+             <property name="visible">True</property>
+             <property name="can_focus">True</property>
+             <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+             <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+             <property name="shadow_type">GTK_SHADOW_IN</property>
+             <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+             <child>
+               <widget class="GtkTreeView" id="treeview_bookmarks">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="headers_visible">True</property>
+                 <property name="rules_hint">False</property>
+                 <property name="reorderable">False</property>
+                 <property name="enable_search">True</property>
+               </widget>
+             </child>
+           </widget>
+           <packing>
+             <property name="shrink">True</property>
+             <property name="resize">True</property>
+           </packing>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">True</property>
+         <property name="fill">True</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkStatusbar" id="statusbar">
+         <property name="visible">True</property>
+         <property name="has_resize_grip">True</property>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">False</property>
+       </packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkMenu" id="menu_bookmark">
+
+  <child>
+    <widget class="GtkImageMenuItem" id="open">
+      <property name="visible">True</property>
+      <property name="label">gtk-open</property>
+      <property name="use_stock">True</property>
+      <signal name="activate" handler="on_open_activate" last_modification_time="Thu, 14 Sep 2006 08:27:19 GMT"/>
+    </widget>
+  </child>
+
+  <child>
+    <widget class="GtkImageMenuItem" id="open_web">
+      <property name="visible">True</property>
+      <property name="label" translatable="yes">Open with _WebBrowser</property>
+      <property name="use_underline">True</property>
+      <signal name="activate" handler="on_open_web_activate" last_modification_time="Thu, 14 Sep 2006 11:48:47 GMT"/>
+
+      <child internal-child="image">
+       <widget class="GtkImage" id="image15">
+         <property name="visible">True</property>
+         <property name="stock">gtk-open</property>
+         <property name="icon_size">1</property>
+         <property name="xalign">0.5</property>
+         <property name="yalign">0.5</property>
+         <property name="xpad">0</property>
+         <property name="ypad">0</property>
+       </widget>
+      </child>
+    </widget>
+  </child>
+
+  <child>
+    <widget class="GtkSeparatorMenuItem" id="separator2">
+      <property name="visible">True</property>
+    </widget>
+  </child>
+
+  <child>
+    <widget class="GtkImageMenuItem" id="delete">
+      <property name="visible">True</property>
+      <property name="label">gtk-delete</property>
+      <property name="use_stock">True</property>
+      <signal name="activate" handler="on_delete_activate" last_modification_time="Thu, 14 Sep 2006 08:27:19 GMT"/>
+    </widget>
+  </child>
+
+  <child>
+    <widget class="GtkSeparatorMenuItem" id="separator1">
+      <property name="visible">True</property>
+    </widget>
+  </child>
+
+  <child>
+    <widget class="GtkImageMenuItem" id="properties">
+      <property name="visible">True</property>
+      <property name="label">gtk-properties</property>
+      <property name="use_stock">True</property>
+      <signal name="activate" handler="on_properties_activate" last_modification_time="Thu, 14 Sep 2006 08:27:19 GMT"/>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
index 96a7154..5d6702a 100644 (file)
@@ -52,7 +52,7 @@
                          <signal name="activate" handler="on_show_board_activate" last_modification_time="Mon, 21 Aug 2006 03:30:53 GMT"/>
 
                          <child internal-child="image">
                          <signal name="activate" handler="on_show_board_activate" last_modification_time="Mon, 21 Aug 2006 03:30:53 GMT"/>
 
                          <child internal-child="image">
-                           <widget class="GtkImage" id="image10">
+                           <widget class="GtkImage" id="image22">
                              <property name="visible">True</property>
                              <property name="stock">gtk-go-up</property>
                              <property name="icon_size">1</property>
                              <property name="visible">True</property>
                              <property name="stock">gtk-go-up</property>
                              <property name="icon_size">1</property>
@@ -74,7 +74,7 @@
                          <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
                          <child internal-child="image">
                          <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
                          <child internal-child="image">
-                           <widget class="GtkImage" id="image11">
+                           <widget class="GtkImage" id="image23">
                              <property name="visible">True</property>
                              <property name="stock">gnome-stock-mail-new</property>
                              <property name="icon_size">1</property>
                              <property name="visible">True</property>
                              <property name="stock">gnome-stock-mail-new</property>
                              <property name="icon_size">1</property>
@@ -95,7 +95,7 @@
                          <signal name="activate" handler="on_delete_activate" last_modification_time="Thu, 14 Sep 2006 04:44:02 GMT"/>
 
                          <child internal-child="image">
                          <signal name="activate" handler="on_delete_activate" last_modification_time="Thu, 14 Sep 2006 04:44:02 GMT"/>
 
                          <child internal-child="image">
-                           <widget class="GtkImage" id="image12">
+                           <widget class="GtkImage" id="image24">
                              <property name="visible">True</property>
                              <property name="stock">gtk-delete</property>
                              <property name="icon_size">1</property>
                              <property name="visible">True</property>
                              <property name="stock">gtk-delete</property>
                              <property name="icon_size">1</property>
                          <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
                          <child internal-child="image">
                          <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
 
                          <child internal-child="image">
-                           <widget class="GtkImage" id="image13">
+                           <widget class="GtkImage" id="image25">
                              <property name="visible">True</property>
                              <property name="stock">gtk-refresh</property>
                              <property name="icon_size">1</property>
                              <property name="visible">True</property>
                              <property name="stock">gtk-refresh</property>
                              <property name="icon_size">1</property>
              </child>
 
              <child>
              </child>
 
              <child>
+               <widget class="GtkMenuItem" id="bookmarks1">
+                 <property name="visible">True</property>
+                 <property name="label" translatable="yes">_Bookmarks</property>
+                 <property name="use_underline">True</property>
+
+                 <child>
+                   <widget class="GtkMenu" id="bookmarks1_menu">
+
+                     <child>
+                       <widget class="GtkMenuItem" id="add_bookmark">
+                         <property name="visible">True</property>
+                         <property name="label" translatable="yes">Bookmark This Page</property>
+                         <property name="use_underline">True</property>
+                         <signal name="activate" handler="on_add_bookmark_activate" last_modification_time="Thu, 14 Sep 2006 12:23:15 GMT"/>
+                       </widget>
+                     </child>
+
+                     <child>
+                       <widget class="GtkMenuItem" id="manage_bookmarks">
+                         <property name="visible">True</property>
+                         <property name="label" translatable="yes">Show Bookmarks</property>
+                         <property name="use_underline">True</property>
+                         <signal name="activate" handler="on_manage_bookmarks_activate" last_modification_time="Thu, 14 Sep 2006 12:23:15 GMT"/>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+
+             <child>
                <widget class="GtkMenuItem" id="help1">
                  <property name="visible">True</property>
                  <property name="stock_item">GNOMEUIINFO_MENU_HELP_TREE</property>
                <widget class="GtkMenuItem" id="help1">
                  <property name="visible">True</property>
                  <property name="stock_item">GNOMEUIINFO_MENU_HELP_TREE</property>
index a94640d..fb0c5a1 100755 (executable)
@@ -26,6 +26,7 @@ import getopt
 import sys
 import dbus
 import dbus.service
 import sys
 import dbus
 import dbus.service
+import os.path
 
 APPNAME = 'Fukui-no-Namari'
 APPVERSION = '0.1'
 
 APPNAME = 'Fukui-no-Namari'
 APPVERSION = '0.1'
@@ -37,6 +38,7 @@ from FukuiNoNamari import board_window
 from FukuiNoNamari import dbus_object
 from FukuiNoNamari import uri_opener
 from FukuiNoNamari import session
 from FukuiNoNamari import dbus_object
 from FukuiNoNamari import uri_opener
 from FukuiNoNamari import session
+from FukuiNoNamari import bookmark_list
 
 # where are these values ?
 DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 1
 
 # where are these values ?
 DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 1
@@ -92,6 +94,10 @@ if __name__ == "__main__":
         gnome.init(APPNAME, APPVERSION)
         gobject.threads_init()
 
         gnome.init(APPNAME, APPVERSION)
         gobject.threads_init()
 
+        bookmark_path = os.path.join(config.get_config_dir_path(),
+                                     "bookmarks.txt")
+        bookmark_list.init(bookmark_path, "http://menu.2ch.net/bbsmenu.html")
+
         # open some windows
         if uris:
             from FukuiNoNamari.BbsType import bbs_type_judge_uri
         # open some windows
         if uris:
             from FukuiNoNamari.BbsType import bbs_type_judge_uri