OSDN Git Service

split timeline class and statusvies class
authorHirotaka Kawata <info@techno-st.net>
Tue, 20 Jul 2010 12:29:53 +0000 (21:29 +0900)
committerHirotaka Kawata <info@techno-st.net>
Tue, 20 Jul 2010 12:29:53 +0000 (21:29 +0900)
gwit/main.py
gwit/statusdetail.py
gwit/statusview.py [new file with mode: 0644]
gwit/timeline.py

index 6dd450f..b4066c9 100644 (file)
@@ -93,10 +93,10 @@ class Main:
         self.charcount = self.builder.get_object("label1")
         self.dsettings = self.builder.get_object("dialog_settings")
         
-        menu_tweet = self.builder.get_object("menu_tweet")
-        self.builder.get_object("menuitem_tweet").set_submenu(menu_tweet)
-        menu_timeline = self.builder.get_object("menu_timeline")
-        self.builder.get_object("menuitem_timeline").set_submenu(menu_timeline)
+        self.menu_tweet = self.builder.get_object("menu_tweet")
+        self.builder.get_object("menuitem_tweet").set_submenu(self.menu_tweet)
+        self.menu_timeline = self.builder.get_object("menu_timeline")
+        self.builder.get_object("menuitem_timeline").set_submenu(self.menu_timeline)
         
         # set tools
         self.twtools = twittertools.TwitterTools()
@@ -177,20 +177,19 @@ class Main:
         # Create Timeline Object
         tl = Timeline(self.twitter, self.icons, self.iconmode)
         
-        interval = self.get_default_interval(method)
-        
         # Start sync timeline
+        interval = self.get_default_interval(method)
         tl.init_timeline(method, interval, self.scounts, args, kwargs)
-        tl.new_timeline = self.new_timeline
+        tl.view.new_timeline = self.new_timeline
         
         # Add Notebook (Tab view)
-        self.new_tab(tl.scrwin, label, tl)
-
+        self.new_tab(tl, label, tl)
+        
        # Set color
-        tl.set_color(self.status_color)
+        tl.view.set_color(self.status_color)
         
         if method != "mentions":
-            tl.on_status_added = self.on_status_added
+            tl.view.on_status_added = self.on_status_added
         
         # Set API Limit label
         tl.timeline.on_timeline_refresh = self.on_timeline_refresh
@@ -198,12 +197,12 @@ class Main:
         tl.timeline.on_twitterapi_error = self.on_twitterapi_error
         
         # Put tweet information to statusbar
-        tl.on_status_selection_changed = self.on_status_selection_changed
+        tl.view.on_status_selection_changed = self.on_status_selection_changed
         # Reply on double click
-        tl.on_status_activated = self.on_status_activated
+        tl.view.on_status_activated = self.on_status_activated
         
+        tl.view.add_popup(self.menu_tweet)
         tl.start_timeline()        
-        tl.add_popup(self.builder.get_object("menu_tweet"))
     
     # Append Tab to Notebook
     def new_tab(self, widget, label, timeline = None):
@@ -237,8 +236,9 @@ class Main:
     
     def get_selected_status(self):
         n = self.notebook.get_current_page()
-        return self.timelines[n].get_selected_status()
-    
+        if self.timelines[n] != None:
+            return self.timelines[n].view.get_selected_status()
+        
     def get_current_tab(self):
         return self.notebook.get_current_page()
 
@@ -267,7 +267,8 @@ class Main:
     # Reply to selected status
     def reply_to_selected_status(self):
         status = self.get_selected_status()
-
+        self.reply_to_status(status)
+    
     def reply_to_status(self, status):
         self.re = status.id
         name = status.user.screen_name
@@ -484,7 +485,7 @@ class Main:
     # Tab right clicked
     def on_notebook_tabbar_button_press(self, widget, event):
         if event.button == 3:
-            self.builder.get_object("menu_timeline").popup(None, None, None, event.button, event.time)
+            self.menu_timeline.popup(None, None, None, event.button, event.time)
     
     # Character count
     def on_textbuffer1_changed(self, textbuffer):
@@ -512,8 +513,8 @@ class Main:
     # disable menu when switched tab
     def on_notebook1_switch_page(self, notebook, page, page_num):
         self.builder.get_object("menuitem_tweet").set_sensitive(False)
-        menu_timeline = self.builder.get_object("menuitem_timeline")
-        menu_timeline.set_sensitive(False)        
+        menuitem_timeline = self.builder.get_object("menuitem_timeline")
+        menuitem_timeline.set_sensitive(False)
         if page_num < 0: return
         
         tl = self.timelines[page_num]
@@ -545,7 +546,7 @@ class Main:
                 self.builder.get_object("menuitem_time_30").set_active(True)
             
             self._toggle_change_flg = False
-            menu_timeline.set_sensitive(True)
+            menuitem_timeline.set_sensitive(True)
     
     ########################################
     # Tweet menu event
@@ -576,7 +577,8 @@ class Main:
     # Status detail
     def on_menuitem_detail_activate(self, menuitem):
         status = self.get_selected_status()
-        detail = StatusDetail(status, self.twitter, self.icons)
+        detail = StatusDetail(status, self.twitter, self.icons, self.iconmode)
+        #detail.view.add_popup(self.menu_tweet)
         self.new_tab(detail, "S: %d" % status.id)
     
     # favorite
@@ -715,7 +717,7 @@ class Main:
                              self.builder.get_object("entry_color_selected_user").get_text())
         for t in self.timelines:
             if t != None:
-                t.set_color(self.status_color)
+                t.view.set_color(self.status_color)
         
         self.save_settings()
         self.dsettings.hide()
index 5bec1e0..78b062e 100644 (file)
@@ -31,11 +31,12 @@ import gtk
 import gobject
 
 import threading
+from statusview import StatusView
 
 class StatusDetail(gtk.VPaned):
     _old_alloc = None
     
-    def __init__(self, status, twitterapi, icons):
+    def __init__(self, status, twitterapi, icons, iconmode):
         gtk.VPaned.__init__(self)
         
         ico = gtk.image_new_from_pixbuf(icons.get(status.user))
@@ -57,16 +58,11 @@ class StatusDetail(gtk.VPaned):
         hbox.pack_start(ico, expand = False, fill = False)
         hbox.pack_start(text)
         
-        self.store = gtk.ListStore(gtk.gdk.Pixbuf, str, gobject.TYPE_INT64)
-        treeview = gtk.TreeView(self.store)
-        treeview.set_headers_visible(False)
-        treeview.set_rules_hint(True)
-        treeview.append_column(gtk.TreeViewColumn("Icon", gtk.CellRendererPixbuf(), pixbuf = 0))
-        treeview.append_column(gtk.TreeViewColumn("Status", gtk.CellRendererText(), markup = 1))
+        self.view = StatusView(twitterapi, icons, iconmode)
         
         win = gtk.ScrolledWindow()
         win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-        win.add(treeview)
+        win.add(self.view)
         
         self.pack1(hbox, shrink = False)
         self.pack2(win)
@@ -86,11 +82,8 @@ class StatusDetail(gtk.VPaned):
         
         while i != None:
             if i in self.twitter.statuses:
+                self.view.prepend_new_statuses([i])
                 s = self.twitter.statuses[i]
-                self.store.append(
-                    (self.icons.get(s.user),
-                     "<b>%s</b>\n%s" % (s.user.screen_name, s.text),
-                     s.user.id))
                 i = s.in_reply_to_status_id
             else:
                 statuses = self.twitter.api_wrapper(
diff --git a/gwit/statusview.py b/gwit/statusview.py
new file mode 100644 (file)
index 0000000..e23f18f
--- /dev/null
@@ -0,0 +1,371 @@
+#-*- coding: utf-8 -*-
+
+'''Treeview for timeline statuses
+'''
+
+################################################################################
+#
+# Copyright (c) 2010 University of Tsukuba Linux User Group
+#
+# This file is part of "gwit".
+#
+# "gwit" 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 3 of the License, or
+# (at your option) any later version.
+#
+# "gwit" 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 "gwit".  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gobject
+import pango
+
+import re
+import twittertools
+
+import time
+import threading
+import webbrowser
+
+class StatusView(gtk.TreeView):
+    color = (None, None, None, None, None)
+    pmenu = None
+    
+    def __init__(self, twitter, icons, iconmode):
+        gtk.TreeView.__init__(self)
+        self.twitter = twitter
+        self.icons = icons
+        
+        self.store = gtk.ListStore(
+            gtk.gdk.Pixbuf, str,
+            gobject.TYPE_INT64, gobject.TYPE_INT64, str)
+        self.store.set_sort_column_id(2, gtk.SORT_DESCENDING)
+        
+        self.set_model(self.store)
+        self.set_headers_visible(False)
+        self.set_rules_hint(True)
+        self.connect("size-allocate", self.on_treeview_width_changed)
+        self.connect("cursor-changed", self.on_treeview_cursor_changed)
+        self.connect("row_activated", self.on_treeview_row_activated)
+        self.connect("destroy", self.on_treeview_destroy)
+        
+        # Setup icon column (visible is False if no-icon)
+        cell_p = gtk.CellRendererPixbuf()
+        col_icon = gtk.TreeViewColumn("Icon", cell_p, pixbuf = 0)
+        col_icon.set_visible(iconmode)
+        
+        # Setup status column
+        cell_t = gtk.CellRendererText()
+        cell_t.set_property("wrap-mode", pango.WRAP_WORD)
+        col_status = gtk.TreeViewColumn("Status", cell_t, markup = 1)
+        
+        # Add background color at column_id 4
+        col_icon.add_attribute(cell_p, "cell-background", 4)
+        col_status.add_attribute(cell_t, "cell-background", 4)
+        self.append_column(col_icon)
+        self.append_column(col_status)
+        
+        # Add timeline to IconStore 
+        if iconmode:
+            self.icons.add_store(self.store, 3)
+        
+        # Tools setup
+        self.twtools = twittertools.TwitterTools()
+        self.noent_amp = re.compile("&(?![A-Za-z]+;)")
+        self.added = False
+    
+    # Get selected status
+    def get_selected_status(self):
+        path, column = self.get_cursor()
+        if path == None:
+            return None
+        else:
+            return self.get_status(path)
+    
+    # Get status from treeview path
+    def get_status(self, path):
+        i = self.store[path][2]
+        return self.twitter.statuses[i]
+    
+    # get status from mouse point
+    def get_status_from_point(self, x, y):
+        path = self.get_path_at_pos(x, y)
+        it = self.store.get_iter(path[0])
+        sid = self.store.get_value(it, 2)
+        return self.twitter.statuses[sid]
+
+    # Add popup menu
+    def add_popup(self, menu):
+        self.pmenu = menu
+        self.connect("button-press-event", self.on_treeview_button_press)
+    
+    # Set background color
+    def set_color(self, colortuple):
+        self.color = tuple(colortuple)
+    
+    # Replace & -> &amp;
+    def _replace_amp(self, string):
+        amp = string.find('&')
+        if amp == -1: return string
+        
+        entity_match = self.noent_amp.finditer(string)
+        
+        for i, e in enumerate(entity_match):
+            string = "%s&amp;%s" % (
+                string[:e.start() + (4 * i)],
+                string[e.start() + (4 * i) + 1:])
+        
+        return string    
+    
+    # Tweet menu setup
+    def menu_setup(self, status):
+        # Get Urls
+        urls = self.twtools.get_urls(status.text if "retweeted_status" not in status.keys()
+                                     else status.retweeted_status.text)
+        # Get mentioned users
+        users = self.twtools.get_users(status.text)
+        
+        # URL Menu
+        m = gtk.Menu()
+        if urls:
+            # if exist url in text, add menu
+            for i in urls:
+                label = "%s..." % i[:47] if len(i) > 50 else i
+                
+                # Menuitem create
+                item = gtk.ImageMenuItem(label)
+                item.set_image(gtk.image_new_from_stock("gtk-new", gtk.ICON_SIZE_MENU))
+                # Connect click event (open browser)
+                item.connect("activate", self.on_menuitem_url_clicked, i)
+                # append to menu
+                m.append(item)
+        else:
+            # not, show None
+            item = gtk.MenuItem("None")
+            item.set_sensitive(False)
+            m.append(item)
+        
+        # urls submenu append
+        self.pmenu.get_children()[-1].set_submenu(m)
+        
+        # Mentioned User Menu
+        mm = gtk.Menu()
+        if users:
+            for i in users:
+                # Menuitem create
+                item = gtk.ImageMenuItem("@%s" % i.replace("_", "__"))
+                item.set_image(gtk.image_new_from_stock("gtk-add", gtk.ICON_SIZE_MENU))
+                # Connect click event (add tab)
+                item.connect("activate", self.on_menuitem_user_clicked, i)
+                # append to menu
+                mm.append(item)
+        else:
+            # not, show None
+            item = gtk.MenuItem("None")
+            item.set_sensitive(False)
+            mm.append(item)
+        
+        self.pmenu.get_children()[-2].set_submenu(mm)
+        
+        # Show popup menu
+        m.show_all()
+        mm.show_all()
+    
+    
+    ########################################
+    # Execute in Background Thread Methods    
+    
+    # Prepend new stat uses
+    def prepend_new_statuses(self, new_ids):
+        # pack New Status
+        statuses = [self.status_pack(i) for i in new_ids]
+        
+        # Set added flag (for auto scroll
+        self.added = True
+        
+        # New Status Prepend to Liststore (Add row)
+        gtk.gdk.threads_enter()
+        for i in statuses:
+            self.store.prepend(i)
+        gtk.gdk.threads_leave()
+        
+        self.color_status()
+    
+    def status_pack(self, i):
+        status = self.twitter.statuses[i]
+        background = None
+        
+        name = status.user.screen_name
+        
+        if status.retweeted_status != None:
+            rtstatus = status
+            status = status.retweeted_status
+            name = "%s <span foreground='#333333'><small>- Retweeted by %s</small></span>" % (
+                status.user.screen_name, rtstatus.user.screen_name)
+        
+        # colord url
+        text = self.twtools.get_colored_url(status.text)
+        # replace no entity & -> &amp;
+        text = self._replace_amp(text)
+        
+        if status.user.id in self.twitter.followers or self.twitter.followers == None:
+            # Bold screen_name if follower
+            tmpl = "<b>%s</b>\n%s"
+        else:
+            # or gray
+            tmpl = "<span foreground='#666666'><b>%s</b></span>\n%s"
+        
+        # Bold screen_name
+        message = tmpl % (name, text)
+        
+        self.on_status_added(i)
+        
+        return (self.icons.get(status.user),
+                message,
+                long(i), long(status.user.id),
+                background)
+    
+    # Color status
+    def color_status(self, status = None):
+        t = threading.Thread(target=self.color_status_in_thread, args=(status,))
+        t.setName("color_status")
+        t.start()
+    
+    def color_status_in_thread(self, status = None):
+        myname = self.twitter.myname
+        myid = self.twitter.me.id if self.twitter.me != None else -1
+        
+        # if not set target status
+        if status == None:
+            status = self.get_selected_status()
+        
+        i = self.store.get_iter_first()
+        while i:
+            bg = None   
+            
+            id = self.store.get_value(i, 2)
+            s = self.twitter.statuses[id]
+            u = s.user
+            
+            if u.id == myid:
+                # My status (Blue)
+                bg = self.color[0]
+            elif s.in_reply_to_user_id == myid or \
+                    s.text.find("@%s" % myname) != -1:
+                # Reply to me (Red)
+                bg = self.color[1]
+            
+            if status:
+                if s.id == status.in_reply_to_status_id:
+                    # Reply to (Orange)
+                    bg = self.color[2]
+                elif u.id == status.in_reply_to_user_id:
+                    # Reply to other (Yellow)
+                    bg = self.color[3]
+                elif u.id == status.user.id:
+                    # Selected user (Green)
+                    bg = self.color[4]
+            
+            gtk.gdk.threads_enter()
+            self.store.set_value(i, 4, bg)
+            gtk.gdk.threads_leave()
+            
+            i = self.store.iter_next(i)
+
+    
+    ########################################
+    # Gtk Signal Events    
+    
+    # Status Clicked
+    def on_treeview_cursor_changed(self, treeview):
+        status = self.get_selected_status()
+        self.color_status(status)
+        if self.pmenu != None:
+            self.menu_setup(status)
+        self.on_status_selection_changed(status)
+    
+    # Status double clicked
+    def on_treeview_row_activated(self, treeview, path, view_column):
+        status = self.get_status(path)
+        self.on_status_activated(status)
+    
+    # Menu popup
+    def on_treeview_button_press(self, widget, event):
+        if event.button == 3 and self.pmenu != None:
+            self.pmenu.show_all()
+            if self.get_selected_status().user.screen_name != self.twitter.myname:
+                self.pmenu.get_children()[4].hide()
+            self.pmenu.popup(None, None, None, event.button, event.time)
+    
+    # Treeview width changed Event (text-wrap-width change)
+    def on_treeview_width_changed(self, treeview, allocate):
+        # Get Treeview Width
+        width = treeview.get_allocation().width
+        # Get Treeview Columns
+        columns = treeview.get_columns()
+        
+        # Get !("Status") width
+        width2 = 0
+        for i in columns[:1]:
+            width2 += i.get_property("width")
+        
+        # Set "Status" width
+        cellr = columns[1].get_cell_renderers()
+        cellr[0].set_property("wrap-width", width - width2 - 10)
+        
+        def refresh_text():
+            # Reset all data to change row height
+            i = self.store.get_iter_first()
+            while i:
+                # Maybe no affects performance
+                # if treeview.allocation.width != width:
+                #     break
+                txt = self.store.get_value(i, 1)
+                
+                gtk.gdk.threads_enter()
+                self.store.set_value(i, 1, txt)
+                gtk.gdk.threads_leave()
+                
+                i = self.store.iter_next(i)
+        
+        t = threading.Thread(target=refresh_text)
+        t.setName("refresh_text")
+        t.start()
+    
+    def on_treeview_destroy(self, treeview):
+        self.icons.remove_store(self.store)
+    
+    # dummy events and methods
+    def on_status_added(self, *args, **kwargs): pass
+    def on_status_selection_changed(self, *args, **kwargs): pass
+    def on_status_activated(self, *args, **kwargs): pass
+    def new_timeline(self, *args, **kwargs): pass
+    
+    ########################################
+    # Tweet menu event
+    
+    # Open Web browser if url menuitem clicked
+    def on_menuitem_url_clicked(self, menuitem, url):
+        webbrowser.open_new_tab(url)
+    
+    # Add user timeline tab if mentioned user menu clicked
+    def on_menuitem_user_clicked(self, menuitem, sname):
+        user = self.twitter.get_user_from_screen_name(sname)
+        if user != None:
+            self.new_timeline("@%s" % sname, "user_timeline", user = user.id)
+        else:
+            # force specify screen_name if not found
+            self.new_timeline("@%s" % sname, "user_timeline", user = sname, sn = True)
+        
+        return True
index 752c230..d88218f 100644 (file)
@@ -1,6 +1,6 @@
 #-*- coding: utf-8 -*-
 
-'''Implementation of timeline view
+'''Implementation of timeline scrolled window
 '''
 
 ################################################################################
 #
 ################################################################################
 
+
 import pygtk
 pygtk.require('2.0')
 import gtk
-import gobject
-import pango
-
-import re
-import twittertools
 
-import time
-import threading
-import webbrowser
+from statusview import StatusView
 
-class Timeline:
-    color = (None, None, None, None, None)
-    
+class Timeline(gtk.ScrolledWindow):
     def __init__(self, api, icons, iconmode):
+        gtk.ScrolledWindow.__init__(self)
         self.twitter = api
         self.icons = icons
         
-        # Base scrolledwindow
-        self.scrwin = gtk.ScrolledWindow()
-        
-        # Liststore column setting
-        self.store = gtk.ListStore(
-            gtk.gdk.Pixbuf, str,
-            gobject.TYPE_INT64, gobject.TYPE_INT64, str)
-        self.store.set_sort_column_id(2, gtk.SORT_DESCENDING)
-        self.treeview = gtk.TreeView(self.store)
-        
         # Add treeview to scrolledwindow
-        self.scrwin.add(self.treeview)
-        self.scrwin.set_shadow_type(gtk.SHADOW_IN)
+        self.view = StatusView(api, icons, iconmode)
+        self.add(self.view)
+
         # Scrollbar policy
-        self.scrwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
-        
-        self.treeview.set_headers_visible(False)
-        self.treeview.set_rules_hint(True)
-        self.treeview.connect("size-allocate", self.on_treeview_width_changed)
-        self.treeview.connect("cursor-changed", self.on_treeview_cursor_changed)
-        self.treeview.connect("row_activated", self.on_treeview_row_activated)
-        
-        # Setup icon column (visible is False if no-icon)
-        cell_p = gtk.CellRendererPixbuf()
-        col_icon = gtk.TreeViewColumn("Icon", cell_p, pixbuf = 0)
-        col_icon.set_visible(iconmode)
-        
-        # Setup status column
-        cell_t = gtk.CellRendererText()
-        cell_t.set_property("wrap-mode", pango.WRAP_WORD)
-        col_status = gtk.TreeViewColumn("Status", cell_t, markup = 1)
-        
-        col_icon.add_attribute(cell_p, "cell-background", 4)
-        col_status.add_attribute(cell_t, "cell-background", 4)
-        self.treeview.append_column(col_icon)
-        self.treeview.append_column(col_status)
+        self.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
+        self.set_shadow_type(gtk.SHADOW_IN)
+        self.connect("destroy", self.on_destroy)
         
         # Auto scroll to top setup
-        vadj = self.scrwin.get_vadjustment()
+        vadj = self.get_vadjustment()
         self.vadj_upper = vadj.upper
         self.vadj_lock = False
         vadj.connect("changed", self.on_vadjustment_changed)
-        
-        # Tools setup
-        self.twtools = twittertools.TwitterTools()
-        self.noent_amp = re.compile("&(?![A-Za-z]+;)")
+        vadj.connect("value-changed", self.on_vadjustment_value_changed)
     
     # Start Sync Timeline (new twitter timeline thread create)
     def init_timeline(self, method, interval, counts, args, kwargs):
         self.timeline = self.twitter.create_timeline(method, interval, counts, args, kwargs)
         
         # Set Event Hander (exec in every get timeline
-        self.timeline.reloadEventHandler = self.prepend_new_statuses    
-        # Add timeline to IconStore
-        self.icons.add_store(self.store, 3)
+        self.timeline.reloadEventHandler = self.view.prepend_new_statuses
     
     def start_timeline(self):
         # Start Timeline sync thread
@@ -110,303 +70,30 @@ class Timeline:
             # lock flag set (unlock)
             self.timeline.lock.set()
     
-    # Add popup menu
-    def add_popup(self, menu):
-        self.pmenu = menu
-        self.treeview.connect("button-press-event", self.on_treeview_button_press)
-    
     # Get timeline ids
     def get_timeline_ids(self):
         return self.timeline.timeline
     
-    # Get selected status
-    def get_selected_status(self):
-        path, column = self.treeview.get_cursor()
-        if path == None:
-            return None
-        else:
-            return self.get_status(path)
-    
-    # Get status from treeview path
-    def get_status(self, path):
-        i = self.store[path][2]
-        return self.twitter.statuses[i]
-    
-    # get status from mouse point
-    def get_status_from_point(self, x, y):
-        path = self.treeview.get_path_at_pos(x, y)
-        it = self.store.get_iter(path[0])
-        sid = self.store.get_value(it, 2)
-        return self.twitter.statuses[sid]
-    
-    # Tweet menu setup
-    def menu_setup(self, status):
-        # Get Urls
-        urls = self.twtools.get_urls(status.text if "retweeted_status" not in status.keys()
-                                     else status.retweeted_status.text)
-        # Get mentioned users
-        users = self.twtools.get_users(status.text)
-        
-        # URL Menu
-        m = gtk.Menu()
-        if urls:
-            # if exist url in text, add menu
-            for i in urls:
-                label = "%s..." % i[:47] if len(i) > 50 else i
-                
-                # Menuitem create
-                item = gtk.ImageMenuItem(label)
-                item.set_image(gtk.image_new_from_stock("gtk-new", gtk.ICON_SIZE_MENU))
-                # Connect click event (open browser)
-                item.connect("activate", self.on_menuitem_url_clicked, i)
-                # append to menu
-                m.append(item)
-        else:
-            # not, show None
-            item = gtk.MenuItem("None")
-            item.set_sensitive(False)
-            m.append(item)
-        
-        # urls submenu append
-        self.pmenu.get_children()[-1].set_submenu(m)
-        
-        # Mentioned User Menu
-        mm = gtk.Menu()
-        if users:
-            for i in users:
-                # Menuitem create
-                item = gtk.ImageMenuItem("@%s" % i.replace("_", "__"))
-                item.set_image(gtk.image_new_from_stock("gtk-add", gtk.ICON_SIZE_MENU))
-                # Connect click event (add tab)
-                item.connect("activate", self.on_menuitem_user_clicked, i)
-                # append to menu
-                mm.append(item)
-        else:
-            # not, show None
-            item = gtk.MenuItem("None")
-            item.set_sensitive(False)
-            mm.append(item)
-        
-        self.pmenu.get_children()[-2].set_submenu(mm)
-        
-        # Show popup menu
-        m.show_all()
-        mm.show_all()
-    
-    # Replace & -> &amp;
-    def _replace_amp(self, string):
-        amp = string.find('&')
-        if amp == -1: return string
-        
-        entity_match = self.noent_amp.finditer(string)
-        
-        for i, e in enumerate(entity_match):
-            string = "%s&amp;%s" % (
-                string[:e.start() + (4 * i)],
-                string[e.start() + (4 * i) + 1:])
-        
-        return string
-
-    def set_color(self, colortuple):
-        self.color = tuple(colortuple)
-    
-    def destroy(self):
-        self.timeline.destroy()
-        self.icons.remove_store(self.store)
-        self.scrwin.destroy()
-        self.treeview.destroy()
-    
-    ########################################
-    # Execute in Background Thread Methods
-
-    # Prepend new statuses
-    def prepend_new_statuses(self, new_ids):
-        # Auto scroll lock if adjustment changed manually
-        vadj = self.scrwin.get_vadjustment()
-        self.vadj_lock = True if vadj.value != 0.0 else False
-        
-        # pack New Status
-        statuses = [self.status_pack(i) for i in new_ids]
-        
-        # New Status Prepend to Liststore (Add row)
-        gtk.gdk.threads_enter()
-        for i in statuses:
-            self.store.prepend(i)
-        gtk.gdk.threads_leave()
-        
-        self.color_status()
-    
-    def status_pack(self, i):
-        status = self.twitter.statuses[i]
-        background = None
-        
-        name = status.user.screen_name
-        
-        if status.retweeted_status != None:
-            rtstatus = status
-            status = status.retweeted_status
-            name = "%s <span foreground='#333333'><small>- Retweeted by %s</small></span>" % (
-                status.user.screen_name, rtstatus.user.screen_name)
-        
-        # colord url
-        text = self.twtools.get_colored_url(status.text)
-        # replace no entity & -> &amp;
-        text = self._replace_amp(text)
-        
-        if status.user.id in self.twitter.followers or self.twitter.followers == None:
-            # Bold screen_name if follower
-            tmpl = "<b>%s</b>\n%s"
-        else:
-            # or gray
-            tmpl = "<span foreground='#666666'><b>%s</b></span>\n%s"
-        
-        # Bold screen_name
-        message = tmpl % (name, text)
-        
-        self.on_status_added(i)
-        
-        return (self.icons.get(status.user),
-                message,
-                long(i), long(status.user.id),
-                background)
-    
-    # Color status
-    def color_status(self, status = None):
-        t = threading.Thread(target=self.color_status_in_thread, args=(status,))
-        t.setName("color_status")
-        t.start()
-    
-    def color_status_in_thread(self, status = None):
-        myname = self.twitter.myname
-        myid = self.twitter.me.id if self.twitter.me != None else -1
-        
-        # if not set target status
-        if status == None:
-            status = self.get_selected_status()
-        
-        i = self.store.get_iter_first()
-        while i:
-            bg = None   
-            
-            id = self.store.get_value(i, 2)
-            s = self.twitter.statuses[id]
-            u = s.user
-            
-            if u.id == myid:
-                # My status (Blue)
-                bg = self.color[0]
-            elif s.in_reply_to_user_id == myid or \
-                    s.text.find("@%s" % myname) != -1:
-                # Reply to me (Red)
-                bg = self.color[1]
-            
-            if status:
-                if s.id == status.in_reply_to_status_id:
-                    # Reply to (Orange)
-                    bg = self.color[2]
-                elif u.id == status.in_reply_to_user_id:
-                    # Reply to other (Yellow)
-                    bg = self.color[3]
-                elif u.id == status.user.id:
-                    # Selected user (Green)
-                    bg = self.color[4]
-            
-            gtk.gdk.threads_enter()
-            self.store.set_value(i, 4, bg)
-            gtk.gdk.threads_leave()
-            
-            i = self.store.iter_next(i)
-    
-    # dummy events and methods
-    def on_status_added(self, *args, **kwargs): pass
-    def on_status_selection_changed(self, *args, **kwargs): pass
-    def on_status_activated(self, *args, **kwargs): pass
-    def new_timeline(self, *args, **kwargs): pass
-    
     
     ########################################
-    # Gtk Signal Events
-    
-    # Treeview width changed Event (text-wrap-width change)
-    def on_treeview_width_changed(self, treeview, allocate):
-        # Get Treeview Width
-        width = treeview.get_allocation().width
-        # Get Treeview Columns
-        columns = treeview.get_columns()
-        
-        # Get !("Status") width
-        width2 = 0
-        for i in columns[:1]:
-            width2 += i.get_property("width")
-        
-        # Set "Status" width
-        cellr = columns[1].get_cell_renderers()
-        cellr[0].set_property("wrap-width", width - width2 - 10)
-        
-        def refresh_text():
-            # Reset all data to change row height
-            i = self.store.get_iter_first()
-            while i:
-                # Maybe no affects performance
-                # if treeview.allocation.width != width:
-                #     break
-                txt = self.store.get_value(i, 1)
-                
-                gtk.gdk.threads_enter()
-                self.store.set_value(i, 1, txt)
-                gtk.gdk.threads_leave()
-                
-                i = self.store.iter_next(i)
-        
-            vadj = self.scrwin.get_vadjustment()
-            self.vadj_upper = vadj.upper
-        
-        t = threading.Thread(target=refresh_text)
-        t.setName("refresh_text")
-        t.start()
+    # Gtk Signal Events    
     
     # Scroll to top if upper(list length) changed Event
     def on_vadjustment_changed(self, adj):
         if not self.vadj_lock and self.vadj_upper < adj.upper:
-            if len(self.store):
-                self.treeview.scroll_to_cell((0,))
-            self.vadj_upper = adj.upper
-    
-    # Status Clicked
-    def on_treeview_cursor_changed(self, treeview):
-        status = self.get_selected_status()
-        self.color_status(status)
-        self.menu_setup(status)
-        self.on_status_selection_changed(status)
-
-    # Status double clicked
-    def on_treeview_row_activated(self, treeview, path, view_column):
-        status = self.get_status(path)
-        self.on_status_activated(status)
-    
-    # Menu popup
-    def on_treeview_button_press(self, widget, event):
-        if event.button == 3:
-            self.pmenu.show_all()
-            if self.get_selected_status().user.screen_name != self.twitter.myname:
-                self.pmenu.get_children()[4].hide()
-            self.pmenu.popup(None, None, None, event.button, event.time)
-    
-    
-    ########################################
-    # Tweet menu event
+            if len(self.view.store):
+                self.view.scroll_to_cell((0,))
+                self.view.added = False
+        
+        self.vadj_upper = adj.upper
+        self.vadj_len = len(self.view.store)
     
-    # Open Web browser if url menuitem clicked
-    def on_menuitem_url_clicked(self, menuitem, url):
-        webbrowser.open_new_tab(url)
+    def on_vadjustment_value_changed(self, adj):
+        if adj.value == 0.0:
+            self.vadj_lock = False
+        elif self.vadj_upper == adj.upper and not self.view.added:
+            self.vadj_lock = True
     
-    # Add user timeline tab if mentioned user menu clicked
-    def on_menuitem_user_clicked(self, menuitem, sname):
-        user = self.twitter.get_user_from_screen_name(sname)
-        if user != None:
-            self.new_timeline("@%s" % sname, "user_timeline", user = user.id)
-        else:
-            # force specify screen_name if not found
-            self.new_timeline("@%s" % sname, "user_timeline", user = sname, sn = True)
-        
-        return True
+    def on_destroy(self, widget):
+        self.timeline.destroy()
+        self.view.destroy()