OSDN Git Service

Add thread window.
authorAiwota Programmer <aiwotaprog@tetteke.tk>
Wed, 16 Aug 2006 09:05:32 +0000 (18:05 +0900)
committerAiwota Programmer <aiwotaprog@tetteke.tk>
Wed, 16 Aug 2006 09:05:32 +0000 (18:05 +0900)
src/Hage1/board_window.py
src/Hage1/thread_window.py [new file with mode: 0644]
src/data/thread_window.glade [new file with mode: 0644]

index 0837a8d..dcd4136 100644 (file)
@@ -23,6 +23,7 @@ import os
 import time
 
 import board_data
+import thread_window
 
 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                          "..", "data")
@@ -148,11 +149,11 @@ class WinWrap:
                   "on_quit_activate": self.on_quit_activate,
                   "on_load_local_activate": self.on_load_local_activate,
                   "on_get_remote_activate": self.on_get_remote_activate,
-                  "on_treeview_row_activated": self.on_treeview_row_activated,
+                  "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_popup_menu_open_activate":
-                  self.on_popup_menu_open_activate}
+                  "on_popup_menu_open_activate": self.on_open_thread}
         self.mainwin.signal_autoconnect(sigdic)
 
     def on_board_window_destroy(self, widget):
@@ -169,8 +170,8 @@ class WinWrap:
         t = board_data.GetRemote(self.bbs, self.board, self.update_datastore)
         t.start()
 
-    def on_treeview_row_activated(self, widget, row, column):
-        treeselection = widget.get_selection()
+    def on_open_thread(self, widget):
+        treeselection = self.treeview.get_selection()
         model, iter = treeselection.get_selected()
         if not iter:
             return
@@ -179,6 +180,8 @@ class WinWrap:
         title = model.get_value(iter, 1)["title"]
         print thread + ':"' + title + '"', "activated"
 
+        thread_window.WinWrap(self.bbs, self.board, thread)
+
     def on_treeview_button_press_event(self, widget, event):
         if event.button == 3:
             x = int(event.x)
@@ -192,16 +195,6 @@ class WinWrap:
                 self.popupmenu.popup(None, None, None, event.button, time)
             return 1
 
-    def on_popup_menu_open_activate(self, widget):
-        treeselection = self.treeview.get_selection()
-        model, iter = treeselection.get_selected()
-        if not iter:
-            return
-
-        thread = model.get_value(iter, 0)
-        title = model.get_value(iter, 1)["title"]
-        print thread + ':"' + title + '"', "activated 2"
-
     def update_datastore(self, datalist):
         print "clear datastore"
         self.datastore.clear()
diff --git a/src/Hage1/thread_window.py b/src/Hage1/thread_window.py
new file mode 100644 (file)
index 0000000..134e222
--- /dev/null
@@ -0,0 +1,208 @@
+# 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
+import codecs
+import re
+import pango
+import urllib2
+
+import misc
+import datfile
+import barehtmlparser
+import idxfile
+
+GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                         "..", "data")
+GLADE_FILENAME = "thread_window.glade"
+
+
+class WinWrap:
+
+    def __init__(self, bbs, board, thread):
+        self.bbs = bbs
+        self.board = board
+        self.thread = thread
+        self.size = 0
+        self.num = 0
+        self.title = None
+
+        glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
+        self.widget_tree = gtk.glade.XML(glade_path)
+        self.window = self.widget_tree.get_widget("thread_window")
+        self.textview = self.widget_tree.get_widget("textview")
+        self.textbuffer = self.textview.get_buffer()
+        self.enditer = self.textbuffer.get_end_iter()
+        self.boldtag = self.textbuffer.create_tag(weight=pango.WEIGHT_BOLD)
+        self.leftmargintag = self.textbuffer.create_tag()
+        self.leftmargintag.set_property("left-margin", 20)
+
+        sigdic = {"on_toolbutton_refresh_clicked": self.update,
+                  "on_refresh_activate": self.update,
+                  "on_close_activate": self.on_close_activate,
+                  "on_quit_activate": self.on_quit_activate,
+                  "on_thread_window_destroy": self.on_thread_window_destroy}
+        self.widget_tree.signal_autoconnect(sigdic)
+
+        self.load_dat()
+
+    def on_close_activate(self, widget):
+        self.window.destroy()
+
+    def on_thread_window_destroy(self, widget):
+        -1
+
+    def on_quit_activate(self, widget):
+        gtk.main_quit()
+
+    def http_get_dat(self, on_get_res):
+        datfile_url = misc.get_thread_dat_url(self.bbs, self.board, self.thread)
+
+        idx_dic = idxfile.load_idx(self.bbs, self.board, self.thread)
+        lastmod = idx_dic["lastModified"]
+        etag = idx_dic["etag"]
+
+        req = urllib2.Request(datfile_url)
+        if self.size > 0:
+            req.add_header("Range", "bytes=" + str(self.size) + "-")
+        if lastmod:
+            req.add_header("If-Modified-Since", lastmod)
+        if etag:
+            req.add_header("If-None-Match", etag)
+        print req.headers
+
+        res = urllib2.urlopen(req)
+        headers = res.info()
+        print headers
+
+        line = res.readline()
+        maybe_incomplete = False
+        while line:
+            if not line.endswith("\n"):
+                maybe_incomplete = True
+                print "does not end with \\n. maybe incomplete"
+                break
+            on_get_res(line)
+            line = res.readline()
+
+        res.close()
+
+        if maybe_incomplete:
+            lastmod = None
+            etag = None
+        else:
+            if "Last-Modified" in headers:
+                lastmod = headers["Last-Modified"]
+            if "ETag" in headers:
+                etag = headers["Etag"]
+
+        if self.num > 0:
+            # save idx
+            idx_dic = {"title": self.title, "lineCount": self.num,
+                   "lastModified": lastmod, "etag": etag}
+            idxfile.save_idx(self.bbs, self.board, self.thread, idx_dic)
+
+    def update(self, widget=None):
+        line_count = datfile.get_dat_line_count(
+            self.bbs, self.board, self.thread)
+
+        if line_count > self.num:
+            datfile.load_dat_partly(
+                self.bbs, self.board, self.thread,
+                self.append_rawres_to_buffer, self.num+1)
+
+        dat_path = misc.get_thread_dat_path(self.bbs, self.board, self.thread)
+        dat_file = file(dat_path, "a+")
+
+        def save_line_and_append_to_buffer(line):
+            dat_file.seek(self.size)
+            dat_file.write(line)
+            self.append_rawres_to_buffer(line)
+
+        self.http_get_dat(save_line_and_append_to_buffer)
+        dat_file.close()
+
+        if not self.title:
+            self.title = datfile.get_title_from_dat(
+                self.bbs, self.board, self.thread)
+            if self.title:
+                self.window.set_title(self.title)
+
+    def load_dat(self):
+        self.size = 0
+        self.num = 0
+
+        self.title = datfile.get_title_from_dat(
+            self.bbs, self.board, self.thread)
+        if self.title:
+            self.window.set_title(self.title)
+
+        datfile.load_dat(self.bbs, self.board, self.thread,
+                         self.append_rawres_to_buffer)
+        self.textview.scroll_to_mark(self.textbuffer.get_insert(), 0)
+
+
+    def append_rawres_to_buffer(self, line):
+        self.size += len(line)
+        self.num += 1
+
+        h = lambda name,mail,date,msg: self.reselems_to_buffer(
+            self.num, name, mail, date, msg)
+
+        datfile.split_line_to_elems(line.decode("cp932", "replace"), h)
+        
+    def reselems_to_buffer(self, num, name, mail, date, msg):
+        # number
+        self.textbuffer.insert(self.enditer, str(num) + " ")
+
+        # name
+        p = barehtmlparser.BareHTMLParser(self.untiedata_to_buffer)
+        p.feed("<b>" + name + "</b>")
+        p.close()
+
+        # mail
+        self.textbuffer.insert(self.enditer, "[" + mail + "]")
+
+        # date
+        p.feed(date)
+        p.close()
+        self.textbuffer.insert(self.enditer, "\n")
+
+        # msg
+        p.reset_func(self.untiedata_to_buffer_with_leftmargin)
+        p.feed(msg)
+        p.close()
+
+        self.textbuffer.insert(self.enditer, "\n\n")
+
+    def untiedata_to_buffer(self, data, bold, href):
+        if bold:
+            self.textbuffer.insert_with_tags(self.enditer, data, self.boldtag)
+        else:
+            self.textbuffer.insert(self.enditer, data)
+
+    def untiedata_to_buffer_with_leftmargin(self, data, bold, href):
+        if bold:
+            self.textbuffer.insert_with_tags(self.enditer, data,
+                                             self.boldtag, self.leftmargintag)
+        else:
+            self.textbuffer.insert_with_tags(self.enditer, data,
+                                             self.leftmargintag)
diff --git a/src/data/thread_window.glade b/src/data/thread_window.glade
new file mode 100644 (file)
index 0000000..88d43e1
--- /dev/null
@@ -0,0 +1,224 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+<requires lib="bonobo"/>
+
+<widget class="GnomeApp" id="thread_window">
+  <property name="visible">True</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">600</property>
+  <property name="default_height">600</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>
+  <property name="enable_layout_config">True</property>
+  <signal name="destroy" handler="on_thread_window_destroy" last_modification_time="Wed, 16 Aug 2006 03:37:07 GMT"/>
+
+  <child internal-child="dock">
+    <widget class="BonoboDock" id="bonobodock1">
+      <property name="visible">True</property>
+      <property name="allow_floating">True</property>
+
+      <child>
+       <widget class="BonoboDockItem" id="bonobodockitem1">
+         <property name="visible">True</property>
+         <property name="shadow_type">GTK_SHADOW_NONE</property>
+
+         <child>
+           <widget class="GtkMenuBar" id="menubar1">
+             <property name="visible">True</property>
+
+             <child>
+               <widget class="GtkMenuItem" id="file1">
+                 <property name="visible">True</property>
+                 <property name="stock_item">GNOMEUIINFO_MENU_FILE_TREE</property>
+
+                 <child>
+                   <widget class="GtkMenu" id="file1_menu">
+
+                     <child>
+                       <widget class="GtkImageMenuItem" id="refresh">
+                         <property name="visible">True</property>
+                         <property name="label" translatable="yes">_Refresh</property>
+                         <property name="use_underline">True</property>
+                         <signal name="activate" handler="on_refresh_activate" last_modification_time="Wed, 16 Aug 2006 03:34:22 GMT"/>
+                         <accelerator key="R" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                         <child internal-child="image">
+                           <widget class="GtkImage" id="image1">
+                             <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>
+
+                     <child>
+                       <widget class="GtkSeparatorMenuItem" id="separator2">
+                         <property name="visible">True</property>
+                       </widget>
+                     </child>
+
+                     <child>
+                       <widget class="GtkImageMenuItem" id="close">
+                         <property name="visible">True</property>
+                         <property name="stock_item">GNOMEUIINFO_MENU_CLOSE_ITEM</property>
+                         <signal name="activate" handler="on_close_activate" last_modification_time="Wed, 16 Aug 2006 03:28:25 GMT"/>
+                       </widget>
+                     </child>
+
+                     <child>
+                       <widget class="GtkSeparatorMenuItem" id="separator1">
+                         <property name="visible">True</property>
+                       </widget>
+                     </child>
+
+                     <child>
+                       <widget class="GtkImageMenuItem" id="quit">
+                         <property name="visible">True</property>
+                         <property name="stock_item">GNOMEUIINFO_MENU_EXIT_ITEM</property>
+                         <signal name="activate" handler="on_quit_activate" last_modification_time="Wed, 16 Aug 2006 03:34:30 GMT"/>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+
+             <child>
+               <widget class="GtkMenuItem" id="edit1">
+                 <property name="visible">True</property>
+                 <property name="stock_item">GNOMEUIINFO_MENU_EDIT_TREE</property>
+               </widget>
+             </child>
+
+             <child>
+               <widget class="GtkMenuItem" id="view1">
+                 <property name="visible">True</property>
+                 <property name="stock_item">GNOMEUIINFO_MENU_VIEW_TREE</property>
+               </widget>
+             </child>
+
+             <child>
+               <widget class="GtkMenuItem" id="help1">
+                 <property name="visible">True</property>
+                 <property name="stock_item">GNOMEUIINFO_MENU_HELP_TREE</property>
+               </widget>
+             </child>
+           </widget>
+         </child>
+       </widget>
+       <packing>
+         <property name="placement">BONOBO_DOCK_TOP</property>
+         <property name="band">0</property>
+         <property name="position">0</property>
+         <property name="offset">0</property>
+         <property name="behavior">BONOBO_DOCK_ITEM_BEH_EXCLUSIVE|BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL|BONOBO_DOCK_ITEM_BEH_LOCKED</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="BonoboDockItem" id="bonobodockitem2">
+         <property name="visible">True</property>
+         <property name="shadow_type">GTK_SHADOW_OUT</property>
+
+         <child>
+           <widget class="GtkToolbar" id="toolbar1">
+             <property name="visible">True</property>
+             <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+             <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
+             <property name="tooltips">True</property>
+             <property name="show_arrow">True</property>
+
+             <child>
+               <widget class="GtkToolButton" id="toolbutton_refresh">
+                 <property name="visible">True</property>
+                 <property name="stock_id">gtk-refresh</property>
+                 <property name="visible_horizontal">True</property>
+                 <property name="visible_vertical">True</property>
+                 <property name="is_important">False</property>
+                 <signal name="clicked" handler="on_toolbutton_refresh_clicked" last_modification_time="Wed, 16 Aug 2006 03:30:49 GMT"/>
+               </widget>
+               <packing>
+                 <property name="expand">False</property>
+                 <property name="homogeneous">True</property>
+               </packing>
+             </child>
+           </widget>
+         </child>
+       </widget>
+       <packing>
+         <property name="placement">BONOBO_DOCK_TOP</property>
+         <property name="band">1</property>
+         <property name="position">0</property>
+         <property name="offset">0</property>
+         <property name="behavior">BONOBO_DOCK_ITEM_BEH_EXCLUSIVE|BONOBO_DOCK_ITEM_BEH_LOCKED</property>
+       </packing>
+      </child>
+
+      <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_NONE</property>
+         <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+         <child>
+           <widget class="GtkTextView" id="textview">
+             <property name="visible">True</property>
+             <property name="can_focus">True</property>
+             <property name="editable">False</property>
+             <property name="overwrite">False</property>
+             <property name="accepts_tab">True</property>
+             <property name="justification">GTK_JUSTIFY_LEFT</property>
+             <property name="wrap_mode">GTK_WRAP_CHAR</property>
+             <property name="cursor_visible">False</property>
+             <property name="pixels_above_lines">0</property>
+             <property name="pixels_below_lines">0</property>
+             <property name="pixels_inside_wrap">0</property>
+             <property name="left_margin">0</property>
+             <property name="right_margin">0</property>
+             <property name="indent">0</property>
+             <property name="text" translatable="yes"></property>
+           </widget>
+         </child>
+       </widget>
+      </child>
+    </widget>
+    <packing>
+      <property name="padding">0</property>
+      <property name="expand">True</property>
+      <property name="fill">True</property>
+    </packing>
+  </child>
+
+  <child internal-child="appbar">
+    <widget class="GnomeAppBar" id="appbar1">
+      <property name="visible">True</property>
+      <property name="has_progress">True</property>
+      <property name="has_status">True</property>
+    </widget>
+    <packing>
+      <property name="padding">0</property>
+      <property name="expand">True</property>
+      <property name="fill">True</property>
+    </packing>
+  </child>
+</widget>
+
+</glade-interface>