OSDN Git Service

Fix failure saving to a dat file when the parent directory does not exist.
[fukui-no-namari/fukui-no-namari.git] / src / Hage1 / thread_window.py
1 # Copyright (C) 2006 by Aiwota Programmer
2 # aiwotaprog@tetteke.tk
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 import pygtk
19 pygtk.require('2.0')
20 import gtk
21 import gtk.glade
22 import os.path
23 import codecs
24 import re
25 import pango
26 import urllib2
27
28 import misc
29 import datfile
30 import barehtmlparser
31 import idxfile
32 import session
33 import board_window
34 from http_sub import HTTPRedirectHandler302
35
36 GLADE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
37                          "..", "data")
38 GLADE_FILENAME = "thread_window.glade"
39
40 def open_thread(uri):
41     if not uri:
42         raise ValueError, "parameter must not be empty"
43
44     winwrap = session.get_window(uri)
45     if winwrap:
46         # already opened
47         winwrap.window.present()
48         pass
49     else:
50         win_wrap = WinWrap(uri)
51         session.window_created(uri, win_wrap)
52
53
54 class WinWrap:
55
56     def __init__(self, uri):
57         from BbsType import bbs_type_judge_uri
58         from BbsType import bbs_type_exception
59         self.bbs_type = bbs_type_judge_uri.get_type(uri)
60         if not self.bbs_type.is_thread():
61             raise bbs_type_exception.BbsTypeError, \
62                   "the uri does not represent thread: " + uri
63         self.bbs = self.bbs_type.bbs_type
64         self.board = self.bbs_type.board
65         self.thread = self.bbs_type.thread
66         self.host = self.bbs_type.host
67         self.uri = self.bbs_type.uri
68         self.size = 0
69         self.num = 0
70         self.title = None
71
72         glade_path = os.path.join(GLADE_DIR, GLADE_FILENAME)
73         self.widget_tree = gtk.glade.XML(glade_path)
74         self.window = self.widget_tree.get_widget("thread_window")
75         self.textview = self.widget_tree.get_widget("textview")
76         self.textbuffer = self.textview.get_buffer()
77         self.enditer = self.textbuffer.get_end_iter()
78         self.boldtag = self.textbuffer.create_tag(weight=pango.WEIGHT_BOLD)
79         self.leftmargintag = self.textbuffer.create_tag()
80         self.leftmargintag.set_property("left-margin", 20)
81
82         sigdic = {"on_toolbutton_refresh_clicked": self.update,
83                   "on_refresh_activate": self.update,
84                   "on_close_activate": self.on_close_activate,
85                   "on_quit_activate": self.on_quit_activate,
86                   "on_show_board_activate": self.on_show_board_activate,
87                   "on_thread_window_destroy": self.on_thread_window_destroy}
88         self.widget_tree.signal_autoconnect(sigdic)
89
90         self.load_dat()
91
92     def on_close_activate(self, widget):
93         self.window.destroy()
94
95     def on_thread_window_destroy(self, widget):
96         -1
97
98     def on_quit_activate(self, widget):
99         session.main_quit()
100
101     def on_show_board_activate(self, widget):
102         board_window.open_board(self.bbs_type.get_uri_base())
103
104     def http_get_dat(self, on_get_res):
105         datfile_url = self.bbs_type.get_dat_uri()
106
107         idx_dic = idxfile.load_idx(self.bbs, self.board, self.thread)
108         lastmod = idx_dic["lastModified"]
109         etag = idx_dic["etag"]
110
111         req = urllib2.Request(datfile_url)
112         if self.size > 0:
113             req.add_header("Range", "bytes=" + str(self.size) + "-")
114         if lastmod:
115             req.add_header("If-Modified-Since", lastmod)
116         if etag:
117             req.add_header("If-None-Match", etag)
118         print req.headers
119
120         opener = urllib2.build_opener(HTTPRedirectHandler302)
121         res = opener.open(req)
122         headers = res.info()
123         print headers
124
125         line = res.readline()
126         maybe_incomplete = False
127         while line:
128             if not line.endswith("\n"):
129                 maybe_incomplete = True
130                 print "does not end with \\n. maybe incomplete"
131                 break
132             on_get_res(line)
133             line = res.readline()
134
135         res.close()
136
137         if maybe_incomplete:
138             lastmod = None
139             etag = None
140         else:
141             if "Last-Modified" in headers:
142                 lastmod = headers["Last-Modified"]
143             if "ETag" in headers:
144                 etag = headers["Etag"]
145
146         if self.num > 0:
147             if not self.title:
148                 self.title = datfile.get_title_from_dat(
149                     self.bbs, self.board, self.thread)
150                 if self.title:
151                     self.window.set_title(self.title)
152             # save idx
153             idx_dic = {"title": self.title, "lineCount": self.num,
154                    "lastModified": lastmod, "etag": etag}
155             idxfile.save_idx(self.bbs, self.board, self.thread, idx_dic)
156
157             session.thread_idx_updated(
158                 self.bbs_type.get_thread_uri(), idx_dic)
159
160     def update(self, widget=None):
161         line_count = datfile.get_dat_line_count(
162             self.bbs, self.board, self.thread)
163
164         if line_count > self.num:
165             datfile.load_dat_partly(
166                 self.bbs, self.board, self.thread,
167                 self.append_rawres_to_buffer, self.num+1)
168
169
170         class FileWrap:
171             def __init__(self, path):
172                 self._file = None
173                 self._path = path
174             def __del__(self):
175                 self.close()
176             def seek(self, size):
177                 self.file().seek(size)
178             def write(self, data):
179                 self.file().write(data)
180             def close(self):
181                 if self._file:
182                     self._file.close()
183                     self._file = None
184             def file(self):
185                 if not self._file:
186                     basedir = os.path.dirname(self._path)
187                     if not os.path.isdir(basedir):
188                         os.makedirs(basedir)
189                     self._file = file(self._path, "a+")
190                 return self._file
191
192         dat_path = misc.get_thread_dat_path(self.bbs, self.board, self.thread)
193         dat_file = FileWrap(dat_path)
194
195         def save_line_and_append_to_buffer(line):
196             dat_file.seek(self.size)
197             dat_file.write(line)
198             self.append_rawres_to_buffer(line)
199
200         self.http_get_dat(save_line_and_append_to_buffer)
201         dat_file.close()
202
203     def load_dat(self):
204         self.size = 0
205         self.num = 0
206
207         self.title = datfile.get_title_from_dat(
208             self.bbs, self.board, self.thread)
209         if self.title:
210             self.window.set_title(self.title)
211
212         datfile.load_dat(self.bbs, self.board, self.thread,
213                          self.append_rawres_to_buffer)
214         self.textview.scroll_to_mark(self.textbuffer.get_insert(), 0)
215
216
217     def append_rawres_to_buffer(self, line):
218         self.size += len(line)
219         self.num += 1
220
221         h = lambda name,mail,date,msg: self.reselems_to_buffer(
222             self.num, name, mail, date, msg)
223
224         datfile.split_line_to_elems(line.decode("cp932", "replace"), h)
225         
226     def reselems_to_buffer(self, num, name, mail, date, msg):
227         # number
228         self.textbuffer.insert(self.enditer, str(num) + " ")
229
230         # name
231         p = barehtmlparser.BareHTMLParser(self.untiedata_to_buffer)
232         p.feed("<b>" + name + "</b>")
233         p.close()
234
235         # mail
236         self.textbuffer.insert(self.enditer, "[" + mail + "]")
237
238         # date
239         p.feed(date)
240         p.close()
241         self.textbuffer.insert(self.enditer, "\n")
242
243         # msg
244         p.reset_func(self.untiedata_to_buffer_with_leftmargin)
245         p.feed(msg)
246         p.close()
247
248         self.textbuffer.insert(self.enditer, "\n\n")
249
250     def untiedata_to_buffer(self, data, bold, href):
251         if bold:
252             self.textbuffer.insert_with_tags(self.enditer, data, self.boldtag)
253         else:
254             self.textbuffer.insert(self.enditer, data)
255
256     def untiedata_to_buffer_with_leftmargin(self, data, bold, href):
257         if bold:
258             self.textbuffer.insert_with_tags(self.enditer, data,
259                                              self.boldtag, self.leftmargintag)
260         else:
261             self.textbuffer.insert_with_tags(self.enditer, data,
262                                              self.leftmargintag)