merge ad_hoc branch. so we now have ad hoc commands and pubsub (atom). A great thanks to Tomasz Melcer (liorithiel) and Google Summer Of Code. fixes #189
This commit is contained in:
		
						commit
						0a2cd86fce
					
				
					 24 changed files with 4568 additions and 139 deletions
				
			
		| 
						 | 
				
			
			@ -113,6 +113,26 @@
 | 
			
		|||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkImageMenuItem" id="execute_command_menuitem">
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
      <property name="label" translatable="yes">_Execute Command...</property>
 | 
			
		||||
      <property name="use_underline">True</property>
 | 
			
		||||
 | 
			
		||||
      <child internal-child="image">
 | 
			
		||||
	<widget class="GtkImage" id="image1246">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="stock">gtk-execute</property>
 | 
			
		||||
	  <property name="icon_size">1</property>
 | 
			
		||||
	  <property name="xalign">0.5</property>
 | 
			
		||||
	  <property name="yalign">0.5</property>
 | 
			
		||||
	  <property name="xpad">0</property>
 | 
			
		||||
	  <property name="ypad">0</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
      </child>
 | 
			
		||||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkImageMenuItem" id="edit_account_menuitem">
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										764
									
								
								data/glade/adhoc_commands_window.glade
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										764
									
								
								data/glade/adhoc_commands_window.glade
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,764 @@
 | 
			
		|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
 | 
			
		||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
 | 
			
		||||
 | 
			
		||||
<glade-interface>
 | 
			
		||||
 | 
			
		||||
<widget class="GtkWindow" id="adhoc_commands_window">
 | 
			
		||||
  <property name="visible">True</property>
 | 
			
		||||
  <property name="title" translatable="yes">Ad-hoc Commands - Gajim</property>
 | 
			
		||||
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
 | 
			
		||||
  <property name="window_position">GTK_WIN_POS_NONE</property>
 | 
			
		||||
  <property name="modal">False</property>
 | 
			
		||||
  <property name="resizable">True</property>
 | 
			
		||||
  <property name="destroy_with_parent">False</property>
 | 
			
		||||
  <property name="decorated">True</property>
 | 
			
		||||
  <property name="skip_taskbar_hint">False</property>
 | 
			
		||||
  <property name="skip_pager_hint">False</property>
 | 
			
		||||
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
 | 
			
		||||
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
 | 
			
		||||
  <property name="focus_on_map">True</property>
 | 
			
		||||
  <signal name="destroy" handler="on_adhoc_commands_window_destroy" last_modification_time="Thu, 22 Jun 2006 22:50:45 GMT"/>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkVBox" id="vbox1">
 | 
			
		||||
      <property name="border_width">6</property>
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
      <property name="homogeneous">False</property>
 | 
			
		||||
      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkNotebook" id="stages_notebook">
 | 
			
		||||
	  <property name="width_request">400</property>
 | 
			
		||||
	  <property name="height_request">400</property>
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="show_tabs">False</property>
 | 
			
		||||
	  <property name="show_border">False</property>
 | 
			
		||||
	  <property name="tab_pos">GTK_POS_TOP</property>
 | 
			
		||||
	  <property name="scrollable">False</property>
 | 
			
		||||
	  <property name="enable_popup">False</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkVBox" id="retrieving_commands_stage_vbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="label247">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes">Please wait while retrieving command list...</property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">False</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">False</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0.5</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">0</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">True</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkProgressBar" id="retrieving_commands_progressbar">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
 | 
			
		||||
		  <property name="fraction">0</property>
 | 
			
		||||
		  <property name="pulse_step">0.0500000007451</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">True</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<placeholder/>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="tab_expand">False</property>
 | 
			
		||||
	      <property name="tab_fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label264">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label265">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkVBox" id="command_list_stage_vbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="label253">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes"><b>Choose command to execute:</b></property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">True</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">False</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0.20000000298</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">6</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkVBox" id="command_list_vbox">
 | 
			
		||||
		  <property name="border_width">12</property>
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="homogeneous">False</property>
 | 
			
		||||
		  <property name="spacing">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">True</property>
 | 
			
		||||
		  <property name="fill">True</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkHBox" id="hbox2957">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="homogeneous">False</property>
 | 
			
		||||
		  <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <placeholder/>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="check_commands_1_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label" translatable="yes">Check once more</property>
 | 
			
		||||
		      <property name="use_underline">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_check_commands_1_button_clicked" last_modification_time="Fri, 23 Jun 2006 18:05:14 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		      <property name="pack_type">GTK_PACK_END</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">True</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="tab_expand">False</property>
 | 
			
		||||
	      <property name="tab_fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label266">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label267">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkVBox" id="sending_form_stage_vbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="notes_label">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes">Please wait while the command is sending...</property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">False</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">False</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0.5</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">0</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<placeholder/>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkProgressBar" id="sending_form_progressbar">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
 | 
			
		||||
		  <property name="fraction">0</property>
 | 
			
		||||
		  <property name="pulse_step">0.0500000007451</property>
 | 
			
		||||
		  <property name="text" translatable="yes">Please wait...</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		  <property name="pack_type">GTK_PACK_END</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="tab_expand">False</property>
 | 
			
		||||
	      <property name="tab_fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label268">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label269">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkVBox" id="no_commands_stage_vbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="label263">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes">This jabber entity does not expose any commands.</property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">False</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">False</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0.5</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">0</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<placeholder/>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkHBox" id="hbox2956">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="homogeneous">False</property>
 | 
			
		||||
		  <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <placeholder/>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="check_commands_2_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label" translatable="yes">Check once more</property>
 | 
			
		||||
		      <property name="use_underline">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_check_commands_2_button_clicked" last_modification_time="Fri, 23 Jun 2006 18:05:31 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		      <property name="pack_type">GTK_PACK_END</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		  <property name="pack_type">GTK_PACK_END</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="tab_expand">False</property>
 | 
			
		||||
	      <property name="tab_fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label270">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label271">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkVBox" id="error_stage_vbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="label257">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes"><b>An error has occured:</b></property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">True</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">False</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0.5</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">0</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="error_description_label">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes">Error description...</property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">False</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">True</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0.5</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">0</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="tab_expand">False</property>
 | 
			
		||||
	      <property name="tab_fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label272">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label273">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">True</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkHSeparator" id="hseparator1">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">3</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">False</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkHBox" id="hbox2953">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="homogeneous">False</property>
 | 
			
		||||
	  <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkHBox" id="hbox2958">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkButton" id="cancel_button">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="label">gtk-cancel</property>
 | 
			
		||||
		  <property name="use_stock">True</property>
 | 
			
		||||
		  <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		  <property name="focus_on_click">True</property>
 | 
			
		||||
		  <signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Mon, 10 Jul 2006 16:24:32 GMT"/>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<placeholder/>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">3</property>
 | 
			
		||||
	      <property name="expand">True</property>
 | 
			
		||||
	      <property name="fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkButton" id="back_button">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="label">gtk-go-back</property>
 | 
			
		||||
	      <property name="use_stock">True</property>
 | 
			
		||||
	      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
	      <property name="focus_on_click">True</property>
 | 
			
		||||
	      <signal name="clicked" handler="on_back_button_clicked" last_modification_time="Mon, 10 Jul 2006 16:24:50 GMT"/>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">0</property>
 | 
			
		||||
	      <property name="expand">False</property>
 | 
			
		||||
	      <property name="fill">False</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkButton" id="forward_button">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="label">gtk-go-forward</property>
 | 
			
		||||
	      <property name="use_stock">True</property>
 | 
			
		||||
	      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
	      <property name="focus_on_click">True</property>
 | 
			
		||||
	      <signal name="clicked" handler="on_forward_button_clicked" last_modification_time="Mon, 10 Jul 2006 16:24:47 GMT"/>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">3</property>
 | 
			
		||||
	      <property name="expand">False</property>
 | 
			
		||||
	      <property name="fill">False</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkButton" id="execute_button">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="label">gtk-execute</property>
 | 
			
		||||
	      <property name="use_stock">True</property>
 | 
			
		||||
	      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
	      <property name="focus_on_click">True</property>
 | 
			
		||||
	      <signal name="clicked" handler="on_execute_button_clicked" last_modification_time="Mon, 10 Jul 2006 16:24:42 GMT"/>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">3</property>
 | 
			
		||||
	      <property name="expand">False</property>
 | 
			
		||||
	      <property name="fill">False</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkButton" id="close_button">
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="label">gtk-close</property>
 | 
			
		||||
	      <property name="use_stock">True</property>
 | 
			
		||||
	      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
	      <property name="focus_on_click">True</property>
 | 
			
		||||
	      <signal name="clicked" handler="on_close_button_clicked" last_modification_time="Mon, 10 Jul 2006 16:24:38 GMT"/>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">3</property>
 | 
			
		||||
	      <property name="expand">False</property>
 | 
			
		||||
	      <property name="fill">False</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">False</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
</widget>
 | 
			
		||||
 | 
			
		||||
</glade-interface>
 | 
			
		||||
							
								
								
									
										371
									
								
								data/glade/atom_entry_window.glade
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								data/glade/atom_entry_window.glade
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,371 @@
 | 
			
		|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
 | 
			
		||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
 | 
			
		||||
 | 
			
		||||
<glade-interface>
 | 
			
		||||
 | 
			
		||||
<widget class="GtkWindow" id="atom_entry_window">
 | 
			
		||||
  <property name="visible">True</property>
 | 
			
		||||
  <property name="title" translatable="yes">New entry received</property>
 | 
			
		||||
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
 | 
			
		||||
  <property name="window_position">GTK_WIN_POS_NONE</property>
 | 
			
		||||
  <property name="modal">False</property>
 | 
			
		||||
  <property name="resizable">True</property>
 | 
			
		||||
  <property name="destroy_with_parent">False</property>
 | 
			
		||||
  <property name="decorated">True</property>
 | 
			
		||||
  <property name="skip_taskbar_hint">False</property>
 | 
			
		||||
  <property name="skip_pager_hint">False</property>
 | 
			
		||||
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
 | 
			
		||||
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
 | 
			
		||||
  <property name="focus_on_map">True</property>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkVBox" id="vbox112">
 | 
			
		||||
      <property name="border_width">6</property>
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
      <property name="homogeneous">False</property>
 | 
			
		||||
      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkLabel" id="new_entry_label">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="label" translatable="yes">You have received new entry:</property>
 | 
			
		||||
	  <property name="use_underline">False</property>
 | 
			
		||||
	  <property name="use_markup">False</property>
 | 
			
		||||
	  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	  <property name="wrap">False</property>
 | 
			
		||||
	  <property name="selectable">False</property>
 | 
			
		||||
	  <property name="xalign">0.5</property>
 | 
			
		||||
	  <property name="yalign">0.5</property>
 | 
			
		||||
	  <property name="xpad">0</property>
 | 
			
		||||
	  <property name="ypad">0</property>
 | 
			
		||||
	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	  <property name="width_chars">-1</property>
 | 
			
		||||
	  <property name="single_line_mode">False</property>
 | 
			
		||||
	  <property name="angle">0</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">False</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkHSeparator" id="hseparator14">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkTable" id="table4">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="n_rows">4</property>
 | 
			
		||||
	  <property name="n_columns">2</property>
 | 
			
		||||
	  <property name="homogeneous">False</property>
 | 
			
		||||
	  <property name="row_spacing">0</property>
 | 
			
		||||
	  <property name="column_spacing">6</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label1">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes">Feed name:</property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_RIGHT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">0</property>
 | 
			
		||||
	      <property name="right_attach">1</property>
 | 
			
		||||
	      <property name="top_attach">0</property>
 | 
			
		||||
	      <property name="bottom_attach">1</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label2">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes">Entry:</property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">0</property>
 | 
			
		||||
	      <property name="right_attach">1</property>
 | 
			
		||||
	      <property name="top_attach">2</property>
 | 
			
		||||
	      <property name="bottom_attach">3</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label3">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes">Last modified:</property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">0</property>
 | 
			
		||||
	      <property name="right_attach">1</property>
 | 
			
		||||
	      <property name="top_attach">3</property>
 | 
			
		||||
	      <property name="bottom_attach">4</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="feed_tagline_label">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"><small>Romeo and Juliet</small></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">True</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">1</property>
 | 
			
		||||
	      <property name="right_attach">2</property>
 | 
			
		||||
	      <property name="top_attach">1</property>
 | 
			
		||||
	      <property name="bottom_attach">2</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="last_modified_label">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes">2003-12-13T18:30:02Z</property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">1</property>
 | 
			
		||||
	      <property name="right_attach">2</property>
 | 
			
		||||
	      <property name="top_attach">3</property>
 | 
			
		||||
	      <property name="bottom_attach">4</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkEventBox" id="feed_title_eventbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="visible_window">True</property>
 | 
			
		||||
	      <property name="above_child">False</property>
 | 
			
		||||
	      <signal name="button_press_event" handler="on_feed_title_eventbox_button_press_event" last_modification_time="Thu, 20 Jul 2006 21:53:07 GMT"/>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="feed_title_label">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes">Old stories</property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">True</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">False</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">0</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">1</property>
 | 
			
		||||
	      <property name="right_attach">2</property>
 | 
			
		||||
	      <property name="top_attach">0</property>
 | 
			
		||||
	      <property name="bottom_attach">1</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options">fill</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkEventBox" id="entry_title_eventbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="visible_window">True</property>
 | 
			
		||||
	      <property name="above_child">False</property>
 | 
			
		||||
	      <signal name="button_press_event" handler="on_entry_title_eventbox_button_press_event" last_modification_time="Thu, 20 Jul 2006 21:53:12 GMT"/>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkLabel" id="entry_title_label">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="label" translatable="yes">Soliloquy</property>
 | 
			
		||||
		  <property name="use_underline">False</property>
 | 
			
		||||
		  <property name="use_markup">True</property>
 | 
			
		||||
		  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		  <property name="wrap">False</property>
 | 
			
		||||
		  <property name="selectable">False</property>
 | 
			
		||||
		  <property name="xalign">0</property>
 | 
			
		||||
		  <property name="yalign">0.5</property>
 | 
			
		||||
		  <property name="xpad">0</property>
 | 
			
		||||
		  <property name="ypad">0</property>
 | 
			
		||||
		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		  <property name="width_chars">-1</property>
 | 
			
		||||
		  <property name="single_line_mode">False</property>
 | 
			
		||||
		  <property name="angle">0</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">1</property>
 | 
			
		||||
	      <property name="right_attach">2</property>
 | 
			
		||||
	      <property name="top_attach">2</property>
 | 
			
		||||
	      <property name="bottom_attach">3</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options">fill</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">True</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkHSeparator" id="hseparator15">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkHBox" id="hbox1">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="homogeneous">False</property>
 | 
			
		||||
	  <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkHBox" id="hbox2">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkButton" id="close_button">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="label">gtk-close</property>
 | 
			
		||||
		  <property name="use_stock">True</property>
 | 
			
		||||
		  <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		  <property name="focus_on_click">True</property>
 | 
			
		||||
		  <signal name="clicked" handler="on_close_button_clicked" last_modification_time="Thu, 20 Jul 2006 21:29:17 GMT"/>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<placeholder/>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">0</property>
 | 
			
		||||
	      <property name="expand">True</property>
 | 
			
		||||
	      <property name="fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkButton" id="next_button">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="label">Next entry</property>
 | 
			
		||||
	      <property name="use_underline">True</property>
 | 
			
		||||
	      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
	      <property name="focus_on_click">True</property>
 | 
			
		||||
	      <signal name="clicked" handler="on_next_button_clicked" last_modification_time="Thu, 20 Jul 2006 21:29:21 GMT"/>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">0</property>
 | 
			
		||||
	      <property name="expand">False</property>
 | 
			
		||||
	      <property name="fill">False</property>
 | 
			
		||||
	      <property name="pack_type">GTK_PACK_END</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
</widget>
 | 
			
		||||
 | 
			
		||||
</glade-interface>
 | 
			
		||||
| 
						 | 
				
			
			@ -183,4 +183,626 @@
 | 
			
		|||
  </child>
 | 
			
		||||
</widget>
 | 
			
		||||
 | 
			
		||||
<widget class="GtkWindow" id="data_form_old_fake_window">
 | 
			
		||||
  <property name="visible">True</property>
 | 
			
		||||
  <property name="title" translatable="yes">window1</property>
 | 
			
		||||
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
 | 
			
		||||
  <property name="window_position">GTK_WIN_POS_NONE</property>
 | 
			
		||||
  <property name="modal">False</property>
 | 
			
		||||
  <property name="resizable">True</property>
 | 
			
		||||
  <property name="destroy_with_parent">False</property>
 | 
			
		||||
  <property name="decorated">True</property>
 | 
			
		||||
  <property name="skip_taskbar_hint">False</property>
 | 
			
		||||
  <property name="skip_pager_hint">False</property>
 | 
			
		||||
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
 | 
			
		||||
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
 | 
			
		||||
  <property name="focus_on_map">True</property>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkVBox" id="vbox111">
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
      <property name="homogeneous">False</property>
 | 
			
		||||
      <property name="spacing">10</property>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkScrolledWindow" id="data_form_scrolledwindow">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="can_focus">True</property>
 | 
			
		||||
	  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
	  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
	  <property name="shadow_type">GTK_SHADOW_IN</property>
 | 
			
		||||
	  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkViewport" id="viewport2">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="shadow_type">GTK_SHADOW_IN</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkVBox" id="container_vbox">
 | 
			
		||||
		  <property name="border_width">5</property>
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="homogeneous">False</property>
 | 
			
		||||
		  <property name="spacing">5</property>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkLabel" id="form_instructions_label">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="label" translatable="yes"></property>
 | 
			
		||||
		      <property name="use_underline">False</property>
 | 
			
		||||
		      <property name="use_markup">False</property>
 | 
			
		||||
		      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
		      <property name="wrap">True</property>
 | 
			
		||||
		      <property name="selectable">False</property>
 | 
			
		||||
		      <property name="xalign">0</property>
 | 
			
		||||
		      <property name="yalign">0.5</property>
 | 
			
		||||
		      <property name="xpad">0</property>
 | 
			
		||||
		      <property name="ypad">0</property>
 | 
			
		||||
		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
		      <property name="width_chars">-1</property>
 | 
			
		||||
		      <property name="single_line_mode">False</property>
 | 
			
		||||
		      <property name="angle">0</property>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">5</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkHSeparator" id="form_instructions_hseparator">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">5</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">True</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <placeholder/>
 | 
			
		||||
		  </child>
 | 
			
		||||
		</widget>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">True</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkTable" id="item_list_table">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="n_rows">1</property>
 | 
			
		||||
	  <property name="n_columns">2</property>
 | 
			
		||||
	  <property name="homogeneous">False</property>
 | 
			
		||||
	  <property name="row_spacing">0</property>
 | 
			
		||||
	  <property name="column_spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkScrolledWindow" id="scrolledwindow36">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
 | 
			
		||||
	      <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
 | 
			
		||||
	      <property name="shadow_type">GTK_SHADOW_IN</property>
 | 
			
		||||
	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkTreeView" id="item_treeview">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="headers_visible">False</property>
 | 
			
		||||
		  <property name="rules_hint">False</property>
 | 
			
		||||
		  <property name="reorderable">False</property>
 | 
			
		||||
		  <property name="enable_search">True</property>
 | 
			
		||||
		  <property name="fixed_height_mode">False</property>
 | 
			
		||||
		  <property name="hover_selection">False</property>
 | 
			
		||||
		  <property name="hover_expand">False</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">0</property>
 | 
			
		||||
	      <property name="right_attach">1</property>
 | 
			
		||||
	      <property name="top_attach">0</property>
 | 
			
		||||
	      <property name="bottom_attach">1</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkVBox" id="vbox112">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkButton" id="clear_button">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="label">gtk-clear</property>
 | 
			
		||||
		  <property name="use_stock">True</property>
 | 
			
		||||
		  <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		  <property name="focus_on_click">True</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">True</property>
 | 
			
		||||
		  <property name="pack_type">GTK_PACK_END</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkButton" id="remove_button">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="label">gtk-remove</property>
 | 
			
		||||
		  <property name="use_stock">True</property>
 | 
			
		||||
		  <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		  <property name="focus_on_click">True</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">True</property>
 | 
			
		||||
		  <property name="pack_type">GTK_PACK_END</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkButton" id="add_button">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="label">gtk-add</property>
 | 
			
		||||
		  <property name="use_stock">True</property>
 | 
			
		||||
		  <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		  <property name="focus_on_click">True</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">True</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkButton" id="edit_button">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="label">gtk-edit</property>
 | 
			
		||||
		  <property name="use_stock">True</property>
 | 
			
		||||
		  <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		  <property name="focus_on_click">True</property>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">False</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<placeholder/>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">1</property>
 | 
			
		||||
	      <property name="right_attach">2</property>
 | 
			
		||||
	      <property name="top_attach">0</property>
 | 
			
		||||
	      <property name="bottom_attach">1</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options">fill</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">True</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
</widget>
 | 
			
		||||
 | 
			
		||||
<widget class="GtkWindow" id="data_form_fake_window">
 | 
			
		||||
  <property name="visible">True</property>
 | 
			
		||||
  <property name="title" translatable="yes">window1</property>
 | 
			
		||||
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
 | 
			
		||||
  <property name="window_position">GTK_WIN_POS_NONE</property>
 | 
			
		||||
  <property name="modal">False</property>
 | 
			
		||||
  <property name="resizable">True</property>
 | 
			
		||||
  <property name="destroy_with_parent">False</property>
 | 
			
		||||
  <property name="decorated">True</property>
 | 
			
		||||
  <property name="skip_taskbar_hint">False</property>
 | 
			
		||||
  <property name="skip_pager_hint">False</property>
 | 
			
		||||
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
 | 
			
		||||
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
 | 
			
		||||
  <property name="focus_on_map">True</property>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkVBox" id="data_form_vbox">
 | 
			
		||||
      <property name="border_width">3</property>
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
      <property name="homogeneous">False</property>
 | 
			
		||||
      <property name="spacing">3</property>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkLabel" id="instructions_label">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="label" translatable="yes">Fill in the form.</property>
 | 
			
		||||
	  <property name="use_underline">False</property>
 | 
			
		||||
	  <property name="use_markup">False</property>
 | 
			
		||||
	  <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	  <property name="wrap">False</property>
 | 
			
		||||
	  <property name="selectable">False</property>
 | 
			
		||||
	  <property name="xalign">0.5</property>
 | 
			
		||||
	  <property name="yalign">0.5</property>
 | 
			
		||||
	  <property name="xpad">0</property>
 | 
			
		||||
	  <property name="ypad">0</property>
 | 
			
		||||
	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	  <property name="width_chars">-1</property>
 | 
			
		||||
	  <property name="single_line_mode">False</property>
 | 
			
		||||
	  <property name="angle">0</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">False</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkHSeparator" id="instructions_hseparator">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkNotebook" id="data_form_types_notebook">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="show_tabs">False</property>
 | 
			
		||||
	  <property name="show_border">True</property>
 | 
			
		||||
	  <property name="tab_pos">GTK_POS_TOP</property>
 | 
			
		||||
	  <property name="scrollable">False</property>
 | 
			
		||||
	  <property name="enable_popup">False</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkScrolledWindow" id="single_form_scrolledwindow">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
	      <property name="shadow_type">GTK_SHADOW_NONE</property>
 | 
			
		||||
	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkViewport" id="single_form_viewport">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="shadow_type">GTK_SHADOW_IN</property>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <placeholder/>
 | 
			
		||||
		  </child>
 | 
			
		||||
		</widget>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="tab_expand">False</property>
 | 
			
		||||
	      <property name="tab_fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label1">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label2">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkHBox" id="multiple_form_hbox">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="homogeneous">False</property>
 | 
			
		||||
	      <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkScrolledWindow" id="scrolledwindow38">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="can_focus">True</property>
 | 
			
		||||
		  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
		  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
		  <property name="shadow_type">GTK_SHADOW_IN</property>
 | 
			
		||||
		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkTreeView" id="records_treeview">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="headers_visible">True</property>
 | 
			
		||||
		      <property name="rules_hint">False</property>
 | 
			
		||||
		      <property name="reorderable">False</property>
 | 
			
		||||
		      <property name="enable_search">True</property>
 | 
			
		||||
		      <property name="fixed_height_mode">False</property>
 | 
			
		||||
		      <property name="hover_selection">False</property>
 | 
			
		||||
		      <property name="hover_expand">False</property>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		  </child>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">True</property>
 | 
			
		||||
		  <property name="fill">True</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
 | 
			
		||||
	      <child>
 | 
			
		||||
		<widget class="GtkVBox" id="vbox114">
 | 
			
		||||
		  <property name="visible">True</property>
 | 
			
		||||
		  <property name="homogeneous">False</property>
 | 
			
		||||
		  <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="add_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label">gtk-add</property>
 | 
			
		||||
		      <property name="use_stock">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_add_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:45 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="remove_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label">gtk-remove</property>
 | 
			
		||||
		      <property name="use_stock">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_remove_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:50 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkHSeparator" id="hseparator17">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">3</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="edit_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label">gtk-edit</property>
 | 
			
		||||
		      <property name="use_stock">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_edit_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:54 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkHSeparator" id="hseparator18">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">3</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">True</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="up_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label">gtk-go-up</property>
 | 
			
		||||
		      <property name="use_stock">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_up_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:59 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="down_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label">gtk-go-down</property>
 | 
			
		||||
		      <property name="use_stock">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_down_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:33:03 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkHSeparator" id="hseparator19">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">3</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">True</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
 | 
			
		||||
		  <child>
 | 
			
		||||
		    <widget class="GtkButton" id="clear_button">
 | 
			
		||||
		      <property name="visible">True</property>
 | 
			
		||||
		      <property name="can_focus">True</property>
 | 
			
		||||
		      <property name="label">gtk-clear</property>
 | 
			
		||||
		      <property name="use_stock">True</property>
 | 
			
		||||
		      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
		      <property name="focus_on_click">True</property>
 | 
			
		||||
		      <signal name="clicked" handler="on_clear_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:33:07 GMT"/>
 | 
			
		||||
		    </widget>
 | 
			
		||||
		    <packing>
 | 
			
		||||
		      <property name="padding">0</property>
 | 
			
		||||
		      <property name="expand">False</property>
 | 
			
		||||
		      <property name="fill">False</property>
 | 
			
		||||
		    </packing>
 | 
			
		||||
		  </child>
 | 
			
		||||
		</widget>
 | 
			
		||||
		<packing>
 | 
			
		||||
		  <property name="padding">0</property>
 | 
			
		||||
		  <property name="expand">False</property>
 | 
			
		||||
		  <property name="fill">True</property>
 | 
			
		||||
		</packing>
 | 
			
		||||
	      </child>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="tab_expand">False</property>
 | 
			
		||||
	      <property name="tab_fill">True</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label3">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label4">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes"></property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0.5</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="type">tab</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">True</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
</widget>
 | 
			
		||||
 | 
			
		||||
</glade-interface>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										232
									
								
								data/glade/groups_post_window.glade
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								data/glade/groups_post_window.glade
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,232 @@
 | 
			
		|||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
 | 
			
		||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
 | 
			
		||||
 | 
			
		||||
<glade-interface>
 | 
			
		||||
 | 
			
		||||
<widget class="GtkWindow" id="groups_post_window">
 | 
			
		||||
  <property name="visible">True</property>
 | 
			
		||||
  <property name="title" translatable="yes">Create new post</property>
 | 
			
		||||
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
 | 
			
		||||
  <property name="window_position">GTK_WIN_POS_NONE</property>
 | 
			
		||||
  <property name="modal">False</property>
 | 
			
		||||
  <property name="resizable">True</property>
 | 
			
		||||
  <property name="destroy_with_parent">False</property>
 | 
			
		||||
  <property name="decorated">True</property>
 | 
			
		||||
  <property name="skip_taskbar_hint">False</property>
 | 
			
		||||
  <property name="skip_pager_hint">False</property>
 | 
			
		||||
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
 | 
			
		||||
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
 | 
			
		||||
  <property name="focus_on_map">True</property>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkVBox" id="vbox1">
 | 
			
		||||
      <property name="border_width">6</property>
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
      <property name="homogeneous">False</property>
 | 
			
		||||
      <property name="spacing">4</property>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkTable" id="table1">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="n_rows">2</property>
 | 
			
		||||
	  <property name="n_columns">2</property>
 | 
			
		||||
	  <property name="homogeneous">False</property>
 | 
			
		||||
	  <property name="row_spacing">0</property>
 | 
			
		||||
	  <property name="column_spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label1">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes">From</property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">0</property>
 | 
			
		||||
	      <property name="right_attach">1</property>
 | 
			
		||||
	      <property name="top_attach">0</property>
 | 
			
		||||
	      <property name="bottom_attach">1</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkLabel" id="label2">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="label" translatable="yes">Subject</property>
 | 
			
		||||
	      <property name="use_underline">False</property>
 | 
			
		||||
	      <property name="use_markup">False</property>
 | 
			
		||||
	      <property name="justify">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap">False</property>
 | 
			
		||||
	      <property name="selectable">False</property>
 | 
			
		||||
	      <property name="xalign">0</property>
 | 
			
		||||
	      <property name="yalign">0.5</property>
 | 
			
		||||
	      <property name="xpad">0</property>
 | 
			
		||||
	      <property name="ypad">0</property>
 | 
			
		||||
	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 | 
			
		||||
	      <property name="width_chars">-1</property>
 | 
			
		||||
	      <property name="single_line_mode">False</property>
 | 
			
		||||
	      <property name="angle">0</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">0</property>
 | 
			
		||||
	      <property name="right_attach">1</property>
 | 
			
		||||
	      <property name="top_attach">1</property>
 | 
			
		||||
	      <property name="bottom_attach">2</property>
 | 
			
		||||
	      <property name="x_options">fill</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkEntry" id="from_entry">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="editable">True</property>
 | 
			
		||||
	      <property name="visibility">True</property>
 | 
			
		||||
	      <property name="max_length">0</property>
 | 
			
		||||
	      <property name="text" translatable="yes"></property>
 | 
			
		||||
	      <property name="has_frame">True</property>
 | 
			
		||||
	      <property name="invisible_char">●</property>
 | 
			
		||||
	      <property name="activates_default">False</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">1</property>
 | 
			
		||||
	      <property name="right_attach">2</property>
 | 
			
		||||
	      <property name="top_attach">0</property>
 | 
			
		||||
	      <property name="bottom_attach">1</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkEntry" id="subject_entry">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="editable">True</property>
 | 
			
		||||
	      <property name="visibility">True</property>
 | 
			
		||||
	      <property name="max_length">0</property>
 | 
			
		||||
	      <property name="text" translatable="yes"></property>
 | 
			
		||||
	      <property name="has_frame">True</property>
 | 
			
		||||
	      <property name="invisible_char">●</property>
 | 
			
		||||
	      <property name="activates_default">False</property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="left_attach">1</property>
 | 
			
		||||
	      <property name="right_attach">2</property>
 | 
			
		||||
	      <property name="top_attach">1</property>
 | 
			
		||||
	      <property name="bottom_attach">2</property>
 | 
			
		||||
	      <property name="y_options"></property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkScrolledWindow" id="scrolledwindow1">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="can_focus">True</property>
 | 
			
		||||
	  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
	  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
 | 
			
		||||
	  <property name="shadow_type">GTK_SHADOW_IN</property>
 | 
			
		||||
	  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkTextView" id="contents_textview">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="editable">True</property>
 | 
			
		||||
	      <property name="overwrite">False</property>
 | 
			
		||||
	      <property name="accepts_tab">True</property>
 | 
			
		||||
	      <property name="justification">GTK_JUSTIFY_LEFT</property>
 | 
			
		||||
	      <property name="wrap_mode">GTK_WRAP_NONE</property>
 | 
			
		||||
	      <property name="cursor_visible">True</property>
 | 
			
		||||
	      <property name="pixels_above_lines">0</property>
 | 
			
		||||
	      <property name="pixels_below_lines">0</property>
 | 
			
		||||
	      <property name="pixels_inside_wrap">0</property>
 | 
			
		||||
	      <property name="left_margin">0</property>
 | 
			
		||||
	      <property name="right_margin">0</property>
 | 
			
		||||
	      <property name="indent">0</property>
 | 
			
		||||
	      <property name="text" translatable="yes"></property>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">True</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkHBox" id="hbox1">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="homogeneous">False</property>
 | 
			
		||||
	  <property name="spacing">0</property>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkButton" id="cancel_button">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="label">gtk-cancel</property>
 | 
			
		||||
	      <property name="use_stock">True</property>
 | 
			
		||||
	      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
	      <property name="focus_on_click">True</property>
 | 
			
		||||
	      <signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Fri, 18 Aug 2006 16:27:42 GMT"/>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">0</property>
 | 
			
		||||
	      <property name="expand">False</property>
 | 
			
		||||
	      <property name="fill">False</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkButton" id="send_button">
 | 
			
		||||
	      <property name="visible">True</property>
 | 
			
		||||
	      <property name="can_focus">True</property>
 | 
			
		||||
	      <property name="label">gtk-go-forward</property>
 | 
			
		||||
	      <property name="use_stock">True</property>
 | 
			
		||||
	      <property name="relief">GTK_RELIEF_NORMAL</property>
 | 
			
		||||
	      <property name="focus_on_click">True</property>
 | 
			
		||||
	      <signal name="clicked" handler="on_send_button_clicked" last_modification_time="Fri, 18 Aug 2006 16:27:48 GMT"/>
 | 
			
		||||
	    </widget>
 | 
			
		||||
	    <packing>
 | 
			
		||||
	      <property name="padding">0</property>
 | 
			
		||||
	      <property name="expand">False</property>
 | 
			
		||||
	      <property name="fill">False</property>
 | 
			
		||||
	    </packing>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
	<packing>
 | 
			
		||||
	  <property name="padding">0</property>
 | 
			
		||||
	  <property name="expand">False</property>
 | 
			
		||||
	  <property name="fill">True</property>
 | 
			
		||||
	</packing>
 | 
			
		||||
      </child>
 | 
			
		||||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
</widget>
 | 
			
		||||
 | 
			
		||||
</glade-interface>
 | 
			
		||||
| 
						 | 
				
			
			@ -17,9 +17,7 @@
 | 
			
		|||
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
 | 
			
		||||
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
 | 
			
		||||
  <property name="focus_on_map">True</property>
 | 
			
		||||
  <property name="urgency_hint">False</property>
 | 
			
		||||
  <property name="has_separator">False</property>
 | 
			
		||||
  <signal name="destroy" handler="on_input_dialog_destroy" last_modification_time="Sat, 18 Nov 2006 20:23:21 GMT"/>
 | 
			
		||||
 | 
			
		||||
  <child internal-child="vbox">
 | 
			
		||||
    <widget class="GtkVBox" id="dialog-vbox10">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,6 +157,26 @@
 | 
			
		|||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkImageMenuItem" id="execute_command_menuitem">
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
      <property name="label" translatable="yes">Execute Command...</property>
 | 
			
		||||
      <property name="use_underline">True</property>
 | 
			
		||||
 | 
			
		||||
      <child internal-child="image">
 | 
			
		||||
	<widget class="GtkImage" id="image1467">
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="stock">gtk-execute</property>
 | 
			
		||||
	  <property name="icon_size">1</property>
 | 
			
		||||
	  <property name="xalign">0.5</property>
 | 
			
		||||
	  <property name="yalign">0.5</property>
 | 
			
		||||
	  <property name="xpad">0</property>
 | 
			
		||||
	  <property name="ypad">0</property>
 | 
			
		||||
	</widget>
 | 
			
		||||
      </child>
 | 
			
		||||
    </widget>
 | 
			
		||||
  </child>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkSeparatorMenuItem" id="above_subscription_separator">
 | 
			
		||||
      <property name="visible">True</property>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,6 @@
 | 
			
		|||
  <signal name="focus_in_event" handler="on_roster_window_focus_in_event" last_modification_time="Sun, 04 Sep 2005 16:33:35 GMT"/>
 | 
			
		||||
  <signal name="key_press_event" handler="on_roster_window_key_press_event" last_modification_time="Tue, 20 Sep 2005 19:26:27 GMT"/>
 | 
			
		||||
  <signal name="focus_out_event" handler="on_roster_window_focus_out_event" last_modification_time="Tue, 08 Nov 2005 14:01:01 GMT"/>
 | 
			
		||||
  <signal name="popup_menu" handler="on_roster_window_popup_menu" last_modification_time="Sat, 18 Nov 2006 21:12:06 GMT"/>
 | 
			
		||||
 | 
			
		||||
  <child>
 | 
			
		||||
    <widget class="GtkVBox" id="roster_vbox">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										514
									
								
								src/adhoc_commands.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										514
									
								
								src/adhoc_commands.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,514 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
##	config.py
 | 
			
		||||
##
 | 
			
		||||
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
 | 
			
		||||
##                         Vincent Hanquez <tab@snarc.org>
 | 
			
		||||
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
 | 
			
		||||
##                    Vincent Hanquez <tab@snarc.org>
 | 
			
		||||
##                    Nikos Kouremenos <nkour@jabber.org>
 | 
			
		||||
##                    Dimitur Kirov <dkirov@gmail.com>
 | 
			
		||||
##                    Travis Shirk <travis@pobox.com>
 | 
			
		||||
##                    Norman Rasmussen <norman@rasmussen.co.za>
 | 
			
		||||
##
 | 
			
		||||
## 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; version 2 only.
 | 
			
		||||
##
 | 
			
		||||
## 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.
 | 
			
		||||
 | 
			
		||||
# TODO: think if we need caching command list. it may be wrong if there will
 | 
			
		||||
# TODO: be entities that often change the list, it may be slow to fetch it
 | 
			
		||||
# TODO: every time
 | 
			
		||||
 | 
			
		||||
import gobject
 | 
			
		||||
import gtk
 | 
			
		||||
 | 
			
		||||
from common import xmpp, gajim, dataforms
 | 
			
		||||
 | 
			
		||||
import gtkgui_helpers
 | 
			
		||||
import dialogs
 | 
			
		||||
import dataforms_widget
 | 
			
		||||
 | 
			
		||||
class CommandWindow:
 | 
			
		||||
	'''Class for a window for single ad-hoc commands session. Note, that
 | 
			
		||||
	there might be more than one for one account/jid pair in one moment.
 | 
			
		||||
 | 
			
		||||
	TODO: maybe put this window into MessageWindow? consider this when
 | 
			
		||||
	TODO: it will be possible to manage more than one window of one
 | 
			
		||||
	TODO: account/jid pair in MessageWindowMgr.
 | 
			
		||||
 | 
			
		||||
	TODO: gtk 2.10 has a special wizard-widget, consider using it...'''
 | 
			
		||||
 | 
			
		||||
	def __init__(self, account, jid):
 | 
			
		||||
		'''Create new window.'''
 | 
			
		||||
 | 
			
		||||
		# an account object
 | 
			
		||||
		self.account = gajim.connections[account]
 | 
			
		||||
		self.jid = jid
 | 
			
		||||
 | 
			
		||||
		self.pulse_id=None	# to satisfy self.setup_pulsing()
 | 
			
		||||
		self.commandlist=None	# a list of (commandname, commanddescription)
 | 
			
		||||
 | 
			
		||||
		# command's data
 | 
			
		||||
		self.commandnode = None
 | 
			
		||||
		self.sessionid = None
 | 
			
		||||
		self.dataform = None
 | 
			
		||||
 | 
			
		||||
		# retrieving widgets from xml
 | 
			
		||||
		self.xml = gtkgui_helpers.get_glade('adhoc_commands_window.glade')
 | 
			
		||||
		self.window = self.xml.get_widget('adhoc_commands_window')
 | 
			
		||||
		for name in ('cancel_button', 'back_button', 'forward_button',
 | 
			
		||||
			'execute_button','close_button','stages_notebook',
 | 
			
		||||
			'retrieving_commands_stage_vbox',
 | 
			
		||||
			'command_list_stage_vbox','command_list_vbox',
 | 
			
		||||
			'sending_form_stage_vbox','sending_form_progressbar',
 | 
			
		||||
			'notes_label','no_commands_stage_vbox','error_stage_vbox',
 | 
			
		||||
			'error_description_label'):
 | 
			
		||||
			self.__dict__[name] = self.xml.get_widget(name)
 | 
			
		||||
 | 
			
		||||
		# creating data forms widget
 | 
			
		||||
		self.data_form_widget = dataforms_widget.DataFormWidget()
 | 
			
		||||
		self.data_form_widget.show()
 | 
			
		||||
		self.sending_form_stage_vbox.pack_start(self.data_form_widget)
 | 
			
		||||
 | 
			
		||||
		# setting initial stage
 | 
			
		||||
		self.close_button.set_no_show_all(True)
 | 
			
		||||
		self.stage1()
 | 
			
		||||
 | 
			
		||||
		# displaying the window
 | 
			
		||||
		self.xml.signal_autoconnect(self)
 | 
			
		||||
		self.window.show_all()
 | 
			
		||||
 | 
			
		||||
# these functions are set up by appropriate stageX methods
 | 
			
		||||
	def stage_finish(self, *anything): pass
 | 
			
		||||
	def stage_cancel_button_clicked(self, *anything): assert False
 | 
			
		||||
	def stage_back_button_clicked(self, *anything): assert False
 | 
			
		||||
	def stage_forward_button_clicked(self, *anything): assert False
 | 
			
		||||
	def stage_execute_button_clicked(self, *anything): assert False
 | 
			
		||||
	def stage_close_button_clicked(self, *anything): assert False
 | 
			
		||||
	def stage_adhoc_commands_window_delete_event(self, *anything): assert False
 | 
			
		||||
	def do_nothing(self, *anything): return False
 | 
			
		||||
 | 
			
		||||
# widget callbacks
 | 
			
		||||
	def on_cancel_button_clicked(self, *anything):
 | 
			
		||||
		return self.stage_cancel_button_clicked(*anything)
 | 
			
		||||
 | 
			
		||||
	def on_back_button_clicked(self, *anything):
 | 
			
		||||
		return self.stage_back_button_clicked(*anything)
 | 
			
		||||
 | 
			
		||||
	def on_forward_button_clicked(self, *anything):
 | 
			
		||||
		return self.stage_forward_button_clicked(*anything)
 | 
			
		||||
 | 
			
		||||
	def on_execute_button_clicked(self, *anything):
 | 
			
		||||
		return self.stage_execute_button_clicked(*anything)
 | 
			
		||||
 | 
			
		||||
	def on_close_button_clicked(self, *anything):
 | 
			
		||||
		return self.stage_close_button_clicked(*anything)
 | 
			
		||||
 | 
			
		||||
	def on_adhoc_commands_window_destroy(self, *anything):
 | 
			
		||||
		# TODO: do all actions that are needed to remove this object from memory...
 | 
			
		||||
		self.remove_pulsing()
 | 
			
		||||
 | 
			
		||||
	def on_adhoc_commands_window_delete_event(self, *anything):
 | 
			
		||||
		return self.stage_adhoc_commands_window_delete_event(self, *anything)
 | 
			
		||||
 | 
			
		||||
	def __del__(self):
 | 
			
		||||
		print "Object has been deleted."
 | 
			
		||||
 | 
			
		||||
# stage 1: waiting for command list
 | 
			
		||||
	def stage1(self):
 | 
			
		||||
		'''Prepare the first stage. Request command list,
 | 
			
		||||
		set appropriate state of widgets.'''
 | 
			
		||||
		# close old stage...
 | 
			
		||||
		self.stage_finish()
 | 
			
		||||
 | 
			
		||||
		# show the stage
 | 
			
		||||
		self.stages_notebook.set_current_page(
 | 
			
		||||
			self.stages_notebook.page_num(
 | 
			
		||||
				self.retrieving_commands_stage_vbox))
 | 
			
		||||
 | 
			
		||||
		# set widgets' state
 | 
			
		||||
		self.cancel_button.set_sensitive(True)
 | 
			
		||||
		self.back_button.set_sensitive(False)
 | 
			
		||||
		self.forward_button.set_sensitive(False)
 | 
			
		||||
		self.execute_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		# request command list
 | 
			
		||||
		self.request_command_list()
 | 
			
		||||
		self.setup_pulsing(
 | 
			
		||||
			self.xml.get_widget('retrieving_commands_progressbar'))
 | 
			
		||||
 | 
			
		||||
		# setup the callbacks
 | 
			
		||||
		self.stage_finish = self.stage1_finish
 | 
			
		||||
		self.stage_cancel_button_clicked = self.stage1_cancel_button_clicked
 | 
			
		||||
		self.stage_adhoc_commands_window_delete_event = self.stage1_adhoc_commands_window_delete_event
 | 
			
		||||
 | 
			
		||||
	def stage1_finish(self):
 | 
			
		||||
		self.remove_pulsing()
 | 
			
		||||
 | 
			
		||||
	def stage1_cancel_button_clicked(self, widget):
 | 
			
		||||
		# cancelling in this stage is not critical, so we don't
 | 
			
		||||
		# show any popups to user
 | 
			
		||||
		self.stage1_finish()
 | 
			
		||||
		self.window.destroy()
 | 
			
		||||
 | 
			
		||||
	def stage1_adhoc_commands_window_delete_event(self, widget):
 | 
			
		||||
		self.stage1_finish()
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
# stage 2: choosing the command to execute
 | 
			
		||||
	def stage2(self):
 | 
			
		||||
		'''Populate the command list vbox with radiobuttons
 | 
			
		||||
		(TODO: if there is more commands, maybe some kind of list?),
 | 
			
		||||
		set widgets' state.'''
 | 
			
		||||
		# close old stage
 | 
			
		||||
		self.stage_finish()
 | 
			
		||||
 | 
			
		||||
		assert len(self.commandlist)>0
 | 
			
		||||
 | 
			
		||||
		self.stages_notebook.set_current_page(
 | 
			
		||||
			self.stages_notebook.page_num(
 | 
			
		||||
				self.command_list_stage_vbox))
 | 
			
		||||
 | 
			
		||||
		self.cancel_button.set_sensitive(True)
 | 
			
		||||
		self.back_button.set_sensitive(False)
 | 
			
		||||
		self.forward_button.set_sensitive(True)
 | 
			
		||||
		self.execute_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		# build the commands list radiobuttons
 | 
			
		||||
		first_radio = None
 | 
			
		||||
		for (commandnode, commandname) in self.commandlist:
 | 
			
		||||
			radio = gtk.RadioButton(first_radio, label=commandname)
 | 
			
		||||
			radio.connect("toggled", self.on_command_radiobutton_toggled, commandnode)
 | 
			
		||||
			if first_radio is None:
 | 
			
		||||
				first_radio = radio
 | 
			
		||||
				self.commandnode = commandnode
 | 
			
		||||
			self.command_list_vbox.pack_start(radio, expand=False)
 | 
			
		||||
		self.command_list_vbox.show_all()
 | 
			
		||||
 | 
			
		||||
		self.stage_finish = self.stage2_finish
 | 
			
		||||
		self.stage_cancel_button_clicked = self.stage2_cancel_button_clicked
 | 
			
		||||
		self.stage_forward_button_clicked = self.stage2_forward_button_clicked
 | 
			
		||||
		self.stage_adhoc_commands_window_delete_event = self.do_nothing
 | 
			
		||||
 | 
			
		||||
	def stage2_finish(self):
 | 
			
		||||
		'''Remove widgets we created. Not needed when the window is destroyed.'''
 | 
			
		||||
		def remove_widget(widget):
 | 
			
		||||
			self.command_list_vbox.remove(widget)
 | 
			
		||||
		self.command_list_vbox.foreach(remove_widget)
 | 
			
		||||
 | 
			
		||||
	def stage2_cancel_button_clicked(self, widget):
 | 
			
		||||
		self.stage_finish()
 | 
			
		||||
		self.window.destroy()
 | 
			
		||||
 | 
			
		||||
	def stage2_forward_button_clicked(self, widget):
 | 
			
		||||
		self.stage3()
 | 
			
		||||
 | 
			
		||||
	def on_command_radiobutton_toggled(self, widget, commandnode):
 | 
			
		||||
		self.commandnode = commandnode
 | 
			
		||||
 | 
			
		||||
	def on_check_commands_1_button_clicked(self, widget):
 | 
			
		||||
		self.stage1()
 | 
			
		||||
 | 
			
		||||
# stage 3: command invocation
 | 
			
		||||
	def stage3(self):
 | 
			
		||||
		# close old stage
 | 
			
		||||
		self.stage_finish()
 | 
			
		||||
 | 
			
		||||
		assert isinstance(self.commandnode, unicode)
 | 
			
		||||
 | 
			
		||||
		self.form_status = None
 | 
			
		||||
 | 
			
		||||
		self.stages_notebook.set_current_page(
 | 
			
		||||
			self.stages_notebook.page_num(
 | 
			
		||||
				self.sending_form_stage_vbox))
 | 
			
		||||
 | 
			
		||||
		self.cancel_button.set_sensitive(True)
 | 
			
		||||
		self.back_button.set_sensitive(False)
 | 
			
		||||
		self.forward_button.set_sensitive(False)
 | 
			
		||||
		self.execute_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		self.stage3_submit_form()
 | 
			
		||||
 | 
			
		||||
		self.stage_finish = self.stage3_finish
 | 
			
		||||
		self.stage_cancel_button_clicked = self.stage3_cancel_button_clicked
 | 
			
		||||
		self.stage_back_button_clicked = self.stage3_back_button_clicked
 | 
			
		||||
		self.stage_forward_button_clicked = self.stage3_forward_button_clicked
 | 
			
		||||
		self.stage_execute_button_clicked = self.stage3_execute_button_clicked
 | 
			
		||||
		self.stage_close_button_clicked = self.stage3_close_button_clicked
 | 
			
		||||
		self.stage_adhoc_commands_window_delete_event = self.stage3_cancel_button_clicked
 | 
			
		||||
 | 
			
		||||
	def stage3_finish(self):
 | 
			
		||||
		pass
 | 
			
		||||
 | 
			
		||||
	def stage3_cancel_button_clicked(self, widget, *anything):
 | 
			
		||||
		''' We are in the middle of executing command. Ask user if he really want to cancel
 | 
			
		||||
		the process, then... cancel it. '''
 | 
			
		||||
		# this works also as a handler for window_delete_event, so we have to return appropriate
 | 
			
		||||
		# values
 | 
			
		||||
		# TODO: translate it
 | 
			
		||||
		dialog = dialogs.HigDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
 | 
			
		||||
			gtk.BUTTONS_YES_NO, 'Cancel confirmation',
 | 
			
		||||
			'You are in process of executing command. Do you really want to cancel it?')
 | 
			
		||||
		dialog.popup()
 | 
			
		||||
		if dialog.get_response()==gtk.RESPONSE_YES:
 | 
			
		||||
			self.send_cancel()
 | 
			
		||||
			if widget==self.window:
 | 
			
		||||
				return False
 | 
			
		||||
			else:
 | 
			
		||||
				self.window.destroy()
 | 
			
		||||
			return False
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
	def stage3_close_button_clicked(self, widget):
 | 
			
		||||
		# this works also as a handler for window_delete_event, so we have to return appropriate
 | 
			
		||||
		# values
 | 
			
		||||
		if widget==self.window:
 | 
			
		||||
			return False
 | 
			
		||||
		else:
 | 
			
		||||
			self.window.destroy()
 | 
			
		||||
 | 
			
		||||
	def stage3_back_button_clicked(self, widget):
 | 
			
		||||
		self.stage3_submit_form('prev')
 | 
			
		||||
 | 
			
		||||
	def stage3_forward_button_clicked(self, widget):
 | 
			
		||||
		self.stage3_submit_form('next')
 | 
			
		||||
 | 
			
		||||
	def stage3_execute_button_clicked(self, widget):
 | 
			
		||||
		self.stage3_submit_form('execute')
 | 
			
		||||
 | 
			
		||||
	def stage3_submit_form(self, action='execute'):
 | 
			
		||||
		self.data_form_widget.set_sensitive(False)
 | 
			
		||||
		if self.data_form_widget.get_data_form() is None:
 | 
			
		||||
			self.data_form_widget.hide()
 | 
			
		||||
 | 
			
		||||
		self.cancel_button.set_sensitive(True)
 | 
			
		||||
		self.back_button.set_sensitive(False)
 | 
			
		||||
		self.forward_button.set_sensitive(False)
 | 
			
		||||
		self.execute_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		self.sending_form_progressbar.show()
 | 
			
		||||
		self.setup_pulsing(self.sending_form_progressbar)
 | 
			
		||||
		self.send_command(action)
 | 
			
		||||
 | 
			
		||||
	def stage3_next_form(self, command):
 | 
			
		||||
		assert isinstance(command, xmpp.Node)
 | 
			
		||||
 | 
			
		||||
		self.remove_pulsing()
 | 
			
		||||
		self.sending_form_progressbar.hide()
 | 
			
		||||
 | 
			
		||||
		if self.sessionid is None:
 | 
			
		||||
			self.sessionid = command.getAttr('sessionid')
 | 
			
		||||
 | 
			
		||||
		self.form_status = command.getAttr('status')
 | 
			
		||||
 | 
			
		||||
		if command.getTag('x') is not None:
 | 
			
		||||
			self.dataform = dataforms.ExtendForm(node=command.getTag('x'))
 | 
			
		||||
 | 
			
		||||
			self.data_form_widget.set_sensitive(True)
 | 
			
		||||
			try:
 | 
			
		||||
				self.data_form_widget.data_form=self.dataform
 | 
			
		||||
			except dataforms.Error:
 | 
			
		||||
				# TODO: translate
 | 
			
		||||
				self.stage5(error='Service sent malformed data', senderror=True)
 | 
			
		||||
			self.data_form_widget.show()
 | 
			
		||||
		else:
 | 
			
		||||
			self.data_form_widget.hide()
 | 
			
		||||
 | 
			
		||||
		action = command.getTag('action')
 | 
			
		||||
		if action is None:
 | 
			
		||||
			self.cancel_button.set_sensitive(True)
 | 
			
		||||
			self.back_button.set_sensitive(False)
 | 
			
		||||
			self.forward_button.set_sensitive(False)
 | 
			
		||||
			self.execute_button.set_sensitive(True)
 | 
			
		||||
		else:
 | 
			
		||||
			# actions, actions, actions...
 | 
			
		||||
			self.cancel_button.set_sensitive(True)
 | 
			
		||||
			self.back_button.set_sensitive(action.getTag('prev') is not None)
 | 
			
		||||
			self.forward_button.set_sensitive(action.getTag('next') is not None)
 | 
			
		||||
			self.execute_button.set_sensitive(True)
 | 
			
		||||
 | 
			
		||||
		if self.form_status == 'completed':
 | 
			
		||||
			self.cancel_button.set_sensitive(False)
 | 
			
		||||
			self.back_button.set_sensitive(False)
 | 
			
		||||
			self.forward_button.set_sensitive(False)
 | 
			
		||||
			self.execute_button.set_no_show_all(True)
 | 
			
		||||
			self.execute_button.hide()
 | 
			
		||||
			self.close_button.set_no_show_all(False)
 | 
			
		||||
			self.close_button.show()
 | 
			
		||||
			self.stage_adhoc_commands_window_delete_event = self.stage3_close_button_clicked
 | 
			
		||||
 | 
			
		||||
		note = command.getTag('note')
 | 
			
		||||
		if note is not None:
 | 
			
		||||
			self.notes_label.set_text(note.getData().decode('utf-8'))
 | 
			
		||||
			self.notes_label.set_no_show_all(False)
 | 
			
		||||
			self.notes_label.show()
 | 
			
		||||
		else:
 | 
			
		||||
			self.notes_label.set_no_show_all(True)
 | 
			
		||||
			self.notes_label.hide()
 | 
			
		||||
 | 
			
		||||
# stage 4: no commands are exposed
 | 
			
		||||
	def stage4(self):
 | 
			
		||||
		'''Display the message. Wait for user to close the window'''
 | 
			
		||||
		# close old stage
 | 
			
		||||
		self.stage_finish()
 | 
			
		||||
 | 
			
		||||
		self.stages_notebook.set_current_page(
 | 
			
		||||
			self.stages_notebook.page_num(
 | 
			
		||||
				self.no_commands_stage_vbox))
 | 
			
		||||
 | 
			
		||||
		self.cancel_button.set_sensitive(True)
 | 
			
		||||
		self.back_button.set_sensitive(False)
 | 
			
		||||
		self.forward_button.set_sensitive(False)
 | 
			
		||||
		self.execute_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		self.stage_finish = self.do_nothing
 | 
			
		||||
		self.stage_cancel_button_clicked = self.stage4_cancel_button_clicked
 | 
			
		||||
		self.stage_adhoc_commands_window_delete_event = self.do_nothing
 | 
			
		||||
 | 
			
		||||
	def stage4_cancel_button_clicked(self, widget):
 | 
			
		||||
		self.window.destroy()
 | 
			
		||||
 | 
			
		||||
	def on_check_commands_2_button_clicked(self, widget):
 | 
			
		||||
		self.stage1()
 | 
			
		||||
 | 
			
		||||
# stage 5: an error has occured
 | 
			
		||||
	def stage5(self, error=None, errorid=None, senderror=False):
 | 
			
		||||
		'''Display the error message. Wait for user to close the window'''
 | 
			
		||||
		# TODO: sending error to responder
 | 
			
		||||
		# close old stage
 | 
			
		||||
		self.stage_finish()
 | 
			
		||||
 | 
			
		||||
		assert errorid is not None or error is not None
 | 
			
		||||
 | 
			
		||||
		if errorid is not None:
 | 
			
		||||
			# we've got error code, display appropriate message
 | 
			
		||||
			errorname = xmpp.NS_STANZAS + ' ' + str(errorid)
 | 
			
		||||
			errordesc = xmpp.ERRORS[errorname][2]
 | 
			
		||||
			error = errordesc.decode('utf-8')
 | 
			
		||||
			del errorname, errordesc
 | 
			
		||||
		elif error is not None:
 | 
			
		||||
			# we've got error message
 | 
			
		||||
			pass
 | 
			
		||||
		else:
 | 
			
		||||
			# we don't know what's that, bailing out
 | 
			
		||||
			assert False
 | 
			
		||||
 | 
			
		||||
		self.stages_notebook.set_current_page(
 | 
			
		||||
			self.stages_notebook.page_num(
 | 
			
		||||
				self.error_stage_vbox))
 | 
			
		||||
 | 
			
		||||
		self.cancel_button.set_sensitive(True)
 | 
			
		||||
		self.back_button.set_sensitive(False)
 | 
			
		||||
		self.forward_button.set_sensitive(False)
 | 
			
		||||
		self.execute_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		self.error_description_label.set_text(error)
 | 
			
		||||
 | 
			
		||||
		self.stage_finish = self.do_nothing
 | 
			
		||||
		self.stage_cancel_button_clicked = self.stage5_cancel_button_clicked
 | 
			
		||||
		self.stage_adhoc_commands_window_delete_event = self.do_nothing
 | 
			
		||||
 | 
			
		||||
	def stage5_cancel_button_clicked(self, widget):
 | 
			
		||||
		self.window.destroy()
 | 
			
		||||
 | 
			
		||||
# helpers to handle pulsing in progressbar
 | 
			
		||||
	def setup_pulsing(self, progressbar):
 | 
			
		||||
		'''Set the progressbar to pulse. Makes a custom
 | 
			
		||||
		function to repeatedly call progressbar.pulse() method.'''
 | 
			
		||||
		assert self.pulse_id is None
 | 
			
		||||
		assert isinstance(progressbar, gtk.ProgressBar)
 | 
			
		||||
 | 
			
		||||
		def callback():
 | 
			
		||||
			progressbar.pulse()
 | 
			
		||||
			return True	# important to keep callback be called back!
 | 
			
		||||
 | 
			
		||||
		# 12 times per second (80 miliseconds)
 | 
			
		||||
		self.pulse_id = gobject.timeout_add(80, callback)
 | 
			
		||||
 | 
			
		||||
	def remove_pulsing(self):
 | 
			
		||||
		'''Stop pulsing, useful when especially when removing widget.'''
 | 
			
		||||
		if self.pulse_id is not None:
 | 
			
		||||
			gobject.source_remove(self.pulse_id)
 | 
			
		||||
		self.pulse_id=None
 | 
			
		||||
 | 
			
		||||
# handling xml stanzas
 | 
			
		||||
	def request_command_list(self):
 | 
			
		||||
		'''Request the command list. Change stage on delivery.'''
 | 
			
		||||
		query = xmpp.Iq(typ='get', to=xmpp.JID(self.jid), xmlns=xmpp.NS_DISCO_ITEMS)
 | 
			
		||||
		query.setQuerynode(xmpp.NS_COMMANDS)
 | 
			
		||||
 | 
			
		||||
		def callback(response):
 | 
			
		||||
			'''Called on response to query.'''
 | 
			
		||||
			# TODO: move to connection_handlers.py
 | 
			
		||||
			# is error => error stage
 | 
			
		||||
			error = response.getError()
 | 
			
		||||
			if error is not None:
 | 
			
		||||
				# extracting error description from xmpp/protocol.py
 | 
			
		||||
				self.stage5(errorid = error)
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
			# no commands => no commands stage
 | 
			
		||||
			# commands => command selection stage
 | 
			
		||||
			items = response.getTag('query').getTags('item')
 | 
			
		||||
			if len(items)==0:
 | 
			
		||||
				self.commandlist = []
 | 
			
		||||
				self.stage4()
 | 
			
		||||
			else:
 | 
			
		||||
				self.commandlist = [(t.getAttr('node'), t.getAttr('name')) for t in items]
 | 
			
		||||
				self.stage2()
 | 
			
		||||
 | 
			
		||||
		self.account.connection.SendAndCallForResponse(query, callback)
 | 
			
		||||
 | 
			
		||||
	def send_command(self, action='execute'):
 | 
			
		||||
		'''Send the command with data form. Wait for reply.'''
 | 
			
		||||
		# create the stanza
 | 
			
		||||
		assert isinstance(self.commandnode, unicode)
 | 
			
		||||
		assert action in ('execute', 'prev', 'next', 'complete')
 | 
			
		||||
 | 
			
		||||
		stanza = xmpp.Iq(typ='set', to=self.jid)
 | 
			
		||||
		cmdnode = stanza.addChild('command', attrs={
 | 
			
		||||
				'xmlns':xmpp.NS_COMMANDS,
 | 
			
		||||
				'node':self.commandnode,
 | 
			
		||||
				'action':action
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
		if self.sessionid is not None:
 | 
			
		||||
			cmdnode.setAttr('sessionid', self.sessionid)
 | 
			
		||||
 | 
			
		||||
		if self.data_form_widget.data_form is not None:
 | 
			
		||||
#			cmdnode.addChild(node=dataforms.DataForm(tofill=self.data_form_widget.data_form))
 | 
			
		||||
			# TODO: simplified form to send
 | 
			
		||||
			cmdnode.addChild(node=self.data_form_widget.data_form)
 | 
			
		||||
 | 
			
		||||
		def callback(response):
 | 
			
		||||
			# TODO: move to connection_handlers.py
 | 
			
		||||
			err = response.getError()
 | 
			
		||||
			if err is not None:
 | 
			
		||||
				self.stage5(errorid = err)
 | 
			
		||||
			else:
 | 
			
		||||
				self.stage3_next_form(response.getTag('command'))
 | 
			
		||||
 | 
			
		||||
		self.account.connection.SendAndCallForResponse(stanza, callback)
 | 
			
		||||
 | 
			
		||||
	def send_cancel(self):
 | 
			
		||||
		'''Send the command with action='cancel'. '''
 | 
			
		||||
		assert self.commandnode is not None
 | 
			
		||||
		if self.sessionid is not None:
 | 
			
		||||
			# we already have sessionid, so the service sent at least one reply.
 | 
			
		||||
			stanza = xmpp.Iq(typ='set', to=self.jid)
 | 
			
		||||
			stanza.addChild('command', attrs={
 | 
			
		||||
					'xmlns':xmpp.NS_COMMANDS,
 | 
			
		||||
					'node':self.commandnode,
 | 
			
		||||
					'sessionid':self.sessionid,
 | 
			
		||||
					'action':'cancel'
 | 
			
		||||
				})
 | 
			
		||||
	
 | 
			
		||||
			self.account.connection.send(stanza)
 | 
			
		||||
		else:
 | 
			
		||||
			# we did not received any reply from service; TODO: we should wait and
 | 
			
		||||
			# then send cancel; for now we do nothing
 | 
			
		||||
			pass
 | 
			
		||||
							
								
								
									
										113
									
								
								src/atom_window.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/atom_window.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
'''atom_window.py - a window to display atom entries from pubsub. For now greatly simplified,
 | 
			
		||||
supports only simple feeds like the one from pubsub.com. '''
 | 
			
		||||
 | 
			
		||||
import gtk
 | 
			
		||||
import gtk.gdk
 | 
			
		||||
 | 
			
		||||
import gtkgui_helpers
 | 
			
		||||
from common import helpers
 | 
			
		||||
 | 
			
		||||
class AtomWindow:
 | 
			
		||||
	window = None
 | 
			
		||||
	entries = []
 | 
			
		||||
 | 
			
		||||
	@classmethod				# python2.4 decorator
 | 
			
		||||
	def newAtomEntry(cls, entry):
 | 
			
		||||
		''' Queue new entry, open window if there's no one opened. '''
 | 
			
		||||
		cls.entries.append(entry)
 | 
			
		||||
 | 
			
		||||
		if cls.window is None:
 | 
			
		||||
			cls.window = AtomWindow()
 | 
			
		||||
		else:
 | 
			
		||||
			cls.window.updateCounter()
 | 
			
		||||
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		''' Create new window... only if we have anything to show. '''
 | 
			
		||||
		assert len(self.__class__.entries)>0
 | 
			
		||||
 | 
			
		||||
		self.entry = None	# the entry actually displayed
 | 
			
		||||
 | 
			
		||||
		self.xml = gtkgui_helpers.get_glade('atom_entry_window.glade')
 | 
			
		||||
		self.window = self.xml.get_widget('atom_entry_window')
 | 
			
		||||
		for name in ('new_entry_label', 'feed_title_label', 'feed_title_eventbox',
 | 
			
		||||
			'feed_tagline_label', 'entry_title_label', 'entry_title_eventbox',
 | 
			
		||||
			'last_modified_label', 'close_button', 'next_button'):
 | 
			
		||||
			self.__dict__[name] = self.xml.get_widget(name)
 | 
			
		||||
 | 
			
		||||
		self.displayNextEntry()
 | 
			
		||||
 | 
			
		||||
		self.xml.signal_autoconnect(self)
 | 
			
		||||
		self.window.show_all()
 | 
			
		||||
 | 
			
		||||
		self.entry_title_eventbox.add_events(gtk.gdk.BUTTON_PRESS_MASK)
 | 
			
		||||
		self.feed_title_eventbox.add_events(gtk.gdk.BUTTON_PRESS_MASK)
 | 
			
		||||
 | 
			
		||||
	def displayNextEntry(self):
 | 
			
		||||
		''' Get next entry from the queue and display it in the window. '''
 | 
			
		||||
		assert len(self.__class__.entries)>0
 | 
			
		||||
 | 
			
		||||
		newentry = self.__class__.entries.pop(0)
 | 
			
		||||
		
 | 
			
		||||
		# fill the fields
 | 
			
		||||
		if newentry.feed_link is not None:
 | 
			
		||||
			self.feed_title_label.set_markup(
 | 
			
		||||
				u'<span foreground="blue" underline="single">%s</span>' % \
 | 
			
		||||
				gtkgui_helpers.escape_for_pango_markup(newentry.feed_title))
 | 
			
		||||
		else:
 | 
			
		||||
			self.feed_title_label.set_markup(
 | 
			
		||||
				gtkgui_helpers.escape_for_pango_markup(newentry.feed_title))
 | 
			
		||||
 | 
			
		||||
		self.feed_tagline_label.set_markup(
 | 
			
		||||
			u'<small>%s</small>' % \
 | 
			
		||||
			gtkgui_helpers.escape_for_pango_markup(newentry.feed_tagline))
 | 
			
		||||
 | 
			
		||||
		if newentry.uri is not None:
 | 
			
		||||
			self.entry_title_label.set_markup(
 | 
			
		||||
				u'<span foreground="blue" underline="single">%s</span>' % \
 | 
			
		||||
				gtkgui_helpers.escape_for_pango_markup(newentry.title))
 | 
			
		||||
		else:
 | 
			
		||||
			self.entry_title_label.set_markup(
 | 
			
		||||
				gtkgui_helpers.escape_for_pango_markup(newentry.title))
 | 
			
		||||
 | 
			
		||||
		self.last_modified_label.set_text(newentry.updated)
 | 
			
		||||
 | 
			
		||||
		# update the counters
 | 
			
		||||
		self.updateCounter()
 | 
			
		||||
 | 
			
		||||
		self.entry = newentry
 | 
			
		||||
 | 
			
		||||
	def updateCounter(self):
 | 
			
		||||
		''' We display number of events on the top of window, sometimes it needs to be
 | 
			
		||||
		changed...'''
 | 
			
		||||
		count = len(self.__class__.entries)
 | 
			
		||||
		# TODO: translate
 | 
			
		||||
		if count>0:
 | 
			
		||||
			self.new_entry_label.set_text( \
 | 
			
		||||
				'You have received new entries (and %(count)d not displayed):' % \
 | 
			
		||||
				{'count': count})
 | 
			
		||||
			self.next_button.set_sensitive(True)
 | 
			
		||||
		else:
 | 
			
		||||
			self.new_entry_label.set_text('You have received new entry:')
 | 
			
		||||
			self.next_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
	def on_close_button_clicked(self, widget):
 | 
			
		||||
		self.window.destroy()
 | 
			
		||||
 | 
			
		||||
	def on_next_button_clicked(self, widget):
 | 
			
		||||
		self.displayNextEntry()
 | 
			
		||||
 | 
			
		||||
	def on_entry_title_button_press_event(self, widget, event):
 | 
			
		||||
		# TODO: make it using special gtk2.10 widget
 | 
			
		||||
		if event.button == 1:	# left click
 | 
			
		||||
			uri = self.entry.uri
 | 
			
		||||
			if uri is not None:
 | 
			
		||||
				helpers.launch_browser_mailer('url', uri)
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
	def on_feed_title_button_press_event(self, widget, event):
 | 
			
		||||
		# TODO: make it using special gtk2.10 widget
 | 
			
		||||
		if event.button == 1:	# left click
 | 
			
		||||
			uri = self.entry.feed_uri
 | 
			
		||||
			if uri is not None:
 | 
			
		||||
				helpers.launch_browser_mailer('url', uri)
 | 
			
		||||
		return True
 | 
			
		||||
							
								
								
									
										138
									
								
								src/common/atom.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/common/atom.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,138 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
''' Atom (rfc 4287) feed parser, used to read data from atom-over-pubsub transports
 | 
			
		||||
and services. Very simple. Actually implements only atom:entry. Implement more features
 | 
			
		||||
if you need. '''
 | 
			
		||||
 | 
			
		||||
# suggestion: rewrite functions that return dates to return standard python time tuples,
 | 
			
		||||
# exteneded to contain timezone
 | 
			
		||||
 | 
			
		||||
import xmpp
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
class PersonConstruct(xmpp.Node, object):
 | 
			
		||||
	''' Not used for now, as we don't need authors/contributors in pubsub.com feeds.
 | 
			
		||||
	They rarely exist there. '''
 | 
			
		||||
	def __init__(self, node):
 | 
			
		||||
		''' Create person construct from node. '''
 | 
			
		||||
		xmpp.Node.__init__(self, node=node)
 | 
			
		||||
 | 
			
		||||
	def get_name(self):
 | 
			
		||||
		return self.getTagData('name')
 | 
			
		||||
 | 
			
		||||
	name = property(get_name, None, None,
 | 
			
		||||
		'''Conveys a human-readable name for the person. Should not be None,
 | 
			
		||||
		although some badly generated atom feeds don't put anything here
 | 
			
		||||
		(this is non-standard behavior, still pubsub.com sometimes does that.)''')
 | 
			
		||||
 | 
			
		||||
	def get_uri(self):
 | 
			
		||||
		return self.getTagData('uri')
 | 
			
		||||
 | 
			
		||||
	uri = property(get_uri, None, None,
 | 
			
		||||
		'''Conveys an IRI associated with the person. Might be None when not set.''')
 | 
			
		||||
 | 
			
		||||
	def get_email(self):
 | 
			
		||||
		return self.getTagData('email')
 | 
			
		||||
 | 
			
		||||
	email = property(get_email, None, None,
 | 
			
		||||
		'''Conveys an e-mail address associated with the person. Might be None when
 | 
			
		||||
		not set.''')
 | 
			
		||||
 | 
			
		||||
class Entry(xmpp.Node, object):
 | 
			
		||||
	def __init__(self, node=None):
 | 
			
		||||
		''' Create new atom entry object. '''
 | 
			
		||||
		xmpp.Node.__init__(self, 'entry', node=node)
 | 
			
		||||
 | 
			
		||||
	def __repr__(self):
 | 
			
		||||
		return '<Atom:Entry object of id="%r">' % self.id
 | 
			
		||||
 | 
			
		||||
class OldEntry(xmpp.Node, object):
 | 
			
		||||
	''' Parser for feeds from pubsub.com. They use old Atom 0.3 format with
 | 
			
		||||
	their extensions. '''
 | 
			
		||||
	def __init__(self, node=None):
 | 
			
		||||
		''' Create new Atom 0.3 entry object. '''
 | 
			
		||||
		xmpp.Node.__init__(self, 'entry', node=node)
 | 
			
		||||
 | 
			
		||||
	def __repr__(self):
 | 
			
		||||
		return '<Atom0.3:Entry object of id="%r">' % self.id
 | 
			
		||||
 | 
			
		||||
	def get_feed_title(self):
 | 
			
		||||
		''' Returns title of feed, where the entry was created. The result is the feed name
 | 
			
		||||
		concatenated with source-feed title. '''
 | 
			
		||||
		title = u''
 | 
			
		||||
 | 
			
		||||
		if self.parent is not None:
 | 
			
		||||
			main_feed = self.parent.getTagData('title')
 | 
			
		||||
		else:
 | 
			
		||||
			main_feed = None
 | 
			
		||||
 | 
			
		||||
		if self.getTag('source-feed') is not None:
 | 
			
		||||
			source_feed = self.getTag('source-feed').getTagData('title')
 | 
			
		||||
		else:
 | 
			
		||||
			source_feed = None
 | 
			
		||||
 | 
			
		||||
		if main_feed is not None and source_feed is not None:
 | 
			
		||||
			return u'%s: %s' % (main_feed, source_feed)
 | 
			
		||||
		elif main_feed is not None:
 | 
			
		||||
			return main_feed
 | 
			
		||||
		elif source_feed is not None:
 | 
			
		||||
			return source_feed
 | 
			
		||||
		else:
 | 
			
		||||
			return u''
 | 
			
		||||
 | 
			
		||||
	feed_title = property(get_feed_title, None, None, 
 | 
			
		||||
		''' Title of feed. It is built from entry's original feed title and title of feed
 | 
			
		||||
		which delivered this entry. ''')
 | 
			
		||||
 | 
			
		||||
	def get_feed_link(self):
 | 
			
		||||
		''' Get a link to main page of feed (in pubsub.com: second link of rel='alternate',
 | 
			
		||||
		first contains raw xml data). '''
 | 
			
		||||
		try:
 | 
			
		||||
			return self.getTag('source-feed').getTags('link', {'rel':'alternate'})[1].getData()
 | 
			
		||||
		except:
 | 
			
		||||
			return None
 | 
			
		||||
 | 
			
		||||
	feed_link = property(get_feed_link, None, None,
 | 
			
		||||
		''' Link to main webpage of the feed. ''')
 | 
			
		||||
 | 
			
		||||
	def get_title(self):
 | 
			
		||||
		''' Get an entry's title. '''
 | 
			
		||||
		return self.getTagData('title')
 | 
			
		||||
 | 
			
		||||
	title = property(get_title, None, None,
 | 
			
		||||
		''' Entry's title. ''')
 | 
			
		||||
 | 
			
		||||
	def get_uri(self):
 | 
			
		||||
		''' Get the uri the entry points to (entry's first link element with rel='alternate'
 | 
			
		||||
		or without rel attribute). '''
 | 
			
		||||
		for element in self.getTags('link'):
 | 
			
		||||
			if element.attrs.has_key('rel') and element.attrs['rel']<>'alternate': continue
 | 
			
		||||
			try:
 | 
			
		||||
				return element.attrs['href']
 | 
			
		||||
			except AttributeError:
 | 
			
		||||
				pass
 | 
			
		||||
		return None
 | 
			
		||||
 | 
			
		||||
	uri = property(get_uri, None, None,
 | 
			
		||||
		''' URI that is pointed by the entry. ''')
 | 
			
		||||
 | 
			
		||||
	def get_updated(self):
 | 
			
		||||
		''' Get the time the entry was updated last time. This should be standarized,
 | 
			
		||||
		but pubsub.com sends it in human-readable format. We won't try to parse it.
 | 
			
		||||
		(Atom 0.3 uses the word	«modified» for that).
 | 
			
		||||
 | 
			
		||||
		If there's no time given in the entry, we try with <published>
 | 
			
		||||
		and <issued> elements. '''
 | 
			
		||||
		for name in ('updated', 'modified', 'published', 'issued'):
 | 
			
		||||
			date = self.getTagData(name)
 | 
			
		||||
			if date is not None: break
 | 
			
		||||
 | 
			
		||||
		if date is None:
 | 
			
		||||
			# it is not in the standard format
 | 
			
		||||
			return time.asctime()
 | 
			
		||||
 | 
			
		||||
		return date
 | 
			
		||||
 | 
			
		||||
	updated = property(get_updated, None, None, 
 | 
			
		||||
		''' Last significant modification time. ''')
 | 
			
		||||
 | 
			
		||||
	feed_tagline = u''
 | 
			
		||||
							
								
								
									
										252
									
								
								src/common/commands.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								src/common/commands.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,252 @@
 | 
			
		|||
##
 | 
			
		||||
## Copyright (C) 2006 Gajim Team
 | 
			
		||||
##
 | 
			
		||||
## 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; version 2 only.
 | 
			
		||||
##
 | 
			
		||||
## 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.
 | 
			
		||||
##
 | 
			
		||||
 | 
			
		||||
import xmpp
 | 
			
		||||
import helpers
 | 
			
		||||
import dataforms
 | 
			
		||||
import gajim
 | 
			
		||||
 | 
			
		||||
class AdHocCommand:
 | 
			
		||||
	commandnode = 'command'
 | 
			
		||||
	commandname = 'The Command'
 | 
			
		||||
	commandfeatures = (xmpp.NS_DATA,)
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def isVisibleFor(samejid):
 | 
			
		||||
		''' This returns True if that command should be visible and invokable
 | 
			
		||||
		for others.
 | 
			
		||||
		samejid - True when command is invoked by an entity with the same bare jid.
 | 
			
		||||
		'''
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
	def __init__(self, conn, jid, sessionid):
 | 
			
		||||
		self.connection = conn
 | 
			
		||||
		self.jid = jid
 | 
			
		||||
		self.sessionid = sessionid
 | 
			
		||||
 | 
			
		||||
	def buildResponse(self, request, status='executing', defaultaction=None, actions=None):
 | 
			
		||||
		assert status in ('executing', 'completed', 'canceled')
 | 
			
		||||
 | 
			
		||||
		response = request.buildReply('result')
 | 
			
		||||
		cmd = response.addChild('command', {
 | 
			
		||||
			'xmlns': xmpp.NS_COMMANDS,
 | 
			
		||||
			'sessionid': self.sessionid,
 | 
			
		||||
			'node': self.commandnode,
 | 
			
		||||
			'status': status})
 | 
			
		||||
		if defaultaction is not None or actions is not None:
 | 
			
		||||
			if defaultaction is not None:
 | 
			
		||||
				assert defaultaction in ('cancel', 'execute', 'prev', 'next', 'complete')
 | 
			
		||||
				attrs = {'action': defaultaction}
 | 
			
		||||
			else:
 | 
			
		||||
				attrs = {}
 | 
			
		||||
 | 
			
		||||
			cmd.addChild('actions', attrs, actions)
 | 
			
		||||
		return response, cmd
 | 
			
		||||
 | 
			
		||||
	def badRequest(self, stanza):
 | 
			
		||||
		self.connection.connection.send(xmpp.Error(stanza, xmpp.NS_STANZAS+' bad-request'))
 | 
			
		||||
 | 
			
		||||
	def cancel(self, request):
 | 
			
		||||
		response, cmd = self.buildResponse(request, status='canceled')
 | 
			
		||||
		self.connection.connection.send(response)
 | 
			
		||||
		return False	# finish the session
 | 
			
		||||
 | 
			
		||||
class ChangeStatusCommand(AdHocCommand):
 | 
			
		||||
	commandnode = 'change-status'
 | 
			
		||||
	commandname = 'Change status information'
 | 
			
		||||
 | 
			
		||||
	@staticmethod
 | 
			
		||||
	def isVisibleFor(samejid):
 | 
			
		||||
		''' Change status is visible only if the entity has the same bare jid. '''
 | 
			
		||||
		return True	# TODO: Remove that!
 | 
			
		||||
		return samejid
 | 
			
		||||
 | 
			
		||||
	def execute(self, request):
 | 
			
		||||
		# first query...
 | 
			
		||||
		response, cmd = self.buildResponse(request, defaultaction='execute', actions=['execute'])
 | 
			
		||||
		
 | 
			
		||||
		cmd.addChild(node=dataforms.SimpleDataForm(
 | 
			
		||||
			title='Change status',
 | 
			
		||||
			instructions='Set the presence type and description',
 | 
			
		||||
			fields=[
 | 
			
		||||
				dataforms.Field('list-single',
 | 
			
		||||
					var='presence-type',
 | 
			
		||||
					label='Type of presence:',
 | 
			
		||||
					options=[
 | 
			
		||||
						(u'free-for-chat', u'Free for chat'),
 | 
			
		||||
						(u'online', u'Online'),
 | 
			
		||||
						(u'away', u'Away'),
 | 
			
		||||
						(u'xa', u'Extended away'),
 | 
			
		||||
						(u'dnd', u'Do not disturb'),
 | 
			
		||||
						(u'offline', u'Offline - disconnect')],
 | 
			
		||||
					value='online',
 | 
			
		||||
					required=True),
 | 
			
		||||
				dataforms.Field('text-multi',
 | 
			
		||||
					var='presence-desc',
 | 
			
		||||
					label='Presence description:')]))
 | 
			
		||||
 | 
			
		||||
		self.connection.connection.send(response)
 | 
			
		||||
 | 
			
		||||
		# for next invocation
 | 
			
		||||
		self.execute = self.changestatus
 | 
			
		||||
		
 | 
			
		||||
		return True	# keep the session
 | 
			
		||||
 | 
			
		||||
	def changestatus(self, request):
 | 
			
		||||
		# check if the data is correct
 | 
			
		||||
		try:
 | 
			
		||||
			form=dataforms.SimpleDataForm(extend=request.getTag('command').getTag('x'))
 | 
			
		||||
		except:
 | 
			
		||||
			self.badRequest(request)
 | 
			
		||||
			return False
 | 
			
		||||
		
 | 
			
		||||
		try:
 | 
			
		||||
			presencetype = form['presence-type'].value
 | 
			
		||||
			if not presencetype in \
 | 
			
		||||
			    ('free-for-chat', 'online', 'away', 'xa', 'dnd', 'offline'):
 | 
			
		||||
				self.badRequest(request)
 | 
			
		||||
				return False
 | 
			
		||||
		except:	# KeyError if there's no presence-type field in form or
 | 
			
		||||
			# AttributeError if that field is of wrong type
 | 
			
		||||
			self.badRequest(request)
 | 
			
		||||
			return False
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			presencedesc = form['presence-desc'].value
 | 
			
		||||
		except:	# same exceptions as in last comment
 | 
			
		||||
			presencedesc = u''
 | 
			
		||||
 | 
			
		||||
		response, cmd = self.buildResponse(request, status='completed')
 | 
			
		||||
		cmd.addChild('note', {}, 'The status has been changed.')
 | 
			
		||||
 | 
			
		||||
		self.connection.connection.send(response)
 | 
			
		||||
 | 
			
		||||
		# send new status
 | 
			
		||||
		gajim.interface.roster.send_status(self.connection.name, presencetype, presencedesc)
 | 
			
		||||
 | 
			
		||||
		return False	# finish the session
 | 
			
		||||
 | 
			
		||||
class ConnectionCommands:
 | 
			
		||||
	''' This class depends on that it is a part of Connection() class. '''
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		# a list of all commands exposed: node -> command class
 | 
			
		||||
		self.__commands = {}
 | 
			
		||||
		for cmdobj in (ChangeStatusCommand,):
 | 
			
		||||
			self.__commands[cmdobj.commandnode] = cmdobj
 | 
			
		||||
 | 
			
		||||
		# a list of sessions; keys are tuples (jid, sessionid, node)
 | 
			
		||||
		self.__sessions = {}
 | 
			
		||||
 | 
			
		||||
	def getOurBareJID(self):
 | 
			
		||||
		return gajim.get_jid_from_account(self.name)
 | 
			
		||||
 | 
			
		||||
	def isSameJID(self, jid):
 | 
			
		||||
		''' Tests if the bare jid given is the same as our bare jid. '''
 | 
			
		||||
		return xmpp.JID(jid).getStripped() == self.getOurBareJID()
 | 
			
		||||
 | 
			
		||||
	def commandListQuery(self, con, iq_obj):
 | 
			
		||||
		iq = iq_obj.buildReply('result')
 | 
			
		||||
		jid = helpers.get_full_jid_from_iq(iq_obj)
 | 
			
		||||
		q = iq.getTag('query')
 | 
			
		||||
 | 
			
		||||
		for node, cmd in self.__commands.iteritems():
 | 
			
		||||
			if cmd.isVisibleFor(self.isSameJID(jid)):
 | 
			
		||||
				q.addChild('item', {
 | 
			
		||||
					# TODO: find the jid
 | 
			
		||||
					'jid': self.getOurBareJID()+u'/'+self.server_resource,
 | 
			
		||||
					'node': node,
 | 
			
		||||
					'name': cmd.commandname})
 | 
			
		||||
 | 
			
		||||
		self.connection.send(iq)
 | 
			
		||||
 | 
			
		||||
	def commandQuery(self, con, iq_obj):
 | 
			
		||||
		''' Send disco result for query for command (JEP-0050, example 6.).
 | 
			
		||||
		Return True if the result was sent, False if not. '''
 | 
			
		||||
		jid = helpers.get_full_jid_from_iq(iq_obj)
 | 
			
		||||
		node = iq_obj.getTagAttr('query', 'node')
 | 
			
		||||
 | 
			
		||||
		if node not in self.__commands: return False
 | 
			
		||||
 | 
			
		||||
		cmd = self.__commands[node]
 | 
			
		||||
		if cmd.isVisibleFor(self.isSameJID(jid)):
 | 
			
		||||
			iq = iq_obj.buildReply('result')
 | 
			
		||||
			q = iq.getTag('query')
 | 
			
		||||
			q.addChild('identity', attrs = {'type': 'command-node',
 | 
			
		||||
			                                'category': 'automation',
 | 
			
		||||
			                                'name': cmd.commandname})
 | 
			
		||||
			q.addChild('feature', attrs = {'var': xmpp.NS_COMMANDS})
 | 
			
		||||
			for feature in cmd.commandfeatures:
 | 
			
		||||
				q.addChild('feature', attrs = {'var': feature})
 | 
			
		||||
 | 
			
		||||
			self.connection.send(iq)
 | 
			
		||||
			return True
 | 
			
		||||
 | 
			
		||||
		return False
 | 
			
		||||
 | 
			
		||||
	def _CommandExecuteCB(self, con, iq_obj):
 | 
			
		||||
		jid = helpers.get_full_jid_from_iq(iq_obj)
 | 
			
		||||
 | 
			
		||||
		cmd = iq_obj.getTag('command')
 | 
			
		||||
		if cmd is None: return
 | 
			
		||||
 | 
			
		||||
		node = cmd.getAttr('node')
 | 
			
		||||
		if node is None: return
 | 
			
		||||
 | 
			
		||||
		sessionid = cmd.getAttr('sessionid')
 | 
			
		||||
		if sessionid is None:
 | 
			
		||||
			# we start a new command session... only if we are visible for the jid
 | 
			
		||||
			newcmd = self.__commands[node]
 | 
			
		||||
			if not newcmd.isVisibleFor(self.isSameJID(jid)):
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
			# generate new sessionid
 | 
			
		||||
			sessionid = self.connection.getAnID()
 | 
			
		||||
 | 
			
		||||
			# create new instance and run it
 | 
			
		||||
			obj = newcmd(conn=self, jid=jid, sessionid=sessionid)
 | 
			
		||||
			rc = obj.execute(iq_obj)
 | 
			
		||||
			if rc:
 | 
			
		||||
				self.__sessions[(jid, sessionid, node)] = obj
 | 
			
		||||
			raise xmpp.NodeProcessed
 | 
			
		||||
		else:
 | 
			
		||||
			# the command is already running, check for it
 | 
			
		||||
			magictuple = (jid, sessionid, node)
 | 
			
		||||
			if magictuple not in self.__sessions:
 | 
			
		||||
				# we don't have this session... ha!
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
			action = cmd.getAttr('action')
 | 
			
		||||
			obj = self.__sessions[magictuple]
 | 
			
		||||
 | 
			
		||||
			try:
 | 
			
		||||
				if action == 'cancel':		rc = obj.cancel(iq_obj)
 | 
			
		||||
				elif action == 'prev':		rc = obj.prev(iq_obj)
 | 
			
		||||
				elif action == 'next':		rc = obj.next(iq_obj)
 | 
			
		||||
				elif action == 'execute' or action is None:
 | 
			
		||||
								rc = obj.execute(iq_obj)
 | 
			
		||||
				elif action == 'complete':	rc = obj.complete(iq_obj)
 | 
			
		||||
				else:
 | 
			
		||||
					# action is wrong. stop the session, send error
 | 
			
		||||
					raise AttributeError
 | 
			
		||||
			except AttributeError:
 | 
			
		||||
				# the command probably doesn't handle invoked action...
 | 
			
		||||
				# stop the session, return error
 | 
			
		||||
				del self.__sessions[magictuple]
 | 
			
		||||
				return
 | 
			
		||||
 | 
			
		||||
			# delete the session if rc is False
 | 
			
		||||
			if not rc:
 | 
			
		||||
				del self.__sessions[magictuple]
 | 
			
		||||
 | 
			
		||||
			raise xmpp.NodeProcessed
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +33,10 @@ import common.xmpp
 | 
			
		|||
from common import GnuPG
 | 
			
		||||
from common import helpers
 | 
			
		||||
from common import gajim
 | 
			
		||||
from common import dataforms
 | 
			
		||||
from common import atom
 | 
			
		||||
from common.commands import ConnectionCommands
 | 
			
		||||
from common.pubsub import ConnectionPubSub
 | 
			
		||||
 | 
			
		||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
 | 
			
		||||
	'invisible']
 | 
			
		||||
| 
						 | 
				
			
			@ -661,6 +665,10 @@ class ConnectionDisco:
 | 
			
		|||
		feature = common.xmpp.Node('feature')
 | 
			
		||||
		feature.setAttr('var', common.xmpp.NS_FILE)
 | 
			
		||||
		query.addChild(node=feature)
 | 
			
		||||
		# exposing adhoc commands
 | 
			
		||||
		feature = common.xmpp.Node('feature')
 | 
			
		||||
		feature.setAttr('var', common.xmpp.NS_COMMANDS)
 | 
			
		||||
		query.addChild(node=feature)
 | 
			
		||||
		
 | 
			
		||||
		self.connection.send(iq)
 | 
			
		||||
		raise common.xmpp.NodeProcessed
 | 
			
		||||
| 
						 | 
				
			
			@ -705,20 +713,34 @@ class ConnectionDisco:
 | 
			
		|||
		else:
 | 
			
		||||
			self.dispatch('AGENT_INFO_ITEMS', (jid, node, items))
 | 
			
		||||
 | 
			
		||||
	def _DiscoverItemsGetCB(self, con, iq_obj):
 | 
			
		||||
		gajim.log.debug('DiscoverItemsGetCB')
 | 
			
		||||
		node = iq_obj.getTagAttr('query', 'node')
 | 
			
		||||
		if node==common.xmpp.NS_COMMANDS:
 | 
			
		||||
			self.commandListQuery(con, iq_obj)
 | 
			
		||||
			raise common.xmpp.NodeProcessed
 | 
			
		||||
 | 
			
		||||
	def _DiscoverInfoGetCB(self, con, iq_obj):
 | 
			
		||||
		gajim.log.debug('DiscoverInfoGetCB')
 | 
			
		||||
		iq = iq_obj.buildReply('result')
 | 
			
		||||
		q = iq.getTag('query')
 | 
			
		||||
		q.addChild('identity', attrs = {'type': 'pc',
 | 
			
		||||
						'category': 'client',
 | 
			
		||||
						'name': 'Gajim'})
 | 
			
		||||
		q.addChild('feature', attrs = {'var': common.xmpp.NS_BYTESTREAM})
 | 
			
		||||
		q.addChild('feature', attrs = {'var': common.xmpp.NS_SI})
 | 
			
		||||
		q.addChild('feature', attrs = {'var': common.xmpp.NS_FILE})
 | 
			
		||||
		q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC})
 | 
			
		||||
		q.addChild('feature', attrs = {'var': common.xmpp.NS_XHTML_IM})
 | 
			
		||||
		self.connection.send(iq)
 | 
			
		||||
		raise common.xmpp.NodeProcessed
 | 
			
		||||
		q = iq_obj.getTag('query')
 | 
			
		||||
		node = q.getAttr('node')
 | 
			
		||||
 | 
			
		||||
		if self.commandQuery(con, iq_obj):
 | 
			
		||||
			raise NodeProcessed
 | 
			
		||||
		
 | 
			
		||||
		elif node is None:
 | 
			
		||||
			iq = iq_obj.buildReply('result')
 | 
			
		||||
			q = iq.getTag('query')
 | 
			
		||||
			q.addChild('identity', attrs = {'type': 'pc',
 | 
			
		||||
							'category': 'client',
 | 
			
		||||
							'name': 'Gajim'})
 | 
			
		||||
			q.addChild('feature', attrs = {'var': common.xmpp.NS_BYTESTREAM})
 | 
			
		||||
			q.addChild('feature', attrs = {'var': common.xmpp.NS_SI})
 | 
			
		||||
			q.addChild('feature', attrs = {'var': common.xmpp.NS_FILE})
 | 
			
		||||
			q.addChild('feature', attrs = {'var': common.xmpp.NS_MUC})
 | 
			
		||||
			q.addChild('feature', attrs = {'var': common.xmpp.NS_XHTML_IM})
 | 
			
		||||
			self.connection.send(iq)
 | 
			
		||||
			raise common.xmpp.NodeProcessed
 | 
			
		||||
 | 
			
		||||
	def _DiscoverInfoErrorCB(self, con, iq_obj):
 | 
			
		||||
		gajim.log.debug('DiscoverInfoErrorCB')
 | 
			
		||||
| 
						 | 
				
			
			@ -1103,11 +1125,12 @@ class ConnectionVcard:
 | 
			
		|||
			else:
 | 
			
		||||
				self.dispatch('VCARD', vcard)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco):
 | 
			
		||||
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub):
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		ConnectionVcard.__init__(self)
 | 
			
		||||
		ConnectionBytestream.__init__(self)
 | 
			
		||||
		ConnectionCommands.__init__(self)
 | 
			
		||||
		ConnectionPubSub.__init__(self)
 | 
			
		||||
		# List of IDs we are waiting answers for {id: (type_of_request, data), }
 | 
			
		||||
		self.awaiting_answers = {}
 | 
			
		||||
		# List of IDs that will produce a timeout is answer doesn't arrive
 | 
			
		||||
| 
						 | 
				
			
			@ -1333,6 +1356,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
 | 
			
		|||
 | 
			
		||||
	def _messageCB(self, con, msg):
 | 
			
		||||
		'''Called when we receive a message'''
 | 
			
		||||
		# check if the message is pubsub#event
 | 
			
		||||
		if msg.getTag('event') is not None:
 | 
			
		||||
			self._pubsubEventCB(con, msg)
 | 
			
		||||
			return
 | 
			
		||||
		msgtxt = msg.getBody()
 | 
			
		||||
		msghtml = msg.getXHTML()
 | 
			
		||||
		mtype = msg.getType()
 | 
			
		||||
| 
						 | 
				
			
			@ -1449,6 +1476,32 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
 | 
			
		|||
				subject, chatstate, msg_id, composing_jep, user_nick, msghtml))
 | 
			
		||||
	# END messageCB
 | 
			
		||||
 | 
			
		||||
	def _pubsubEventCB(self, con, msg):
 | 
			
		||||
		''' Called when we receive <message/> with pubsub event. '''
 | 
			
		||||
		# TODO: Logging? (actually services where logging would be useful, should
 | 
			
		||||
		# TODO: allow to access archives remotely...)
 | 
			
		||||
		event = msg.getTag('event')
 | 
			
		||||
 | 
			
		||||
		items = event.getTag('items')
 | 
			
		||||
		if items is None: return
 | 
			
		||||
 | 
			
		||||
		for item in items.getTags('item'):
 | 
			
		||||
			# check for event type (for now only one type supported: pubsub.com events)
 | 
			
		||||
			child = item.getTag('pubsub-message')
 | 
			
		||||
			if child is not None:
 | 
			
		||||
				# we have pubsub.com notification
 | 
			
		||||
				child = child.getTag('feed')
 | 
			
		||||
				if child is None: continue
 | 
			
		||||
 | 
			
		||||
				for entry in child.getTags('entry'):
 | 
			
		||||
					# for each entry in feed (there shouldn't be more than one,
 | 
			
		||||
					# but to be sure...
 | 
			
		||||
					self.dispatch('ATOM_ENTRY', (atom.OldEntry(node=entry),))
 | 
			
		||||
				continue
 | 
			
		||||
			# unknown type... probably user has another client who understands that event
 | 
			
		||||
		
 | 
			
		||||
		raise common.xmpp.NodeProcessed
 | 
			
		||||
 | 
			
		||||
	def _presenceCB(self, con, prs):
 | 
			
		||||
		'''Called when we receive a presence'''
 | 
			
		||||
		ptype = prs.getType()
 | 
			
		||||
| 
						 | 
				
			
			@ -1918,12 +1971,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
 | 
			
		|||
			common.xmpp.NS_PRIVATE)
 | 
			
		||||
		con.RegisterHandler('iq', self._HttpAuthCB, 'get',
 | 
			
		||||
			common.xmpp.NS_HTTP_AUTH)
 | 
			
		||||
		con.RegisterHandler('iq', self._CommandExecuteCB, 'set',
 | 
			
		||||
			common.xmpp.NS_COMMANDS)
 | 
			
		||||
		con.RegisterHandler('iq', self._gMailNewMailCB, 'set',
 | 
			
		||||
			common.xmpp.NS_GMAILNOTIFY)
 | 
			
		||||
		con.RegisterHandler('iq', self._gMailQueryCB, 'result',
 | 
			
		||||
			common.xmpp.NS_GMAILNOTIFY)
 | 
			
		||||
		con.RegisterHandler('iq', self._DiscoverInfoGetCB, 'get',
 | 
			
		||||
			common.xmpp.NS_DISCO_INFO)
 | 
			
		||||
		con.RegisterHandler('iq', self._DiscoverItemsGetCB, 'get',
 | 
			
		||||
			common.xmpp.NS_DISCO_ITEMS)
 | 
			
		||||
		con.RegisterHandler('iq', self._PubSubCB, 'result')
 | 
			
		||||
		con.RegisterHandler('iq', self._ErrorCB, 'error')
 | 
			
		||||
		con.RegisterHandler('iq', self._IqCB)
 | 
			
		||||
		con.RegisterHandler('iq', self._StanzaArrivedCB)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										424
									
								
								src/common/dataforms.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								src/common/dataforms.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,424 @@
 | 
			
		|||
# this will go to src/common/xmpp later, for now it is in src/common
 | 
			
		||||
""" This module contains wrappers for different parts of data forms (JEP 0004). For information
 | 
			
		||||
how to use them, read documentation. """
 | 
			
		||||
 | 
			
		||||
import xmpp
 | 
			
		||||
 | 
			
		||||
# exceptions used in this module
 | 
			
		||||
class Error(Exception): pass	# base class
 | 
			
		||||
class UnknownDataForm(Error): pass	# when we get xmpp.Node which we do not understand
 | 
			
		||||
class WrongFieldValue(Error): pass	# when we get xmpp.Node which contains bad fields
 | 
			
		||||
 | 
			
		||||
# helper class to change class of already existing object
 | 
			
		||||
class ExtendedNode(xmpp.Node, object):
 | 
			
		||||
	@classmethod
 | 
			
		||||
	def __new__(cls,  *a, **b):
 | 
			
		||||
		if 'extend' not in b.keys():
 | 
			
		||||
			return object.__new__(cls)
 | 
			
		||||
 | 
			
		||||
		extend = b['extend']
 | 
			
		||||
		assert issubclass(cls, extend.__class__)
 | 
			
		||||
		extend.__class__ = cls
 | 
			
		||||
		return extend
 | 
			
		||||
 | 
			
		||||
# helper decorator to create properties in cleaner way
 | 
			
		||||
def nested_property(f):
 | 
			
		||||
	ret = f()
 | 
			
		||||
	p = {'doc': f.__doc__}
 | 
			
		||||
	for v in ('fget', 'fset', 'fdel', 'doc'):
 | 
			
		||||
		if v in ret.keys(): p[v]=ret[v]
 | 
			
		||||
	return property(**p)
 | 
			
		||||
 | 
			
		||||
# helper to create fields from scratch
 | 
			
		||||
def Field(typ, **attrs):
 | 
			
		||||
	''' Helper function to create a field of given type. '''
 | 
			
		||||
	f = {
 | 
			
		||||
		'boolean': BooleanField,
 | 
			
		||||
		'fixed': StringField,
 | 
			
		||||
		'hidden': StringField,
 | 
			
		||||
		'text-private': StringField,
 | 
			
		||||
		'text-single': StringField,
 | 
			
		||||
		'jid-multi': ListMultiField,
 | 
			
		||||
		'jid-single': ListSingleField,
 | 
			
		||||
		'list-multi': ListMultiField,
 | 
			
		||||
		'list-single': ListSingleField,
 | 
			
		||||
		'text-multi': TextMultiField,
 | 
			
		||||
	}[typ](typ=typ, **attrs)
 | 
			
		||||
	return f
 | 
			
		||||
 | 
			
		||||
def ExtendField(node):
 | 
			
		||||
	''' Helper function to extend a node to field of appropriate type. '''
 | 
			
		||||
	# TODO: move the dict out
 | 
			
		||||
	typ=node.getAttr('type')
 | 
			
		||||
	f = {
 | 
			
		||||
		'boolean': BooleanField,
 | 
			
		||||
		'fixed': StringField,
 | 
			
		||||
		'hidden': StringField,
 | 
			
		||||
		'text-private': StringField,
 | 
			
		||||
		'text-single': StringField,
 | 
			
		||||
		'jid-multi': ListMultiField,
 | 
			
		||||
		'jid-single': ListSingleField,
 | 
			
		||||
		'list-multi': ListMultiField,
 | 
			
		||||
		'list-single': ListSingleField,
 | 
			
		||||
		'text-multi': TextMultiField,
 | 
			
		||||
	}[typ](extend=node)
 | 
			
		||||
	return f
 | 
			
		||||
 | 
			
		||||
def ExtendForm(node):
 | 
			
		||||
	''' Helper function to extend a node to form of appropriate type. '''
 | 
			
		||||
	if node.getTag('recorded') is not None:
 | 
			
		||||
		return MultipleDataForm(extend=node)
 | 
			
		||||
	else:
 | 
			
		||||
		return SimpleDataForm(extend=node)
 | 
			
		||||
 | 
			
		||||
class DataField(ExtendedNode):
 | 
			
		||||
	""" Keeps data about one field - var, field type, labels, instructions... """
 | 
			
		||||
	def __init__(self, typ=None, var=None, value=None, label=None, desc=None, required=False,
 | 
			
		||||
		options=None, extend=None):
 | 
			
		||||
 | 
			
		||||
		if extend is None:
 | 
			
		||||
			ExtendedNode.__init__(self, 'field')
 | 
			
		||||
 | 
			
		||||
			self.type = typ
 | 
			
		||||
			self.var = var
 | 
			
		||||
			if value is not None:	self.value = value
 | 
			
		||||
			if label is not None:	self.label = label
 | 
			
		||||
			if desc  is not None:	self.desc = desc
 | 
			
		||||
			self.required = required
 | 
			
		||||
			self.options = options
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def type():
 | 
			
		||||
		'''Type of field. Recognized values are: 'boolean', 'fixed', 'hidden', 'jid-multi',
 | 
			
		||||
		'jid-single', 'list-multi', 'list-single', 'text-multi', 'text-private',
 | 
			
		||||
		'text-single'. If you set this to something different, DataField will store
 | 
			
		||||
		given name, but treat all data as text-single.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			t = self.getAttr('type')
 | 
			
		||||
			if t is None: return 'text-single'
 | 
			
		||||
			return t
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			assert isinstance(value, basestring)
 | 
			
		||||
			self.setAttr('type', value)
 | 
			
		||||
		return locals()
 | 
			
		||||
	
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def var():
 | 
			
		||||
		'''Field identifier.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getAttr('var')
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			assert isinstance(value, basestring)
 | 
			
		||||
			self.setAttr('var', value)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			self.delAttr('var')
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def label():
 | 
			
		||||
		'''Human-readable field name.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getAttr('label')
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			assert isinstance(value, basestring)
 | 
			
		||||
			self.setAttr('label', value)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			self.delAttr('label')
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def description():
 | 
			
		||||
		'''Human-readable description of field meaning.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getTagData('desc') or u''
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			assert isinstance(value, basestring)
 | 
			
		||||
			if value == '':
 | 
			
		||||
				fdel(self)
 | 
			
		||||
			else:
 | 
			
		||||
				self.setTagData('desc', value)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			t = self.getTag('desc')
 | 
			
		||||
			if t is not None:
 | 
			
		||||
				self.delChild(t)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def required():
 | 
			
		||||
		'''Controls whether this field required to fill. Boolean.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return boolean(self.getTag('required'))
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			t = self.getTag('required')
 | 
			
		||||
			if t and not value:
 | 
			
		||||
				self.delChild(t)
 | 
			
		||||
			elif not t and value:
 | 
			
		||||
				self.addChild('required')
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
class BooleanField(DataField):
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def value():
 | 
			
		||||
		'''Value of field. May contain True, False or None.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			v = self.getTagData('value')
 | 
			
		||||
			if v in ('0', 'false'): return False
 | 
			
		||||
			if v in ('1', 'true'): return True
 | 
			
		||||
			if v is None: return None
 | 
			
		||||
			raise WrongFieldValue
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			self.setTagData('value', value and '1' or '0')
 | 
			
		||||
		def fdel(self, value):
 | 
			
		||||
			t = self.getTag('value')
 | 
			
		||||
			if t is not None:
 | 
			
		||||
				self.delChild(t)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
class StringField(DataField):
 | 
			
		||||
	''' Covers fields of types: fixed, hidden, text-private, text-single. '''
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def value():
 | 
			
		||||
		'''Value of field. May be any unicode string.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getTagData('value') or u''
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			assert isinstance(value, basestring)
 | 
			
		||||
			if value == '':
 | 
			
		||||
				return fdel(self)
 | 
			
		||||
			self.setTagData('value', value)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			try:
 | 
			
		||||
				self.delChild(self.getTag('value'))
 | 
			
		||||
			except ValueError: # if there already were no value tag
 | 
			
		||||
				pass
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListField(DataField):
 | 
			
		||||
	''' Covers fields of types: jid-multi, jid-single, list-multi, list-single. '''
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def options():
 | 
			
		||||
		'''Options.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			options = []
 | 
			
		||||
			for element in self.getTags('option'):
 | 
			
		||||
				v = element.getTagData('value')
 | 
			
		||||
				if v is None: raise WrongFieldValue
 | 
			
		||||
				options.append((element.getAttr('label'), v))
 | 
			
		||||
			return options
 | 
			
		||||
		def fset(self, values):
 | 
			
		||||
			fdel(self)
 | 
			
		||||
			for value, label in values:
 | 
			
		||||
				self.addChild('option', {'label': label}).setTagData('value', value)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			for element in self.getTags('option'):
 | 
			
		||||
				self.delChild(element)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	def iter_options(self):
 | 
			
		||||
		for element in self.getTags('option'):	# TODO: iter!
 | 
			
		||||
			v = element.getTagData('value')
 | 
			
		||||
			if v is None: raise WrongFieldValue
 | 
			
		||||
			yield (v, element.getAttr('label'))
 | 
			
		||||
 | 
			
		||||
class ListSingleField(ListField, StringField):
 | 
			
		||||
	'''Covers list-single and jid-single fields.'''
 | 
			
		||||
	pass
 | 
			
		||||
 | 
			
		||||
class ListMultiField(ListField):
 | 
			
		||||
	'''Covers list-multi and jid-multi fields.'''
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def values():
 | 
			
		||||
		'''Values held in field.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			values = []
 | 
			
		||||
			for element in self.getTags('value'):
 | 
			
		||||
				values.append(element.getData())
 | 
			
		||||
			return values
 | 
			
		||||
		def fset(self, values):
 | 
			
		||||
			fdel(self)
 | 
			
		||||
			for value in values:
 | 
			
		||||
				self.addChild('value').setData(value)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			for element in self.getTags('value'):
 | 
			
		||||
				self.delChild(element)
 | 
			
		||||
		return locals()
 | 
			
		||||
	
 | 
			
		||||
	def iter_values():
 | 
			
		||||
		for element in self.getTags('value'):
 | 
			
		||||
			yield element.getData()
 | 
			
		||||
 | 
			
		||||
class TextMultiField(DataField):
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def value():
 | 
			
		||||
		'''Value held in field.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			value = u''
 | 
			
		||||
			for element in self.getTags('value'):	# TODO: iter!
 | 
			
		||||
				value += '\n' + element.getData()
 | 
			
		||||
			return value[1:]
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			fdel(self)
 | 
			
		||||
			if value == '': return
 | 
			
		||||
			for line in value.split('\n'):
 | 
			
		||||
				self.addChild('value').setData(line)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			for element in self.getTags('value'):
 | 
			
		||||
				self.delChild(element)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
class DataRecord(ExtendedNode):
 | 
			
		||||
	'''The container for data fields - an xml element which has DataField
 | 
			
		||||
	elements as children.'''
 | 
			
		||||
	def __init__(self, fields=None, associated=None, extend=None):
 | 
			
		||||
		self.associated = associated
 | 
			
		||||
		self.vars = {}
 | 
			
		||||
		if extend is None:
 | 
			
		||||
			# we have to build this object from scratch
 | 
			
		||||
			xmpp.Node.__init__(self)
 | 
			
		||||
 | 
			
		||||
			if fields is not None: self.fields = fields
 | 
			
		||||
		else:
 | 
			
		||||
			# we already have xmpp.Node inside - try to convert all
 | 
			
		||||
			# fields into DataField objects
 | 
			
		||||
			if fields is None:
 | 
			
		||||
				for field in self.getTags('field'): # TODO: iter!
 | 
			
		||||
					if not isinstance(field, DataField):
 | 
			
		||||
						ExtendField(field)
 | 
			
		||||
					self.vars[field.var] = field
 | 
			
		||||
			else:
 | 
			
		||||
				for field in self.getTags('field'):
 | 
			
		||||
					self.delChild(field)
 | 
			
		||||
				self.fields = fields
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def fields():
 | 
			
		||||
		'''List of fields in this record.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getTags('field')
 | 
			
		||||
		def fset(self, fields):
 | 
			
		||||
			fdel(self)
 | 
			
		||||
			for field in fields:
 | 
			
		||||
				if not isinstance(field, DataField):
 | 
			
		||||
					ExtendField(extend=field)
 | 
			
		||||
				self.addChild(node=field)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			for element in self.getTags('field'):
 | 
			
		||||
				self.delChild(element)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	def iter_fields(self):
 | 
			
		||||
		''' Iterate over fields in this record. Do not take associated
 | 
			
		||||
		into account. '''
 | 
			
		||||
		for field in self.getTags('field'):	# TODO: iter!
 | 
			
		||||
			yield field
 | 
			
		||||
 | 
			
		||||
	def iter_with_associated(self):
 | 
			
		||||
		''' Iterate over associated, yielding both our field and
 | 
			
		||||
		associated one together. '''
 | 
			
		||||
		for field in self.associated.iter_fields():
 | 
			
		||||
			yield self[field.var], field
 | 
			
		||||
 | 
			
		||||
	def __getitem__(self, item):
 | 
			
		||||
		return self.vars[item]
 | 
			
		||||
 | 
			
		||||
class DataForm(ExtendedNode):
 | 
			
		||||
	def __init__(self, type=None, title=None, instructions=None, extend=None):
 | 
			
		||||
		if extend is None:
 | 
			
		||||
			# we have to build form from scratch
 | 
			
		||||
			xmpp.Node.__init__(self, 'x', attrs={'xmlns': xmpp.NS_DATA})
 | 
			
		||||
 | 
			
		||||
		if type is not None:		self.type=type
 | 
			
		||||
		if title is not None:		self.title=title
 | 
			
		||||
		if instructions is not None:	self.instructions=instructions
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def type():
 | 
			
		||||
		''' Type of the form. Must be one of: 'form', 'submit', 'cancel', 'result'.
 | 
			
		||||
		'form' - this form is to be filled in; you will be able soon to do:
 | 
			
		||||
			filledform = DataForm(replyto=thisform)...'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getAttr('type')
 | 
			
		||||
		def fset(self):
 | 
			
		||||
			assert type in ('form', 'submit', 'cancel', 'result')
 | 
			
		||||
			self.setAttr('type', type)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def title():
 | 
			
		||||
		''' Title of the form. Human-readable, should not contain any \\r\\n.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getTagData('title')
 | 
			
		||||
		def fset(self, title):
 | 
			
		||||
			self.setTagData('title', title)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			try:
 | 
			
		||||
				self.delChild('title')
 | 
			
		||||
			except ValueError:
 | 
			
		||||
				pass
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def instructions():
 | 
			
		||||
		''' Instructions for this form. Human-readable, may contain \\r\\n. '''
 | 
			
		||||
		# TODO: the same code is in TextMultiField. join them
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			value = u''
 | 
			
		||||
			for value in self.getTags('value'):
 | 
			
		||||
				value += '\n' + value.getData()
 | 
			
		||||
			return value[1:]
 | 
			
		||||
		def fset(self, value):
 | 
			
		||||
			fdel(self)
 | 
			
		||||
			if value == '': return
 | 
			
		||||
			for line in value.split('\n'):
 | 
			
		||||
				self.addChild('value').setData(line)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			for value in self.getTags('value'):
 | 
			
		||||
				self.delChild(value)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
class SimpleDataForm(DataForm, DataRecord):
 | 
			
		||||
	def __init__(self, type=None, title=None, instructions=None, fields=None, extend=None):
 | 
			
		||||
		DataForm.__init__(self, type=type, title=title, instructions=instructions, extend=extend)
 | 
			
		||||
		DataRecord.__init__(self, fields=fields, extend=self, associated=self)
 | 
			
		||||
 | 
			
		||||
class MultipleDataForm(DataForm):
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		# all records, recorded into DataRecords
 | 
			
		||||
		pass
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def items():
 | 
			
		||||
		''' A list of all records. '''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return list(self.iter_records())
 | 
			
		||||
		def fset(self, records):
 | 
			
		||||
			fdel(self)
 | 
			
		||||
			for record in records:
 | 
			
		||||
				if not isinstance(record, DataRecord):
 | 
			
		||||
					DataRecord(extend=record)
 | 
			
		||||
				self.addChild(node=record)
 | 
			
		||||
		def fdel(self):
 | 
			
		||||
			for record in self.getTags('record'):
 | 
			
		||||
				self.delChild(record)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
	def iter_records():
 | 
			
		||||
		for record in self.getTags('item'):
 | 
			
		||||
			yield item
 | 
			
		||||
 | 
			
		||||
	@nested_property
 | 
			
		||||
	def recorded():
 | 
			
		||||
		''' DataRecord that contains descriptions of fields in records.'''
 | 
			
		||||
		def fget(self):
 | 
			
		||||
			return self.getTag('recorded')
 | 
			
		||||
		def fset(self, record):
 | 
			
		||||
			try:
 | 
			
		||||
				self.delChild('recorded')
 | 
			
		||||
			except:
 | 
			
		||||
				pass
 | 
			
		||||
 | 
			
		||||
			record.setName('recorded')
 | 
			
		||||
			self.addChild(node=record)
 | 
			
		||||
		return locals()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										51
									
								
								src/common/pubsub.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/common/pubsub.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
import xmpp
 | 
			
		||||
import gajim
 | 
			
		||||
 | 
			
		||||
class ConnectionPubSub:
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		self.__callbacks={}
 | 
			
		||||
 | 
			
		||||
	def send_pb_subscription_query(self, jid, cb, *args, **kwargs):
 | 
			
		||||
		query = xmpp.Iq('get', to=jid)
 | 
			
		||||
		pb = query.addChild('pubsub', {'xmlns': xmpp.NS_PUBSUB})
 | 
			
		||||
		pb.addChild('subscriptions')
 | 
			
		||||
 | 
			
		||||
		id = self.connection.send(query)
 | 
			
		||||
 | 
			
		||||
		self.__callbacks[id]=(cb, args, kwargs)
 | 
			
		||||
 | 
			
		||||
	def send_pb_subscribe(self, jid, node, cb, *args, **kwargs):
 | 
			
		||||
		our_jid = gajim.get_jid_from_account(self.name)
 | 
			
		||||
		query = xmpp.Iq('set', to=jid)
 | 
			
		||||
		pb = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
 | 
			
		||||
		pb.addChild('subscribe', {'node': node, 'jid': our_jid})
 | 
			
		||||
 | 
			
		||||
		id = self.connection.send(query)
 | 
			
		||||
 | 
			
		||||
		self.__callbacks[id]=(cb, args, kwargs)
 | 
			
		||||
 | 
			
		||||
	def send_pb_unsubscribe(self, jid, node, cb, *args, **kwargs):
 | 
			
		||||
		our_jid = gajim.get_jid_from_account(self.name)
 | 
			
		||||
		query = xmpp.Iq('set', to=jid)
 | 
			
		||||
		pb = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
 | 
			
		||||
		pb.addChild('unsubscribe', {'node': node, 'jid': our_jid})
 | 
			
		||||
 | 
			
		||||
		id = self.connection.send(query)
 | 
			
		||||
 | 
			
		||||
		self.__callbacks[id]=(cb, args, kwargs)
 | 
			
		||||
 | 
			
		||||
	def send_pb_publish(self, jid, node, item, id):
 | 
			
		||||
		'''Publish item to a node.'''
 | 
			
		||||
		query = xmpp.Iq('set', to=jid)
 | 
			
		||||
		e = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
 | 
			
		||||
		e = e.addChild('publish', {'node': node})
 | 
			
		||||
		e = e.addChild('item', {'id': id}, [item])	# TODO: we should generate id... or we shouldn't?
 | 
			
		||||
 | 
			
		||||
		self.connection.send(query)
 | 
			
		||||
 | 
			
		||||
	def _PubSubCB(self, conn, stanza):
 | 
			
		||||
		try:
 | 
			
		||||
			cb, args, kwargs = self.__callbacks.pop(stanza.getID())
 | 
			
		||||
			cb(conn, stanza, *args, **kwargs)
 | 
			
		||||
		except KeyError:
 | 
			
		||||
			pass
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ def ustr(what):
 | 
			
		|||
	if type(r)<>type(u''): return unicode(r,ENCODING)
 | 
			
		||||
	return r
 | 
			
		||||
 | 
			
		||||
class Node:
 | 
			
		||||
class Node(object):
 | 
			
		||||
	""" Node class describes syntax of separate XML Node. It have a constructor that permits node creation
 | 
			
		||||
		from set of "namespace name", attributes and payload of text strings and other nodes.
 | 
			
		||||
		It does not natively support building node from text string and uses NodeBuilder class for that purpose.
 | 
			
		||||
| 
						 | 
				
			
			@ -58,9 +58,9 @@ class Node:
 | 
			
		|||
			"node" and other arguments is provided then the node initially created as replica of "node"
 | 
			
		||||
			provided and then modified to be compliant with other arguments."""
 | 
			
		||||
		if node:
 | 
			
		||||
			if self.FORCE_NODE_RECREATION and type(node)==type(self): 
 | 
			
		||||
			if self.FORCE_NODE_RECREATION and isinstance(node, Node): 
 | 
			
		||||
				node=str(node)
 | 
			
		||||
			if type(node)<>type(self): 
 | 
			
		||||
			if not isinstance(node, Node): 
 | 
			
		||||
				node=NodeBuilder(node,self)
 | 
			
		||||
			else:
 | 
			
		||||
				self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = node.name,node.namespace,{},[],[],node.parent
 | 
			
		||||
| 
						 | 
				
			
			@ -74,9 +74,9 @@ class Node:
 | 
			
		|||
		if self.parent and not self.namespace: self.namespace=self.parent.namespace
 | 
			
		||||
		for attr in attrs.keys():
 | 
			
		||||
			self.attrs[attr]=attrs[attr]
 | 
			
		||||
		if type(payload) in (type(''),type(u'')): payload=[payload]
 | 
			
		||||
		if isinstance(payload, basestring): payload=[payload]
 | 
			
		||||
		for i in payload:
 | 
			
		||||
			if type(i)==type(self): self.addChild(node=i)
 | 
			
		||||
			if isinstance(i, Node): self.addChild(node=i)
 | 
			
		||||
			else: self.data.append(ustr(i))
 | 
			
		||||
 | 
			
		||||
	def __str__(self,fancy=0):
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ class Node:
 | 
			
		|||
	def delChild(self, node, attrs={}):
 | 
			
		||||
		""" Deletes the "node" from the node's childs list, if "node" is an instance.
 | 
			
		||||
			Else deletes the first node that have specified name and (optionally) attributes. """
 | 
			
		||||
		if type(node)<>type(self): node=self.getTag(node,attrs)
 | 
			
		||||
		if not isinstance(node, Node): node=self.getTag(node,attrs)
 | 
			
		||||
		self.kids.remove(node)
 | 
			
		||||
		return node
 | 
			
		||||
	def getAttrs(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -191,6 +191,18 @@ class Node:
 | 
			
		|||
				else: nodes.append(node)
 | 
			
		||||
			if one and nodes: return nodes[0]
 | 
			
		||||
		if not one: return nodes
 | 
			
		||||
	
 | 
			
		||||
	def iterTags(self, name, attrs={}, namespace=None):
 | 
			
		||||
		""" Iterate over all children using specified arguments as filter. """
 | 
			
		||||
		for node in self.kids:
 | 
			
		||||
			if namespace is not None and namespace!=node.getNamespace(): continue
 | 
			
		||||
			if node.getName() == name:
 | 
			
		||||
				for key in attrs.keys():
 | 
			
		||||
					if not node.attrs.has_key(key) or \
 | 
			
		||||
						node.attrs[key]!=attrs[key]: break
 | 
			
		||||
					else:
 | 
			
		||||
						yield node
 | 
			
		||||
 | 
			
		||||
	def setAttr(self, key, val):
 | 
			
		||||
		""" Sets attribute "key" with the value "val". """
 | 
			
		||||
		self.attrs[key]=val
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2068,7 +2068,7 @@ class DataFormWindow:
 | 
			
		|||
	def __init__(self, account, config):
 | 
			
		||||
		self.account = account
 | 
			
		||||
		self.config = config
 | 
			
		||||
		self.xml = gtkgui_helpers.get_glade('data_form_window.glade')
 | 
			
		||||
		self.xml = gtkgui_helpers.get_glade('data_form_window.glade', 'data_form_window')
 | 
			
		||||
		self.window = self.xml.get_widget('data_form_window')
 | 
			
		||||
		self.window.set_transient_for(gajim.interface.roster.window)
 | 
			
		||||
		self.config_vbox = self.xml.get_widget('config_vbox')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										491
									
								
								src/dataforms_widget.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								src/dataforms_widget.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,491 @@
 | 
			
		|||
##	dataforms.py
 | 
			
		||||
##
 | 
			
		||||
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
 | 
			
		||||
## Copyright (C) 2005-2006 Nikos Kouremenos <nkour@jabber.org>
 | 
			
		||||
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
 | 
			
		||||
## Copyright (C) 2003-2005 Vincent Hanquez <tab@snarc.org>
 | 
			
		||||
##
 | 
			
		||||
## 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; version 2 only.
 | 
			
		||||
##
 | 
			
		||||
## 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.
 | 
			
		||||
##
 | 
			
		||||
""" This module contains widget that can display data form (JEP-0004).
 | 
			
		||||
Words single and multiple refers here to types of data forms:
 | 
			
		||||
single means these with one record of data (without <recorded/> element),
 | 
			
		||||
multiple - these which may contain more data (with <recorded/> element)."""
 | 
			
		||||
 | 
			
		||||
# TODO: forms of type='result' should be read-only
 | 
			
		||||
# TODO: remove tabs from dialog
 | 
			
		||||
 | 
			
		||||
import gtk
 | 
			
		||||
import pango
 | 
			
		||||
 | 
			
		||||
import gtkgui_helpers
 | 
			
		||||
 | 
			
		||||
import common.xmpp as xmpp
 | 
			
		||||
import common.dataforms as dataforms
 | 
			
		||||
 | 
			
		||||
import itertools
 | 
			
		||||
 | 
			
		||||
class DataFormWidget(gtk.Alignment, object):
 | 
			
		||||
# "public" interface
 | 
			
		||||
	""" Data Form widget. Use like any other widget. """
 | 
			
		||||
	def __init__(self, dataformnode=None):
 | 
			
		||||
		""" Create a widget. """
 | 
			
		||||
		gtk.Alignment.__init__(self, xscale=1.0, yscale=1.0)
 | 
			
		||||
 | 
			
		||||
		self._data_form = None
 | 
			
		||||
 | 
			
		||||
		self.xml=gtkgui_helpers.get_glade('data_form_window.glade', 'data_form_vbox')
 | 
			
		||||
		self.xml.signal_autoconnect(self)
 | 
			
		||||
		for name in ('instructions_label', 'instructions_hseparator',
 | 
			
		||||
				'single_form_viewport', 'data_form_types_notebook',
 | 
			
		||||
				'single_form_scrolledwindow', 'multiple_form_hbox',
 | 
			
		||||
				'records_treeview', 'add_button', 'remove_button',
 | 
			
		||||
				'edit_button', 'up_button', 'down_button', 'clear_button'):
 | 
			
		||||
			self.__dict__[name] = self.xml.get_widget(name)
 | 
			
		||||
 | 
			
		||||
		self.add(self.xml.get_widget('data_form_vbox'))
 | 
			
		||||
 | 
			
		||||
		if dataformnode is not None:
 | 
			
		||||
			self.set_data_form(dataformnode)
 | 
			
		||||
 | 
			
		||||
		selection = self.records_treeview.get_selection()
 | 
			
		||||
		selection.connect('changed', self.on_records_selection_changed)
 | 
			
		||||
		selection.set_mode(gtk.SELECTION_MULTIPLE)
 | 
			
		||||
 | 
			
		||||
	def set_data_form(self, dataform):
 | 
			
		||||
		""" Set the data form (xmpp.DataForm) displayed in widget. """
 | 
			
		||||
		assert isinstance(dataform, dataforms.DataForm)
 | 
			
		||||
 | 
			
		||||
		self.del_data_form()
 | 
			
		||||
		self._data_form = dataform
 | 
			
		||||
		if isinstance(dataform, dataforms.SimpleDataForm):
 | 
			
		||||
			self.build_single_data_form()
 | 
			
		||||
		else:
 | 
			
		||||
			self.build_multiple_data_form()
 | 
			
		||||
 | 
			
		||||
		# create appropriate description for instructions field if there isn't any
 | 
			
		||||
		if dataform.instructions=='':
 | 
			
		||||
			if dataform.type=='result':
 | 
			
		||||
				# form is single
 | 
			
		||||
				instructions = _('This is result of query.')
 | 
			
		||||
			else:
 | 
			
		||||
				# form is writable (TODO: move that to build_*_data_form()?
 | 
			
		||||
				if isinstance(dataform, dataforms.SimpleDataForm):
 | 
			
		||||
					instructions = _('Fill in the form.')
 | 
			
		||||
				else:
 | 
			
		||||
					instructions = _('Edit items on the list')
 | 
			
		||||
		else:
 | 
			
		||||
			instructions = dataform.instructions
 | 
			
		||||
 | 
			
		||||
		self.instructions_label.set_text(instructions)
 | 
			
		||||
 | 
			
		||||
	def get_data_form(self):
 | 
			
		||||
		""" Data form displayed in the widget or None if no form. """
 | 
			
		||||
		return self._data_form
 | 
			
		||||
 | 
			
		||||
	def del_data_form(self):
 | 
			
		||||
		self.clean_data_form()
 | 
			
		||||
		self._data_form = None
 | 
			
		||||
 | 
			
		||||
	data_form = property(get_data_form, set_data_form, del_data_form,
 | 
			
		||||
		"Data form presented in a widget")
 | 
			
		||||
 | 
			
		||||
	def get_title(self):
 | 
			
		||||
		""" Get the title of data form, as a unicode object. If no
 | 
			
		||||
		title or no form, returns u''. Useful for setting window title. """
 | 
			
		||||
		if self._data_form is not None:
 | 
			
		||||
			if self._data_form.title is not None:
 | 
			
		||||
				return self._data_form.title
 | 
			
		||||
		return u''
 | 
			
		||||
 | 
			
		||||
	title = property(get_title, None, None, "Data form title")
 | 
			
		||||
 | 
			
		||||
	def show(self):
 | 
			
		||||
		""" Treat 'us' as one widget. """
 | 
			
		||||
		self.show_all()
 | 
			
		||||
 | 
			
		||||
# "private" methods
 | 
			
		||||
 | 
			
		||||
# we have actually two different kinds of data forms: one is a simple form to fill,
 | 
			
		||||
# second is a table with several records; 
 | 
			
		||||
 | 
			
		||||
	def clean_data_form(self):
 | 
			
		||||
		'''Remove data about existing form. This metod is empty, because
 | 
			
		||||
		it is rewritten by build_*_data_form, according to type of form
 | 
			
		||||
		which is actually displayed.'''
 | 
			
		||||
		pass
 | 
			
		||||
 | 
			
		||||
	def build_single_data_form(self):
 | 
			
		||||
		'''Invoked when new single form is to be created.'''
 | 
			
		||||
		assert isinstance(self._data_form, dataforms.SimpleDataForm)
 | 
			
		||||
 | 
			
		||||
		self.clean_data_form()
 | 
			
		||||
 | 
			
		||||
		self.singleform = SingleForm(self._data_form)
 | 
			
		||||
		self.singleform.show()
 | 
			
		||||
		self.single_form_viewport.add(self.singleform)
 | 
			
		||||
		self.data_form_types_notebook.set_current_page(
 | 
			
		||||
			self.data_form_types_notebook.page_num(
 | 
			
		||||
				self.single_form_scrolledwindow))
 | 
			
		||||
 | 
			
		||||
		self.clean_data_form = self.clean_single_data_form
 | 
			
		||||
 | 
			
		||||
	def clean_single_data_form(self):
 | 
			
		||||
		'''(Called as clean_data_form, read the docs of clean_data_form()).
 | 
			
		||||
		Remove form from widget.'''
 | 
			
		||||
		self.singleform.destroy()
 | 
			
		||||
		del self.singleform
 | 
			
		||||
 | 
			
		||||
	def build_multiple_data_form(self):
 | 
			
		||||
		'''Invoked when new multiple form is to be created.'''
 | 
			
		||||
		assert isinstance(self._data_form, dataforms.MultipleDataForm)
 | 
			
		||||
 | 
			
		||||
		self.clean_data_form()
 | 
			
		||||
 | 
			
		||||
		# creating model for form...
 | 
			
		||||
		fieldtypes = []
 | 
			
		||||
		for field in self._data_form.recorded.iter_fields():
 | 
			
		||||
			# note: we store also text-private and hidden fields,
 | 
			
		||||
			# we just do not display them.
 | 
			
		||||
			# TODO: boolean fields
 | 
			
		||||
			#elif field.type=='boolean': fieldtypes.append(bool)
 | 
			
		||||
			fieldtypes.append(str)
 | 
			
		||||
 | 
			
		||||
		self.multiplemodel = gtk.ListStore(*fieldtypes)
 | 
			
		||||
 | 
			
		||||
		# moving all data to model
 | 
			
		||||
		for item in self._data_form.iter_records():
 | 
			
		||||
			# TODO: probably wrong... (.value[s]?, fields not in the same order?)
 | 
			
		||||
			# not checking multiple-item forms...
 | 
			
		||||
			self.multiplemodel.append([field.value for field in item.iter_fields()])
 | 
			
		||||
 | 
			
		||||
		# constructing columns...
 | 
			
		||||
		for field, counter in zip(self._data_form.iter_fields(), itertools.count()):
 | 
			
		||||
			print repr(field), repr(counter)
 | 
			
		||||
			self.records_treeview.append_column(
 | 
			
		||||
				gtk.TreeViewColumn(field.label,	gtk.CellRendererText(),
 | 
			
		||||
					text=counter))
 | 
			
		||||
 | 
			
		||||
		self.records_treeview.set_model(self.multiplemodel)
 | 
			
		||||
		self.records_treeview.show_all()
 | 
			
		||||
 | 
			
		||||
		self.data_form_types_notebook.set_current_page(
 | 
			
		||||
			self.data_form_types_notebook.page_num(
 | 
			
		||||
				self.multiple_form_hbox))
 | 
			
		||||
 | 
			
		||||
		self.clean_data_form = self.clean_multiple_data_form
 | 
			
		||||
 | 
			
		||||
		# refresh list look
 | 
			
		||||
		self.refresh_multiple_buttons()
 | 
			
		||||
 | 
			
		||||
	def clean_multiple_data_form(self):
 | 
			
		||||
		'''(Called as clean_data_form, read the docs of clean_data_form()).
 | 
			
		||||
		Remove form from widget.'''
 | 
			
		||||
		del self.multiplemodel
 | 
			
		||||
 | 
			
		||||
	def refresh_multiple_buttons(self):
 | 
			
		||||
		''' Checks for treeview state and makes control buttons sensitive.'''
 | 
			
		||||
		selection = self.records_treeview.get_selection()
 | 
			
		||||
		model = self.records_treeview.get_model()
 | 
			
		||||
		count = selection.count_selected_rows()
 | 
			
		||||
		if count==0:
 | 
			
		||||
			self.remove_button.set_sensitive(False)
 | 
			
		||||
			self.edit_button.set_sensitive(False)
 | 
			
		||||
			self.up_button.set_sensitive(False)
 | 
			
		||||
			self.down_button.set_sensitive(False)
 | 
			
		||||
		elif count==1:
 | 
			
		||||
			self.remove_button.set_sensitive(True)
 | 
			
		||||
			self.edit_button.set_sensitive(True)
 | 
			
		||||
			_, (path,) = selection.get_selected_rows()
 | 
			
		||||
			iter = model.get_iter(path)
 | 
			
		||||
			if model.iter_next(iter) is None:
 | 
			
		||||
				self.up_button.set_sensitive(True)
 | 
			
		||||
				self.down_button.set_sensitive(False)
 | 
			
		||||
			elif path==(0,):
 | 
			
		||||
				self.up_button.set_sensitive(False)
 | 
			
		||||
				self.down_button.set_sensitive(True)
 | 
			
		||||
			else:
 | 
			
		||||
				self.up_button.set_sensitive(True)
 | 
			
		||||
				self.down_button.set_sensitive(True)
 | 
			
		||||
		else:
 | 
			
		||||
			self.remove_button.set_sensitive(True)
 | 
			
		||||
			self.edit_button.set_sensitive(True)
 | 
			
		||||
			self.up_button.set_sensitive(False)
 | 
			
		||||
			self.down_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		if len(model)==0:
 | 
			
		||||
			self.clear_button.set_sensitive(False)
 | 
			
		||||
		else:
 | 
			
		||||
			self.clear_button.set_sensitive(True)
 | 
			
		||||
 | 
			
		||||
	def on_clear_button_clicked(self, widget):
 | 
			
		||||
		self.records_treeview.get_model().clear()
 | 
			
		||||
 | 
			
		||||
	def on_remove_button_clicked(self, widget):
 | 
			
		||||
		selection = self.records_treeview.get_selection()
 | 
			
		||||
		model, rowrefs = selection.get_selected_rows()	# rowref is a list of paths
 | 
			
		||||
		for i in xrange(len(rowrefs)):
 | 
			
		||||
			rowrefs[i] = gtk.TreeRowReference(model, rowrefs[i])
 | 
			
		||||
		# rowref is a list of row references; need to convert because we will modify the model,
 | 
			
		||||
		# paths would change
 | 
			
		||||
		for rowref in rowrefs:
 | 
			
		||||
			del model[rowref.get_path()]
 | 
			
		||||
	
 | 
			
		||||
	def on_up_button_clicked(self, widget):
 | 
			
		||||
		selection = self.records_treeview.get_selection()
 | 
			
		||||
		model, (path,) = selection.get_selected_rows()
 | 
			
		||||
		iter = model.get_iter(path)
 | 
			
		||||
		previter = model.get_iter((path[0]-1,))	# constructing path for previous iter
 | 
			
		||||
		model.swap(iter, previter)
 | 
			
		||||
 | 
			
		||||
		self.refresh_multiple_buttons()
 | 
			
		||||
 | 
			
		||||
	def on_down_button_clicked(self, widget):
 | 
			
		||||
		selection = self.records_treeview.get_selection()
 | 
			
		||||
		model, (path,) = selection.get_selected_rows()
 | 
			
		||||
		iter = model.get_iter(path)
 | 
			
		||||
		nextiter = model.iter_next(iter)
 | 
			
		||||
		model.swap(iter, nextiter)
 | 
			
		||||
 | 
			
		||||
		self.refresh_multiple_buttons()
 | 
			
		||||
 | 
			
		||||
	def on_records_selection_changed(self, widget):
 | 
			
		||||
		self.refresh_multiple_buttons()
 | 
			
		||||
 | 
			
		||||
class SingleForm(gtk.Table, object):
 | 
			
		||||
	""" Widget that represent DATAFORM_SINGLE mode form. Because this is used
 | 
			
		||||
	not only to display single forms, but to form input windows of multiple-type
 | 
			
		||||
	forms, it is in another class."""
 | 
			
		||||
	def __init__(self, dataform):
 | 
			
		||||
		assert isinstance(dataform, dataforms.SimpleDataForm)
 | 
			
		||||
 | 
			
		||||
		gtk.Table.__init__(self)
 | 
			
		||||
		self.set_col_spacings(6)
 | 
			
		||||
		self.set_row_spacings(6)
 | 
			
		||||
 | 
			
		||||
		self._data_form = dataform
 | 
			
		||||
 | 
			
		||||
		# building widget
 | 
			
		||||
		linecounter = 0
 | 
			
		||||
 | 
			
		||||
		# for each field...
 | 
			
		||||
		for field in self._data_form.iter_fields():
 | 
			
		||||
			if field.type=='hidden': continue
 | 
			
		||||
 | 
			
		||||
			commonlabel = True
 | 
			
		||||
			commondesc = True
 | 
			
		||||
			commonwidget = True
 | 
			
		||||
			widget = None
 | 
			
		||||
 | 
			
		||||
			if field.type=='boolean':
 | 
			
		||||
				widget = gtk.CheckButton()
 | 
			
		||||
				widget.connect('toggled', self.on_boolean_checkbutton_toggled, field)
 | 
			
		||||
				widget.set_active(field.value)
 | 
			
		||||
 | 
			
		||||
			elif field.type=='fixed':
 | 
			
		||||
				leftattach = 1
 | 
			
		||||
				rightattach = 2
 | 
			
		||||
				if field.label is None:
 | 
			
		||||
					commonlabel = False
 | 
			
		||||
					leftattach = 0
 | 
			
		||||
				if field.description is None:
 | 
			
		||||
					commondesc = False
 | 
			
		||||
					rightattach = 3
 | 
			
		||||
				
 | 
			
		||||
				commonwidget=False
 | 
			
		||||
				widget = gtk.Label(field.value)
 | 
			
		||||
				widget.set_line_wrap(True)
 | 
			
		||||
				self.attach(widget, leftattach, rightattach, linecounter, linecounter+1,
 | 
			
		||||
					xoptions=gtk.FILL, yoptions=gtk.FILL)
 | 
			
		||||
 | 
			
		||||
			elif field.type == 'list-single':
 | 
			
		||||
				# TODO: When more than few choices, make a list
 | 
			
		||||
				# TODO: Think of moving that to another function (it could be used
 | 
			
		||||
				# TODO: in stage2 of adhoc commands too).
 | 
			
		||||
				# TODO: What if we have radio buttons and non-required field?
 | 
			
		||||
				# TODO: We cannot deactivate them all...
 | 
			
		||||
				widget = gtk.VBox()
 | 
			
		||||
				first_radio = None
 | 
			
		||||
				for value, label in field.iter_options():
 | 
			
		||||
					radio = gtk.RadioButton(first_radio, label=label)
 | 
			
		||||
					radio.connect('toggled', self.on_list_single_radiobutton_toggled,
 | 
			
		||||
						field, value)
 | 
			
		||||
					if first_radio is None:
 | 
			
		||||
						first_radio = radio
 | 
			
		||||
						if field.value=='':	# TODO: is None when done
 | 
			
		||||
							field.value = value
 | 
			
		||||
					if value == field.value:
 | 
			
		||||
						radio.set_active(True)
 | 
			
		||||
					widget.pack_start(radio, expand=False)
 | 
			
		||||
 | 
			
		||||
			elif field.type == 'list-multi':
 | 
			
		||||
				# TODO: When more than few choices, make a list
 | 
			
		||||
				widget = gtk.VBox()
 | 
			
		||||
				for value, label in field.iter_options():
 | 
			
		||||
					check = gtk.CheckButton(label, use_underline=False)
 | 
			
		||||
					check.set_active(value in field.values)
 | 
			
		||||
					check.connect('toggled', self.on_list_multi_checkbutton_toggled,
 | 
			
		||||
						field, value)
 | 
			
		||||
					widget.pack_start(check, expand=False)
 | 
			
		||||
 | 
			
		||||
			elif field.type == 'jid-single':
 | 
			
		||||
				widget = gtk.Entry()
 | 
			
		||||
				widget.connect('changed', self.on_text_single_entry_changed, field)
 | 
			
		||||
				widget.set_text(field.value)
 | 
			
		||||
 | 
			
		||||
			elif field.type == 'jid-multi':
 | 
			
		||||
				commonwidget = False
 | 
			
		||||
 | 
			
		||||
				xml = gtkgui_helpers.get_glade('data_form_window.glade', 'item_list_table')
 | 
			
		||||
				widget = xml.get_widget('item_list_table')
 | 
			
		||||
				treeview = xml.get_widget('item_treeview')
 | 
			
		||||
 | 
			
		||||
				listmodel = gtk.ListStore(str)
 | 
			
		||||
				for value in field.iter_values():
 | 
			
		||||
					# nobody will create several megabytes long stanza
 | 
			
		||||
					listmodel.insert(999999, (value,))
 | 
			
		||||
 | 
			
		||||
				treeview.set_model(listmodel)
 | 
			
		||||
 | 
			
		||||
				renderer = gtk.CellRendererText()
 | 
			
		||||
				renderer.set_property('editable', True)
 | 
			
		||||
				renderer.connect('edited',
 | 
			
		||||
					self.on_jid_multi_cellrenderertext_edited, listmodel, field)
 | 
			
		||||
 | 
			
		||||
				treeview.append_column(gtk.TreeViewColumn(None, renderer,
 | 
			
		||||
					text=0))
 | 
			
		||||
 | 
			
		||||
				xml.get_widget('add_button').connect('clicked',
 | 
			
		||||
					self.on_jid_multi_add_button_clicked, treeview, listmodel, field)
 | 
			
		||||
				xml.get_widget('edit_button').connect('clicked',
 | 
			
		||||
					self.on_jid_multi_edit_button_clicked, treeview)
 | 
			
		||||
				xml.get_widget('remove_button').connect('clicked',
 | 
			
		||||
					self.on_jid_multi_remove_button_clicked, treeview, field)
 | 
			
		||||
				xml.get_widget('clear_button').connect('clicked',
 | 
			
		||||
					self.on_jid_multi_clean_button_clicked, listmodel, field)
 | 
			
		||||
 | 
			
		||||
				self.attach(widget, 1, 2, linecounter, linecounter+1)
 | 
			
		||||
 | 
			
		||||
				del xml
 | 
			
		||||
 | 
			
		||||
			elif field.type == 'text-private':
 | 
			
		||||
				widget = gtk.Entry()
 | 
			
		||||
				widget.connect('changed', self.on_text_single_entry_changed, field)
 | 
			
		||||
				widget.set_visibility(False)
 | 
			
		||||
				widget.set_text(field.value)
 | 
			
		||||
 | 
			
		||||
			elif field.type == 'text-multi':
 | 
			
		||||
				# TODO: bigger text view
 | 
			
		||||
				commonwidget = False
 | 
			
		||||
 | 
			
		||||
				textwidget = gtk.TextView()
 | 
			
		||||
				textwidget.set_wrap_mode(gtk.WRAP_WORD)
 | 
			
		||||
				textwidget.get_buffer().connect('changed', self.on_text_multi_textbuffer_changed,
 | 
			
		||||
					field)
 | 
			
		||||
				textwidget.get_buffer().set_text(field.value)
 | 
			
		||||
				
 | 
			
		||||
				widget = gtk.ScrolledWindow()
 | 
			
		||||
				widget.add(textwidget)
 | 
			
		||||
 | 
			
		||||
				self.attach(widget, 1, 2, linecounter, linecounter+1)
 | 
			
		||||
 | 
			
		||||
			else:# field.type == 'text-single' or field.type is nonstandard:
 | 
			
		||||
				# JEP says that if we don't understand some type, we
 | 
			
		||||
				# should handle it as text-single
 | 
			
		||||
				widget = gtk.Entry()
 | 
			
		||||
				widget.connect('changed', self.on_text_single_entry_changed, field)
 | 
			
		||||
				if field.value is None:
 | 
			
		||||
					field.value = u''
 | 
			
		||||
				widget.set_text(field.value)
 | 
			
		||||
 | 
			
		||||
			if commonlabel and field.label is not None:
 | 
			
		||||
				label = gtk.Label(field.label)
 | 
			
		||||
				label.set_alignment(1.0, 0.5)
 | 
			
		||||
				self.attach(label, 0, 1, linecounter, linecounter+1,
 | 
			
		||||
					xoptions=gtk.FILL, yoptions=gtk.FILL)
 | 
			
		||||
 | 
			
		||||
			if commonwidget:
 | 
			
		||||
				assert widget is not None
 | 
			
		||||
				self.attach(widget, 1, 2, linecounter, linecounter+1,
 | 
			
		||||
					yoptions=gtk.FILL)
 | 
			
		||||
			widget.show_all()
 | 
			
		||||
 | 
			
		||||
			if commondesc and field.description!='':
 | 
			
		||||
				label = gtk.Label()
 | 
			
		||||
				label.set_markup('<small>'+\
 | 
			
		||||
					gtkgui_helpers.escape_for_pango_markup(field.description)+\
 | 
			
		||||
					'</small>')
 | 
			
		||||
				label.set_line_wrap(True)
 | 
			
		||||
				self.attach(label, 2, 3, linecounter, linecounter+1,
 | 
			
		||||
					xoptions=gtk.FILL|gtk.SHRINK, yoptions=gtk.FILL|gtk.SHRINK)
 | 
			
		||||
 | 
			
		||||
			linecounter+=1
 | 
			
		||||
		if self.get_property('visible'):
 | 
			
		||||
			self.show_all()
 | 
			
		||||
 | 
			
		||||
	def show(self):
 | 
			
		||||
		# simulate that we are one widget
 | 
			
		||||
		self.show_all()
 | 
			
		||||
 | 
			
		||||
	def on_boolean_checkbutton_toggled(self, widget, field):
 | 
			
		||||
		field.value = widget.get_active()
 | 
			
		||||
 | 
			
		||||
	def on_list_single_radiobutton_toggled(self, widget, field, value):
 | 
			
		||||
		field.value = value
 | 
			
		||||
 | 
			
		||||
	def on_list_multi_checkbutton_toggled(self, widget, field, value):
 | 
			
		||||
		# TODO: make some methods like add_value and remove_value
 | 
			
		||||
		if widget.get_active() and value not in field.values:
 | 
			
		||||
			field.values += [value]
 | 
			
		||||
		elif not widget.get_active() and value in field.values:
 | 
			
		||||
			field.values = [v for v in field.values if v!=value]
 | 
			
		||||
 | 
			
		||||
	def on_text_single_entry_changed(self, widget, field):
 | 
			
		||||
		field.value = widget.get_text()
 | 
			
		||||
 | 
			
		||||
	def on_text_multi_textbuffer_changed(self, widget, field):
 | 
			
		||||
		field.value = widget.get_text(
 | 
			
		||||
			widget.get_start_iter(),
 | 
			
		||||
			widget.get_end_iter())
 | 
			
		||||
 | 
			
		||||
	def on_jid_multi_cellrenderertext_edited(self, cell, path, newtext, model, field):
 | 
			
		||||
		old=model[path][0]
 | 
			
		||||
		model[path][0]=newtext
 | 
			
		||||
 | 
			
		||||
		values = field.values
 | 
			
		||||
		values[values.index(old)]=newtext
 | 
			
		||||
		field.values = values
 | 
			
		||||
 | 
			
		||||
	def on_jid_multi_add_button_clicked(self, widget, treeview, model, field):
 | 
			
		||||
		iter = model.insert(999999, ("new@jabber.id",))
 | 
			
		||||
		treeview.set_cursor(model.get_path(iter), treeview.get_column(0), True)
 | 
			
		||||
		field.values = field.values + ["new@jabber.id"]
 | 
			
		||||
 | 
			
		||||
	def on_jid_multi_edit_button_clicked(self, widget, treeview):
 | 
			
		||||
		model, iter = treeview.get_selection().get_selected()
 | 
			
		||||
		assert iter is not None
 | 
			
		||||
 | 
			
		||||
		treeview.set_cursor(model.get_path(iter), treeview.get_column(0), True)
 | 
			
		||||
 | 
			
		||||
	def on_jid_multi_remove_button_clicked(self, widget, treeview, field):
 | 
			
		||||
		selection = treeview.get_selection()
 | 
			
		||||
		model = treeview.get_model()
 | 
			
		||||
		deleted = []
 | 
			
		||||
 | 
			
		||||
		def remove(model, path, iter, deleted):
 | 
			
		||||
			deleted+=model[iter]
 | 
			
		||||
			model.remove(iter)
 | 
			
		||||
 | 
			
		||||
		selection.selected_foreach(remove, deleted)
 | 
			
		||||
		field.values = (v for v in field.values if v not in deleted)
 | 
			
		||||
 | 
			
		||||
	def on_jid_multi_clean_button_clicked(self, widget, model, field):
 | 
			
		||||
		model.clear()
 | 
			
		||||
		del field.values
 | 
			
		||||
| 
						 | 
				
			
			@ -1002,7 +1002,7 @@ class FTOverwriteConfirmationDialog(ConfirmationDialog):
 | 
			
		|||
class InputDialog:
 | 
			
		||||
	'''Class for Input dialog'''
 | 
			
		||||
	def __init__(self, title, label_str, input_str = None, is_modal = True,
 | 
			
		||||
	ok_handler = None, cancel_handler = None):
 | 
			
		||||
ok_handler = None):
 | 
			
		||||
		# if modal is True you also need to call get_response()
 | 
			
		||||
		# and ok_handler won't be used
 | 
			
		||||
		self.xml = gtkgui_helpers.get_glade('input_dialog.glade')
 | 
			
		||||
| 
						 | 
				
			
			@ -1011,7 +1011,6 @@ class InputDialog:
 | 
			
		|||
		self.input_entry = self.xml.get_widget('input_entry')
 | 
			
		||||
		self.dialog.set_title(title)
 | 
			
		||||
		label.set_markup(label_str)
 | 
			
		||||
		self.cancel_handler = cancel_handler
 | 
			
		||||
		if input_str:
 | 
			
		||||
			self.input_entry.set_text(input_str)
 | 
			
		||||
			self.input_entry.select_region(0, -1) # select all
 | 
			
		||||
| 
						 | 
				
			
			@ -1023,22 +1022,14 @@ class InputDialog:
 | 
			
		|||
			okbutton.connect('clicked', self.on_okbutton_clicked)
 | 
			
		||||
			cancelbutton = self.xml.get_widget('cancelbutton')
 | 
			
		||||
			cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
 | 
			
		||||
			self.xml.signal_autoconnect(self)
 | 
			
		||||
			self.dialog.show_all()
 | 
			
		||||
 | 
			
		||||
	def on_input_dialog_destroy(self, widget):
 | 
			
		||||
		if self.cancel_handler:
 | 
			
		||||
			self.cancel_handler()
 | 
			
		||||
 | 
			
		||||
	def on_okbutton_clicked(self, widget):
 | 
			
		||||
	def on_okbutton_clicked(self,  widget):
 | 
			
		||||
		user_input = self.input_entry.get_text().decode('utf-8')
 | 
			
		||||
		self.dialog.destroy()
 | 
			
		||||
		if isinstance(self.ok_handler, tuple):
 | 
			
		||||
			self.ok_handler[0](user_input, *self.ok_handler[1:])
 | 
			
		||||
		else:
 | 
			
		||||
			self.ok_handler(user_input)
 | 
			
		||||
		self.ok_handler(user_input)
 | 
			
		||||
	
 | 
			
		||||
	def on_cancelbutton_clicked(self, widget):
 | 
			
		||||
	def on_cancelbutton_clicked(self,  widget):
 | 
			
		||||
		self.dialog.destroy()
 | 
			
		||||
 | 
			
		||||
	def get_response(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										216
									
								
								src/disco.py
									
										
									
									
									
								
							
							
						
						
									
										216
									
								
								src/disco.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -46,6 +46,7 @@ import pango
 | 
			
		|||
import dialogs
 | 
			
		||||
import tooltips
 | 
			
		||||
import gtkgui_helpers
 | 
			
		||||
import groups
 | 
			
		||||
 | 
			
		||||
from common import gajim
 | 
			
		||||
from common import xmpp
 | 
			
		||||
| 
						 | 
				
			
			@ -73,6 +74,7 @@ def _gen_agent_type_info():
 | 
			
		|||
		('gateway', 'sip'):			(False, 'sip.png'),
 | 
			
		||||
		('directory', 'user'):		(None, 'jud.png'),
 | 
			
		||||
		('pubsub', 'generic'):		(None, 'pubsub.png'),
 | 
			
		||||
		('pubsub', 'service'):		(PubSubBrowser, 'pubsub.png'),
 | 
			
		||||
		('proxy', 'bytestreams'):	(None, 'bytestreams.png'), # Socks5 FT proxy
 | 
			
		||||
 | 
			
		||||
		# Transports
 | 
			
		||||
| 
						 | 
				
			
			@ -216,6 +218,7 @@ class ServicesCache:
 | 
			
		|||
		self.account = account
 | 
			
		||||
		self._items = CacheDictionary(0, getrefresh = False)
 | 
			
		||||
		self._info = CacheDictionary(0, getrefresh = False)
 | 
			
		||||
		self._subscriptions = CacheDictionary(5, getrefresh=False)
 | 
			
		||||
		self._cbs = {}
 | 
			
		||||
 | 
			
		||||
	def cleanup(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -580,7 +583,7 @@ _('Without a connection, you can not browse available services'))
 | 
			
		|||
		self.connect_style_event(opts[0], opts[1])
 | 
			
		||||
	
 | 
			
		||||
	def destroy(self, chain = False):
 | 
			
		||||
		'''Close the browser. This can optionally close it's children and
 | 
			
		||||
		'''Close the browser. This can optionally close its children and
 | 
			
		||||
		propagate to the parent. This should happen on actions like register,
 | 
			
		||||
		or join to kill off the entire browser chain.'''
 | 
			
		||||
		if self.dying:
 | 
			
		||||
| 
						 | 
				
			
			@ -604,7 +607,8 @@ _('Without a connection, you can not browse available services'))
 | 
			
		|||
				child.destroy(chain = chain)
 | 
			
		||||
				self.children.remove(child)
 | 
			
		||||
		if self.parent:
 | 
			
		||||
			self.parent.children.remove(self)
 | 
			
		||||
			if self in self.parent.children:
 | 
			
		||||
				self.parent.children.remove(self)
 | 
			
		||||
			if chain and not self.parent.children:
 | 
			
		||||
				self.parent.destroy(chain = chain)
 | 
			
		||||
				self.parent = None
 | 
			
		||||
| 
						 | 
				
			
			@ -1662,6 +1666,214 @@ class MucBrowser(AgentBrowser):
 | 
			
		|||
		# switch to alternate query mode
 | 
			
		||||
		self.cache.get_items(jid, node, self._channel_altinfo)
 | 
			
		||||
 | 
			
		||||
def PubSubBrowser(account, jid, node):
 | 
			
		||||
	''' Returns an AgentBrowser subclass that will display service discovery
 | 
			
		||||
	for particular pubsub service. Different pubsub services may need to
 | 
			
		||||
	present different data during browsing. '''
 | 
			
		||||
	# for now, only discussion groups are supported...
 | 
			
		||||
	# TODO: check if it has appropriate features to be such kind of service
 | 
			
		||||
	return DiscussionGroupsBrowser(account, jid, node)
 | 
			
		||||
 | 
			
		||||
class DiscussionGroupsBrowser(AgentBrowser):
 | 
			
		||||
	''' For browsing pubsub-based discussion groups service. '''
 | 
			
		||||
	def __init__(self, account, jid, node):
 | 
			
		||||
		AgentBrowser.__init__(self, account, jid, node)
 | 
			
		||||
 | 
			
		||||
		# this will become set object when we get subscriptions; None means
 | 
			
		||||
		# we don't know yet which groups are subscribed
 | 
			
		||||
		self.subscriptions = None
 | 
			
		||||
 | 
			
		||||
		# this will become our action widgets when we create them; None means
 | 
			
		||||
		# we don't have them yet (needed for check in callback)
 | 
			
		||||
		self.subscribe_button = None
 | 
			
		||||
		self.unsubscribe_button = None
 | 
			
		||||
 | 
			
		||||
		gajim.connections[account].send_pb_subscription_query(jid, self._subscriptionsCB)
 | 
			
		||||
 | 
			
		||||
	def _create_treemodel(self):
 | 
			
		||||
		''' Create treemodel for the window. '''
 | 
			
		||||
		# JID, node, name (with description) - pango markup, dont have info?, subscribed?
 | 
			
		||||
		model = gtk.ListStore(str, str, str, bool, bool)
 | 
			
		||||
		model.set_sort_column_id(3, gtk.SORT_ASCENDING)
 | 
			
		||||
		self.window.services_treeview.set_model(model)
 | 
			
		||||
 | 
			
		||||
		# Name column
 | 
			
		||||
		# Pango markup for name and description, description printed with
 | 
			
		||||
		# <small/> font
 | 
			
		||||
		renderer = gtk.CellRendererText()
 | 
			
		||||
		col = gtk.TreeViewColumn(_('Name'))
 | 
			
		||||
		col.pack_start(renderer)
 | 
			
		||||
		col.set_attributes(renderer, markup=2)
 | 
			
		||||
		col.set_resizable(True)
 | 
			
		||||
		self.window.services_treeview.insert_column(col, -1)
 | 
			
		||||
 | 
			
		||||
		# Subscription state
 | 
			
		||||
		renderer = gtk.CellRendererToggle()
 | 
			
		||||
		col = gtk.TreeViewColumn(_('Subscribed'))
 | 
			
		||||
		col.pack_start(renderer)
 | 
			
		||||
		col.set_attributes(renderer, inconsistent=3, active=4)
 | 
			
		||||
		col.set_resizable(False)
 | 
			
		||||
		self.window.services_treeview.insert_column(col, -1)
 | 
			
		||||
 | 
			
		||||
		self.window.services_treeview.set_headers_visible(True)
 | 
			
		||||
 | 
			
		||||
	def _add_item(self, model, jid, node, item, force):
 | 
			
		||||
		''' Called when we got basic information about new node from query.
 | 
			
		||||
		Show the item. '''
 | 
			
		||||
		name = item.get('name', '')
 | 
			
		||||
 | 
			
		||||
		if self.subscriptions is not None:
 | 
			
		||||
			dunno = False
 | 
			
		||||
			subscribed = name in self.subscriptions
 | 
			
		||||
		else:
 | 
			
		||||
			dunno = True
 | 
			
		||||
			subscribed = False
 | 
			
		||||
 | 
			
		||||
		name = gtkgui_helpers.escape_for_pango_markup(name)
 | 
			
		||||
		name = '<b>%s</b>' % name
 | 
			
		||||
 | 
			
		||||
		model.append((jid, node, name, dunno, subscribed))
 | 
			
		||||
 | 
			
		||||
	def _add_actions(self):
 | 
			
		||||
		self.post_button = gtk.Button(label=_('New post'), use_underline=True)
 | 
			
		||||
		self.post_button.set_sensitive(False)
 | 
			
		||||
		self.post_button.connect('clicked', self.on_post_button_clicked)
 | 
			
		||||
		self.window.action_buttonbox.add(self.post_button)
 | 
			
		||||
		self.post_button.show_all()
 | 
			
		||||
 | 
			
		||||
		self.subscribe_button = gtk.Button(label=_('_Subscribe'), use_underline=True)
 | 
			
		||||
		self.subscribe_button.set_sensitive(False)
 | 
			
		||||
		self.subscribe_button.connect('clicked', self.on_subscribe_button_clicked)
 | 
			
		||||
		self.window.action_buttonbox.add(self.subscribe_button)
 | 
			
		||||
		self.subscribe_button.show_all()
 | 
			
		||||
 | 
			
		||||
		self.unsubscribe_button = gtk.Button(label=_('_Unsubscribe'), use_underline=True)
 | 
			
		||||
		self.unsubscribe_button.set_sensitive(False)
 | 
			
		||||
		self.unsubscribe_button.connect('clicked', self.on_unsubscribe_button_clicked)
 | 
			
		||||
		self.window.action_buttonbox.add(self.unsubscribe_button)
 | 
			
		||||
		self.unsubscribe_button.show_all()
 | 
			
		||||
 | 
			
		||||
	def _clean_actions(self):
 | 
			
		||||
		if self.post_button is not None:
 | 
			
		||||
			self.post_button.destroy()
 | 
			
		||||
			self.post_button = None
 | 
			
		||||
 | 
			
		||||
		if self.subscribe_button is not None:
 | 
			
		||||
			self.subscribe_button.destroy()
 | 
			
		||||
			self.subscribe_button = None
 | 
			
		||||
 | 
			
		||||
		if self.unsubscribe_button is not None:
 | 
			
		||||
			self.unsubscribe_button.destroy()
 | 
			
		||||
			self.unsubscribe_button = None
 | 
			
		||||
 | 
			
		||||
	def update_actions(self):
 | 
			
		||||
		'''Called when user selected a row. Make subscribe/unsubscribe buttons
 | 
			
		||||
		sensitive appropriatelly.'''
 | 
			
		||||
		# we have nothing to do if we don't have buttons...
 | 
			
		||||
		if self.subscribe_button is None: return
 | 
			
		||||
 | 
			
		||||
		model, iter = self.window.services_treeview.get_selection().get_selected()
 | 
			
		||||
		if not iter or self.subscriptions is None:
 | 
			
		||||
			# no item selected or no subscriptions info, all buttons are insensitive
 | 
			
		||||
			self.post_button.set_sensitive(False)
 | 
			
		||||
			self.subscribe_button.set_sensitive(False)
 | 
			
		||||
			self.unsubscribe_button.set_sensitive(False)
 | 
			
		||||
		else:
 | 
			
		||||
			subscribed = model.get_value(iter, 4) # 4 = subscribed?
 | 
			
		||||
			self.post_button.set_sensitive(subscribed)
 | 
			
		||||
			self.subscribe_button.set_sensitive(not subscribed)
 | 
			
		||||
			self.unsubscribe_button.set_sensitive(subscribed)
 | 
			
		||||
 | 
			
		||||
	def on_post_button_clicked(self, widget):
 | 
			
		||||
		'''Called when 'post' button is pressed. Open window to create post'''
 | 
			
		||||
		model, iter = self.window.services_treeview.get_selection().get_selected()
 | 
			
		||||
		if iter is None: return
 | 
			
		||||
 | 
			
		||||
		groupnode = model.get_value(iter, 1)	# 1 = groupnode
 | 
			
		||||
 | 
			
		||||
		groups.GroupsPostWindow(self.account, self.jid, groupnode)
 | 
			
		||||
 | 
			
		||||
	def on_subscribe_button_clicked(self, widget):
 | 
			
		||||
		'''Called when 'subscribe' button is pressed. Send subscribtion request.'''
 | 
			
		||||
		model, iter = self.window.services_treeview.get_selection().get_selected()
 | 
			
		||||
		if iter is None: return
 | 
			
		||||
 | 
			
		||||
		groupnode = model.get_value(iter, 1)	# 1 = groupnode
 | 
			
		||||
 | 
			
		||||
		gajim.connections[self.account].send_pb_subscribe(self.jid, groupnode, self._subscribeCB, groupnode)
 | 
			
		||||
		
 | 
			
		||||
	def on_unsubscribe_button_clicked(self, widget):
 | 
			
		||||
		'''Called when 'unsubscribe' button is pressed. Send unsubscription request.'''
 | 
			
		||||
		model, iter = self.window.services_treeview.get_selection().get_selected()
 | 
			
		||||
		if iter is None: return
 | 
			
		||||
 | 
			
		||||
		groupnode = model.get_value(iter, 1)    # 1 = groupnode
 | 
			
		||||
		
 | 
			
		||||
		gajim.connections[self.account].send_pb_unsubscribe(self.jid, groupnode, self._unsubscribeCB, groupnode)
 | 
			
		||||
 | 
			
		||||
	def _subscriptionsCB(self, conn, request):
 | 
			
		||||
		''' We got the subscribed groups list stanza. Now, if we already
 | 
			
		||||
		have items on the list, we should actualize them. '''
 | 
			
		||||
		print 0
 | 
			
		||||
		try:
 | 
			
		||||
			subscriptions = request.getTag('pubsub').getTag('subscriptions')
 | 
			
		||||
		except:
 | 
			
		||||
			return 
 | 
			
		||||
 | 
			
		||||
		print 1
 | 
			
		||||
		groups = set()
 | 
			
		||||
		for child in subscriptions.getTags('subscription'):
 | 
			
		||||
			print 2, repr(child), str(child)
 | 
			
		||||
			groups.add(child['node'])
 | 
			
		||||
		print 3, groups
 | 
			
		||||
 | 
			
		||||
		self.subscriptions = groups
 | 
			
		||||
 | 
			
		||||
		# try to setup existing items in model
 | 
			
		||||
		model = self.window.services_treeview.get_model()
 | 
			
		||||
		print 4
 | 
			
		||||
		for row in model:
 | 
			
		||||
			print 5
 | 
			
		||||
			# 1 = group node
 | 
			
		||||
			# 3 = insensitive checkbox for subscribed
 | 
			
		||||
			# 4 = subscribed?
 | 
			
		||||
			groupnode = row[1]
 | 
			
		||||
			row[3]=False
 | 
			
		||||
			row[4]=groupnode in groups
 | 
			
		||||
		print 6
 | 
			
		||||
 | 
			
		||||
		# we now know subscriptions, update button states
 | 
			
		||||
		self.update_actions()
 | 
			
		||||
 | 
			
		||||
		raise xmpp.NodeProcessed
 | 
			
		||||
 | 
			
		||||
	def _subscribeCB(self, conn, request, groupnode):
 | 
			
		||||
		'''We have just subscribed to a node. Update UI'''
 | 
			
		||||
		self.subscriptions.add(groupnode)
 | 
			
		||||
 | 
			
		||||
		model = self.window.services_treeview.get_model()
 | 
			
		||||
		for row in model:
 | 
			
		||||
			if row[1] == groupnode: # 1 = groupnode
 | 
			
		||||
				row[4]=True
 | 
			
		||||
				break
 | 
			
		||||
 | 
			
		||||
		self.update_actions()
 | 
			
		||||
 | 
			
		||||
		raise xmpp.NodeProcessed
 | 
			
		||||
 | 
			
		||||
	def _unsubscribeCB(self, conn, request, groupnode):
 | 
			
		||||
		'''We have just unsubscribed from a node. Update UI'''
 | 
			
		||||
		self.subscriptions.remove(groupnode)
 | 
			
		||||
 | 
			
		||||
		model = self.window.services_treeview.get_model()
 | 
			
		||||
		for row in model:
 | 
			
		||||
			if row[1] == groupnode: # 1 = groupnode
 | 
			
		||||
				row[4]=False
 | 
			
		||||
				break
 | 
			
		||||
 | 
			
		||||
		self.update_actions()
 | 
			
		||||
 | 
			
		||||
		raise xmpp.NodeProcessed
 | 
			
		||||
 | 
			
		||||
# Fill the global agent type info dictionary
 | 
			
		||||
_agent_type_info = _gen_agent_type_info()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										11
									
								
								src/gajim.py
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								src/gajim.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -26,6 +26,7 @@ from common import i18n
 | 
			
		|||
import message_control
 | 
			
		||||
 | 
			
		||||
from chat_control import ChatControlBase
 | 
			
		||||
from atom_window import AtomWindow
 | 
			
		||||
 | 
			
		||||
from common import exceptions
 | 
			
		||||
from common.zeroconf import connection_zeroconf
 | 
			
		||||
| 
						 | 
				
			
			@ -1405,6 +1406,10 @@ class Interface:
 | 
			
		|||
	def handle_event_metacontacts(self, account, tags_list):
 | 
			
		||||
		gajim.contacts.define_metacontacts(account, tags_list)
 | 
			
		||||
 | 
			
		||||
	def handle_atom_entry(self, account, data):
 | 
			
		||||
		atom_entry, = data
 | 
			
		||||
		AtomWindow.newAtomEntry(atom_entry)
 | 
			
		||||
 | 
			
		||||
	def handle_event_privacy_lists_received(self, account, data):
 | 
			
		||||
		# ('PRIVACY_LISTS_RECEIVED', account, list)
 | 
			
		||||
		if not self.instances.has_key(account):
 | 
			
		||||
| 
						 | 
				
			
			@ -1749,6 +1754,7 @@ class Interface:
 | 
			
		|||
			'ASK_NEW_NICK': self.handle_event_ask_new_nick,
 | 
			
		||||
			'SIGNED_IN': self.handle_event_signed_in,
 | 
			
		||||
			'METACONTACTS': self.handle_event_metacontacts,
 | 
			
		||||
			'ATOM_ENTRY': self.handle_atom_entry,
 | 
			
		||||
			'PRIVACY_LISTS_RECEIVED': self.handle_event_privacy_lists_received,
 | 
			
		||||
			'PRIVACY_LIST_RECEIVED': self.handle_event_privacy_list_received,
 | 
			
		||||
			'PRIVACY_LISTS_ACTIVE_DEFAULT': \
 | 
			
		||||
| 
						 | 
				
			
			@ -1963,10 +1969,9 @@ class Interface:
 | 
			
		|||
 | 
			
		||||
		self.show_vcard_when_connect = []
 | 
			
		||||
 | 
			
		||||
		path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'gajim.png')
 | 
			
		||||
		path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps/gajim.png')
 | 
			
		||||
		pix = gtk.gdk.pixbuf_new_from_file(path_to_file)
 | 
			
		||||
		# set the icon to all newly opened windows
 | 
			
		||||
		gtk.window_set_default_icon(pix)
 | 
			
		||||
		gtk.window_set_default_icon(pix) # set the icon to all newly opened windows
 | 
			
		||||
		self.roster.window.set_icon_from_file(path_to_file) # and to roster window
 | 
			
		||||
		self.sleeper = common.sleepy.Sleepy(
 | 
			
		||||
			gajim.config.get('autoawaytime') * 60, # make minutes to seconds
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								src/groups.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/groups.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
'''Window to create new post for discussion groups service.'''
 | 
			
		||||
 | 
			
		||||
import gtk
 | 
			
		||||
 | 
			
		||||
from common import gajim, xmpp
 | 
			
		||||
import gtkgui_helpers
 | 
			
		||||
 | 
			
		||||
class GroupsPostWindow:
 | 
			
		||||
	def __init__(self, account, servicejid, groupid):
 | 
			
		||||
		'''Open new 'create post' window to create message for groupid on servicejid service.'''
 | 
			
		||||
		assert isinstance(servicejid, basestring)
 | 
			
		||||
		assert isinstance(groupid, basestring)
 | 
			
		||||
 | 
			
		||||
		self.account = account
 | 
			
		||||
		self.servicejid = servicejid
 | 
			
		||||
		self.groupid = groupid
 | 
			
		||||
 | 
			
		||||
		self.xml = gtkgui_helpers.get_glade('groups_post_window.glade')
 | 
			
		||||
		self.window = self.xml.get_widget('groups_post_window')
 | 
			
		||||
		for name in ('from_entry', 'subject_entry', 'contents_textview'):
 | 
			
		||||
			self.__dict__[name] = self.xml.get_widget(name)
 | 
			
		||||
 | 
			
		||||
		self.xml.signal_autoconnect(self)
 | 
			
		||||
		self.window.show_all()
 | 
			
		||||
 | 
			
		||||
	def on_cancel_button_clicked(self, w):
 | 
			
		||||
		'''Close window.'''
 | 
			
		||||
		self.window.destroy()
 | 
			
		||||
 | 
			
		||||
	def on_send_button_clicked(self, w):
 | 
			
		||||
		'''Gather info from widgets and send it as a message.'''
 | 
			
		||||
		# constructing item to publish... that's atom:entry element
 | 
			
		||||
		item = xmpp.Node('entry', {'xmlns':'http://www.w3.org/2005/Atom'})
 | 
			
		||||
		author = item.addChild('author')
 | 
			
		||||
		author.addChild('name', {}, [self.from_entry.get_text()])
 | 
			
		||||
		item.addChild('generator', {}, ['Gajim'])
 | 
			
		||||
		item.addChild('title', {}, [self.subject_entry.get_text()])
 | 
			
		||||
		item.addChild('id', {}, ['0'])
 | 
			
		||||
 | 
			
		||||
		buffer = self.contents_textview.get_buffer()
 | 
			
		||||
		item.addChild('content', {}, [buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter())])
 | 
			
		||||
 | 
			
		||||
		# publish it to node
 | 
			
		||||
		gajim.connections[self.account].send_pb_publish(self.servicejid, self.groupid, item, '0')
 | 
			
		||||
 | 
			
		||||
		# close the window
 | 
			
		||||
		self.window.destroy()
 | 
			
		||||
| 
						 | 
				
			
			@ -658,9 +658,11 @@ def get_state_image_from_file_path_show(file_path, show):
 | 
			
		|||
def get_possible_button_event(event):
 | 
			
		||||
	'''mouse or keyboard caused the event?'''
 | 
			
		||||
	if event.type == gtk.gdk.KEY_PRESS:
 | 
			
		||||
		return 0 # no event.button so pass 0
 | 
			
		||||
	# BUTTON_PRESS event, so pass event.button
 | 
			
		||||
	return event.button
 | 
			
		||||
		event_button = 0 # no event.button so pass 0
 | 
			
		||||
	else: # BUTTON_PRESS event, so pass event.button
 | 
			
		||||
		event_button = event.button
 | 
			
		||||
 | 
			
		||||
	return event_button
 | 
			
		||||
 | 
			
		||||
def destroy_widget(widget):
 | 
			
		||||
	widget.destroy()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ import gtkgui_helpers
 | 
			
		|||
import cell_renderer_image
 | 
			
		||||
import tooltips
 | 
			
		||||
import message_control
 | 
			
		||||
import adhoc_commands
 | 
			
		||||
import notify
 | 
			
		||||
 | 
			
		||||
from common import gajim
 | 
			
		||||
| 
						 | 
				
			
			@ -54,8 +55,9 @@ C_NAME, # cellrenderer text that holds contact nickame
 | 
			
		|||
C_TYPE, # account, group or contact?
 | 
			
		||||
C_JID, # the jid of the row
 | 
			
		||||
C_ACCOUNT, # cellrenderer text that holds account name
 | 
			
		||||
C_EDITABLE, # cellrenderer text that holds name editable or not?
 | 
			
		||||
C_SECPIXBUF, # secondary_pixbuf (holds avatar or padlock)
 | 
			
		||||
) = range(6)
 | 
			
		||||
) = range(7)
 | 
			
		||||
 | 
			
		||||
class RosterWindow:
 | 
			
		||||
	'''Class for main window of the GTK+ interface'''
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +166,7 @@ class RosterWindow:
 | 
			
		|||
		if self.regroup:
 | 
			
		||||
			show = helpers.get_global_show()
 | 
			
		||||
			model.append(None, [self.jabber_state_images['16'][show],
 | 
			
		||||
				_('Merged accounts'), 'account', '', 'all', None])
 | 
			
		||||
				_('Merged accounts'), 'account', '', 'all', False, None])
 | 
			
		||||
			self.draw_account(account)
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +181,7 @@ class RosterWindow:
 | 
			
		|||
 | 
			
		||||
		model.append(None, [self.jabber_state_images['16'][show],
 | 
			
		||||
			gtkgui_helpers.escape_for_pango_markup(account),
 | 
			
		||||
			'account', our_jid, account, tls_pixbuf])
 | 
			
		||||
			'account', our_jid, account, False, tls_pixbuf])
 | 
			
		||||
 | 
			
		||||
	def draw_account(self, account):
 | 
			
		||||
		model = self.tree.get_model()
 | 
			
		||||
| 
						 | 
				
			
			@ -312,7 +314,8 @@ class RosterWindow:
 | 
			
		|||
				name = contact.get_shown_name()
 | 
			
		||||
				for i in parent_iters:
 | 
			
		||||
					# we add some values here. see draw_contact for more
 | 
			
		||||
					model.append(i, (None, name, 'contact', jid, account, None))
 | 
			
		||||
					model.append(i, (None, name, 'contact', jid, account,
 | 
			
		||||
						False, None))
 | 
			
		||||
				self.draw_contact(jid, account)
 | 
			
		||||
				self.draw_avatar(jid, account)
 | 
			
		||||
				# Redraw parent to change icon
 | 
			
		||||
| 
						 | 
				
			
			@ -345,7 +348,7 @@ class RosterWindow:
 | 
			
		|||
				iterG = model.append(IterAcct, [
 | 
			
		||||
					self.jabber_state_images['16']['closed'],
 | 
			
		||||
					gtkgui_helpers.escape_for_pango_markup(group), 'group',
 | 
			
		||||
					group, account, None])
 | 
			
		||||
					group, account, False, None])
 | 
			
		||||
				self.draw_group(group, account)
 | 
			
		||||
				if model.iter_n_children(IterAcct) == 1: # We added the first one
 | 
			
		||||
					self.draw_account(account)
 | 
			
		||||
| 
						 | 
				
			
			@ -364,7 +367,8 @@ class RosterWindow:
 | 
			
		|||
 | 
			
		||||
			name = contact.get_shown_name()
 | 
			
		||||
			# we add some values here. see draw_contact for more
 | 
			
		||||
			model.append(iterG, (None, name, typestr, contact.jid, account, None))
 | 
			
		||||
			model.append(iterG, (None, name, typestr, contact.jid, account,
 | 
			
		||||
				False, None))
 | 
			
		||||
 | 
			
		||||
			if gajim.groups[account][group]['expand']:
 | 
			
		||||
				self.tree.expand_row(model.get_path(iterG), False)
 | 
			
		||||
| 
						 | 
				
			
			@ -434,7 +438,7 @@ class RosterWindow:
 | 
			
		|||
		model = self.tree.get_model()
 | 
			
		||||
		iterAcct = self.get_account_iter(account)
 | 
			
		||||
		model.append(iterAcct, (None, gajim.nicks[account], 'self_contact', jid,
 | 
			
		||||
			account, None))
 | 
			
		||||
			account, False, None))
 | 
			
		||||
		self.draw_contact(jid, account)
 | 
			
		||||
		self.draw_avatar(jid, account)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1394,9 +1398,12 @@ class RosterWindow:
 | 
			
		|||
 | 
			
		||||
	def on_rename(self, widget, iter, path):
 | 
			
		||||
		# this function is called either by F2 or by Rename menuitem
 | 
			
		||||
		if gajim.interface.instances.has_key('rename'):
 | 
			
		||||
			gajim.interface.instances['rename'].dialog.window.present()
 | 
			
		||||
			return
 | 
			
		||||
		# to display that menuitem we show a menu, that does focus-out
 | 
			
		||||
		# we then select Rename and focus-in
 | 
			
		||||
		# focus-in callback checks on this var and if is NOT None
 | 
			
		||||
		# it redraws the selected contact resulting in stopping our rename
 | 
			
		||||
		# procedure. So set this to None to stop that
 | 
			
		||||
		self._last_selected_contact = []
 | 
			
		||||
		model = self.tree.get_model()
 | 
			
		||||
 | 
			
		||||
		row_type = model[iter][C_TYPE]
 | 
			
		||||
| 
						 | 
				
			
			@ -1407,66 +1414,16 @@ class RosterWindow:
 | 
			
		|||
			return
 | 
			
		||||
		if row_type in ('contact', 'agent'):
 | 
			
		||||
			# it's jid
 | 
			
		||||
			title = _('Rename Contact')
 | 
			
		||||
			message = _('Enter a new nickname for contact %s') % jid
 | 
			
		||||
			old_text = gajim.contacts.get_contact_with_highest_priority(account,
 | 
			
		||||
				jid).name
 | 
			
		||||
			# Remove possible resource indicator (Name (2))
 | 
			
		||||
			contact = gajim.contacts.get_first_contact_from_jid(account, jid)
 | 
			
		||||
			name = contact.name
 | 
			
		||||
			model[iter][C_NAME] = gtkgui_helpers.escape_for_pango_markup(name)
 | 
			
		||||
		elif row_type == 'group':
 | 
			
		||||
			if jid in helpers.special_groups + (_('General'),):
 | 
			
		||||
				return
 | 
			
		||||
			old_text = model[iter][C_JID].decode('utf-8')
 | 
			
		||||
			title = _('Rename Group')
 | 
			
		||||
			message = _('Enter a new name for group %s') % old_text
 | 
			
		||||
 | 
			
		||||
		def on_renamed(new_text, account, row_type, jid, old_text):
 | 
			
		||||
			if gajim.interface.instances.has_key('rename'):
 | 
			
		||||
				del gajim.interface.instances['rename']
 | 
			
		||||
			if row_type in ('contact', 'agent'):
 | 
			
		||||
				if old_text == new_text:
 | 
			
		||||
					return
 | 
			
		||||
				for u in gajim.contacts.get_contact(account, jid):
 | 
			
		||||
					u.name = new_text
 | 
			
		||||
				gajim.connections[account].update_contact(jid, new_text, u.groups)
 | 
			
		||||
				self.draw_contact(jid, account)
 | 
			
		||||
				# Update opened chat
 | 
			
		||||
				ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
 | 
			
		||||
				if ctrl:
 | 
			
		||||
					ctrl.update_ui()
 | 
			
		||||
					win = gajim.interface.msg_win_mgr.get_window(jid, account)
 | 
			
		||||
					win.redraw_tab(ctrl)
 | 
			
		||||
					win.show_title()
 | 
			
		||||
			elif row_type == 'group':
 | 
			
		||||
				# in C_JID column, we hold the group name (which is not escaped)
 | 
			
		||||
				if old_text == new_text:
 | 
			
		||||
					return
 | 
			
		||||
				# Groups may not change name from or to a special groups
 | 
			
		||||
				for g in helpers.special_groups:
 | 
			
		||||
					if g in (new_text, old_text):
 | 
			
		||||
						return
 | 
			
		||||
				# get all contacts in that group
 | 
			
		||||
				for jid in gajim.contacts.get_jid_list(account):
 | 
			
		||||
					contact = gajim.contacts.get_contact_with_highest_priority(
 | 
			
		||||
						account, jid)
 | 
			
		||||
					if old_text in contact.groups:
 | 
			
		||||
						# set them in the new one and remove it from the old
 | 
			
		||||
						contact.groups.remove(old_text)
 | 
			
		||||
						self.remove_contact(contact, account)
 | 
			
		||||
						if new_text not in contact.groups:
 | 
			
		||||
							contact.groups.append(new_text)
 | 
			
		||||
						self.add_contact_to_roster(contact.jid, account)
 | 
			
		||||
						gajim.connections[account].update_contact(contact.jid,
 | 
			
		||||
							contact.name, contact.groups)
 | 
			
		||||
				# If last removed iter was not visible, gajim.groups is not cleaned
 | 
			
		||||
				if gajim.groups[account].has_key(old_text):
 | 
			
		||||
					del gajim.groups[account][old_text]
 | 
			
		||||
 | 
			
		||||
		def on_canceled():
 | 
			
		||||
			if gajim.interface.instances.has_key('rename'):
 | 
			
		||||
				del gajim.interface.instances['rename']
 | 
			
		||||
			
 | 
			
		||||
		gajim.interface.instances['rename'] = dialogs.InputDialog(title, message,
 | 
			
		||||
			old_text, False, (on_renamed, account, row_type, jid, old_text),
 | 
			
		||||
			on_canceled)
 | 
			
		||||
		model[iter][C_EDITABLE] = True # set 'editable' to True
 | 
			
		||||
		self.tree.set_cursor(path, self.tree.get_column(0), True)
 | 
			
		||||
 | 
			
		||||
	def on_remove_group_item_activated(self, widget, group, account):
 | 
			
		||||
		dlg = dialogs.ConfirmationDialogCheck(_('Remove Group'),
 | 
			
		||||
| 
						 | 
				
			
			@ -1690,6 +1647,8 @@ class RosterWindow:
 | 
			
		|||
			'assign_openpgp_key_menuitem')
 | 
			
		||||
		add_special_notification_menuitem = xml.get_widget(
 | 
			
		||||
			'add_special_notification_menuitem')
 | 
			
		||||
		execute_command_menuitem = xml.get_widget(
 | 
			
		||||
			'execute_command_menuitem')
 | 
			
		||||
		
 | 
			
		||||
		add_special_notification_menuitem.hide()
 | 
			
		||||
		add_special_notification_menuitem.set_no_show_all(True)
 | 
			
		||||
| 
						 | 
				
			
			@ -1721,25 +1680,41 @@ class RosterWindow:
 | 
			
		|||
 | 
			
		||||
		contacts = gajim.contacts.get_contact(account, jid)
 | 
			
		||||
		if len(contacts) > 1: # sevral resources
 | 
			
		||||
			sub_menu = gtk.Menu()
 | 
			
		||||
			start_chat_menuitem.set_submenu(sub_menu)
 | 
			
		||||
			def resources_submenu(action):
 | 
			
		||||
				""" Build a submenu with contact's resources. """
 | 
			
		||||
				sub_menu = gtk.Menu()
 | 
			
		||||
 | 
			
		||||
			iconset = gajim.config.get('iconset')
 | 
			
		||||
			path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
 | 
			
		||||
			for c in contacts:
 | 
			
		||||
				# icon MUST be different instance for every item
 | 
			
		||||
				state_images = self.load_iconset(path)
 | 
			
		||||
				item = gtk.ImageMenuItem(c.resource + ' (' + str(c.priority) + ')')
 | 
			
		||||
				icon_name = helpers.get_icon_name_to_show(c, account)
 | 
			
		||||
				icon = state_images[icon_name]
 | 
			
		||||
				item.set_image(icon)
 | 
			
		||||
				sub_menu.append(item)
 | 
			
		||||
				item.connect('activate', self.on_open_chat_window, c, account,
 | 
			
		||||
					c.resource)
 | 
			
		||||
				iconset = gajim.config.get('iconset')
 | 
			
		||||
				if not iconset:
 | 
			
		||||
					iconset = DEFAULT_ICONSET
 | 
			
		||||
				path = os.path.join(gajim.DATA_DIR, 'iconsets', iconset, '16x16')
 | 
			
		||||
				for c in contacts:
 | 
			
		||||
					# icon MUST be different instance for every item
 | 
			
		||||
					state_images = self.load_iconset(path)
 | 
			
		||||
					item = gtk.ImageMenuItem(c.resource + ' (' + str(c.priority) + ')')
 | 
			
		||||
					icon_name = helpers.get_icon_name_to_show(c, account)
 | 
			
		||||
					icon = state_images[icon_name]
 | 
			
		||||
					item.set_image(icon)
 | 
			
		||||
					sub_menu.append(item)
 | 
			
		||||
					item.connect('activate', action, c, account,
 | 
			
		||||
						c.resource)
 | 
			
		||||
				return sub_menu
 | 
			
		||||
 | 
			
		||||
			start_chat_menuitem.set_submenu(resources_submenu(self.on_open_chat_window))
 | 
			
		||||
			execute_command_menuitem.set_submenu(resources_submenu(self.on_execute_command))
 | 
			
		||||
 | 
			
		||||
		else: # one resource
 | 
			
		||||
			start_chat_menuitem.connect('activate',
 | 
			
		||||
				self.on_roster_treeview_row_activated, tree_path)
 | 
			
		||||
				self.on_open_chat_window, contact, account)
 | 
			
		||||
			# we cannot execute commands when the resource is unknown
 | 
			
		||||
			# TODO: that's true only if the entity is a contact,
 | 
			
		||||
			# TODO: we need to show this also for transports
 | 
			
		||||
			if contact.resource:
 | 
			
		||||
				execute_command_menuitem.connect('activate',
 | 
			
		||||
					self.on_execute_command, contact, account, contact.resource)
 | 
			
		||||
			else:
 | 
			
		||||
				execute_command_menuitem.hide()
 | 
			
		||||
				execute_command_menuitem.set_no_show_all(True)
 | 
			
		||||
 | 
			
		||||
		if contact.resource:
 | 
			
		||||
			send_file_menuitem.connect('activate',
 | 
			
		||||
| 
						 | 
				
			
			@ -1846,7 +1821,7 @@ class RosterWindow:
 | 
			
		|||
			for widget in [start_chat_menuitem, send_single_message_menuitem,
 | 
			
		||||
			rename_menuitem, edit_groups_menuitem, send_file_menuitem,
 | 
			
		||||
			subscription_menuitem, add_to_roster_menuitem,
 | 
			
		||||
			remove_from_roster_menuitem]:
 | 
			
		||||
			remove_from_roster_menuitem, execute_command_menuitem]:
 | 
			
		||||
				widget.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
			
		||||
| 
						 | 
				
			
			@ -2094,7 +2069,16 @@ class RosterWindow:
 | 
			
		|||
		item.connect('activate', self.on_edit_agent, contact, account)
 | 
			
		||||
		if gajim.account_is_disconnected(account):
 | 
			
		||||
			item.set_sensitive(False)
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		item = gtk.ImageMenuItem(_('Execute Command...'))
 | 
			
		||||
		icon = gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
 | 
			
		||||
		item.set_image(icon)
 | 
			
		||||
		menu.append(item)
 | 
			
		||||
		item.connect('activate', self.on_execute_command, contact, account,
 | 
			
		||||
			contact.resource)
 | 
			
		||||
		if not is_connected:
 | 
			
		||||
			item.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
		item = gtk.ImageMenuItem(_('_Rename'))
 | 
			
		||||
		# add a special img for rename menuitem
 | 
			
		||||
		path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
 | 
			
		||||
| 
						 | 
				
			
			@ -2167,6 +2151,7 @@ class RosterWindow:
 | 
			
		|||
			new_message_menuitem = xml.get_widget('new_message_menuitem')
 | 
			
		||||
			add_contact_menuitem = xml.get_widget('add_contact_menuitem')
 | 
			
		||||
			service_discovery_menuitem = xml.get_widget('service_discovery_menuitem')
 | 
			
		||||
			execute_command_menuitem = xml.get_widget('execute_command_menuitem')
 | 
			
		||||
			edit_account_menuitem = xml.get_widget('edit_account_menuitem')
 | 
			
		||||
			sub_menu = gtk.Menu()
 | 
			
		||||
			status_menuitem.set_submenu(sub_menu)
 | 
			
		||||
| 
						 | 
				
			
			@ -2210,6 +2195,10 @@ class RosterWindow:
 | 
			
		|||
			add_contact_menuitem.connect('activate', self.on_add_new_contact, account)
 | 
			
		||||
			service_discovery_menuitem.connect('activate',
 | 
			
		||||
				self.on_service_disco_menuitem_activate, account)
 | 
			
		||||
			hostname = gajim.config.get_per('accounts', account, 'hostname')
 | 
			
		||||
			contact = gajim.contacts.create_contact(jid = hostname) # Fake contact
 | 
			
		||||
			execute_command_menuitem.connect('activate',
 | 
			
		||||
				self.on_execute_command, contact, account)
 | 
			
		||||
			
 | 
			
		||||
			gc_sub_menu = gtk.Menu() # gc is always a submenu
 | 
			
		||||
			join_group_chat_menuitem.set_submenu(gc_sub_menu)
 | 
			
		||||
| 
						 | 
				
			
			@ -2220,7 +2209,8 @@ class RosterWindow:
 | 
			
		|||
			# make some items insensitive if account is offline
 | 
			
		||||
			if gajim.connections[account].connected < 2:
 | 
			
		||||
				for widget in [add_contact_menuitem, service_discovery_menuitem,
 | 
			
		||||
				join_group_chat_menuitem, new_message_menuitem]:
 | 
			
		||||
				join_group_chat_menuitem, new_message_menuitem,
 | 
			
		||||
				execute_command_menuitem]:
 | 
			
		||||
					widget.set_sensitive(False)
 | 
			
		||||
		else:
 | 
			
		||||
			xml = gtkgui_helpers.get_glade('zeroconf_context_menu.glade')
 | 
			
		||||
| 
						 | 
				
			
			@ -2360,7 +2350,10 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
	def on_roster_treeview_key_press_event(self, widget, event):
 | 
			
		||||
		'''when a key is pressed in the treeviews'''
 | 
			
		||||
		self.tooltip.hide_tooltip()
 | 
			
		||||
		if event.keyval == gtk.keysyms.Escape:
 | 
			
		||||
		if event.keyval == gtk.keysyms.Menu:
 | 
			
		||||
			self.show_treeview_menu(event)
 | 
			
		||||
			return True
 | 
			
		||||
		elif event.keyval == gtk.keysyms.Escape:
 | 
			
		||||
			self.tree.get_selection().unselect_all()
 | 
			
		||||
		elif event.keyval == gtk.keysyms.F2:
 | 
			
		||||
			treeselection = self.tree.get_selection()
 | 
			
		||||
| 
						 | 
				
			
			@ -2368,10 +2361,12 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
			if len(list_of_paths) != 1:
 | 
			
		||||
				return
 | 
			
		||||
			path = list_of_paths[0]
 | 
			
		||||
			type_ = model[path][C_TYPE]
 | 
			
		||||
			if type_ in ('contact', 'group', 'agent'):
 | 
			
		||||
				iter = model.get_iter(path)
 | 
			
		||||
				self.on_rename(widget, iter, path)
 | 
			
		||||
			type = model[path][C_TYPE]
 | 
			
		||||
			if type in ('contact', 'group', 'agent'):
 | 
			
		||||
				if not model[path][C_EDITABLE]:
 | 
			
		||||
					# we are NOT already renaming it
 | 
			
		||||
					iter = model.get_iter(path)
 | 
			
		||||
					self.on_rename(widget, iter, path)
 | 
			
		||||
 | 
			
		||||
		elif event.keyval == gtk.keysyms.Delete:
 | 
			
		||||
			treeselection = self.tree.get_selection()
 | 
			
		||||
| 
						 | 
				
			
			@ -3178,10 +3173,6 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
				self.tooltip.hide_tooltip()
 | 
			
		||||
				self.window.hide()
 | 
			
		||||
 | 
			
		||||
	def on_roster_window_popup_menu(self, widget):
 | 
			
		||||
		event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
 | 
			
		||||
		self.show_treeview_menu(event)
 | 
			
		||||
 | 
			
		||||
	def quit_gtkgui_interface(self):
 | 
			
		||||
		'''When we quit the gtk interface :
 | 
			
		||||
		tell that to the core and exit gtk'''
 | 
			
		||||
| 
						 | 
				
			
			@ -3298,6 +3289,15 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
			return True
 | 
			
		||||
		return False
 | 
			
		||||
 | 
			
		||||
	def on_execute_command(self, widget, contact, account, resource=None):
 | 
			
		||||
		'''Execute command. Full JID needed; if it is other contact,
 | 
			
		||||
		resource is necessary. Widget is unnecessary, only to be
 | 
			
		||||
		able to make this a callback.'''
 | 
			
		||||
		jid = contact.jid
 | 
			
		||||
		if resource is not None:
 | 
			
		||||
			jid = jid + u'/' + resource
 | 
			
		||||
		adhoc_commands.CommandWindow(account, jid)
 | 
			
		||||
 | 
			
		||||
	def on_open_chat_window(self, widget, contact, account, resource = None):
 | 
			
		||||
		# Get the window containing the chat
 | 
			
		||||
		fjid = contact.jid
 | 
			
		||||
| 
						 | 
				
			
			@ -3420,6 +3420,91 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
			account = model[iter][C_ACCOUNT].decode('utf-8')
 | 
			
		||||
			self.draw_contact(jid, account)
 | 
			
		||||
 | 
			
		||||
	def on_editing_started(self, cell, event, row):
 | 
			
		||||
		''' start editing a cell in the tree'''
 | 
			
		||||
		path = self.tree.get_cursor()[0]
 | 
			
		||||
		self.editing_path = path
 | 
			
		||||
 | 
			
		||||
	def on_editing_canceled(self, cell):
 | 
			
		||||
		'''editing has been canceled'''
 | 
			
		||||
		path = self.tree.get_cursor()[0]
 | 
			
		||||
		# do not set new name if row order has changed
 | 
			
		||||
		if path != self.editing_path:
 | 
			
		||||
			self.editing_path = None
 | 
			
		||||
			return
 | 
			
		||||
		self.editing_path = None
 | 
			
		||||
		model = self.tree.get_model()
 | 
			
		||||
		iter = model.get_iter(path)
 | 
			
		||||
		account = model[iter][C_ACCOUNT].decode('utf-8')
 | 
			
		||||
		jid = model[iter][C_JID].decode('utf-8')
 | 
			
		||||
		type = model[iter][C_TYPE]
 | 
			
		||||
		# restore the number of resources string at the end of contact name
 | 
			
		||||
		contacts = gajim.contacts.get_contact(account, jid)
 | 
			
		||||
		if type in ('contact', 'agent'):
 | 
			
		||||
			self.draw_contact(jid, account)
 | 
			
		||||
		# reset editable to False
 | 
			
		||||
		model[iter][C_EDITABLE] = False
 | 
			
		||||
 | 
			
		||||
	def on_cell_edited(self, cell, row, new_text):
 | 
			
		||||
		'''When an iter is edited:
 | 
			
		||||
		if text has changed, rename the contact'''
 | 
			
		||||
		model = self.tree.get_model()
 | 
			
		||||
		# if this is a last item in the group, row is invalid
 | 
			
		||||
		try:
 | 
			
		||||
			iter = model.get_iter_from_string(row)
 | 
			
		||||
		except:
 | 
			
		||||
			self.editing_path = None
 | 
			
		||||
			return
 | 
			
		||||
		path = model.get_path(iter)
 | 
			
		||||
		# do not set new name if row order has changed
 | 
			
		||||
		if path != self.editing_path:
 | 
			
		||||
			self.editing_path = None
 | 
			
		||||
			return
 | 
			
		||||
		self.editing_path = None
 | 
			
		||||
		new_text = new_text.decode('utf-8')
 | 
			
		||||
		account = model[iter][C_ACCOUNT].decode('utf-8')
 | 
			
		||||
		jid = model[iter][C_JID].decode('utf-8')
 | 
			
		||||
		type = model[iter][C_TYPE]
 | 
			
		||||
		model[iter][C_EDITABLE] = False
 | 
			
		||||
		if type in ('contact', 'agent'):
 | 
			
		||||
			old_text = gajim.contacts.get_contact_with_highest_priority(account,
 | 
			
		||||
				jid).name
 | 
			
		||||
			if old_text != new_text:
 | 
			
		||||
				for u in gajim.contacts.get_contact(account, jid):
 | 
			
		||||
					u.name = new_text
 | 
			
		||||
				gajim.connections[account].update_contact(jid, new_text, u.groups)
 | 
			
		||||
			self.draw_contact(jid, account)
 | 
			
		||||
			# Update opened chat
 | 
			
		||||
			ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
 | 
			
		||||
			if ctrl:
 | 
			
		||||
				ctrl.update_ui()
 | 
			
		||||
				win = gajim.interface.msg_win_mgr.get_window(jid, account)
 | 
			
		||||
				win.redraw_tab(ctrl)
 | 
			
		||||
				win.show_title()
 | 
			
		||||
		elif type == 'group':
 | 
			
		||||
			# in C_JID column, we hold the group name (which is not escaped)
 | 
			
		||||
			old_name = model[iter][C_JID].decode('utf-8')
 | 
			
		||||
			# Groups may not change name from or to a special groups
 | 
			
		||||
			for g in helpers.special_groups:
 | 
			
		||||
				if g in (new_text, old_name):
 | 
			
		||||
					return
 | 
			
		||||
			# get all contacts in that group
 | 
			
		||||
			for jid in gajim.contacts.get_jid_list(account):
 | 
			
		||||
				contact = gajim.contacts.get_contact_with_highest_priority(account,
 | 
			
		||||
					jid)
 | 
			
		||||
				if old_name in contact.groups:
 | 
			
		||||
					# set them in the new one and remove it from the old
 | 
			
		||||
					contact.groups.remove(old_name)
 | 
			
		||||
					self.remove_contact(contact, account)
 | 
			
		||||
					if not new_text in contact.groups:
 | 
			
		||||
						contact.groups.append(new_text)
 | 
			
		||||
					self.add_contact_to_roster(contact.jid, account)
 | 
			
		||||
					gajim.connections[account].update_contact(contact.jid,
 | 
			
		||||
						contact.name, contact.groups)
 | 
			
		||||
			# If last removed iter was not visible, gajim.groups is not cleaned
 | 
			
		||||
			if gajim.groups[account].has_key(old_name):
 | 
			
		||||
				del gajim.groups[account][old_name]
 | 
			
		||||
 | 
			
		||||
	def on_service_disco_menuitem_activate(self, widget, account):
 | 
			
		||||
		server_jid = gajim.config.get_per('accounts', account, 'hostname')
 | 
			
		||||
		if gajim.interface.instances[account]['disco'].has_key(server_jid):
 | 
			
		||||
| 
						 | 
				
			
			@ -4086,6 +4171,10 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
 | 
			
		||||
	def _on_treeview_selection_changed(self, selection):
 | 
			
		||||
		model, list_of_paths = selection.get_selected_rows()
 | 
			
		||||
		if len(list_of_paths) == 1 and model[list_of_paths[0]][C_EDITABLE]:
 | 
			
		||||
			# We are editing this row, do not modify self._last_selected_contact
 | 
			
		||||
			# Cause that cancel editing
 | 
			
		||||
			return
 | 
			
		||||
		if len(self._last_selected_contact):
 | 
			
		||||
			# update unselected rows
 | 
			
		||||
			for (jid, account) in self._last_selected_contact:
 | 
			
		||||
| 
						 | 
				
			
			@ -4154,7 +4243,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
		self.gpg_passphrase = {}
 | 
			
		||||
 | 
			
		||||
		#(icon, name, type, jid, account, editable, secondary_pixbuf)
 | 
			
		||||
		model = gtk.TreeStore(gtk.Image, str, str, str, str, gtk.gdk.Pixbuf)
 | 
			
		||||
		model = gtk.TreeStore(gtk.Image, str, str, str, str, bool, gtk.gdk.Pixbuf)
 | 
			
		||||
 | 
			
		||||
		model.set_sort_func(1, self.compareIters)
 | 
			
		||||
		model.set_sort_column_id(1, gtk.SORT_ASCENDING)
 | 
			
		||||
| 
						 | 
				
			
			@ -4251,8 +4340,12 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
 | 
			
		|||
		col.set_cell_data_func(render_image, self.iconCellDataFunc, None)
 | 
			
		||||
 | 
			
		||||
		render_text = gtk.CellRendererText() # contact or group or account name
 | 
			
		||||
		render_text.connect('edited', self.on_cell_edited)
 | 
			
		||||
		render_text.connect('editing-canceled', self.on_editing_canceled)
 | 
			
		||||
		render_text.connect('editing-started', self.on_editing_started)
 | 
			
		||||
		col.pack_start(render_text, expand = True)
 | 
			
		||||
		col.add_attribute(render_text, 'markup', C_NAME) # where we hold the name
 | 
			
		||||
		col.add_attribute(render_text, 'editable', C_EDITABLE) # where we hold if the row is editable
 | 
			
		||||
		col.set_cell_data_func(render_text, self.nameCellDataFunc, None)
 | 
			
		||||
 | 
			
		||||
		render_pixbuf = gtk.CellRendererPixbuf() # tls or avatar img
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue