Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gajim/gajim-plugins
  • lovetox/gajim-plugins
  • ag/gajim-plugins
  • FlorianMuenchbach/gajim-plugins
  • rom1dep/gajim-plugins
  • pitchum/gajim-plugins
  • wurstsalat/gajim-plugins
  • Dicson/gajim-plugins
  • andre/gajim-plugins
  • link2xt/gajim-plugins
  • marmistrz/gajim-plugins
  • Jens/gajim-plugins
  • muelli/gajim-plugins
  • asterix/gajim-plugins
  • orhideous/gajim-plugins
  • ngvelprz/gajim-plugins
  • appleorange1/gajim-plugins
  • Martin/gajim-plugins
  • maltel/gajim-plugins
  • Seve/gajim-plugins
  • evert-mouw/gajim-plugins
  • Yuki/gajim-plugins
  • mxre/gajim-plugins
  • ValdikSS/gajim-plugins
  • SaltyBones/gajim-plugins
  • comradekingu/gajim-plugins
  • ritzmann/gajim-plugins
  • genofire/gajim-plugins
  • jjrh/gajim-plugins
  • yarmak/gajim-plugins
  • PapaTutuWawa/gajim-plugins
  • weblate/gajim-plugins
  • XutaxKamay/gajim-plugins
  • nekk/gajim-plugins
  • principis/gajim-plugins
  • cbix/gajim-plugins
  • bodqhrohro/gajim-plugins
  • airtower-luna/gajim-plugins
  • toms/gajim-plugins
  • mesonium/gajim-plugins
  • lissine/gajim-plugins
  • anviar/gajim-plugins
42 results
Show changes
Showing
with 1225 additions and 183 deletions
from .emoticons_pack import EmoticonsPackPlugin
<?xml version="1.0"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkTextBuffer" id="textbuffer1">
<property name="text">Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization.</property>
</object>
<object class="GtkWindow" id="window1">
<child>
<object class="GtkHPaned" id="hpaned2">
<property name="width_request">600</property>
<property name="height_request">350</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">340</property>
<property name="position_set">True</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">6</property>
<property name="hscrollbar_policy">never</property>
<child>
<object class="GtkTreeView" id="available_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="search_column">1</property>
</object>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox4">
<property name="visible">True</property>
<property name="border_width">5</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="set_name">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label">empty</property>
<property name="selectable">True</property>
<property name="ellipsize">end</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox8">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="label" translatable="yes">Authors:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="authors_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="xpad">6</property>
<property name="label">&lt;empty&gt;</property>
<property name="selectable">True</property>
<property name="ellipsize">end</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">Converted by:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="converter_label">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="xpad">6</property>
<property name="label">&lt;empty&gt;</property>
<property name="selectable">True</property>
<property name="ellipsize">end</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox9">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="label" translatable="yes">Homepage:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLinkButton" id="homepage_linkbutton">
<property name="label">button</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<property name="focus_on_click">False</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox5">
<property name="visible">True</property>
<child>
<object class="GtkHBox" id="hbox10">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="label" translatable="yes">Description:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox15">
<property name="visible">True</property>
<child>
<object class="GtkHButtonBox" id="hbuttonbox3">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="inslall_upgrade_button">
<property name="label" translatable="yes">Install/Upgrade</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="image">image1</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_inslall_upgrade_clicked"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="legend_button">
<property name="label" translatable="yes">Legend</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="image">image2</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_legend_button_clicked"/>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">False</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
</object>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="stock">gtk-dialog-info</property>
</object>
</interface>
[MacThemes2]
icon: Smile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Mac Themes 2 Smilies <br/>
<img src="preview.image"/>
</body>
authors: David Lanham
homepage: https://trac.gajim.org/wiki/MacThemes2
[Citters Emoticons from Psi]
icon: smile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Citters Emoticons from Psi<br/>
<img src="preview.image"/>
</body>
converter: churchyard (JID: churchyard@njs.netlab.cz)
homepage: https://trac.gajim.org/wiki/cittersemoticons
[EmotiPonies]
icon: pinkiesmile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
EmotiPonies <br/>
<img src="preview.image"/>
</body>
authors: midori-no-ink (Copyright (c) Hasbro, Inc.)
homepage: https://trac.gajim.org/wiki/EmotiPonies
[Google Talk Emoticons]
icon: equal_smile.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Google Talk Emoticons<br/>
<img src="preview.image"/>
</body>
converter: Chris Cook
homepage: https://trac.gajim.org/wiki/gtalkemoticons
[ICQ6 Emoticons]
icon: Smiley1.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
ICQ6 Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/ICQ6-emotions
[Kolobok Animated Emoticons]
icon: ab.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Kolobok Animated Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/kolobok-animated-emoticons
[Pidgin Emoticons]
icon: smile.png
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Pidgin Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/PidginEmoticons
[Psynova qip's theme]
icon: ab.gif
authors: zOrg
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Psynova qip's theme<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/psynovaemoticons
[Yahoo Emoticons]
icon: equal_smile.gif
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Yahoo Emoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/YahooEmoticons
[Nekomoticons]
icon: s01.jpg
authors: somik (http://somik.deviantart.com/)
converter: Dotterian (http://dotterian.ru/)
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Nekomoticons<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/Nekomoticons
[Trollicons]
icon: Happy-EverythingWentBetterThanExpected.png
authors: Sagar Pandya
Chris Li
Jonathan E. Chen
converter: Matthieu L.
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Trollicons (Rage Icons for Gajim)<br/>
<img src="preview.image"/>
</body>
homepage: https://github.com/sagargp/trollicons/blob/master/readme.md#downloads
[Twemoji]
icon: 1f600.svg
authors: Twitter, Inc. and other contributors
converter: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Twemoji (Twitter Emoji for Gajim)<br/>
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/Twemoji
[Twemoji resized]
icon: 1f600.png
authors: Twitter, Inc. and other contributors
converter: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>, Thilo Molitor <thilo@eightysoft.de>
description: <body xmlns='http://www.w3.org/1999/xhtml'>
Twemoji (Twitter Emoji for Gajim)<br/>
The size of this Emojis is 24px and thus a bit more usable than those of the Twemoji packet (having a size of 48px)<br/>
Also the sorting is more intuitive (displaying all important emojis at the top of the emoticons dropdown).<br />
<img src="preview.image"/>
</body>
homepage: https://trac.gajim.org/wiki/TwemojiResized
[Noto]
icon: 1f600.png
authors: Google Inc. and other contributors
converter: Tommaso Sardelli <lacapannadelloziotom@gmail.com>, Thilo Molitor <thilo@eightysoft.de>
description: <body xmlns='http://www.w3.org/1999/xhtml'>
<h1>Noto Emoji for Gajim</h1>
<br/>
<p>The size of this Emojis is 24px in order to fit well with text size.</p>
<br/>
<img src="preview.image"/>
</body>
homepage: https://github.com/googlei18n/noto-emoji
emoticons_pack/emoticons_pack.png

840 B

# -*- coding: utf-8 -*-
##
import gtk
import pango
import gobject
import io
import ConfigParser
import os
import zipfile
import tempfile
from shutil import rmtree
import sys
import imp
from common import gajim
from plugins import GajimPlugin
from plugins.helpers import log_calls
from htmltextview import HtmlTextView
from conversation_textview import ConversationTextview
from dialogs import WarningDialog, HigDialog
(
C_PIXBUF,
C_NAME,
C_DESCRIPTION,
C_AUTHORS,
C_CONVERTER,
C_HOMEPAGE,
C_UPGRADE) = range(7)
class EmoticonsPackPlugin(GajimPlugin):
@log_calls('EmoticonsPackPlugin')
def init(self):
self.config_dialog = None # EmoticonsPackPluginConfigDialog(self)
self.window = None
self.model = None
self.connected_ids = {}
self.tmp_dir = ''
@log_calls('EmoticonsPackPlugin')
def activate(self):
self.pl_menuitem = gajim.interface.roster.xml.get_object(
'plugins_menuitem')
self.id_ = self.pl_menuitem.connect_after('activate', self.on_activate)
if 'plugins' in gajim.interface.instances:
self.on_activate(None)
@log_calls('EmoticonsPackPlugin')
def deactivate(self):
self.pl_menuitem.disconnect(self.id_)
if hasattr(self, 'page_num'):
self.notebook.remove_page(self.notebook.page_num(self.hpaned))
self.notebook.set_current_page(0)
for id_, widget in list(self.connected_ids.items()):
widget.disconnect(id_)
del self.page_num
def on_activate(self, widget):
if 'plugins' not in gajim.interface.instances:
return
if hasattr(self, 'page_num'):
# 'Available' tab exists
return
self.installed_plugins_model = gajim.interface.instances[
'plugins'].installed_plugins_model
self.notebook = gajim.interface.instances['plugins'].plugins_notebook
id_ = self.notebook.connect(
'switch-page', self.on_notebook_switch_page)
self.connected_ids[id_] = self.notebook
self.window = gajim.interface.instances['plugins'].window
id_ = self.window.connect('destroy', self.on_win_destroy)
self.connected_ids[id_] = self.window
self.Gtk_BUILDER_FILE_PATH = self.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,
['hpaned2', 'image1', 'image2'])
self.hpaned = self.xml.get_object('hpaned2')
self.page_num = self.notebook.append_page(self.hpaned, gtk.Label(_(
'Emoticons')))
widgets_to_extract = (
'set_name', 'available_treeview', 'homepage_linkbutton',
'inslall_upgrade_button', 'authors_label', 'converter_label',)
for widget_name in widgets_to_extract:
setattr(self, widget_name, self.xml.get_object(widget_name))
self.model = gtk.ListStore(
gtk.gdk.Pixbuf, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
self.available_treeview.set_model(self.model)
self.available_treeview.set_rules_hint(True)
self.model.set_sort_column_id(1, gtk.SORT_ASCENDING)
#self.progressbar.set_property('no-show-all', True)
renderer = gtk.CellRendererText()
col = gtk.TreeViewColumn(_('Name'))
cell = gtk.CellRendererPixbuf()
col.pack_start(cell, False)
col.add_attribute(cell, 'pixbuf', C_PIXBUF)
col.pack_start(renderer, True)
col.add_attribute(renderer, 'text', C_NAME)
col.set_property('expand', True)
col.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
self.available_treeview.append_column(col)
renderer = gtk.CellRendererToggle()
renderer.set_property('activatable', True)
renderer.connect('toggled', self.available_emoticons_toggled_cb)
col = gtk.TreeViewColumn(
_('Install /\nUpgrade'), renderer, active=C_UPGRADE)
col.set_property('expand', False)
col.set_resizable(False)
self.available_treeview.append_column(col)
selection = self.available_treeview.get_selection()
selection.connect(
'changed', self.available_emoticons_treeview_selection_changed)
selection.set_mode(gtk.SELECTION_SINGLE)
self.emoticons_description_textview = ConversationTextview(None)
sw = self.xml.get_object('scrolledwindow1')
sw.add(self.emoticons_description_textview.tv)
self.xml.connect_signals(self)
self.window.show_all()
def on_legend_button_clicked(self, widget):
self.xml.get_object('scrolledwindow1').get_children()[0].destroy()
treeview_selection = self.available_treeview.get_selection()
model, iter = treeview_selection.get_selected()
name = model.get_value(iter, C_NAME)
button = self.xml.get_object('legend_button')
if button.get_label() == _('Legend'):
button.set_label(_('Description'))
sys.path.append(os.path.join(self.tmp_dir, name))
import emoticons
imp.reload(emoticons)
self.emoticons_description_textview = gtk.TextView()
sw = self.xml.get_object('scrolledwindow1')
sw.add(self.emoticons_description_textview)
sw.show_all()
buff = self.emoticons_description_textview.get_buffer()
for icon in emoticons.emoticons:
icon_file = os.path.join(self.tmp_dir, name, icon)
with open(icon_file, 'rb') as _file:
data = _file.read()
pbl = gtk.gdk.PixbufLoader()
pbl.write(data)
pbl.close()
if icon.endswith('.gif'):
img = gtk.Image()
img.set_from_animation(pbl.get_animation())
img.show()
anchor = buff.create_child_anchor(buff.get_end_iter())
self.emoticons_description_textview.add_child_at_anchor(
img, anchor)
else:
buff.insert_pixbuf(buff.get_end_iter(), pbl.get_pixbuf())
text = ' , '.join(emoticons.emoticons[icon])
buff.insert(buff.get_end_iter(), text + '\n', -1)
self.emoticons_description_textview.set_property('sensitive', True)
sys.path.remove(os.path.join(self.tmp_dir, name))
else:
self.emoticons_description_textview = ConversationTextview(None)
sw = self.xml.get_object('scrolledwindow1')
sw.add(self.emoticons_description_textview.tv)
sw.show_all()
button.set_label(_('Legend'))
desc = _(model.get_value(iter, C_DESCRIPTION))
if not desc.startswith('<body '):
desc = '<body xmlns=\'http://www.w3.org/1999/xhtml\'>' + \
desc + ' </body>'
desc = desc.replace('preview.image', ('file:' + os.path.join(
self.tmp_dir, name, 'preview.png'))).replace('\n', '<br/>')
self.emoticons_description_textview.tv.display_html(
desc, self.emoticons_description_textview.tv,
self.emoticons_description_textview)
self.emoticons_description_textview.tv.set_property('sensitive', True)
def dict_to_html(self, dict_):
desc = ''
for icon in dict_:
acr = ' , '.join(dict_[icon])
desc += ' '+ icon + ' '+ acr + '\n'
return desc
def on_inslall_upgrade_clicked(self, widget):
self.inslall_upgrade_button.set_property('sensitive', False)
self.errors = ''
def on_error(func, path, error):
if func == os.path.islink:
# if symlink
os.unlink(path)
return
# access is denied or other
# WarningDialog(_('Can\'t remove dir'), error[1], self.window)
self.errors += str(error[1])
name_list = []
for i in range(len(self.model)):
if self.model[i][C_UPGRADE]:
name_list.append(self.model[i][C_NAME])
for name in name_list:
# remove dirs
target_dir = os.path.join(gajim.MY_EMOTS_PATH, name)
if os.path.isdir(target_dir):
rmtree(target_dir, False, on_error)
# unzip new files
zip_file = os.path.join(self.__path__, 'emoticons_pack.zip')
with zipfile.ZipFile(zip_file, 'r') as myzip:
namelist = myzip.namelist()
for n in namelist:
if not n.startswith(name):
continue
try:
icon_file = myzip.extract(n, path=gajim.MY_EMOTS_PATH)
except Exception as e:
self.errors += str(e)
# unset all checkbattons
for i in range(len(self.model)):
self.model[i][C_UPGRADE] = False
if self.errors:
WarningDialog(
_('Not fully installed'),
'Access is denied or other', self.window)
else:
# show dialog
dialog = HigDialog(
None, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, '',
_('All selected emoticons installed(upgraded)'))
dialog.set_modal(False)
dialog.set_transient_for(self.window)
dialog.popup()
def on_win_destroy(self, widget):
if hasattr(self, 'page_num'):
del self.page_num
if os.path.isdir(self.tmp_dir):
rmtree(self.tmp_dir, True)
def available_emoticons_toggled_cb(self, cell, path):
is_active = self.model[path][C_UPGRADE]
self.model[path][C_UPGRADE] = not is_active
dir_list = []
for i in range(len(self.model)):
if self.model[i][C_UPGRADE]:
dir_list.append(self.model[i][C_NAME])
if not dir_list:
self.inslall_upgrade_button.set_property('sensitive', False)
else:
self.inslall_upgrade_button.set_property('sensitive', True)
def on_notebook_switch_page(self, widget, page, page_num):
tab_label_text = self.notebook.get_tab_label_text(self.hpaned)
if tab_label_text != (_('Emoticons')):
return
self.model.clear()
self.fill_table()
self.select_root_iter()
def fill_table(self):
conf = ConfigParser.ConfigParser()
# read metadata from contents.ini
contents_path = os.path.join(self.__path__, 'contents.ini')
with open(contents_path) as _file:
conf.readfp(_file)
for section in conf.sections():
# get icon
try:
filename = conf.get(section, 'icon')
filename = section + '/' + filename
zip_file = os.path.join(self.__path__, 'emoticons_pack.zip')
with zipfile.ZipFile(zip_file, 'r') as myzip:
icon_file = myzip.open(filename, mode='r')
data = icon_file.read()
pbl = gtk.gdk.PixbufLoader()
pbl.set_size(16, 16)
pbl.write(data)
pbl.close()
icon = pbl.get_pixbuf()
except Exception:
continue
authors = _('Unknown')
converter = _('Unknown')
if conf.has_option(section, 'authors'):
authors = conf.get(section, 'authors')
if conf.has_option(section, 'converter'):
converter = conf.get(section, 'converter')
self.model.append(
[icon, section,
conf.get(section, 'description'),
authors, converter,
conf.get(section, 'homepage'), False])
conf.remove_section(section)
def available_emoticons_treeview_selection_changed(self, treeview_selection):
model, iter = treeview_selection.get_selected()
button = self.xml.get_object('legend_button')
button.set_label(_('Legend'))
if iter:
set_name = model.get_value(iter, C_NAME)
if os.path.isdir(self.tmp_dir):
rmtree(self.tmp_dir, True)
self.tmp_dir = tempfile.mkdtemp()
# unzip new files
zip_file = os.path.join(self.__path__, 'emoticons_pack.zip')
with zipfile.ZipFile(zip_file, 'r') as myzip:
namelist = myzip.namelist()
for n in namelist:
if not n.startswith(set_name):
continue
myzip.extract(n, path=self.tmp_dir)
self.set_name.set_text(set_name)
self.authors_label.set_text(model.get_value(iter, C_AUTHORS))
self.converter_label.set_text(model.get_value(iter, C_CONVERTER))
self.homepage_linkbutton.set_uri(
model.get_value(iter, C_HOMEPAGE))
self.homepage_linkbutton.set_label(
model.get_value(iter, C_HOMEPAGE))
label = self.homepage_linkbutton.get_children()[0]
label.set_ellipsize(pango.ELLIPSIZE_END)
self.homepage_linkbutton.set_property('sensitive', True)
self.xml.get_object('scrolledwindow1').get_children()[0].destroy()
self.emoticons_description_textview = ConversationTextview(None)
sw = self.xml.get_object('scrolledwindow1')
sw.add(self.emoticons_description_textview.tv)
sw.show_all()
desc = _(model.get_value(iter, C_DESCRIPTION))
if not desc.startswith('<body '):
desc = '<body xmlns=\'http://www.w3.org/1999/xhtml\'>' + \
desc + ' </body>'
else:
desc = desc.replace('preview.image', ('file:' + os.path.join(
self.tmp_dir, set_name, 'preview.png')))
self.emoticons_description_textview.tv.display_html(desc,
self.emoticons_description_textview.tv,
self.emoticons_description_textview)
self.emoticons_description_textview.tv.set_property('sensitive', True)
else:
self.set_name.set_text('')
self.authors_label.set_text('')
self.homepage_linkbutton.set_uri('')
self.homepage_linkbutton.set_label('')
self.homepage_linkbutton.set_property('sensitive', False)
def select_root_iter(self):
if hasattr(self, 'page_num'):
selection = self.available_treeview.get_selection()
if selection.count_selected_rows() == 0:
root_iter = self.model.get_iter_first()
selection.select_iter(root_iter)
scr_win = self.xml.get_object('scrolledwindow2')
vadjustment = scr_win.get_vadjustment()
if vadjustment:
vadjustment.set_value(0)
#GObject.idle_add(self.available_treeview.grab_focus)
File added
[info]
name: Emoticons pack
short_name: emoticons_pack
version: 0.1.5
description: Install, update and view detailed legend of emoticons
authors: Denis Fomin <fominde@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/
min_gajim_version: 0.16
max_gajim_version: 0.16.9
import sqlite3
from common import gajim
import sys
import os
'''
TODO:
1-) Modify the database class to use models instead of method arguments
2-) Normalize the database. Don't save dirs and files in the same table
'''
class FilesharingDatabase:
def __init__(self, plugin):
self.plugin = plugin
path_l = os.path.split(plugin.config.FILE_PATH)
def __init__(self, FILE_PATH=None):
if not FILE_PATH:
return
path_l = os.path.split(FILE_PATH)
path = os.path.join(path_l[0], 'shared_files.db')
db_exist = os.path.exists(path)
self.conn = sqlite3.connect(path)
......@@ -31,13 +38,13 @@ class FilesharingDatabase:
self.conn.commit()
c.close()
def get_toplevel_files(self, account, requester):
def get_toplevel_dirs(self, account, requester):
c = self.conn.cursor()
data = (account, requester)
c.execute("SELECT relative_path, hash_sha1, size, description, " +
"mod_date, is_dir FROM (files JOIN permissions ON" +
" files.fid=permissions.fid) WHERE account=? AND requester=?" +
" AND relative_path NOT LIKE '%/%'", data)
" AND is_dir=1 AND relative_path NOT LIKE '%/%'", data)
result = c.fetchall()
c.close()
return result
......@@ -88,16 +95,15 @@ class FilesharingDatabase:
else:
data = (account, requester, name)
sql = "SELECT relative_path, hash_sha1, size, description, " + \
"mod_date, file_path FROM (files JOIN permissions ON" + \
"mod_date, file_path, is_dir FROM (files JOIN permissions ON" + \
" files.fid=permissions.fid) WHERE account=? AND requester=?" +\
" AND relative_path=?"
c.execute(sql, data)
result = c.fetchall()
c.close()
if result == []:
return None
else:
if result != []:
return result[0]
return result
def get_files_name(self, account, requester):
result = self.get_files(account, requester)
......@@ -115,7 +121,8 @@ class FilesharingDatabase:
>>> _delete_file(1)
"""
self._check_duplicate(account, requester, file_)
requester = gajim.get_jid_without_resource(requester)
if requester.find('/') != -1:
raise Exception('The requester must be given without a resource attached')
c = self.conn.cursor()
c.execute("INSERT INTO files (file_path, " +
"relative_path, hash_sha1, size, description, mod_date, " +
......@@ -138,7 +145,7 @@ class FilesharingDatabase:
data = (account, requester, file_[2])
c.execute("SELECT * FROM (files JOIN permissions ON" +
" files.fid=permissions.fid) WHERE account=? AND requester=?" +
" AND hash_sha1=?)", data)
" AND hash_sha1=?", data)
result.extend(c.fetchall())
if len(result) > 0:
raise Exception('Duplicated entry')
......
......@@ -14,7 +14,7 @@ from fileshare_window import FileShareWindow
import fshare_protocol
from common import ged
from common import caps_cache
from common import xmpp
from common import nbxmpp
from plugins.gui import GajimPluginConfigDialog
......@@ -26,17 +26,15 @@ class FileSharePlugin(GajimPlugin):
@log_calls('FileSharePlugin')
def init(self):
self.activated = False
self.description = _('This plugin allows you to share folders'
' with a peer using jingle file transfer.')
self.config_dialog = FileSharePluginConfigDialog(self)
home_path = os.path.expanduser('~/')
self.config_default_values = {'incoming_dir': (home_path, '')}
self.database = database.FilesharingDatabase(self)
self.database = database.FilesharingDatabase(self.config.FILE_PATH)
# Create one protocol handler per account
accounts = gajim.contacts.get_accounts()
for account in gajim.contacts.get_accounts():
FileSharePlugin.prohandler[account] = \
fshare_protocol.Protocol(account, self)
fshare_protocol.ProtocolDispatcher(account, self)
self.events_handlers = {
'raw-iq-received': (ged.CORE, self._nec_raw_iq)
}
......@@ -81,12 +79,13 @@ class FileSharePlugin(GajimPlugin):
gajim.connections[a].status)
def _nec_raw_iq(self, obj):
if obj.stanza.getTag('match',
if obj.stanza.getTag('query',
namespace=fshare_protocol.NS_FILE_SHARING) and self.activated:
account = obj.conn.name
pro = FileSharePlugin.prohandler[account]
pro.handler(obj.stanza)
raise xmpp.NodeProcessed
peerjid = obj.stanza.getFrom()
pro.handler(obj.stanza, str(peerjid))
raise nbxmpp.NodeProcessed
def __get_contact_menu(self, contact, account):
raise NotImplementedError
......@@ -176,7 +175,7 @@ class FileSharePluginConfigDialog(GajimPluginConfigDialog):
def on_run(self):
widget = self.xml.get_object('dl_folder')
widget.set_text(str(self.plugin.config['incoming_dir']))
def on_hide(self, widget):
widget = self.xml.get_object('dl_folder')
self.plugin.config['incoming_dir'] = widget.get_text()
from common import xmpp
from common import helpers
from common import gajim
from common import XMPPDispatcher
from common.xmpp import Hashes
import nbxmpp
from nbxmpp import Hashes
try:
from common import helpers
from common import gajim
except ImportError:
print "Import Error: Ignore if we are testing"
# Namespace for file sharing
NS_FILE_SHARING = 'http://gajim.org/protocol/filesharing'
NS_FILE_SHARING = 'urn:xmpp:fis:0'
class Protocol():
'''
Creates and extracts information from stanzas
'''
def __init__(self, ourjid):
# set our jid with resource
self.ourjid = ourjid
def convert_dbformat(self, records):
# Converts db output format to the one expected by the Protocol methods
formatted = []
for record in records:
r = {'name' : record[0]}
if record[-1] == 0:
r['type'] = 'file'
if record[1] != None or record[1] != '':
r['hash'] = record[1]
if record[2] != None or record[2] != '':
r['size'] = record[2]
if record[3] != None or record[3] != '':
r['desc'] = record[3]
if record[4] != None or record[4] != '':
r['date'] = record[4]
else:
r['type'] = 'directory'
formatted.append(r)
return formatted
def request(self, contact, path=None):
iq = nbxmpp.Iq(typ='get', to=contact, frm=self.ourjid)
query = iq.setQuery()
query.setNamespace(NS_FILE_SHARING)
if path:
query.setAttr('node', path)
return iq
def buildFileNode(self, file_info):
node = nbxmpp.Node(tag='file')
node.setNamespace(nbxmpp.NS_JINGLE_FILE_TRANSFER)
if not 'name' in file_info:
raise Exception("Child name is required.")
node.addChild(name='name').setData(file_info['name'])
if 'date' in file_info:
node.addChild(name='date').setData(file_info['date'])
if 'desc' in file_info:
node.addChild(name='desc').setData(file_info['desc'])
if 'size' in file_info:
node.addChild(name='size').setData(file_info['size'])
if 'hash' in file_info:
h = Hashes()
h.addHash(file_info['hash'], 'sha-1')
node.addChild(node=h)
return node
def offer(self, id_, contact, node, items):
iq = nbxmpp.Iq(typ='result', to=contact, frm=self.ourjid,
attrs={'id': id_})
query = iq.setQuery()
query.setNamespace(NS_FILE_SHARING)
if node:
query.setAttr('node', node)
for item in items:
if item['type'] == 'file':
fn = self.buildFileNode(item)
query.addChild(node=fn)
elif item['type'] == 'directory':
query.addChild(name='directory', attrs={'name': item['name']})
else:
raise Exception("Unexpected Type")
return iq
class ProtocolDispatcher(Protocol):
'''
Sends and receives stanzas
'''
def __init__(self, account, plugin):
self.account = account
self.plugin = plugin
self.conn = gajim.connections[self.account]
# get our jid with resource
self.ourjid = gajim.get_jid_from_account(self.account)
ourjid = gajim.get_jid_from_account(self.account)
Protocol.__init__(self, ourjid)
self.fsw = None
def set_window(self, window):
self.fsw = window
def request(self, contact, name=None, isFile=False):
iq = xmpp.Iq(typ='get', to=contact, frm=self.ourjid)
match = iq.addChild(name='match', namespace=NS_FILE_SHARING)
request = match.addChild(name='request')
if not isFile and name is None:
request.addChild(name='directory')
elif not isFile and name is not None:
dir_ = request.addChild(name='directory')
dir_.addChild(name='name').addData('/' + name)
elif isFile:
pass
return iq
def __buildReply(self, typ, stanza):
iq = xmpp.Iq(typ, to=stanza.getFrom(), frm=stanza.getTo(),
attrs={'id': stanza.getID()})
iq.addChild(name='match', namespace=NS_FILE_SHARING)
return iq
def set_window(self, fsw):
self.fsw = fsw
def on_request(self, stanza):
try:
fjid = helpers.get_full_jid_from_iq(stanza)
except helpers.InvalidFormat:
# A message from a non-valid JID arrived, it has been ignored.
return
if stanza.getTag('error'):
# TODO: better handle this
return
jid = gajim.get_jid_without_resource(fjid)
req = stanza.getTag('match').getTag('request')
if req.getTag('directory') and not \
req.getTag('directory').getChildren():
# We just received a toplevel directory request
files = self.plugin.database.get_toplevel_files(self.account, jid)
response = self.offer(stanza.getID(), fjid, files)
self.conn.connection.send(response)
elif req.getTag('directory') and req.getTag('directory').getTag('name'):
dir_ = req.getTag('directory').getTag('name').getData()[1:]
files = self.plugin.database.get_files_from_dir(self.account, jid, dir_)
response = self.offer(stanza.getID(), fjid, files)
self.conn.connection.send(response)
def on_offer(self, stanza):
# We just got a stanza offering files
fjid = helpers.get_full_jid_from_iq(stanza)
info = get_files_info(stanza)
if fjid not in self.fsw.browse_jid or not info:
# We weren't expecting anything from this contact, do nothing
# Or we didn't receive any offering files
return
flist = []
for f in info[0]:
flist.append(f['name'])
flist.extend(info[1])
self.fsw.browse_fref = self.fsw.add_file_list(flist, self.fsw.ts_search,
self.fsw.browse_fref,
self.fsw.browse_jid[fjid]
)
for f in info[0]:
iter_ = self.fsw.browse_fref[f['name']]
path = self.fsw.ts_search.get_path(iter_)
self.fsw.brw_file_info[path] = (f['name'], f['date'], f['size'],
f['hash'], f['desc'])
# TODO: add tooltip
'''
for f in info[0]:
r = self.fsw.browse_fref[f['name']]
path = self.fsw.ts_search.get_path(r)
# AM HERE WORKING ON THE TOOLTIP
tooltip.set_text('noooo')
self.fsw.tv_search.set_tooltip_row(tooltip, path)
'''
for dir_ in info[1]:
if dir_ not in self.fsw.empty_row_child:
parent = self.fsw.browse_fref[dir_]
row = self.fsw.ts_search.append(parent, ('',))
self.fsw.empty_row_child[dir_] = row
def handler(self, stanza):
# handles incoming match stanza
if stanza.getTag('match').getTag('offer'):
self.on_offer(stanza)
elif stanza.getTag('match').getTag('request'):
self.on_request(stanza)
def handler(self, stanza, fjid):
# handles incoming stanza
# TODO: Stanza checking
if stanza.getType() == 'get':
offer = self.on_request(stanza, fjid)
self.conn.connection.send(offer)
elif stanza.getType() == 'result':
return self.on_offer(stanza, fjid)
else:
# TODO: reply with malformed stanza error
pass
def offer(self, id_, contact, items):
iq = xmpp.Iq(typ='result', to=contact, frm=self.ourjid,
attrs={'id': id_})
match = iq.addChild(name='match', namespace=NS_FILE_SHARING)
offer = match.addChild(name='offer')
if len(items) == 0:
offer.addChild(name='directory')
def on_toplevel_request(self, stanza, fjid):
jid = get_jid_without_resource(fjid)
roots = self.plugin.database.get_toplevel_dirs(self.account, jid)
items = []
for root in roots:
items.append({'type' : 'directory',
'name' : root[0]
})
return self.offer(stanza.getID(), fjid, None, items)
def on_dir_request(self, stanza, fjid, jid, dir_):
result = self.plugin.database.get_files_from_dir(self.account, jid, dir_)
result = self.convert_dbformat(result)
return self.offer(stanza.getID(), fjid, dir_, result)
def on_request(self, stanza, fjid):
jid = get_jid_without_resource(fjid)
if stanza.getTag('error'):
# TODO: better handle this
return -1
node = stanza.getQuery().getAttr('node')
if node is None:
return self.on_toplevel_request(stanza, fjid)
result = self.plugin.database.get_file(self.account, jid, None, node)
if result == []:
return self.offer(stanza.getID(), fjid, node, result)
if result[-1] == 1:
# The peer asked for the content of a dir
reply = self.on_dir_request(self, stanza, fjid)
else:
for i in items:
# if it is a directory
if i[5] == True:
item = offer.addChild(name='directory')
name = item.addChild('name')
name.setData('/' + i[0])
else:
item = offer.addChild(name='file')
item.addChild('name').setData('/' + i[0])
if i[1] != '':
h = Hashes()
h.addHash(i[1], 'sha-1')
item.addChild(node=h)
item.addChild('size').setData(i[2])
item.addChild('desc').setData(i[3])
item.addChild('date').setData(i[4])
return iq
# The peer asked for more information on a file
# TODO: Refactor, make method to convert db format to expect file
# format
file_ = [{'type' : 'file',
'name' : result[0].split('/')[-1],
'hash' : result[1],
'size' : result[2],
'desc' : result[3],
'date' : result[4],
}]
reply = self.offer(stanza.getID(), fjid, node, file_)
return reply
def set_window(self, fsw):
self.fsw = fsw
def on_offer(self, stanza, fjid):
offered = []
query = stanza.getQuery()
for child in query.getChildren():
if child.getName() == 'directory':
offered.append({'name' : child.getAttr('name'),
'type' : 'directory'})
elif child.getName() == 'file':
attrs = {'type' : 'file'}
grandchildren = child.getChildren()
for grandchild in grandchildren:
attrs[grandchild.getName()] = grandchild.getData()
offered.append(attrs)
else:
print 'File sharing. Cant handle unknown type: ' + str(child)
return offered
def get_files_info(stanza):
# Crawls the stanza in search for file and dir structure.
files = []
dirs = []
children = stanza.getTag('match').getTag('offer').getChildren()
for c in children:
if c.getName() == 'file':
f = {'name' : \
c.getTag('name').getData()[1:] if c.getTag('name') else '',
'size' : c.getTag('size').getData() if c.getTag('size') else '',
'date' : c.getTag('date').getData() if c.getTag('date') else '',
'desc' : c.getTag('desc').getData() if c.getTag('desc') else '',
# TODO: handle different hash algo
'hash' : c.getTag('hash').getData() if c.getTag('hash') else '',
}
files.append(f)
else:
dirname = c.getTag('name')
if dirname is None:
return None
dirs.append(dirname.getData()[1:])
return (files, dirs)
def get_jid_without_resource(jid):
return jid.split('/')[0]
[info]
name: File Sharing
short_name: fshare
#version: 0.1
#version: 0.1.1
description: This plugin allows you to share folders with your peers using jingle file transfer.
authors: Jefry Lagrange <jefry.reyes@gmail.com>
homepage: www.google.com
homepage: www.gajim.org
max_gajim_version: 0.16.9
#/usr/bin/python
import unittest
from mock import Mock
import sys, os
sys.path.append(os.path.abspath(sys.path[0]) + '/../')
import fshare_protocol
import nbxmpp
class TestProtocol(unittest.TestCase):
def setUp(self):
self.protocol = fshare_protocol.Protocol('test@gajim.org/test')
def test_request(self):
iq = self.protocol.request('peer@gajim.org/test', 'documents/test2.txt')
self.assertEqual(iq.getType(), 'get')
self.assertEqual(iq.getQuery().getName(), 'query')
self.assertEqual(iq.getQuery().getNamespace(), fshare_protocol.NS_FILE_SHARING)
self.assertEqual(iq.getQuery().getAttr('node'), 'documents/test2.txt')
def test_convert_dbformat(self):
file_ = [(u'relative_path', u'hash', 999, u'description',
u'date', u'file_path', 0)]
formatted = self.protocol.convert_dbformat(file_)
self.assertNotEqual(len(formatted), 0)
for item in formatted:
self.assertEqual(type(item), type({}))
self.assertEqual(formatted[0]['type'], 'file')
def test_buildFileNode(self):
file_info = {'name' : 'test2.text',
'desc' : 'test',
'hash' : '00000',
'size' : '00000',
'type' : 'file'
}
node = self.protocol.buildFileNode(file_info)
self.assertEqual(node.getName(), 'file')
self.assertEqual(node.getNamespace(), nbxmpp.NS_JINGLE_FILE_TRANSFER)
self.assertEqual(len(node.getChildren()), 4)
def test_offer(self):
items = [ {'name' : 'test2.text',
'desc' : 'test',
'hash' : '00000',
'size' : '00000',
'type' : 'file'
},
{
'name' : 'secret docs',
'type' : 'directory'
}
]
iq = self.protocol.offer('1234', 'peer@gajim.org/test',
'documents', items)
self.assertEqual(iq.getType(), 'result')
self.assertNotEqual(iq.getID(), None)
self.assertEqual(iq.getQuery().getName(), 'query')
self.assertEqual(iq.getQuery().getNamespace(), fshare_protocol.NS_FILE_SHARING)
self.assertEqual(iq.getQuery().getAttr('node'), 'documents')
node = iq.getQuery()
self.assertEqual(len(node.getChildren()), 2)
# Mock modules
gajim = Mock()
attr = {'get_jid_from_account.return_value': 'test@gajim.org/test'}
gajim.configure_mock(**attr)
fshare_protocol.gajim = gajim
fshare_protocol.helpers = Mock()
class TestProtocolDispatcher(unittest.TestCase):
def setUp(self):
self.account = 'test@gajim.org'
self.protocol = fshare_protocol.Protocol(self.account)
testc = {self.account : Mock()}
fshare_protocol.gajim.connections = testc
database = Mock()
top_dirs = [(u'relative_path1', None, None, None, None, 1),
(u'relative_path2', None, None, None, None, 1)]
file_ = (u'file1', u'hash', 999, u'description',
u'date', u'file_path', 0)
attr = {'get_toplevel_dirs.return_value': top_dirs,
'get_file.return_value': file_,
'get_files_from_dir.return_value' : [file_, top_dirs[0]]
}
database.configure_mock(**attr)
plugin = Mock()
plugin.database = database
self.dispatcher = fshare_protocol.ProtocolDispatcher(
self.account, plugin)
def test_handler(self):
iq = self.protocol.request('peer@gajim.org/test',
'documents/test2.txt')
#offer = self.dispatcher.on_offer
request = self.dispatcher.on_request
#self.dispatcher.on_offer = Mock()
self.dispatcher.on_request = Mock()
self.dispatcher.handler(iq, 'peer@gajim.org/test')
assert(self.dispatcher.on_request.called)
self.dispatcher.on_request = request
def test_on_offer(self):
items = [ {'name' : 'test2.text',
'type' : 'file'
},
{
'name' : 'secret docs',
'type' : 'directory'
}
]
iq = self.protocol.offer('1234', 'peer@gajim.org/test',
'documents', items)
offered_files = self.dispatcher.on_offer(iq, 'peer@gajim.org/test')
self.assertEqual(len(offered_files), 2)
def test_on_dir_request(self):
iq = self.protocol.request('peer@gajim.org/test', 'documents')
response = self.dispatcher.on_dir_request(iq, 'peer@gajim.org/test',
'peer@gajim.org', 'documents')
self.assertEqual(response.getType(), 'result')
self.assertEqual(response.getQuery().getName(), 'query')
self.assertEqual(response.getQuery().getNamespace(), fshare_protocol.NS_FILE_SHARING)
self.assertEqual(response.getQuery().getAttr('node'), 'documents')
node = response.getQuery()
self.assertEqual(len(node.getChildren()), 2)
def test_on_request(self):
iq = self.protocol.request('peer@gajim.org/test','documents/file1.txt')
response = self.dispatcher.on_request(iq, 'peer@gajim.org/test')
self.assertEqual(response.getType(), 'result')
self.assertEqual(response.getQuery().getName(), 'query')
self.assertEqual(response.getQuery().getNamespace(), fshare_protocol.NS_FILE_SHARING)
self.assertEqual(response.getQuery().getAttr('node'), 'documents/file1.txt')
node = response.getQuery()
self.assertEqual(len(node.getChildren()), 1)
def test_on_toplevel_request(self):
iq = self.protocol.request('peer@gajim.org/test')
response = self.dispatcher.on_toplevel_request(iq, 'peer@gajim.org')
self.assertEqual(response.getType(), 'result')
self.assertEqual(response.getQuery().getName(), 'query')
self.assertEqual(response.getQuery().getNamespace(), fshare_protocol.NS_FILE_SHARING)
node = response.getQuery()
self.assertEqual(len(node.getChildren()), 2)
if __name__ == '__main__':
unittest.main()
......@@ -7,7 +7,7 @@
<object class="GtkTable" id="config_table">
<property name="visible">True</property>
<property name="border_width">6</property>
<property name="n_rows">2</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">7</property>
<property name="row_spacing">5</property>
......@@ -45,7 +45,7 @@
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
<property name="y_options"/>
</packing>
</child>
<child>
......@@ -59,9 +59,28 @@
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="flash_cb">
<property name="label" translatable="yes">Do not flash the LED, only switch it on.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"/>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
......
......@@ -13,11 +13,12 @@ from plugins.gui import GajimPluginConfigDialog
class FlashingKeyboard(GajimPlugin):
@log_calls('FlashingKeyboard')
def init(self):
self.description = _('Flashing keyboard led when there are unread messages.')
self.config_dialog = FlashingKeyboardPluginConfigDialog(self)
self.config_default_values = {
'command1': ("xset led named 'Scroll Lock'", ''),
'command2': ("xset -led named 'Scroll Lock'", '')}
'command1': ("xset led named 'Scroll Lock'", ''),
'command2': ("xset -led named 'Scroll Lock'", ''),
'flash': (True, ''),
}
self.is_active = None
self.timeout = 500
......@@ -35,15 +36,22 @@ class FlashingKeyboard(GajimPlugin):
if gajim.events.get_nb_systray_events():
if self.id_0:
return
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
if self.config['flash']:
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
else:
self.led_on()
self.id_0 = True
else:
if self.id_0:
gobject.source_remove(self.id_0)
if self.config['flash']:
gobject.source_remove(self.id_0)
self.id_0 = None
self.led_off()
def led_on(self):
subprocess.Popen('%s' % self.config['command1'], shell=True).wait()
gobject.timeout_add(self.timeout_off, self.led_off)
if self.config['flash']:
gobject.timeout_add(self.timeout_off, self.led_off)
return True
def led_off(self):
......@@ -54,7 +62,11 @@ class FlashingKeyboard(GajimPlugin):
gajim.events.event_added_subscribe(self.on_event_added)
gajim.events.event_removed_subscribe(self.on_event_removed)
if gajim.events.get_nb_systray_events():
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
if self.config['flash']:
self.id_0 = gobject.timeout_add(self.timeout, self.led_on)
else:
self.led_on()
self.id_0 = True
@log_calls('FlashingKeyboard')
def deactivate(self):
......@@ -62,6 +74,7 @@ class FlashingKeyboard(GajimPlugin):
gajim.events.event_removed_unsubscribe(self.on_event_removed)
if self.id_0:
gobject.source_remove(self.id_0)
self.led_off()
class FlashingKeyboardPluginConfigDialog(GajimPluginConfigDialog):
......@@ -81,14 +94,19 @@ class FlashingKeyboardPluginConfigDialog(GajimPluginConfigDialog):
self.isactive = self.plugin.active
if self.plugin.active:
gajim.plugin_manager.deactivate_plugin(self.plugin)
for name in self.plugin.config_default_values:
for name in ('command1', 'command2'):
widget = self.xml.get_object(name)
widget.set_text(self.plugin.config[name])
widget = self.xml.get_object('flash_cb')
widget.set_active(not self.plugin.config['flash'])
def on_close_button_clicked(self, widget):
widget = self.xml.get_object('command1')
self.plugin.config['command1'] = widget.get_text()
widget = self.xml.get_object('command2')
self.plugin.config['command2'] = widget.get_text()
widget = self.xml.get_object('flash_cb')
self.plugin.config['flash'] = not widget.get_active()
if self.isactive:
gajim.plugin_manager.activate_plugin(self.plugin)
self.hide()
[info]
name: Flashing Keyboard
short_name: flashing_keyboard
version: 0.1.3
version: 0.1.5
description: Flashing keyboard led when there are unread messages.
authors: Denis Fomin <fominde@gmail.com>
homepage: http://trac-plugins.gajim.org/wiki/FlashingKeyboardPlugin
max_gajim_version: 0.16.9
[info]
name: GNOME SessionManager
short_name: gnome_session_manager
version: 0.1.2
version: 0.1.3
description: Set and react on GNOME Session presence settings
authors: Philippe Normand <phil@base-art.net>
homepage: http://base-art.net
max_gajim_version: 0.16.9
......@@ -32,7 +32,6 @@ class GnomeSessionManagerPlugin(GajimPlugin):
@log_calls('GnomeSessionManagerPlugin')
def init(self):
self.description = _('Set and react on GNOME Session presence settings')
self.config_dialog = None
self.events_handlers = {}
......
[info]
name: Google Translation
short_name: google_translation
version: 0.3.1
version: 0.3.2
description: Translates (currently only incoming) messages using Google Translate.
authors = Mateusz Biliński <mateusz@bilinski.it>
mrDoctorWho <mrdoctorwho@gmail.com>
homepage = http://trac-plugins.gajim.org/wiki/GoogleTranslationPlugin
max_gajim_version: 0.16.9
......@@ -109,8 +109,6 @@ class GoogleTranslationPlugin(GajimPlugin):
@log_calls('GoogleTranslationPlugin')
def init(self):
self.description = _('Translates (currently only incoming)'
'messages using Google Translate.')
self.config_dialog = None
self.config_default_values = {
......
[info]
name: Off-The-Record Encryption
short_name: gotr
version: 1.7.1
description: Provide OTR encryption
version: 1.9.6
description: Provide OTR encryption. Read <a href="https://github.com/python-otr/gajim-otr/wiki">https://github.com/python-otr/gajim-otr/wiki</a> before use.
authors: Kjell Braden <afflux.gajim@pentabarf.de>
homepage: http://gajim-otr.pentabarf.de
max_gajim_version: 0.16.9