Rework emoticon menu
This commit is contained in:
parent
0d0671374a
commit
79716f421f
12 changed files with 372 additions and 229 deletions
|
@ -1,7 +1,7 @@
|
|||
emoticonsdir = $(pkgdatadir)/data/emoticons
|
||||
nobase_dist_emoticons_DATA = \
|
||||
$(srcdir)/*/*.png \
|
||||
$(srcdir)/*/*.gif \
|
||||
$(srcdir)/*/emoticons.py
|
||||
$(srcdir)/*/LICENSE \
|
||||
$(srcdir)/*/emoticons_theme.py
|
||||
|
||||
MAINTAINERCLEANFILES = Makefile.in
|
||||
|
|
|
@ -664,23 +664,17 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="emoticons_button">
|
||||
<object class="GtkMenuButton" id="emoticons_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="has_tooltip">True</property>
|
||||
<property name="tooltip_markup" translatable="yes">Show a list of emoticons (Alt+M)</property>
|
||||
<property name="tooltip_text" translatable="yes">Show a list of emoticons (Alt+M)</property>
|
||||
<property name="relief">none</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="emoticons_button_image">
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="stock">gtk-missing-image</property>
|
||||
<property name="icon_size">1</property>
|
||||
<property name="icon_name">face-smile</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -224,20 +224,17 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="emoticons_button">
|
||||
<object class="GtkMenuButton" id="emoticons_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="has_tooltip">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Show a list of emoticons (Alt+M)</property>
|
||||
<property name="relief">none</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="emoticons_button_image">
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-missing-image</property>
|
||||
<property name="icon_size">1</property>
|
||||
<property name="icon_name">face-smile</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -4,4 +4,11 @@
|
|||
#ChatControl-AuthenticationButton { padding-top: 0px; padding-bottom: 0px}
|
||||
|
||||
/* VCardWindow */
|
||||
.VCard-GtkLinkButton { padding-left: 5px; border-left: none; }
|
||||
.VCard-GtkLinkButton { padding-left: 5px; border-left: none; }
|
||||
|
||||
popover#EmoticonPopover button { background: none; border: none; box-shadow:none; padding: 0px;}
|
||||
popover#EmoticonPopover button > label { font-size: 24px; }
|
||||
popover#EmoticonPopover flowboxchild > label { font-size: 24px; }
|
||||
popover#EmoticonPopover notebook label { font-size: 24px; }
|
||||
popover#EmoticonPopover flowbox { padding-left: 5px; padding-right: 6px; }
|
||||
popover#EmoticonPopover flowboxchild { padding-top: 5px; padding-bottom: 5px; }
|
||||
|
|
|
@ -102,6 +102,8 @@ class ChatControl(ChatControlBase):
|
|||
self.handlers[id_] = self.actions_button
|
||||
|
||||
self._formattings_button = self.xml.get_object('formattings_button')
|
||||
self.emoticons_button = self.xml.get_object('emoticons_button')
|
||||
self.toggle_emoticons()
|
||||
|
||||
self._add_to_roster_button = self.xml.get_object(
|
||||
'add_to_roster_button')
|
||||
|
@ -325,8 +327,6 @@ class ChatControl(ChatControlBase):
|
|||
if (gajim.connections[self.account].connected > 1 and not \
|
||||
self.TYPE_ID == 'pm') or (self.contact.show != 'offline' and \
|
||||
self.TYPE_ID == 'pm'):
|
||||
emoticons_button = self.xml.get_object('emoticons_button')
|
||||
emoticons_button.set_sensitive(True)
|
||||
send_button = self.xml.get_object('send_button')
|
||||
send_button.set_sensitive(True)
|
||||
# Formatting
|
||||
|
@ -1663,15 +1663,11 @@ class ChatControl(ChatControlBase):
|
|||
if contact:
|
||||
self.contact = contact
|
||||
self.draw_banner()
|
||||
emoticons_button = self.xml.get_object('emoticons_button')
|
||||
emoticons_button.set_sensitive(True)
|
||||
send_button = self.xml.get_object('send_button')
|
||||
send_button.set_sensitive(True)
|
||||
|
||||
def got_disconnected(self):
|
||||
# Emoticons button
|
||||
emoticons_button = self.xml.get_object('emoticons_button')
|
||||
emoticons_button.set_sensitive(False)
|
||||
send_button = self.xml.get_object('send_button')
|
||||
send_button.set_sensitive(False)
|
||||
# Add to roster
|
||||
|
|
|
@ -43,6 +43,7 @@ import history_window
|
|||
import notify
|
||||
import re
|
||||
|
||||
import emoticons
|
||||
from common import events
|
||||
from common import gajim
|
||||
from common import helpers
|
||||
|
@ -263,12 +264,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
id_ = widget.connect('clicked', self._on_history_menuitem_activate)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
# when/if we do XHTML we will put formatting buttons back
|
||||
widget = self.xml.get_object('emoticons_button')
|
||||
widget.set_sensitive(False)
|
||||
id_ = widget.connect('clicked', self.on_emoticons_button_clicked)
|
||||
self.handlers[id_] = widget
|
||||
|
||||
# Create banner and connect signals
|
||||
widget = self.xml.get_object('banner_eventbox')
|
||||
id_ = widget.connect('button-press-event',
|
||||
|
@ -364,13 +359,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
self.received_history_pos = 0
|
||||
self.orig_msg = None
|
||||
|
||||
# Emoticons menu
|
||||
# set image no matter if user wants at this time emoticons or not
|
||||
# (so toggle works ok)
|
||||
img = self.xml.get_object('emoticons_button_image')
|
||||
img.set_from_file(os.path.join(gajim.DATA_DIR, 'emoticons', 'static',
|
||||
'smile.png'))
|
||||
self.toggle_emoticons()
|
||||
self.set_emoticon_popover()
|
||||
|
||||
# Attach speller
|
||||
if gajim.config.get('use_speller') and HAS_GTK_SPELL:
|
||||
|
@ -568,6 +557,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
When send button is pressed: send the current message
|
||||
"""
|
||||
message_buffer = self.msg_textview.get_buffer()
|
||||
emoticons.replace_with_codepoint(message_buffer)
|
||||
start_iter = message_buffer.get_start_iter()
|
||||
end_iter = message_buffer.get_end_iter()
|
||||
message = message_buffer.get_text(start_iter, end_iter, False)
|
||||
|
@ -588,12 +578,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
self.parent_win.notebook.event(event)
|
||||
return True
|
||||
|
||||
def show_emoticons_menu(self):
|
||||
if not gajim.config.get('emoticons_theme'):
|
||||
return
|
||||
gajim.interface.emoticon_menuitem_clicked = self.append_emoticon
|
||||
gajim.interface.emoticons_menu.popup(None, None, None, None, 1, 0)
|
||||
|
||||
def _on_message_textview_key_press_event(self, widget, event):
|
||||
if event.keyval == Gdk.KEY_space:
|
||||
self.space_pressed = True
|
||||
|
@ -683,6 +667,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
event.keyval == Gdk.KEY_KP_Enter: # ENTER
|
||||
message_textview = widget
|
||||
message_buffer = message_textview.get_buffer()
|
||||
emoticons.replace_with_codepoint(message_buffer)
|
||||
start_iter, end_iter = message_buffer.get_bounds()
|
||||
message = message_buffer.get_text(start_iter, end_iter, False)
|
||||
xhtml = self.msg_textview.get_xhtml()
|
||||
|
@ -1012,31 +997,26 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
|
||||
def toggle_emoticons(self):
|
||||
"""
|
||||
Hide show emoticons_button and make sure emoticons_menu is always there
|
||||
when needed
|
||||
Hide show emoticons_button
|
||||
"""
|
||||
emoticons_button = self.xml.get_object('emoticons_button')
|
||||
if gajim.config.get('emoticons_theme'):
|
||||
emoticons_button.show()
|
||||
emoticons_button.set_no_show_all(False)
|
||||
self.emoticons_button.set_no_show_all(False)
|
||||
self.emoticons_button.show()
|
||||
else:
|
||||
emoticons_button.hide()
|
||||
emoticons_button.set_no_show_all(True)
|
||||
self.emoticons_button.set_no_show_all(True)
|
||||
self.emoticons_button.hide()
|
||||
|
||||
def append_emoticon(self, str_):
|
||||
buffer_ = self.msg_textview.get_buffer()
|
||||
if buffer_.get_char_count():
|
||||
buffer_.insert_at_cursor(' %s ' % str_)
|
||||
else: # we are the beginning of buffer
|
||||
buffer_.insert_at_cursor('%s ' % str_)
|
||||
self.msg_textview.grab_focus()
|
||||
def set_emoticon_popover(self):
|
||||
if not gajim.config.get('emoticons_theme'):
|
||||
return
|
||||
|
||||
def on_emoticons_button_clicked(self, widget):
|
||||
"""
|
||||
Popup emoticons menu
|
||||
"""
|
||||
gajim.interface.emoticon_menuitem_clicked = self.append_emoticon
|
||||
gajim.interface.popup_emoticons_under_button(widget, self.parent_win)
|
||||
if not self.parent_win:
|
||||
return
|
||||
|
||||
popover = emoticons.get_popover()
|
||||
popover.set_callbacks(self.msg_textview)
|
||||
emoticons_button = self.xml.get_object('emoticons_button')
|
||||
emoticons_button.set_popover(popover)
|
||||
|
||||
def on_color_menuitem_activate(self, widget):
|
||||
color_dialog = Gtk.ColorChooserDialog(None, self.parent_win.window)
|
||||
|
@ -1139,6 +1119,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
|||
|
||||
def set_control_active(self, state):
|
||||
if state:
|
||||
self.set_emoticon_popover()
|
||||
jid = self.contact.jid
|
||||
if self.was_at_the_end:
|
||||
# we are at the end
|
||||
|
|
|
@ -657,7 +657,7 @@ class PreferencesWindow:
|
|||
else:
|
||||
gajim.config.set('emoticons_theme', emot_theme)
|
||||
|
||||
gajim.interface.init_emoticons(need_reload = True)
|
||||
gajim.interface.init_emoticons()
|
||||
gajim.interface.make_regexps()
|
||||
self.toggle_emoticons()
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ from common import helpers
|
|||
from common import i18n
|
||||
from calendar import timegm
|
||||
from common.fuzzyclock import FuzzyClock
|
||||
import emoticons
|
||||
|
||||
from htmltextview import HtmlTextView
|
||||
from common.exceptions import GajimGeneralException
|
||||
|
@ -952,22 +953,17 @@ class ConversationTextview(GObject.GObject):
|
|||
end_iter = iter_
|
||||
else:
|
||||
end_iter = buffer_.get_end_iter()
|
||||
if gajim.config.get('emoticons_theme') and \
|
||||
possible_emot_ascii_caps in gajim.interface.emoticons.keys() and graphics:
|
||||
# it's an emoticon
|
||||
emot_ascii = possible_emot_ascii_caps
|
||||
anchor = buffer_.create_child_anchor(end_iter)
|
||||
img = TextViewImage(anchor,
|
||||
GLib.markup_escape_text(special_text))
|
||||
animations = gajim.interface.emoticons_animations
|
||||
if not emot_ascii in animations:
|
||||
animations[emot_ascii] = GdkPixbuf.PixbufAnimation.new_from_file(
|
||||
gajim.interface.emoticons[emot_ascii])
|
||||
img.set_from_animation(animations[emot_ascii])
|
||||
img.show()
|
||||
self.images.append(img)
|
||||
# add with possible animation
|
||||
self.tv.add_child_at_anchor(img, anchor)
|
||||
if gajim.config.get('emoticons_theme') and graphics:
|
||||
pixbuf = emoticons.get_pixbuf(possible_emot_ascii_caps)
|
||||
if pixbuf:
|
||||
# it's an emoticon
|
||||
anchor = buffer_.create_child_anchor(end_iter)
|
||||
img = TextViewImage(anchor,
|
||||
GLib.markup_escape_text(special_text))
|
||||
img.set_from_pixbuf(pixbuf)
|
||||
img.show()
|
||||
self.images.append(img)
|
||||
self.tv.add_child_at_anchor(img, anchor)
|
||||
elif special_text.startswith('www.') or \
|
||||
special_text.startswith('ftp.') or \
|
||||
text_is_valid_uri and not is_xhtml_link:
|
||||
|
|
309
gajim/emoticons.py
Normal file
309
gajim/emoticons.py
Normal file
|
@ -0,0 +1,309 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2017 Philipp Hörist <philipp AT hoerist.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import logging
|
||||
import importlib.util as imp
|
||||
from collections import OrderedDict
|
||||
|
||||
from gi.repository import GdkPixbuf, Gtk, GLib
|
||||
|
||||
MODIFIER_MAX_CHILDREN_PER_LINE = 6
|
||||
MAX_CHILDREN_PER_LINE = 10
|
||||
MIN_HEIGHT = 200
|
||||
|
||||
pixbufs = dict()
|
||||
codepoints = dict()
|
||||
popover_instance = None
|
||||
|
||||
log = logging.getLogger('gajim.emoticons')
|
||||
|
||||
class SubPixbuf:
|
||||
|
||||
height = 24
|
||||
width = 24
|
||||
columns = 20
|
||||
|
||||
def __init__(self, path):
|
||||
self.cur_column = 0
|
||||
self.src_x = 0
|
||||
self.src_y = 0
|
||||
self.atlas = GdkPixbuf.Pixbuf.new_from_file(path)
|
||||
|
||||
def get_pixbuf(self):
|
||||
self.src_x = self.cur_column * self.width
|
||||
|
||||
subpixbuf = self.atlas.new_subpixbuf(self.src_x, self.src_y, self.width, self.height)
|
||||
|
||||
if self.cur_column == self.columns - 1:
|
||||
self.src_y += self.width
|
||||
self.cur_column = 0
|
||||
else:
|
||||
self.cur_column += 1
|
||||
|
||||
return subpixbuf
|
||||
|
||||
def load(path):
|
||||
theme_path = os.path.join(path, 'emoticons_theme.py')
|
||||
spec = imp.spec_from_file_location("emoticons_theme.py", theme_path)
|
||||
try:
|
||||
theme = imp.module_from_spec(spec)
|
||||
spec.loader.exec_module(theme)
|
||||
except FileNotFoundError:
|
||||
log.exception('Emoticons theme not found')
|
||||
return
|
||||
|
||||
if not theme.use_image:
|
||||
# Use Font to display emoticons
|
||||
set_popover(theme.emoticons, False)
|
||||
return True
|
||||
|
||||
try:
|
||||
sub = SubPixbuf(os.path.join(path, 'emoticons.png'))
|
||||
except GLib.GError:
|
||||
log.exception('Error while creating subpixbuf')
|
||||
return False
|
||||
|
||||
def add_emoticon(codepoint_, sub, mod_list=None):
|
||||
pix = sub.get_pixbuf()
|
||||
for alternate in codepoint_:
|
||||
codepoints[alternate.upper()] = pix
|
||||
if pix not in pixbufs:
|
||||
pixbufs[pix] = alternate.upper()
|
||||
if mod_list is not None:
|
||||
mod_list.append(pix)
|
||||
else:
|
||||
pixbuf_list.append(pix)
|
||||
|
||||
popover_dict = OrderedDict()
|
||||
try:
|
||||
for category in theme.emoticons:
|
||||
if not theme.emoticons[category]:
|
||||
# Empty category
|
||||
continue
|
||||
|
||||
pixbuf_list = []
|
||||
for filename, codepoint_ in theme.emoticons[category]:
|
||||
if codepoint_ is None:
|
||||
# Category image
|
||||
pixbuf_list.append(sub.get_pixbuf())
|
||||
continue
|
||||
if not filename:
|
||||
# We have an emoticon with a modifier
|
||||
mod_list = []
|
||||
for _, mod_codepoint in codepoint_:
|
||||
add_emoticon(mod_codepoint, sub, mod_list)
|
||||
pixbuf_list.append(mod_list)
|
||||
else:
|
||||
add_emoticon(codepoint_, sub)
|
||||
|
||||
popover_dict[category] = pixbuf_list
|
||||
|
||||
except Exception:
|
||||
log.exception('Error while loading emoticon theme')
|
||||
return
|
||||
|
||||
set_popover(popover_dict, True)
|
||||
|
||||
return True
|
||||
|
||||
def set_popover(popover_dict, use_image):
|
||||
global popover_instance
|
||||
popover_instance = EmoticonPopover(popover_dict, use_image)
|
||||
|
||||
def get_popover():
|
||||
return popover_instance
|
||||
|
||||
def get_pixbuf(codepoint_):
|
||||
try:
|
||||
return codepoints[codepoint_]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def get_codepoint(pixbuf_):
|
||||
try:
|
||||
return pixbufs[pixbuf_]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def replace_with_codepoint(buffer_):
|
||||
if not pixbufs:
|
||||
# We use font emoticons
|
||||
return
|
||||
iter_ = buffer_.get_start_iter()
|
||||
pix = iter_.get_pixbuf()
|
||||
|
||||
def replace(pix):
|
||||
if pix:
|
||||
emote = get_codepoint(pix)
|
||||
if not emote:
|
||||
return
|
||||
iter_2 = iter_.copy()
|
||||
iter_2.forward_char()
|
||||
buffer_.delete(iter_, iter_2)
|
||||
buffer_.insert(iter_, emote)
|
||||
|
||||
replace(pix)
|
||||
while iter_.forward_char():
|
||||
pix = iter_.get_pixbuf()
|
||||
replace(pix)
|
||||
|
||||
class EmoticonPopover(Gtk.Popover):
|
||||
def __init__(self, emoji_dict, use_image):
|
||||
super().__init__()
|
||||
self.set_name('EmoticonPopover')
|
||||
self.text_widget = None
|
||||
self.use_image = use_image
|
||||
|
||||
notebook = Gtk.Notebook()
|
||||
self.add(notebook)
|
||||
self.handler_id = self.connect('key_press_event', self.on_key_press)
|
||||
|
||||
for category in emoji_dict:
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
scrolled_window.set_min_content_height(MIN_HEIGHT)
|
||||
|
||||
flowbox = Gtk.FlowBox()
|
||||
flowbox.set_max_children_per_line(MAX_CHILDREN_PER_LINE)
|
||||
flowbox.connect('child_activated', self.on_emoticon_press)
|
||||
|
||||
scrolled_window.add(flowbox)
|
||||
|
||||
# Use first entry as a label for the notebook page
|
||||
if self.use_image:
|
||||
cat_image = Gtk.Image()
|
||||
cat_image.set_from_pixbuf(emoji_dict[category][0])
|
||||
notebook.append_page(scrolled_window, cat_image)
|
||||
else:
|
||||
notebook.append_page(scrolled_window, Gtk.Label(label=emoji_dict[category][0]))
|
||||
|
||||
# Populate the category with emojis
|
||||
for pix in emoji_dict[category][1:]:
|
||||
if isinstance(pix, list):
|
||||
widget = self.add_emoticon_modifier(pix)
|
||||
else:
|
||||
if self.use_image:
|
||||
widget = Gtk.Image()
|
||||
widget.set_from_pixbuf(pix)
|
||||
else:
|
||||
widget = Gtk.Label(pix)
|
||||
flowbox.add(widget)
|
||||
|
||||
notebook.show_all()
|
||||
|
||||
def add_emoticon_modifier(self, pixbuf_list):
|
||||
button = Gtk.MenuButton()
|
||||
button.set_relief(Gtk.ReliefStyle.NONE)
|
||||
|
||||
if self.use_image:
|
||||
# We use the first item of the list as image for the button
|
||||
button.get_child().set_from_pixbuf(pixbuf_list[0])
|
||||
else:
|
||||
button.remove(button.get_child())
|
||||
label = Gtk.Label(pixbuf_list[0])
|
||||
button.add(label)
|
||||
|
||||
button.connect('button-press-event', self.on_modifier_press)
|
||||
|
||||
popover = Gtk.Popover()
|
||||
popover.set_name('EmoticonPopover')
|
||||
popover.connect('key_press_event', self.on_key_press)
|
||||
|
||||
flowbox = Gtk.FlowBox()
|
||||
flowbox.set_size_request(200, -1)
|
||||
flowbox.set_max_children_per_line(MODIFIER_MAX_CHILDREN_PER_LINE)
|
||||
flowbox.connect('child_activated', self.on_emoticon_press)
|
||||
|
||||
popover.add(flowbox)
|
||||
|
||||
for pix in pixbuf_list[1:]:
|
||||
if self.use_image:
|
||||
widget = Gtk.Image()
|
||||
widget.set_from_pixbuf(pix)
|
||||
else:
|
||||
widget = Gtk.Label(pix)
|
||||
flowbox.add(widget)
|
||||
|
||||
flowbox.show_all()
|
||||
button.set_popover(popover)
|
||||
return button
|
||||
|
||||
def set_callbacks(self, widget):
|
||||
self.text_widget = widget
|
||||
# Because the handlers getting disconnected when on_destroy() is called
|
||||
# we connect them again
|
||||
if self.handler_id:
|
||||
self.disconnect(self.handler_id)
|
||||
self.handler_id = self.connect('key_press_event', self.on_key_press)
|
||||
|
||||
def on_key_press(self, widget, event):
|
||||
self.text_widget.grab_focus()
|
||||
|
||||
def on_modifier_press(self, button, event):
|
||||
if event.button == 3:
|
||||
button.get_popover().show()
|
||||
button.get_popover().get_child().unselect_all()
|
||||
if event.button == 1:
|
||||
button.get_parent().emit('activate')
|
||||
if self.use_image:
|
||||
self.append_emoticon(button.get_child().get_pixbuf())
|
||||
else:
|
||||
self.append_emoticon(button.get_child().get_text())
|
||||
return True
|
||||
|
||||
def on_emoticon_press(self, flowbox, child):
|
||||
GLib.timeout_add(100, flowbox.unselect_all)
|
||||
|
||||
if isinstance(child.get_child(), Gtk.MenuButton):
|
||||
return
|
||||
|
||||
if self.use_image:
|
||||
self.append_emoticon(child.get_child().get_pixbuf())
|
||||
else:
|
||||
self.append_emoticon(child.get_child().get_text())
|
||||
|
||||
def append_emoticon(self, pix):
|
||||
buffer_ = self.text_widget.get_buffer()
|
||||
if buffer_.get_char_count():
|
||||
buffer_.insert_at_cursor(' ')
|
||||
insert_mark = buffer_.get_insert()
|
||||
insert_iter = buffer_.get_iter_at_mark(insert_mark)
|
||||
if self.use_image:
|
||||
buffer_.insert_pixbuf(insert_iter, pix)
|
||||
else:
|
||||
buffer_.insert(insert_iter, pix)
|
||||
buffer_.insert_at_cursor(' ')
|
||||
else: # we are the beginning of buffer
|
||||
insert_mark = buffer_.get_insert()
|
||||
insert_iter = buffer_.get_iter_at_mark(insert_mark)
|
||||
if self.use_image:
|
||||
buffer_.insert_pixbuf(insert_iter, pix)
|
||||
else:
|
||||
buffer_.insert(insert_iter, pix)
|
||||
buffer_.insert_at_cursor(' ')
|
||||
|
||||
def do_destroy(self):
|
||||
# Remove the references we hold to other objects
|
||||
self.text_widget = None
|
||||
# Even though we dont destroy the Popover, handlers are getting
|
||||
# still disconnected, which makes the handler_id invalid
|
||||
# FIXME: find out how we can prevent handlers getting disconnected
|
||||
self.handler_id = None
|
||||
# Never destroy, creating a new EmoticonPopover is expensive
|
||||
return True
|
||||
|
||||
|
|
@ -304,6 +304,9 @@ class GroupchatControl(ChatControlBase):
|
|||
self.on_actions_button_clicked)
|
||||
self.handlers[id_] = self.actions_button
|
||||
|
||||
self.emoticons_button = self.xml.get_object('emoticons_button')
|
||||
self.toggle_emoticons()
|
||||
|
||||
widget = self.xml.get_object('change_nick_button')
|
||||
widget.set_sensitive(False)
|
||||
id_ = widget.connect('clicked', self._on_change_nick_menuitem_activate)
|
||||
|
@ -1429,8 +1432,6 @@ class GroupchatControl(ChatControlBase):
|
|||
|
||||
send_button = self.xml.get_object('send_button')
|
||||
send_button.set_sensitive(True)
|
||||
emoticons_button = self.xml.get_object('emoticons_button')
|
||||
emoticons_button.set_sensitive(True)
|
||||
formattings_button = self.xml.get_object('formattings_button')
|
||||
formattings_button.set_sensitive(True)
|
||||
change_nick_button = self.xml.get_object('change_nick_button')
|
||||
|
@ -1441,8 +1442,6 @@ class GroupchatControl(ChatControlBase):
|
|||
def got_disconnected(self):
|
||||
send_button = self.xml.get_object('send_button')
|
||||
send_button.set_sensitive(False)
|
||||
emoticons_button = self.xml.get_object('emoticons_button')
|
||||
emoticons_button.set_sensitive(False)
|
||||
formattings_button = self.xml.get_object('formattings_button')
|
||||
formattings_button.set_sensitive(False)
|
||||
change_nick_button = self.xml.get_object('change_nick_button')
|
||||
|
|
|
@ -87,6 +87,7 @@ from common.connection_handlers_events import OurShowEvent, \
|
|||
from common.connection import Connection
|
||||
from common.file_props import FilesProp
|
||||
from common import pep
|
||||
import emoticons
|
||||
|
||||
import roster_window
|
||||
import profile_window
|
||||
|
@ -1839,20 +1840,6 @@ class Interface:
|
|||
### Methods dealing with emoticons
|
||||
################################################################################
|
||||
|
||||
@staticmethod
|
||||
def image_is_ok(image):
|
||||
if not os.path.exists(image):
|
||||
return False
|
||||
img = Gtk.Image()
|
||||
try:
|
||||
img.set_from_file(image)
|
||||
except Exception:
|
||||
return False
|
||||
t = img.get_storage_type()
|
||||
if t != Gtk.ImageType.PIXBUF and t != Gtk.ImageType.ANIMATION:
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def basic_pattern_re(self):
|
||||
if not self._basic_pattern_re:
|
||||
|
@ -1942,7 +1929,7 @@ class Interface:
|
|||
# NOT expanded. e.g., foo:) NO, foo :) YES, (brb) NO, (:)) YES, etc
|
||||
# We still allow multiple emoticons side-by-side like :P:P:P
|
||||
# sort keys by length so :qwe emot is checked before :q
|
||||
keys = sorted(self.emoticons, key=len, reverse=True)
|
||||
keys = sorted(emoticons.codepoints.keys(), key=len, reverse=True)
|
||||
emoticons_pattern_prematch = ''
|
||||
emoticons_pattern_postmatch = ''
|
||||
emoticon_length = 0
|
||||
|
@ -1981,97 +1968,7 @@ class Interface:
|
|||
self.invalid_XML_chars = '[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x1f]|'\
|
||||
'[\ud800-\udfff]|[\ufffe-\uffff]'
|
||||
|
||||
def popup_emoticons_under_button(self, button, parent_win):
|
||||
"""
|
||||
Popup the emoticons menu under button, located in parent_win
|
||||
"""
|
||||
gtkgui_helpers.popup_emoticons_under_button(self.emoticons_menu,
|
||||
button, parent_win)
|
||||
|
||||
def prepare_emoticons_menu(self):
|
||||
menu = Gtk.Menu()
|
||||
def emoticon_clicked(w, str_):
|
||||
if self.emoticon_menuitem_clicked:
|
||||
self.emoticon_menuitem_clicked(str_)
|
||||
# don't keep reference to CB of object
|
||||
# this will prevent making it uncollectable
|
||||
self.emoticon_menuitem_clicked = None
|
||||
def selection_done(widget):
|
||||
# remove reference to CB of object, which will
|
||||
# make it uncollectable
|
||||
self.emoticon_menuitem_clicked = None
|
||||
counter = 0
|
||||
# Calculate the side lenght of the popup to make it a square
|
||||
size = int(round(math.sqrt(len(self.emoticons_images))))
|
||||
for image in self.emoticons_images:
|
||||
# In Gtk 3.6, Gtk.MenuItem() doesn't contain a label child
|
||||
item = Gtk.MenuItem.new_with_label('q')
|
||||
img = Gtk.Image()
|
||||
if isinstance(image[1], GdkPixbuf.PixbufAnimation):
|
||||
img.set_from_animation(image[1])
|
||||
else:
|
||||
img.set_from_pixbuf(image[1])
|
||||
c = item.get_child()
|
||||
item.remove(c)
|
||||
item.add(img)
|
||||
item.connect('activate', emoticon_clicked, image[0])
|
||||
# add tooltip with ascii
|
||||
item.set_tooltip_text(image[0])
|
||||
menu.attach(item, counter % size, counter % size + 1,
|
||||
counter / size, counter / size + 1)
|
||||
counter += 1
|
||||
menu.connect('selection-done', selection_done)
|
||||
menu.show_all()
|
||||
return menu
|
||||
|
||||
def _init_emoticons(self, path, need_reload = False):
|
||||
#initialize emoticons dictionary and unique images list
|
||||
self.emoticons_images = list()
|
||||
self.emoticons = dict()
|
||||
self.emoticons_animations = dict()
|
||||
|
||||
sys.path.insert(0, path)
|
||||
import emoticons
|
||||
try:
|
||||
if need_reload:
|
||||
# we need to reload else that doesn't work when changing
|
||||
# emoticons set
|
||||
import imp
|
||||
imp.reload(emoticons)
|
||||
emots = emoticons.emoticons
|
||||
self.emoticons_sorting = None
|
||||
try:
|
||||
self.emoticons_sorting = emoticons.sorting
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
return True
|
||||
for emot_filename in emots:
|
||||
emot_file = os.path.join(path, emot_filename)
|
||||
if not self.image_is_ok(emot_file):
|
||||
continue
|
||||
for emot in emots[emot_filename]:
|
||||
emot = emot
|
||||
# This avoids duplicated emoticons with the same image eg. :)
|
||||
# and :-)
|
||||
if not emot_file in self.emoticons.values():
|
||||
if emot_file.endswith('.gif'):
|
||||
pix = GdkPixbuf.PixbufAnimation.new_from_file(emot_file)
|
||||
else:
|
||||
pix = GdkPixbuf.Pixbuf.new_from_file_at_size(emot_file,
|
||||
16, 16)
|
||||
self.emoticons_images.append((emot, pix))
|
||||
self.emoticons[emot.upper()] = emot_file
|
||||
def emoticons_sorter(item):
|
||||
try:
|
||||
return self.emoticons_sorting.index(item[0])
|
||||
except:
|
||||
return 0
|
||||
self.emoticons_images = sorted(self.emoticons_images, key=emoticons_sorter)
|
||||
del emoticons
|
||||
sys.path.remove(path)
|
||||
|
||||
def init_emoticons(self, need_reload = False):
|
||||
def init_emoticons(self):
|
||||
emot_theme = gajim.config.get('emoticons_theme')
|
||||
if not emot_theme:
|
||||
return
|
||||
|
@ -2092,40 +1989,14 @@ class Interface:
|
|||
transient_for=transient_for)
|
||||
gajim.config.set('emoticons_theme', '')
|
||||
return
|
||||
if self._init_emoticons(path, need_reload):
|
||||
dialogs.WarningDialog(_('Emoticons disabled'),
|
||||
_('Your configured emoticons theme cannot been loaded. You '
|
||||
'maybe need to update the format of emoticons.py file. See '
|
||||
'http://trac.gajim.org/wiki/Emoticons for more details.'),
|
||||
if not emoticons.load(path):
|
||||
dialogs.WarningDialog(
|
||||
_('Emoticons disabled'),
|
||||
_('Your configured emoticons theme could not be loaded.'
|
||||
' See the log for more details.'),
|
||||
transient_for=transient_for)
|
||||
gajim.config.set('emoticons_theme', '')
|
||||
return
|
||||
if len(self.emoticons) == 0:
|
||||
# maybe old format of emoticons file, try to convert it
|
||||
try:
|
||||
import pprint
|
||||
import emoticons
|
||||
emots = emoticons.emoticons
|
||||
fd = open(os.path.join(path, 'emoticons.py'), 'w')
|
||||
fd.write('emoticons = ')
|
||||
pprint.pprint( dict([
|
||||
(file_, [i for i in emots.keys() if emots[i] == file_])
|
||||
for file_ in set(emots.values())]), fd)
|
||||
fd.close()
|
||||
del emoticons
|
||||
self._init_emoticons(path, need_reload=True)
|
||||
except Exception:
|
||||
pass
|
||||
if len(self.emoticons) == 0:
|
||||
dialogs.WarningDialog(_('Emoticons disabled'),
|
||||
_('Your configured emoticons theme cannot been loaded. You '
|
||||
'maybe need to update the format of emoticons.py file. See '
|
||||
'http://trac.gajim.org/wiki/Emoticons for more details.'),
|
||||
transient_for=transient_for)
|
||||
gajim.config.set('emoticons_theme', '')
|
||||
if self.emoticons_menu:
|
||||
self.emoticons_menu.destroy()
|
||||
self.emoticons_menu = self.prepare_emoticons_menu()
|
||||
|
||||
################################################################################
|
||||
### Methods for opening new messages controls
|
||||
|
@ -2791,9 +2662,6 @@ class Interface:
|
|||
self.msg_win_mgr = None
|
||||
self.jabber_state_images = {'16': {}, '24': {}, '32': {}, 'opened': {},
|
||||
'closed': {}}
|
||||
self.emoticons_menu = None
|
||||
# handler when an emoticon is clicked in emoticons_menu
|
||||
self.emoticon_menuitem_clicked = None
|
||||
self.minimized_controls = {}
|
||||
self.status_sent_to_users = {}
|
||||
self.status_sent_to_groups = {}
|
||||
|
@ -2822,10 +2690,6 @@ class Interface:
|
|||
self.emot_and_basic = None
|
||||
self.sth_at_sth_dot_sth = None
|
||||
self.emot_only = None
|
||||
self.emoticons = []
|
||||
self.emoticons_animations = {}
|
||||
self.emoticons_images = {}
|
||||
self.emoticons_sorting = None
|
||||
|
||||
cfg_was_read = parser.read()
|
||||
|
||||
|
|
|
@ -438,7 +438,7 @@ class MessageWindow(object):
|
|||
control.chat_buttons_set_visible(not control.hide_chat_buttons)
|
||||
return True
|
||||
elif keyval == Gdk.KEY_m: # ALT + M show emoticons menu
|
||||
control.show_emoticons_menu()
|
||||
control.emoticons_button.get_popover().show()
|
||||
return True
|
||||
elif keyval == Gdk.KEY_d: # ALT + D show actions menu
|
||||
if Gtk.Settings.get_default().get_property(
|
||||
|
|
Loading…
Add table
Reference in a new issue