Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim-plugins.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Brötzmann <mailtrash@posteo.de>2020-04-24 21:49:41 +0300
committerDaniel Brötzmann <mailtrash@posteo.de>2020-04-26 14:22:23 +0300
commit63484c5b59b2f1943f5f3ef6e971eab0d6c07f59 (patch)
tree0a5197c34728224a6b47a1c1ecb79a6b7435bee4 /quick_replies
parent4098a77addf0517771de4c1052322a10d89ee98e (diff)
[quick_replies] Complete rework
Diffstat (limited to 'quick_replies')
-rw-r--r--quick_replies/gtk/config.py85
-rw-r--r--quick_replies/gtk/config.ui103
-rw-r--r--quick_replies/plugin.py233
-rw-r--r--quick_replies/qicon.pngbin1590 -> 0 bytes
-rw-r--r--quick_replies/quick_replies.py5
5 files changed, 296 insertions, 130 deletions
diff --git a/quick_replies/gtk/config.py b/quick_replies/gtk/config.py
new file mode 100644
index 0000000..a0844c0
--- /dev/null
+++ b/quick_replies/gtk/config.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
+#
+# This file is part of Quick Replies.
+#
+# Quick Replies 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; version 3 only.
+#
+# Quick Replies 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 Quick Replies. If not, see <http://www.gnu.org/licenses/>.
+
+from pathlib import Path
+
+from gi.repository import Gtk
+from gi.repository import Gdk
+
+from gajim.common import app
+
+from gajim.plugins.plugins_i18n import _
+from gajim.plugins.helpers import get_builder
+
+
+class ConfigDialog(Gtk.ApplicationWindow):
+ def __init__(self, plugin, transient):
+ Gtk.ApplicationWindow.__init__(self)
+ self.set_application(app.app)
+ self.set_show_menubar(False)
+ self.set_title(_('Quick Replies Configuration'))
+ self.set_transient_for(transient)
+ self.set_default_size(400, 400)
+ self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
+ self.set_modal(True)
+ self.set_destroy_with_parent(True)
+
+ ui_path = Path(__file__).parent
+ self._ui = get_builder(ui_path.resolve() / 'config.ui')
+
+ self._plugin = plugin
+
+ self.add(self._ui.box)
+
+ self._fill_list()
+ self.show_all()
+
+ self._ui.connect_signals(self)
+ self.connect('destroy', self._on_destroy)
+
+ def _fill_list(self):
+ for reply in self._plugin.quick_replies:
+ self._ui.replies_store.append([reply])
+
+ def _on_reply_edited(self, _renderer, path, new_text):
+ iter_ = self._ui.replies_store.get_iter(path)
+ self._ui.replies_store.set_value(iter_, 0, new_text)
+
+ def _on_add_clicked(self, _button):
+ self._ui.replies_store.append([_('New Quick Reply')])
+ row = self._ui.replies_store[-1]
+ self._ui.replies_treeview.scroll_to_cell(
+ row.path, None, False, 0, 0)
+ self._ui.selection.unselect_all()
+ self._ui.selection.select_path(row.path)
+
+ def _on_remove_clicked(self, _button):
+ model, paths = self._ui.selection.get_selected_rows()
+ references = []
+ for path in paths:
+ references.append(Gtk.TreeRowReference.new(model, path))
+
+ for ref in references:
+ iter_ = model.get_iter(ref.get_path())
+ self._ui.replies_store.remove(iter_)
+
+ def _on_destroy(self, *args):
+ replies = []
+ for row in self._ui.replies_store:
+ if row[0] == '':
+ continue
+ replies.append(row[0])
+ self._plugin.set_quick_replies(replies)
diff --git a/quick_replies/gtk/config.ui b/quick_replies/gtk/config.ui
new file mode 100644
index 0000000..7683f8d
--- /dev/null
+++ b/quick_replies/gtk/config.ui
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkListStore" id="replies_store">
+ <columns>
+ <!-- column-name reply -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkBox" id="box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">18</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="vexpand">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="replies_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">replies_store</property>
+ <property name="search_column">1</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="selection">
+ <property name="mode">multiple</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Quick Reply</property>
+ <property name="clickable">True</property>
+ <property name="sort_indicator">True</property>
+ <property name="sort_column_id">0</property>
+ <child>
+ <object class="GtkCellRendererText">
+ <property name="editable">True</property>
+ <signal name="edited" handler="_on_reply_edited" swapped="no"/>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="toolbar_style">icons</property>
+ <property name="icon_size">4</property>
+ <child>
+ <object class="GtkToolButton">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add</property>
+ <property name="icon_name">list-add-symbolic</property>
+ <signal name="clicked" handler="_on_add_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove</property>
+ <property name="icon_name">list-remove-symbolic</property>
+ <signal name="clicked" handler="_on_remove_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <style>
+ <class name="inline-toolbar"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/quick_replies/plugin.py b/quick_replies/plugin.py
index 799558d..683bb52 100644
--- a/quick_replies/plugin.py
+++ b/quick_replies/plugin.py
@@ -1,140 +1,113 @@
+import json
+from pathlib import Path
+from functools import partial
+
from gi.repository import Gtk
-from gi.repository import GdkPixbuf
-from gajim.common import app
+from gajim.common import configpaths
from gajim.plugins import GajimPlugin
-from gajim.plugins.gui import GajimPluginConfigDialog
-from gajim.plugins.helpers import log_calls
from gajim.plugins.plugins_i18n import _
-class QuickRepliesPlugin(GajimPlugin):
+from quick_replies.quick_replies import DEFAULT_DATA
+from quick_replies.gtk.config import ConfigDialog
- @log_calls('QuickRepliesPlugin')
- def init(self):
- self.description = _('Plugin for quick replies')
- self.config_dialog = QuickRepliesPluginConfigDialog(self)
- self.chat_control = None
+class QuickRepliesPlugin(GajimPlugin):
+ def init(self):
+ self.description = _('Adds a menu with customizable quick replies')
+ self.config_dialog = partial(ConfigDialog, self)
self.gui_extension_points = {
- 'chat_control_base': (self.connect_with_chat_control,
- self.disconnect_from_chat_control),
- 'chat_control_base_update_toolbar': (self.update_button_state,
- None)}
- self.config_default_values = {
- 'entry1': ('Hello!', ''),
- 'entry2': ('How are you?', ''),
- 'entry3': ('Good bye.', ''),
- 'entry4': ('', ''),
- 'entry5': ('', ''),
- 'entry6': ('', ''),
- 'entry7': ('', ''),
- 'entry8': ('', ''),
- 'entry9': ('', ''),
- 'entry10': ('', ''),
+ 'chat_control_base': (self._connect_chat_control,
+ self._disconnect_chat_control),
}
- self.controls = []
-
- @log_calls('QuickRepliesPlugin')
- def connect_with_chat_control(self, chat_control):
-
- self.chat_control = chat_control
- base = Base(self, chat_control)
- self.controls.append(base)
-
- @log_calls('QuickRepliesPlugin')
- def disconnect_from_chat_control(self, chat_control):
-
- for control in self.controls:
- control.disconnect_from_chat_control()
- self.controls = []
-
- @log_calls('QuickRepliesPlugin')
- def update_button_state(self, chat_control):
- for base in self.controls:
- if base.chat_control != chat_control:
- continue
- base.button.set_sensitive(
- app.account_is_connected(chat_control.account))
-
-
-class Base(object):
-
- def __init__(self, plugin, chat_control):
-
- self.plugin = plugin
- self.chat_control = chat_control
- self.create_button()
- self.create_menu()
-
- def create_button(self):
-
- actions_hbox = self.chat_control.xml.get_object('hbox')
- self.button = Gtk.MenuButton(label=None, stock=None, use_underline=True)
- self.button.get_style_context().add_class(
- 'chatcontrol-actionbar-button')
- self.button.set_property('relief', Gtk.ReliefStyle.NONE)
- self.button.set_property('can-focus', False)
- img = Gtk.Image()
- img_path = self.plugin.local_file_path('quick_replies.png')
- pixbuf = GdkPixbuf.Pixbuf.new_from_file(img_path)
- img.set_from_pixbuf(pixbuf)
- self.button.set_image(img)
- self.button.set_tooltip_text(_('Quick replies'))
- actions_hbox.pack_start(self.button, False, False , 0)
- actions_hbox.reorder_child(self.button,
- len(actions_hbox.get_children()) - 2)
- self.button.show()
-
- def on_insert(self, widget, text):
-
- text = text.rstrip() + ' '
- message_buffer = self.chat_control.msg_textview.get_buffer()
- self.chat_control.msg_textview.remove_placeholder()
- message_buffer.insert_at_cursor(text)
- self.chat_control.msg_textview.grab_focus()
-
- def create_menu(self):
-
- self.menu = Gtk.Menu()
-
- for count in range(1, 11):
- text = self.plugin.config['entry' + str(count)]
- if not text:
- continue
- item = Gtk.MenuItem(text)
- item.connect('activate', self.on_insert, text)
- self.menu.append(item)
- self.menu.show_all()
- self.button.set_popup(self.menu)
-
- def disconnect_from_chat_control(self):
- actions_hbox = self.chat_control.xml.get_object('hbox')
- actions_hbox.remove(self.button)
-
-
-class QuickRepliesPluginConfigDialog(GajimPluginConfigDialog):
-
- def init(self):
-
- self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
- 'config_dialog.ui')
- self.xml = Gtk.Builder()
- self.xml.set_translation_domain('gajim_plugins')
- self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['table1'])
- hbox = self.xml.get_object('table1')
- self.get_child().pack_start(hbox, True, True, 0)
- self.xml.connect_signals(self)
-
- def on_run(self):
-
- for count in range(1, 11):
- self.xml.get_object('entry' + str(count)).set_text(
- self.plugin.config['entry' + str(count)])
-
- def entry_changed(self, widget):
-
- name = Gtk.Buildable.get_name(widget)
- self.plugin.config[name] = widget.get_text()
- for control in self.plugin.controls:
- control.create_menu()
+ self._buttons = {}
+ self.quick_replies = self._load_quick_replies()
+
+ def _connect_chat_control(self, chat_control):
+ button = QuickRepliesButton(chat_control, self.quick_replies)
+ self._buttons[chat_control.control_id] = button
+ actions_hbox = chat_control.xml.get_object('hbox')
+ actions_hbox.pack_start(button, False, False, 0)
+ actions_hbox.reorder_child(
+ button, len(actions_hbox.get_children()) - 2)
+ button.show()
+
+ def _disconnect_chat_control(self, chat_control):
+ button = self._buttons.get(chat_control.control_id)
+ if button is not None:
+ button.destroy()
+ self._buttons.pop(chat_control.control_id, None)
+
+ @staticmethod
+ def _load_quick_replies():
+ try:
+ data_path = Path(configpaths.get('PLUGINS_DATA'))
+ except KeyError:
+ # PLUGINS_DATA was added in 1.0.99.1
+ return DEFAULT_DATA
+
+ path = data_path / 'quick_replies' / 'quick_replies'
+ if not path.exists():
+ return DEFAULT_DATA
+
+ with path.open('r') as file:
+ quick_replies = json.load(file)
+ return quick_replies
+
+ @staticmethod
+ def _save_quick_replies(quick_replies):
+ try:
+ data_path = Path(configpaths.get('PLUGINS_DATA'))
+ except KeyError:
+ # PLUGINS_DATA was added in 1.0.99.1
+ return
+
+ path = data_path / 'quick_replies'
+ if not path.exists():
+ path.mkdir(parents=True)
+
+ filepath = path / 'quick_replies'
+ with filepath.open('w') as file:
+ json.dump(quick_replies, file)
+
+ def set_quick_replies(self, quick_replies):
+ self.quick_replies = quick_replies
+ self._save_quick_replies(quick_replies)
+ self._update_buttons()
+
+ def _update_buttons(self):
+ for button in self._buttons.values():
+ button.update_menu(self.quick_replies)
+
+
+class QuickRepliesButton(Gtk.MenuButton):
+ def __init__(self, chat_control, replies):
+ Gtk.MenuButton.__init__(self)
+ self.get_style_context().add_class('chatcontrol-actionbar-button')
+ self.set_property('relief', Gtk.ReliefStyle.NONE)
+ self.set_can_focus(False)
+ plugin_path = Path(__file__).parent
+ img_path = plugin_path.resolve() / 'quick_replies.png'
+ img = Gtk.Image.new_from_file(str(img_path))
+ self.set_image(img)
+ self.set_tooltip_text(_('Quick Replies'))
+
+ self._chat_control = chat_control
+
+ self.update_menu(replies)
+
+ def update_menu(self, replies):
+ self._menu = Gtk.Menu()
+ for reply in replies:
+ item = Gtk.MenuItem.new_with_label(label=reply)
+ item.connect('activate', self._on_insert, reply)
+ self._menu.append(item)
+ self._menu.show_all()
+ self.set_popup(self._menu)
+
+ def _on_insert(self, widget, text):
+ message_buffer = self._chat_control.msg_textview.get_buffer()
+ self._chat_control.msg_textview.remove_placeholder()
+ message_buffer.insert_at_cursor(text.rstrip() + ' ')
+ self._chat_control.msg_textview.grab_focus()
diff --git a/quick_replies/qicon.png b/quick_replies/qicon.png
deleted file mode 100644
index bc9da4b..0000000
--- a/quick_replies/qicon.png
+++ /dev/null
Binary files differ
diff --git a/quick_replies/quick_replies.py b/quick_replies/quick_replies.py
new file mode 100644
index 0000000..502dd01
--- /dev/null
+++ b/quick_replies/quick_replies.py
@@ -0,0 +1,5 @@
+DEFAULT_DATA = [
+ 'Hello!',
+ 'How are you?',
+ 'Good bye.',
+]