diff --git a/doc/changelog.md b/doc/changelog.md index 513204c2..cc097af8 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,15 @@ TWBlue Changelog ## changes in this version +* Now it is possible to create a tweet from a trending topics buffer again. +* TWBlue now includes a completely new set of dialogs to handle tweeting, replying and sending direct messages that takes advantage of more Twitter features. + * It is possible to add videos in tweets and direct messages by using the new "add" button, located in every dialog where media can be added. Twitter suggests to add videos from 5 seconds up to 2 minutes lenght, in mp4 format (video Codec H.264 and audio codec AAC). Currently, TWBlue does not check if the uploaded video complies with Twitter media requirements. You can add only a video in a tweet or direct message. No other kind of media can be added after a video is in a tweet. If the video was unable to be uploaded successfully, the tweet or direct message won't be created. + * Now you can add a poll to tweets. Polls can have up to 4 different options and allow voting up to 7 days after being created. Take into account, though, that currently TWBlue does not support reading polls in tweets. + * TWBlue now support threads while creating a new tweet. There is a new button, called add tweet which will add the current tweet to the thread and will allow you to write another tweet in the thread. Every tweet might include media (up to 4 photos, or one GIF image or a video) or up to one poll. + * Some functionality was removed from tweet dialogs within TWBlue. Particularly, URL shorteners and long tweets via Twishort. You still can read long tweets posted via Twishort, though. + +## Changes in version 2021.11.07 + * TWBlue should retrieve tweets from threads and conversations in a more reliable way. Tweets in the same thread (made by the same author) will be sorted correctly, although replies to the thread (made by different people) may not be ordered in the same way they are displayed in Twitter apps. ([#417](https://github.com/manuelcortez/TWBlue/issues/417)) * When creating a filter, TWBlue will show an error if user has not provided a name for the filter. Before, unnamed filters were a cause of config breaks in the application. * It is again possible to read the changelog for TWBlue from the help menu in the menu bar. diff --git a/src/controller/attach.py b/src/controller/attach.py deleted file mode 100644 index 08427142..00000000 --- a/src/controller/attach.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from builtins import object -import os -import widgetUtils -import logging -from wxUI.dialogs import attach as gui -log = logging.getLogger("controller.attach") - -class attach(object): - def __init__(self): - self.attachments = list() - self.dialog = gui.attachDialog() - widgetUtils.connect_event(self.dialog.photo, widgetUtils.BUTTON_PRESSED, self.upload_image) - widgetUtils.connect_event(self.dialog.remove, widgetUtils.BUTTON_PRESSED, self.remove_attachment) - self.dialog.get_response() - log.debug("Attachments controller started.") - - def upload_image(self, *args, **kwargs): - image, description = self.dialog.get_image() - if image != None: - imageInfo = {"type": "photo", "file": image, "description": description} - log.debug("Image data to upload: %r" % (imageInfo,)) - self.attachments.append(imageInfo) - info = [_(u"Photo"), description] - self.dialog.attachments.insert_item(False, *info) - self.dialog.remove.Enable(True) - - def remove_attachment(self, *args, **kwargs): - current_item = self.dialog.attachments.get_selected() - log.debug("Removing item %d" % (current_item,)) - if current_item == -1: current_item = 0 - self.attachments.pop(current_item) - self.dialog.attachments.remove_item(current_item) - self.check_remove_status() - log.debug("Removed") - - def check_remove_status(self): - if len(self.attachments) == 0 and self.dialog.attachments.get_count() == 0: - self.dialog.remove.Enable(False) diff --git a/src/controller/buffers/twitter/base.py b/src/controller/buffers/twitter/base.py index 8da54709..1d81a604 100644 --- a/src/controller/buffers/twitter/base.py +++ b/src/controller/buffers/twitter/base.py @@ -84,42 +84,15 @@ class BaseBuffer(base.Buffer): return _(u"Unknown buffer") def post_status(self, *args, **kwargs): - item = None - title = _(u"Tweet") - caption = _(u"Write the tweet here") + title = _("Tweet") + caption = _("Write the tweet here") tweet = messages.tweet(self.session, title, caption, "") - if tweet.message.get_response() == widgetUtils.OK: - if config.app["app-settings"]["remember_mention_and_longtweet"]: - config.app["app-settings"]["longtweet"] = tweet.message.long_tweet.GetValue() - config.app.write() - text = tweet.message.get_text() - if len(text) > 280 and tweet.message.get("long_tweet") == True: - if not hasattr(tweet, "attachments"): - text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text) - else: - text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1) - if not hasattr(tweet, "attachments") or len(tweet.attachments) == 0: - item = self.session.api_call(call_name="update_status", status=text, _sound="tweet_send.ogg", tweet_mode="extended") - else: - call_threaded(self.post_with_media, text=text, attachments=tweet.attachments) - # We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item. - # but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly. -# if item != None: -# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"]) - if hasattr(tweet.message, "destroy"): tweet.message.destroy() - self.session.settings.write() - - def post_with_media(self, text, attachments): - media_ids = [] - for i in attachments: - img = self.session.twitter.media_upload(i["file"]) - self.session.twitter.create_media_metadata(media_id=img.media_id, alt_text=i["description"]) - media_ids.append(img.media_id) - item = self.session.twitter.update_status(status=text, media_ids=media_ids) - # We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item. - # but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly. -# if item != None: -# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"]) + response = tweet.message.ShowModal() + if response == wx.ID_OK: + tweet_data = tweet.get_tweet_data() + call_threaded(self.session.send_tweet, *tweet_data) + if hasattr(tweet.message, "destroy"): + tweet.message.destroy() def get_formatted_message(self): if self.type == "dm" or self.name == "direct_messages": @@ -422,7 +395,6 @@ class BaseBuffer(base.Buffer): user = self.session.get_user(tweet.user) screen_name = user.screen_name id = tweet.id - twishort_enabled = hasattr(tweet, "twishort") users = utils.get_all_mentioned(tweet, self.session.db, field="screen_name") ids = utils.get_all_mentioned(tweet, self.session.db, field="id") # Build the window title @@ -430,38 +402,14 @@ class BaseBuffer(base.Buffer): title=_("Reply to {arg0}").format(arg0=screen_name) else: title=_("Reply") - message = messages.reply(self.session, title, _(u"Reply to %s") % (screen_name,), "", users=users, ids=ids) - if message.message.get_response() == widgetUtils.OK: + message = messages.reply(self.session, title, _("Reply to %s") % (screen_name,), "", users=users, ids=ids) + if message.message.ShowModal() == widgetUtils.OK: if config.app["app-settings"]["remember_mention_and_longtweet"]: - config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue() if len(users) > 0: - config.app["app-settings"]["mention_all"] = message.message.mentionAll.GetValue() + config.app["app-settings"]["mention_all"] = message.message.mention_all.GetValue() config.app.write() - params = {"_sound": "reply_send.ogg", "in_reply_to_status_id": id, "tweet_mode": "extended"} - text = message.message.get_text() - if twishort_enabled == False: - excluded_ids = message.get_ids() - params["exclude_reply_user_ids"] =excluded_ids - params["auto_populate_reply_metadata"] =True - else: - mentioned_people = message.get_people() - text = "@"+screen_name+" "+mentioned_people+u" "+text - if len(text) > 280 and message.message.get("long_tweet") == True: - if message.image == None: - text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text) - else: - text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1) - params["status"] = text - if message.image == None: - params["call_name"] = "update_status" - else: - params["call_name"] = "update_status_with_media" - params["media"] = message.file - item = self.session.api_call(**params) - # We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item. - # but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly. -# if item != None: -# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"]) + tweet_data = dict(text=message.message.text.GetValue(), attachments=message.attachments, poll_options=message.poll_options, poll_period=message.poll_period) + call_threaded(self.session.reply, in_reply_to_status_id=id, text=message.message.text.GetValue(), attachments=message.attachments, exclude_reply_user_ids=message.get_ids()) if hasattr(message.message, "destroy"): message.message.destroy() self.session.settings.write() @@ -477,21 +425,17 @@ class BaseBuffer(base.Buffer): else: screen_name = self.session.get_user(tweet.user).screen_name users = utils.get_all_users(tweet, self.session) - dm = messages.dm(self.session, _(u"Direct message to %s") % (screen_name,), _(u"New direct message"), users) - if dm.message.get_response() == widgetUtils.OK: - screen_name = dm.message.get("cb") + dm = messages.dm(self.session, _("Direct message to %s") % (screen_name,), _("New direct message"), users) + if dm.message.ShowModal() == widgetUtils.OK: + screen_name = dm.message.cb.GetValue() user = self.session.get_user_by_screen_name(screen_name) recipient_id = user - text = dm.message.get_text() - val = self.session.api_call(call_name="send_direct_message", recipient_id=recipient_id, text=text) - if val != None: - sent_dms = self.session.db["sent_direct_messages"] - if self.session.settings["general"]["reverse_timelines"] == False: - sent_dms.append(val) - else: - sent_dms.insert(0, val) - self.session.db["sent_direct_messages"] = sent_dms - pub.sendMessage("sent-dm", data=val, user=self.session.db["user_name"]) + text = dm.message.text.GetValue() + if len(dm.attachments) > 0: + attachment = dm.attachments[0] + else: + attachment = None + call_threaded(self.session.direct_message, text=text, recipient=recipient_id, attachment=attachment) if hasattr(dm.message, "destroy"): dm.message.destroy() @_tweets_exist @@ -509,40 +453,20 @@ class BaseBuffer(base.Buffer): else: self._retweet_with_comment(tweet, id) - def _retweet_with_comment(self, tweet, id, comment=''): - # If quoting a retweet, let's quote the original tweet instead the retweet. + def _retweet_with_comment(self, tweet, id): if hasattr(tweet, "retweeted_status"): tweet = tweet.retweeted_status - if hasattr(tweet, "full_text"): - comments = tweet.full_text - else: - comments = tweet.text - retweet = messages.tweet(self.session, _(u"Quote"), _(u"Add your comment to the tweet"), u"“@%s: %s ”" % (self.session.get_user(tweet.user).screen_name, comments), max=256, messageType="retweet") - if comment != '': - retweet.message.set_text(comment) - if retweet.message.get_response() == widgetUtils.OK: - text = retweet.message.get_text() + retweet = messages.tweet(session=self.session, title=_("Quote"), caption=_("Add your comment to the tweet"), max=256, thread_mode=False) + if retweet.message.ShowModal() == widgetUtils.OK: + text = retweet.message.text.GetValue() text = text+" https://twitter.com/{0}/status/{1}".format(self.session.get_user(tweet.user).screen_name, id) - if retweet.image == None: - # We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item. - # but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly. - item = self.session.api_call(call_name="update_status", _sound="retweet_send.ogg", status=text, in_reply_to_status_id=id, tweet_mode="extended") -# if item != None: -# new_item = self.session.twitter.get_status(id=item.id, include_ext_alt_text=True, tweet_mode="extended") -# pub.sendMessage("sent-tweet", data=new_item, user=self.session.db["user_name"]) - else: - call_threaded(self.session.api_call, call_name="update_status", _sound="retweet_send.ogg", status=text, media=retweet.image) - if hasattr(retweet.message, "destroy"): retweet.message.destroy() + tweet_data = dict(text=text, attachments=retweet.attachments, poll_period=retweet.poll_period, poll_options=retweet.poll_options) + call_threaded(self.session.send_tweet, *[tweet_data]) + if hasattr(retweet.message, "destroy"): + retweet.message.destroy() def _direct_retweet(self, id): item = self.session.api_call(call_name="retweet", _sound="retweet_send.ogg", id=id) - # We will no longer will reuse the sent item from here as Streaming API should give us the new and correct item. - # but in case we'd need it, just uncomment the following couple of lines and make sure we reduce the item correctly. -# if item != None: - # Retweets are returned as non-extended tweets, so let's get the object as extended - # just before sending the event message. See https://github.com/manuelcortez/TWBlue/issues/253 -# item = self.session.twitter.get_status(id=item.id, include_ext_alt_text=True, tweet_mode="extended") -# pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"]) def onFocus(self, *args, **kwargs): tweet = self.get_tweet() diff --git a/src/controller/buffers/twitter/directMessages.py b/src/controller/buffers/twitter/directMessages.py index b0c51e5b..06cbed9a 100644 --- a/src/controller/buffers/twitter/directMessages.py +++ b/src/controller/buffers/twitter/directMessages.py @@ -90,18 +90,12 @@ class DirectMessagesBuffer(base.BaseBuffer): def reply(self, *args, **kwargs): tweet = self.get_right_tweet() screen_name = self.session.get_user(tweet.message_create["sender_id"]).screen_name - message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,]) - if message.message.get_response() == widgetUtils.OK: - if config.app["app-settings"]["remember_mention_and_longtweet"]: - config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue() - config.app.write() - if message.image == None: - item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended") - if item != None: - pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"]) - else: - call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file) - if hasattr(message.message, "destroy"): message.message.destroy() + message = messages.reply(session=self.session, title=_("Mention"), caption=_("Mention to %s") % (screen_name,), text="@%s " % (screen_name,), thread_mode=False, users=[screen_name,]) + if message.message.ShowModal() == widgetUtils.OK: + tweet_data = message.get_tweet_data() + call_threaded(self.session.send_tweet, tweet_data) + if hasattr(message.message, "destroy"): + message.message.destroy() def onFocus(self, *args, **kwargs): tweet = self.get_tweet() diff --git a/src/controller/buffers/twitter/people.py b/src/controller/buffers/twitter/people.py index 7f5c90b1..d29a46dc 100644 --- a/src/controller/buffers/twitter/people.py +++ b/src/controller/buffers/twitter/people.py @@ -92,18 +92,12 @@ class PeopleBuffer(base.BaseBuffer): def reply(self, *args, **kwargs): tweet = self.get_right_tweet() screen_name = tweet.screen_name - message = messages.reply(self.session, _(u"Mention"), _(u"Mention to %s") % (screen_name,), "@%s " % (screen_name,), [screen_name,]) - if message.message.get_response() == widgetUtils.OK: - if config.app["app-settings"]["remember_mention_and_longtweet"]: - config.app["app-settings"]["longtweet"] = message.message.long_tweet.GetValue() - config.app.write() - if message.image == None: - item = self.session.api_call(call_name="update_status", _sound="reply_send.ogg", status=message.message.get_text(), tweet_mode="extended") - if item != None: - pub.sendMessage("sent-tweet", data=item, user=self.session.db["user_name"]) - else: - call_threaded(self.session.api_call, call_name="update_status_with_media", _sound="reply_send.ogg", status=message.message.get_text(), media=message.file) - if hasattr(message.message, "destroy"): message.message.destroy() + message = messages.tweet(session=self.session, title=_("Mention"), caption=_("Mention to %s") % (screen_name,), text="@%s " % (screen_name,), thread_mode=False) + if message.message.ShowModal() == widgetUtils.OK: + tweet_data = message.get_tweet_data() + call_threaded(self.session.send_tweet, tweet_data) + if hasattr(message.message, "destroy"): + message.message.destroy() def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False): # starts stream every 3 minutes. diff --git a/src/controller/buffers/twitter/trends.py b/src/controller/buffers/twitter/trends.py index e3be681d..39809df1 100644 --- a/src/controller/buffers/twitter/trends.py +++ b/src/controller/buffers/twitter/trends.py @@ -4,7 +4,7 @@ import platform if platform.system() == "Windows": import wx from wxUI import buffers, commonMessageDialogs, menus - from controller import user + from controller import user, messages elif platform.system() == "Linux": from gi.repository import Gtk from gtkUI import buffers, commonMessageDialogs @@ -38,6 +38,18 @@ class TrendsBuffer(base.Buffer): self.get_formatted_message = self.get_message self.reply = self.search_topic + + def post_status(self, *args, **kwargs): + title = _("Tweet") + caption = _("Write the tweet here") + tweet = messages.tweet(self.session, title, caption, "") + response = tweet.message.ShowModal() + if response == wx.ID_OK: + tweet_data = tweet.get_tweet_data() + call_threaded(self.session.send_tweet, *tweet_data) + if hasattr(tweet.message, "destroy"): + tweet.message.destroy() + def start_stream(self, mandatory=False, play_sound=True, avoid_autoreading=False): # starts stream every 3 minutes. current_time = time.time() @@ -119,21 +131,13 @@ class TrendsBuffer(base.Buffer): def tweet_about_this_trend(self, *args, **kwargs): if self.buffer.list.get_count() == 0: return - title = _(u"Tweet") - caption = _(u"Write the tweet here") - tweet = messages.tweet(self.session, title, caption, self.get_message()+ " ") - tweet.message.set_cursor_at_end() - if tweet.message.get_response() == widgetUtils.OK: - text = tweet.message.get_text() - if len(text) > 280 and tweet.message.get("long_tweet") == True: - if tweet.image == None: - text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text) - else: - text = twishort.create_tweet(self.session.settings["twitter"]["user_key"], self.session.settings["twitter"]["user_secret"], text, 1) - if tweet.image == None: - call_threaded(self.session.api_call, call_name="update_status", status=text) - else: - call_threaded(self.session.api_call, call_name="update_status_with_media", status=text, media=tweet.image) + title = _("Tweet") + caption = _("Write the tweet here") + tweet = messages.tweet(session=self.session, title=title, caption=caption, text=self.get_message()+ " ") + tweet.message.SetInsertionPoint(len(tweet.message.GetValue())) + if tweet.message.ShowModal() == widgetUtils.OK: + tweet_data = tweet.get_tweet_data() + call_threaded(self.session.send_tweet, *tweet_data) if hasattr(tweet.message, "destroy"): tweet.message.destroy() def show_menu_by_key(self, ev): diff --git a/src/controller/messages.py b/src/controller/messages.py index a748464d..421be6d7 100644 --- a/src/controller/messages.py +++ b/src/controller/messages.py @@ -1,123 +1,69 @@ # -*- coding: utf-8 -*- -import re -import platform +import os import arrow import languageHandler -system = platform.system() +import wx import widgetUtils import output -import url_shortener import sound import config from pubsub import pub from twitter_text import parse_tweet -if system == "Windows": - from wxUI.dialogs import message, urlList - from wxUI import commonMessageDialogs - from extra import translator, SpellChecker, autocompletionUsers - from extra.AudioUploader import audioUploader -elif system == "Linux": - from gtkUI.dialogs import message +from wxUI.dialogs import twitterDialogs, urlList +from wxUI import commonMessageDialogs +from extra import translator, SpellChecker, autocompletionUsers +from extra.AudioUploader import audioUploader from sessions.twitter import utils -from . import attach class basicTweet(object): """ This class handles the tweet main features. Other classes should derive from this class.""" - def __init__(self, session, title, caption, text, messageType="tweet", max=280, *args, **kwargs): + def __init__(self, session, title, caption, text="", messageType="tweet", max=280, *args, **kwargs): super(basicTweet, self).__init__() self.max = max self.title = title self.session = session - self.message = getattr(message, messageType)(title, caption, text, *args, **kwargs) + self.message = getattr(twitterDialogs, messageType)(title=title, caption=caption, message=text, *args, **kwargs) + self.message.text.SetValue(text) + self.message.text.SetInsertionPoint(len(self.message.text.GetValue())) widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck) - widgetUtils.connect_event(self.message.attach, widgetUtils.BUTTON_PRESSED, self.attach) + widgetUtils.connect_event(self.message.add_audio, widgetUtils.BUTTON_PRESSED, self.attach) widgetUtils.connect_event(self.message.text, widgetUtils.ENTERED_TEXT, self.text_processor) - widgetUtils.connect_event(self.message.shortenButton, widgetUtils.BUTTON_PRESSED, self.shorten) - widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten) - widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate) - if hasattr(self.message, "long_tweet"): - widgetUtils.connect_event(self.message.long_tweet, widgetUtils.CHECKBOX, self.text_processor) - if config.app["app-settings"]["remember_mention_and_longtweet"]: - self.message.long_tweet.SetValue(config.app["app-settings"]["longtweet"]) + widgetUtils.connect_event(self.message.translate, widgetUtils.BUTTON_PRESSED, self.translate) + if hasattr(self.message, "add"): + widgetUtils.connect_event(self.message.add, widgetUtils.BUTTON_PRESSED, self.on_attach) self.attachments = [] def translate(self, event=None): dlg = translator.gui.translateDialog() if dlg.get_response() == widgetUtils.OK: - text_to_translate = self.message.get_text() + text_to_translate = self.message.text.GetValue() language_dict = translator.translator.available_languages() for k in language_dict: if language_dict[k] == dlg.dest_lang.GetStringSelection(): dst = k msg = translator.translator.translate(text=text_to_translate, target=dst) - self.message.set_text(msg) + self.message.text.ChangeValue(msg) + self.message.text.SetInsertionPoint(len(self.message.text.GetValue())) self.text_processor() - self.message.text_focus() + self.message.text.SetFocus() output.speak(_(u"Translated")) else: return - def shorten(self, event=None): - urls = utils.find_urls_in_text(self.message.get_text()) - if len(urls) == 0: - output.speak(_(u"There's no URL to be shortened")) - self.message.text_focus() - elif len(urls) == 1: - self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.shorten(urls[0]))) - output.speak(_(u"URL shortened")) - self.text_processor() - self.message.text_focus() - elif len(urls) > 1: - list_urls = urlList.urlList() - list_urls.populate_list(urls) - if list_urls.get_response() == widgetUtils.OK: - self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.shorten(list_urls.get_string()))) - output.speak(_(u"URL shortened")) - self.text_processor() - self.message.text_focus() - - def unshorten(self, event=None): - urls = utils.find_urls_in_text(self.message.get_text()) - if len(urls) == 0: - output.speak(_(u"There's no URL to be expanded")) - self.message.text_focus() - elif len(urls) == 1: - self.message.set_text(self.message.get_text().replace(urls[0], url_shortener.unshorten(urls[0]))) - output.speak(_(u"URL expanded")) - self.text_processor() - self.message.text_focus() - elif len(urls) > 1: - list_urls = urlList.urlList() - list_urls.populate_list(urls) - if list_urls.get_response() == widgetUtils.OK: - self.message.set_text(self.message.get_text().replace(urls[list_urls.get_item()], url_shortener.unshorten(list_urls.get_string()))) - output.speak(_(u"URL expanded")) - self.text_processor() - self.message.text_focus() - def text_processor(self, *args, **kwargs): - if len(self.message.get_text()) > 1: - self.message.enable_button("shortenButton") - self.message.enable_button("unshortenButton") - else: - self.message.disable_button("shortenButton") - self.message.disable_button("unshortenButton") - if self.message.get("long_tweet") == False and hasattr(self, "max"): - text = self.message.get_text() - results = parse_tweet(text) - self.message.set_title(_(u"%s - %s of %d characters") % (self.title, results.weightedLength, self.max)) - if results.weightedLength > self.max: - self.session.sound.play("max_length.ogg") - else: - self.message.set_title(_(u"%s - %s characters") % (self.title, len(self.message.get_text()))) + text = self.message.text.GetValue() + results = parse_tweet(text) + self.message.SetTitle(_("%s - %s of %d characters") % (self.title, results.weightedLength, self.max)) + if results.weightedLength > self.max: + self.session.sound.play("max_length.ogg") def spellcheck(self, event=None): - text = self.message.get_text() + text = self.message.text.GetValue() checker = SpellChecker.spellchecker.spellChecker(text, "") if hasattr(checker, "fixed_text"): - self.message.set_text(checker.fixed_text) + self.message.text.ChangeValue(checker.fixed_text) self.text_processor() - self.message.text_focus() + self.message.text.SetFocus() def attach(self, *args, **kwargs): def completed_callback(dlg): @@ -125,48 +71,170 @@ class basicTweet(object): pub.unsubscribe(dlg.uploaderDialog.update, "uploading") dlg.uploaderDialog.destroy() if "sndup.net/" in url: - self.message.set_text(self.message.get_text()+url+" #audio") + self.message.text.ChangeValue(self.message.text.GetValue()+url+" #audio") self.text_processor() else: commonMessageDialogs.common_error(url) - dlg.cleanup() dlg = audioUploader.audioUploader(self.session.settings, completed_callback) - self.message.text_focus() + self.message.text.SetFocus() + + def can_attach(self): + if len(self.attachments) == 0: + return True + elif len(self.attachments) == 1 and (self.attachments[0]["type"] == "video" or self.attachments[0]["type"] == "gif"): + return False + elif len(self.attachments) < 4: + return True + return False + + def on_attach(self, *args, **kwargs): + can_attach = self.can_attach() + menu = self.message.attach_menu(can_attach) + self.message.Bind(wx.EVT_MENU, self.on_attach_image, self.message.add_image) + self.message.Bind(wx.EVT_MENU, self.on_attach_video, self.message.add_video) + if hasattr(self.message, "add_poll"): + self.message.Bind(wx.EVT_MENU, self.on_attach_poll, self.message.add_poll) + self.message.PopupMenu(menu, self.message.add.GetPosition()) + + def on_attach_image(self, *args, **kwargs): + can_attach = self.can_attach() + video_or_gif_present = False + for a in self.attachments: + if a["type"] == "video" or a["type"] == "gif": + video_or_gif = True + break + if can_attach == False or video_or_gif_present == True: + return self.message.unable_to_attach_file() + image, description = self.message.get_image() + if image != None: + if image.endswith("gif"): + image_type = "gif" + else: + image_type = "photo" + imageInfo = {"type": image_type, "file": image, "description": description} + if len(self.attachments) > 0 and image_type == "gif": + return self.message.unable_to_attach_file() + self.attachments.append(imageInfo) + self.message.add_item(item=[os.path.basename(imageInfo["file"]), imageInfo["type"], imageInfo["description"]]) + self.text_processor() + + def on_attach_video(self, *args, **kwargs): + if len(self.attachments) > 0: + return self.message.unable_to_attach_file() + video = self.message.get_video() + if video != None: + videoInfo = {"type": "video", "file": video, "description": ""} + if len(self.attachments) > 0: + return self.message.unable_to_attach_file() + self.attachments.append(videoInfo) + self.message.add_item(item=[os.path.basename(videoInfo["file"]), videoInfo["type"], videoInfo["description"]]) + self.text_processor() + + def on_attach_poll(self, *args, **kwargs): + dlg = twitterDialogs.poll() + if dlg.ShowModal() == wx.ID_OK: + self.poll_options = dlg.get_options() + self.poll_period = 60*24*dlg.period.GetValue() + dlg.Destroy() + + def remove_attachment(self, *args, **kwargs): + attachment = self.message.attachments.GetFocusedItem() + if attachment > -1 and len(self.attachments) > attachment: + self.attachments.pop(attachment) + self.message.remove_item(list_type="attachment") + self.text_processor() + self.message.text.SetFocus() class tweet(basicTweet): - def __init__(self, session, title, caption, text, max=280, messageType="tweet", *args, **kwargs): + def __init__(self, session, title, caption, text="", max=280, messageType="tweet", *args, **kwargs): + self.thread = [] + self.poll_options = None + self.poll_period = None super(tweet, self).__init__(session, title, caption, text, messageType, max, *args, **kwargs) - self.image = None - widgetUtils.connect_event(self.message.upload_image, widgetUtils.BUTTON_PRESSED, self.upload_image) - widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users) + widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users) + if hasattr(self.message, "add_tweet"): + widgetUtils.connect_event(self.message.add_tweet, widgetUtils.BUTTON_PRESSED, self.add_tweet) + widgetUtils.connect_event(self.message.remove_tweet, widgetUtils.BUTTON_PRESSED, self.remove_tweet) + widgetUtils.connect_event(self.message.remove_attachment, widgetUtils.BUTTON_PRESSED, self.remove_attachment) self.text_processor() - def upload_image(self, *args, **kwargs): - a = attach.attach() - if len(a.attachments) != 0: - self.attachments = a.attachments - def autocomplete_users(self, *args, **kwargs): c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id) c.show_menu() + def add_tweet(self, event, update_gui=True, *args, **kwargs): + text = self.message.text.GetValue() + attachments = self.attachments[::] + tweetdata = dict(text=text, attachments=attachments, poll_options=self.poll_options, poll_period=self.poll_period) + self.thread.append(tweetdata) + self.attachments = [] + self.poll_options = None + self.poll_period = None + if update_gui: + self.message.reset_controls() + self.message.add_item(item=[text, len(attachments)], list_type="tweet") + self.message.text.SetFocus() + self.text_processor() + + def get_tweet_data(self): + self.add_tweet(event=None, update_gui=False) + return self.thread + + def text_processor(self, *args, **kwargs): + super(tweet, self).text_processor(*args, **kwargs) + if len(self.thread) > 0: + self.message.tweets.Enable(True) + self.message.remove_tweet.Enable(True) + else: + self.message.tweets.Enable(False) + self.message.remove_tweet.Enable(False) + if len(self.attachments) > 0: + self.message.attachments.Enable(True) + self.message.remove_attachment.Enable(True) + else: + self.message.attachments.Enable(False) + self.message.remove_attachment.Enable(False) + if hasattr(self.message, "add_tweet"): + if len(self.message.text.GetValue()) > 0 or len(self.attachments) > 0: + self.message.add_tweet.Enable(True) + else: + self.message.add_tweet.Enable(False) + + def remove_tweet(self, *args, **kwargs): + tweet = self.message.tweets.GetFocusedItem() + if tweet > -1 and len(self.thread) > tweet: + self.thread.pop(tweet) + self.message.remove_item(list_type="tweet") + self.text_processor() + self.message.text.SetFocus() + + class reply(tweet): def __init__(self, session, title, caption, text, users=[], ids=[]): super(reply, self).__init__(session, title, caption, text, messageType="reply", users=users) self.ids = ids self.users = users if len(users) > 0: - widgetUtils.connect_event(self.message.mentionAll, widgetUtils.CHECKBOX, self.mention_all) - self.message.enable_button("mentionAll") + widgetUtils.connect_event(self.message.mention_all, widgetUtils.CHECKBOX, self.mention_all) + self.message.mention_all.Enable(True) if config.app["app-settings"]["remember_mention_and_longtweet"]: - self.message.mentionAll.SetValue(config.app["app-settings"]["mention_all"]) + self.message.mention_all.SetValue(config.app["app-settings"]["mention_all"]) self.mention_all() - self.message.set_cursor_at_end() + self.message.text.SetInsertionPoint(len(self.message.text.GetValue())) self.text_processor() + def text_processor(self, *args, **kwargs): + super(tweet, self).text_processor(*args, **kwargs) + if len(self.attachments) > 0: + self.message.attachments.Enable(True) + self.message.remove_attachment.Enable(True) + else: + self.message.attachments.Enable(False) + self.message.remove_attachment.Enable(False) + def mention_all(self, *args, **kwargs): - if self.message.mentionAll.GetValue() == True: + if self.message.mention_all.GetValue() == True: for i in self.message.checkboxes: i.SetValue(True) i.Hide() @@ -176,10 +244,10 @@ class reply(tweet): i.Show() def get_ids(self): - excluded_ids = "" + excluded_ids = [] for i in range(0, len(self.message.checkboxes)): if self.message.checkboxes[i].GetValue() == False: - excluded_ids = excluded_ids + "{0},".format(self.ids[i],) + excluded_ids.append(self.ids[i]) return excluded_ids def get_people(self): @@ -190,20 +258,34 @@ class reply(tweet): return people class dm(basicTweet): - def __init__(self, session, title, caption, text): - super(dm, self).__init__(session, title, caption, text, messageType="dm", max=10000) - widgetUtils.connect_event(self.message.autocompletionButton, widgetUtils.BUTTON_PRESSED, self.autocomplete_users) + def __init__(self, session, title, caption, users): + super(dm, self).__init__(session, title, caption, messageType="dm", max=10000, users=users) + widgetUtils.connect_event(self.message.autocomplete_users, widgetUtils.BUTTON_PRESSED, self.autocomplete_users) self.text_processor() widgetUtils.connect_event(self.message.cb, widgetUtils.ENTERED_TEXT, self.user_changed) def user_changed(self, *args, **kwargs): - self.title = _("Direct message to %s") % (self.message.get_user()) + self.title = _("Direct message to %s") % (self.message.cb.GetValue()) self.text_processor() def autocomplete_users(self, *args, **kwargs): c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id) c.show_menu("dm") + def text_processor(self, *args, **kwargs): + super(dm, self).text_processor(*args, **kwargs) + if len(self.attachments) > 0: + self.message.attachments.Enable(True) + self.message.remove_attachment.Enable(True) + else: + self.message.attachments.Enable(False) + self.message.remove_attachment.Enable(False) + + def can_attach(self): + if len(self.attachments) == 0: + return True + return False + class viewTweet(basicTweet): def __init__(self, tweet, tweetList, is_tweet=True, utc_offset=0, date="", item_url=""): """ This represents a tweet displayer. However it could be used for showing something wich is not a tweet, like a direct message or an event. @@ -264,29 +346,25 @@ class viewTweet(basicTweet): for z in tweet.retweeted_status.extended_entities["media"]: if "ext_alt_text" in z and z["ext_alt_text"] != None: image_description.append(z["ext_alt_text"]) - self.message = message.viewTweet(text, rt_count, favs_count, source, date) + self.message = twitterDialogs.viewTweet(text, rt_count, favs_count, source, date) results = parse_tweet(text) self.message.set_title(results.weightedLength) [self.message.set_image_description(i) for i in image_description] else: self.title = _(u"View item") text = tweet - self.message = message.viewNonTweet(text, date) + self.message = twitterDialogs.viewNonTweet(text, date) widgetUtils.connect_event(self.message.spellcheck, widgetUtils.BUTTON_PRESSED, self.spellcheck) if item_url != "": self.message.enable_button("share") widgetUtils.connect_event(self.message.share, widgetUtils.BUTTON_PRESSED, self.share) self.item_url = item_url widgetUtils.connect_event(self.message.translateButton, widgetUtils.BUTTON_PRESSED, self.translate) - if self.contain_urls() == True: - self.message.enable_button("unshortenButton") - widgetUtils.connect_event(self.message.unshortenButton, widgetUtils.BUTTON_PRESSED, self.unshorten) - self.message.get_response() + self.message.ShowModal() - def contain_urls(self): - if len(utils.find_urls_in_text(self.message.get_text())) > 0: - return True - return False + # We won't need text_processor in this dialog, so let's avoid it. + def text_processor(self): + pass def clear_text(self, text): urls = utils.find_urls_in_text(text) diff --git a/src/extra/autocompletionUsers/completion.py b/src/extra/autocompletionUsers/completion.py index 826dc699..9fa8c8ec 100644 --- a/src/extra/autocompletionUsers/completion.py +++ b/src/extra/autocompletionUsers/completion.py @@ -10,9 +10,9 @@ class autocompletionUsers(object): self.db = storage.storage(session_id) def show_menu(self, mode="tweet"): - position = self.window.get_position() + position = self.window.text.GetInsertionPoint() if mode == "tweet": - text = self.window.get_text() + text = self.window.text.GetValue() text = text[:position] try: pattern = text.split()[-1] @@ -24,14 +24,14 @@ class autocompletionUsers(object): users = self.db.get_users(pattern[1:]) if len(users) > 0: menu.append_options(users) - self.window.popup_menu(menu) + self.window.PopupMenu(menu, self.window.text.GetPosition()) menu.destroy() else: output.speak(_(u"There are no results in your users database")) else: output.speak(_(u"Autocompletion only works for users.")) elif mode == "dm": - text = self.window.get_user() + text = self.window.cb.GetValue() try: pattern = text.split()[-1] except IndexError: @@ -41,7 +41,7 @@ class autocompletionUsers(object): users = self.db.get_users(pattern) if len(users) > 0: menu.append_options(users) - self.window.popup_menu(menu) + self.window.PopupMenu(menu, self.window.text.GetPosition()) menu.destroy() else: output.speak(_(u"There are no results in your users database")) diff --git a/src/sessions/twitter/session.py b/src/sessions/twitter/session.py index a32ead2d..38f4baae 100644 --- a/src/sessions/twitter/session.py +++ b/src/sessions/twitter/session.py @@ -204,7 +204,7 @@ class Session(base.baseSession): except TweepyException as e: output.speak(str(e)) val = None - if type(e) != NotFound and type(e) != Forvidden: + if type(e) != NotFound and type(e) != Forbidden: tries = tries+1 time.sleep(5) elif report_failure: @@ -218,6 +218,30 @@ class Session(base.baseSession): if _sound != None: self.sound.play(_sound) return val + def api_call_v2(self, call_name, action="", _sound=None, report_success=False, report_failure=True, preexec_message="", *args, **kwargs): + finished = False + tries = 0 + if preexec_message: + output.speak(preexec_message, True) + while finished==False and tries < 25: + try: + val = getattr(self.twitter_v2, call_name)(*args, **kwargs) + finished = True + except TweepyException as e: + log.exception("Error sending the tweet.") + output.speak(str(e)) + val = None + if type(e) != NotFound and type(e) != Forbidden: + tries = tries+1 + time.sleep(5) + elif report_failure: + output.speak(_("%s failed. Reason: %s") % (action, str(e))) + finished = True + if report_success: + output.speak(_("%s succeeded.") % action) + if _sound != None: self.sound.play(_sound) + return val + def search(self, name, *args, **kwargs): """ Search in twitter, passing args and kwargs as arguments to the Twython function.""" tl = self.twitter.search_tweets(*args, **kwargs) @@ -597,24 +621,48 @@ class Session(base.baseSession): in_reply_to_status_id = None for obj in tweets: if len(obj["attachments"]) == 0: - item = self.api_call(call_name="update_status", status=obj["text"], _sound="tweet_send.ogg", tweet_mode="extended", in_reply_to_status_id=in_reply_to_status_id) - in_reply_to_status_id = item.id + item = self.api_call_v2(call_name="create_tweet", text=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"]) + print(item) + in_reply_to_status_id = item.data.id else: media_ids = [] for i in obj["attachments"]: img = self.api_call("media_upload", filename=i["file"]) - self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"]) + if i["type"] == "photo": + self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"]) media_ids.append(img.media_id) - item = self.api_call(call_name="update_status", status=obj["text"], _sound="tweet_send.ogg", tweet_mode="extended", in_reply_to_status_id=in_reply_to_status_id, media_ids=media_ids) - in_reply_to_status_id = item.id + item = self.api_call_v2(call_name="create_tweet", status=obj["text"], _sound="tweet_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, media_ids=media_ids, poll_duration_minutes=obj["poll_period"], poll_options=obj["poll_options"]) + in_reply_to_status_id = item.data.id def reply(self, text="", in_reply_to_status_id=None, attachments=[], *args, **kwargs): if len(attachments) == 0: - item = self.api_call(call_name="update_status", status=text, _sound="reply_send.ogg", tweet_mode="extended", in_reply_to_status_id=in_reply_to_status_id, *args, **kwargs) + item = self.api_call_v2(call_name="create_tweet", text=text, _sound="reply_send.ogg", in_reply_to_tweet_id=in_reply_to_status_id, *args, **kwargs) else: media_ids = [] for i in attachments: img = self.api_call("media_upload", filename=i["file"]) - self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"]) + if i["type"] == "photo": + self.api_call(call_name="create_media_metadata", media_id=img.media_id, alt_text=i["description"]) media_ids.append(img.media_id) item = self.api_call(call_name="update_status", status=text, _sound="reply_send.ogg", tweet_mode="extended", in_reply_to_status_id=in_reply_to_status_id, media_ids=media_ids, *args, **kwargs) + + def direct_message(self, text, recipient, attachment=None, *args, **kwargs): + if attachment == None: + item = self.api_call(call_name="send_direct_message", recipient_id=recipient, text=text) + else: + if attachment["type"] == "photo": + media_category = "DmImage" + elif attachment["type"] == "gif": + media_category = "DmGif" + elif attachment["type"] == "video": + media_category = "DmVideo" + media = self.api_call("media_upload", filename=attachment["file"], media_category=media_category) + item = self.api_call(call_name="send_direct_message", recipient_id=recipient, text=text, attachment_type="media", attachment_media_id=media.media_id) + if item != None: + sent_dms = self.db["sent_direct_messages"] + if self.settings["general"]["reverse_timelines"] == False: + sent_dms.append(item) + else: + sent_dms.insert(0, item) + self.db["sent_direct_messages"] = sent_dms + pub.sendMessage("sent-dm", data=item, user=self.db["user_name"]) diff --git a/src/sessions/twitter/utils.py b/src/sessions/twitter/utils.py index f84dfaca..d44e0e3f 100644 --- a/src/sessions/twitter/utils.py +++ b/src/sessions/twitter/utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import url_shortener, re +import re import output import config import logging diff --git a/src/sound.py b/src/sound.py index df47f221..cd0f5a93 100644 --- a/src/sound.py +++ b/src/sound.py @@ -6,7 +6,6 @@ import subprocess import platform import tempfile import glob -import url_shortener import audio_services import paths import sound_lib @@ -121,7 +120,7 @@ class URLStream(object): """ Takes an URL and prepares it to be streamed. This function will try to unshorten the passed URL and, if needed, to transform it into a valid URL.""" log.debug("Preparing URL: %s" % (url,)) self.prepared = False - self.url = url_shortener.unshorten(url) + self.url = url if self.url == None: self.url = url log.debug("Expanded URL: %s" % (self.url,)) diff --git a/src/url_shortener/__init__.py b/src/url_shortener/__init__.py deleted file mode 100644 index 419c8170..00000000 --- a/src/url_shortener/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from __future__ import unicode_literals -from . import shorteners -from . __main__ import * diff --git a/src/url_shortener/__main__.py b/src/url_shortener/__main__.py deleted file mode 100644 index 7e3dcf05..00000000 --- a/src/url_shortener/__main__.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import unicode_literals -from functools import wraps -from . import shorteners - - -def service_selecter (func): - @wraps(func) - def wrapper (*args, **kwargs): - tmp = dict(kwargs) - if 'service' in tmp: - del(tmp['service']) - kwargs['service'] = find_service(kwargs['service'], **tmp) or default_service() - else: - kwargs['service'] = default_service() - return func(*args, **kwargs) - return wrapper - -@service_selecter -def shorten (url, service=None, **kwargs): - return service(**kwargs).shorten(url) - - -@service_selecter -def unshorten (url, service=None, **kwargs): - return service(**kwargs).unshorten(url) - - -def default_service (): - return shorteners.AcortameShortener - -def find_service (service, **kwargs): - for i in shorteners.__all__: - obj = getattr(shorteners, i)(**kwargs) - if obj.name.lower() == service.lower(): - return getattr(shorteners, i) - -def list_services (): - return [getattr(shorteners, i)().name for i in shorteners.__all__] - -def unshorten_any (url): - """Unshortens an URL using any available unshortener. Check to see if unshortened URL was created by a shortener (nested) and unshorten if so.""" - unshortened_url = shorteners.URLShortener().unshorten(url) - # None is returned if URL not unshortened - if unshortened_url: - return unshorten_any(unshortened_url) - return url diff --git a/src/url_shortener/shorteners/__init__.py b/src/url_shortener/shorteners/__init__.py deleted file mode 100644 index aa0b763d..00000000 --- a/src/url_shortener/shorteners/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from __future__ import unicode_literals -from .url_shortener import URLShortener -from .hkcim import HKCShortener -from . isgd import IsgdShortener -from . onjme import OnjmeShortener -from . tinyarrows import TinyArrowsShortener -from . tinyurl import TinyurlShortener -from . xedcc import XedccShortener -from . clckru import ClckruShortener -from . acortame import AcortameShortener -__all__ = ["HKCShortener", "IsgdShortener", "OnjmeShortener", "TinyArrowsShortener", "TinyurlShortener", "XedccShortener", "ClckruShortener", "AcortameShortener"] diff --git a/src/url_shortener/shorteners/acortame.py b/src/url_shortener/shorteners/acortame.py deleted file mode 100644 index e7adafb6..00000000 --- a/src/url_shortener/shorteners/acortame.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -from . url_shortener import URLShortener -import requests -import urllib.request, urllib.parse, urllib.error -class AcortameShortener (URLShortener): - def __init__(self, *args, **kwargs): - self.name = "acorta.me" - super(AcortameShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get ("https://acorta.me/api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer - - def created_url (self, url): - return 'acorta.me' in url - - def unshorten (self, url): - if not 'acorta.me' in url: - #use generic expand method - return super(AcortameShortener, self).unshorten(url) - answer = url - api = requests.get ("https://acorta.me/api.php?action=expand&format=simple&shorturl=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer diff --git a/src/url_shortener/shorteners/clckru.py b/src/url_shortener/shorteners/clckru.py deleted file mode 100644 index 9c527ea1..00000000 --- a/src/url_shortener/shorteners/clckru.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -import urllib.request, urllib.parse, urllib.error -import requests -from . url_shortener import URLShortener - - -class ClckruShortener (URLShortener): - def __init__ (self, *args, **kwargs): - self.name = "clck.ru" - super(ClckruShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get ("http://clck.ru/--?url=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer - - def created_url (self, url): - return 'clck.ru' in url diff --git a/src/url_shortener/shorteners/hkcim.py b/src/url_shortener/shorteners/hkcim.py deleted file mode 100644 index 29ecd9ea..00000000 --- a/src/url_shortener/shorteners/hkcim.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -import urllib.request, urllib.parse, urllib.error -import requests -from . url_shortener import URLShortener - -class HKCShortener (URLShortener): - def __init__ (self, *args, **kwargs): - self.name = "HKC.im" - super(HKCShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get ("http://hkc.im/yourls-api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer - - def created_url (self, url): - return 'hkc.im' in url.lower() diff --git a/src/url_shortener/shorteners/isgd.py b/src/url_shortener/shorteners/isgd.py deleted file mode 100644 index ee817360..00000000 --- a/src/url_shortener/shorteners/isgd.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -import urllib.request, urllib.parse, urllib.error -import requests -from . url_shortener import URLShortener - - -class IsgdShortener (URLShortener): - def __init__ (self, *args, **kwargs): - self.name = "Is.gd" - super(IsgdShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get ("http://is.gd/api.php?longurl=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer - - def created_url (self, url): - return 'is.gd' in url diff --git a/src/url_shortener/shorteners/onjme.py b/src/url_shortener/shorteners/onjme.py deleted file mode 100644 index c6a21315..00000000 --- a/src/url_shortener/shorteners/onjme.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -import urllib.request, urllib.parse, urllib.error -import requests -from . url_shortener import URLShortener - -class OnjmeShortener (URLShortener): - def __init__ (self, *args, **kwargs): - self.name = "Onj.me" - super(OnjmeShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get ("http://onj.me/yourls-api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer - - def created_url (self, url): - return 'onj.me' in url.lower() diff --git a/src/url_shortener/shorteners/tinyarrows.py b/src/url_shortener/shorteners/tinyarrows.py deleted file mode 100644 index 45a8990c..00000000 --- a/src/url_shortener/shorteners/tinyarrows.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -import urllib.request, urllib.parse, urllib.error -import requests -from . url_shortener import URLShortener - -class TinyArrowsShortener (URLShortener): - def __init__ (self, *args, **kwargs): - self.name = "TinyArro.ws" - super(TinyArrowsShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get("http://tinyarro.ws/api-create.php?utfpure=1&url=%s" % urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer.decode('UTF-8') - - def created_url(self, url): - return "tinyarro.ws" in url diff --git a/src/url_shortener/shorteners/tinyurl.py b/src/url_shortener/shorteners/tinyurl.py deleted file mode 100644 index 0a5703a3..00000000 --- a/src/url_shortener/shorteners/tinyurl.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -from .url_shortener import URLShortener -import requests -import urllib.request, urllib.parse, urllib.error -class TinyurlShortener (URLShortener): - def __init__(self, *args, **kwargs): - self.name = "TinyURL.com" - super(TinyurlShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get ("http://tinyurl.com/api-create.php?url=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer - - def created_url (self, url): - return 'tinyurl.com' in url diff --git a/src/url_shortener/shorteners/url_shortener.py b/src/url_shortener/shorteners/url_shortener.py deleted file mode 100644 index cf1d6f76..00000000 --- a/src/url_shortener/shorteners/url_shortener.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import unicode_literals -from builtins import object -import requests - -class URLShortener (object): - - def __init__ (self, *args, **kwargs): - #Stub out arguments, silly object. :( - return super(URLShortener, self).__init__() - - def shorten (self, url): - if self.created_url(url): - return url - else: - return self._shorten(url) - - def _shorten (self, url): - raise NotImplementedError - - def created_url (self, url): - """Returns a boolean indicating whether or not this shortener created a provided url""" - raise NotImplementedError - - def unshorten(self, url): - try: - r=requests.head(url) - if 'location' in list(r.headers.keys()): - if 'dropbox.com' in r.headers['location']: - return handle_dropbox(r.headers['location']) - else: - return r.headers['location'] - else: # if the head method does not work, use get instead. Performance may decrease - r=requests.get(url, allow_redirects=False, stream=True) - # release the connection without downloading the content, we only need the response headers - r.close() - return r.headers['location'] - except: - return url #we cannot expand - -def handle_dropbox(url): - if url.endswith("dl=1"): - return url - else: - return url.replace("dl=0", "dl=1") diff --git a/src/url_shortener/shorteners/xedcc.py b/src/url_shortener/shorteners/xedcc.py deleted file mode 100644 index 117b4f7c..00000000 --- a/src/url_shortener/shorteners/xedcc.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -import urllib.request, urllib.parse, urllib.error -import requests -from . url_shortener import URLShortener - -class XedccShortener (URLShortener): - def __init__ (self, *args, **kwargs): - self.name = "Xed.cc" - super(XedccShortener, self).__init__(*args, **kwargs) - - def _shorten (self, url): - answer = url - api = requests.get ("http://xed.cc/yourls-api.php?action=shorturl&format=simple&url=" + urllib.parse.quote(url)) - if api.status_code == 200: - answer = api.text - return answer - - def created_url (self, url): - return 'xed.cc' in url.lower() diff --git a/src/wxUI/dialogs/__init__.py b/src/wxUI/dialogs/__init__.py index a21239a7..d00a14c0 100644 --- a/src/wxUI/dialogs/__init__.py +++ b/src/wxUI/dialogs/__init__.py @@ -1,3 +1 @@ -from __future__ import absolute_import -from __future__ import unicode_literals -from . import baseDialog, trends, configuration, lists, message, search, find, show_user, update_profile, urlList, userSelection, utils, filterDialogs, userAliasDialogs +from . import baseDialog, trends, configuration, lists, search, find, show_user, update_profile, urlList, userSelection, utils, filterDialogs, userAliasDialogs diff --git a/src/wxUI/dialogs/attach.py b/src/wxUI/dialogs/attach.py deleted file mode 100644 index 0b20a422..00000000 --- a/src/wxUI/dialogs/attach.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" Attach dialog. Taken from socializer: https://github.com/manuelcortez/socializer""" -from __future__ import unicode_literals -import wx -import widgetUtils -from multiplatform_widgets import widgets - -class attachDialog(widgetUtils.BaseDialog): - def __init__(self): - super(attachDialog, self).__init__(None, title=_(u"Add an attachment")) - panel = wx.Panel(self) - sizer = wx.BoxSizer(wx.VERTICAL) - lbl1 = wx.StaticText(panel, wx.ID_ANY, _(u"Attachments")) - self.attachments = widgets.list(panel, _(u"Type"), _(u"Title"), style=wx.LC_REPORT) - box = wx.BoxSizer(wx.HORIZONTAL) - box.Add(lbl1, 0, wx.ALL, 5) - box.Add(self.attachments.list, 0, wx.ALL, 5) - sizer.Add(box, 0, wx.ALL, 5) - static = wx.StaticBox(panel, label=_(u"Add attachments")) - self.photo = wx.Button(panel, wx.ID_ANY, _(u"&Photo")) - self.remove = wx.Button(panel, wx.ID_ANY, _(u"Remove attachment")) - self.remove.Enable(False) - btnsizer = wx.StaticBoxSizer(static, wx.HORIZONTAL) - btnsizer.Add(self.photo, 0, wx.ALL, 5) - sizer.Add(btnsizer, 0, wx.ALL, 5) - ok = wx.Button(panel, wx.ID_OK) - ok.SetDefault() - cancelBtn = wx.Button(panel, wx.ID_CANCEL) - btnSizer = wx.BoxSizer() - btnSizer.Add(ok, 0, wx.ALL, 5) - btnSizer.Add(cancelBtn, 0, wx.ALL, 5) - sizer.Add(btnSizer, 0, wx.ALL, 5) - panel.SetSizer(sizer) - self.SetClientSize(sizer.CalcMin()) - - def get_image(self): - openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) - if openFileDialog.ShowModal() == wx.ID_CANCEL: - return (None, None) - dsc = self.ask_description() - return (openFileDialog.GetPath(), dsc) - - def ask_description(self): - dlg = wx.TextEntryDialog(self, _(u"please provide a description"), _(u"Description")) - dlg.ShowModal() - result = dlg.GetValue() - dlg.Destroy() - return result diff --git a/src/wxUI/dialogs/message.py b/src/wxUI/dialogs/message.py deleted file mode 100644 index 8eadfdd9..00000000 --- a/src/wxUI/dialogs/message.py +++ /dev/null @@ -1,471 +0,0 @@ -# -*- coding: utf-8 -*- -import wx -import widgetUtils - -class textLimited(widgetUtils.BaseDialog): - def __init__(self, *args, **kwargs): - super(textLimited, self).__init__(parent=None, *args, **kwargs) - - def createTextArea(self, message="", text=""): - if not hasattr(self, "panel"): - self.panel = wx.Panel(self) - self.label = wx.StaticText(self.panel, -1, message) - self.SetTitle(str(len(text))) - self.text = wx.TextCtrl(self.panel, -1, text, size=(439, -1),style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER) -# font = self.text.GetFont() -# dc = wx.WindowDC(self.text) -# dc.SetFont(font) -# x, y = dc.GetTextExtent("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") -# self.text.SetSize((x, y)) - self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text) - self.text.SetFocus() - self.textBox = wx.BoxSizer(wx.HORIZONTAL) - self.textBox.Add(self.label, 0, wx.ALL, 5) - self.textBox.Add(self.text, 0, wx.ALL, 5) - - def text_focus(self): - self.text.SetFocus() - - def get_text(self): - return self.text.GetValue() - - def set_text(self, text): - return self.text.ChangeValue(text) - - def set_title(self, new_title): - return self.SetTitle(new_title) - - def enable_button(self, buttonName): - if hasattr(self, buttonName): - return getattr(self, buttonName).Enable() - - def disable_button(self, buttonName): - if hasattr(self, buttonName): - return getattr(self, buttonName).Disable() - - def onSelect(self, ev): - self.text.SelectAll() - - def handle_keys(self, event): - shift=event.ShiftDown() - if event.GetKeyCode() == wx.WXK_RETURN and shift==False and hasattr(self,'okButton'): - wx.PostEvent(self.okButton.GetEventHandler(), wx.PyCommandEvent(wx.EVT_BUTTON.typeId,wx.ID_OK)) - else: - event.Skip() - - def set_cursor_at_end(self): - self.text.SetInsertionPoint(len(self.text.GetValue())) - - def set_cursor_at_position(self, position): - self.text.SetInsertionPoint(position) - - def get_position(self): - return self.text.GetInsertionPoint() - - def popup_menu(self, menu): - self.PopupMenu(menu, self.text.GetPosition()) - -class tweet(textLimited): - def createControls(self, title, message, text): - self.mainBox = wx.BoxSizer(wx.VERTICAL) - self.createTextArea(message, text) - self.mainBox.Add(self.textBox, 0, wx.ALL, 5) - self.long_tweet = wx.CheckBox(self.panel, -1, _(u"&Long tweet")) - self.upload_image = wx.Button(self.panel, -1, _(u"&Upload image..."), size=wx.DefaultSize) - self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize) - self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize) - self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize) - self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize) - self.shortenButton.Disable() - self.unshortenButton.Disable() - self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize) - self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users")) - self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize) - self.okButton.SetDefault() - cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) - self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox1.Add(self.upload_image, 0, wx.ALL, 10) - self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 10) - self.buttonsBox1.Add(self.attach, 0, wx.ALL, 10) - self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 10) - self.buttonsBox2 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox2.Add(self.shortenButton, 0, wx.ALL, 10) - self.buttonsBox2.Add(self.unshortenButton, 0, wx.ALL, 10) - self.buttonsBox2.Add(self.translateButton, 0, wx.ALL, 10) - self.mainBox.Add(self.buttonsBox2, 0, wx.ALL, 10) - self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL) - self.ok_cancelSizer.Add(self.autocompletionButton, 0, wx.ALL, 10) - self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 10) - self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 10) - self.mainBox.Add(self.ok_cancelSizer) - selectId = wx.ID_ANY - self.Bind(wx.EVT_MENU, self.onSelect, id=selectId) - self.accel_tbl = wx.AcceleratorTable([ - (wx.ACCEL_CTRL, ord('A'), selectId), - ]) - self.SetAcceleratorTable(self.accel_tbl) - self.panel.SetSizer(self.mainBox) - - def __init__(self, title, message, text, *args, **kwargs): - super(tweet, self).__init__() - self.shift=False - self.createControls(message, title, text) - self.SetClientSize(self.mainBox.CalcMin()) - - def get_image(self): - openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) - if openFileDialog.ShowModal() == wx.ID_CANCEL: - return None - return open(openFileDialog.GetPath(), "rb") - - -class retweet(tweet): - def createControls(self, title, message, text): - self.mainBox = wx.BoxSizer(wx.VERTICAL) - self.createTextArea(message, "") - label = wx.StaticText(self.panel, -1, _(u"Retweet")) - self.text2 = wx.TextCtrl(self.panel, -1, text, size=(439, -1), style=wx.TE_MULTILINE|wx.TE_READONLY) - self.retweetBox = wx.BoxSizer(wx.HORIZONTAL) - self.retweetBox.Add(label, 0, wx.ALL, 5) - self.retweetBox.Add(self.text2, 0, wx.ALL, 5) - self.mainBox.Add(self.textBox, 0, wx.ALL, 5) - self.mainBox.Add(self.retweetBox, 0, wx.ALL, 5) - self.upload_image = wx.Button(self.panel, -1, _(u"&Upload image..."), size=wx.DefaultSize) - self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize) - self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize) - self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize) - self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize) - self.shortenButton.Disable() - self.unshortenButton.Disable() - self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize) - self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users")) - self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize) - self.okButton.SetDefault() - cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) - self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox1.Add(self.upload_image, 0, wx.ALL, 10) - self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 10) - self.buttonsBox1.Add(self.attach, 0, wx.ALL, 10) - self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 10) - self.buttonsBox2 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox2.Add(self.shortenButton, 0, wx.ALL, 10) - self.buttonsBox2.Add(self.unshortenButton, 0, wx.ALL, 10) - self.buttonsBox2.Add(self.translateButton, 0, wx.ALL, 10) - self.mainBox.Add(self.buttonsBox2, 0, wx.ALL, 10) - self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL) - self.ok_cancelSizer.Add(self.autocompletionButton, 0, wx.ALL, 10) - self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 10) - self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 10) - self.mainBox.Add(self.ok_cancelSizer) - selectId = wx.ID_ANY - self.Bind(wx.EVT_MENU, self.onSelect, id=selectId) - self.accel_tbl = wx.AcceleratorTable([ - (wx.ACCEL_CTRL, ord('A'), selectId), - ]) - self.SetAcceleratorTable(self.accel_tbl) - self.panel.SetSizer(self.mainBox) - - def __init__(self, title, message, text, *args, **kwargs): - super(tweet, self).__init__() - self.createControls(message, title, text) -# self.onTimer(wx.EVT_CHAR_HOOK) - self.SetClientSize(self.mainBox.CalcMin()) - - def get_image(self): - openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) - if openFileDialog.ShowModal() == wx.ID_CANCEL: - return None - return open(openFileDialog.GetPath(), "rb") - -class dm(textLimited): - def createControls(self, title, message, users): - self.panel = wx.Panel(self) - self.mainBox = wx.BoxSizer(wx.VERTICAL) - label = wx.StaticText(self.panel, -1, _(u"&Recipient")) - self.cb = wx.ComboBox(self.panel, -1, choices=users, value=users[0], size=wx.DefaultSize) - self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users")) - self.createTextArea(message, text="") - userBox = wx.BoxSizer(wx.HORIZONTAL) - userBox.Add(label, 0, wx.ALL, 5) - userBox.Add(self.cb, 0, wx.ALL, 5) - userBox.Add(self.autocompletionButton, 0, wx.ALL, 5) - self.mainBox.Add(userBox, 0, wx.ALL, 5) - self.mainBox.Add(self.textBox, 0, wx.ALL, 5) - self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize) - self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize) - self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize) - self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize) - self.shortenButton.Disable() - self.unshortenButton.Disable() - self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize) - self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize) - self.okButton.SetDefault() - cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) - self.buttonsBox = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5) - self.buttonsBox.Add(self.attach, 0, wx.ALL, 5) - self.mainBox.Add(self.buttonsBox, 0, wx.ALL, 5) - self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox1.Add(self.shortenButton, 0, wx.ALL, 5) - self.buttonsBox1.Add(self.unshortenButton, 0, wx.ALL, 5) - self.buttonsBox1.Add(self.translateButton, 0, wx.ALL, 5) - self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 5) - self.buttonsBox3 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox3.Add(self.okButton, 0, wx.ALL, 5) - self.buttonsBox3.Add(cancelButton, 0, wx.ALL, 5) - self.mainBox.Add(self.buttonsBox3, 0, wx.ALL, 5) - self.panel.SetSizer(self.mainBox) - self.SetClientSize(self.mainBox.CalcMin()) - - def __init__(self, title, message, users, *args, **kwargs): - super(dm, self).__init__() - self.createControls(title, message, users) -# self.onTimer(wx.EVT_CHAR_HOOK) -# self.SetClientSize(self.mainBox.CalcMin()) - - def get_user(self): - return self.cb.GetValue() - - def set_user(self, user): - return self.cb.SetValue(user) - -class reply(textLimited): - - def get_image(self): - openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) - if openFileDialog.ShowModal() == wx.ID_CANCEL: - return None - return open(openFileDialog.GetPath(), "rb") - - def createControls(self, title, message, text): - self.mainBox = wx.BoxSizer(wx.VERTICAL) - self.createTextArea(message, text) - self.mainBox.Add(self.textBox, 0, wx.ALL, 5) - self.usersbox = wx.BoxSizer(wx.VERTICAL) - self.mentionAll = wx.CheckBox(self.panel, -1, _(u"&Mention to all"), size=wx.DefaultSize) - self.mentionAll.Disable() - self.usersbox.Add(self.mentionAll, 0, wx.ALL, 5) - self.checkboxes = [] - for i in self.users: - user_checkbox = wx.CheckBox(self.panel, -1, "@"+i, size=wx.DefaultSize) - self.checkboxes.append(user_checkbox) - self.usersbox.Add(self.checkboxes[-1], 0, wx.ALL, 5) - self.mainBox.Add(self.usersbox, 0, wx.ALL, 10) - self.long_tweet = wx.CheckBox(self.panel, -1, _(u"&Long tweet")) - self.upload_image = wx.Button(self.panel, -1, _(u"&Upload image..."), size=wx.DefaultSize) - self.spellcheck = wx.Button(self.panel, -1, _("Check &spelling..."), size=wx.DefaultSize) - self.attach = wx.Button(self.panel, -1, _(u"&Attach audio..."), size=wx.DefaultSize) - self.shortenButton = wx.Button(self.panel, -1, _(u"Sh&orten URL"), size=wx.DefaultSize) - self.unshortenButton = wx.Button(self.panel, -1, _(u"&Expand URL"), size=wx.DefaultSize) - self.shortenButton.Disable() - self.unshortenButton.Disable() - self.translateButton = wx.Button(self.panel, -1, _(u"&Translate..."), size=wx.DefaultSize) - self.autocompletionButton = wx.Button(self.panel, -1, _(u"Auto&complete users")) - self.okButton = wx.Button(self.panel, wx.ID_OK, _(u"Sen&d"), size=wx.DefaultSize) - self.okButton.SetDefault() - cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) - self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox1.Add(self.upload_image, 0, wx.ALL, 10) - self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 10) - self.buttonsBox1.Add(self.attach, 0, wx.ALL, 10) - self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 10) - self.buttonsBox2 = wx.BoxSizer(wx.HORIZONTAL) - self.buttonsBox2.Add(self.shortenButton, 0, wx.ALL, 10) - self.buttonsBox2.Add(self.unshortenButton, 0, wx.ALL, 10) - self.buttonsBox2.Add(self.translateButton, 0, wx.ALL, 10) - self.mainBox.Add(self.buttonsBox2, 0, wx.ALL, 10) - self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL) - self.ok_cancelSizer.Add(self.autocompletionButton, 0, wx.ALL, 10) - self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 10) - self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 10) - self.mainBox.Add(self.ok_cancelSizer, 0, wx.ALL, 10) - selectId = wx.ID_ANY - self.Bind(wx.EVT_MENU, self.onSelect, id=selectId) - self.accel_tbl = wx.AcceleratorTable([ - (wx.ACCEL_CTRL, ord('A'), selectId), - ]) - self.SetAcceleratorTable(self.accel_tbl) - self.panel.SetSizer(self.mainBox) - - def __init__(self, title, message, text, users=[], *args, **kwargs): - self.users = users - super(reply, self).__init__() - self.shift=False - self.createControls(message, title, text) - self.SetClientSize(self.mainBox.CalcMin()) - -class viewTweet(widgetUtils.BaseDialog): - def set_title(self, lenght): - self.SetTitle(_(u"Tweet - %i characters ") % (lenght,)) - - def __init__(self, text, rt_count, favs_count, source, date="", *args, **kwargs): - super(viewTweet, self).__init__(None, size=(850,850)) - panel = wx.Panel(self) - label = wx.StaticText(panel, -1, _(u"Tweet")) - self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180)) - dc = wx.WindowDC(self.text) - dc.SetFont(self.text.GetFont()) - (x, y) = dc.GetMultiLineTextExtent("0"*140) - self.text.SetSize((x, y)) - self.text.SetFocus() - textBox = wx.BoxSizer(wx.HORIZONTAL) - textBox.Add(label, 0, wx.ALL, 5) - textBox.Add(self.text, 1, wx.EXPAND, 5) - mainBox = wx.BoxSizer(wx.VERTICAL) - mainBox.Add(textBox, 0, wx.ALL, 5) - label2 = wx.StaticText(panel, -1, _(u"Image description")) - self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180)) - dc = wx.WindowDC(self.image_description) - dc.SetFont(self.image_description.GetFont()) - (x, y) = dc.GetMultiLineTextExtent("0"*450) - self.image_description.SetSize((x, y)) - self.image_description.Enable(False) - iBox = wx.BoxSizer(wx.HORIZONTAL) - iBox.Add(label2, 0, wx.ALL, 5) - iBox.Add(self.image_description, 1, wx.EXPAND, 5) - mainBox.Add(iBox, 0, wx.ALL, 5) - rtCountLabel = wx.StaticText(panel, -1, _(u"Retweets: ")) - rtCount = wx.TextCtrl(panel, -1, rt_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) - rtBox = wx.BoxSizer(wx.HORIZONTAL) - rtBox.Add(rtCountLabel, 0, wx.ALL, 5) - rtBox.Add(rtCount, 0, wx.ALL, 5) - favsCountLabel = wx.StaticText(panel, -1, _(u"Likes: ")) - favsCount = wx.TextCtrl(panel, -1, favs_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) - favsBox = wx.BoxSizer(wx.HORIZONTAL) - favsBox.Add(favsCountLabel, 0, wx.ALL, 5) - favsBox.Add(favsCount, 0, wx.ALL, 5) - sourceLabel = wx.StaticText(panel, -1, _(u"Source: ")) - sourceTweet = wx.TextCtrl(panel, -1, source, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) - sourceBox = wx.BoxSizer(wx.HORIZONTAL) - sourceBox.Add(sourceLabel, 0, wx.ALL, 5) - sourceBox.Add(sourceTweet, 0, wx.ALL, 5) - dateLabel = wx.StaticText(panel, -1, _(u"Date: ")) - dateTweet = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) - dc = wx.WindowDC(dateTweet) - dc.SetFont(dateTweet.GetFont()) - (x, y) = dc.GetTextExtent("0"*100) - dateTweet.SetSize((x, y)) - dateBox = wx.BoxSizer(wx.HORIZONTAL) - dateBox.Add(dateLabel, 0, wx.ALL, 5) - dateBox.Add(dateTweet, 0, wx.ALL, 5) - infoBox = wx.BoxSizer(wx.HORIZONTAL) - infoBox.Add(rtBox, 0, wx.ALL, 5) - infoBox.Add(favsBox, 0, wx.ALL, 5) - infoBox.Add(sourceBox, 0, wx.ALL, 5) - mainBox.Add(infoBox, 0, wx.ALL, 5) - mainBox.Add(dateBox, 0, wx.ALL, 5) - self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard")) - self.share.Enable(False) - self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize) - self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize) - self.unshortenButton.Disable() - self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize) - cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) - cancelButton.SetDefault() - buttonsBox = wx.BoxSizer(wx.HORIZONTAL) - buttonsBox.Add(self.share, 0, wx.ALL, 5) - buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5) - buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5) - buttonsBox.Add(self.translateButton, 0, wx.ALL, 5) - buttonsBox.Add(cancelButton, 0, wx.ALL, 5) - mainBox.Add(buttonsBox, 0, wx.ALL, 5) - selectId = wx.ID_ANY - self.Bind(wx.EVT_MENU, self.onSelect, id=selectId) - self.accel_tbl = wx.AcceleratorTable([ - (wx.ACCEL_CTRL, ord('A'), selectId), - ]) - self.SetAcceleratorTable(self.accel_tbl) - panel.SetSizer(mainBox) - self.SetClientSize(mainBox.CalcMin()) - - def set_text(self, text): - self.text.ChangeValue(text) - - def get_text(self): - return self.text.GetValue() - - def set_image_description(self, desc): - self.image_description.Enable(True) - if len(self.image_description.GetValue()) == 0: - self.image_description.SetValue(desc) - else: - self.image_description.SetValue(self.image_description.GetValue()+"\n"+desc) - - def text_focus(self): - self.text.SetFocus() - - def onSelect(self, ev): - self.text.SelectAll() - - def enable_button(self, buttonName): - if hasattr(self, buttonName): - return getattr(self, buttonName).Enable() - -class viewNonTweet(widgetUtils.BaseDialog): - - def __init__(self, text, date="", *args, **kwargs): - super(viewNonTweet, self).__init__(None, size=(850,850)) - self.SetTitle(_(u"View")) - panel = wx.Panel(self) - label = wx.StaticText(panel, -1, _(u"Item")) - self.text = wx.TextCtrl(parent=panel, id=-1, value=text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180)) - dc = wx.WindowDC(self.text) - dc.SetFont(self.text.GetFont()) - (x, y) = dc.GetMultiLineTextExtent("0"*140) - self.text.SetSize((x, y)) - self.text.SetFocus() - textBox = wx.BoxSizer(wx.HORIZONTAL) - textBox.Add(label, 0, wx.ALL, 5) - textBox.Add(self.text, 1, wx.EXPAND, 5) - mainBox = wx.BoxSizer(wx.VERTICAL) - mainBox.Add(textBox, 0, wx.ALL, 5) - if date != "": - dateLabel = wx.StaticText(panel, -1, _(u"Date: ")) - date = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) - dc = wx.WindowDC(date) - dc.SetFont(date.GetFont()) - (x, y) = dc.GetTextExtent("0"*100) - date.SetSize((x, y)) - dateBox = wx.BoxSizer(wx.HORIZONTAL) - dateBox.Add(dateLabel, 0, wx.ALL, 5) - dateBox.Add(date, 0, wx.ALL, 5) - mainBox.Add(dateBox, 0, wx.ALL, 5) - self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard")) - self.share.Enable(False) - self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize) - self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize) - self.unshortenButton.Disable() - self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize) - cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) - cancelButton.SetDefault() - buttonsBox = wx.BoxSizer(wx.HORIZONTAL) - buttonsBox.Add(self.share, 0, wx.ALL, 5) - buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5) - buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5) - buttonsBox.Add(self.translateButton, 0, wx.ALL, 5) - buttonsBox.Add(cancelButton, 0, wx.ALL, 5) - mainBox.Add(buttonsBox, 0, wx.ALL, 5) - selectId = wx.ID_ANY - self.Bind(wx.EVT_MENU, self.onSelect, id=selectId) - self.accel_tbl = wx.AcceleratorTable([ - (wx.ACCEL_CTRL, ord('A'), selectId), - ]) - self.SetAcceleratorTable(self.accel_tbl) - panel.SetSizer(mainBox) - self.SetClientSize(mainBox.CalcMin()) - - def onSelect(self, ev): - self.text.SelectAll() - - def set_text(self, text): - self.text.ChangeValue(text) - - def get_text(self): - return self.text.GetValue() - - def text_focus(self): - self.text.SetFocus() - - def enable_button(self, buttonName): - if hasattr(self, buttonName): - return getattr(self, buttonName).Enable() diff --git a/src/wxUI/dialogs/twitterDialogs/__init__.py b/src/wxUI/dialogs/twitterDialogs/__init__.py new file mode 100644 index 00000000..6a829f06 --- /dev/null +++ b/src/wxUI/dialogs/twitterDialogs/__init__.py @@ -0,0 +1 @@ +from .tweetDialogs import tweet, reply, dm, viewTweet, viewNonTweet, poll \ No newline at end of file diff --git a/src/wxUI/dialogs/twitterDialogs/tweetDialogs.py b/src/wxUI/dialogs/twitterDialogs/tweetDialogs.py new file mode 100644 index 00000000..84ea5fde --- /dev/null +++ b/src/wxUI/dialogs/twitterDialogs/tweetDialogs.py @@ -0,0 +1,544 @@ +""" GUI dialogs for tweet writing and displaying. """ +import wx +from typing import List + +class tweet(wx.Dialog): + def __init__(self, title: str, caption: str, message: str = "", max_length: int = 280, thread_mode: bool = True, *args, **kwds) -> None: + """ Creates the basic Tweet dialog. This might be considered the base class for other dialogs. + title str: title to be used in the dialog. + caption str: This is the text to be placed alongside the text field. + message str: Text to be inserted in the tweet. + max_length int: Maximum amount of characters the tweet will accept. By default is 280 chahracters. + thread_mode bool: If set to False, disables the button that allows to make threads by adding more tweets. + """ + super(tweet, self).__init__(parent=None, *args, **kwds) + self.SetTitle(title) + self.create_controls(max_length=max_length, caption=caption, message=message, thread_mode=thread_mode) + + def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None: + panel = wx.Panel(self) + mainBox = wx.BoxSizer(wx.VERTICAL) + text_sizer = wx.BoxSizer(wx.VERTICAL) + mainBox.Add(text_sizer, 1, wx.EXPAND, 0) + label_1 = wx.StaticText(panel, wx.ID_ANY, caption) + text_sizer.Add(label_1, 0, 0, 0) + self.text = wx.TextCtrl(panel, wx.ID_ANY, "", style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER) + self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text) + self.text.SetMinSize((1000, 158)) + self.text.SetMaxLength(max_length) + text_sizer.Add(self.text, 1, wx.EXPAND, 0) + list_sizer = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(list_sizer, 1, wx.EXPAND, 0) + Attachment_sizer = wx.BoxSizer(wx.VERTICAL) + list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0) + label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments")) + Attachment_sizer.Add(label_2, 0, 0, 0) + self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES) + self.attachments.AppendColumn(_("File")) + self.attachments.AppendColumn(_("Type")) + self.attachments.AppendColumn(_("Description")) + Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0) + self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment")) + self.remove_attachment.Enable(False) + Attachment_sizer.Add(self.remove_attachment, 0, 0, 0) + tweet_sizer = wx.BoxSizer(wx.VERTICAL) + list_sizer.Add(tweet_sizer, 1, wx.EXPAND, 0) + label_3 = wx.StaticText(panel, wx.ID_ANY, _("Added Tweets")) + tweet_sizer.Add(label_3, 0, 0, 0) + self.tweets = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES) + self.tweets.AppendColumn(_("Text")) + self.tweets.AppendColumn(_("Attachments")) + self.tweets.Enable(False) + tweet_sizer.Add(self.tweets, 1, wx.EXPAND, 0) + self.remove_tweet = wx.Button(panel, wx.ID_ANY, _("Delete tweet")) + self.remove_tweet.Enable(False) + tweet_sizer.Add(self.remove_tweet, 0, 0, 0) + btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0) + self.add = wx.Button(panel, wx.ID_ANY, _("A&dd...")) + btn_sizer_1.Add(self.add, 0, 0, 0) + self.add_tweet = wx.Button(panel, wx.ID_ANY, _("Add t&weet")) + self.add_tweet.Enable(thread_mode) + btn_sizer_1.Add(self.add_tweet, 0, 0, 0) + self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio...")) + btn_sizer_1.Add(self.add_audio, 0, 0, 0) + btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0) + self.autocomplete_users = wx.Button(panel, wx.ID_ANY, _("Auto&complete users")) + btn_sizer_2.Add(self.autocomplete_users, 0, 0, 0) + self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling...")) + btn_sizer_2.Add(self.spellcheck, 0, 0, 0) + self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate")) + btn_sizer_2.Add(self.translate, 0, 0, 0) + ok_cancel_sizer = wx.StdDialogButtonSizer() + mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + self.send = wx.Button(panel, wx.ID_OK, _("Sen&d")) + self.send.SetDefault() + ok_cancel_sizer.Add(self.send, 0, 0, 0) + self.cancel = wx.Button(panel, wx.ID_CANCEL, "") + ok_cancel_sizer.AddButton(self.cancel) + ok_cancel_sizer.Realize() + panel.SetSizer(mainBox) + self.Fit() + self.SetAffirmativeId(self.send.GetId()) + self.SetEscapeId(self.cancel.GetId()) + self.Layout() + + def handle_keys(self, event: wx.Event, *args, **kwargs) -> None: + """ Allows to react to certain keyboard events from the text control. """ + shift=event.ShiftDown() + if event.GetKeyCode() == wx.WXK_RETURN and shift==False and hasattr(self,'send'): + self.EndModal(wx.ID_OK) + else: + event.Skip() + + def reset_controls(self) -> None: + """ Resetss text control and attachments to their default, empty values. This is used while adding more tweets in a thread. """ + self.text.ChangeValue("") + self.attachments.DeleteAllItems() + + def add_item(self, list_type: str = "attachment", item: List[str] = []) -> None: + """ Adds an item to a list control. Item should be a list with the same amount of items for each column present in the ListCtrl. """ + if list_type == "attachment": + self.attachments.Append(item) + else: + self.tweets.Append(item) + + def remove_item(self, list_type: str = "attachment") -> None: + if list_type == "attachment": + item = self.attachments.GetFocusedItem() + if item > -1: + self.attachments.DeleteItem(item) + else: + item = self.tweets.GetFocusedItem() + if item > -1: + self.tweets.DeleteItem(item) + + def attach_menu(self, event=None, enabled=True, *args, **kwargs): + menu = wx.Menu() + self.add_image = menu.Append(wx.ID_ANY, _("Image")) + self.add_image.Enable(enabled) + self.add_video = menu.Append(wx.ID_ANY, _("Video")) + self.add_video.Enable(enabled) + self.add_poll = menu.Append(wx.ID_ANY, _("Poll")) + self.add_poll.Enable(enabled) + return menu + + def ask_description(self): + dlg = wx.TextEntryDialog(self, _(u"please provide a description"), _(u"Description")) + dlg.ShowModal() + result = dlg.GetValue() + dlg.Destroy() + return result + + def get_image(self): + openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) + if openFileDialog.ShowModal() == wx.ID_CANCEL: + return (None, None) + dsc = self.ask_description() + return (openFileDialog.GetPath(), dsc) + + def get_video(self): + openFileDialog = wx.FileDialog(self, _("Select the video to be uploaded"), "", "", _("Video files (*.mp4)|*.mp4"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) + if openFileDialog.ShowModal() == wx.ID_CANCEL: + return None + return openFileDialog.GetPath() + + def unable_to_attach_file(self, *args, **kwargs): + return wx.MessageDialog(self, _("It is not possible to add more attachments. Please make sure your tweet complies with Twitter'S attachment rules. You can add only one video or GIF in every tweet, and a maximum of 4 photos."), _("Error adding attachment"), wx.ICON_ERROR).ShowModal() + +class reply(tweet): + + def __init__(self, users: List[str] = [], *args, **kwargs) -> None: + self.users = users + super(reply, self).__init__(*args, **kwargs) + + def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None: + panel = wx.Panel(self) + mainBox = wx.BoxSizer(wx.VERTICAL) + text_sizer = wx.BoxSizer(wx.VERTICAL) + mainBox.Add(text_sizer, 1, wx.EXPAND, 0) + label_1 = wx.StaticText(panel, wx.ID_ANY, caption) + text_sizer.Add(label_1, 0, 0, 0) + self.text = wx.TextCtrl(panel, wx.ID_ANY, "", style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER) + self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text) + self.text.SetMinSize((1000, 158)) + self.text.SetMaxLength(max_length) + text_sizer.Add(self.text, 1, wx.EXPAND, 0) + list_sizer = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(list_sizer, 1, wx.EXPAND, 0) + Attachment_sizer = wx.BoxSizer(wx.VERTICAL) + list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0) + label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments")) + Attachment_sizer.Add(label_2, 0, 0, 0) + self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES) + self.attachments.AppendColumn(_("File")) + self.attachments.AppendColumn(_("Type")) + self.attachments.AppendColumn(_("Description")) + Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0) + self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment")) + self.remove_attachment.Enable(False) + Attachment_sizer.Add(self.remove_attachment, 0, 0, 0) + user_sizer = wx.BoxSizer(wx.VERTICAL) + list_sizer.Add(user_sizer, 0, 0, 0) + self.mention_all = wx.CheckBox(panel, -1, _(u"&Mention to all"), size=wx.DefaultSize) + self.mention_all.Disable() + user_sizer.Add(self.mention_all, 0, wx.ALL, 5) + self.checkboxes = [] + for i in self.users: + user_checkbox = wx.CheckBox(panel, -1, "@"+i, size=wx.DefaultSize) + self.checkboxes.append(user_checkbox) + user_sizer.Add(self.checkboxes[-1], 0, wx.ALL, 5) + btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0) + self.add = wx.Button(panel, wx.ID_ANY, _("A&dd...")) + btn_sizer_1.Add(self.add, 0, 0, 0) + self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio...")) + btn_sizer_1.Add(self.add_audio, 0, 0, 0) + btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0) + self.autocomplete_users = wx.Button(panel, wx.ID_ANY, _("Auto&complete users")) + btn_sizer_2.Add(self.autocomplete_users, 0, 0, 0) + self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling...")) + btn_sizer_2.Add(self.spellcheck, 0, 0, 0) + self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate")) + btn_sizer_2.Add(self.translate, 0, 0, 0) + ok_cancel_sizer = wx.StdDialogButtonSizer() + mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + self.send = wx.Button(panel, wx.ID_OK, _("Sen&d")) + self.send.SetDefault() + ok_cancel_sizer.Add(self.send, 0, 0, 0) + self.cancel = wx.Button(panel, wx.ID_CANCEL, "") + ok_cancel_sizer.AddButton(self.cancel) + ok_cancel_sizer.Realize() + panel.SetSizer(mainBox) + self.Fit() + self.SetAffirmativeId(self.send.GetId()) + self.SetEscapeId(self.cancel.GetId()) + self.Layout() + + def attach_menu(self, event=None, enabled=True, *args, **kwargs): + menu = wx.Menu() + self.add_image = menu.Append(wx.ID_ANY, _("Image")) + self.add_image.Enable(enabled) + self.add_video = menu.Append(wx.ID_ANY, _("Video")) + self.add_video.Enable(enabled) + return menu + +class dm(tweet): + + def __init__(self, users: List[str] = [], *args, **kwargs) -> None: + self.users = users + super(dm, self).__init__(*args, **kwargs) + + def create_controls(self, message: str, caption: str, max_length: int, thread_mode: bool) -> None: + panel = wx.Panel(self) + mainBox = wx.BoxSizer(wx.VERTICAL) + label_recipient = wx.StaticText(panel, -1, _(u"&Recipient")) + self.cb = wx.ComboBox(panel, -1, choices=self.users, value=self.users[0], size=wx.DefaultSize) + self.autocomplete_users = wx.Button(panel, -1, _(u"Auto&complete users")) + recipient_sizer = wx.BoxSizer(wx.HORIZONTAL) + recipient_sizer.Add(label_recipient, 0, 0, 0) + recipient_sizer.Add(self.cb, 1, wx.EXPAND, 0) + mainBox.Add(recipient_sizer, 0, wx.EXPAND, 0) + text_sizer = wx.BoxSizer(wx.VERTICAL) + mainBox.Add(text_sizer, 1, wx.EXPAND, 0) + label_1 = wx.StaticText(panel, wx.ID_ANY, caption) + text_sizer.Add(label_1, 0, 0, 0) + self.text = wx.TextCtrl(panel, wx.ID_ANY, "", style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER) + self.Bind(wx.EVT_CHAR_HOOK, self.handle_keys, self.text) + self.text.SetMinSize((1000, 158)) + self.text.SetMaxLength(max_length) + self.text.SetFocus() + text_sizer.Add(self.text, 1, wx.EXPAND, 0) + list_sizer = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(list_sizer, 1, wx.EXPAND, 0) + Attachment_sizer = wx.BoxSizer(wx.VERTICAL) + list_sizer.Add(Attachment_sizer, 1, wx.EXPAND, 0) + label_2 = wx.StaticText(panel, wx.ID_ANY, _("Attachments")) + Attachment_sizer.Add(label_2, 0, 0, 0) + self.attachments = wx.ListCtrl(panel, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES) + self.attachments.AppendColumn(_("File")) + self.attachments.AppendColumn(_("Type")) + self.attachments.AppendColumn(_("Description")) + Attachment_sizer.Add(self.attachments, 1, wx.EXPAND, 0) + self.remove_attachment = wx.Button(panel, wx.ID_ANY, _("Delete attachment")) + self.remove_attachment.Enable(False) + Attachment_sizer.Add(self.remove_attachment, 0, 0, 0) + btn_sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(btn_sizer_1, 1, wx.EXPAND, 0) + self.add = wx.Button(panel, wx.ID_ANY, _("A&dd...")) + btn_sizer_1.Add(self.add, 0, 0, 0) + self.add_audio = wx.Button(panel, wx.ID_ANY, _("&Attach audio...")) + btn_sizer_1.Add(self.add_audio, 0, 0, 0) + btn_sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + mainBox.Add(btn_sizer_2, 1, wx.EXPAND, 0) + self.spellcheck = wx.Button(panel, wx.ID_ANY, _("Check &spelling...")) + btn_sizer_2.Add(self.spellcheck, 0, 0, 0) + self.translate = wx.Button(panel, wx.ID_ANY, _("&Translate")) + btn_sizer_2.Add(self.translate, 0, 0, 0) + ok_cancel_sizer = wx.StdDialogButtonSizer() + mainBox.Add(ok_cancel_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + self.send = wx.Button(panel, wx.ID_OK, _("Sen&d")) + self.send.SetDefault() + ok_cancel_sizer.Add(self.send, 0, 0, 0) + self.cancel = wx.Button(panel, wx.ID_CANCEL, "") + ok_cancel_sizer.AddButton(self.cancel) + ok_cancel_sizer.Realize() + panel.SetSizer(mainBox) + self.Fit() + self.SetAffirmativeId(self.send.GetId()) + self.SetEscapeId(self.cancel.GetId()) + self.Layout() + + def get_image(self): + openFileDialog = wx.FileDialog(self, _(u"Select the picture to be uploaded"), "", "", _("Image files (*.png, *.jpg, *.gif)|*.png; *.jpg; *.gif"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) + if openFileDialog.ShowModal() == wx.ID_CANCEL: + return (None, None) + return (openFileDialog.GetPath(), "") + + def attach_menu(self, event=None, enabled=True, *args, **kwargs): + menu = wx.Menu() + self.add_image = menu.Append(wx.ID_ANY, _("Image")) + self.add_image.Enable(enabled) + self.add_video = menu.Append(wx.ID_ANY, _("Video")) + self.add_video.Enable(enabled) + return menu + +class viewTweet(wx.Dialog): + def set_title(self, lenght): + self.SetTitle(_(u"Tweet - %i characters ") % (lenght,)) + + def __init__(self, text, rt_count, favs_count, source, date="", *args, **kwargs): + super(viewTweet, self).__init__(None, size=(850,850)) + panel = wx.Panel(self) + label = wx.StaticText(panel, -1, _(u"Tweet")) + self.text = wx.TextCtrl(panel, -1, text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180)) + dc = wx.WindowDC(self.text) + dc.SetFont(self.text.GetFont()) + (x, y) = dc.GetMultiLineTextExtent("W"*280) + self.text.SetSize((x, y)) + self.text.SetFocus() + textBox = wx.BoxSizer(wx.HORIZONTAL) + textBox.Add(label, 0, wx.ALL, 5) + textBox.Add(self.text, 1, wx.EXPAND, 5) + mainBox = wx.BoxSizer(wx.VERTICAL) + mainBox.Add(textBox, 0, wx.ALL, 5) + label2 = wx.StaticText(panel, -1, _(u"Image description")) + self.image_description = wx.TextCtrl(panel, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180)) + dc = wx.WindowDC(self.image_description) + dc.SetFont(self.image_description.GetFont()) + (x, y) = dc.GetMultiLineTextExtent("0"*450) + self.image_description.SetSize((x, y)) + self.image_description.Enable(False) + iBox = wx.BoxSizer(wx.HORIZONTAL) + iBox.Add(label2, 0, wx.ALL, 5) + iBox.Add(self.image_description, 1, wx.EXPAND, 5) + mainBox.Add(iBox, 0, wx.ALL, 5) + rtCountLabel = wx.StaticText(panel, -1, _(u"Retweets: ")) + rtCount = wx.TextCtrl(panel, -1, rt_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) + rtBox = wx.BoxSizer(wx.HORIZONTAL) + rtBox.Add(rtCountLabel, 0, wx.ALL, 5) + rtBox.Add(rtCount, 0, wx.ALL, 5) + favsCountLabel = wx.StaticText(panel, -1, _(u"Likes: ")) + favsCount = wx.TextCtrl(panel, -1, favs_count, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) + favsBox = wx.BoxSizer(wx.HORIZONTAL) + favsBox.Add(favsCountLabel, 0, wx.ALL, 5) + favsBox.Add(favsCount, 0, wx.ALL, 5) + sourceLabel = wx.StaticText(panel, -1, _(u"Source: ")) + sourceTweet = wx.TextCtrl(panel, -1, source, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) + sourceBox = wx.BoxSizer(wx.HORIZONTAL) + sourceBox.Add(sourceLabel, 0, wx.ALL, 5) + sourceBox.Add(sourceTweet, 0, wx.ALL, 5) + dateLabel = wx.StaticText(panel, -1, _(u"Date: ")) + dateTweet = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) + dc = wx.WindowDC(dateTweet) + dc.SetFont(dateTweet.GetFont()) + (x, y) = dc.GetTextExtent("0"*100) + dateTweet.SetSize((x, y)) + dateBox = wx.BoxSizer(wx.HORIZONTAL) + dateBox.Add(dateLabel, 0, wx.ALL, 5) + dateBox.Add(dateTweet, 0, wx.ALL, 5) + infoBox = wx.BoxSizer(wx.HORIZONTAL) + infoBox.Add(rtBox, 0, wx.ALL, 5) + infoBox.Add(favsBox, 0, wx.ALL, 5) + infoBox.Add(sourceBox, 0, wx.ALL, 5) + mainBox.Add(infoBox, 0, wx.ALL, 5) + mainBox.Add(dateBox, 0, wx.ALL, 5) + self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard")) + self.share.Enable(False) + self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize) + self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize) + cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) + cancelButton.SetDefault() + buttonsBox = wx.BoxSizer(wx.HORIZONTAL) + buttonsBox.Add(self.share, 0, wx.ALL, 5) + buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5) + buttonsBox.Add(self.translateButton, 0, wx.ALL, 5) + buttonsBox.Add(cancelButton, 0, wx.ALL, 5) + mainBox.Add(buttonsBox, 0, wx.ALL, 5) + selectId = wx.ID_ANY + self.Bind(wx.EVT_MENU, self.onSelect, id=selectId) + self.accel_tbl = wx.AcceleratorTable([ + (wx.ACCEL_CTRL, ord('A'), selectId), + ]) + self.SetAcceleratorTable(self.accel_tbl) + panel.SetSizer(mainBox) + self.SetClientSize(mainBox.CalcMin()) + + def set_text(self, text): + self.text.ChangeValue(text) + + def get_text(self): + return self.text.GetValue() + + def set_image_description(self, desc): + self.image_description.Enable(True) + if len(self.image_description.GetValue()) == 0: + self.image_description.SetValue(desc) + else: + self.image_description.SetValue(self.image_description.GetValue()+"\n"+desc) + + def text_focus(self): + self.text.SetFocus() + + def onSelect(self, ev): + self.text.SelectAll() + + def enable_button(self, buttonName): + if hasattr(self, buttonName): + return getattr(self, buttonName).Enable() + +class viewNonTweet(wx.Dialog): + + def __init__(self, text, date="", *args, **kwargs): + super(viewNonTweet, self).__init__(None, size=(850,850)) + self.SetTitle(_(u"View")) + panel = wx.Panel(self) + label = wx.StaticText(panel, -1, _(u"Item")) + self.text = wx.TextCtrl(parent=panel, id=-1, value=text, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(250, 180)) + dc = wx.WindowDC(self.text) + dc.SetFont(self.text.GetFont()) + (x, y) = dc.GetMultiLineTextExtent("0"*140) + self.text.SetSize((x, y)) + self.text.SetFocus() + textBox = wx.BoxSizer(wx.HORIZONTAL) + textBox.Add(label, 0, wx.ALL, 5) + textBox.Add(self.text, 1, wx.EXPAND, 5) + mainBox = wx.BoxSizer(wx.VERTICAL) + mainBox.Add(textBox, 0, wx.ALL, 5) + if date != "": + dateLabel = wx.StaticText(panel, -1, _(u"Date: ")) + date = wx.TextCtrl(panel, -1, date, size=wx.DefaultSize, style=wx.TE_READONLY|wx.TE_MULTILINE) + dc = wx.WindowDC(date) + dc.SetFont(date.GetFont()) + (x, y) = dc.GetTextExtent("0"*100) + date.SetSize((x, y)) + dateBox = wx.BoxSizer(wx.HORIZONTAL) + dateBox.Add(dateLabel, 0, wx.ALL, 5) + dateBox.Add(date, 0, wx.ALL, 5) + mainBox.Add(dateBox, 0, wx.ALL, 5) + self.share = wx.Button(panel, wx.ID_ANY, _("Copy link to clipboard")) + self.share.Enable(False) + self.spellcheck = wx.Button(panel, -1, _("Check &spelling..."), size=wx.DefaultSize) + self.unshortenButton = wx.Button(panel, -1, _(u"&Expand URL"), size=wx.DefaultSize) + self.unshortenButton.Disable() + self.translateButton = wx.Button(panel, -1, _(u"&Translate..."), size=wx.DefaultSize) + cancelButton = wx.Button(panel, wx.ID_CANCEL, _(u"C&lose"), size=wx.DefaultSize) + cancelButton.SetDefault() + buttonsBox = wx.BoxSizer(wx.HORIZONTAL) + buttonsBox.Add(self.share, 0, wx.ALL, 5) + buttonsBox.Add(self.spellcheck, 0, wx.ALL, 5) + buttonsBox.Add(self.unshortenButton, 0, wx.ALL, 5) + buttonsBox.Add(self.translateButton, 0, wx.ALL, 5) + buttonsBox.Add(cancelButton, 0, wx.ALL, 5) + mainBox.Add(buttonsBox, 0, wx.ALL, 5) + selectId = wx.ID_ANY + self.Bind(wx.EVT_MENU, self.onSelect, id=selectId) + self.accel_tbl = wx.AcceleratorTable([ + (wx.ACCEL_CTRL, ord('A'), selectId), + ]) + self.SetAcceleratorTable(self.accel_tbl) + panel.SetSizer(mainBox) + self.SetClientSize(mainBox.CalcMin()) + + def onSelect(self, ev): + self.text.SelectAll() + + def set_text(self, text): + self.text.ChangeValue(text) + + def get_text(self): + return self.text.GetValue() + + def text_focus(self): + self.text.SetFocus() + + def enable_button(self, buttonName): + if hasattr(self, buttonName): + return getattr(self, buttonName).Enable() + +class poll(wx.Dialog): + def __init__(self, *args, **kwds): + super(poll, self).__init__(parent=None, id=wx.NewId(), title=_("Add a poll")) + sizer_1 = wx.BoxSizer(wx.VERTICAL) + period_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_1.Add(period_sizer, 1, wx.EXPAND, 0) + label_period = wx.StaticText(self, wx.ID_ANY, _("Participation time (in days)")) + period_sizer.Add(label_period, 0, 0, 0) + self.period = wx.SpinCtrl(self, wx.ID_ANY) + self.period.SetFocus() + self.period.SetRange(1, 7) + self.period.SetValue(7) + period_sizer.Add(self.period, 0, 0, 0) + sizer_2 = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Choices")), wx.VERTICAL) + sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) + option1_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option1_sizer, 1, wx.EXPAND, 0) + label_2 = wx.StaticText(self, wx.ID_ANY, _("Option 1")) + option1_sizer.Add(label_2, 0, 0, 0) + self.option1 = wx.TextCtrl(self, wx.ID_ANY, "") + option1_sizer.Add(self.option1, 0, 0, 0) + option2_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option2_sizer, 1, wx.EXPAND, 0) + label_3 = wx.StaticText(self, wx.ID_ANY, _("Option 2")) + option2_sizer.Add(label_3, 0, 0, 0) + self.option2 = wx.TextCtrl(self, wx.ID_ANY, "") + option2_sizer.Add(self.option2, 0, 0, 0) + option3_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option3_sizer, 1, wx.EXPAND, 0) + label_4 = wx.StaticText(self, wx.ID_ANY, _("Option 3")) + option3_sizer.Add(label_4, 0, 0, 0) + self.option3 = wx.TextCtrl(self, wx.ID_ANY, "") + option3_sizer.Add(self.option3, 0, 0, 0) + option4_sizer = wx.BoxSizer(wx.HORIZONTAL) + sizer_2.Add(option4_sizer, 1, wx.EXPAND, 0) + label_5 = wx.StaticText(self, wx.ID_ANY, _("Option 4")) + option4_sizer.Add(label_5, 0, 0, 0) + self.option4 = wx.TextCtrl(self, wx.ID_ANY, "") + option4_sizer.Add(self.option4, 0, 0, 0) + btn_sizer = wx.StdDialogButtonSizer() + sizer_1.Add(btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + self.button_OK = wx.Button(self, wx.ID_OK) + self.button_OK.SetDefault() + self.button_OK.Bind(wx.EVT_BUTTON, self.validate_data) + btn_sizer.AddButton(self.button_OK) + self.button_CANCEL = wx.Button(self, wx.ID_CANCEL, "") + btn_sizer.AddButton(self.button_CANCEL) + btn_sizer.Realize() + self.SetSizer(sizer_1) + sizer_1.Fit(self) + self.SetAffirmativeId(self.button_OK.GetId()) + self.SetEscapeId(self.button_CANCEL.GetId()) + self.Layout() + + def get_options(self): + controls = [self.option1, self.option2, self.option3, self.option4] + options = [option.GetValue() for option in controls if option.GetValue() != ""] + return options + + def validate_data(self, *args, **kwargs): + options = self.get_options() + if len(options) < 2: + return wx.MessageDialog(self, _("Please make sure you have provided at least two options for the poll."), _("Not enough information"), wx.ICON_ERROR).ShowModal() + self.EndModal(wx.ID_OK) \ No newline at end of file