import winwrapbase
import bookmark_list
import bookmark_window
+import thread_view
GLADE_FILENAME = "thread_window.glade"
winwrap.load(update)
# jump to the res if necessary.
- winwrap.jump_to_res(bbs_type.uri)
+ strict_uri = bbs_type.get_thread_uri()
+ if (bbs_type.uri != strict_uri and
+ bbs_type.uri.startswith(strict_uri)):
+ resnum = bbs_type.uri[len(strict_uri):]
+ match = re.match("\d+", resnum)
+ if match:
+ resnum = int(match.group())
+ winwrap.jump_to_res(resnum)
+
+
+class HTMLParserToThreadView:
+ def __init__(self, threadview, resnum):
+ self.threadview = threadview
+ self.resnum = resnum
+ self.initialize()
+
+ def initialize(self):
+ self.buf = ""
+ self.attrlist = pango.AttrList()
+ self.urilist = []
+
+ def from_html_parser(self, data, bold, href):
+ data = data.encode("utf8")
+ start = len(self.buf)
+ end = start + len(data)
+ self.buf += data
+ if bold:
+ attr = pango.AttrWeight(pango.WEIGHT_BOLD, start, end)
+ self.attrlist.insert(attr)
+ if href:
+ attr = pango.AttrUnderline(pango.UNDERLINE_SINGLE, start, end)
+ self.attrlist.insert(attr)
+ self.urilist.append((start, end, href))
+
+ def to_thread_view(self, marginleft):
+ layout = self.threadview.create_pango_layout(self.buf)
+ layout.set_wrap(pango.WRAP_CHAR)
+ layout.posY = 0
+ layout.resnum = self.resnum
+ layout.marginleft = marginleft
+ layout.set_attributes(self.attrlist)
+ layout.urilist = self.urilist
+ gobject.idle_add(self.threadview.add_layout, layout)
+ self.initialize()
class WinWrap(winwrapbase.WinWrapBase):
hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2)
regular_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM)
-
def __init__(self, uri):
from BbsType import bbs_type_judge_uri
from BbsType import bbs_type_exception
self.window = self.widget_tree.get_widget("thread_window")
self.toolbar = self.widget_tree.get_widget("toolbar")
self.toolbar.unset_style()
- self.statusbar = self.widget_tree.get_widget("appbar")
- self.textview = self.widget_tree.get_widget("textview")
- self.textview.drag_dest_unset()
+ self.statusbar = self.widget_tree.get_widget("statusbar")
+ self.vbox = self.widget_tree.get_widget("vbox")
+
+ self.threadview = thread_view.ThreadView()
+ self.vbox.pack_start(self.threadview)
+ self.vbox.reorder_child(self.threadview, 2)
+
+ self.threadview.on_uri_clicked = self.on_threadview_uri_clicked
+
+ self.threadview.popupmenu = self.widget_tree.get_widget(
+ "popup_threadview_menu")
+ self.threadview.menu_openuri = self.widget_tree.get_widget(
+ "popup_threadview_menu_openuri")
+ self.threadview.menu_copylinkaddress = self.widget_tree.get_widget(
+ "popup_threadview_menu_copylinkaddress")
+ self.threadview.menu_separator_link = self.widget_tree.get_widget(
+ "popup_threadview_menu_separator_link")
+ self.threadview.menu_copyselection = self.widget_tree.get_widget(
+ "popup_threadview_menu_copyselection")
+ self.threadview.menu_openasuri = self.widget_tree.get_widget(
+ "popup_threadview_menu_openasuri")
+ self.threadview.menu_separator_selection = self.widget_tree.get_widget(
+ "popup_threadview_menu_separator_selection")
self.initialize_buffer()
- self.hint = HintWrap()
-
sigdic = {"on_refresh_activate": self.update,
"on_compose_activate": self.on_compose_clicked,
"on_toolbar_activate": self.on_toolbar_activate,
"on_add_bookmark_activate": self.on_add_bookmark_activate,
"on_manage_bookmarks_activate": \
self.on_manage_bookmarks_activate,
+ "on_popup_threadview_menu_openuri_activate":
+ self.on_popup_threadview_menu_openuri_activate,
+ "on_popup_threadview_menu_copylinkaddress_activate":
+ self.on_popup_threadview_menu_copylinkaddress_activate,
+ "on_popup_threadview_menu_copyselection_activate":
+ self.on_popup_threadview_menu_copyselection_activate,
+ "on_popup_threadview_menu_openasuri_activate":
+ self.on_popup_threadview_menu_openasuri_activate,
+ "on_popup_threadview_menu_refresh_activate":
+ self.on_popup_threadview_menu_refresh_activate,
"on_thread_window_destroy": self.on_thread_window_destroy}
self.widget_tree.signal_autoconnect(sigdic)
- self.textview.connect("event-after", self.on_event_after)
- self.textview.connect("motion-notify-event",
- self.on_motion_notify_event)
- self.textview.connect("visibility-notify-event",
- self.on_visibility_notify_event)
self.restore()
- self.window.show()
+ self.window.show_all()
self.created()
def initialize_buffer(self):
self.textbuffer = gtk.TextBuffer()
- self.textview.set_buffer(self.textbuffer)
+
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)
+ self.threadview.initialize_buffer()
+
def destroy(self):
self.save()
self.window.destroy()
submit_window.open(self.bbs_type.get_thread_uri())
def on_toolbar_activate(self, widget):
- if self.toolbar.parent.get_property("visible"):
- self.toolbar.parent.hide()
+ if self.toolbar.get_property("visible"):
+ self.toolbar.hide()
else:
- self.toolbar.parent.show()
+ self.toolbar.show()
def on_statusbar_activate(self, widget):
if self.statusbar.get_property("visible"):
else:
self.statusbar.show()
- def on_event_after(self, widget, event):
- if event.type != gtk.gdk.BUTTON_RELEASE:
- return False
- if event.button != 1:
- return False
- buffer = widget.get_buffer()
-
- try:
- start, end = buffer.get_selection_bounds()
- except ValueError:
- pass
- else:
- if start.get_offset() != end.get_offset():
- return False
-
- x, y = widget.window_to_buffer_coords(
- gtk.TEXT_WINDOW_WIDGET, int (event.x), int(event.y))
- iter = widget.get_iter_at_location(x, y)
- if not iter.has_tag(self.leftmargintag) or x > 20:
- tags = iter.get_tags()
- for tag in tags:
- href = tag.get_data("href")
- if href:
- self.on_link_clicked(widget, href)
- return False
-
- def on_link_clicked(self, widget, href):
-
- if not href.startswith("http://"):
- # maybe a relative uri.
- href = urlparse.urljoin(self.bbs_type.get_uri_base(), href)
-
- try:
- uri_opener.open_uri(href)
- except bbs_type_exception.BbsTypeError:
- # not supported, show with the web browser.
- gnome.url_show(href)
-
- def on_motion_notify_event(self, widget, event):
- x, y = widget.window_to_buffer_coords(
- gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y))
- self.set_cursor_if_appropriate(widget, x, y)
- widget.window.get_pointer()
- return False
-
- def on_visibility_notify_event(self, widget, event):
- wx, wy, mod = widget.window.get_pointer()
- bx, by = widget.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, wx, wy)
-
- self.set_cursor_if_appropriate(widget, bx, by)
- return False
-
- def set_cursor_if_appropriate(self, widget, x, y):
- hovering = False
- href = ""
-
- buffer = widget.get_buffer()
- iter = widget.get_iter_at_location(x, y)
- if not iter.has_tag(self.leftmargintag) or x > 20:
- tags = iter.get_tags()
- for tag in tags:
- href = tag.get_data("href")
- if href:
- hovering = True
-
- if hovering != self.hovering_over_link:
- self.hovering_over_link = hovering
-
- if self.hovering_over_link:
- widget.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
- self.hand_cursor)
- if href:
- if not href.startswith("http://"):
- href = urlparse.urljoin(self.bbs_type.get_uri_base(), href)
-
- strict_uri = self.bbs_type.get_thread_uri()
- if href != strict_uri and href.startswith(strict_uri):
- resnum = href[len(strict_uri):]
- match = re.match("\d+", resnum)
- if match:
- resnum = int(match.group())
- if not self.hint.visible(resnum):
- mark = self.textbuffer.get_mark(str(resnum))
- n_mark = self.textbuffer.get_mark(str(resnum+1))
- if mark and n_mark:
- iter = self.textbuffer.get_iter_at_mark(mark)
- n_iter = self.textbuffer.get_iter_at_mark(
- n_mark)
- text = self.textbuffer.get_text(
- iter, n_iter, False)
- if text:
- iter = self.textview.get_iter_at_location(
- x, y)
- rect = self.textview.get_iter_location(
- iter)
- x, y = \
- self.textview.buffer_to_window_coords(
- gtk.TEXT_WINDOW_WIDGET, rect.x, rect.y)
- x, y = self.textview.translate_coordinates(
- self.window, x, y)
- wx, wy = self.window.get_position()
- x += wx
- y += wy
- y += rect.height
- self.hint.show(x, y, text, resnum)
- else:
- widget.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(
- self.regular_cursor)
- self.hint.destroy()
-
def on_close_activate(self, widget):
self.destroy()
except OSError:
traceback.print_exc()
+ def on_threadview_uri_clicked(self, uri):
+
+ if not uri.startswith("http://"):
+ # maybe a relative uri.
+ uri = urlparse.urljoin(self.bbs_type.get_uri_base(), uri)
+
+ try:
+ uri_opener.open_uri(uri)
+ except bbs_type_exception.BbsTypeError:
+ # not supported, show with the web browser.
+ gnome.url_show(uri)
+
+ def on_popup_threadview_menu_openuri_activate(self, widget):
+ self.on_threadview_uri_clicked(widget.uri)
+
+ def on_popup_threadview_menu_copylinkaddress_activate(self, widget):
+ clip = gtk.Clipboard()
+ clip.set_text(widget.uri, len(widget.uri))
+
+ def on_popup_threadview_menu_copyselection_activate(self, widget):
+ pass
+
+ def on_popup_threadview_menu_openasuri_activate(self, widget):
+ pass
+
+ def on_popup_threadview_menu_refresh_activate(self, widget):
+ self.update(widget)
+
def http_get_dat(self, on_get_res):
datfile_url = self.bbs_type.get_dat_uri()
try:
res = opener.open(req)
except urllib2.HTTPError, e:
- gobject.idle_add(
- self.statusbar.set_status, "%d %s" % (e.code, e.msg))
+ pass
+# gobject.idle_add(
+# lambda x: self.statusbar.push(0, x), "%d %s" % (e.code, e.msg))
else:
headers = res.info()
- gobject.idle_add(
- self.statusbar.set_status, "%d %s" % (res.code, res.msg))
+# gobject.idle_add(
+# lambda x: self.statusbar.push(0, x), "%d %s" % (res.code, res.msg))
maybe_incomplete = False
for line in res:
datfile.load_dat_partly(
self.bbs_type, self.append_rawres_to_buffer, self.num+1)
- def do_jump(num):
+ def do_jump():
if self.jump_request_num:
if self.jump_request_num <= num:
- # jump if enable, otherwize jump later.
num = self.jump_request_num
self.jump_request_num = 0
- mark = self.textbuffer.get_mark(str(num))
- if mark:
- self.textview.scroll_to_mark(
- mark, 0, True, 0, 0)
+ self.jump_to_res(num)
else:
- self.jump_to_the_end(num)
+ self.jump_to_the_end()
- gobject.idle_add(do_jump, self.num)
+ gobject.idle_add(do_jump)
def get():
dat_path = misc.get_thread_dat_path(self.bbs_type)
if self.jump_request_num:
num = self.jump_request_num
self.jump_request_num = 0
- mark = self.textbuffer.get_mark(str(num))
- if mark:
- self.textview.scroll_to_mark(mark, 0, True, 0, 0)
+ self.jump_to_res(num)
gobject.idle_add(do_jump)
self.jump_request_num = 0
def load():
-
- def create_mark():
- self.textbuffer.create_mark("1", self.enditer, True)
- gobject.idle_add(create_mark)
-
datfile.load_dat(self.bbs_type, self.append_rawres_to_buffer)
+
def jump():
- def do_jump(num):
+ def do_jump():
if self.jump_request_num:
num = self.jump_request_num
self.jump_request_num = 0
- mark = self.textbuffer.get_mark(str(num))
- if mark:
- self.textview.scroll_to_mark(mark, 0, True, 0, 0)
+ self.jump_to_res(num)
else:
- self.jump_to_the_end(num)
+ self.jump_to_the_end()
- gobject.idle_add(do_jump, self.num)
+ gobject.idle_add(do_jump)
if self.lock():
self.title = title
gobject.idle_add(self.window.set_title, title)
- self.res_queue = []
-
line = line.decode(self.bbs_type.encoding, "replace")
m = self.bbs_type.dat_reg.match(line)
if m:
date += " ID:" + id
self.reselems_to_buffer(num, name, mail, date, msg)
else:
- self.res_queue.append((str(self.num)+"\n", False, None, False))
- self.res_queue.append((line, False, None, True))
+ self.reselems_to_buffer(
+ str(self.num), "Invalid Name", "Invalid Mail",
+ "Invalid Date", line)
print "maybe syntax error.", self.num, line
- def process_res_queue(res_queue, num):
- self.process_queue(res_queue)
- # for next res
- self.textbuffer.create_mark(str(num+1), self.enditer, True)
-
- gobject.idle_add(
- process_res_queue, self.res_queue, self.num)
-
def reselems_to_buffer(self, num, name, mail, date, msg):
- p = barehtmlparser.BareHTMLParser(
- lambda d,b,h: self.res_queue.append((d,b,h,False)))
+ pipe = HTMLParserToThreadView(self.threadview, num)
+ p = barehtmlparser.BareHTMLParser(pipe.from_html_parser)
+
+ # First, create a pango layout for num,name,mail,date
+ # 'margin left' is 0
# number
p.feed(str(num) + " ")
# date
p.feed(date)
- p.feed("<br>")
+ p.flush()
+
+ pipe.to_thread_view(0)
+
+ # Second, create a pango layout for message
+ # 'margin left' is 20
# msg
- p.reset_func(lambda d,b,h: self.res_queue.append((d,b,h,True)))
p.feed(msg.lstrip(" "))
- p.feed("<br><br>")
+ p.feed("<br>")
p.close()
+ pipe.to_thread_view(20)
+
def href_tag(self, href):
tag = self.textbuffer.create_tag(underline=pango.UNDERLINE_SINGLE)
tag.set_data("href", href)
return tag
- def process_queue(self, queue):
- for data, bold, href, margin in queue:
- taglist = []
- if bold:
- taglist.append(self.boldtag)
- if href:
- taglist.append(self.href_tag(href))
- if margin:
- taglist.append(self.leftmargintag)
-
- if taglist:
- self.textbuffer.insert_with_tags(self.enditer, data, *taglist)
- else:
- self.textbuffer.insert(self.enditer, data)
+ def jump(self, value):
+ gobject.idle_add(self.threadview.jump, value)
- def jump_to_the_end(self, num):
- mark = self.textbuffer.get_mark(str(num+1))
- if mark:
- self.textview.scroll_to_mark(mark, 0)
+ def jump_to_layout(self, layout):
+ gobject.idle_add(self.threadview.jump_to_layout, layout)
+
+ def jump_to_the_end(self):
+ gobject.idle_add(self.threadview.jump_to_the_end)
def lock(self):
if self.lock_obj:
self.lock_obj = False
print "unlock"
- def jump_to_res(self, uri):
- strict_uri = self.bbs_type.get_thread_uri()
- if uri != strict_uri and uri.startswith(strict_uri):
- resnum = uri[len(strict_uri):]
- match = re.match("\d+", resnum)
- if match:
- resnum = match.group()
- mark = self.textbuffer.get_mark(resnum)
- if mark:
- self.textview.scroll_to_mark(mark, 0, True, 0, 0)
- elif self.progress:
- # try later.
- self.jump_request_num = int(resnum)
+ def jump_to_res(self, resnum):
+ if self.threadview.jump_to_res(resnum):
+ return
+ self.jump_request_num = resnum
def load(self, update=False):
dat_path = misc.get_thread_dat_path(self.bbs_type)
# save only if dat file exists.
if os.path.exists(dat_path):
window_width, window_height = self.window.get_size()
- toolbar_visible = self.toolbar.parent.get_property("visible")
+ toolbar_visible = self.toolbar.get_property("visible")
statusbar_visible = self.statusbar.get_property("visible")
dirname = os.path.dirname(states_path)
self.window.set_default_size(window_width, window_height)
if not toolbar_visible:
- gobject.idle_add(self.toolbar.parent.hide,
+ gobject.idle_add(self.toolbar.hide,
priority=gobject.PRIORITY_HIGH)
if not statusbar_visible:
gobject.idle_add(self.statusbar.hide,
priority=gobject.PRIORITY_HIGH)
except:
traceback.print_exc()
-
-
-class HintWrap:
-
- def __init__(self):
- self.window = None
- self.textview = None
- self.nums = None
-
- def __del__(self):
- print "destruct"
- self.destroy()
-
- def destroy(self):
- if self.window:
- self.window.destroy()
- self.window = None
- self.textview = None
- self.nums = None
-
- def show(self, x, y, text, *nums):
- self.destroy()
-
- self.window = gtk.Window(gtk.WINDOW_POPUP)
- self.window.set_default_size(400, 10)
- self.window.move(x, y)
-
- self.textview = gtk.TextView()
- self.window.add(self.textview)
-
- self.textview.set_wrap_mode(gtk.WRAP_CHAR)
- self.textview.set_editable(False)
-
- buffer = self.textview.get_buffer()
- buffer.set_text(text.rstrip())
- self.nums = nums
-
- self.window.show_all()
-
- def visible(self, *nums):
- if not self.nums or len(self.nums) != len(nums):
- return False
-
- for num, mun in itertools.izip(self.nums, nums):
- if num != mun:
- return False
- return True