mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2026-01-15 14:33:18 +01:00
Merge branch 'next-gen' of github.com:mcv-software/twblue into next-gen
This commit is contained in:
@@ -280,6 +280,12 @@ class BaseBuffer(base.Buffer):
|
||||
return
|
||||
menu = menus.base()
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
|
||||
# Enable/disable edit based on whether the post belongs to the user
|
||||
item = self.get_item()
|
||||
if item and item.account.id == self.session.db["user_id"] and item.reblog == None:
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.edit_status, menuitem=menu.edit)
|
||||
else:
|
||||
menu.edit.Enable(False)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
||||
if self.can_share() == True:
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)
|
||||
@@ -501,6 +507,49 @@ class BaseBuffer(base.Buffer):
|
||||
log.exception("")
|
||||
self.session.db[self.name] = items
|
||||
|
||||
def edit_status(self, event=None, item=None, *args, **kwargs):
|
||||
if item == None:
|
||||
item = self.get_item()
|
||||
# Check if the post belongs to the current user
|
||||
if item.account.id != self.session.db["user_id"] or item.reblog != None:
|
||||
output.speak(_("You can only edit your own posts."))
|
||||
return
|
||||
# Check if post has a poll with votes - warn user before proceeding
|
||||
if hasattr(item, 'poll') and item.poll is not None:
|
||||
votes_count = item.poll.votes_count if hasattr(item.poll, 'votes_count') else 0
|
||||
if votes_count > 0:
|
||||
# Show confirmation dialog
|
||||
warning_title = _("Warning: Poll with votes")
|
||||
warning_message = _("This post contains a poll with {votes} votes.\n\n"
|
||||
"According to Mastodon's API, editing this post will reset ALL votes to zero, "
|
||||
"even if you don't modify the poll itself.\n\n"
|
||||
"Do you want to continue editing?").format(votes=votes_count)
|
||||
dialog = wx.MessageDialog(self.buffer, warning_message, warning_title,
|
||||
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
|
||||
result = dialog.ShowModal()
|
||||
dialog.Destroy()
|
||||
if result != wx.ID_YES:
|
||||
output.speak(_("Edit cancelled"))
|
||||
return
|
||||
# Log item info for debugging
|
||||
log.debug("Editing status: id={}, has_media_attachments={}, media_count={}".format(
|
||||
item.id,
|
||||
hasattr(item, 'media_attachments'),
|
||||
len(item.media_attachments) if hasattr(item, 'media_attachments') else 0
|
||||
))
|
||||
# Create edit dialog with existing post data
|
||||
title = _("Edit post")
|
||||
caption = _("Edit your post here")
|
||||
post = messages.editPost(session=self.session, item=item, title=title, caption=caption)
|
||||
response = post.message.ShowModal()
|
||||
if response == wx.ID_OK:
|
||||
post_data = post.get_data()
|
||||
# Call edit_post method in session
|
||||
# Note: visibility and language cannot be changed when editing per Mastodon API
|
||||
call_threaded(self.session.edit_post, post_id=post.post_id, posts=post_data)
|
||||
if hasattr(post.message, "destroy"):
|
||||
post.message.destroy()
|
||||
|
||||
def user_details(self):
|
||||
item = self.get_item()
|
||||
pass
|
||||
|
||||
@@ -161,6 +161,13 @@ class NotificationsBuffer(BaseBuffer):
|
||||
menu = menus.notification(notification.type)
|
||||
if self.is_post():
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.reply, menuitem=menu.reply)
|
||||
# Enable/disable edit based on whether the post belongs to the user
|
||||
if hasattr(menu, 'edit'):
|
||||
status = self.get_post()
|
||||
if status and status.account.id == self.session.db["user_id"] and status.reblog == None:
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.edit_status, menuitem=menu.edit)
|
||||
else:
|
||||
menu.edit.Enable(False)
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.user_actions, menuitem=menu.userActions)
|
||||
if self.can_share() == True:
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.share_item, menuitem=menu.boost)
|
||||
|
||||
@@ -449,6 +449,15 @@ class Controller(object):
|
||||
buffer = self.search_buffer(buffer.name, buffer.account)
|
||||
buffer.destroy_status()
|
||||
|
||||
def edit_post(self, *args, **kwargs):
|
||||
""" Edits a post in the current buffer.
|
||||
Users can only edit their own posts."""
|
||||
buffer = self.view.get_current_buffer()
|
||||
if hasattr(buffer, "account"):
|
||||
buffer = self.search_buffer(buffer.name, buffer.account)
|
||||
if hasattr(buffer, "edit_status"):
|
||||
buffer.edit_status()
|
||||
|
||||
def exit(self, *args, **kwargs):
|
||||
if config.app["app-settings"]["ask_at_exit"] == True:
|
||||
answer = commonMessageDialogs.exit_dialog(self.view)
|
||||
|
||||
@@ -48,7 +48,7 @@ class Handler(object):
|
||||
addAlias=_("Add a&lias"),
|
||||
addToList=None,
|
||||
removeFromList=None,
|
||||
details=_("Show user profile"),
|
||||
details=_("S&how user profile"),
|
||||
favs=None,
|
||||
# In buffer Menu.
|
||||
community_timeline =_("Create c&ommunity timeline"),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import os
|
||||
import re
|
||||
import wx
|
||||
import logging
|
||||
import widgetUtils
|
||||
import config
|
||||
import output
|
||||
@@ -14,6 +15,8 @@ from wxUI.dialogs.mastodon import postDialogs
|
||||
from extra.autocompletionUsers import completion
|
||||
from . import userList
|
||||
|
||||
log = logging.getLogger("controller.mastodon.messages")
|
||||
|
||||
def character_count(post_text, post_cw, character_limit=500):
|
||||
# We will use text for counting character limit only.
|
||||
full_text = post_text+post_cw
|
||||
@@ -262,6 +265,108 @@ class post(messages.basicMessage):
|
||||
visibility_setting = visibility_settings.index(setting)
|
||||
self.message.visibility.SetSelection(setting)
|
||||
|
||||
class editPost(post):
|
||||
def __init__(self, session, item, title, caption, *args, **kwargs):
|
||||
""" Initialize edit dialog with existing post data.
|
||||
|
||||
Note: Per Mastodon API, visibility and language cannot be changed when editing.
|
||||
These fields will be displayed but disabled in the UI.
|
||||
"""
|
||||
# Extract text from post
|
||||
if item.reblog != None:
|
||||
item = item.reblog
|
||||
text = item.content
|
||||
# Remove HTML tags from content
|
||||
import re
|
||||
text = re.sub('<[^<]+?>', '', text)
|
||||
# Initialize parent class
|
||||
super(editPost, self).__init__(session, title, caption, text=text, *args, **kwargs)
|
||||
# Store the post ID for editing
|
||||
self.post_id = item.id
|
||||
# Set visibility (read-only, cannot be changed)
|
||||
visibility_settings = dict(public=0, unlisted=1, private=2, direct=3)
|
||||
self.message.visibility.SetSelection(visibility_settings.get(item.visibility, 0))
|
||||
self.message.visibility.Enable(False) # Disable as it cannot be edited
|
||||
# Set language (read-only, cannot be changed)
|
||||
if item.language:
|
||||
self.set_language(item.language)
|
||||
self.message.language.Enable(False) # Disable as it cannot be edited
|
||||
# Set sensitive content and spoiler
|
||||
if item.sensitive:
|
||||
self.message.sensitive.SetValue(True)
|
||||
if item.spoiler_text:
|
||||
self.message.spoiler.ChangeValue(item.spoiler_text)
|
||||
self.message.on_sensitivity_changed()
|
||||
# Load existing poll (if any)
|
||||
# Note: You cannot have both media and a poll, so check poll first
|
||||
if hasattr(item, 'poll') and item.poll is not None:
|
||||
log.debug("Loading existing poll for post {}".format(self.post_id))
|
||||
poll = item.poll
|
||||
# Extract poll options (just the text, not the votes)
|
||||
poll_options = [option.title for option in poll.options]
|
||||
# Calculate expires_in based on current time and expires_at
|
||||
# For editing, we need to provide a new expiration time
|
||||
# Since we can't get the original expires_in, use a default or let user configure
|
||||
# For now, use 1 day (86400 seconds) as default
|
||||
expires_in = 86400
|
||||
if hasattr(poll, 'expires_at') and poll.expires_at and not poll.expired:
|
||||
# Calculate remaining time if poll hasn't expired
|
||||
from dateutil import parser as date_parser
|
||||
import datetime
|
||||
try:
|
||||
expires_at = poll.expires_at
|
||||
if isinstance(expires_at, str):
|
||||
expires_at = date_parser.parse(expires_at)
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
remaining = (expires_at - now).total_seconds()
|
||||
if remaining > 0:
|
||||
expires_in = int(remaining)
|
||||
except Exception as e:
|
||||
log.warning("Could not calculate poll expiration: {}".format(e))
|
||||
|
||||
poll_info = {
|
||||
"type": "poll",
|
||||
"file": "",
|
||||
"description": _("Poll with {} options").format(len(poll_options)),
|
||||
"options": poll_options,
|
||||
"expires_in": expires_in,
|
||||
"multiple": poll.multiple if hasattr(poll, 'multiple') else False,
|
||||
"hide_totals": poll.voters_count == 0 if hasattr(poll, 'voters_count') else False
|
||||
}
|
||||
self.attachments.append(poll_info)
|
||||
self.message.add_item(item=[poll_info["file"], poll_info["type"], poll_info["description"]])
|
||||
log.debug("Loaded poll with {} options. WARNING: Editing will reset all votes!".format(len(poll_options)))
|
||||
# Load existing media attachments (only if no poll)
|
||||
elif hasattr(item, 'media_attachments'):
|
||||
log.debug("Loading existing media attachments for post {}".format(self.post_id))
|
||||
log.debug("Item has media_attachments attribute, count: {}".format(len(item.media_attachments)))
|
||||
if len(item.media_attachments) > 0:
|
||||
for media in item.media_attachments:
|
||||
log.debug("Processing media: id={}, type={}, url={}".format(media.id, media.type, media.url))
|
||||
media_info = {
|
||||
"id": media.id, # Keep the existing media ID
|
||||
"type": media.type,
|
||||
"file": media.url, # URL of existing media
|
||||
"description": media.description or ""
|
||||
}
|
||||
# Include focus point if available
|
||||
if hasattr(media, 'meta') and media.meta and 'focus' in media.meta:
|
||||
focus = media.meta['focus']
|
||||
media_info["focus"] = (focus.get('x'), focus.get('y'))
|
||||
log.debug("Added focus point: {}".format(media_info["focus"]))
|
||||
self.attachments.append(media_info)
|
||||
# Display in the attachment list
|
||||
display_name = media.url.split('/')[-1]
|
||||
log.debug("Adding item to UI: name={}, type={}, desc={}".format(display_name, media.type, media.description or ""))
|
||||
self.message.add_item(item=[display_name, media.type, media.description or ""])
|
||||
log.debug("Total attachments loaded: {}".format(len(self.attachments)))
|
||||
else:
|
||||
log.debug("media_attachments list is empty")
|
||||
else:
|
||||
log.debug("Item has no poll or media attachments")
|
||||
# Update text processor to reflect the loaded content
|
||||
self.text_processor()
|
||||
|
||||
class viewPost(post):
|
||||
def __init__(self, session, post, offset_hours=0, date="", item_url=""):
|
||||
self.session = session
|
||||
|
||||
@@ -13,8 +13,8 @@ class autocompletionManageDialog(widgetUtils.BaseDialog):
|
||||
self.users = widgets.list(panel, _(u"Username"), _(u"Name"), style=wx.LC_REPORT)
|
||||
sizer.Add(label, 0, wx.ALL, 5)
|
||||
sizer.Add(self.users.list, 0, wx.ALL, 5)
|
||||
self.add = wx.Button(panel, -1, _(u"Add user"))
|
||||
self.remove = wx.Button(panel, -1, _(u"Remove user"))
|
||||
self.add = wx.Button(panel, -1, _(u"&Add user"))
|
||||
self.remove = wx.Button(panel, -1, _(u"&Remove user"))
|
||||
optionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
optionsBox.Add(self.add, 0, wx.ALL, 5)
|
||||
optionsBox.Add(self.remove, 0, wx.ALL, 5)
|
||||
|
||||
@@ -23,6 +23,7 @@ url = string(default="control+win+b")
|
||||
go_home = string(default="control+win+home")
|
||||
go_end = string(default="control+win+end")
|
||||
delete = string(default="control+win+delete")
|
||||
edit_post = string(default="")
|
||||
clear_buffer = string(default="control+win+shift+delete")
|
||||
repeat_item = string(default="control+win+space")
|
||||
copy_to_clipboard = string(default="control+win+shift+c")
|
||||
|
||||
@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
|
||||
go_page_down = string(default="control+win+pagedown")
|
||||
update_profile = string(default="control+win+shift+p")
|
||||
delete = string(default="control+win+delete")
|
||||
edit_post = string(default="")
|
||||
clear_buffer = string(default="control+win+shift+delete")
|
||||
repeat_item = string(default="control+win+space")
|
||||
copy_to_clipboard = string(default="control+win+shift+c")
|
||||
|
||||
@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
|
||||
go_page_down = string(default="control+win+pagedown")
|
||||
update_profile = string(default="alt+win+p")
|
||||
delete = string(default="alt+win+delete")
|
||||
edit_post = string(default="")
|
||||
clear_buffer = string(default="alt+win+shift+delete")
|
||||
repeat_item = string(default="alt+win+space")
|
||||
copy_to_clipboard = string(default="alt+win+shift+c")
|
||||
|
||||
@@ -33,6 +33,7 @@ go_page_up = string(default="control+win+pageup")
|
||||
go_page_down = string(default="control+win+pagedown")
|
||||
update_profile = string(default="alt+win+p")
|
||||
delete = string(default="alt+win+delete")
|
||||
edit_post = string(default="")
|
||||
clear_buffer = string(default="alt+win+shift+delete")
|
||||
repeat_item = string(default="control+alt+win+space")
|
||||
copy_to_clipboard = string(default="alt+win+shift+c")
|
||||
|
||||
@@ -34,6 +34,7 @@ go_page_up = string(default="control+win+pageup")
|
||||
go_page_down = string(default="control+win+pagedown")
|
||||
update_profile = string(default="alt+win+p")
|
||||
delete = string(default="control+win+delete")
|
||||
edit_post = string(default="")
|
||||
clear_buffer = string(default="control+win+shift+delete")
|
||||
repeat_item = string(default="control+win+space")
|
||||
copy_to_clipboard = string(default="control+win+shift+c")
|
||||
|
||||
@@ -17,6 +17,11 @@ def compose_post(post, db, settings, relative_times, show_screen_names, safe=Tru
|
||||
text = _("Boosted from @{}: {}").format(post.reblog.account.acct, templates.process_text(post.reblog, safe=safe))
|
||||
else:
|
||||
text = templates.process_text(post, safe=safe)
|
||||
# Handle quoted posts
|
||||
if hasattr(post, 'quote') and post.quote != None and hasattr(post.quote, 'quoted_status') and post.quote.quoted_status != None:
|
||||
quoted_user = post.quote.quoted_status.account.acct
|
||||
quoted_text = templates.process_text(post.quote.quoted_status, safe=safe)
|
||||
text = text + " " + _("Quoting @{}: {}").format(quoted_user, quoted_text)
|
||||
filtered = utils.evaluate_filters(post=post, current_context="home")
|
||||
if filtered != None:
|
||||
text = _("hidden by filter {}").format(filtered)
|
||||
|
||||
@@ -248,6 +248,106 @@ class Session(base.baseSession):
|
||||
pub.sendMessage("mastodon.error_post", name=self.get_name(), reply_to=reply_to, visibility=visibility, posts=posts, lang=language)
|
||||
return
|
||||
|
||||
def edit_post(self, post_id, posts=[]):
|
||||
""" Convenience function to edit a post. Only the first item in posts list is used as threads cannot be edited.
|
||||
|
||||
Note: According to Mastodon API, not all fields can be edited. Visibility, language, and reply context cannot be changed.
|
||||
|
||||
Args:
|
||||
post_id: ID of the status to edit
|
||||
posts: List with post data. Only first item is used.
|
||||
|
||||
Returns:
|
||||
Updated status object or None on failure
|
||||
"""
|
||||
if len(posts) == 0:
|
||||
log.warning("edit_post called with empty posts list")
|
||||
return None
|
||||
|
||||
obj = posts[0]
|
||||
text = obj.get("text")
|
||||
|
||||
if not text:
|
||||
log.warning("edit_post called without text content")
|
||||
return None
|
||||
|
||||
media_ids = []
|
||||
media_attributes = []
|
||||
|
||||
try:
|
||||
poll = None
|
||||
# Handle poll attachments
|
||||
if len(obj["attachments"]) == 1 and obj["attachments"][0]["type"] == "poll":
|
||||
poll = self.api.make_poll(
|
||||
options=obj["attachments"][0]["options"],
|
||||
expires_in=obj["attachments"][0]["expires_in"],
|
||||
multiple=obj["attachments"][0]["multiple"],
|
||||
hide_totals=obj["attachments"][0]["hide_totals"]
|
||||
)
|
||||
log.debug("Editing post with poll (this will reset votes)")
|
||||
# Handle media attachments
|
||||
elif len(obj["attachments"]) > 0:
|
||||
for i in obj["attachments"]:
|
||||
# If attachment has an 'id', it's an existing media that we keep
|
||||
if "id" in i:
|
||||
media_ids.append(i["id"])
|
||||
# If existing media has metadata to update, use generate_media_edit_attributes
|
||||
if "description" in i or "focus" in i:
|
||||
media_attr = self.api.generate_media_edit_attributes(
|
||||
id=i["id"],
|
||||
description=i.get("description"),
|
||||
focus=i.get("focus")
|
||||
)
|
||||
media_attributes.append(media_attr)
|
||||
# Otherwise it's a new file to upload
|
||||
elif "file" in i:
|
||||
description = i.get("description", "")
|
||||
focus = i.get("focus", None)
|
||||
media = self.api_call(
|
||||
"media_post",
|
||||
media_file=i["file"],
|
||||
description=description,
|
||||
focus=focus,
|
||||
synchronous=True
|
||||
)
|
||||
media_ids.append(media.id)
|
||||
log.debug("Uploaded new media with id: {}".format(media.id))
|
||||
|
||||
# Prepare parameters for status_update
|
||||
update_params = {
|
||||
"id": post_id,
|
||||
"status": text,
|
||||
"_sound": "tweet_send.ogg",
|
||||
"sensitive": obj.get("sensitive", False),
|
||||
"spoiler_text": obj.get("spoiler_text", None),
|
||||
}
|
||||
|
||||
# Add optional parameters only if provided
|
||||
if media_ids:
|
||||
update_params["media_ids"] = media_ids
|
||||
if media_attributes:
|
||||
update_params["media_attributes"] = media_attributes
|
||||
if poll:
|
||||
update_params["poll"] = poll
|
||||
|
||||
# Call status_update API
|
||||
log.debug("Editing post {} with params: {}".format(post_id, {k: v for k, v in update_params.items() if k not in ["_sound"]}))
|
||||
item = self.api_call(call_name="status_update", **update_params)
|
||||
|
||||
if item:
|
||||
log.info("Successfully edited post {}".format(post_id))
|
||||
return item
|
||||
|
||||
except MastodonAPIError as e:
|
||||
log.exception("Mastodon API error updating post {}: {}".format(post_id, str(e)))
|
||||
output.speak(_("Error editing post: {}").format(str(e)))
|
||||
pub.sendMessage("mastodon.error_edit", name=self.get_name(), post_id=post_id, error=str(e))
|
||||
return None
|
||||
except Exception as e:
|
||||
log.exception("Unexpected error updating post {}: {}".format(post_id, str(e)))
|
||||
output.speak(_("Error editing post: {}").format(str(e)))
|
||||
return None
|
||||
|
||||
def get_name(self):
|
||||
instance = self.settings["mastodon"]["instance"]
|
||||
instance = instance.replace("https://", "")
|
||||
|
||||
@@ -76,6 +76,13 @@ def render_post(post, template, settings, relative_times=False, offset_hours=0):
|
||||
else:
|
||||
text = process_text(post, safe=False)
|
||||
safe_text = process_text(post)
|
||||
# Handle quoted posts
|
||||
if hasattr(post, 'quote') and post.quote != None and hasattr(post.quote, 'quoted_status') and post.quote.quoted_status != None:
|
||||
quoted_user = post.quote.quoted_status.account.acct
|
||||
quoted_text = process_text(post.quote.quoted_status, safe=False)
|
||||
quoted_safe_text = process_text(post.quote.quoted_status, safe=True)
|
||||
text = text + " " + _("Quoting @{}: {}").format(quoted_user, quoted_text)
|
||||
safe_text = safe_text + " " + _("Quoting @{}: {}").format(quoted_user, quoted_safe_text)
|
||||
filtered = utils.evaluate_filters(post=post, current_context="home")
|
||||
if filtered != None:
|
||||
text = _("hidden by filter {}").format(filtered)
|
||||
|
||||
@@ -3,23 +3,47 @@ import demoji
|
||||
from html.parser import HTMLParser
|
||||
from datetime import datetime, timezone
|
||||
|
||||
url_re = re.compile('<a\s*href=[\'|"](.*?)[\'"].*?>')
|
||||
url_re = re.compile(r'<a\s*href=[\'|"](.*?)[\'"].*?>')
|
||||
|
||||
class HTMLFilter(HTMLParser):
|
||||
# Classes to ignore when parsing HTML
|
||||
IGNORED_CLASSES = ["quote-inline"]
|
||||
|
||||
text = ""
|
||||
first_paragraph = True
|
||||
skip_depth = 0 # Track nesting depth of ignored elements
|
||||
|
||||
def handle_data(self, data):
|
||||
self.text += data
|
||||
# Only add data if we're not inside an ignored element
|
||||
if self.skip_depth == 0:
|
||||
self.text += data
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == "br":
|
||||
self.text = self.text+"\n"
|
||||
elif tag == "p":
|
||||
if self.first_paragraph:
|
||||
self.first_paragraph = False
|
||||
else:
|
||||
self.text = self.text+"\n\n"
|
||||
# Check if this tag has a class that should be ignored
|
||||
attrs_dict = dict(attrs)
|
||||
tag_class = attrs_dict.get("class", "")
|
||||
|
||||
# Check if any ignored class is present in this tag
|
||||
should_skip = any(ignored_class in tag_class for ignored_class in self.IGNORED_CLASSES)
|
||||
|
||||
if should_skip:
|
||||
self.skip_depth += 1
|
||||
elif self.skip_depth == 0: # Only process tags if we're not skipping
|
||||
if tag == "br":
|
||||
self.text = self.text+"\n"
|
||||
elif tag == "p":
|
||||
if self.first_paragraph:
|
||||
self.first_paragraph = False
|
||||
else:
|
||||
self.text = self.text+"\n\n"
|
||||
else:
|
||||
# We're inside a skipped element, increment depth for nested tags
|
||||
self.skip_depth += 1
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
# Decrement skip depth when closing any tag while skipping
|
||||
if self.skip_depth > 0:
|
||||
self.skip_depth -= 1
|
||||
|
||||
def html_filter(data):
|
||||
f = HTMLFilter()
|
||||
|
||||
@@ -8,6 +8,8 @@ class base(wx.Menu):
|
||||
self.Append(self.boost)
|
||||
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
||||
self.Append(self.reply)
|
||||
self.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit"))
|
||||
self.Append(self.edit)
|
||||
self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites"))
|
||||
self.Append(self.fav)
|
||||
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))
|
||||
@@ -36,6 +38,8 @@ class notification(wx.Menu):
|
||||
self.Append(self.boost)
|
||||
self.reply = wx.MenuItem(self, wx.ID_ANY, _(u"Re&ply"))
|
||||
self.Append(self.reply)
|
||||
self.edit = wx.MenuItem(self, wx.ID_ANY, _(u"&Edit"))
|
||||
self.Append(self.edit)
|
||||
self.fav = wx.MenuItem(self, wx.ID_ANY, _(u"&Add to favorites"))
|
||||
self.Append(self.fav)
|
||||
self.unfav = wx.MenuItem(self, wx.ID_ANY, _(u"R&emove from favorites"))
|
||||
|
||||
@@ -141,7 +141,7 @@ class ShowUserProfile(wx.Dialog):
|
||||
mainSizer.Add(privateSizer, 0, wx.ALL | wx.CENTER)
|
||||
|
||||
botSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
botLabel = wx.StaticText(self.panel, label=_("&Bot account: "))
|
||||
botLabel = wx.StaticText(self.panel, label=_("B&ot account: "))
|
||||
botText = self.createTextCtrl(bullSwitch[user.bot], (30, 30))
|
||||
botSizer.Add(botLabel, wx.SizerFlags().Center())
|
||||
botSizer.Add(botText, wx.SizerFlags().Center())
|
||||
@@ -154,7 +154,7 @@ class ShowUserProfile(wx.Dialog):
|
||||
discoverSizer.Add(discoverText, wx.SizerFlags().Center())
|
||||
mainSizer.Add(discoverSizer, 0, wx.ALL | wx.CENTER)
|
||||
|
||||
posts = wx.Button(self.panel, label=_("{} p&osts. Click to open posts timeline").format(user.statuses_count))
|
||||
posts = wx.Button(self.panel, label=_("{} pos&ts. Click to open posts timeline").format(user.statuses_count))
|
||||
# posts.SetToolTip(_("Click to open {}'s posts").format(user.display_name))
|
||||
posts.Bind(wx.EVT_BUTTON, self.onPost)
|
||||
mainSizer.Add(posts, wx.SizerFlags().Center())
|
||||
|
||||
@@ -119,7 +119,7 @@ class UpdateProfileDialog(wx.Dialog):
|
||||
|
||||
self.locked = wx.CheckBox(panel, label=_("&Private account"))
|
||||
self.locked.SetValue(locked)
|
||||
self.bot = wx.CheckBox(panel, label=_("&Bot account"))
|
||||
self.bot = wx.CheckBox(panel, label=_("B&ot account"))
|
||||
self.bot.SetValue(bot)
|
||||
self.discoverable = wx.CheckBox(panel, label=_("&Discoverable account"))
|
||||
self.discoverable.SetValue(discoverable)
|
||||
|
||||
@@ -19,7 +19,7 @@ class mainFrame(wx.Frame):
|
||||
self.menuitem_search = self.menubar_application.Append(wx.ID_ANY, _(u"&Search"))
|
||||
self.lists = self.menubar_application.Append(wx.ID_ANY, _(u"&Lists manager"))
|
||||
self.lists.Enable(False)
|
||||
self.manageAliases = self.menubar_application.Append(wx.ID_ANY, _("Manage user aliases"))
|
||||
self.manageAliases = self.menubar_application.Append(wx.ID_ANY, _("M&anage user aliases"))
|
||||
self.keystroke_editor = self.menubar_application.Append(wx.ID_ANY, _(u"&Edit keystrokes"))
|
||||
self.account_settings = self.menubar_application.Append(wx.ID_ANY, _(u"Account se&ttings"))
|
||||
self.prefs = self.menubar_application.Append(wx.ID_PREFERENCES, _(u"&Global settings"))
|
||||
@@ -56,7 +56,7 @@ class mainFrame(wx.Frame):
|
||||
self.trends = self.menubar_buffer.Append(wx.ID_ANY, _(u"New &trending topics buffer..."))
|
||||
self.filter = self.menubar_buffer.Append(wx.ID_ANY, _(u"Create a &filter"))
|
||||
self.manage_filters = self.menubar_buffer.Append(wx.ID_ANY, _(u"&Manage filters"))
|
||||
self.find = self.menubar_buffer.Append(wx.ID_ANY, _(u"Find a string in the currently focused buffer..."))
|
||||
self.find = self.menubar_buffer.Append(wx.ID_ANY, _(u"F&ind a string in the currently focused buffer..."))
|
||||
self.load_previous_items = self.menubar_buffer.Append(wx.ID_ANY, _(u"&Load previous items"))
|
||||
self.menubar_buffer.AppendSeparator()
|
||||
self.mute_buffer = self.menubar_buffer.AppendCheckItem(wx.ID_ANY, _(u"&Mute"))
|
||||
@@ -66,8 +66,8 @@ class mainFrame(wx.Frame):
|
||||
|
||||
# audio menu
|
||||
self.menubar_audio = wx.Menu()
|
||||
self.seekLeft = self.menubar_audio.Append(wx.ID_ANY, _(u"&Seek back 5 seconds"))
|
||||
self.seekRight = self.menubar_audio.Append(wx.ID_ANY, _(u"&Seek forward 5 seconds"))
|
||||
self.seekLeft = self.menubar_audio.Append(wx.ID_ANY, _(u"Seek &back 5 seconds"))
|
||||
self.seekRight = self.menubar_audio.Append(wx.ID_ANY, _(u"Seek &forward 5 seconds"))
|
||||
|
||||
# Help Menu
|
||||
self.menubar_help = wx.Menu()
|
||||
|
||||
Reference in New Issue
Block a user