merge default branch to jingle
This commit is contained in:
		
						commit
						6b99db8980
					
				
					 25 changed files with 1662 additions and 619 deletions
				
			
		| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
AC_INIT([Gajim - A Jabber Instant Messager],
 | 
					AC_INIT([Gajim - A Jabber Instant Messager],
 | 
				
			||||||
		[0.12.5.1-dev],[http://trac.gajim.org/],[gajim])
 | 
							[0.12.5.2-dev],[http://trac.gajim.org/],[gajim])
 | 
				
			||||||
AC_PREREQ([2.59])
 | 
					AC_PREREQ([2.59])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AC_CONFIG_HEADER(config.h)
 | 
					AC_CONFIG_HEADER(config.h)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
					<?xml version="1.0"?>
 | 
				
			||||||
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
 | 
					 | 
				
			||||||
<!--*- mode: xml -*-->
 | 
					 | 
				
			||||||
<glade-interface>
 | 
					<glade-interface>
 | 
				
			||||||
 | 
					  <!-- interface-requires gtk+ 2.14 -->
 | 
				
			||||||
 | 
					  <!-- interface-naming-policy toplevel-contextual -->
 | 
				
			||||||
  <widget class="GtkWindow" id="service_discovery_window">
 | 
					  <widget class="GtkWindow" id="service_discovery_window">
 | 
				
			||||||
    <property name="border_width">6</property>
 | 
					    <property name="border_width">6</property>
 | 
				
			||||||
    <property name="role">Service Discovery</property>
 | 
					    <property name="role">Service Discovery</property>
 | 
				
			||||||
    <property name="default_width">450</property>
 | 
					    <property name="default_width">550</property>
 | 
				
			||||||
    <property name="default_height">420</property>
 | 
					    <property name="default_height">420</property>
 | 
				
			||||||
    <signal name="destroy" handler="on_service_discovery_window_destroy"/>
 | 
					    <signal name="destroy" handler="on_service_discovery_window_destroy"/>
 | 
				
			||||||
    <child>
 | 
					    <child>
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,9 @@
 | 
				
			||||||
Agent JID - node</property>
 | 
					Agent JID - node</property>
 | 
				
			||||||
                    <property name="use_markup">True</property>
 | 
					                    <property name="use_markup">True</property>
 | 
				
			||||||
                  </widget>
 | 
					                  </widget>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="position">0</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
                </child>
 | 
					                </child>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
                  <widget class="GtkImage" id="banner_agent_icon">
 | 
					                  <widget class="GtkImage" id="banner_agent_icon">
 | 
				
			||||||
| 
						 | 
					@ -47,6 +50,7 @@ Agent JID - node</property>
 | 
				
			||||||
          <packing>
 | 
					          <packing>
 | 
				
			||||||
            <property name="expand">False</property>
 | 
					            <property name="expand">False</property>
 | 
				
			||||||
            <property name="fill">False</property>
 | 
					            <property name="fill">False</property>
 | 
				
			||||||
 | 
					            <property name="position">0</property>
 | 
				
			||||||
          </packing>
 | 
					          </packing>
 | 
				
			||||||
        </child>
 | 
					        </child>
 | 
				
			||||||
        <child>
 | 
					        <child>
 | 
				
			||||||
| 
						 | 
					@ -55,22 +59,12 @@ Agent JID - node</property>
 | 
				
			||||||
            <property name="n_rows">3</property>
 | 
					            <property name="n_rows">3</property>
 | 
				
			||||||
            <property name="n_columns">3</property>
 | 
					            <property name="n_columns">3</property>
 | 
				
			||||||
            <property name="column_spacing">6</property>
 | 
					            <property name="column_spacing">6</property>
 | 
				
			||||||
            <child>
 | 
					 | 
				
			||||||
              <placeholder/>
 | 
					 | 
				
			||||||
            </child>
 | 
					 | 
				
			||||||
            <child>
 | 
					 | 
				
			||||||
              <placeholder/>
 | 
					 | 
				
			||||||
            </child>
 | 
					 | 
				
			||||||
            <child>
 | 
					            <child>
 | 
				
			||||||
              <widget class="GtkComboBoxEntry" id="address_comboboxentry">
 | 
					              <widget class="GtkComboBoxEntry" id="address_comboboxentry">
 | 
				
			||||||
                <property name="visible">True</property>
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
                <property name="items" translatable="yes"></property>
 | 
					                <property name="items" translatable="yes"></property>
 | 
				
			||||||
                <signal name="changed" handler="on_address_comboboxentry_changed"/>
 | 
					                <signal name="changed" handler="on_address_comboboxentry_changed"/>
 | 
				
			||||||
                <signal name="key_press_event" handler="on_address_comboboxentry_key_press_event"/>
 | 
					                <signal name="key_press_event" handler="on_address_comboboxentry_key_press_event"/>
 | 
				
			||||||
                <child internal-child="entry">
 | 
					 | 
				
			||||||
                  <widget class="GtkEntry" id="comboboxentry-entry1">
 | 
					 | 
				
			||||||
                  </widget>
 | 
					 | 
				
			||||||
                </child>
 | 
					 | 
				
			||||||
              </widget>
 | 
					              </widget>
 | 
				
			||||||
              <packing>
 | 
					              <packing>
 | 
				
			||||||
                <property name="left_attach">1</property>
 | 
					                <property name="left_attach">1</property>
 | 
				
			||||||
| 
						 | 
					@ -86,7 +80,7 @@ Agent JID - node</property>
 | 
				
			||||||
                <property name="can_focus">True</property>
 | 
					                <property name="can_focus">True</property>
 | 
				
			||||||
                <property name="can_default">True</property>
 | 
					                <property name="can_default">True</property>
 | 
				
			||||||
                <property name="has_default">True</property>
 | 
					                <property name="has_default">True</property>
 | 
				
			||||||
                <property name="response_id">0</property>
 | 
					                <property name="receives_default">False</property>
 | 
				
			||||||
                <signal name="clicked" handler="on_go_button_clicked"/>
 | 
					                <signal name="clicked" handler="on_go_button_clicked"/>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
                  <widget class="GtkAlignment" id="alignment93">
 | 
					                  <widget class="GtkAlignment" id="alignment93">
 | 
				
			||||||
| 
						 | 
					@ -105,6 +99,7 @@ Agent JID - node</property>
 | 
				
			||||||
                          <packing>
 | 
					                          <packing>
 | 
				
			||||||
                            <property name="expand">False</property>
 | 
					                            <property name="expand">False</property>
 | 
				
			||||||
                            <property name="fill">False</property>
 | 
					                            <property name="fill">False</property>
 | 
				
			||||||
 | 
					                            <property name="position">0</property>
 | 
				
			||||||
                          </packing>
 | 
					                          </packing>
 | 
				
			||||||
                        </child>
 | 
					                        </child>
 | 
				
			||||||
                        <child>
 | 
					                        <child>
 | 
				
			||||||
| 
						 | 
					@ -145,6 +140,12 @@ Agent JID - node</property>
 | 
				
			||||||
                <property name="y_options"></property>
 | 
					                <property name="y_options"></property>
 | 
				
			||||||
              </packing>
 | 
					              </packing>
 | 
				
			||||||
            </child>
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <placeholder/>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <placeholder/>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
          </widget>
 | 
					          </widget>
 | 
				
			||||||
          <packing>
 | 
					          <packing>
 | 
				
			||||||
            <property name="expand">False</property>
 | 
					            <property name="expand">False</property>
 | 
				
			||||||
| 
						 | 
					@ -155,9 +156,9 @@ Agent JID - node</property>
 | 
				
			||||||
          <widget class="GtkScrolledWindow" id="services_scrollwin">
 | 
					          <widget class="GtkScrolledWindow" id="services_scrollwin">
 | 
				
			||||||
            <property name="visible">True</property>
 | 
					            <property name="visible">True</property>
 | 
				
			||||||
            <property name="can_focus">True</property>
 | 
					            <property name="can_focus">True</property>
 | 
				
			||||||
            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
					            <property name="hscrollbar_policy">automatic</property>
 | 
				
			||||||
            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
					            <property name="vscrollbar_policy">automatic</property>
 | 
				
			||||||
            <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
 | 
					            <property name="shadow_type">etched-in</property>
 | 
				
			||||||
            <child>
 | 
					            <child>
 | 
				
			||||||
              <widget class="GtkTreeView" id="services_treeview">
 | 
					              <widget class="GtkTreeView" id="services_treeview">
 | 
				
			||||||
                <property name="visible">True</property>
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
| 
						 | 
					@ -184,6 +185,7 @@ Agent JID - node</property>
 | 
				
			||||||
              <packing>
 | 
					              <packing>
 | 
				
			||||||
                <property name="expand">False</property>
 | 
					                <property name="expand">False</property>
 | 
				
			||||||
                <property name="fill">False</property>
 | 
					                <property name="fill">False</property>
 | 
				
			||||||
 | 
					                <property name="position">0</property>
 | 
				
			||||||
              </packing>
 | 
					              </packing>
 | 
				
			||||||
            </child>
 | 
					            </child>
 | 
				
			||||||
            <child>
 | 
					            <child>
 | 
				
			||||||
| 
						 | 
					@ -200,19 +202,20 @@ Agent JID - node</property>
 | 
				
			||||||
                <property name="spacing">6</property>
 | 
					                <property name="spacing">6</property>
 | 
				
			||||||
                <child>
 | 
					                <child>
 | 
				
			||||||
                  <widget class="GtkButton" id="close_button">
 | 
					                  <widget class="GtkButton" id="close_button">
 | 
				
			||||||
 | 
					                    <property name="label">gtk-close</property>
 | 
				
			||||||
                    <property name="visible">True</property>
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
                    <property name="can_focus">True</property>
 | 
					                    <property name="can_focus">True</property>
 | 
				
			||||||
                    <property name="has_focus">True</property>
 | 
					                    <property name="has_focus">True</property>
 | 
				
			||||||
                    <property name="can_default">True</property>
 | 
					                    <property name="can_default">True</property>
 | 
				
			||||||
                    <property name="label">gtk-close</property>
 | 
					                    <property name="receives_default">False</property>
 | 
				
			||||||
                    <property name="use_stock">True</property>
 | 
					                    <property name="use_stock">True</property>
 | 
				
			||||||
                    <property name="response_id">0</property>
 | 
					 | 
				
			||||||
                    <signal name="clicked" handler="on_close_button_clicked"/>
 | 
					                    <signal name="clicked" handler="on_close_button_clicked"/>
 | 
				
			||||||
                  </widget>
 | 
					                  </widget>
 | 
				
			||||||
                  <packing>
 | 
					                  <packing>
 | 
				
			||||||
                    <property name="expand">False</property>
 | 
					                    <property name="expand">False</property>
 | 
				
			||||||
                    <property name="fill">False</property>
 | 
					                    <property name="fill">False</property>
 | 
				
			||||||
                    <property name="pack_type">GTK_PACK_END</property>
 | 
					                    <property name="pack_type">end</property>
 | 
				
			||||||
 | 
					                    <property name="position">0</property>
 | 
				
			||||||
                  </packing>
 | 
					                  </packing>
 | 
				
			||||||
                </child>
 | 
					                </child>
 | 
				
			||||||
              </widget>
 | 
					              </widget>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
#!/bin/sh
 | 
					#!/bin/sh
 | 
				
			||||||
cd "$(dirname $0)/src"
 | 
					cd "$(dirname $0)/src"
 | 
				
			||||||
exec python -OOt gajim.py $@
 | 
					exec python -Ot gajim.py $@
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,12 +41,17 @@ gajimsrc3dir = $(pkgdatadir)/src/common/zeroconf
 | 
				
			||||||
gajimsrc3_PYTHON = \
 | 
					gajimsrc3_PYTHON = \
 | 
				
			||||||
				$(srcdir)/common/zeroconf/*.py
 | 
									$(srcdir)/common/zeroconf/*.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gajimsrc4dir = $(pkgdatadir)/src/commands
 | 
				
			||||||
 | 
					gajimsrc4_PYTHON = \
 | 
				
			||||||
 | 
									   $(srcdir)/commands/*.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DISTCLEANFILES =
 | 
					DISTCLEANFILES =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = $(gajimsrc_PYTHON) \
 | 
					EXTRA_DIST = $(gajimsrc_PYTHON) \
 | 
				
			||||||
			$(gajimsrc1_PYTHON) \
 | 
								$(gajimsrc1_PYTHON) \
 | 
				
			||||||
			$(gajimsrc2_PYTHON) \
 | 
								$(gajimsrc2_PYTHON) \
 | 
				
			||||||
			$(gajimsrc3_PYTHON) \
 | 
								$(gajimsrc3_PYTHON) \
 | 
				
			||||||
 | 
								$(gajimsrc4_PYTHON) \
 | 
				
			||||||
			eggtrayicon.c \
 | 
								eggtrayicon.c \
 | 
				
			||||||
			trayiconmodule.c \
 | 
								trayiconmodule.c \
 | 
				
			||||||
			eggtrayicon.h \
 | 
								eggtrayicon.h \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,8 @@ from common.xmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC
 | 
				
			||||||
from common.xmpp.protocol import NS_RECEIPTS, NS_ESESSION
 | 
					from common.xmpp.protocol import NS_RECEIPTS, NS_ESESSION
 | 
				
			||||||
from common.xmpp.protocol import NS_JINGLE_RTP_AUDIO
 | 
					from common.xmpp.protocol import NS_JINGLE_RTP_AUDIO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from commands.implementation import CommonCommands, ChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
	import gtkspell
 | 
						import gtkspell
 | 
				
			||||||
	HAS_GTK_SPELL = True
 | 
						HAS_GTK_SPELL = True
 | 
				
			||||||
| 
						 | 
					@ -75,11 +77,15 @@ if gajim.config.get('use_speller') and HAS_GTK_SPELL:
 | 
				
			||||||
			spell.set_language(langs[lang])
 | 
								spell.set_language(langs[lang])
 | 
				
			||||||
		except OSError:
 | 
							except OSError:
 | 
				
			||||||
			del langs[lang]
 | 
								del langs[lang]
 | 
				
			||||||
 | 
						if spell:
 | 
				
			||||||
 | 
							spell.detach()
 | 
				
			||||||
 | 
						del tv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
class ChatControlBase(MessageControl):
 | 
					class ChatControlBase(MessageControl, CommonCommands):
 | 
				
			||||||
	'''A base class containing a banner, ConversationTextview, MessageTextView
 | 
						'''A base class containing a banner, ConversationTextview, MessageTextView
 | 
				
			||||||
	'''
 | 
						'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def make_href(self, match):
 | 
						def make_href(self, match):
 | 
				
			||||||
		url_color = gajim.config.get('urlmsgcolor')
 | 
							url_color = gajim.config.get('urlmsgcolor')
 | 
				
			||||||
		return '<a href="%s"><span color="%s">%s</span></a>' % (match.group(),
 | 
							return '<a href="%s"><span color="%s">%s</span></a>' % (match.group(),
 | 
				
			||||||
| 
						 | 
					@ -146,7 +152,54 @@ class ChatControlBase(MessageControl):
 | 
				
			||||||
	event_keymod):
 | 
						event_keymod):
 | 
				
			||||||
		# Derived should implement this rather than connecting to the event
 | 
							# Derived should implement this rather than connecting to the event
 | 
				
			||||||
		# itself.
 | 
							# itself.
 | 
				
			||||||
		pass
 | 
					
 | 
				
			||||||
 | 
							event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
 | 
				
			||||||
 | 
							event.keyval = event_keyval
 | 
				
			||||||
 | 
							event.state = event_keymod
 | 
				
			||||||
 | 
							event.time = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							buffer = widget.get_buffer()
 | 
				
			||||||
 | 
							start, end = buffer.get_bounds()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if event.keyval -- gtk.keysyms.Tab:
 | 
				
			||||||
 | 
								position = buffer.get_insert()
 | 
				
			||||||
 | 
								end = buffer.get_iter_at_mark(position)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								text = buffer.get_text(start, end, False)
 | 
				
			||||||
 | 
								text = text.decode('utf8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								splitted = text.split()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (text.startswith(self.COMMAND_PREFIX) and not
 | 
				
			||||||
 | 
									text.startswith(self.COMMAND_PREFIX * 2) and len(splitted) == 1):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									text = splitted[0]
 | 
				
			||||||
 | 
									bare = text.lstrip(self.COMMAND_PREFIX)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if len(text) == 1:
 | 
				
			||||||
 | 
										self.command_hits = []
 | 
				
			||||||
 | 
										for command in self.list_commands():
 | 
				
			||||||
 | 
											for name in command.names:
 | 
				
			||||||
 | 
												self.command_hits.append(name)
 | 
				
			||||||
 | 
									else:
 | 
				
			||||||
 | 
										if (self.last_key_tabs and self.command_hits and
 | 
				
			||||||
 | 
											self.command_hits[0].startswith(bare)):
 | 
				
			||||||
 | 
											self.command_hits.append(self.command_hits.pop(0))
 | 
				
			||||||
 | 
										else:
 | 
				
			||||||
 | 
											self.command_hits = []
 | 
				
			||||||
 | 
											for command in self.list_commands():
 | 
				
			||||||
 | 
												for name in command.names:
 | 
				
			||||||
 | 
													if name.startswith(bare):
 | 
				
			||||||
 | 
														self.command_hits.append(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if self.command_hits:
 | 
				
			||||||
 | 
										buffer.delete(start, end)
 | 
				
			||||||
 | 
										buffer.insert_at_cursor(self.COMMAND_PREFIX + self.command_hits[0] + ' ')
 | 
				
			||||||
 | 
										self.last_key_tabs = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								self.last_key_tabs = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def status_url_clicked(self, widget, url):
 | 
						def status_url_clicked(self, widget, url):
 | 
				
			||||||
		helpers.launch_browser_mailer('url', url)
 | 
							helpers.launch_browser_mailer('url', url)
 | 
				
			||||||
| 
						 | 
					@ -303,6 +356,9 @@ class ChatControlBase(MessageControl):
 | 
				
			||||||
		self.smooth = True
 | 
							self.smooth = True
 | 
				
			||||||
		self.msg_textview.grab_focus()
 | 
							self.msg_textview.grab_focus()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							self.command_hits = []
 | 
				
			||||||
 | 
							self.last_key_tabs = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def set_speller(self):
 | 
						def set_speller(self):
 | 
				
			||||||
		# now set the one the user selected
 | 
							# now set the one the user selected
 | 
				
			||||||
		per_type = 'contacts'
 | 
							per_type = 'contacts'
 | 
				
			||||||
| 
						 | 
					@ -604,45 +660,27 @@ class ChatControlBase(MessageControl):
 | 
				
			||||||
			self.drag_entered_conv = True
 | 
								self.drag_entered_conv = True
 | 
				
			||||||
			self.conv_textview.tv.set_editable(True)
 | 
								self.conv_textview.tv.set_editable(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def _process_command(self, message):
 | 
					 | 
				
			||||||
		if not message or message[0] != '/':
 | 
					 | 
				
			||||||
			return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		message = message[1:]
 | 
					 | 
				
			||||||
		message_array = message.split(' ', 1)
 | 
					 | 
				
			||||||
		command = message_array.pop(0).lower()
 | 
					 | 
				
			||||||
		if message_array == ['']:
 | 
					 | 
				
			||||||
			message_array = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if command == 'clear' and not len(message_array):
 | 
					 | 
				
			||||||
			self.conv_textview.clear() # clear conversation
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview) # clear message textview too
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif message == 'compact' and not len(message_array):
 | 
					 | 
				
			||||||
			self.chat_buttons_set_visible(not self.hide_chat_buttons)
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	def send_message(self, message, keyID='', type_='chat', chatstate=None,
 | 
						def send_message(self, message, keyID='', type_='chat', chatstate=None,
 | 
				
			||||||
	msg_id=None, composing_xep=None, resource=None, process_command=True,
 | 
						msg_id=None, composing_xep=None, resource=None,
 | 
				
			||||||
	xhtml=None, callback=None, callback_args=[]):
 | 
						xhtml=None, callback=None, callback_args=[], process_commands=True):
 | 
				
			||||||
		'''Send the given message to the active tab. Doesn't return None if error
 | 
							'''Send the given message to the active tab. Doesn't return None if error
 | 
				
			||||||
		'''
 | 
							'''
 | 
				
			||||||
		if not message or message == '\n':
 | 
							if not message or message == '\n':
 | 
				
			||||||
			return None
 | 
								return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if not process_command or not self._process_command(message):
 | 
							if process_commands and self.process_as_command(message):
 | 
				
			||||||
			MessageControl.send_message(self, message, keyID, type_=type_,
 | 
								return
 | 
				
			||||||
				chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
 | 
					 | 
				
			||||||
				resource=resource, user_nick=self.user_nick, xhtml=xhtml,
 | 
					 | 
				
			||||||
				callback=callback, callback_args=callback_args)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			# Record message history
 | 
							MessageControl.send_message(self, message, keyID, type_=type_,
 | 
				
			||||||
			self.save_sent_message(message)
 | 
								chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
 | 
				
			||||||
 | 
								resource=resource, user_nick=self.user_nick, xhtml=xhtml,
 | 
				
			||||||
 | 
								callback=callback, callback_args=callback_args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			# Be sure to send user nickname only once according to JEP-0172
 | 
							# Record message history
 | 
				
			||||||
			self.user_nick = None
 | 
							self.save_sent_message(message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							# Be sure to send user nickname only once according to JEP-0172
 | 
				
			||||||
 | 
							self.user_nick = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		# Clear msg input
 | 
							# Clear msg input
 | 
				
			||||||
		message_buffer = self.msg_textview.get_buffer()
 | 
							message_buffer = self.msg_textview.get_buffer()
 | 
				
			||||||
| 
						 | 
					@ -1126,7 +1164,7 @@ class ChatControlBase(MessageControl):
 | 
				
			||||||
		# FIXME: Set sensitivity for toolbar
 | 
							# FIXME: Set sensitivity for toolbar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
class ChatControl(ChatControlBase):
 | 
					class ChatControl(ChatControlBase, ChatCommands):
 | 
				
			||||||
	'''A control for standard 1-1 chat'''
 | 
						'''A control for standard 1-1 chat'''
 | 
				
			||||||
	(
 | 
						(
 | 
				
			||||||
		AUDIO_STATE_NOT_AVAILABLE,
 | 
							AUDIO_STATE_NOT_AVAILABLE,
 | 
				
			||||||
| 
						 | 
					@ -1139,7 +1177,8 @@ class ChatControl(ChatControlBase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TYPE_ID = message_control.TYPE_CHAT
 | 
						TYPE_ID = message_control.TYPE_CHAT
 | 
				
			||||||
	old_msg_kind = None # last kind of the printed message
 | 
						old_msg_kind = None # last kind of the printed message
 | 
				
			||||||
	CHAT_CMDS = ['clear', 'compact', 'help', 'me', 'ping', 'say']
 | 
					
 | 
				
			||||||
 | 
						DISPATCHED_BY = ChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def __init__(self, parent_win, contact, acct, session, resource = None):
 | 
						def __init__(self, parent_win, contact, acct, session, resource = None):
 | 
				
			||||||
		ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
 | 
							ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
 | 
				
			||||||
| 
						 | 
					@ -1490,6 +1529,19 @@ class ChatControl(ChatControlBase):
 | 
				
			||||||
			self._audio_image.set_from_stock(gtk.STOCK_DIALOG_WARNING, 1)
 | 
								self._audio_image.set_from_stock(gtk.STOCK_DIALOG_WARNING, 1)
 | 
				
			||||||
		self.update_toolbar()
 | 
							self.update_toolbar()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def change_resource(self, resource):
 | 
				
			||||||
 | 
							old_full_jid = self.get_full_jid()
 | 
				
			||||||
 | 
							self.resource = resource
 | 
				
			||||||
 | 
							new_full_jid = self.get_full_jid()
 | 
				
			||||||
 | 
							# update gajim.last_message_time
 | 
				
			||||||
 | 
							if old_full_jid in gajim.last_message_time[self.account]:
 | 
				
			||||||
 | 
								gajim.last_message_time[self.account][new_full_jid] = \
 | 
				
			||||||
 | 
									gajim.last_message_time[self.account][old_full_jid]
 | 
				
			||||||
 | 
							# update events
 | 
				
			||||||
 | 
							gajim.events.change_jid(self.account, old_full_jid, new_full_jid)
 | 
				
			||||||
 | 
							# update MessageWindow._controls
 | 
				
			||||||
 | 
							self.parent_win.change_jid(self.account, old_full_jid, new_full_jid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def set_audio_state(self, state, sid=None, reason=None):
 | 
						def set_audio_state(self, state, sid=None, reason=None):
 | 
				
			||||||
		if state in ('connecting', 'connected', 'stop'):
 | 
							if state in ('connecting', 'connected', 'stop'):
 | 
				
			||||||
			str = _('Audio state : %s') % state
 | 
								str = _('Audio state : %s') % state
 | 
				
			||||||
| 
						 | 
					@ -1813,83 +1865,12 @@ class ChatControl(ChatControlBase):
 | 
				
			||||||
		elif self.session and self.session.enable_encryption:
 | 
							elif self.session and self.session.enable_encryption:
 | 
				
			||||||
			dialogs.ESessionInfoWindow(self.session)
 | 
								dialogs.ESessionInfoWindow(self.session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def _process_command(self, message):
 | 
						def send_message(self, message, keyID='', chatstate=None, xhtml=None,
 | 
				
			||||||
		if message[0] != '/':
 | 
								process_commands=True):
 | 
				
			||||||
			return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		# Handle common commands
 | 
					 | 
				
			||||||
		if ChatControlBase._process_command(self, message):
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		message = message[1:]
 | 
					 | 
				
			||||||
		message_array = message.split(' ', 1)
 | 
					 | 
				
			||||||
		command = message_array.pop(0).lower()
 | 
					 | 
				
			||||||
		if message_array == ['']:
 | 
					 | 
				
			||||||
			message_array = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if command == 'me':
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				return False # /me is not really a command
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
				return True # do not send "/me" as message
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if command == 'help':
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				subcommand = message_array.pop(0)
 | 
					 | 
				
			||||||
				self.get_command_help(subcommand)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'ping':
 | 
					 | 
				
			||||||
			if not len(message_array):
 | 
					 | 
				
			||||||
				if self.account == gajim.ZEROCONF_ACC_NAME:
 | 
					 | 
				
			||||||
					self.print_conversation(
 | 
					 | 
				
			||||||
						_('Command not supported for zeroconf account.'), 'info')
 | 
					 | 
				
			||||||
				else:
 | 
					 | 
				
			||||||
					gajim.connections[self.account].sendPing(self.contact)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	def get_command_help(self, command):
 | 
					 | 
				
			||||||
		if command == 'help':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Commands: %s') % ChatControl.CHAT_CMDS,
 | 
					 | 
				
			||||||
				'info')
 | 
					 | 
				
			||||||
		elif command == 'clear':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s, clears the text window.') % \
 | 
					 | 
				
			||||||
				command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'compact':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s, hide the chat buttons.') % \
 | 
					 | 
				
			||||||
				command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'me':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%(command)s <action>, sends action '
 | 
					 | 
				
			||||||
				'to the current group chat. Use third person. (e.g. /%(command)s '
 | 
					 | 
				
			||||||
				'explodes.)'
 | 
					 | 
				
			||||||
				) % {'command': command}, 'info')
 | 
					 | 
				
			||||||
		elif command == 'ping':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s, sends a ping to the contact') %\
 | 
					 | 
				
			||||||
				command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'say':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s, send the message to the contact') %\
 | 
					 | 
				
			||||||
				command, 'info')
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			self.print_conversation(_('No help info for /%s') % command, 'info')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	def send_message(self, message, keyID='', chatstate=None, xhtml=None):
 | 
					 | 
				
			||||||
		'''Send a message to contact'''
 | 
							'''Send a message to contact'''
 | 
				
			||||||
		if message in ('', None, '\n') or self._process_command(message):
 | 
							if message in ('', None, '\n'):
 | 
				
			||||||
			return None
 | 
								return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		# Do we need to process command for the message ?
 | 
					 | 
				
			||||||
		process_command = True
 | 
					 | 
				
			||||||
		if message.startswith('/say'):
 | 
					 | 
				
			||||||
			message = message[5:]
 | 
					 | 
				
			||||||
			process_command = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		# refresh timers
 | 
							# refresh timers
 | 
				
			||||||
		self.reset_kbd_mouse_timeout_vars()
 | 
							self.reset_kbd_mouse_timeout_vars()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1948,8 +1929,9 @@ class ChatControl(ChatControlBase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ChatControlBase.send_message(self, message, keyID, type_='chat',
 | 
							ChatControlBase.send_message(self, message, keyID, type_='chat',
 | 
				
			||||||
			chatstate=chatstate_to_send, composing_xep=composing_xep,
 | 
								chatstate=chatstate_to_send, composing_xep=composing_xep,
 | 
				
			||||||
			process_command=process_command, xhtml=xhtml, callback=_on_sent,
 | 
								xhtml=xhtml, callback=_on_sent,
 | 
				
			||||||
			callback_args=[contact, message, encrypted, xhtml])
 | 
								callback_args=[contact, message, encrypted, xhtml],
 | 
				
			||||||
 | 
								process_commands=process_commands)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def check_for_possible_paused_chatstate(self, arg):
 | 
						def check_for_possible_paused_chatstate(self, arg):
 | 
				
			||||||
		''' did we move mouse of that window or write something in message
 | 
							''' did we move mouse of that window or write something in message
 | 
				
			||||||
| 
						 | 
					@ -2428,6 +2410,10 @@ class ChatControl(ChatControlBase):
 | 
				
			||||||
				self.handlers[i].disconnect(i)
 | 
									self.handlers[i].disconnect(i)
 | 
				
			||||||
			del self.handlers[i]
 | 
								del self.handlers[i]
 | 
				
			||||||
		self.conv_textview.del_handlers()
 | 
							self.conv_textview.del_handlers()
 | 
				
			||||||
 | 
							if gajim.config.get('use_speller') and HAS_GTK_SPELL:
 | 
				
			||||||
 | 
								spell_obj = gtkspell.get_from_text_view(self.msg_textview)
 | 
				
			||||||
 | 
								if spell_obj:
 | 
				
			||||||
 | 
									spell_obj.detach()
 | 
				
			||||||
		self.msg_textview.destroy()
 | 
							self.msg_textview.destroy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def minimizable(self):
 | 
						def minimizable(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								src/commands/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/commands/__init__.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					# Copyright (C) 2009  red-agent <hell.director@gmail.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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					The command system providing scalable and convenient architecture in combination
 | 
				
			||||||
 | 
					with declarative way of defining commands and a fair amount of automatization
 | 
				
			||||||
 | 
					for routine processes.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										88
									
								
								src/commands/custom.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/commands/custom.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,88 @@
 | 
				
			||||||
 | 
					# Copyright (C) 2009  red-agent <hell.director@gmail.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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					This module contains examples of how to create your own commands by creating an
 | 
				
			||||||
 | 
					adhoc command processor. Each adhoc command processor should be hosted by one or
 | 
				
			||||||
 | 
					more which dispatch the real deal and droppped in to where it belongs.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from framework import command
 | 
				
			||||||
 | 
					from implementation import ChatCommands, PrivateChatCommands, GroupChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomCommonCommands(ChatCommands, PrivateChatCommands, GroupChatCommands):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This adhoc processor will be hosted by a multiple processors which dispatch
 | 
				
			||||||
 | 
					    commands from all, chat, private chat and group chat. So commands defined
 | 
				
			||||||
 | 
					    here will be available to all of them.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DISPATCH = True
 | 
				
			||||||
 | 
					    HOSTED_BY = ChatCommands, PrivateChatCommands, GroupChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def dance(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        First line of the doc string is called a description and will be
 | 
				
			||||||
 | 
					        programmatically extracted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        After that you can give more help, like explanation of the options. This
 | 
				
			||||||
 | 
					        one will be programatically extracted and formatted too. After this one
 | 
				
			||||||
 | 
					        there will be autogenerated (based on the method signature) usage
 | 
				
			||||||
 | 
					        information appended. You can turn it off though, if you want.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return "I can't dance, you stupid fuck, I'm just a command system! A cool one, though..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomChatCommands(ChatCommands):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This adhoc processor will be hosted by a ChatCommands processor which
 | 
				
			||||||
 | 
					    dispatches commands from a chat. So commands defined here will be available
 | 
				
			||||||
 | 
					    only to a chat.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DISPATCH = True
 | 
				
			||||||
 | 
					    HOSTED_BY = ChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def sing(self):
 | 
				
			||||||
 | 
					        return "Are you phreaking kidding me? Buy yourself a damn stereo..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomPrivateChatCommands(PrivateChatCommands):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This adhoc processor will be hosted by a PrivateChatCommands processor which
 | 
				
			||||||
 | 
					    dispatches commands from a private chat. So commands defined here will be
 | 
				
			||||||
 | 
					    available only to a private chat.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DISPATCH = True
 | 
				
			||||||
 | 
					    HOSTED_BY = PrivateChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def make_coffee(self):
 | 
				
			||||||
 | 
					        return "What do I look like, you ass? A coffee machine!?"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomGroupChatCommands(GroupChatCommands):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    This adhoc processor will be hosted by a GroupChatCommands processor which
 | 
				
			||||||
 | 
					    dispatches commands from a group chat. So commands defined here will be
 | 
				
			||||||
 | 
					    available only to a group chat.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DISPATCH = True
 | 
				
			||||||
 | 
					    HOSTED_BY = GroupChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def fetch(self):
 | 
				
			||||||
 | 
					        return "You should really buy yourself a dog and start torturing it instead of me..."
 | 
				
			||||||
							
								
								
									
										764
									
								
								src/commands/framework.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										764
									
								
								src/commands/framework.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,764 @@
 | 
				
			||||||
 | 
					# Copyright (C) 2009  red-agent <hell.director@gmail.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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Provides a tiny framework with simple, yet powerful and extensible architecture
 | 
				
			||||||
 | 
					to implement commands in a streight and flexible, declarative way.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					from types import FunctionType, UnicodeType, TupleType, ListType, BooleanType
 | 
				
			||||||
 | 
					from inspect import getargspec
 | 
				
			||||||
 | 
					from operator import itemgetter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InternalError(Exception):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CommandError(Exception):
 | 
				
			||||||
 | 
					    def __init__(self, message=None, command=None, name=None):
 | 
				
			||||||
 | 
					        self.command = command
 | 
				
			||||||
 | 
					        self.name = name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if command:
 | 
				
			||||||
 | 
					            self.name = command.first_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if message:
 | 
				
			||||||
 | 
					            super(CommandError, self).__init__(message)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            super(CommandError, self).__init__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Command(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DOC_STRIP_PATTERN = re.compile(r'(?:^[ \t]+|\A\n)', re.MULTILINE)
 | 
				
			||||||
 | 
					    DOC_FORMAT_PATTERN = re.compile(r'(?<!\n)\n(?!\n)', re.MULTILINE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ARG_USAGE_PATTERN = 'Usage: %s %s'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, handler, usage, source, raw, extra, overlap, empty, expand_short):
 | 
				
			||||||
 | 
					        self.handler = handler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.usage = usage
 | 
				
			||||||
 | 
					        self.source = source
 | 
				
			||||||
 | 
					        self.raw = raw
 | 
				
			||||||
 | 
					        self.extra = extra
 | 
				
			||||||
 | 
					        self.overlap = overlap
 | 
				
			||||||
 | 
					        self.empty = empty
 | 
				
			||||||
 | 
					        self.expand_short = expand_short
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return self.handler(*args, **kwargs)
 | 
				
			||||||
 | 
					        except CommandError, exception:
 | 
				
			||||||
 | 
					            # Re-raise an excepttion with a proper command attribute set,
 | 
				
			||||||
 | 
					            # unless it is already set by the one who raised an exception.
 | 
				
			||||||
 | 
					            if not exception.command and not exception.name:
 | 
				
			||||||
 | 
					                raise CommandError(exception.message, self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Do not forget to re-raise an exception just like it was if at
 | 
				
			||||||
 | 
					            # least either, command or name attribute is set properly.
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # This one is a little bit too wide, but as Python does not have
 | 
				
			||||||
 | 
					        # anything more constrained - there is no other choice. Take a look here
 | 
				
			||||||
 | 
					        # if command complains about invalid arguments while they are ok.
 | 
				
			||||||
 | 
					        except TypeError:
 | 
				
			||||||
 | 
					            raise CommandError("Command received invalid arguments", self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self):
 | 
				
			||||||
 | 
					        return "<Command %s>" % ', '.join(self.names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __cmp__(self, other):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Comparison is implemented based on a first name.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return cmp(self.first_name, other.first_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def first_name(self):
 | 
				
			||||||
 | 
					        return self.names[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def native_name(self):
 | 
				
			||||||
 | 
					        return self.handler.__name__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extract_doc(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Extract handler's doc-string and transform it to a usable format.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        doc = self.handler.__doc__ or None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not doc:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        doc = re.sub(self.DOC_STRIP_PATTERN, str(), doc)
 | 
				
			||||||
 | 
					        doc = re.sub(self.DOC_FORMAT_PATTERN, ' ', doc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return doc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extract_description(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Extract handler's description (which is a first line of the doc). Try to
 | 
				
			||||||
 | 
					        keep them simple yet meaningful.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        doc = self.extract_doc()
 | 
				
			||||||
 | 
					        return doc.split('\n', 1)[0] if doc else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extract_arg_spec(self):
 | 
				
			||||||
 | 
					        names, var_args, var_kwargs, defaults = getargspec(self.handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Behavior of this code need to be checked. Might yield incorrect
 | 
				
			||||||
 | 
					        # results on some rare occasions.
 | 
				
			||||||
 | 
					        spec_args = names[:-len(defaults) if defaults else len(names)]
 | 
				
			||||||
 | 
					        spec_kwargs = list(zip(names[-len(defaults):], defaults)) if defaults else {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Removing self from arguments specification. Command handler should
 | 
				
			||||||
 | 
					        # normally be an instance method.
 | 
				
			||||||
 | 
					        if spec_args.pop(0) != 'self':
 | 
				
			||||||
 | 
					            raise InternalError("First argument must be self")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return spec_args, spec_kwargs, var_args, var_kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extract_arg_usage(self, complete=True):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Extract handler's arguments specification and wrap them in a
 | 
				
			||||||
 | 
					        human-readable format. If complete is given - then ARG_USAGE_PATTERN
 | 
				
			||||||
 | 
					        will be used to render it completly.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        spec_args, spec_kwargs, var_args, var_kwargs = self.extract_arg_spec()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Remove some special positional arguments from the specifiaction, but
 | 
				
			||||||
 | 
					        # store their names so they can be used for usage info generation.
 | 
				
			||||||
 | 
					        sp_source = spec_args.pop(0) if self.source else None
 | 
				
			||||||
 | 
					        sp_extra = spec_args.pop() if self.extra else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        kwargs = []
 | 
				
			||||||
 | 
					        letters = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for key, value in spec_kwargs:
 | 
				
			||||||
 | 
					            letter = key[0]
 | 
				
			||||||
 | 
					            key = key.replace('_', '-')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if isinstance(value, BooleanType):
 | 
				
			||||||
 | 
					                value = str()
 | 
				
			||||||
 | 
					            elif isinstance(value, (TupleType, ListType)):
 | 
				
			||||||
 | 
					                value = '={%s}' % ', '.join(value)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                value = '=%s' % value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if letter not in letters:
 | 
				
			||||||
 | 
					                kwargs.append('-(-%s)%s%s' % (letter, key[1:], value))
 | 
				
			||||||
 | 
					                letters.append(letter)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                kwargs.append('--%s%s' % (key, value))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        usage = str()
 | 
				
			||||||
 | 
					        args = str()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.raw:
 | 
				
			||||||
 | 
					            spec_len = len(spec_args) - 1
 | 
				
			||||||
 | 
					            if spec_len:
 | 
				
			||||||
 | 
					                args += ('<%s>' % ', '.join(spec_args[:spec_len])) + ' '
 | 
				
			||||||
 | 
					            args += ('(|%s|)' if self.empty else '|%s|') % spec_args[-1]
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if spec_args:
 | 
				
			||||||
 | 
					                args += '<%s>' % ', '.join(spec_args)
 | 
				
			||||||
 | 
					            if var_args or sp_extra:
 | 
				
			||||||
 | 
					                args += (' ' if spec_args else str()) + '<<%s>>' % (var_args or sp_extra)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        usage += args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if kwargs or var_kwargs:
 | 
				
			||||||
 | 
					            if kwargs:
 | 
				
			||||||
 | 
					                usage += (' ' if args else str()) + '[%s]' % ', '.join(kwargs)
 | 
				
			||||||
 | 
					            if var_kwargs:
 | 
				
			||||||
 | 
					                usage += (' ' if args else str()) + '[[%s]]' % var_kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Native name will be the first one if it is included. Otherwise, names
 | 
				
			||||||
 | 
					        # will be in the order they were specified.
 | 
				
			||||||
 | 
					        if len(self.names) > 1:
 | 
				
			||||||
 | 
					            names = '%s (%s)' % (self.first_name, ', '.join(self.names[1:]))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            names = self.first_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return usage if not complete else self.ARG_USAGE_PATTERN % (names, usage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Dispatcher(type):
 | 
				
			||||||
 | 
					    table = {}
 | 
				
			||||||
 | 
					    hosted = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(cls, name, bases, dct):
 | 
				
			||||||
 | 
					        dispatchable = Dispatcher.check_if_dispatchable(bases, dct)
 | 
				
			||||||
 | 
					        hostable = Dispatcher.check_if_hostable(bases, dct)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cls.check_if_conformed(dispatchable, hostable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if Dispatcher.is_suitable(cls, dct):
 | 
				
			||||||
 | 
					            Dispatcher.register_processor(cls)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Sanitize names even if processor is not suitable for registering,
 | 
				
			||||||
 | 
					        # because it might be inherited by an another processor.
 | 
				
			||||||
 | 
					        Dispatcher.sanitize_names(cls)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(Dispatcher, cls).__init__(name, bases, dct)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def is_suitable(cls, proc, dct):
 | 
				
			||||||
 | 
					        is_not_root = dct.get('__metaclass__') is not cls
 | 
				
			||||||
 | 
					        to_be_dispatched = bool(dct.get('DISPATCH'))
 | 
				
			||||||
 | 
					        return is_not_root and to_be_dispatched
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def check_if_dispatchable(cls, bases, dct):
 | 
				
			||||||
 | 
					        dispatcher = dct.get('DISPATCHED_BY')
 | 
				
			||||||
 | 
					        if not dispatcher:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if dispatcher not in bases:
 | 
				
			||||||
 | 
					            raise InternalError("Should be dispatched by the same processor it inherits from")
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def check_if_hostable(cls, bases, dct):
 | 
				
			||||||
 | 
					        hosters = dct.get('HOSTED_BY')
 | 
				
			||||||
 | 
					        if not hosters:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					        if not isinstance(hosters, (TupleType, ListType)):
 | 
				
			||||||
 | 
					            hosters = (hosters,)
 | 
				
			||||||
 | 
					        for hoster in hosters:
 | 
				
			||||||
 | 
					            if hoster not in bases:
 | 
				
			||||||
 | 
					                raise InternalError("Should be hosted by the same processors it inherits from")
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def check_if_conformed(cls, dispatchable, hostable):
 | 
				
			||||||
 | 
					        if dispatchable and hostable:
 | 
				
			||||||
 | 
					            raise InternalError("Processor can not be dispatchable and hostable at the same time")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def register_processor(cls, proc):
 | 
				
			||||||
 | 
					        cls.table[proc] = {}
 | 
				
			||||||
 | 
					        inherit = proc.__dict__.get('INHERIT')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'HOSTED_BY' in proc.__dict__:
 | 
				
			||||||
 | 
					            cls.register_adhocs(proc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        commands = cls.traverse_commands(proc, inherit)
 | 
				
			||||||
 | 
					        cls.register_commands(proc, commands)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def sanitize_names(cls, proc):
 | 
				
			||||||
 | 
					        inherit = proc.__dict__.get('INHERIT')
 | 
				
			||||||
 | 
					        commands = cls.traverse_commands(proc, inherit)
 | 
				
			||||||
 | 
					        for key, command in commands:
 | 
				
			||||||
 | 
					            if not proc.SAFE_NAME_SCAN_PATTERN.match(key):
 | 
				
			||||||
 | 
					                setattr(proc, proc.SAFE_NAME_SUBS_PATTERN % key, command)
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    delattr(proc, key)
 | 
				
			||||||
 | 
					                except AttributeError:
 | 
				
			||||||
 | 
					                    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def traverse_commands(cls, proc, inherit=True):
 | 
				
			||||||
 | 
					        keys = dir(proc) if inherit else proc.__dict__.iterkeys()
 | 
				
			||||||
 | 
					        for key in keys:
 | 
				
			||||||
 | 
					            value = getattr(proc, key)
 | 
				
			||||||
 | 
					            if isinstance(value, Command):
 | 
				
			||||||
 | 
					                yield key, value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def register_commands(cls, proc, commands):
 | 
				
			||||||
 | 
					        for key, command in commands:
 | 
				
			||||||
 | 
					            for name in command.names:
 | 
				
			||||||
 | 
					                name = proc.prepare_name(name)
 | 
				
			||||||
 | 
					                if name not in cls.table[proc]:
 | 
				
			||||||
 | 
					                    cls.table[proc][name] = command
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    raise InternalError("Command with name %s already exists" % name)
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def register_adhocs(cls, proc):
 | 
				
			||||||
 | 
					        hosters = proc.HOSTED_BY
 | 
				
			||||||
 | 
					        if not isinstance(hosters, (TupleType, ListType)):
 | 
				
			||||||
 | 
					            hosters = (hosters,)
 | 
				
			||||||
 | 
					        for hoster in hosters:
 | 
				
			||||||
 | 
					            if hoster in cls.hosted:
 | 
				
			||||||
 | 
					                cls.hosted[hoster].append(proc)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                cls.hosted[hoster] = [proc]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def retrieve_command(cls, proc, name):
 | 
				
			||||||
 | 
					        command = cls.table[proc.DISPATCHED_BY].get(name)
 | 
				
			||||||
 | 
					        if command:
 | 
				
			||||||
 | 
					            return command
 | 
				
			||||||
 | 
					        if proc.DISPATCHED_BY in cls.hosted:
 | 
				
			||||||
 | 
					            for adhoc in cls.hosted[proc.DISPATCHED_BY]:
 | 
				
			||||||
 | 
					                command = cls.table[adhoc].get(name)
 | 
				
			||||||
 | 
					                if command:
 | 
				
			||||||
 | 
					                    return command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def list_commands(cls, proc):
 | 
				
			||||||
 | 
					        commands = dict(cls.traverse_commands(proc.DISPATCHED_BY))
 | 
				
			||||||
 | 
					        if proc.DISPATCHED_BY in cls.hosted:
 | 
				
			||||||
 | 
					            for adhoc in cls.hosted[proc.DISPATCHED_BY]:
 | 
				
			||||||
 | 
					                inherit = adhoc.__dict__.get('INHERIT')
 | 
				
			||||||
 | 
					                commands.update(dict(cls.traverse_commands(adhoc, inherit)))
 | 
				
			||||||
 | 
					        return commands.values()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CommandProcessor(object):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A base class for a drop-in command processor which you can drop (make your
 | 
				
			||||||
 | 
					    class to inherit from it) in any of your classes to support commands. In
 | 
				
			||||||
 | 
					    order to get it done you need to make your own processor, inheriter from
 | 
				
			||||||
 | 
					    CommandProcessor and then drop it in. Don't forget about few important steps
 | 
				
			||||||
 | 
					    described below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Every command in the processor (normally) will gain full access through self
 | 
				
			||||||
 | 
					    to an object you are adding commands to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Your subclass, which will contain commands should define in its body
 | 
				
			||||||
 | 
					    DISPATCH = True in order to be included in the dispatching table.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Every class you will drop the processor in should define DISPATCHED_BY set
 | 
				
			||||||
 | 
					    to the same processor you are inheriting from.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Names of the commands after preparation stuff id done will be sanitized
 | 
				
			||||||
 | 
					    (based on SAFE_NAME_SCAN_PATTERN and SAFE_NAME_SUBS_PATTERN) in order not to
 | 
				
			||||||
 | 
					    interfere with the methods defined in a class you will drop a processor in.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If you want to create an adhoc processor (then one that parasites on the
 | 
				
			||||||
 | 
					    other one (the host), so it does not have to be included directly into
 | 
				
			||||||
 | 
					    whatever includes the host) you need to inherit you processor from the host
 | 
				
			||||||
 | 
					    and set HOSTED_BY to that host.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    INHERIT controls whether commands inherited from base classes (which could
 | 
				
			||||||
 | 
					    include other processors) will be registered or not. This is disabled
 | 
				
			||||||
 | 
					    by-default because it leads to unpredictable consequences when used in adhoc
 | 
				
			||||||
 | 
					    processors which inherit from more then one processor or has such processors
 | 
				
			||||||
 | 
					    in its inheritance tree. In that case - encapsulation is being broken and
 | 
				
			||||||
 | 
					    some (all) commands are shared between non-related processors.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    __metaclass__ = Dispatcher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SAFE_NAME_SCAN_PATTERN = re.compile(r'_(?P<name>\w+)_')
 | 
				
			||||||
 | 
					    SAFE_NAME_SUBS_PATTERN = '_%s_'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Quite complex piece of regular expression logic.
 | 
				
			||||||
 | 
					    ARG_PATTERN = re.compile(r'(\'|")?(?P<body>(?(1).+?|\S+))(?(1)\1)')
 | 
				
			||||||
 | 
					    OPT_PATTERN = re.compile(r'(?<!\w)--?(?P<key>[\w-]+)(?:(?:=|\s)(\'|")?(?P<value>(?(2)[^-]+?|[^-\s]+))(?(2)\2))?')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    COMMAND_PREFIX = '/'
 | 
				
			||||||
 | 
					    CASE_SENSITIVE_COMMANDS = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ARG_ENCODING = 'utf8'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, name):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        This allows to reach and directly (internally) call commands which are
 | 
				
			||||||
 | 
					        defined in (other) adhoc processors.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        command_name = self.SAFE_NAME_SCAN_PATTERN.match(name)
 | 
				
			||||||
 | 
					        if command_name:
 | 
				
			||||||
 | 
					            command = self.retrieve_command(command_name.group('name'))
 | 
				
			||||||
 | 
					            if command:
 | 
				
			||||||
 | 
					                return command
 | 
				
			||||||
 | 
					        raise AttributeError(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def prepare_name(cls, name):
 | 
				
			||||||
 | 
					        return name if cls.CASE_SENSITIVE_COMMANDS else name.lower()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def retrieve_command(cls, name):
 | 
				
			||||||
 | 
					        name = cls.prepare_name(name)
 | 
				
			||||||
 | 
					        command = Dispatcher.retrieve_command(cls, name)
 | 
				
			||||||
 | 
					        if not command:
 | 
				
			||||||
 | 
					            raise CommandError("Command does not exist", name=name)
 | 
				
			||||||
 | 
					        return command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def list_commands(cls):
 | 
				
			||||||
 | 
					        commands = Dispatcher.list_commands(cls)
 | 
				
			||||||
 | 
					        return sorted(set(commands))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def parse_command_arguments(cls, arguments):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Simple yet effective and sufficient in most cases parser which parses
 | 
				
			||||||
 | 
					        command arguments and returns them as two lists. First represents
 | 
				
			||||||
 | 
					        positional arguments as (argument, position), and second representing
 | 
				
			||||||
 | 
					        options as (key, value, position) tuples, where position is a (start,
 | 
				
			||||||
 | 
					        end) span tuple of where it was found in the string.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The format of the input arguments should be:
 | 
				
			||||||
 | 
					            <arg1, arg2> <<extra>> [-(-o)ption=value1, -(-a)nother=value2] [[extra_options]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Options may be given in --long or -short format. As --option=value or
 | 
				
			||||||
 | 
					        --option value or -option value. Keys without values will get True as
 | 
				
			||||||
 | 
					        value. Arguments and option values that contain spaces may be given as
 | 
				
			||||||
 | 
					        'one two three' or "one two three"; that is between single or double
 | 
				
			||||||
 | 
					        quotes.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        args, opts = [], []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def intersects_opts((given_start, given_end)):
 | 
				
			||||||
 | 
					            """
 | 
				
			||||||
 | 
					            Check if something intersects with boundaries of any parsed option.
 | 
				
			||||||
 | 
					            """
 | 
				
			||||||
 | 
					            for key, value, (start, end) in opts:
 | 
				
			||||||
 | 
					                if given_start >= start and given_end <= end:
 | 
				
			||||||
 | 
					                    return True
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def intersects_args((given_start, given_end)):
 | 
				
			||||||
 | 
					            """
 | 
				
			||||||
 | 
					            Check if something intersects with boundaries of any parsed argument.
 | 
				
			||||||
 | 
					            """
 | 
				
			||||||
 | 
					            for arg, (start, end) in args:
 | 
				
			||||||
 | 
					                if given_start >= start and given_end <= end:
 | 
				
			||||||
 | 
					                    return True
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for match in re.finditer(cls.OPT_PATTERN, arguments):
 | 
				
			||||||
 | 
					            if match:
 | 
				
			||||||
 | 
					                key = match.group('key')
 | 
				
			||||||
 | 
					                value = match.group('value') or None
 | 
				
			||||||
 | 
					                position = match.span()
 | 
				
			||||||
 | 
					                opts.append((key, value, position))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for match in re.finditer(cls.ARG_PATTERN, arguments):
 | 
				
			||||||
 | 
					            if match and not intersects_opts(match.span()):
 | 
				
			||||||
 | 
					                body = match.group('body')
 | 
				
			||||||
 | 
					                position = match.span()
 | 
				
			||||||
 | 
					                args.append((body, position))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # In rare occasions quoted options are being captured, while they should
 | 
				
			||||||
 | 
					        # not be. This fixes the problem by finding options which intersect with
 | 
				
			||||||
 | 
					        # arguments and removing them.
 | 
				
			||||||
 | 
					        for key, value, position in opts[:]:
 | 
				
			||||||
 | 
					            if intersects_args(position):
 | 
				
			||||||
 | 
					                opts.remove((key, value, position))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return args, opts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def adapt_command_arguments(cls, command, arguments, args, opts):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Adapts args and opts got from the parser to a specific handler by means
 | 
				
			||||||
 | 
					        of arguments specified on command definition. That is transforms them to
 | 
				
			||||||
 | 
					        *args and **kwargs suitable for passing to a command handler.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Extra arguments which are not considered extra (or optional) - will be
 | 
				
			||||||
 | 
					        passed as if they were value for keywords, in the order keywords are
 | 
				
			||||||
 | 
					        defined and printed in usage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Dashes (-) in the option names will be converted to underscores. So you
 | 
				
			||||||
 | 
					        can map --one-more-option to a one_more_option=None.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If initial value of a keyword argument is a boolean (False in most
 | 
				
			||||||
 | 
					        cases) then this option will be treated as a switch, that is an option
 | 
				
			||||||
 | 
					        which does not take an argument. Argument preceded by a switch will be
 | 
				
			||||||
 | 
					        treated just like a normal positional argument.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If keyword argument's initial value is a sequence (tuple or a string)
 | 
				
			||||||
 | 
					        then possible values of the option will be restricted to one of the
 | 
				
			||||||
 | 
					        values given by the sequence.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        spec_args, spec_kwargs, var_args, var_kwargs = command.extract_arg_spec()
 | 
				
			||||||
 | 
					        norm_kwargs = dict(spec_kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Quite complex piece of neck-breaking logic to extract raw arguments if
 | 
				
			||||||
 | 
					        # there is more, then one positional argument specified by the command.
 | 
				
			||||||
 | 
					        # In case if it's just one argument which is the collector this is
 | 
				
			||||||
 | 
					        # fairly easy. But when it's more then one argument - the neck-breaking
 | 
				
			||||||
 | 
					        # logic of how to retrieve residual arguments as a raw, all in one piece
 | 
				
			||||||
 | 
					        # string, kicks on.
 | 
				
			||||||
 | 
					        if command.raw:
 | 
				
			||||||
 | 
					            if spec_kwargs or var_args or var_kwargs:
 | 
				
			||||||
 | 
					                raise InternalError("Raw commands should define only positional arguments")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if arguments:
 | 
				
			||||||
 | 
					                spec_fix = 1 if command.source else 0
 | 
				
			||||||
 | 
					                spec_len = len(spec_args) - spec_fix
 | 
				
			||||||
 | 
					                arguments_end = len(arguments) - 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # If there are any optional arguments given they should be
 | 
				
			||||||
 | 
					                # either an unquoted postional argument or part of the raw
 | 
				
			||||||
 | 
					                # argument. So we find all optional arguments that can possibly
 | 
				
			||||||
 | 
					                # be unquoted argument and append them as is to the args.
 | 
				
			||||||
 | 
					                for key, value, (start, end) in opts[:spec_len]:
 | 
				
			||||||
 | 
					                    if value:
 | 
				
			||||||
 | 
					                        end -= len(value) + 1
 | 
				
			||||||
 | 
					                        args.append((arguments[start:end], (start, end)))
 | 
				
			||||||
 | 
					                        args.append((value, (end, end + len(value) + 1)))
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        args.append((arguments[start:end], (start, end)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # We need in-place sort here because after manipulations with
 | 
				
			||||||
 | 
					                # options order of arguments might be wrong and we just can't
 | 
				
			||||||
 | 
					                # have more complex logic to not let that happen.
 | 
				
			||||||
 | 
					                args.sort(key=itemgetter(1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if spec_len > 1:
 | 
				
			||||||
 | 
					                    try:
 | 
				
			||||||
 | 
					                        stopper, (start, end) = args[spec_len - 2]
 | 
				
			||||||
 | 
					                    except IndexError:
 | 
				
			||||||
 | 
					                        raise CommandError("Missing arguments", command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    raw = arguments[end:]
 | 
				
			||||||
 | 
					                    raw = raw.strip() or None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if not raw and not command.empty:
 | 
				
			||||||
 | 
					                        raise CommandError("Missing arguments", command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # Discard residual arguments and all of the options as raw
 | 
				
			||||||
 | 
					                    # command does not support options and if an option is given
 | 
				
			||||||
 | 
					                    # it is rather a part of a raw argument.
 | 
				
			||||||
 | 
					                    args = args[:spec_len - 1]
 | 
				
			||||||
 | 
					                    opts = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    args.append((raw, (end, arguments_end)))
 | 
				
			||||||
 | 
					                elif spec_len == 1:
 | 
				
			||||||
 | 
					                    args = [(arguments, (0, arguments_end))]
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    raise InternalError("Raw command must define a collector")
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                if command.empty:
 | 
				
			||||||
 | 
					                    args.append((None, (0, 0)))
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    raise CommandError("Missing arguments", command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # The first stage of transforming options we have got to a format that
 | 
				
			||||||
 | 
					        # can be used to associate them with declared keyword arguments.
 | 
				
			||||||
 | 
					        # Substituting dashes (-) in their names with underscores (_).
 | 
				
			||||||
 | 
					        for index, (key, value, position) in enumerate(opts):
 | 
				
			||||||
 | 
					            if '-' in key:
 | 
				
			||||||
 | 
					                opts[index] = (key.replace('-', '_'), value, position)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # The second stage of transforming options to an associatable state.
 | 
				
			||||||
 | 
					        # Expanding short, one-letter options to a verbose ones, if
 | 
				
			||||||
 | 
					        # corresponding optin has been given.
 | 
				
			||||||
 | 
					        if command.expand_short:
 | 
				
			||||||
 | 
					            expanded = []
 | 
				
			||||||
 | 
					            for spec_key, spec_value in norm_kwargs.iteritems():
 | 
				
			||||||
 | 
					                letter = spec_key[0] if len(spec_key) > 1 else None
 | 
				
			||||||
 | 
					                if letter and letter not in expanded:
 | 
				
			||||||
 | 
					                    for index, (key, value, position) in enumerate(opts):
 | 
				
			||||||
 | 
					                        if key == letter:
 | 
				
			||||||
 | 
					                            expanded.append(letter)
 | 
				
			||||||
 | 
					                            opts[index] = (spec_key, value, position)
 | 
				
			||||||
 | 
					                            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Detect switches and set their values accordingly. If any of them
 | 
				
			||||||
 | 
					        # carries a value - append it to args.
 | 
				
			||||||
 | 
					        for index, (key, value, position) in enumerate(opts):
 | 
				
			||||||
 | 
					            if isinstance(norm_kwargs.get(key), BooleanType):
 | 
				
			||||||
 | 
					                opts[index] = (key, True, position)
 | 
				
			||||||
 | 
					                if value:
 | 
				
			||||||
 | 
					                    args.append((value, position))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Sorting arguments and options (just to be sure) in regarding to their
 | 
				
			||||||
 | 
					        # positions in the string.
 | 
				
			||||||
 | 
					        args.sort(key=itemgetter(1))
 | 
				
			||||||
 | 
					        opts.sort(key=itemgetter(2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Stripping down position information supplied with arguments and options as it
 | 
				
			||||||
 | 
					        # won't be needed again.
 | 
				
			||||||
 | 
					        args = map(lambda (arg, position): arg, args)
 | 
				
			||||||
 | 
					        opts = map(lambda (key, value, position): (key, value), opts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # If command has extra option enabled - collect all extra arguments and
 | 
				
			||||||
 | 
					        # pass them to a last positional argument command defines as a list.
 | 
				
			||||||
 | 
					        if command.extra:
 | 
				
			||||||
 | 
					            if not var_args:
 | 
				
			||||||
 | 
					                spec_fix = 1 if not command.source else 2
 | 
				
			||||||
 | 
					                spec_len = len(spec_args) - spec_fix
 | 
				
			||||||
 | 
					                extra = args[spec_len:]
 | 
				
			||||||
 | 
					                args = args[:spec_len]
 | 
				
			||||||
 | 
					                args.append(extra)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise InternalError("Can not have both, extra and *args")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Detect if positional arguments overlap keyword arguments. If so and
 | 
				
			||||||
 | 
					        # this is allowed by command options - then map them directly to their
 | 
				
			||||||
 | 
					        # options, so they can get propert further processings.
 | 
				
			||||||
 | 
					        spec_fix = 1 if command.source else 0
 | 
				
			||||||
 | 
					        spec_len = len(spec_args) - spec_fix
 | 
				
			||||||
 | 
					        if len(args) > spec_len:
 | 
				
			||||||
 | 
					            if command.overlap:
 | 
				
			||||||
 | 
					                overlapped = args[spec_len:]
 | 
				
			||||||
 | 
					                args = args[:spec_len]
 | 
				
			||||||
 | 
					                for arg, (spec_key, spec_value) in zip(overlapped, spec_kwargs):
 | 
				
			||||||
 | 
					                    opts.append((spec_key, arg))
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise CommandError("Excessive arguments", command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Detect every contraint sequences and ensure that if corresponding
 | 
				
			||||||
 | 
					        # options are given - they contain proper values, within constraint
 | 
				
			||||||
 | 
					        # range.
 | 
				
			||||||
 | 
					        for key, value in opts:
 | 
				
			||||||
 | 
					            initial = norm_kwargs.get(key)
 | 
				
			||||||
 | 
					            if isinstance(initial, (TupleType, ListType)) and value not in initial:
 | 
				
			||||||
 | 
					                raise CommandError("Wrong argument", command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Detect every switch and ensure it will not receive any arguments.
 | 
				
			||||||
 | 
					        # Normally this does not happen unless overlapping is enabled.
 | 
				
			||||||
 | 
					        for key, value in opts:
 | 
				
			||||||
 | 
					            initial = norm_kwargs.get(key)
 | 
				
			||||||
 | 
					            if isinstance(initial, BooleanType) and not isinstance(value, BooleanType):
 | 
				
			||||||
 | 
					                raise CommandError("Switches do not take arguments", command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # We need to encode every keyword argument to a simple string, not the
 | 
				
			||||||
 | 
					        # unicode one, because ** expansion does not support it.
 | 
				
			||||||
 | 
					        for index, (key, value) in enumerate(opts):
 | 
				
			||||||
 | 
					            if isinstance(key, UnicodeType):
 | 
				
			||||||
 | 
					                opts[index] = (key.encode(cls.ARG_ENCODING), value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Inject the source arguments as a string as a first argument, if
 | 
				
			||||||
 | 
					        # command has enabled the corresponding option.
 | 
				
			||||||
 | 
					        if command.source:
 | 
				
			||||||
 | 
					            args.insert(0, arguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Return *args and **kwargs in the form suitable for passing to a
 | 
				
			||||||
 | 
					        # command handlers and being expanded.
 | 
				
			||||||
 | 
					        return tuple(args), dict(opts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def process_as_command(self, text):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Try to process text as a command. Returns True if it is a command and
 | 
				
			||||||
 | 
					        False if it is not.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if not text.startswith(self.COMMAND_PREFIX):
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        text = text[len(self.COMMAND_PREFIX):]
 | 
				
			||||||
 | 
					        text = text.strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        parts = text.split(' ', 1)
 | 
				
			||||||
 | 
					        name, arguments = parts if len(parts) > 1 else (parts[0], None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flag = self.looks_like_command(text, name, arguments)
 | 
				
			||||||
 | 
					        if flag is not None:
 | 
				
			||||||
 | 
					            return flag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.execute_command(name, arguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute_command(self, name, arguments):
 | 
				
			||||||
 | 
					        command = self.retrieve_command(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        args, opts = self.parse_command_arguments(arguments) if arguments else ([], [])
 | 
				
			||||||
 | 
					        args, kwargs = self.adapt_command_arguments(command, arguments, args, opts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.command_preprocessor(name, command, arguments, args, kwargs):
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        value = command(self, *args, **kwargs)
 | 
				
			||||||
 | 
					        self.command_postprocessor(name, command, arguments, args, kwargs, value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def command_preprocessor(self, name, command, arguments, args, kwargs):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Redefine this method in the subclass to execute custom code before
 | 
				
			||||||
 | 
					        command gets executed. If returns True then command execution will be
 | 
				
			||||||
 | 
					        interrupted and command will not be executed.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def command_postprocessor(self, name, command, arguments, args, kwargs, output):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Redefine this method in the subclass to execute custom code after
 | 
				
			||||||
 | 
					        command gets executed.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def looks_like_command(self, text, name, arguments):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        This hook is being called before any processing, but after it was
 | 
				
			||||||
 | 
					        determined that text looks like a command. If returns non None value
 | 
				
			||||||
 | 
					        - then further processing will be interrupted and that value will be
 | 
				
			||||||
 | 
					        used to return from process_as_command.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def command(*names, **kwargs):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A decorator which provides a declarative way of defining commands.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    You can specify a set of names by which you can call the command. If names
 | 
				
			||||||
 | 
					    is empty - then the name of the command will be set to native one (extracted
 | 
				
			||||||
 | 
					    from the handler name).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If include_native=True argument is given and names is non-empty - then
 | 
				
			||||||
 | 
					    native name will be added as well.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If usage=True is given - then handler's doc will be appended with an
 | 
				
			||||||
 | 
					    auto-generated usage info.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If source=True is given - then the first positional argument of the command
 | 
				
			||||||
 | 
					    handler will receive a string with a raw and unprocessed source arguments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If raw=True is given - then command should define only one argument to
 | 
				
			||||||
 | 
					    which all raw and unprocessed source arguments will be given.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If empty=True is given - then when raw=True is set and command receives no
 | 
				
			||||||
 | 
					    arguments - an exception will be raised.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If extra=True is given - then last positional argument will receive every
 | 
				
			||||||
 | 
					    extra positional argument that will be given to a command. This is an
 | 
				
			||||||
 | 
					    analogue to specifing *args, but the latter one should be used in simplest
 | 
				
			||||||
 | 
					    cases only because of some Python limitations on this - arguments can't be
 | 
				
			||||||
 | 
					    mapped correctly when there are keyword arguments present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If overlap=True is given - then if extra=False and there is extra arguments
 | 
				
			||||||
 | 
					    given to the command - they will be mapped as if they were values for the
 | 
				
			||||||
 | 
					    keyword arguments, in the order they are defined.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If expand_short=True is given - then if command receives one-letter
 | 
				
			||||||
 | 
					    options (like -v or -f) they will be expanded to a verbose ones (like
 | 
				
			||||||
 | 
					    --verbose or --file) if the latter are defined as a command optional
 | 
				
			||||||
 | 
					    arguments. Expansion is made on a first-letter comparison basis. If more
 | 
				
			||||||
 | 
					    then one long option with the same first letter defined - only first one
 | 
				
			||||||
 | 
					    will be used in expansion.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    names = list(names)
 | 
				
			||||||
 | 
					    include_native = kwargs.get('include_native', True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    usage = kwargs.get('usage', True)
 | 
				
			||||||
 | 
					    source = kwargs.get('source', False)
 | 
				
			||||||
 | 
					    raw = kwargs.get('raw', False)
 | 
				
			||||||
 | 
					    extra = kwargs.get('extra', False)
 | 
				
			||||||
 | 
					    overlap = kwargs.get('overlap', False)
 | 
				
			||||||
 | 
					    empty = kwargs.get('empty', False)
 | 
				
			||||||
 | 
					    expand_short = kwargs.get('expand_short', True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if extra and overlap:
 | 
				
			||||||
 | 
					        raise InternalError("Extra and overlap options can not be used together")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decorator(handler):
 | 
				
			||||||
 | 
					        command = Command(handler, usage, source, raw, extra, overlap, empty, expand_short)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Extract and inject native name while making sure it is going to be the
 | 
				
			||||||
 | 
					        # first one in the list.
 | 
				
			||||||
 | 
					        if not names or include_native:
 | 
				
			||||||
 | 
					            names.insert(0, command.native_name)
 | 
				
			||||||
 | 
					        command.names = tuple(names)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return command
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Workaround if we are getting called without parameters. Keep in mind that
 | 
				
			||||||
 | 
					    # in that case - first item in the names will be the handler.
 | 
				
			||||||
 | 
					    if len(names) == 1 and isinstance(names[0], FunctionType):
 | 
				
			||||||
 | 
					        return decorator(names.pop())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return decorator
 | 
				
			||||||
							
								
								
									
										262
									
								
								src/commands/implementation.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								src/commands/implementation.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,262 @@
 | 
				
			||||||
 | 
					# Copyright (C) 2009  red-agent <hell.director@gmail.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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Provides an actual implementation of the standard commands.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import dialogs
 | 
				
			||||||
 | 
					from common import gajim
 | 
				
			||||||
 | 
					from common import helpers
 | 
				
			||||||
 | 
					from common.exceptions import GajimGeneralException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from framework import command, CommandError
 | 
				
			||||||
 | 
					from middleware import ChatMiddleware
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CommonCommands(ChatMiddleware):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Here defined commands will be common to all, chat, private chat and group
 | 
				
			||||||
 | 
					    chat. Keep in mind that self is set to an instance of either ChatControl,
 | 
				
			||||||
 | 
					    PrivateChatControl or GroupchatControl when command is being called.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def clear(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Clear the text window
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.conv_textview.clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def compact(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Hide the chat buttons
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.chat_buttons_set_visible(not self.hide_chat_buttons)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(overlap=True)
 | 
				
			||||||
 | 
					    def help(self, command=None, all=False):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Show help on a given command or a list of available commands if -(-a)ll is
 | 
				
			||||||
 | 
					        given
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if command:
 | 
				
			||||||
 | 
					            command = self.retrieve_command(command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            doc = _(command.extract_doc())
 | 
				
			||||||
 | 
					            usage = command.extract_arg_usage()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if doc:
 | 
				
			||||||
 | 
					                return (doc + '\n\n' + usage) if command.usage else doc
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                return usage
 | 
				
			||||||
 | 
					        elif all:
 | 
				
			||||||
 | 
					            for command in self.list_commands():
 | 
				
			||||||
 | 
					                names = ', '.join(command.names)
 | 
				
			||||||
 | 
					                description = command.extract_description()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.echo("%s - %s" % (names, description))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.echo(self._help_(self, 'help'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True)
 | 
				
			||||||
 | 
					    def say(self, message):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Send a message to the contact
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.send(message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True)
 | 
				
			||||||
 | 
					    def me(self, action):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Send action (in the third person) to the current chat
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.send("/me %s" % action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChatCommands(CommonCommands):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Here defined commands will be unique to a chat. Use it as a hoster to provide
 | 
				
			||||||
 | 
					    commands which should be unique to a chat. Keep in mind that self is set to
 | 
				
			||||||
 | 
					    an instance of ChatControl when command is being called.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DISPATCH = True
 | 
				
			||||||
 | 
					    INHERIT = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def ping(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Send a ping to the contact
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.account == gajim.ZEROCONF_ACC_NAME:
 | 
				
			||||||
 | 
					            raise CommandError(_('Command is not supported for zeroconf accounts'))
 | 
				
			||||||
 | 
					        gajim.connections[self.account].sendPing(self.contact)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PrivateChatCommands(CommonCommands):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Here defined commands will be unique to a private chat. Use it as a hoster to
 | 
				
			||||||
 | 
					    provide commands which should be unique to a private chat. Keep in mind that
 | 
				
			||||||
 | 
					    self is set to an instance of PrivateChatControl when command is being called.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DISPATCH = True
 | 
				
			||||||
 | 
					    INHERIT = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GroupChatCommands(CommonCommands):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Here defined commands will be unique to a group chat. Use it as a hoster to
 | 
				
			||||||
 | 
					    provide commands which should be unique to a group chat. Keep in mind that
 | 
				
			||||||
 | 
					    self is set to an instance of GroupchatControl when command is being called.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DISPATCH = True
 | 
				
			||||||
 | 
					    INHERIT = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True)
 | 
				
			||||||
 | 
					    def nick(self, new_nick):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Change your nickname in a group chat
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            new_nick = helpers.parse_resource(new_nick)
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            raise CommandError(_("Invalid nickname"))
 | 
				
			||||||
 | 
					        self.connection.join_gc(new_nick, self.room_jid, None, change_nick=True)
 | 
				
			||||||
 | 
					        self.new_nick = new_nick
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command('query', raw=True)
 | 
				
			||||||
 | 
					    def chat(self, nick):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Open a private chat window with a specified occupant
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
 | 
				
			||||||
 | 
					        if nick in nicks:
 | 
				
			||||||
 | 
					            self.on_send_pm(nick=nick)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise CommandError(_("Nickname not found"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command('msg', raw=True)
 | 
				
			||||||
 | 
					    def message(self, nick, a_message):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Open a private chat window with a specified occupant and send him a
 | 
				
			||||||
 | 
					        message
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
 | 
				
			||||||
 | 
					        if nick in nicks:
 | 
				
			||||||
 | 
					            self.on_send_pm(nick=nick, msg=a_message)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise CommandError(_("Nickname not found"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True, empty=True)
 | 
				
			||||||
 | 
					    def topic(self, new_topic):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Display or change a group chat topic
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if new_topic:
 | 
				
			||||||
 | 
					            self.connection.send_gc_subject(self.room_jid, new_topic)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return self.subject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True, empty=True)
 | 
				
			||||||
 | 
					    def invite(self, jid, reason):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Invite a user to a room for a reason
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.connection.send_invite(self.room_jid, jid, reason)
 | 
				
			||||||
 | 
					        return _("Invited %s to %s") % (jid, self.room_jid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True, empty=True)
 | 
				
			||||||
 | 
					    def join(self, jid, nick):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Join a group chat given by a jid, optionally using given nickname
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if not nick:
 | 
				
			||||||
 | 
					            nick = self.nick
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if '@' not in jid:
 | 
				
			||||||
 | 
					            jid = jid + '@' + gajim.get_server_from_jid(self.room_jid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            gajim.interface.instances[self.account]['join_gc'].window.present()
 | 
				
			||||||
 | 
					        except KeyError:
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                dialogs.JoinGroupchatWindow(account=None, room_jid=jid, nick=nick)
 | 
				
			||||||
 | 
					            except GajimGeneralException:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command('part', 'close', raw=True, empty=True)
 | 
				
			||||||
 | 
					    def leave(self, reason):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Leave the groupchat, optionally giving a reason, and close tab or window
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.parent_win.remove_tab(self, self.parent_win.CLOSE_COMMAND, reason)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True, empty=True)
 | 
				
			||||||
 | 
					    def ban(self, who, reason):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Ban user by a nick or a jid from a groupchat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If given nickname is not found it will be treated as a jid.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if who in gajim.contacts.get_nick_list(self.account, self.room_jid):
 | 
				
			||||||
 | 
					            contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, who)
 | 
				
			||||||
 | 
					            who = contact.jid
 | 
				
			||||||
 | 
					        self.connection.gc_set_affiliation(self.room_jid, who, 'outcast', reason or str())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True, empty=True)
 | 
				
			||||||
 | 
					    def kick(self, who, reason):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Kick user by a nick from a groupchat
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if not who in gajim.contacts.get_nick_list(self.account, self.room_jid):
 | 
				
			||||||
 | 
					            raise CommandError(_("Nickname not found"))
 | 
				
			||||||
 | 
					        self.connection.gc_set_role(self.room_jid, who, 'none', reason or str())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command
 | 
				
			||||||
 | 
					    def names(self, verbose=False):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Display names of all group chat occupants
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get_contact = lambda nick: gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
 | 
				
			||||||
 | 
					        nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # First we do alpha-numeric sort and then role-based one.
 | 
				
			||||||
 | 
					        nicks.sort()
 | 
				
			||||||
 | 
					        nicks.sort(key=lambda nick: get_contact(nick).role)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if verbose:
 | 
				
			||||||
 | 
					            for nick in nicks:
 | 
				
			||||||
 | 
					                contact = get_contact(nick)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                role = helpers.get_uf_role(contact.role)
 | 
				
			||||||
 | 
					                affiliation = helpers.get_uf_affiliation(contact.affiliation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.echo("%s - %s - %s" % (nick, role, affiliation))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return ', '.join(nicks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True)
 | 
				
			||||||
 | 
					    def block(self, who):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Forbid an occupant to send you public or private messages
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.on_block(None, who)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @command(raw=True)
 | 
				
			||||||
 | 
					    def unblock(self, who):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Allow an occupant to send you public or privates messages
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.on_unblock(None, who)
 | 
				
			||||||
							
								
								
									
										105
									
								
								src/commands/middleware.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/commands/middleware.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,105 @@
 | 
				
			||||||
 | 
					# Copyright (C) 2009  red-agent <hell.director@gmail.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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Provides a glue to tie command system framework and the actual code where it
 | 
				
			||||||
 | 
					would be dropped in. Defines a little bit of scaffolding to support interaction
 | 
				
			||||||
 | 
					between the two and a few utility methods so you don't need to dig up the host
 | 
				
			||||||
 | 
					code to write basic commands.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from common import gajim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from types import StringTypes
 | 
				
			||||||
 | 
					from framework import CommandProcessor, CommandError
 | 
				
			||||||
 | 
					from traceback import print_exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChatMiddleware(CommandProcessor):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Provides basic scaffolding for the convenient interaction with ChatControl.
 | 
				
			||||||
 | 
					    Also provides some few basic utilities for the same purpose.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def process_as_command(self, text):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return super(ChatMiddleware, self).process_as_command(text)
 | 
				
			||||||
 | 
					        except CommandError, exception:
 | 
				
			||||||
 | 
					            self.echo("%s: %s" %(exception.name, exception.message), 'error')
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        except Exception:
 | 
				
			||||||
 | 
					            self.echo("An error occured while trying to execute the command", 'error')
 | 
				
			||||||
 | 
					            print_exc()
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        finally:
 | 
				
			||||||
 | 
					            self.add_history(text)
 | 
				
			||||||
 | 
					            self.clear_input()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def looks_like_command(self, text, name, arguments):
 | 
				
			||||||
 | 
					        # Command escape stuff ggoes here. If text was prepended by the command
 | 
				
			||||||
 | 
					        # prefix twice, like //not_a_command (if prefix is set to /) then it
 | 
				
			||||||
 | 
					        # will be escaped, that is sent just as a regular message with one (only
 | 
				
			||||||
 | 
					        # one) prefix removed, so message will be /not_a_command.
 | 
				
			||||||
 | 
					        if name.startswith(self.COMMAND_PREFIX):
 | 
				
			||||||
 | 
					            self._say_(self, text)
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def command_preprocessor(self, name, command, arguments, args, kwargs):
 | 
				
			||||||
 | 
					        if 'h' in kwargs or 'help' in kwargs:
 | 
				
			||||||
 | 
					            # Forwarding to the /help command. Dont forget to pass self, as
 | 
				
			||||||
 | 
					            # all commands are unbound. And also don't forget to print output.
 | 
				
			||||||
 | 
					            self.echo(self._help_(self, name))
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def command_postprocessor(self, name, command, arguments, args, kwargs, value):
 | 
				
			||||||
 | 
					        if value and isinstance(value, StringTypes):
 | 
				
			||||||
 | 
					            self.echo(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def echo(self, text, kind='info'):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Print given text to the user.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.print_conversation(str(text), kind)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def send(self, text):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Send a message to the contact.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.send_message(text, process_commands=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_input(self, text):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Set given text into the input.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        message_buffer = self.msg_textview.get_buffer()
 | 
				
			||||||
 | 
					        message_buffer.set_text(text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def clear_input(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Clear input.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.set_input(str())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_history(self, text):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Add given text to the input history, so user can scroll through it
 | 
				
			||||||
 | 
					        using ctrl + up/down arrow keys.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.save_sent_message(text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def connection(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Get the current connection object.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return gajim.connections[self.account]
 | 
				
			||||||
| 
						 | 
					@ -67,7 +67,6 @@ class Config:
 | 
				
			||||||
	__options = {
 | 
						__options = {
 | 
				
			||||||
		# name: [ type, default_value, help_string ]
 | 
							# name: [ type, default_value, help_string ]
 | 
				
			||||||
		'verbose': [ opt_bool, False, '', True ],
 | 
							'verbose': [ opt_bool, False, '', True ],
 | 
				
			||||||
		'alwaysauth': [ opt_bool, False ],
 | 
					 | 
				
			||||||
		'autopopup': [ opt_bool, False ],
 | 
							'autopopup': [ opt_bool, False ],
 | 
				
			||||||
		'notify_on_signin': [ opt_bool, True ],
 | 
							'notify_on_signin': [ opt_bool, True ],
 | 
				
			||||||
		'notify_on_signout': [ opt_bool, False ],
 | 
							'notify_on_signout': [ opt_bool, False ],
 | 
				
			||||||
| 
						 | 
					@ -249,6 +248,8 @@ class Config:
 | 
				
			||||||
		'gc_nicknames_colors': [ opt_str, '#a34526:#c000ff:#0012ff:#388a99:#045723:#7c7c7c:#ff8a00:#94452d:#244b5a:#32645a', _('List of colors, separated by ":", that will be used to color nicknames in group chats.'), True ],
 | 
							'gc_nicknames_colors': [ opt_str, '#a34526:#c000ff:#0012ff:#388a99:#045723:#7c7c7c:#ff8a00:#94452d:#244b5a:#32645a', _('List of colors, separated by ":", that will be used to color nicknames in group chats.'), True ],
 | 
				
			||||||
		'ctrl_tab_go_to_next_composing': [opt_bool, True, _('Ctrl-Tab go to next composing tab when none is unread.')],
 | 
							'ctrl_tab_go_to_next_composing': [opt_bool, True, _('Ctrl-Tab go to next composing tab when none is unread.')],
 | 
				
			||||||
		'confirm_metacontacts': [ opt_str, '', _('Should we show the confirm metacontacts creation dialog or not? Empty string means we never show the dialog.')],
 | 
							'confirm_metacontacts': [ opt_str, '', _('Should we show the confirm metacontacts creation dialog or not? Empty string means we never show the dialog.')],
 | 
				
			||||||
 | 
							'confirm_block': [ opt_str, '', _('Should we show the confirm block contact dialog or not? Empty string means we never show the dialog.')],
 | 
				
			||||||
 | 
							'confirm_custom_status': [ opt_str, '', _('Should we show the confirm custom status dialog or not? Empty string means we never show the dialog.')],
 | 
				
			||||||
		'enable_negative_priority': [ opt_bool, False, _('If True, you will be able to set a negative priority to your account in account modification window. BE CAREFUL, when you are logged in with a negative priority, you will NOT receive any message from your server.')],
 | 
							'enable_negative_priority': [ opt_bool, False, _('If True, you will be able to set a negative priority to your account in account modification window. BE CAREFUL, when you are logged in with a negative priority, you will NOT receive any message from your server.')],
 | 
				
			||||||
		'use_gnomekeyring': [opt_bool, True, _('If True, Gajim will use Gnome Keyring (if available) to store account passwords.')],
 | 
							'use_gnomekeyring': [opt_bool, True, _('If True, Gajim will use Gnome Keyring (if available) to store account passwords.')],
 | 
				
			||||||
		'use_kwalletcli': [opt_bool, True, _('If True, Gajim will use KDE Wallet (if kwalletcli is available) to store account passwords.')],
 | 
							'use_kwalletcli': [opt_bool, True, _('If True, Gajim will use KDE Wallet (if kwalletcli is available) to store account passwords.')],
 | 
				
			||||||
| 
						 | 
					@ -285,6 +286,7 @@ class Config:
 | 
				
			||||||
			'autoconnect_as': [ opt_str, 'online', _('Status used to autoconnect as. Can be online, chat, away, xa, dnd, invisible. NOTE: this option is used only if restore_last_status is disabled'), True ],
 | 
								'autoconnect_as': [ opt_str, 'online', _('Status used to autoconnect as. Can be online, chat, away, xa, dnd, invisible. NOTE: this option is used only if restore_last_status is disabled'), True ],
 | 
				
			||||||
			'restore_last_status': [ opt_bool, False, _('If enabled, restore the last status that was used.') ],
 | 
								'restore_last_status': [ opt_bool, False, _('If enabled, restore the last status that was used.') ],
 | 
				
			||||||
			'autoreconnect': [ opt_bool, True ],
 | 
								'autoreconnect': [ opt_bool, True ],
 | 
				
			||||||
 | 
								'autoauth': [ opt_bool, False, _('If True, Contacts requesting authorization will be automatically accepted.')],
 | 
				
			||||||
			'active': [ opt_bool, True],
 | 
								'active': [ opt_bool, True],
 | 
				
			||||||
			'proxy': [ opt_str, '', '', True ],
 | 
								'proxy': [ opt_str, '', '', True ],
 | 
				
			||||||
			'keyid': [ opt_str, '', '', True ],
 | 
								'keyid': [ opt_str, '', '', True ],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,7 +219,7 @@ class Connection(ConnectionHandlers):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# We are doing disconnect at so many places, better use one function in all
 | 
						# We are doing disconnect at so many places, better use one function in all
 | 
				
			||||||
	def disconnect(self, on_purpose=False):
 | 
						def disconnect(self, on_purpose=False):
 | 
				
			||||||
		gajim.interface.roster.music_track_changed(None, None, self.name)
 | 
							gajim.interface.music_track_changed(None, None, self.name)
 | 
				
			||||||
		self.on_purpose = on_purpose
 | 
							self.on_purpose = on_purpose
 | 
				
			||||||
		self.connected = 0
 | 
							self.connected = 0
 | 
				
			||||||
		self.time_to_reconnect = None
 | 
							self.time_to_reconnect = None
 | 
				
			||||||
| 
						 | 
					@ -1199,7 +1199,7 @@ class Connection(ConnectionHandlers):
 | 
				
			||||||
		msgenc = ''
 | 
							msgenc = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if session:
 | 
							if session:
 | 
				
			||||||
			fjid = str(session.jid)
 | 
								fjid = session.get_to()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if keyID and self.USE_GPG:
 | 
							if keyID and self.USE_GPG:
 | 
				
			||||||
			xhtml = None
 | 
								xhtml = None
 | 
				
			||||||
| 
						 | 
					@ -1953,8 +1953,15 @@ class Connection(ConnectionHandlers):
 | 
				
			||||||
				hostname = gajim.config.get_per('accounts', self.name, 'hostname')
 | 
									hostname = gajim.config.get_per('accounts', self.name, 'hostname')
 | 
				
			||||||
				iq = common.xmpp.Iq(typ = 'set', to = hostname)
 | 
									iq = common.xmpp.Iq(typ = 'set', to = hostname)
 | 
				
			||||||
				iq.setTag(common.xmpp.NS_REGISTER + ' query').setTag('remove')
 | 
									iq.setTag(common.xmpp.NS_REGISTER + ' query').setTag('remove')
 | 
				
			||||||
				con.send(iq)
 | 
									def _on_answer(result):
 | 
				
			||||||
				on_remove_success(True)
 | 
										if result.getType() == 'result':
 | 
				
			||||||
 | 
											on_remove_success(True)
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										self.dispatch('ERROR', (_('Unregister failed'),
 | 
				
			||||||
 | 
											_('Unregistration with server %(server)s failed: %(error)s') \
 | 
				
			||||||
 | 
											% {'server': hostname, 'error': result.getErrorMsg()}))
 | 
				
			||||||
 | 
										on_remove_success(False)
 | 
				
			||||||
 | 
									con.SendAndCallForResponse(iq, _on_answer)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			on_remove_success(False)
 | 
								on_remove_success(False)
 | 
				
			||||||
		if self.connected == 0:
 | 
							if self.connected == 0:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -900,8 +900,8 @@ class ConnectionDisco:
 | 
				
			||||||
							track = listener.get_playing_track()
 | 
												track = listener.get_playing_track()
 | 
				
			||||||
							if gajim.config.get_per('accounts', self.name,
 | 
												if gajim.config.get_per('accounts', self.name,
 | 
				
			||||||
							'publish_tune'):
 | 
												'publish_tune'):
 | 
				
			||||||
								gajim.interface.roster.music_track_changed(listener,
 | 
													gajim.interface.music_track_changed(listener, track,
 | 
				
			||||||
										track, self.name)
 | 
														self.name)
 | 
				
			||||||
						break
 | 
											break
 | 
				
			||||||
			if features.__contains__(common.xmpp.NS_VCARD):
 | 
								if features.__contains__(common.xmpp.NS_VCARD):
 | 
				
			||||||
				self.vcard_supported = True
 | 
									self.vcard_supported = True
 | 
				
			||||||
| 
						 | 
					@ -2355,13 +2355,14 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ptype == 'subscribe':
 | 
							if ptype == 'subscribe':
 | 
				
			||||||
			log.debug('subscribe request from %s' % who)
 | 
								log.debug('subscribe request from %s' % who)
 | 
				
			||||||
			if gajim.config.get('alwaysauth') or who.find("@") <= 0 or \
 | 
								if gajim.config.get_per('accounts', self.name, 'autoauth') or \
 | 
				
			||||||
			jid_stripped in self.jids_for_auto_auth or transport_auto_auth:
 | 
								who.find('@') <= 0 or jid_stripped in self.jids_for_auto_auth or \
 | 
				
			||||||
 | 
								transport_auto_auth:
 | 
				
			||||||
				if self.connection:
 | 
									if self.connection:
 | 
				
			||||||
					p = common.xmpp.Presence(who, 'subscribed')
 | 
										p = common.xmpp.Presence(who, 'subscribed')
 | 
				
			||||||
					p = self.add_sha(p)
 | 
										p = self.add_sha(p)
 | 
				
			||||||
					self.connection.send(p)
 | 
										self.connection.send(p)
 | 
				
			||||||
				if who.find("@") <= 0 or transport_auto_auth:
 | 
									if who.find('@') <= 0 or transport_auto_auth:
 | 
				
			||||||
					self.dispatch('NOTIFY', (jid_stripped, 'offline', 'offline',
 | 
										self.dispatch('NOTIFY', (jid_stripped, 'offline', 'offline',
 | 
				
			||||||
						resource, prio, keyID, timestamp, None))
 | 
											resource, prio, keyID, timestamp, None))
 | 
				
			||||||
				if transport_auto_auth:
 | 
									if transport_auto_auth:
 | 
				
			||||||
| 
						 | 
					@ -2617,9 +2618,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
 | 
				
			||||||
		if sign_msg and not signed:
 | 
							if sign_msg and not signed:
 | 
				
			||||||
			signed = self.get_signed_presence(msg)
 | 
								signed = self.get_signed_presence(msg)
 | 
				
			||||||
			if signed is None:
 | 
								if signed is None:
 | 
				
			||||||
				self.dispatch('ERROR', (_('OpenPGP passphrase was not given'),
 | 
									self.dispatch('BAD_PASSPHRASE', ())
 | 
				
			||||||
					#%s is the account name here
 | 
					 | 
				
			||||||
					_('You will be connected to %s without OpenPGP.') % self.name))
 | 
					 | 
				
			||||||
				self.USE_GPG = False
 | 
									self.USE_GPG = False
 | 
				
			||||||
				signed = ''
 | 
									signed = ''
 | 
				
			||||||
		self.connected = gajim.SHOW_LIST.index(show)
 | 
							self.connected = gajim.SHOW_LIST.index(show)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ docdir = '../'
 | 
				
			||||||
datadir = '../'
 | 
					datadir = '../'
 | 
				
			||||||
localedir = '../po'
 | 
					localedir = '../po'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
version = '0.12.5.1-dev'
 | 
					version = '0.12.5.2-dev'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sys, os.path
 | 
					import sys, os.path
 | 
				
			||||||
for base in ('.', 'common'):
 | 
					for base in ('.', 'common'):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,6 +114,12 @@ def latex_to_image(str_):
 | 
				
			||||||
	result = None
 | 
						result = None
 | 
				
			||||||
	exitcode = 0
 | 
						exitcode = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try:
 | 
				
			||||||
 | 
							bg_str, fg_str = gajim.interface.get_bg_fg_colors()
 | 
				
			||||||
 | 
						except:
 | 
				
			||||||
 | 
							# interface may not be available when we test latext at startup
 | 
				
			||||||
 | 
							bg_str, fg_str = 'rgb 1.0 1.0 1.0', 'rgb 0.0 0.0 0.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# filter latex code with bad commands
 | 
						# filter latex code with bad commands
 | 
				
			||||||
	if check_blacklist(str_):
 | 
						if check_blacklist(str_):
 | 
				
			||||||
		# we triggered the blacklist, immediately return None
 | 
							# we triggered the blacklist, immediately return None
 | 
				
			||||||
| 
						 | 
					@ -131,7 +137,7 @@ def latex_to_image(str_):
 | 
				
			||||||
	if exitcode == 0:
 | 
						if exitcode == 0:
 | 
				
			||||||
		# convert dvi to png
 | 
							# convert dvi to png
 | 
				
			||||||
		latex_png_dpi = gajim.config.get('latex_png_dpi')
 | 
							latex_png_dpi = gajim.config.get('latex_png_dpi')
 | 
				
			||||||
		exitcode = try_run(['dvipng', '-bg', 'rgb 1.0 1.0 1.0', '-T',
 | 
							exitcode = try_run(['dvipng', '-bg', bg_str, '-fg', fg_str, '-T',
 | 
				
			||||||
				'tight', '-D', latex_png_dpi, tmpfile + '.dvi', '-o',
 | 
									'tight', '-D', latex_png_dpi, tmpfile + '.dvi', '-o',
 | 
				
			||||||
				tmpfile + '.png'])
 | 
									tmpfile + '.png'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -202,6 +202,8 @@ class OptionsParser:
 | 
				
			||||||
			self.update_config_to_01231()
 | 
								self.update_config_to_01231()
 | 
				
			||||||
		if old < [0, 12, 5, 1] and new >= [0, 12, 5, 1]:
 | 
							if old < [0, 12, 5, 1] and new >= [0, 12, 5, 1]:
 | 
				
			||||||
			self.update_config_to_01251()
 | 
								self.update_config_to_01251()
 | 
				
			||||||
 | 
							if old < [0, 12, 5, 2] and new >= [0, 12, 5, 2]:
 | 
				
			||||||
 | 
								self.update_config_to_01252()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		gajim.logger.init_vars()
 | 
							gajim.logger.init_vars()
 | 
				
			||||||
		gajim.config.set('version', new_version)
 | 
							gajim.config.set('version', new_version)
 | 
				
			||||||
| 
						 | 
					@ -727,4 +729,11 @@ class OptionsParser:
 | 
				
			||||||
		con.close()
 | 
							con.close()
 | 
				
			||||||
		gajim.config.set('version', '0.12.5.1')
 | 
							gajim.config.set('version', '0.12.5.1')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def update_config_to_01252(self):
 | 
				
			||||||
 | 
							if 'alwaysauth' in self.old_values:
 | 
				
			||||||
 | 
								val = self.old_values['alwaysauth']
 | 
				
			||||||
 | 
								for account in gajim.config.get_per('accounts'):
 | 
				
			||||||
 | 
									gajim.config.set_per('accounts', account, 'autoauth', val)
 | 
				
			||||||
 | 
							gajim.config.set('version', '0.12.5.2')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# vim: se ts=3:
 | 
					# vim: se ts=3:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,6 +54,7 @@ class StanzaSession(object):
 | 
				
			||||||
		self.conn = conn
 | 
							self.conn = conn
 | 
				
			||||||
		self.jid = jid
 | 
							self.jid = jid
 | 
				
			||||||
		self.type = type_
 | 
							self.type = type_
 | 
				
			||||||
 | 
							self.resource = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if thread_id:
 | 
							if thread_id:
 | 
				
			||||||
			self.received_thread_id = True
 | 
								self.received_thread_id = True
 | 
				
			||||||
| 
						 | 
					@ -75,6 +76,12 @@ class StanzaSession(object):
 | 
				
			||||||
	def is_loggable(self):
 | 
						def is_loggable(self):
 | 
				
			||||||
		return self.loggable and gajim.config.should_log(self.conn.name, self.jid)
 | 
							return self.loggable and gajim.config.should_log(self.conn.name, self.jid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def get_to(self):
 | 
				
			||||||
 | 
							to = str(self.jid)
 | 
				
			||||||
 | 
							if self.resource:
 | 
				
			||||||
 | 
								to += '/' + self.resource
 | 
				
			||||||
 | 
							return to
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def remove_events(self, types):
 | 
						def remove_events(self, types):
 | 
				
			||||||
		'''
 | 
							'''
 | 
				
			||||||
		Remove events associated with this session from the queue.
 | 
							Remove events associated with this session from the queue.
 | 
				
			||||||
| 
						 | 
					@ -107,7 +114,7 @@ class StanzaSession(object):
 | 
				
			||||||
		if self.thread_id:
 | 
							if self.thread_id:
 | 
				
			||||||
			msg.NT.thread = self.thread_id
 | 
								msg.NT.thread = self.thread_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		msg.setAttr('to', self.jid)
 | 
							msg.setAttr('to', self.get_to())
 | 
				
			||||||
		self.conn.send_stanza(msg)
 | 
							self.conn.send_stanza(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if isinstance(msg, xmpp.Message):
 | 
							if isinstance(msg, xmpp.Message):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1077,6 +1077,7 @@ class ManageProxiesWindow:
 | 
				
			||||||
		self.proxytype_combobox = self.xml.get_widget('proxytype_combobox')
 | 
							self.proxytype_combobox = self.xml.get_widget('proxytype_combobox')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		self.init_list()
 | 
							self.init_list()
 | 
				
			||||||
 | 
							self.block_signal = False
 | 
				
			||||||
		self.xml.signal_autoconnect(self)
 | 
							self.xml.signal_autoconnect(self)
 | 
				
			||||||
		self.window.show_all()
 | 
							self.window.show_all()
 | 
				
			||||||
		# hide the BOSH fields by default
 | 
							# hide the BOSH fields by default
 | 
				
			||||||
| 
						 | 
					@ -1134,6 +1135,7 @@ class ManageProxiesWindow:
 | 
				
			||||||
		iter_ = model.append()
 | 
							iter_ = model.append()
 | 
				
			||||||
		model.set(iter_, 0, 'proxy' + unicode(i))
 | 
							model.set(iter_, 0, 'proxy' + unicode(i))
 | 
				
			||||||
		gajim.config.add_per('proxies', 'proxy' + unicode(i))
 | 
							gajim.config.add_per('proxies', 'proxy' + unicode(i))
 | 
				
			||||||
 | 
							self.proxies_treeview.set_cursor(model.get_path(iter_))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_remove_proxy_button_clicked(self, widget):
 | 
						def on_remove_proxy_button_clicked(self, widget):
 | 
				
			||||||
		(model, iter_) = self.proxies_treeview.get_selection().get_selected()
 | 
							(model, iter_) = self.proxies_treeview.get_selection().get_selected()
 | 
				
			||||||
| 
						 | 
					@ -1143,11 +1145,16 @@ class ManageProxiesWindow:
 | 
				
			||||||
		model.remove(iter_)
 | 
							model.remove(iter_)
 | 
				
			||||||
		gajim.config.del_per('proxies', proxy)
 | 
							gajim.config.del_per('proxies', proxy)
 | 
				
			||||||
		self.xml.get_widget('remove_proxy_button').set_sensitive(False)
 | 
							self.xml.get_widget('remove_proxy_button').set_sensitive(False)
 | 
				
			||||||
 | 
							self.block_signal = True
 | 
				
			||||||
 | 
							self.on_proxies_treeview_cursor_changed(self.proxies_treeview)
 | 
				
			||||||
 | 
							self.block_signal = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_close_button_clicked(self, widget):
 | 
						def on_close_button_clicked(self, widget):
 | 
				
			||||||
		self.window.destroy()
 | 
							self.window.destroy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_useauth_checkbutton_toggled(self, widget):
 | 
						def on_useauth_checkbutton_toggled(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		act = widget.get_active()
 | 
							act = widget.get_active()
 | 
				
			||||||
		proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
							proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'useauth', act)
 | 
							gajim.config.set_per('proxies', proxy, 'useauth', act)
 | 
				
			||||||
| 
						 | 
					@ -1155,6 +1162,8 @@ class ManageProxiesWindow:
 | 
				
			||||||
		self.xml.get_widget('proxypass_entry').set_sensitive(act)
 | 
							self.xml.get_widget('proxypass_entry').set_sensitive(act)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_boshuseproxy_checkbutton_toggled(self, widget):
 | 
						def on_boshuseproxy_checkbutton_toggled(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		act = widget.get_active()
 | 
							act = widget.get_active()
 | 
				
			||||||
		proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
							proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'bosh_useproxy', act)
 | 
							gajim.config.set_per('proxies', proxy, 'bosh_useproxy', act)
 | 
				
			||||||
| 
						 | 
					@ -1164,11 +1173,6 @@ class ManageProxiesWindow:
 | 
				
			||||||
	def on_proxies_treeview_cursor_changed(self, widget):
 | 
						def on_proxies_treeview_cursor_changed(self, widget):
 | 
				
			||||||
		#FIXME: check if off proxy settings are correct (see
 | 
							#FIXME: check if off proxy settings are correct (see
 | 
				
			||||||
		# http://trac.gajim.org/changeset/1921#file2 line 1221
 | 
							# http://trac.gajim.org/changeset/1921#file2 line 1221
 | 
				
			||||||
		(model, iter_) = widget.get_selection().get_selected()
 | 
					 | 
				
			||||||
		if not iter_:
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		proxy = model[iter_][0]
 | 
					 | 
				
			||||||
		self.xml.get_widget('proxyname_entry').set_text(proxy)
 | 
					 | 
				
			||||||
		proxyhost_entry = self.xml.get_widget('proxyhost_entry')
 | 
							proxyhost_entry = self.xml.get_widget('proxyhost_entry')
 | 
				
			||||||
		proxyport_entry = self.xml.get_widget('proxyport_entry')
 | 
							proxyport_entry = self.xml.get_widget('proxyport_entry')
 | 
				
			||||||
		proxyuser_entry = self.xml.get_widget('proxyuser_entry')
 | 
							proxyuser_entry = self.xml.get_widget('proxyuser_entry')
 | 
				
			||||||
| 
						 | 
					@ -1176,6 +1180,7 @@ class ManageProxiesWindow:
 | 
				
			||||||
		boshuri_entry = self.xml.get_widget('boshuri_entry')
 | 
							boshuri_entry = self.xml.get_widget('boshuri_entry')
 | 
				
			||||||
		useauth_checkbutton = self.xml.get_widget('useauth_checkbutton')
 | 
							useauth_checkbutton = self.xml.get_widget('useauth_checkbutton')
 | 
				
			||||||
		boshuseproxy_checkbutton = self.xml.get_widget('boshuseproxy_checkbutton')
 | 
							boshuseproxy_checkbutton = self.xml.get_widget('boshuseproxy_checkbutton')
 | 
				
			||||||
 | 
							self.block_signal = True
 | 
				
			||||||
		proxyhost_entry.set_text('')
 | 
							proxyhost_entry.set_text('')
 | 
				
			||||||
		proxyport_entry.set_text('')
 | 
							proxyport_entry.set_text('')
 | 
				
			||||||
		proxyuser_entry.set_text('')
 | 
							proxyuser_entry.set_text('')
 | 
				
			||||||
| 
						 | 
					@ -1188,6 +1193,17 @@ class ManageProxiesWindow:
 | 
				
			||||||
		#useauth_checkbutton.set_active(False)
 | 
							#useauth_checkbutton.set_active(False)
 | 
				
			||||||
		#self.on_useauth_checkbutton_toggled(useauth_checkbutton)
 | 
							#self.on_useauth_checkbutton_toggled(useauth_checkbutton)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							(model, iter_) = widget.get_selection().get_selected()
 | 
				
			||||||
 | 
							if not iter_:
 | 
				
			||||||
 | 
								self.xml.get_widget('proxyname_entry').set_text('')
 | 
				
			||||||
 | 
								self.xml.get_widget('proxytype_combobox').set_sensitive(False)
 | 
				
			||||||
 | 
								self.xml.get_widget('proxy_table').set_sensitive(False)
 | 
				
			||||||
 | 
								self.block_signal = False
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							proxy = model[iter_][0]
 | 
				
			||||||
 | 
							self.xml.get_widget('proxyname_entry').set_text(proxy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if proxy == _('None'): # special proxy None
 | 
							if proxy == _('None'): # special proxy None
 | 
				
			||||||
			self.show_bosh_fields(False)
 | 
								self.show_bosh_fields(False)
 | 
				
			||||||
			self.proxyname_entry.set_editable(False)
 | 
								self.proxyname_entry.set_editable(False)
 | 
				
			||||||
| 
						 | 
					@ -1219,12 +1235,15 @@ class ManageProxiesWindow:
 | 
				
			||||||
				gajim.config.get_per('proxies', proxy, 'bosh_useproxy'))
 | 
									gajim.config.get_per('proxies', proxy, 'bosh_useproxy'))
 | 
				
			||||||
			useauth_checkbutton.set_active(
 | 
								useauth_checkbutton.set_active(
 | 
				
			||||||
				gajim.config.get_per('proxies', proxy, 'useauth'))
 | 
									gajim.config.get_per('proxies', proxy, 'useauth'))
 | 
				
			||||||
 | 
							self.block_signal = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_proxies_treeview_key_press_event(self, widget, event):
 | 
						def on_proxies_treeview_key_press_event(self, widget, event):
 | 
				
			||||||
		if event.keyval == gtk.keysyms.Delete:
 | 
							if event.keyval == gtk.keysyms.Delete:
 | 
				
			||||||
			self.on_remove_proxy_button_clicked(widget)
 | 
								self.on_remove_proxy_button_clicked(widget)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_proxyname_entry_changed(self, widget):
 | 
						def on_proxyname_entry_changed(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		(model, iter_) = self.proxies_treeview.get_selection().get_selected()
 | 
							(model, iter_) = self.proxies_treeview.get_selection().get_selected()
 | 
				
			||||||
		if not iter_:
 | 
							if not iter_:
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
| 
						 | 
					@ -1243,6 +1262,8 @@ class ManageProxiesWindow:
 | 
				
			||||||
		model.set_value(iter_, 0, new_name)
 | 
							model.set_value(iter_, 0, new_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_proxytype_combobox_changed(self, widget):
 | 
						def on_proxytype_combobox_changed(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		types = ['http', 'socks5', 'bosh']
 | 
							types = ['http', 'socks5', 'bosh']
 | 
				
			||||||
		type_ = self.proxytype_combobox.get_active()
 | 
							type_ = self.proxytype_combobox.get_active()
 | 
				
			||||||
		self.show_bosh_fields(types[type_]=='bosh')
 | 
							self.show_bosh_fields(types[type_]=='bosh')
 | 
				
			||||||
| 
						 | 
					@ -1250,26 +1271,36 @@ class ManageProxiesWindow:
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'type', types[type_])
 | 
							gajim.config.set_per('proxies', proxy, 'type', types[type_])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_proxyhost_entry_changed(self, widget):
 | 
						def on_proxyhost_entry_changed(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		value = widget.get_text().decode('utf-8')
 | 
							value = widget.get_text().decode('utf-8')
 | 
				
			||||||
		proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
							proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'host', value)
 | 
							gajim.config.set_per('proxies', proxy, 'host', value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_proxyport_entry_changed(self, widget):
 | 
						def on_proxyport_entry_changed(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		value = widget.get_text().decode('utf-8')
 | 
							value = widget.get_text().decode('utf-8')
 | 
				
			||||||
		proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
							proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'port', value)
 | 
							gajim.config.set_per('proxies', proxy, 'port', value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_proxyuser_entry_changed(self, widget):
 | 
						def on_proxyuser_entry_changed(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		value = widget.get_text().decode('utf-8')
 | 
							value = widget.get_text().decode('utf-8')
 | 
				
			||||||
		proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
							proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'user', value)
 | 
							gajim.config.set_per('proxies', proxy, 'user', value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_boshuri_entry_changed(self, widget):
 | 
						def on_boshuri_entry_changed(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		value = widget.get_text().decode('utf-8')
 | 
							value = widget.get_text().decode('utf-8')
 | 
				
			||||||
		proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
							proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'bosh_uri', value)
 | 
							gajim.config.set_per('proxies', proxy, 'bosh_uri', value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_proxypass_entry_changed(self, widget):
 | 
						def on_proxypass_entry_changed(self, widget):
 | 
				
			||||||
 | 
							if self.block_signal:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		value = widget.get_text().decode('utf-8')
 | 
							value = widget.get_text().decode('utf-8')
 | 
				
			||||||
		proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
							proxy = self.proxyname_entry.get_text().decode('utf-8')
 | 
				
			||||||
		gajim.config.set_per('proxies', proxy, 'pass', value)
 | 
							gajim.config.set_per('proxies', proxy, 'pass', value)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1698,7 +1698,8 @@ class ChangeNickDialog(InputDialogCheck):
 | 
				
			||||||
		if len(self.room_queue) == 0:
 | 
							if len(self.room_queue) == 0:
 | 
				
			||||||
			self.cancel_handler = None
 | 
								self.cancel_handler = None
 | 
				
			||||||
			self.dialog.destroy()
 | 
								self.dialog.destroy()
 | 
				
			||||||
			del gajim.interface.instances['change_nick_dialog']
 | 
								if 'change_nick_dialog' in gajim.interface.instances:
 | 
				
			||||||
 | 
									del gajim.interface.instances['change_nick_dialog']
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		self.account, self.room_jid, self.prompt = self.room_queue.pop(0)
 | 
							self.account, self.room_jid, self.prompt = self.room_queue.pop(0)
 | 
				
			||||||
		self.setup_dialog()
 | 
							self.setup_dialog()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1137,11 +1137,11 @@ class ToplevelAgentBrowser(AgentBrowser):
 | 
				
			||||||
		# Icon Renderer
 | 
							# Icon Renderer
 | 
				
			||||||
		renderer = gtk.CellRendererPixbuf()
 | 
							renderer = gtk.CellRendererPixbuf()
 | 
				
			||||||
		renderer.set_property('xpad', 6)
 | 
							renderer.set_property('xpad', 6)
 | 
				
			||||||
		col.pack_start(renderer, expand = False)
 | 
							col.pack_start(renderer, expand=False)
 | 
				
			||||||
		col.set_cell_data_func(renderer, self._pixbuf_renderer_data_func)
 | 
							col.set_cell_data_func(renderer, self._pixbuf_renderer_data_func)
 | 
				
			||||||
		# Text Renderer
 | 
							# Text Renderer
 | 
				
			||||||
		renderer = gtk.CellRendererText()
 | 
							renderer = gtk.CellRendererText()
 | 
				
			||||||
		col.pack_start(renderer, expand = True)
 | 
							col.pack_start(renderer, expand=True)
 | 
				
			||||||
		col.set_cell_data_func(renderer, self._text_renderer_data_func)
 | 
							col.set_cell_data_func(renderer, self._text_renderer_data_func)
 | 
				
			||||||
		renderer.set_property('foreground', 'dark gray')
 | 
							renderer.set_property('foreground', 'dark gray')
 | 
				
			||||||
		# Save this so we can go along with theme changes
 | 
							# Save this so we can go along with theme changes
 | 
				
			||||||
| 
						 | 
					@ -1487,7 +1487,7 @@ class ToplevelAgentBrowser(AgentBrowser):
 | 
				
			||||||
		if not cat:
 | 
							if not cat:
 | 
				
			||||||
			cat = self._create_category(*cat_args)
 | 
								cat = self._create_category(*cat_args)
 | 
				
			||||||
		self.model.append(cat, (jid, node, pix, descr, 1))
 | 
							self.model.append(cat, (jid, node, pix, descr, 1))
 | 
				
			||||||
		self._expand_all()
 | 
							gobject.idle_add(self._expand_all)
 | 
				
			||||||
		# Grab info on the service
 | 
							# Grab info on the service
 | 
				
			||||||
		self.cache.get_info(jid, node, self._agent_info, force=force)
 | 
							self.cache.get_info(jid, node, self._agent_info, force=force)
 | 
				
			||||||
		self._update_progressbar()
 | 
							self._update_progressbar()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										113
									
								
								src/gajim.py
									
										
									
									
									
								
							
							
						
						
									
										113
									
								
								src/gajim.py
									
										
									
									
									
								
							| 
						 | 
					@ -156,6 +156,7 @@ except exceptions.DatabaseMalformed:
 | 
				
			||||||
else:
 | 
					else:
 | 
				
			||||||
	from common import dbus_support
 | 
						from common import dbus_support
 | 
				
			||||||
	if dbus_support.supported:
 | 
						if dbus_support.supported:
 | 
				
			||||||
 | 
							from music_track_listener import MusicTrackListener
 | 
				
			||||||
		import dbus
 | 
							import dbus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if os.name == 'posix': # dl module is Unix Only
 | 
						if os.name == 'posix': # dl module is Unix Only
 | 
				
			||||||
| 
						 | 
					@ -229,6 +230,15 @@ from chat_control import ChatControlBase
 | 
				
			||||||
from chat_control import ChatControl
 | 
					from chat_control import ChatControl
 | 
				
			||||||
from groupchat_control import GroupchatControl
 | 
					from groupchat_control import GroupchatControl
 | 
				
			||||||
from groupchat_control import PrivateChatControl
 | 
					from groupchat_control import PrivateChatControl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Here custom adhoc processors should be loaded. At this point there is
 | 
				
			||||||
 | 
					# everything they need to function properly. The next line loads custom exmple
 | 
				
			||||||
 | 
					# adhoc processors. Technically, they could be loaded earlier as host processors
 | 
				
			||||||
 | 
					# themself does not depend on the chat controls, but that should not be done
 | 
				
			||||||
 | 
					# uless there is a really good reason for that..
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# from commands import custom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from atom_window import AtomWindow
 | 
					from atom_window import AtomWindow
 | 
				
			||||||
from session import ChatControlSession
 | 
					from session import ChatControlSession
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -243,6 +253,7 @@ from common import helpers
 | 
				
			||||||
from common import optparser
 | 
					from common import optparser
 | 
				
			||||||
from common import dataforms
 | 
					from common import dataforms
 | 
				
			||||||
from common import passwords
 | 
					from common import passwords
 | 
				
			||||||
 | 
					from common import pep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gajimpaths = common.configpaths.gajimpaths
 | 
					gajimpaths = common.configpaths.gajimpaths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1507,10 +1518,15 @@ class Interface:
 | 
				
			||||||
		if use_gpg_agent:
 | 
							if use_gpg_agent:
 | 
				
			||||||
			sectext = _('You configured Gajim to use GPG agent, but there is no '
 | 
								sectext = _('You configured Gajim to use GPG agent, but there is no '
 | 
				
			||||||
			'GPG agent running or it returned a wrong passphrase.\n')
 | 
								'GPG agent running or it returned a wrong passphrase.\n')
 | 
				
			||||||
		sectext += _('You are currently connected without your OpenPGP key.')
 | 
								sectext += _('You are currently connected without your OpenPGP key.')
 | 
				
			||||||
 | 
								dialogs.WarningDialog(_('Your passphrase is incorrect'), sectext)
 | 
				
			||||||
 | 
							else:
 | 
				
			||||||
 | 
								path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'warning.png')
 | 
				
			||||||
 | 
								notify.popup('warning', account, account, 'warning', path,
 | 
				
			||||||
 | 
									_('OpenGPG Passphrase Incorrect'),
 | 
				
			||||||
 | 
									_('You are currently connected without your OpenPGP key.'))
 | 
				
			||||||
		keyID = gajim.config.get_per('accounts', account, 'keyid')
 | 
							keyID = gajim.config.get_per('accounts', account, 'keyid')
 | 
				
			||||||
		self.forget_gpg_passphrase(keyID)
 | 
							self.forget_gpg_passphrase(keyID)
 | 
				
			||||||
		dialogs.WarningDialog(_('Your passphrase is incorrect'), sectext)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def handle_event_gpg_password_required(self, account, array):
 | 
						def handle_event_gpg_password_required(self, account, array):
 | 
				
			||||||
		#('GPG_PASSWORD_REQUIRED', account, (callback,))
 | 
							#('GPG_PASSWORD_REQUIRED', account, (callback,))
 | 
				
			||||||
| 
						 | 
					@ -3048,6 +3064,93 @@ class Interface:
 | 
				
			||||||
### Other Methods
 | 
					### Other Methods
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def _change_awn_icon_status(self, status):
 | 
				
			||||||
 | 
							if not dbus_support.supported:
 | 
				
			||||||
 | 
								# do nothing if user doesn't have D-Bus bindings
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							try:
 | 
				
			||||||
 | 
								bus = dbus.SessionBus()
 | 
				
			||||||
 | 
								if not 'com.google.code.Awn' in bus.list_names():
 | 
				
			||||||
 | 
									# Awn is not installed
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
							except Exception:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							iconset = gajim.config.get('iconset')
 | 
				
			||||||
 | 
							prefix = os.path.join(helpers.get_iconset_path(iconset), '32x32')
 | 
				
			||||||
 | 
							if status in ('chat', 'away', 'xa', 'dnd', 'invisible', 'offline'):
 | 
				
			||||||
 | 
								status = status + '.png'
 | 
				
			||||||
 | 
							elif status == 'online':
 | 
				
			||||||
 | 
								prefix = os.path.join(gajim.DATA_DIR, 'pixmaps')
 | 
				
			||||||
 | 
								status = 'gajim.png'
 | 
				
			||||||
 | 
							path = os.path.join(prefix, status)
 | 
				
			||||||
 | 
							try:
 | 
				
			||||||
 | 
								obj = bus.get_object('com.google.code.Awn', '/com/google/code/Awn')
 | 
				
			||||||
 | 
								awn = dbus.Interface(obj, 'com.google.code.Awn')
 | 
				
			||||||
 | 
								awn.SetTaskIconByName('Gajim', os.path.abspath(path))
 | 
				
			||||||
 | 
							except Exception:
 | 
				
			||||||
 | 
								pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def enable_music_listener(self):
 | 
				
			||||||
 | 
							if not self.music_track_changed_signal:
 | 
				
			||||||
 | 
								listener = MusicTrackListener.get()
 | 
				
			||||||
 | 
								self.music_track_changed_signal = listener.connect(
 | 
				
			||||||
 | 
									'music-track-changed', self.music_track_changed)
 | 
				
			||||||
 | 
							track = listener.get_playing_track()
 | 
				
			||||||
 | 
							self.music_track_changed(listener, track)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def disable_music_listener(self):
 | 
				
			||||||
 | 
							listener = MusicTrackListener.get()
 | 
				
			||||||
 | 
							listener.disconnect(self.music_track_changed_signal)
 | 
				
			||||||
 | 
							self.music_track_changed_signal = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def music_track_changed(self, unused_listener, music_track_info, account=''):
 | 
				
			||||||
 | 
							if account == '':
 | 
				
			||||||
 | 
								accounts = gajim.connections.keys()
 | 
				
			||||||
 | 
							else:
 | 
				
			||||||
 | 
								accounts = [account]
 | 
				
			||||||
 | 
							if music_track_info is None:
 | 
				
			||||||
 | 
								artist = ''
 | 
				
			||||||
 | 
								title = ''
 | 
				
			||||||
 | 
								source = ''
 | 
				
			||||||
 | 
							elif hasattr(music_track_info, 'paused') and music_track_info.paused == 0:
 | 
				
			||||||
 | 
								artist = ''
 | 
				
			||||||
 | 
								title = ''
 | 
				
			||||||
 | 
								source = ''
 | 
				
			||||||
 | 
							else:
 | 
				
			||||||
 | 
								artist = music_track_info.artist
 | 
				
			||||||
 | 
								title = music_track_info.title
 | 
				
			||||||
 | 
								source = music_track_info.album
 | 
				
			||||||
 | 
							for acct in accounts:
 | 
				
			||||||
 | 
								if acct not in gajim.connections:
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								if not gajim.account_is_connected(acct):
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								if not gajim.connections[acct].pep_supported:
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								if gajim.connections[acct].music_track_info == music_track_info:
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								pep.user_send_tune(acct, artist, title, source)
 | 
				
			||||||
 | 
								gajim.connections[acct].music_track_info = music_track_info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def get_bg_fg_colors(self):
 | 
				
			||||||
 | 
							def gdkcolor_to_rgb (gdkcolor):
 | 
				
			||||||
 | 
								return [c / 65535. for c in (gdkcolor.red, gdkcolor.green,
 | 
				
			||||||
 | 
									gdkcolor.blue)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							def format_rgb (r, g, b):
 | 
				
			||||||
 | 
								return ' '.join([str(c) for c in ('rgb', r, g, b)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							def format_gdkcolor (gdkcolor):
 | 
				
			||||||
 | 
								return format_rgb (*gdkcolor_to_rgb (gdkcolor))
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							# get style colors and create string for dvipng
 | 
				
			||||||
 | 
							dummy = gtk.Invisible()
 | 
				
			||||||
 | 
							dummy.ensure_style()
 | 
				
			||||||
 | 
							style = dummy.get_style()
 | 
				
			||||||
 | 
							bg_str = format_gdkcolor(style.base[gtk.STATE_NORMAL])
 | 
				
			||||||
 | 
							fg_str = format_gdkcolor(style.text[gtk.STATE_NORMAL])
 | 
				
			||||||
 | 
							return (bg_str, fg_str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def read_sleepy(self):
 | 
						def read_sleepy(self):
 | 
				
			||||||
		'''Check idle status and change that status if needed'''
 | 
							'''Check idle status and change that status if needed'''
 | 
				
			||||||
		if not self.sleeper.poll():
 | 
							if not self.sleeper.poll():
 | 
				
			||||||
| 
						 | 
					@ -3603,6 +3706,12 @@ class Interface:
 | 
				
			||||||
				except Exception:
 | 
									except Exception:
 | 
				
			||||||
					pass
 | 
										pass
 | 
				
			||||||
		gobject.timeout_add_seconds(5, remote_init)
 | 
							gobject.timeout_add_seconds(5, remote_init)
 | 
				
			||||||
 | 
							self.music_track_changed_signal = None
 | 
				
			||||||
 | 
							for account in gajim.connections:
 | 
				
			||||||
 | 
								if gajim.config.get_per('accounts', account, 'publish_tune') and \
 | 
				
			||||||
 | 
								dbus_support.supported:
 | 
				
			||||||
 | 
									self.enable_music_listener()
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
	def sigint_cb(num, stack):
 | 
						def sigint_cb(num, stack):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,6 +47,8 @@ from chat_control import ChatControl
 | 
				
			||||||
from chat_control import ChatControlBase
 | 
					from chat_control import ChatControlBase
 | 
				
			||||||
from common.exceptions import GajimGeneralException
 | 
					from common.exceptions import GajimGeneralException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from commands.implementation import PrivateChatCommands, GroupChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
log = logging.getLogger('gajim.groupchat_control')
 | 
					log = logging.getLogger('gajim.groupchat_control')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,9 +118,11 @@ def tree_cell_data_func(column, renderer, model, iter_, tv=None):
 | 
				
			||||||
			renderer.set_property('font',
 | 
								renderer.set_property('font',
 | 
				
			||||||
				gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
 | 
									gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PrivateChatControl(ChatControl):
 | 
					class PrivateChatControl(ChatControl, PrivateChatCommands):
 | 
				
			||||||
	TYPE_ID = message_control.TYPE_PM
 | 
						TYPE_ID = message_control.TYPE_PM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DISPATCHED_BY = PrivateChatCommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def __init__(self, parent_win, gc_contact, contact, account, session):
 | 
						def __init__(self, parent_win, gc_contact, contact, account, session):
 | 
				
			||||||
		room_jid = contact.jid.split('/')[0]
 | 
							room_jid = contact.jid.split('/')[0]
 | 
				
			||||||
		room_ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
 | 
							room_ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
 | 
				
			||||||
| 
						 | 
					@ -132,7 +136,7 @@ class PrivateChatControl(ChatControl):
 | 
				
			||||||
		ChatControl.__init__(self, parent_win, contact, account, session)
 | 
							ChatControl.__init__(self, parent_win, contact, account, session)
 | 
				
			||||||
		self.TYPE_ID = 'pm'
 | 
							self.TYPE_ID = 'pm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def send_message(self, message, xhtml=None):
 | 
						def send_message(self, message, xhtml=None, process_commands=True):
 | 
				
			||||||
		'''call this function to send our message'''
 | 
							'''call this function to send our message'''
 | 
				
			||||||
		if not message:
 | 
							if not message:
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
| 
						 | 
					@ -158,7 +162,8 @@ class PrivateChatControl(ChatControl):
 | 
				
			||||||
					'left.') % {'room': room, 'nick': nick})
 | 
										'left.') % {'room': room, 'nick': nick})
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ChatControl.send_message(self, message, xhtml=xhtml)
 | 
							ChatControl.send_message(self, message, xhtml=xhtml,
 | 
				
			||||||
 | 
									process_commands=process_commands)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def update_ui(self):
 | 
						def update_ui(self):
 | 
				
			||||||
		if self.contact.show == 'offline':
 | 
							if self.contact.show == 'offline':
 | 
				
			||||||
| 
						 | 
					@ -180,12 +185,10 @@ class PrivateChatControl(ChatControl):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		self.session.negotiate_e2e(False)
 | 
							self.session.negotiate_e2e(False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GroupchatControl(ChatControlBase):
 | 
					class GroupchatControl(ChatControlBase, GroupChatCommands):
 | 
				
			||||||
	TYPE_ID = message_control.TYPE_GC
 | 
						TYPE_ID = message_control.TYPE_GC
 | 
				
			||||||
	# alphanum sorted
 | 
					
 | 
				
			||||||
	MUC_CMDS = ['ban', 'block', 'chat', 'query', 'clear', 'close', 'compact',
 | 
						DISPATCHED_BY = GroupChatCommands
 | 
				
			||||||
		'help', 'invite', 'join', 'kick', 'leave', 'me', 'msg', 'nick',
 | 
					 | 
				
			||||||
		'part', 'names', 'say', 'topic', 'unblock']
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def __init__(self, parent_win, contact, acct, is_continued=False):
 | 
						def __init__(self, parent_win, contact, acct, is_continued=False):
 | 
				
			||||||
		ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
 | 
							ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
 | 
				
			||||||
| 
						 | 
					@ -281,7 +284,6 @@ class GroupchatControl(ChatControlBase):
 | 
				
			||||||
		self.attention_list = []
 | 
							self.attention_list = []
 | 
				
			||||||
		self.room_creation = int(time.time()) # Use int to reduce mem usage
 | 
							self.room_creation = int(time.time()) # Use int to reduce mem usage
 | 
				
			||||||
		self.nick_hits = []
 | 
							self.nick_hits = []
 | 
				
			||||||
		self.cmd_hits = []
 | 
					 | 
				
			||||||
		self.last_key_tabs = False
 | 
							self.last_key_tabs = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		self.subject = ''
 | 
							self.subject = ''
 | 
				
			||||||
| 
						 | 
					@ -1510,267 +1512,14 @@ class GroupchatControl(ChatControlBase):
 | 
				
			||||||
		if model.iter_n_children(parent_iter) == 0:
 | 
							if model.iter_n_children(parent_iter) == 0:
 | 
				
			||||||
			model.remove(parent_iter)
 | 
								model.remove(parent_iter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def _process_command(self, message):
 | 
						def send_message(self, message, xhtml=None, process_commands=True):
 | 
				
			||||||
		if message[0] != '/':
 | 
					 | 
				
			||||||
			return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		# Handle common commands
 | 
					 | 
				
			||||||
		if ChatControlBase._process_command(self, message):
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		message = message[1:]
 | 
					 | 
				
			||||||
		message_array = message.split(' ', 1)
 | 
					 | 
				
			||||||
		command = message_array.pop(0).lower()
 | 
					 | 
				
			||||||
		if message_array == ['']:
 | 
					 | 
				
			||||||
			message_array = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if command == 'me':
 | 
					 | 
				
			||||||
			return False # This is not really a command
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if command == 'nick':
 | 
					 | 
				
			||||||
			# example: /nick foo
 | 
					 | 
				
			||||||
			if len(message_array) and message_array[0] != self.nick:
 | 
					 | 
				
			||||||
				nick = message_array[0]
 | 
					 | 
				
			||||||
				try:
 | 
					 | 
				
			||||||
					nick = helpers.parse_resource(nick)
 | 
					 | 
				
			||||||
				except Exception:
 | 
					 | 
				
			||||||
					# Invalid Nickname
 | 
					 | 
				
			||||||
					dialogs.ErrorDialog(_('Invalid nickname'),
 | 
					 | 
				
			||||||
					_('The nickname has not allowed characters.'))
 | 
					 | 
				
			||||||
					return True
 | 
					 | 
				
			||||||
				gajim.connections[self.account].join_gc(nick, self.room_jid, None,
 | 
					 | 
				
			||||||
					change_nick=True)
 | 
					 | 
				
			||||||
				self.new_nick = nick
 | 
					 | 
				
			||||||
				self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'query' or command == 'chat':
 | 
					 | 
				
			||||||
			# Open a chat window to the specified nick
 | 
					 | 
				
			||||||
			# example: /query foo
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				nick0 = message_array.pop(0)
 | 
					 | 
				
			||||||
				if nick0[-1] == ' ':
 | 
					 | 
				
			||||||
					nick1 = nick0[:-1]
 | 
					 | 
				
			||||||
				else:
 | 
					 | 
				
			||||||
					nick1 = nick0
 | 
					 | 
				
			||||||
				nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
 | 
					 | 
				
			||||||
				for nick in (nick0, nick1):
 | 
					 | 
				
			||||||
					if nick in nicks:
 | 
					 | 
				
			||||||
						self.on_send_pm(nick=nick)
 | 
					 | 
				
			||||||
						self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
						return True
 | 
					 | 
				
			||||||
				self.print_conversation(_('Nickname not found: %s') % \
 | 
					 | 
				
			||||||
					nick0, 'info')
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'msg':
 | 
					 | 
				
			||||||
			# Send a message to a nick. Also opens a private message window.
 | 
					 | 
				
			||||||
			# example: /msg foo Hey, what's up?
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				message_array = message_array[0].split()
 | 
					 | 
				
			||||||
				nick = message_array.pop(0)
 | 
					 | 
				
			||||||
				room_nicks = gajim.contacts.get_nick_list(self.account,
 | 
					 | 
				
			||||||
					self.room_jid)
 | 
					 | 
				
			||||||
				if nick in room_nicks:
 | 
					 | 
				
			||||||
					privmsg = ' '.join(message_array)
 | 
					 | 
				
			||||||
					self.on_send_pm(nick=nick, msg=privmsg)
 | 
					 | 
				
			||||||
					self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
				else:
 | 
					 | 
				
			||||||
					self.print_conversation(_('Nickname not found: %s') % nick,
 | 
					 | 
				
			||||||
						'info')
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'topic':
 | 
					 | 
				
			||||||
			# display or change the room topic
 | 
					 | 
				
			||||||
			# example: /topic : print topic
 | 
					 | 
				
			||||||
			# /topic foo : change topic to foo
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				new_topic = message_array.pop(0)
 | 
					 | 
				
			||||||
				gajim.connections[self.account].send_gc_subject(self.room_jid,
 | 
					 | 
				
			||||||
					new_topic)
 | 
					 | 
				
			||||||
			elif self.subject is not '':
 | 
					 | 
				
			||||||
				self.print_conversation(self.subject, 'info')
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.print_conversation(_('This group chat has no subject'), 'info')
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'invite':
 | 
					 | 
				
			||||||
			# invite a user to a room for a reason
 | 
					 | 
				
			||||||
			# example: /invite user@example.com reason
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				message_array = message_array[0].split()
 | 
					 | 
				
			||||||
				invitee = message_array.pop(0)
 | 
					 | 
				
			||||||
				reason = ' '.join(message_array)
 | 
					 | 
				
			||||||
				gajim.connections[self.account].send_invite(self.room_jid, invitee,
 | 
					 | 
				
			||||||
					reason)
 | 
					 | 
				
			||||||
				s = _('Invited %(contact_jid)s to %(room_jid)s.') % {
 | 
					 | 
				
			||||||
					'contact_jid': invitee,
 | 
					 | 
				
			||||||
					'room_jid': self.room_jid}
 | 
					 | 
				
			||||||
				self.print_conversation(s, 'info')
 | 
					 | 
				
			||||||
				self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'join':
 | 
					 | 
				
			||||||
			# example: /join room@conference.example.com/nick
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				room_jid = message_array[0]
 | 
					 | 
				
			||||||
				if room_jid.find('@') < 0:
 | 
					 | 
				
			||||||
					room_jid = room_jid + '@' + gajim.get_server_from_jid(
 | 
					 | 
				
			||||||
						self.room_jid)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				room_jid = '@' + gajim.get_server_from_jid(self.room_jid)
 | 
					 | 
				
			||||||
			if room_jid.find('/') >= 0:
 | 
					 | 
				
			||||||
				room_jid, nick = room_jid.split('/', 1)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				nick = ''
 | 
					 | 
				
			||||||
			# join_gc window is needed in order to provide for password entry.
 | 
					 | 
				
			||||||
			if 'join_gc' in gajim.interface.instances[self.account]:
 | 
					 | 
				
			||||||
				gajim.interface.instances[self.account]['join_gc'].\
 | 
					 | 
				
			||||||
					window.present()
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				try:
 | 
					 | 
				
			||||||
					dialogs.JoinGroupchatWindow(account=None, room_jid=room_jid,
 | 
					 | 
				
			||||||
						nick=nick)
 | 
					 | 
				
			||||||
				except GajimGeneralException:
 | 
					 | 
				
			||||||
					pass
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'leave' or command == 'part' or command == 'close':
 | 
					 | 
				
			||||||
			# Leave the room and close the tab or window
 | 
					 | 
				
			||||||
			reason = 'offline'
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				reason = message_array.pop(0)
 | 
					 | 
				
			||||||
			self.parent_win.remove_tab(self, self.parent_win.CLOSE_COMMAND, reason)
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'ban':
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				room_nicks = gajim.contacts.get_nick_list(self.account,
 | 
					 | 
				
			||||||
					self.room_jid)
 | 
					 | 
				
			||||||
				nb_match = 0
 | 
					 | 
				
			||||||
				nick_ban = ''
 | 
					 | 
				
			||||||
				for nick in room_nicks:
 | 
					 | 
				
			||||||
					if message_array[0].startswith(nick):
 | 
					 | 
				
			||||||
						nb_match += 1
 | 
					 | 
				
			||||||
						nick_ban = nick
 | 
					 | 
				
			||||||
						test_reason = message_array[0][len(nick) + 1:]
 | 
					 | 
				
			||||||
						if len(test_reason) == 0:
 | 
					 | 
				
			||||||
							reason = 'None'
 | 
					 | 
				
			||||||
						else:
 | 
					 | 
				
			||||||
							reason = test_reason
 | 
					 | 
				
			||||||
				banned_jid = None
 | 
					 | 
				
			||||||
				if nb_match == 1:
 | 
					 | 
				
			||||||
					gc_contact = gajim.contacts.get_gc_contact(self.account,
 | 
					 | 
				
			||||||
							self.room_jid, nick_ban)
 | 
					 | 
				
			||||||
					banned_jid = gc_contact.jid
 | 
					 | 
				
			||||||
				elif nb_match > 1:
 | 
					 | 
				
			||||||
					self.print_conversation(_('There is an ambiguity: %d nicks '
 | 
					 | 
				
			||||||
						'match.\n Please use graphical interface ') % nb_match,
 | 
					 | 
				
			||||||
						'info')
 | 
					 | 
				
			||||||
					self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
				elif message_array[0].split()[0].find('@') > 0:
 | 
					 | 
				
			||||||
					message_splited = message_array[0].split(' ', 1)
 | 
					 | 
				
			||||||
					banned_jid = message_splited[0]
 | 
					 | 
				
			||||||
					if len(message_splited) == 2:
 | 
					 | 
				
			||||||
						reason = message_splited[1]
 | 
					 | 
				
			||||||
					else:
 | 
					 | 
				
			||||||
						reason = 'None'
 | 
					 | 
				
			||||||
				if banned_jid:
 | 
					 | 
				
			||||||
					gajim.connections[self.account].gc_set_affiliation(self.room_jid,
 | 
					 | 
				
			||||||
						banned_jid, 'outcast', reason)
 | 
					 | 
				
			||||||
					self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
				else:
 | 
					 | 
				
			||||||
					self.print_conversation(_('Nickname not found'), 'info')
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'kick':
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				nick_kick = ''
 | 
					 | 
				
			||||||
				room_nicks = gajim.contacts.get_nick_list(self.account,
 | 
					 | 
				
			||||||
					self.room_jid)
 | 
					 | 
				
			||||||
				nb_match = 0
 | 
					 | 
				
			||||||
				for nick in room_nicks:
 | 
					 | 
				
			||||||
					if message_array[0].startswith(nick):
 | 
					 | 
				
			||||||
						nb_match += 1
 | 
					 | 
				
			||||||
						nick_kick = nick
 | 
					 | 
				
			||||||
						test_reason = message_array[0][len(nick) + 1:]
 | 
					 | 
				
			||||||
						if len(test_reason) == 0:
 | 
					 | 
				
			||||||
							reason = 'None'
 | 
					 | 
				
			||||||
						else:
 | 
					 | 
				
			||||||
							reason = test_reason
 | 
					 | 
				
			||||||
				if nb_match == 1:
 | 
					 | 
				
			||||||
					gajim.connections[self.account].gc_set_role(self.room_jid,
 | 
					 | 
				
			||||||
						nick_kick, 'none', reason)
 | 
					 | 
				
			||||||
					self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
				elif nb_match > 1:
 | 
					 | 
				
			||||||
					self.print_conversation(_('There is an ambiguity: %d nicks '
 | 
					 | 
				
			||||||
						'match.\n Please use graphical interface') % nb_match ,
 | 
					 | 
				
			||||||
						'info' )
 | 
					 | 
				
			||||||
					self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
				else:
 | 
					 | 
				
			||||||
					# We can't do the difference between nick and reason
 | 
					 | 
				
			||||||
					# So we don't say the nick
 | 
					 | 
				
			||||||
					self.print_conversation(_('Nickname not found') , 'info')
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'names':
 | 
					 | 
				
			||||||
			# print the list of participants
 | 
					 | 
				
			||||||
			nicklist=''
 | 
					 | 
				
			||||||
			i=0
 | 
					 | 
				
			||||||
			for contact in self.iter_contact_rows():
 | 
					 | 
				
			||||||
				nicklist += '[ %-12.12s ] ' % (contact[C_NICK].decode('utf-8'))
 | 
					 | 
				
			||||||
				i=i+1
 | 
					 | 
				
			||||||
				if i == 3:
 | 
					 | 
				
			||||||
					i=0
 | 
					 | 
				
			||||||
					self.print_conversation(nicklist, 'info')
 | 
					 | 
				
			||||||
					nicklist=''
 | 
					 | 
				
			||||||
			if nicklist:
 | 
					 | 
				
			||||||
				self.print_conversation(nicklist, 'info')
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'help':
 | 
					 | 
				
			||||||
			if len(message_array):
 | 
					 | 
				
			||||||
				subcommand = message_array.pop(0)
 | 
					 | 
				
			||||||
				self.get_command_help(subcommand)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'say':
 | 
					 | 
				
			||||||
			gajim.connections[self.account].send_gc_message(self.room_jid,
 | 
					 | 
				
			||||||
									message[4:])
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'block':
 | 
					 | 
				
			||||||
			if len(message_array) == 0:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
				return True
 | 
					 | 
				
			||||||
			nick = message_array[0].strip()
 | 
					 | 
				
			||||||
			self.on_block(None, nick)
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
		elif command == 'unblock':
 | 
					 | 
				
			||||||
			if len(message_array) == 0:
 | 
					 | 
				
			||||||
				self.get_command_help(command)
 | 
					 | 
				
			||||||
				return True
 | 
					 | 
				
			||||||
			nick = message_array[0].strip()
 | 
					 | 
				
			||||||
			self.on_unblock(None, nick)
 | 
					 | 
				
			||||||
			self.clear(self.msg_textview)
 | 
					 | 
				
			||||||
			return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	def send_message(self, message, xhtml=None):
 | 
					 | 
				
			||||||
		'''call this function to send our message'''
 | 
							'''call this function to send our message'''
 | 
				
			||||||
		if not message:
 | 
							if not message:
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if process_commands and self.process_as_command(message):
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		message = helpers.remove_invalid_xml_chars(message)
 | 
							message = helpers.remove_invalid_xml_chars(message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if not message:
 | 
							if not message:
 | 
				
			||||||
| 
						 | 
					@ -1778,79 +1527,12 @@ class GroupchatControl(ChatControlBase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if message != '' or message != '\n':
 | 
							if message != '' or message != '\n':
 | 
				
			||||||
			self.save_sent_message(message)
 | 
								self.save_sent_message(message)
 | 
				
			||||||
 | 
					         
 | 
				
			||||||
			if not self._process_command(message):
 | 
								# Send the message
 | 
				
			||||||
				# Send the message
 | 
								gajim.connections[self.account].send_gc_message(self.room_jid, 
 | 
				
			||||||
				gajim.connections[self.account].send_gc_message(self.room_jid,
 | 
									message, xhtml=xhtml)
 | 
				
			||||||
					message, xhtml=xhtml)
 | 
								self.msg_textview.get_buffer().set_text('')
 | 
				
			||||||
				self.msg_textview.get_buffer().set_text('')
 | 
								self.msg_textview.grab_focus()
 | 
				
			||||||
				self.msg_textview.grab_focus()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	def get_command_help(self, command):
 | 
					 | 
				
			||||||
		if command == 'help':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Commands: %s') % GroupchatControl.MUC_CMDS,
 | 
					 | 
				
			||||||
				'info')
 | 
					 | 
				
			||||||
		elif command == 'ban':
 | 
					 | 
				
			||||||
			s = _('Usage: /%s <nickname|JID> [reason], bans the JID from the group'
 | 
					 | 
				
			||||||
				' chat. The nickname of an occupant may be substituted, but not if '
 | 
					 | 
				
			||||||
				'it contains "@". If the JID is currently in the group chat, '
 | 
					 | 
				
			||||||
				'he/she/it will also be kicked.') % command
 | 
					 | 
				
			||||||
			self.print_conversation(s, 'info')
 | 
					 | 
				
			||||||
		elif command == 'chat' or command == 'query':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s <nickname>, opens a private chat'
 | 
					 | 
				
			||||||
				' window with the specified occupant.') % command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'clear':
 | 
					 | 
				
			||||||
			self.print_conversation(
 | 
					 | 
				
			||||||
				_('Usage: /%s, clears the text window.') % command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'close' or command == 'leave' or command == 'part':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s [reason], closes the current '
 | 
					 | 
				
			||||||
				'window or tab, displaying reason if specified.') % command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'compact':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s, hide the chat buttons.') % \
 | 
					 | 
				
			||||||
				command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'invite':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s <JID> [reason], invites JID to '
 | 
					 | 
				
			||||||
				'the current group chat, optionally providing a reason.') % command,
 | 
					 | 
				
			||||||
				'info')
 | 
					 | 
				
			||||||
		elif command == 'join':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s <room>@<server>[/nickname], '
 | 
					 | 
				
			||||||
				'offers to join room@server optionally using specified nickname.') \
 | 
					 | 
				
			||||||
				% command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'kick':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s <nickname> [reason], removes '
 | 
					 | 
				
			||||||
				'the occupant specified by nickname from the group chat and '
 | 
					 | 
				
			||||||
				'optionally displays a reason.') % command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'me':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%(command)s <action>, sends action '
 | 
					 | 
				
			||||||
				'to the current group chat. Use third person. (e.g. /%(command)s '
 | 
					 | 
				
			||||||
				'explodes.)') % {'command': command}, 'info')
 | 
					 | 
				
			||||||
		elif command == 'msg':
 | 
					 | 
				
			||||||
			s = _('Usage: /%s <nickname> [message], opens a private message window'
 | 
					 | 
				
			||||||
				' and sends message to the occupant specified by nickname.') % \
 | 
					 | 
				
			||||||
				command
 | 
					 | 
				
			||||||
			self.print_conversation(s, 'info')
 | 
					 | 
				
			||||||
		elif command == 'nick':
 | 
					 | 
				
			||||||
			s = _('Usage: /%s <nickname>, changes your nickname in current group '
 | 
					 | 
				
			||||||
				'chat.')	% command
 | 
					 | 
				
			||||||
			self.print_conversation(s, 'info')
 | 
					 | 
				
			||||||
		elif command == 'names':
 | 
					 | 
				
			||||||
			s = _('Usage: /%s , display the names of group chat occupants.')\
 | 
					 | 
				
			||||||
				% command
 | 
					 | 
				
			||||||
			self.print_conversation(s, 'info')
 | 
					 | 
				
			||||||
		elif command == 'topic':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s [topic], displays or updates the'
 | 
					 | 
				
			||||||
				' current group chat topic.') % command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'say':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s <message>, sends a message '
 | 
					 | 
				
			||||||
				'without looking for other commands.') % command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'block':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s <nickname>, prevent <nickname> '
 | 
					 | 
				
			||||||
				'to send you messages or private messages.') % command, 'info')
 | 
					 | 
				
			||||||
		elif command == 'unblock':
 | 
					 | 
				
			||||||
			self.print_conversation(_('Usage: /%s <nickname>, allow <nickname> '
 | 
					 | 
				
			||||||
				'to send you messages and private messages.') % command, 'info')
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			self.print_conversation(_('No help info for /%s') % command, 'info')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def get_role(self, nick):
 | 
						def get_role(self, nick):
 | 
				
			||||||
		gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
 | 
							gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
 | 
				
			||||||
| 
						 | 
					@ -2100,41 +1782,13 @@ class GroupchatControl(ChatControlBase):
 | 
				
			||||||
				'utf-8')
 | 
									'utf-8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			splitted_text = text.split()
 | 
								splitted_text = text.split()
 | 
				
			||||||
			# topic completion
 | 
					 | 
				
			||||||
			splitted_text2 = text.split(None, 1)
 | 
					 | 
				
			||||||
			if text.startswith('/topic '):
 | 
					 | 
				
			||||||
				if len(splitted_text2) == 2 and \
 | 
					 | 
				
			||||||
				self.subject.startswith(splitted_text2[1]) and\
 | 
					 | 
				
			||||||
				len(self.subject) > len(splitted_text2[1]):
 | 
					 | 
				
			||||||
					message_buffer.insert_at_cursor(
 | 
					 | 
				
			||||||
						self.subject[len(splitted_text2[1]):])
 | 
					 | 
				
			||||||
					return True
 | 
					 | 
				
			||||||
				elif len(splitted_text2) == 1 and text.startswith('/topic  '):
 | 
					 | 
				
			||||||
					message_buffer.delete(start_iter, end_iter)
 | 
					 | 
				
			||||||
					message_buffer.insert_at_cursor('/topic '+self.subject)
 | 
					 | 
				
			||||||
					return True
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			# command completion
 | 
								# HACK: Not the best soltution.
 | 
				
			||||||
			if text.startswith('/') and len(splitted_text) == 1:
 | 
								if (text.startswith(self.COMMAND_PREFIX) and not
 | 
				
			||||||
				text = splitted_text[0]
 | 
									text.startswith(self.COMMAND_PREFIX * 2) and len(splitted_text) == 1):
 | 
				
			||||||
				if len(text) == 1: # user wants to cycle all commands
 | 
									return super(GroupchatControl,
 | 
				
			||||||
					self.cmd_hits = GroupchatControl.MUC_CMDS
 | 
										self).handle_message_textview_mykey_press(widget, event_keyval,
 | 
				
			||||||
				else:
 | 
												event_keymod)
 | 
				
			||||||
					# cycle possible commands depending on what the user typed
 | 
					 | 
				
			||||||
					if self.last_key_tabs and len(self.cmd_hits) and \
 | 
					 | 
				
			||||||
					self.cmd_hits[0].startswith(text.lstrip('/')):
 | 
					 | 
				
			||||||
						self.cmd_hits.append(self.cmd_hits[0])
 | 
					 | 
				
			||||||
						self.cmd_hits.pop(0)
 | 
					 | 
				
			||||||
					else: # find possible commands
 | 
					 | 
				
			||||||
						self.cmd_hits = []
 | 
					 | 
				
			||||||
						for cmd in GroupchatControl.MUC_CMDS:
 | 
					 | 
				
			||||||
							if cmd.startswith(text.lstrip('/')):
 | 
					 | 
				
			||||||
								self.cmd_hits.append(cmd)
 | 
					 | 
				
			||||||
				if len(self.cmd_hits):
 | 
					 | 
				
			||||||
					message_buffer.delete(start_iter, end_iter)
 | 
					 | 
				
			||||||
					message_buffer.insert_at_cursor('/' + self.cmd_hits[0] + ' ')
 | 
					 | 
				
			||||||
					self.last_key_tabs = True
 | 
					 | 
				
			||||||
				return True
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			# nick completion
 | 
								# nick completion
 | 
				
			||||||
			# check if tab is pressed with empty message
 | 
								# check if tab is pressed with empty message
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -158,6 +158,15 @@ class MessageWindow(object):
 | 
				
			||||||
		if self.account == old_name:
 | 
							if self.account == old_name:
 | 
				
			||||||
			self.account = new_name
 | 
								self.account = new_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def change_jid(self, account, old_jid, new_jid):
 | 
				
			||||||
 | 
							''' call then when the full jid of a contral change'''
 | 
				
			||||||
 | 
							if account not in self._controls:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							if old_jid not in self._controls[account]:
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							self._controls[account][new_jid] = self._controls[account][old_jid]
 | 
				
			||||||
 | 
							del self._controls[account][old_jid]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def get_num_controls(self):
 | 
						def get_num_controls(self):
 | 
				
			||||||
		return sum(len(d) for d in self._controls.values())
 | 
							return sum(len(d) for d in self._controls.values())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,7 +62,6 @@ from message_window import MessageWindowMgr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from common import dbus_support
 | 
					from common import dbus_support
 | 
				
			||||||
if dbus_support.supported:
 | 
					if dbus_support.supported:
 | 
				
			||||||
	from music_track_listener import MusicTrackListener
 | 
					 | 
				
			||||||
	import dbus
 | 
						import dbus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from common.xmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC
 | 
					from common.xmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC
 | 
				
			||||||
| 
						 | 
					@ -1736,64 +1735,6 @@ class RosterWindow:
 | 
				
			||||||
			if chat_control:
 | 
								if chat_control:
 | 
				
			||||||
				chat_control.contact = contact1
 | 
									chat_control.contact = contact1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def _change_awn_icon_status(self, status):
 | 
					 | 
				
			||||||
		if not dbus_support.supported:
 | 
					 | 
				
			||||||
			# do nothing if user doesn't have D-Bus bindings
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		try:
 | 
					 | 
				
			||||||
			bus = dbus.SessionBus()
 | 
					 | 
				
			||||||
			if not 'com.google.code.Awn' in bus.list_names():
 | 
					 | 
				
			||||||
				# Awn is not installed
 | 
					 | 
				
			||||||
				return
 | 
					 | 
				
			||||||
		except Exception:
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		iconset = gajim.config.get('iconset')
 | 
					 | 
				
			||||||
		prefix = os.path.join(helpers.get_iconset_path(iconset), '32x32')
 | 
					 | 
				
			||||||
		if status in ('chat', 'away', 'xa', 'dnd', 'invisible', 'offline'):
 | 
					 | 
				
			||||||
			status = status + '.png'
 | 
					 | 
				
			||||||
		elif status == 'online':
 | 
					 | 
				
			||||||
			prefix = os.path.join(gajim.DATA_DIR, 'pixmaps')
 | 
					 | 
				
			||||||
			status = 'gajim.png'
 | 
					 | 
				
			||||||
		path = os.path.join(prefix, status)
 | 
					 | 
				
			||||||
		try:
 | 
					 | 
				
			||||||
			obj = bus.get_object('com.google.code.Awn', '/com/google/code/Awn')
 | 
					 | 
				
			||||||
			awn = dbus.Interface(obj, 'com.google.code.Awn')
 | 
					 | 
				
			||||||
			awn.SetTaskIconByName('Gajim', os.path.abspath(path))
 | 
					 | 
				
			||||||
		except Exception:
 | 
					 | 
				
			||||||
			pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	def music_track_changed(self, unused_listener, music_track_info,
 | 
					 | 
				
			||||||
	account=''):
 | 
					 | 
				
			||||||
		if account == '':
 | 
					 | 
				
			||||||
			accounts = gajim.connections.keys()
 | 
					 | 
				
			||||||
		if music_track_info is None:
 | 
					 | 
				
			||||||
			artist = ''
 | 
					 | 
				
			||||||
			title = ''
 | 
					 | 
				
			||||||
			source = ''
 | 
					 | 
				
			||||||
		elif hasattr(music_track_info, 'paused') and music_track_info.paused == 0:
 | 
					 | 
				
			||||||
			artist = ''
 | 
					 | 
				
			||||||
			title = ''
 | 
					 | 
				
			||||||
			source = ''
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			artist = music_track_info.artist
 | 
					 | 
				
			||||||
			title = music_track_info.title
 | 
					 | 
				
			||||||
			source = music_track_info.album
 | 
					 | 
				
			||||||
		if account == '':
 | 
					 | 
				
			||||||
			for account in accounts:
 | 
					 | 
				
			||||||
				if not gajim.account_is_connected(account):
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				if not gajim.connections[account].pep_supported:
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				if gajim.connections[account].music_track_info == music_track_info:
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
				pep.user_send_tune(account, artist, title, source)
 | 
					 | 
				
			||||||
				gajim.connections[account].music_track_info = music_track_info
 | 
					 | 
				
			||||||
		elif account in gajim.connections and \
 | 
					 | 
				
			||||||
		gajim.connections[account].pep_supported:
 | 
					 | 
				
			||||||
			if gajim.connections[account].music_track_info != music_track_info:
 | 
					 | 
				
			||||||
				pep.user_send_tune(account, artist, title, source)
 | 
					 | 
				
			||||||
				gajim.connections[account].music_track_info = music_track_info
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	def connected_rooms(self, account):
 | 
						def connected_rooms(self, account):
 | 
				
			||||||
		if account in gajim.gc_connected[account].values():
 | 
							if account in gajim.gc_connected[account].values():
 | 
				
			||||||
			return True
 | 
								return True
 | 
				
			||||||
| 
						 | 
					@ -2189,7 +2130,7 @@ class RosterWindow:
 | 
				
			||||||
			liststore.prepend([status_combobox_text,
 | 
								liststore.prepend([status_combobox_text,
 | 
				
			||||||
				gajim.interface.jabber_state_images['16'][show], show, False])
 | 
									gajim.interface.jabber_state_images['16'][show], show, False])
 | 
				
			||||||
			self.status_combobox.set_active(0)
 | 
								self.status_combobox.set_active(0)
 | 
				
			||||||
		self._change_awn_icon_status(show)
 | 
							gajim.interface._change_awn_icon_status(show)
 | 
				
			||||||
		self.combobox_callback_active = True
 | 
							self.combobox_callback_active = True
 | 
				
			||||||
		if gajim.interface.systray_enabled:
 | 
							if gajim.interface.systray_enabled:
 | 
				
			||||||
			gajim.interface.systray.change_status(show)
 | 
								gajim.interface.systray.change_status(show)
 | 
				
			||||||
| 
						 | 
					@ -2634,7 +2575,24 @@ class RosterWindow:
 | 
				
			||||||
					connection.set_default_list('block')
 | 
										connection.set_default_list('block')
 | 
				
			||||||
				connection.get_privacy_list('block')
 | 
									connection.get_privacy_list('block')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		self.get_status_message('offline', on_continue, show_pep=False)
 | 
							def _block_it(is_checked=None):
 | 
				
			||||||
 | 
								if is_checked is not None: # dialog has been shown
 | 
				
			||||||
 | 
									if is_checked: # user does not want to be asked again
 | 
				
			||||||
 | 
										gajim.config.set('confirm_block', 'no')
 | 
				
			||||||
 | 
									else:
 | 
				
			||||||
 | 
										gajim.config.set('confirm_block', 'yes')
 | 
				
			||||||
 | 
								self.get_status_message('offline', on_continue, show_pep=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							confirm_block = gajim.config.get('confirm_block')
 | 
				
			||||||
 | 
							if confirm_block == 'no':
 | 
				
			||||||
 | 
								_block_it()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							pritext = _('You are about to block a contact. Are you sure you want'
 | 
				
			||||||
 | 
								' to continue?')
 | 
				
			||||||
 | 
							sectext = _('This contact will see you offline and you will not receive '
 | 
				
			||||||
 | 
								'messages he will send you.')
 | 
				
			||||||
 | 
							dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
 | 
				
			||||||
 | 
								_('Do _not ask me again'), on_response_ok=_block_it)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_unblock(self, widget, list_, group=None):
 | 
						def on_unblock(self, widget, list_, group=None):
 | 
				
			||||||
		''' When clicked on the 'unblock' button in context menu. '''
 | 
							''' When clicked on the 'unblock' button in context menu. '''
 | 
				
			||||||
| 
						 | 
					@ -2886,8 +2844,15 @@ class RosterWindow:
 | 
				
			||||||
			ctrl.got_disconnected()
 | 
								ctrl.got_disconnected()
 | 
				
			||||||
		self.remove_groupchat(jid, account)
 | 
							self.remove_groupchat(jid, account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def on_reconnect(self, widget, jid, account):
 | 
				
			||||||
 | 
							'''When disconnect menuitem is activated: disconect from room'''
 | 
				
			||||||
 | 
							if jid in gajim.interface.minimized_controls[account]:
 | 
				
			||||||
 | 
								ctrl = gajim.interface.minimized_controls[account][jid]
 | 
				
			||||||
 | 
							gajim.interface.join_gc_room(account, jid, ctrl.nick,
 | 
				
			||||||
 | 
								gajim.gc_passwords.get(jid, ''))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_send_single_message_menuitem_activate(self, widget, account,
 | 
						def on_send_single_message_menuitem_activate(self, widget, account,
 | 
				
			||||||
	contact = None):
 | 
						contact=None):
 | 
				
			||||||
		if contact is None:
 | 
							if contact is None:
 | 
				
			||||||
			dialogs.SingleMessageWindow(account, action='send')
 | 
								dialogs.SingleMessageWindow(account, action='send')
 | 
				
			||||||
		elif isinstance(contact, list):
 | 
							elif isinstance(contact, list):
 | 
				
			||||||
| 
						 | 
					@ -2937,7 +2902,7 @@ class RosterWindow:
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_invite_to_room(self, widget, list_, room_jid, room_account,
 | 
						def on_invite_to_room(self, widget, list_, room_jid, room_account,
 | 
				
			||||||
		resource = None):
 | 
							resource=None):
 | 
				
			||||||
		''' resource parameter MUST NOT be used if more than one contact in
 | 
							''' resource parameter MUST NOT be used if more than one contact in
 | 
				
			||||||
		list '''
 | 
							list '''
 | 
				
			||||||
		for e in list_:
 | 
							for e in list_:
 | 
				
			||||||
| 
						 | 
					@ -3243,8 +3208,26 @@ class RosterWindow:
 | 
				
			||||||
					jid += '/' + contact.resource
 | 
										jid += '/' + contact.resource
 | 
				
			||||||
				self.send_status(account, show, message, to=jid)
 | 
									self.send_status(account, show, message, to=jid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		self.get_status_message(show, on_response, show_pep=False,
 | 
							def send_it(is_checked=None):
 | 
				
			||||||
			always_ask=True)
 | 
								if is_checked is not None: # dialog has been shown
 | 
				
			||||||
 | 
									if is_checked: # user does not want to be asked again
 | 
				
			||||||
 | 
										gajim.config.set('confirm_custom_status', 'no')
 | 
				
			||||||
 | 
									else:
 | 
				
			||||||
 | 
										gajim.config.set('confirm_custom_status', 'yes')
 | 
				
			||||||
 | 
								self.get_status_message(show, on_response, show_pep=False,
 | 
				
			||||||
 | 
									always_ask=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							confirm_custom_status = gajim.config.get('confirm_custom_status')
 | 
				
			||||||
 | 
							if confirm_custom_status == 'no':
 | 
				
			||||||
 | 
								send_it()
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							pritext = _('You are about to send a custom status. Are you sure you want'
 | 
				
			||||||
 | 
								' to continue?')
 | 
				
			||||||
 | 
							sectext = _('This contact will temporarily see you as %(status)s, '
 | 
				
			||||||
 | 
								'but only until you change your status. Then he will see your global '
 | 
				
			||||||
 | 
								'status.') % {'status': show}
 | 
				
			||||||
 | 
							dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
 | 
				
			||||||
 | 
								_('Do _not ask me again'), on_response_ok=send_it)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	def on_status_combobox_changed(self, widget):
 | 
						def on_status_combobox_changed(self, widget):
 | 
				
			||||||
		'''When we change our status via the combobox'''
 | 
							'''When we change our status via the combobox'''
 | 
				
			||||||
| 
						 | 
					@ -3354,21 +3337,14 @@ class RosterWindow:
 | 
				
			||||||
		act = widget.get_active()
 | 
							act = widget.get_active()
 | 
				
			||||||
		gajim.config.set_per('accounts', account, 'publish_tune', act)
 | 
							gajim.config.set_per('accounts', account, 'publish_tune', act)
 | 
				
			||||||
		if act:
 | 
							if act:
 | 
				
			||||||
			listener = MusicTrackListener.get()
 | 
								gajim.interface.enable_music_listener()
 | 
				
			||||||
			if not self.music_track_changed_signal:
 | 
					 | 
				
			||||||
				self.music_track_changed_signal = listener.connect(
 | 
					 | 
				
			||||||
					'music-track-changed', self.music_track_changed)
 | 
					 | 
				
			||||||
			track = listener.get_playing_track()
 | 
					 | 
				
			||||||
			self.music_track_changed(listener, track)
 | 
					 | 
				
			||||||
		else:
 | 
							else:
 | 
				
			||||||
			# disable it only if no other account use it
 | 
								# disable it only if no other account use it
 | 
				
			||||||
			for acct in gajim.connections:
 | 
								for acct in gajim.connections:
 | 
				
			||||||
				if gajim.config.get_per('accounts', acct, 'publish_tune'):
 | 
									if gajim.config.get_per('accounts', acct, 'publish_tune'):
 | 
				
			||||||
					break
 | 
										break
 | 
				
			||||||
			else:
 | 
								else:
 | 
				
			||||||
				listener = MusicTrackListener.get()
 | 
									gajim.interface.disable_music_listener()
 | 
				
			||||||
				listener.disconnect(self.music_track_changed_signal)
 | 
					 | 
				
			||||||
				self.music_track_changed_signal = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if gajim.connections[account].pep_supported:
 | 
								if gajim.connections[account].pep_supported:
 | 
				
			||||||
				# As many implementations don't support retracting items, we send a
 | 
									# As many implementations don't support retracting items, we send a
 | 
				
			||||||
| 
						 | 
					@ -5919,6 +5895,13 @@ class RosterWindow:
 | 
				
			||||||
				jid, account)
 | 
									jid, account)
 | 
				
			||||||
			menu.append(maximize_menuitem)
 | 
								menu.append(maximize_menuitem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if not gajim.gc_connected[account].get(jid, False):
 | 
				
			||||||
 | 
								connect_menuitem = gtk.ImageMenuItem(_('_Reconnect'))
 | 
				
			||||||
 | 
								connect_icon = gtk.image_new_from_stock(gtk.STOCK_CONNECT, \
 | 
				
			||||||
 | 
									gtk.ICON_SIZE_MENU)
 | 
				
			||||||
 | 
								connect_menuitem.set_image(connect_icon)
 | 
				
			||||||
 | 
								connect_menuitem.connect('activate', self.on_reconnect, jid, account)
 | 
				
			||||||
 | 
								menu.append(connect_menuitem)
 | 
				
			||||||
		disconnect_menuitem = gtk.ImageMenuItem(_('_Disconnect'))
 | 
							disconnect_menuitem = gtk.ImageMenuItem(_('_Disconnect'))
 | 
				
			||||||
		disconnect_icon = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, \
 | 
							disconnect_icon = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, \
 | 
				
			||||||
			gtk.ICON_SIZE_MENU)
 | 
								gtk.ICON_SIZE_MENU)
 | 
				
			||||||
| 
						 | 
					@ -6190,7 +6173,6 @@ class RosterWindow:
 | 
				
			||||||
		self.xml = gtkgui_helpers.get_glade('roster_window.glade')
 | 
							self.xml = gtkgui_helpers.get_glade('roster_window.glade')
 | 
				
			||||||
		self.window = self.xml.get_widget('roster_window')
 | 
							self.window = self.xml.get_widget('roster_window')
 | 
				
			||||||
		self.hpaned = self.xml.get_widget('roster_hpaned')
 | 
							self.hpaned = self.xml.get_widget('roster_hpaned')
 | 
				
			||||||
		self.music_track_changed_signal = None
 | 
					 | 
				
			||||||
		gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
 | 
							gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
 | 
				
			||||||
		gajim.interface.msg_win_mgr.connect('window-delete',
 | 
							gajim.interface.msg_win_mgr.connect('window-delete',
 | 
				
			||||||
			self.on_message_window_delete)
 | 
								self.on_message_window_delete)
 | 
				
			||||||
| 
						 | 
					@ -6411,16 +6393,6 @@ class RosterWindow:
 | 
				
			||||||
		self._toggeling_row = False
 | 
							self._toggeling_row = False
 | 
				
			||||||
		self.setup_and_draw_roster()
 | 
							self.setup_and_draw_roster()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for account in gajim.connections:
 | 
					 | 
				
			||||||
			if gajim.config.get_per('accounts', account, 'publish_tune') and \
 | 
					 | 
				
			||||||
			dbus_support.supported:
 | 
					 | 
				
			||||||
				listener = MusicTrackListener.get()
 | 
					 | 
				
			||||||
				self.music_track_changed_signal = listener.connect(
 | 
					 | 
				
			||||||
					'music-track-changed', self.music_track_changed)
 | 
					 | 
				
			||||||
				track = listener.get_playing_track()
 | 
					 | 
				
			||||||
				self.music_track_changed(listener, track)
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if gajim.config.get('show_roster_on_startup'):
 | 
							if gajim.config.get('show_roster_on_startup'):
 | 
				
			||||||
			self.window.show_all()
 | 
								self.window.show_all()
 | 
				
			||||||
		else:
 | 
							else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,6 +86,10 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
 | 
				
			||||||
		'''dispatch a received <message> stanza'''
 | 
							'''dispatch a received <message> stanza'''
 | 
				
			||||||
		msg_type = msg.getType()
 | 
							msg_type = msg.getType()
 | 
				
			||||||
		subject = msg.getSubject()
 | 
							subject = msg.getSubject()
 | 
				
			||||||
 | 
							if self.jid != full_jid_with_resource:
 | 
				
			||||||
 | 
								self.resource = gajim.get_nick_from_fjid(full_jid_with_resource)
 | 
				
			||||||
 | 
								if self.control and self.control.resource:
 | 
				
			||||||
 | 
									self.control.change_resource(self.resource)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if not msg_type or msg_type not in ('chat', 'groupchat', 'error'):
 | 
							if not msg_type or msg_type not in ('chat', 'groupchat', 'error'):
 | 
				
			||||||
			msg_type = 'normal'
 | 
								msg_type = 'normal'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue