diff --git a/src/chat_control.py b/src/chat_control.py index 5f0801afc..9c5e33efa 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -34,6 +34,8 @@ from message_textview import MessageTextView from common.contacts import GC_Contact from common.logger import Constants constants = Constants() +from rst_xhtml_generator import create_xhtml +from common.xmpp.protocol import NS_XHTML try: import gtkspell @@ -1251,6 +1253,8 @@ class ChatControl(ChatControlBase): else: kind = 'outgoing' name = gajim.nicks[self.account] + if not xhtml and not encrypted and gajim.config.get('rst_formatting_outgoing_messages'): + xhtml = '%s' % (NS_XHTML, create_xhtml(text)) ChatControlBase.print_conversation_line(self, text, kind, name, tim, subject = subject, old_kind = self.old_msg_kind, xhtml = xhtml) if text.startswith('/me ') or text.startswith('/me\n'): diff --git a/src/common/config.py b/src/common/config.py index cab7c4dee..6bcbbc43e 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -91,6 +91,8 @@ class Config: _('Treat * / _ pairs as possible formatting characters.'), True], 'show_ascii_formatting_chars': [ opt_bool, True , _('If True, do not ' 'remove */_ . So *abc* will be bold but with * * not removed.')], + 'rst_formatting_outgoing_messages': [ opt_bool, False, + _('Uses ReStructured text markup for HTML, plus ascii formatting if selected.')], 'sounds_on': [ opt_bool, True ], # 'aplay', 'play', 'esdplay', 'artsplay' detected first time only 'soundplayer': [ opt_str, '' ], diff --git a/src/common/connection.py b/src/common/connection.py index 803fca602..47f27ad4a 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -652,7 +652,6 @@ class Connection(ConnectionHandlers): p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed) if self.connection: self.connection.send(p) - self.priority = priority self.dispatch('STATUS', show) def _on_disconnected(self): @@ -677,6 +676,8 @@ class Connection(ConnectionHandlers): user_nick = None, xhtml = None): if not self.connection: return + if not xhtml and gajim.config.get('rst_formatting_outgoing_messages'): + xhtml = create_xhtml(msg) if not msg and chatstate is None: return fjid = jid @@ -694,6 +695,10 @@ class Connection(ConnectionHandlers): # one in locale and one en msgtxt = _('[This message is *encrypted* (See :JEP:`27`]') +\ ' ([This message is *encrypted* (See :JEP:`27`])' + if msgtxt and not xhtml and gajim.config.get( + 'rst_formatting_outgoing_messages'): + # Generate a XHTML part using reStructured text markup + xhtml = create_xhtml(msgtxt) if type == 'chat': msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, typ = type, xhtml = xhtml) @@ -987,6 +992,8 @@ class Connection(ConnectionHandlers): def send_gc_message(self, jid, msg, xhtml = None): if not self.connection: return + if not xhtml and gajim.config.get('rst_formatting_outgoing_messages'): + xhtml = create_xhtml(msg) msg_iq = common.xmpp.Message(jid, msg, typ = 'groupchat', xhtml = xhtml) self.connection.send(msg_iq) self.dispatch('MSGSENT', (jid, msg)) diff --git a/src/rst_xhtml_generator.py b/src/rst_xhtml_generator.py new file mode 100644 index 000000000..4e2fddba8 --- /dev/null +++ b/src/rst_xhtml_generator.py @@ -0,0 +1,100 @@ +from docutils import io +from docutils.core import Publisher +from docutils.parsers.rst import roles + +def jep_reference_role(role, rawtext, text, lineno, inliner, + options={}, content=[]): + """Role to make handy references to Jabber Enhancement Proposals (JEP). + + Use as :JEP:`71` (or jep, or jep-reference). + Modeled after the sample in docutils documentation. + """ + from docutils import nodes,utils + from docutils.parsers.rst.roles import set_classes + + jep_base_url = 'http://www.jabber.org/jeps/' + jep_url = 'jep-%04d.html' + try: + jepnum = int(text) + if jepnum <= 0: + raise ValueError + except ValueError: + msg = inliner.reporter.error( + 'JEP number must be a number greater than or equal to 1; ' + '"%s" is invalid.' % text, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + ref = jep_base_url + jep_url % jepnum + set_classes(options) + node = nodes.reference(rawtext, 'JEP ' + utils.unescape(text), refuri=ref, + **options) + return [node], [] + +roles.register_canonical_role('jep-reference', jep_reference_role) +from docutils.parsers.rst.languages.en import roles +roles['jep-reference'] = 'jep-reference' +roles['jep'] = 'jep-reference' + +class HTMLGenerator: + """Really simple HTMLGenerator starting from publish_parts. + + It reuses the docutils.core.Publisher class, which means it is *not* + threadsafe. + """ + def __init__(self, + settings_spec=None, + settings_overrides=dict(report_level=5, halt_level=5), + config_section='general'): + self.pub = Publisher(reader=None, parser=None, writer=None, + settings=None, + source_class=io.StringInput, + destination_class=io.StringOutput) + self.pub.set_components(reader_name='standalone', + parser_name='restructuredtext', + writer_name='html') + #hack: JEP-0071 does not allow HTML char entities, so we hack our way out of it. + # — == u"\u2014" + # a setting to only emit charater entities in the writer would be nice + # TODO: several   are emitted, and they are explicitly forbidden in the JEP + #   == u"\u00a0" + self.pub.writer.translator_class.attribution_formats["dash"] = (u"\u2014", "") + self.pub.process_programmatic_settings(settings_spec, + settings_overrides, + config_section) + + + def create_xhtml(self, text, + destination=None, + destination_path=None, + enable_exit_status=None): + """ Create xhtml for a fragment of IM dialog. + We can use the source_name to store info about + the message.""" + self.pub.set_source(text, None) + self.pub.set_destination(destination, destination_path) + output = self.pub.publish(enable_exit_status=enable_exit_status) + #kludge until we can get docutils to stop generating (rare)   entities + return u'\u00a0'.join(self.pub.writer.parts['fragment'].strip().split(' ')) + +Generator = HTMLGenerator() + +def create_xhtml(text): + return Generator.create_xhtml(text) + +if __name__ == '__main__': + print Generator.create_xhtml(""" +test:: + + >>> print 1 + 1 + +*I* like it. It is for :JEP:`71` + +this `` should trigger`` should trigger the   problem. + +""") + print Generator.create_xhtml(""" +*test1 + +test2_ +""")