6 ################################################################################
8 # Copyright (c) 2010 University of Tsukuba Linux User Group
10 # This file is part of "gwit".
12 # "gwit" is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
17 # "gwit" is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with "gwit". If not, see <http://www.gnu.org/licenses/>.
25 ################################################################################
48 from timeline import Timeline
49 from statusview import StatusView
50 from timelinethread import BaseThread
51 from twitterapi import TwitterAPI
52 from iconstore import IconStore, IconThread
53 from saveconfig import Config
54 from userselection import UserSelection
55 from listsselection import ListsSelection, ListsView
56 from statusdetail import StatusDetail
57 from twittertools import TwitterTools
58 from getfriendswizard import GetFriendsWizard
63 interval = (300, 300, -1)
65 alloc = gtk.gdk.Rectangle(0, 0, 240, 320)
69 # My status, Mentions to me, Reply to, Reply to user, Selected user
70 status_color = ("#CCCCFF", "#FFCCCC", "#FFCC99", "#FFFFCC", "#CCFFCC")
75 # Streaming API filter
78 _toggle_change_flag = False
81 def __init__(self, screen_name, keys):
82 # Gtk Multithread Setup
83 if sys.platform == "win32":
84 gobject.threads_init()
86 gtk.gdk.threads_init()
91 # force change gtk icon settings
92 settings = gtk.settings_get_default()
93 if not getattr(settings.props, 'gtk_button_images', True):
94 settings.props.gtk_button_images = True
96 # init status timelines
97 self.timelines = list()
99 self.timeline_mention = None
101 # Twitter class instance
102 self.twitter = TwitterAPI(screen_name, *keys)
103 self.twitter.on_tweet_event = self.refresh_tweet
104 self.twitter.on_notify_event = self.notify
108 # set event (show remaining api count)
109 self.twitter.on_twitterapi_requested = self.on_timeline_refresh
110 self.twitter.new_timeline = self.new_timeline
113 self.twitter.update_configuration_bg()
116 self.twitter.get_followers_bg()
117 if not self.userstream: self.twitter.get_following_bg()
120 IconStore.iconmode = self.iconmode
121 self.iconstore = IconStore()
123 # GtkBuilder instance
124 self.builder = gtk.Builder()
126 gladefile = os.path.join(os.path.dirname(__file__), "ui/gwit.ui")
127 self.builder.add_from_file(gladefile)
129 self.builder.connect_signals(self)
131 self.notebook = self.builder.get_object("notebook1")
132 self.textview = self.builder.get_object("textview1")
133 self.btnupdate = self.builder.get_object("button1")
134 self.charcount = self.builder.get_object("label1")
135 self.dsettings = self.builder.get_object("dialog_settings")
136 self.imgtable = self.builder.get_object("table_images")
138 self.menu_tweet = self.builder.get_object("menu_tweet")
139 self.builder.get_object("menuitem_tweet").set_submenu(self.menu_tweet)
140 self.menu_timeline = self.builder.get_object("menu_timeline")
141 self.builder.get_object("menuitem_timeline").set_submenu(
144 # set class variables
145 Timeline.twitter = self.twitter
146 StatusView.twitter = self.twitter
147 StatusView.iconstore = self.iconstore
148 StatusView.iconmode = self.iconmode
149 StatusView.pmenu = self.menu_tweet
150 BaseThread.twitter = self.twitter
151 StatusDetail.twitter = self.twitter
152 StatusDetail.iconstore = self.iconstore
153 ListsView.twitter = self.twitter
154 ListsView.iconstore = self.iconstore
155 UserSelection.twitter = self.twitter
156 UserSelection.iconstore = self.iconstore
157 GetFriendsWizard.twitter = self.twitter
158 IconThread.twitter = self.twitter
160 imgpath = os.path.join(os.path.dirname(__file__), "images/")
161 StatusView.favico_off = gtk.gdk.pixbuf_new_from_file(
162 imgpath + "favorite.png")
163 StatusView.favico_hover = gtk.gdk.pixbuf_new_from_file(
164 imgpath + "favorite_hover.png")
165 StatusView.favico_on = gtk.gdk.pixbuf_new_from_file(
166 imgpath + "favorite_on.png")
168 StatusView.rtico_off = gtk.gdk.pixbuf_new_from_file(
169 imgpath + "retweet.png")
170 StatusView.rtico_hover = gtk.gdk.pixbuf_new_from_file(
171 imgpath + "retweet_hover.png")
172 StatusView.rtico_on = gtk.gdk.pixbuf_new_from_file(
173 imgpath + "retweet_on.png")
178 window = self.builder.get_object("window1")
180 # settings allocation
181 window.resize(self.alloc.width, self.alloc.height)
184 # Start gtk main loop
187 def read_settings(self):
190 d = Config.get_section("DEFAULT")
191 self.interval = eval(d["interval"])
192 self.alloc = eval(d["allocation"])
193 self.scounts = eval(d["counts"])
194 self.iconmode = eval(d["iconmode"])
195 self.userstream = eval(d["userstream"])
196 self.status_color = eval(d["color"])
197 u = Config.get_section(self.twitter.my_name)
198 self.msgfooter = u["footer"]
200 print "[Error] Read settings: %s" % e
202 # Initialize Tabs (in another thread)
203 def initialize(self):
205 for i in (("Home", "home_timeline", self.userstream),
206 ("@Mentions", "mentions")):
207 # create new timeline and tab view
208 deny_close = {"deny_close" : True}
209 self.new_timeline(*i, **deny_close)
211 # Set statusbar (Show API Remaining)
212 self.label_apilimit = gtk.Label()
213 self.statusbar = self.builder.get_object("statusbar1")
214 self.statusbar.pack_start(self.label_apilimit,
215 expand = False, padding = 10)
216 self.statusbar.show_all()
219 users = UserSelection()
220 self.new_tab(users, "Users", deny_close = True)
223 lists = ListsSelection()
224 self.new_tab(lists, "Lists", deny_close = True)
226 self.notebook.set_current_page(0)
229 def close(self, widget):
230 # Save Allocation (window position, size)
231 window = self.builder.get_object("window1")
232 alloc = repr(window.allocation)
233 Config.save("DEFAULT", "allocation", alloc)
236 self.iconstore.stop()
238 def exit(self, widget):
239 # hide window quickly
240 while gtk.events_pending():
244 for i in self.timelines:
247 if i.timeline != None: i.timeline.join(1)
248 if i.stream != None: i.stream.join(1)
253 # Create new Timeline and append to notebook
254 def new_timeline(self, label, method, userstream = False, *args, **kwargs):
255 # Create Timeline Object
258 if method == "filter":
259 if self.get_filter_tab():
260 # filter method only one connection
262 "May create only one standing connection to the Streaming API.\n"
263 "Please close existing Streaming API tab if you want.")
267 # set Streaming API stream
268 tl.set_stream("filter", kwargs)
270 interval = self.get_default_interval(method)
271 tl.set_timeline(method, interval, self.scounts, args, kwargs)
272 # Put error to statubar
273 tl.timeline.on_twitterapi_error = self.on_twitterapi_error
276 tl.view.new_timeline = self.new_timeline
278 # Add Notebook (Tab view)
279 uid = self.new_tab(tl, label, tl, kwargs.get("deny_close", False))
280 if method == "filter": self.set_filter_tab(uid)
283 tl.view.set_color(self.status_color)
285 if method == "mentions":
286 # memory mentions tab_id
287 self.timeline_mention = uid
288 tl.on_status_added = self.on_mentions_added
290 tl.on_status_added = self.on_status_added
292 # Put tweet information to statusbar
293 tl.view.on_status_selection_changed = self.on_status_selection_changed
294 # Reply on double click
295 tl.view.on_status_activated = self.on_status_activated
297 # Set UserStream parameter
299 tl.set_stream("user")
304 # Append Tab to Notebook
305 def new_tab(self, widget, label, timeline = None, deny_close = False):
307 button = gtk.Button()
308 button.set_relief(gtk.RELIEF_NONE)
309 icon = gtk.image_new_from_stock("gtk-close", gtk.ICON_SIZE_MENU)
310 button.set_image(icon)
312 uid = uuid.uuid4().int
313 button.connect("clicked", self.on_tabclose_clicked, uid)
314 n = self.notebook.get_n_pages()
316 self.timelines.append(timeline)
319 lbl = gtk.Label(label)
322 box.pack_start(lbl, True, True)
324 box.pack_start(button, False, False)
328 button.connect("button-press-event",
329 self.on_notebook_tabbar_button_press)
332 self.notebook.append_page(widget, box)
333 self.notebook.show_all()
334 self.notebook.set_current_page(n)
338 def get_selected_status(self):
339 tab = self.get_current_tab()
341 return tab.view.get_selected_status()
343 def get_current_tab_n(self):
344 return self.notebook.get_current_page()
346 def get_current_tab(self):
347 return self.timelines[self.notebook.get_current_page()]
350 def get_textview(self):
351 buf = self.textview.get_buffer()
352 start, end = buf.get_start_iter(), buf.get_end_iter()
353 return buf.get_text(start, end)
356 def set_textview(self, txt, focus = False):
357 buf = self.textview.get_buffer()
359 if focus: self.textview.grab_focus()
362 def add_textview(self, txt, focus = False):
363 buf = self.textview.get_buffer()
364 buf.insert_at_cursor(txt)
365 if focus: self.textview.grab_focus()
368 def clear_textview(self, focus = False):
369 self.set_textview("", focus)
371 # Reply to selected status
372 def reply_to_selected_status(self):
373 status = self.get_selected_status()
374 self.reply_to_status(status)
376 def reply_to_status(self, status):
377 self.twparams["reply_to"] = status.id
378 name = status.user.screen_name
380 buf = self.textview.get_buffer()
381 buf.set_text("@%s " % (name))
382 self.textview.grab_focus()
384 # Color selection dialog run for settings
385 def color_dialog_run(self, title, color, entry):
386 # Sample treeview setup
387 store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
388 treeview = gtk.TreeView(store)
389 cellp = gtk.CellRendererPixbuf()
390 colp = gtk.TreeViewColumn("icon", cellp, pixbuf = 0)
391 cellt = gtk.CellRendererText()
392 cellt.set_property("wrap-mode", pango.WRAP_CHAR)
393 colt = gtk.TreeViewColumn("status", cellt, markup = 1)
394 colp.add_attribute(cellp, "cell-background", 2)
395 colt.add_attribute(cellt, "cell-background", 2)
396 treeview.append_column(colp)
397 treeview.append_column(colt)
399 status = self.twitter.statuses.values()[0]
400 store.append((self.iconstore.get(status.user),
401 "<b>%s</b>\n%s" % (status.user.screen_name, status.text),
404 def on_changed_cursor(view):
405 view.get_selection().unselect_all()
407 treeview.set_property("can-focus", False)
408 treeview.set_headers_visible(False)
409 treeview.connect("cursor-changed", on_changed_cursor)
412 swin = gtk.ScrolledWindow()
414 swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
417 label = gtk.Label("Sample")
418 label.set_justify(gtk.JUSTIFY_LEFT)
419 label.set_padding(0, 10)
420 label.set_alignment(0, 0.5)
423 def on_colorselection_color_changed(colorselection, liststore):
424 strcolor = colorselection.get_current_color().to_string()
425 liststore.set_value(liststore.get_iter_first(), 2, strcolor)
428 dialog = gtk.ColorSelectionDialog(title)
429 selection = dialog.get_color_selection()
430 selection.set_current_color(gtk.gdk.color_parse(color))
431 selection.connect("color-changed",
432 on_colorselection_color_changed, store)
434 selection.pack_start(label)
435 selection.pack_start(swin)
439 if dialog.run() == -5:
440 color = selection.get_current_color().to_string()
441 entry.set_text(color)
449 def status_update_thread(self, status):
450 t = threading.Thread(target = self._status_update, args = (status,))
453 def _status_update(self, status):
456 gtk.gdk.threads_enter()
457 self.textview.set_sensitive(False)
458 self.btnupdate.set_sensitive(False)
459 gtk.gdk.threads_leave()
461 if self.twparams.get("reply_to", None):
462 args["in_reply_to_status_id"] = self.twparams.get("reply_to", None)
463 elif self.msgfooter != "":
464 status = u"%s %s" % (status, self.msgfooter)
466 if self.twparams.get("media", None):
467 resp = self.twitter.api_wrapper(
468 self.twitter.api.status_update_with_media,
469 status, self.twparams["media"], **args)
471 resp = self.twitter.api_wrapper(
472 self.twitter.api.status_update, status, **args)
475 gtk.gdk.threads_enter()
476 self.clear_textview()
477 gtk.gdk.threads_leave()
478 self.twparams.pop("reply_to", None)
479 self.twparams.pop("media", None)
480 self.imgtable.forall(self.imgtable.remove)
481 self.imgtable.set_visible(False)
483 gtk.gdk.threads_enter()
484 self.textview.set_sensitive(True)
485 self.btnupdate.set_sensitive(True)
486 self.textview.grab_focus()
487 gtk.gdk.threads_leave()
489 def get_default_interval(self, method):
490 if method == "home_timeline":
491 interval = self.interval[0]
492 elif method == "mentions":
493 interval = self.interval[1]
495 interval = self.interval[2]
499 def get_filter_tab(self):
500 if not self._filter_tab:
502 elif self.tlhash.get(self._filter_tab, -1) < 0:
503 # filter tab already closed
504 self.set_filter_tab(None)
507 return self._filter_tab
510 def set_filter_tab(cls, tab_id):
511 cls._filter_tab = tab_id
513 def save_settings(self):
514 conf = (("DEFAULT", "interval", self.interval),
515 ("DEFAULT", "counts", self.scounts),
516 ("DEFAULT", "iconmode", self.iconmode),
517 ("DEFAULT", "userstream", self.userstream),
518 ("DEFAULT", "color", self.status_color),
519 (self.twitter.my_name, "footer", self.msgfooter))
520 Config.save_section(conf)
523 def notify(self, title, text, icon_user = None):
525 notify = pynotify.Notification(title, text)
527 icon = self.iconstore.get(icon_user)
528 notify.set_icon_from_pixbuf(icon)
531 def refresh_tweet(self, i):
532 for tl in self.timelines:
533 if tl: tl.view.reset_status_text()
535 def message_dialog(self, message,
536 type = gtk.MESSAGE_ERROR,
537 buttons = gtk.BUTTONS_OK):
538 md = gtk.MessageDialog(type = type, buttons = buttons)
539 md.set_markup(message)
545 ########################################
549 def on_status_added(self, i):
550 status = self.twitter.statuses[i]
551 myid = self.twitter.my_id
552 myname = self.twitter.my_name
554 if status.in_reply_to_user_id == myid or status.text.find("@%s" % myname) >= 0:
556 mentiontab = self.timelines[self.tlhash[self.timeline_mention]]
557 if status.id not in mentiontab.get_timeline_ids():
558 mentiontab.timeline.add_statuses(((status,)))
560 def on_mentions_added(self, i):
561 status = self.twitter.statuses[i]
562 self.notify("@%s mentioned you." % status.user.screen_name,
563 status.text, status.user)
565 # timeline refreshed event
566 def on_timeline_refresh(self):
567 if self.twitter.api.ratelimit_iplimit != -1:
568 msg = "%d/%d %d/%d" % (
569 self.twitter.api.ratelimit_remaining,
570 self.twitter.api.ratelimit_limit,
571 self.twitter.api.ratelimit_ipremaining,
572 self.twitter.api.ratelimit_iplimit)
575 self.twitter.api.ratelimit_remaining,
576 self.twitter.api.ratelimit_limit)
579 self.label_apilimit.set_text("API: %s" % msg)
583 # status selection changed event
584 def on_status_selection_changed(self, status):
585 self.builder.get_object("menuitem_tweet").set_sensitive(True)
586 self.statusbar.pop(0)
587 self.statusbar.push(0, TwitterTools.get_footer(status))
589 # status activated event (to Reply
590 def on_status_activated(self, status):
591 self.reply_to_status(status)
593 # show error on statusbar
594 def on_twitterapi_error(self, timeline, e):
596 message = "API rate limiting. Reset: %s" % (
597 self.twitter.api.ratelimit_reset.strftime("%H:%M:%S"))
598 elif e.code == 500 or e.code == 502:
599 message = "Twitter something is broken. Try again later."
601 message = "Twitter is over capacity. Try again later."
603 message = "Oops! Couldn't reload timeline."
605 self.statusbar.pop(0)
606 self.statusbar.push(0, "[Error] %s %s (%s)" % (
607 timeline.getName(), message, e.code))
609 ########################################
613 def on_button1_clicked(self, widget):
614 txt = self.get_textview()
618 self.status_update_thread(txt)
620 # Reload timeline if nothing in textview
621 n = self.get_current_tab_n()
622 self.twparams.pop("reply_to", None)
623 if self.timelines[n] != None:
624 self.timelines[n].reload()
626 # key_press textview (for update status when press Ctrl + Enter)
627 def on_textview1_key_press_event(self, textview, event):
629 if event.keyval == 65293 and event.state & gtk.gdk.CONTROL_MASK:
630 txt = self.get_textview()
632 # if update button enabled (== len(text) <= 140
633 if self.btnupdate.get_sensitive() and txt != "":
634 self.status_update_thread(txt)
639 def on_button2_button_release_event(self, widget, event):
640 menu = self.builder.get_object("menu_update")
641 menu.popup(None, None, None, event.button, event.time)
643 # Add an image to tweet
644 def on_menuitem_add_image_activate(self, widget):
645 dialog = gtk.FileChooserDialog("Add an image...")
646 dialog.add_button(gtk.STOCK_OPEN, gtk.RESPONSE_OK)
647 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
649 filename = dialog.get_filename()
652 if ret == gtk.RESPONSE_OK:
653 media = self.twparams.setdefault("media", list())
656 if filename in media:
657 self.message_dialog("Duplicate image.")
660 # max_media_per_upload check
661 max_media = self.twitter.configuration.get("max_media_per_upload",
663 if len(media) >= max_media:
665 "You can upload max %d images per upload." % max_media)
669 ext = os.path.splitext(filename)[1].upper()
670 if ext not in [".JPG", ".JPEG", ".PNG", ".GIF"]:
672 "File is not Image. Only JPG, PNG and GIF.")
676 fsize = os.stat(filename).st_size
677 limit = self.twitter.configuration.get("photo_size_limit", 3145728)
680 "Image file size must be less than %d KB." % (
684 pix = gtk.gdk.pixbuf_new_from_file(filename)
685 ratio = float(pix.get_height()) / float(pix.get_width())
686 pix = pix.scale_simple(120, int(ratio * 120),
687 gtk.gdk.INTERP_BILINEAR)
690 img.set_from_pixbuf(pix)
693 box.connect("button-release-event",
694 self.on_image_button_release, len(media))
697 if len(media) % 4 == 0:
698 self.imgtable.resize(len(media) / 4 + 1, 4)
701 self.imgtable.attach(
702 box, len(media) % 4, len(media) % 4 + 1,
703 len(media) / 4, len(media) / 4 + 1,
704 xoptions = gtk.SHRINK, yoptions = gtk.SHRINK,
705 xpadding = 0, ypadding = 10)
707 cursor = gtk.gdk.Cursor(gtk.gdk.HAND1)
708 box.window.set_cursor(cursor)
710 self.imgtable.set_visible(True)
711 self.imgtable.show_all()
714 media.append(filename)
716 def on_image_button_release(self, widget, event, media_num):
717 if event.button == 1:
719 if sys.platform == "win32":
720 os.startfile(self.twparams["media"][media_num])
722 os.system("xdg-open %s" % self.twparams["media"][media_num])
723 elif event.button == 3:
725 r = self.message_dialog(
726 "Are you sure you want to delete this image?",
727 gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO)
728 if r == gtk.RESPONSE_YES:
729 self.imgtable.remove(widget)
730 del self.twparams["media"][media_num]
731 if self.twparams["media"]:
732 self.imgtable.set_visible(False)
735 def on_tabclose_clicked(self, widget, uid):
739 if self.timeline_mention == uid:
740 self.timeline_mention = None
742 self.notebook.remove_page(n)
744 if self.timelines[n] != None:
745 self.timelines[n].destroy()
747 del self.timelines[n]
749 for i, m in self.tlhash.iteritems():
750 if m > n: self.tlhash[i] -= 1
752 p = self.notebook.get_current_page()
753 self.on_notebook1_switch_page(
754 self.notebook, self.notebook.get_nth_page(p), p)
757 def on_notebook_tabbar_button_press(self, widget, event):
758 if event.button == 3:
759 self.menu_timeline.popup(
760 None, None, None, event.button, event.time)
763 def on_textbuffer1_changed(self, textbuffer):
764 text = self.get_textview().decode("utf-8")
766 if self.msgfooter != "" and self.twparams.get("reply_to", None):
767 text = u"%s %s" % (text, self.msgfooter)
769 n = TwitterTools.get_tweet_length(
770 text, len(self.twparams.get("media", [])),
771 self.twitter.configuration.get("short_url_length", 20),
772 self.twitter.configuration.get("short_url_length_https", 20),
773 self.twitter.configuration.get("characters_reserved_per_media", 20)
777 self.charcount.set_text(str(n))
778 self.btnupdate.set_sensitive(True)
780 self.charcount.set_markup(
781 "<b><span foreground='#FF0000'>%s</span></b>" % n)
782 self.btnupdate.set_sensitive(False)
785 def on_menuitem_about_activate(self, menuitem):
786 self.builder.get_object("dialog_about").show_all()
789 # About dialog closed
790 def on_dialog_about_response(self, dialog, response_id):
793 # disable menu when switched tab
794 def on_notebook1_switch_page(self, notebook, page, page_num):
795 self.builder.get_object("menuitem_tweet").set_sensitive(False)
796 menuitem_timeline = self.builder.get_object("menuitem_timeline")
797 menuitem_timeline.set_sensitive(False)
798 if page_num < 0: return False
800 tab = self.timelines[page_num]
801 if tab != None and tab.timeline != None:
802 self._toggle_change_flg = True
805 default = self.get_default_interval(method)
807 if default == -1: default = None
809 menu_default = self.builder.get_object("menuitem_time_default")
810 menu_default.get_child().set_text("Default (%s)" % default)
812 interval = tl.interval
814 if interval == default:
815 menu_default.set_active(True)
817 self.builder.get_object("menuitem_time_none").set_active(True)
818 elif interval == 600:
819 self.builder.get_object("menuitem_time_600").set_active(True)
820 elif interval == 300:
821 self.builder.get_object("menuitem_time_300").set_active(True)
822 elif interval == 120:
823 self.builder.get_object("menuitem_time_120").set_active(True)
825 self.builder.get_object("menuitem_time_60").set_active(True)
827 self.builder.get_object("menuitem_time_30").set_active(True)
829 self._toggle_change_flg = False
830 menuitem_timeline.set_sensitive(True)
833 def on_menuitem_streaming_activate(self, menuitem):
834 dialog = gtk.MessageDialog(buttons = gtk.BUTTONS_OK)
835 dialog.set_markup("Please enter track keywords.")
836 dialog.format_secondary_markup(
837 "Examples: <i>hashtag, username, keyword</i>\n(Split comma. Unnecessary #, @)")
839 dialog.vbox.pack_start(entry)
842 text = entry.get_text()
845 params = {"track" : text.split(",")}
846 self.new_timeline("Stream: %s" % text, "filter", **params)
848 def on_destroy(self, widget, *args, **kwargs):
851 ########################################
854 def on_menuitem_reply_activate(self, menuitem):
855 self.reply_to_selected_status()
857 # Retweet menu clicked
858 def on_menuitem_retweet_activate(self, memuitem):
859 self.get_current_tab().view.retweet_selected_status()
861 # Retweet with comment menu clicked
862 def on_menuitem_reteet_with_comment_activate(self, memuitem):
863 status = self.get_selected_status()
864 name = status.user.screen_name
867 self.twparams.pop("reply_to", None)
868 self.set_textview("RT @%s: %s" % (name, text), True)
870 # Added user timeline tab
871 def on_menuitem_usertl_activate(self, menuitem):
872 status = self.get_selected_status()
873 self.new_timeline("@%s" % status.user.screen_name,
874 "user_timeline", user = status.user.id)
877 def on_menuitem_detail_activate(self, menuitem):
878 status = self.get_selected_status()
879 detail = StatusDetail(status)
880 self.new_tab(detail, "S: %d" % status.id)
883 def on_menuitem_fav_activate(self, menuitem):
884 self.get_current_tab().view.favorite_selected_status()
887 def on_menuitem_destroy_activate(self, menuitem):
888 status = self.get_selected_status()
889 self.twitter.destory_tweet(status)
891 # view on twitter.com
892 def on_menuitem_ontwitter_activate(self, menuitem):
893 status = self.get_selected_status()
894 url = "https://twitter.com/%s/status/%s" % (
895 status.user.screen_name, status.id)
896 webbrowser.open_new_tab(url)
898 ########################################
899 # Timeline menu Event
901 def change_interval(self, interval):
902 if self._toggle_change_flg: return
904 tl = self.get_current_tab().timeline
907 method = tl.api_method.func_name
908 interval = self.get_default_interval(method)
911 tl.interval = interval
912 if old == -1: tl.lock.set()
914 def on_menuitem_time_600_toggled(self, menuitem):
915 if menuitem.get_active() == True:
916 self.change_interval(600)
917 def on_menuitem_time_300_toggled(self, menuitem):
918 if menuitem.get_active() == True:
919 self.change_interval(300)
920 def on_menuitem_time_120_toggled(self, menuitem):
921 if menuitem.get_active() == True:
922 self.change_interval(120)
923 def on_menuitem_time_60_toggled(self, menuitem):
924 if menuitem.get_active() == True:
925 self.change_interval(60)
926 def on_menuitem_time_30_toggled(self, menuitem):
927 if menuitem.get_active() == True:
928 self.change_interval(30)
929 def on_menuitem_time_default_toggled(self, menuitem):
930 if menuitem.get_active() == True:
931 self.change_interval(0)
932 def on_menuitem_time_none_toggled(self, menuitem):
933 if menuitem.get_active() == True:
934 self.change_interval(-1)
936 ########################################
937 # Settings dialog event
940 def on_imageitem_settings_activate(self, menuitem):
941 home, mentions, other = self.interval
945 self.builder.get_object("checkbutton_home").set_active(False)
947 self.builder.get_object("checkbutton_mentions").set_active(False)
949 self.builder.get_object("checkbutton_other").set_active(False)
950 self.builder.get_object("spinbutton_home").set_value(home)
951 self.builder.get_object("spinbutton_mentions").set_value(mentions)
952 self.builder.get_object("spinbutton_other").set_value(other)
955 self.builder.get_object("spinbutton_firstn").set_value(self.scounts[0])
956 self.builder.get_object("spinbutton_maxn").set_value(self.scounts[1])
958 self.builder.get_object("checkbutton_showicon").set_active(self.iconmode)
960 self.builder.get_object("checkbutton_userstream").set_active(self.userstream)
963 self.builder.get_object("entry_footer").set_text(self.msgfooter)
966 self.builder.get_object("entry_myname").set_text(self.twitter.my_name)
967 self.builder.get_object("entry_ckey").set_text(self.twitter.api.oauth.ckey)
968 self.builder.get_object("entry_csecret").set_text(self.twitter.api.oauth.csecret)
969 self.builder.get_object("entry_atoken").set_text(self.twitter.api.oauth.atoken)
970 self.builder.get_object("entry_asecret").set_text(self.twitter.api.oauth.asecret)
973 color_entrys = (self.builder.get_object("entry_color_mytweet"),
974 self.builder.get_object("entry_color_mentions"),
975 self.builder.get_object("entry_color_replyto"),
976 self.builder.get_object("entry_color_replyto_user"),
977 self.builder.get_object("entry_color_selected_user"))
978 for i, entry in enumerate(color_entrys):
979 entry.set_text(self.status_color[i])
981 self.dsettings.show()
984 def on_dialog_settings_close(self, widget):
985 self.dsettings.hide()
988 def on_dialog_settings_ok(self, widget):
990 if self.builder.get_object("checkbutton_home").get_active():
991 home = self.builder.get_object("spinbutton_home").get_value_as_int()
993 self.charcount.set_markup("<b><span foreground='#FF0000'>%s</span></b>" % n)
994 self.btnupdate.set_sensitive(False)
996 if self.builder.get_object("checkbutton_mentions").get_active():
997 mentions = self.builder.get_object("spinbutton_mentions").get_value_as_int()
1000 if self.builder.get_object("checkbutton_other").get_active():
1001 other = self.builder.get_object("spinbutton_other").get_value_as_int()
1005 self.interval = (home, mentions, other)
1009 self.builder.get_object("spinbutton_firstn").get_value_as_int(),
1010 self.builder.get_object("spinbutton_maxn").get_value_as_int())
1013 self.iconmode = self.builder.get_object("checkbutton_showicon").get_active()
1016 self.userstream = self.builder.get_object("checkbutton_userstream").get_active()
1019 self.msgfooter = unicode(self.builder.get_object("entry_footer").get_text())
1022 self.status_color = (self.builder.get_object("entry_color_mytweet").get_text(),
1023 self.builder.get_object("entry_color_mentions").get_text(),
1024 self.builder.get_object("entry_color_replyto").get_text(),
1025 self.builder.get_object("entry_color_replyto_user").get_text(),
1026 self.builder.get_object("entry_color_selected_user").get_text())
1027 for t in self.timelines:
1029 t.view.set_color(self.status_color)
1031 self.save_settings()
1032 self.dsettings.hide()
1035 def on_checkbutton_home_toggled(self, checkbutton):
1036 sb = self.builder.get_object("spinbutton_home")
1037 sb.set_sensitive(checkbutton.get_active())
1038 def on_checkbutton_mentions_toggled(self, checkbutton):
1039 sb = self.builder.get_object("spinbutton_mentions")
1040 sb.set_sensitive(checkbutton.get_active())
1041 def on_checkbutton_other_toggled(self, checkbutton):
1042 sb = self.builder.get_object("spinbutton_other")
1043 sb.set_sensitive(checkbutton.get_active())
1045 def on_entry_color_changed(self, entry):
1046 entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(entry.get_text()))
1048 # Color selection dialog open
1049 def on_button_color1_clicked(self, widget):
1050 entry = self.builder.get_object("entry_color_mytweet")
1051 self.color_dialog_run("My status", entry.get_text(), entry)
1052 def on_button_color2_clicked(self, widget):
1053 entry = self.builder.get_object("entry_color_mentions")
1054 self.color_dialog_run("Mentions to me", entry.get_text(), entry)
1055 def on_button_color3_clicked(self, widget):
1056 entry = self.builder.get_object("entry_color_replyto")
1057 self.color_dialog_run("Reply to", entry.get_text(), entry)
1058 def on_button_color4_clicked(self, widget):
1059 entry = self.builder.get_object("entry_color_replyto_user")
1060 self.color_dialog_run("Reply to user", entry.get_text(), entry)
1061 def on_button_color5_clicked(self, widget):
1062 entry = self.builder.get_object("entry_color_selected_user")
1063 self.color_dialog_run("Selected user", entry.get_text(), entry)