← Back to team overview

do-plugins team mailing list archive

[Merge] lp:~kriomant/do-plugins/transmission into lp:do-plugins

 

kriomant has proposed merging lp:~kriomant/do-plugins/transmission into lp:do-plugins.

Requested reviews:
  Do Plugins Team (do-plugins)


Transmission plug-in.

Allows to control Transmission torrent client using XML-RPC API. Supports starting and stopping torrent downloads, choosing files to download, setting speed limits.
-- 
https://code.launchpad.net/~kriomant/do-plugins/transmission/+merge/30966
Your team Do Plugins Team is requested to review the proposed merge of lp:~kriomant/do-plugins/transmission into lp:do-plugins.
=== modified file '.bzrignore'
--- .bzrignore	2010-06-29 08:19:20 +0000
+++ .bzrignore	2010-07-26 16:47:47 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
 Makefile.in
 Makefile
 *.pc
@@ -23,3 +24,11 @@
 *.addin.xml
 *.gmo
 m4/intltool.m4
+=======
+Emesene.addin.xml
+Transmission/Makefile
+Transmission/Makefile.in
+Transmission/Transmission.pidb
+Transmission/bin
+Transmission/Resources/Transmission.addin.xml
+>>>>>>> MERGE-SOURCE

=== modified file 'DoPlugins.mds'
--- DoPlugins.mds	2009-11-08 23:58:01 +0000
+++ DoPlugins.mds	2010-07-26 16:47:47 +0000
@@ -78,6 +78,7 @@
       <Entry build="True" name="Baconator" configuration="Debug" />
       <Entry build="True" name="Chromium" configuration="Debug" />
       <Entry build="True" name="GNOME-Calculator" configuration="Debug" />
+      <Entry build="True" name="Transmission" configuration="Debug" />
     </Configuration>
     <Configuration name="Release" ctype="CombineConfiguration">
       <Entry build="True" name="Rhythmbox" configuration="Release" />
@@ -157,6 +158,7 @@
       <Entry build="True" name="Baconator" configuration="Release" />
       <Entry build="True" name="Chromium" configuration="Release" />
       <Entry build="True" name="GNOME-Calculator" configuration="Release" />
+      <Entry build="True" name="Transmission" configuration="Release" />
     </Configuration>
   </Configurations>
   <StartMode startupentry="Rhythmbox" single="True">
@@ -237,6 +239,7 @@
     <Execute type="None" entry="Baconator" />
     <Execute type="None" entry="Chromium" />
     <Execute type="None" entry="GNOME-Calculator" />
+    <Execute type="None" entry="Transmission" />
   </StartMode>
   <MonoDevelop.ChangeLogAddIn.ChangeLogInfo policy="UpdateNearestChangeLog" />
   <Entries>
@@ -317,5 +320,6 @@
     <Entry filename="Baconator/Baconator.mdp" />
     <Entry filename="Chromium/Chromium.mdp" />
     <Entry filename="GNOME-Calculator/GNOME-Calculator.mdp" />
+    <Entry filename="Transmission/Transmission.mdp" />
   </Entries>
 </Combine>
\ No newline at end of file

=== modified file 'Makefile.am'
--- Makefile.am	2009-11-08 23:58:01 +0000
+++ Makefile.am	2010-07-26 16:47:47 +0000
@@ -70,6 +70,7 @@
 	TinyUrl \
 	Tracker \
 	Translate \
+	Transmission \
 	Tomboy \
 	Vinagre \
 	VirtualBox \

=== added directory 'Transmission'
=== added file 'Transmission/Makefile.am'
--- Transmission/Makefile.am	1970-01-01 00:00:00 +0000
+++ Transmission/Makefile.am	2010-07-26 16:47:47 +0000
@@ -0,0 +1,40 @@
+include $(top_srcdir)/build.rules.mk
+
+ASSEMBLY=Transmission
+
+FILES = \
+  gtk-gui/Transmission.TransmissionConfig.cs \
+  gtk-gui/generated.cs \
+  src/Config/TransmissionConfig.cs \
+  src/ITorrentEntry.cs \
+  src/JsonCollectionImporter.cs \
+  src/Utils.cs \
+  src/TorrentDirectoryItem.cs \
+  src/TorrentFileItem.cs \
+  src/TorrentFileSetPriorityAction.cs \
+  src/TorrentItem.cs \
+  src/TorrentItemSource.cs \
+  src/TorrentAbstractLimitSpeedAction.cs \
+  src/TorrentLimitDownloadSpeedAction.cs \
+  src/TorrentLimitUploadSpeedAction.cs \
+  src/TorrentMarkForDownloadAction.cs \
+  src/TorrentStartAction.cs \
+  src/TorrentStopAction.cs \
+  src/TorrentUnmarkForDownloadAction.cs \
+  src/TorrentVerifyAction.cs \
+  src/TorrentOperateAction.cs \
+  src/TransmissionAPI.cs \
+  src/TransmissionPlugin.cs
+
+RESOURCES = \
+  Resources/Transmission.addin.xml \
+  gtk-gui/gui.stetic \
+  Resources/icons/transmission.png
+
+REFERENCES = \
+  System \
+  System.Core \
+  lib/Jayrock.Json.dll \
+  $(DO_PLATFORM_LINUX_LIBS) \
+  $(DO_UNIVERSE_LIBS) \
+  $(GTK_SHARP_20_LIBS)

=== added directory 'Transmission/Resources'
=== added file 'Transmission/Resources/Transmission.addin.xml.in'
--- Transmission/Resources/Transmission.addin.xml.in	1970-01-01 00:00:00 +0000
+++ Transmission/Resources/Transmission.addin.xml.in	2010-07-26 16:47:47 +0000
@@ -0,0 +1,37 @@
+<Addin
+	id="Transmission"
+	namespace="Do"
+	version="0.1"
+	name="Transmission"
+	description="Control Transmission torrent client."
+	author="Mikhail Trishchenkov"
+	category="Community"
+	defaultEnabled="true"
+    url="http://do.davebsd.com/wiki/Transmission_Plugin";
+	>
+
+	<Runtime>
+		<Import assembly="Transmission.dll"/>
+	</Runtime>
+
+	<!--Localizer type="Gettext" catalog="gnome-do-plugins" location="@expanded_datadir@/locale" /-->
+
+	<Dependencies>
+		<Addin id="Universe" version="1.0" />
+	</Dependencies>
+
+	<Extension path="/Do/ItemSource">
+		<ItemSource type="Transmission.TorrentItemSource" />
+	</Extension>
+	<Extension path="/Do/Action">
+		<Action type="Transmission.TorrentStopAction" />
+		<Action type="Transmission.TorrentStartAction" />
+		<Action type="Transmission.TorrentVerifyAction" />
+		<Action type="Transmission.TorrentLimitDownloadSpeedAction" />
+		<Action type="Transmission.TorrentLimitUploadSpeedAction" />
+		<Action type="Transmission.TorrentFileSetPriorityAction" />
+		<Action type="Transmission.TorrentMarkForDownloadAction" />
+		<Action type="Transmission.TorrentUnmarkForDownloadAction" />
+		<Action type="Transmission.TorrentOperateAction" />
+	</Extension>
+</Addin>

=== added directory 'Transmission/Resources/icons'
=== added file 'Transmission/Resources/icons/transmission.png'
Binary files Transmission/Resources/icons/transmission.png	1970-01-01 00:00:00 +0000 and Transmission/Resources/icons/transmission.png	2010-07-26 16:47:47 +0000 differ
=== added file 'Transmission/Transmission.mdp'
--- Transmission/Transmission.mdp	1970-01-01 00:00:00 +0000
+++ Transmission/Transmission.mdp	2010-07-26 16:47:47 +0000
@@ -0,0 +1,64 @@
+<Project name="Transmission" fileversion="2.0" DefaultNamespace="Transmission" language="C#" targetFramework="3.5" ctype="DotNetProject">
+  <Configurations active="Debug">
+    <Configuration name="Debug" ctype="DotNetProjectConfiguration">
+      <Output directory="bin/Debug" assembly="Transmission" />
+      <Build debugmode="True" target="Library" />
+      <Execution consolepause="True" runwithwarnings="True" runtime="MsNet" />
+      <CodeGeneration compiler="Mcs" warninglevel="4" optimize="False" unsafecodeallowed="False" generateoverflowchecks="False" definesymbols="DEBUG" generatexmldocumentation="False" ctype="CSharpCompilerParameters" />
+    </Configuration>
+    <Configuration name="Release" ctype="DotNetProjectConfiguration">
+      <Output directory="bin/Release" assembly="Transmission" />
+      <Build debugmode="False" target="Library" />
+      <Execution consolepause="True" runwithwarnings="True" runtime="MsNet" />
+      <CodeGeneration compiler="Mcs" warninglevel="4" optimize="False" unsafecodeallowed="False" generateoverflowchecks="False" generatexmldocumentation="False" ctype="CSharpCompilerParameters" />
+    </Configuration>
+  </Configurations>
+  <Contents>
+    <File subtype="Directory" buildaction="Compile" name="src" />
+    <File subtype="Directory" buildaction="Compile" name="Resources" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/Transmission.addin.xml" />
+    <File subtype="Code" buildaction="EmbedAsResource" name="gtk-gui/gui.stetic" />
+    <File subtype="Code" buildaction="Compile" name="gtk-gui/generated.cs" />
+    <File subtype="Code" buildaction="Compile" name="gtk-gui/Transmission.TransmissionConfig.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/Config/TransmissionConfig.cs" />
+    <File subtype="Directory" buildaction="Compile" name="Resources/icons" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-add.png" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-remove.png" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-revisions.png" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-share.png" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-start.png" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-stop.png" />
+    <File subtype="Code" buildaction="Nothing" name="Resources/icons/transmission-web.png" />
+    <File subtype="Code" buildaction="Compile" name="src/ITorrentEntry.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/JsonCollectionImporter.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentDirectoryItem.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentFileItem.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentFileSetPriorityAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentItem.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentItemSource.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentAbstractLimitSpeedAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentLimitDownloadSpeedAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentMarkForDownloadAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentStartAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentStopAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentUnmarkForDownloadAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentVerifyAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TransmissionAPI.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TransmissionPlugin.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/Utils.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentLimitUploadSpeedAction.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/TorrentOperateAction.cs" />
+  </Contents>
+  <References>
+    <ProjectReference type="Gac" localcopy="True" refto="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+    <ProjectReference type="Gac" localcopy="True" refto="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+    <ProjectReference type="Gac" localcopy="True" refto="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+    <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+    <ProjectReference type="Gac" localcopy="True" refto="Mono.Addins, Version=0.4.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
+    <ProjectReference type="Gac" localcopy="True" refto="Do.Platform, Version=0.9.0.0, Culture=neutral" />
+    <ProjectReference type="Gac" localcopy="True" refto="Do.Universe, Version=0.9.0.0, Culture=neutral" />
+    <ProjectReference type="Gac" localcopy="True" refto="Do.Platform.Linux, Version=0.9.0.0, Culture=neutral" />
+    <ProjectReference type="Assembly" localcopy="True" specificVersion="False" refto="lib/Jayrock.Json.dll" />
+  </References>
+  <GtkDesignInfo gettextClass="Mono.Addins.AddinManager.CurrentLocalizer" />
+</Project>
\ No newline at end of file

=== added directory 'Transmission/gtk-gui'
=== added file 'Transmission/gtk-gui/Transmission.TransmissionConfig.cs'
--- Transmission/gtk-gui/Transmission.TransmissionConfig.cs	1970-01-01 00:00:00 +0000
+++ Transmission/gtk-gui/Transmission.TransmissionConfig.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,161 @@
+// ------------------------------------------------------------------------------
+//  <autogenerated>
+//      This code was generated by a tool.
+//      
+// 
+//      Changes to this file may cause incorrect behavior and will be lost if 
+//      the code is regenerated.
+//  </autogenerated>
+// ------------------------------------------------------------------------------
+
+namespace Transmission {
+    
+    
+    public partial class TransmissionConfig {
+        
+        private Gtk.Alignment alignment108;
+        
+        private Gtk.Table table1;
+        
+        private Gtk.HBox hbox1;
+        
+        private Gtk.Entry address_entry;
+        
+        private Gtk.Label remote_port_label;
+        
+        private Gtk.Entry port_entry;
+        
+        private Gtk.Label label1;
+        
+        private Gtk.Label label2;
+        
+        private Gtk.Label label3;
+        
+        private Gtk.Entry password_entry;
+        
+        private Gtk.Entry user_name_entry;
+        
+        protected virtual void Build() {
+            Stetic.Gui.Initialize(this);
+            // Widget Transmission.TransmissionConfig
+            Stetic.BinContainer.Attach(this);
+            this.Name = "Transmission.TransmissionConfig";
+            // Container child Transmission.TransmissionConfig.Gtk.Container+ContainerChild
+            this.alignment108 = new Gtk.Alignment(0.5F, 0F, 1F, 0F);
+            this.alignment108.Name = "alignment108";
+            // Container child alignment108.Gtk.Container+ContainerChild
+            this.table1 = new Gtk.Table(((uint)(3)), ((uint)(2)), false);
+            this.table1.Name = "table1";
+            this.table1.RowSpacing = ((uint)(6));
+            this.table1.ColumnSpacing = ((uint)(6));
+            // Container child table1.Gtk.Table+TableChild
+            this.hbox1 = new Gtk.HBox();
+            this.hbox1.Name = "hbox1";
+            this.hbox1.Spacing = 6;
+            // Container child hbox1.Gtk.Box+BoxChild
+            this.address_entry = new Gtk.Entry();
+            this.address_entry.CanFocus = true;
+            this.address_entry.Name = "address_entry";
+            this.address_entry.IsEditable = true;
+            this.address_entry.InvisibleChar = '●';
+            this.hbox1.Add(this.address_entry);
+            Gtk.Box.BoxChild w1 = ((Gtk.Box.BoxChild)(this.hbox1[this.address_entry]));
+            w1.Position = 0;
+            // Container child hbox1.Gtk.Box+BoxChild
+            this.remote_port_label = new Gtk.Label();
+            this.remote_port_label.Name = "remote_port_label";
+            this.remote_port_label.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("Port");
+            this.hbox1.Add(this.remote_port_label);
+            Gtk.Box.BoxChild w2 = ((Gtk.Box.BoxChild)(this.hbox1[this.remote_port_label]));
+            w2.Position = 1;
+            w2.Expand = false;
+            w2.Fill = false;
+            // Container child hbox1.Gtk.Box+BoxChild
+            this.port_entry = new Gtk.Entry();
+            this.port_entry.CanFocus = true;
+            this.port_entry.Name = "port_entry";
+            this.port_entry.IsEditable = true;
+            this.port_entry.WidthChars = 5;
+            this.port_entry.MaxLength = 5;
+            this.port_entry.InvisibleChar = '●';
+            this.hbox1.Add(this.port_entry);
+            Gtk.Box.BoxChild w3 = ((Gtk.Box.BoxChild)(this.hbox1[this.port_entry]));
+            w3.Position = 2;
+            w3.Expand = false;
+            this.table1.Add(this.hbox1);
+            Gtk.Table.TableChild w4 = ((Gtk.Table.TableChild)(this.table1[this.hbox1]));
+            w4.LeftAttach = ((uint)(1));
+            w4.RightAttach = ((uint)(2));
+            w4.YOptions = ((Gtk.AttachOptions)(4));
+            // Container child table1.Gtk.Table+TableChild
+            this.label1 = new Gtk.Label();
+            this.label1.Name = "label1";
+            this.label1.Xalign = 0F;
+            this.label1.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("_Address");
+            this.label1.UseUnderline = true;
+            this.table1.Add(this.label1);
+            Gtk.Table.TableChild w5 = ((Gtk.Table.TableChild)(this.table1[this.label1]));
+            w5.XOptions = ((Gtk.AttachOptions)(4));
+            w5.YOptions = ((Gtk.AttachOptions)(4));
+            // Container child table1.Gtk.Table+TableChild
+            this.label2 = new Gtk.Label();
+            this.label2.Name = "label2";
+            this.label2.Xalign = 0F;
+            this.label2.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("_Password");
+            this.label2.UseUnderline = true;
+            this.table1.Add(this.label2);
+            Gtk.Table.TableChild w6 = ((Gtk.Table.TableChild)(this.table1[this.label2]));
+            w6.TopAttach = ((uint)(2));
+            w6.BottomAttach = ((uint)(3));
+            w6.XOptions = ((Gtk.AttachOptions)(4));
+            w6.YOptions = ((Gtk.AttachOptions)(4));
+            // Container child table1.Gtk.Table+TableChild
+            this.label3 = new Gtk.Label();
+            this.label3.Name = "label3";
+            this.label3.Xalign = 0F;
+            this.label3.LabelProp = Mono.Addins.AddinManager.CurrentLocalizer.GetString("_Login");
+            this.label3.UseUnderline = true;
+            this.table1.Add(this.label3);
+            Gtk.Table.TableChild w7 = ((Gtk.Table.TableChild)(this.table1[this.label3]));
+            w7.TopAttach = ((uint)(1));
+            w7.BottomAttach = ((uint)(2));
+            w7.XOptions = ((Gtk.AttachOptions)(4));
+            w7.YOptions = ((Gtk.AttachOptions)(4));
+            // Container child table1.Gtk.Table+TableChild
+            this.password_entry = new Gtk.Entry();
+            this.password_entry.CanFocus = true;
+            this.password_entry.Name = "password_entry";
+            this.password_entry.IsEditable = true;
+            this.password_entry.Visibility = false;
+            this.password_entry.InvisibleChar = '●';
+            this.table1.Add(this.password_entry);
+            Gtk.Table.TableChild w8 = ((Gtk.Table.TableChild)(this.table1[this.password_entry]));
+            w8.TopAttach = ((uint)(2));
+            w8.BottomAttach = ((uint)(3));
+            w8.LeftAttach = ((uint)(1));
+            w8.RightAttach = ((uint)(2));
+            w8.YOptions = ((Gtk.AttachOptions)(4));
+            // Container child table1.Gtk.Table+TableChild
+            this.user_name_entry = new Gtk.Entry();
+            this.user_name_entry.CanFocus = true;
+            this.user_name_entry.Name = "user_name_entry";
+            this.user_name_entry.IsEditable = true;
+            this.user_name_entry.InvisibleChar = '●';
+            this.table1.Add(this.user_name_entry);
+            Gtk.Table.TableChild w9 = ((Gtk.Table.TableChild)(this.table1[this.user_name_entry]));
+            w9.TopAttach = ((uint)(1));
+            w9.BottomAttach = ((uint)(2));
+            w9.LeftAttach = ((uint)(1));
+            w9.RightAttach = ((uint)(2));
+            w9.YOptions = ((Gtk.AttachOptions)(4));
+            this.alignment108.Add(this.table1);
+            this.Add(this.alignment108);
+            if ((this.Child != null)) {
+                this.Child.ShowAll();
+            }
+            this.Hide();
+            this.user_name_entry.Changed += new System.EventHandler(this.OnUserNameEntryChanged);
+            this.password_entry.Changed += new System.EventHandler(this.OnPasswordEntryChanged);
+        }
+    }
+}

=== added file 'Transmission/gtk-gui/generated.cs'
--- Transmission/gtk-gui/generated.cs	1970-01-01 00:00:00 +0000
+++ Transmission/gtk-gui/generated.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,82 @@
+// ------------------------------------------------------------------------------
+//  <autogenerated>
+//      This code was generated by a tool.
+//      
+// 
+//      Changes to this file may cause incorrect behavior and will be lost if 
+//      the code is regenerated.
+//  </autogenerated>
+// ------------------------------------------------------------------------------
+
+namespace Stetic {
+    
+    
+    internal class Gui {
+        
+        private static bool initialized;
+        
+        internal static void Initialize(Gtk.Widget iconRenderer) {
+            if ((Stetic.Gui.initialized == false)) {
+                Stetic.Gui.initialized = true;
+            }
+        }
+    }
+    
+    internal class BinContainer {
+        
+        private Gtk.Widget child;
+        
+        private Gtk.UIManager uimanager;
+        
+        public static BinContainer Attach(Gtk.Bin bin) {
+            BinContainer bc = new BinContainer();
+            bin.SizeRequested += new Gtk.SizeRequestedHandler(bc.OnSizeRequested);
+            bin.SizeAllocated += new Gtk.SizeAllocatedHandler(bc.OnSizeAllocated);
+            bin.Added += new Gtk.AddedHandler(bc.OnAdded);
+            return bc;
+        }
+        
+        private void OnSizeRequested(object sender, Gtk.SizeRequestedArgs args) {
+            if ((this.child != null)) {
+                args.Requisition = this.child.SizeRequest();
+            }
+        }
+        
+        private void OnSizeAllocated(object sender, Gtk.SizeAllocatedArgs args) {
+            if ((this.child != null)) {
+                this.child.Allocation = args.Allocation;
+            }
+        }
+        
+        private void OnAdded(object sender, Gtk.AddedArgs args) {
+            this.child = args.Widget;
+        }
+        
+        public void SetUiManager(Gtk.UIManager uim) {
+            this.uimanager = uim;
+            this.child.Realized += new System.EventHandler(this.OnRealized);
+        }
+        
+        private void OnRealized(object sender, System.EventArgs args) {
+            if ((this.uimanager != null)) {
+                Gtk.Widget w;
+                w = this.child.Toplevel;
+                if (((w != null) && typeof(Gtk.Window).IsInstanceOfType(w))) {
+                    ((Gtk.Window)(w)).AddAccelGroup(this.uimanager.AccelGroup);
+                    this.uimanager = null;
+                }
+            }
+        }
+    }
+    
+    internal class ActionGroups {
+        
+        public static Gtk.ActionGroup GetActionGroup(System.Type type) {
+            return Stetic.ActionGroups.GetActionGroup(type.FullName);
+        }
+        
+        public static Gtk.ActionGroup GetActionGroup(string name) {
+            return null;
+        }
+    }
+}

=== added file 'Transmission/gtk-gui/gui.stetic'
--- Transmission/gtk-gui/gui.stetic	1970-01-01 00:00:00 +0000
+++ Transmission/gtk-gui/gui.stetic	2010-07-26 16:47:47 +0000
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="utf-8"?>
+<stetic-interface>
+  <configuration>
+    <images-root-path>..</images-root-path>
+    <target-gtk-version>2.12</target-gtk-version>
+  </configuration>
+  <import>
+    <widget-library name="Do.Platform.Linux, Version=0.9.0.0, Culture=neutral" />
+    <widget-library name="../bin/Debug/Transmission.dll" internal="true" />
+  </import>
+  <widget class="Gtk.Bin" id="Transmission.TransmissionConfig" design-size="444 300">
+    <property name="MemberName" />
+    <property name="Visible">False</property>
+    <child>
+      <widget class="Gtk.Alignment" id="alignment108">
+        <property name="MemberName" />
+        <property name="Yscale">0</property>
+        <property name="Yalign">0</property>
+        <child>
+          <widget class="Gtk.Table" id="table1">
+            <property name="MemberName" />
+            <property name="NRows">3</property>
+            <property name="NColumns">2</property>
+            <property name="RowSpacing">6</property>
+            <property name="ColumnSpacing">6</property>
+            <child>
+              <widget class="Gtk.HBox" id="hbox1">
+                <property name="MemberName" />
+                <property name="Spacing">6</property>
+                <child>
+                  <widget class="Gtk.Entry" id="address_entry">
+                    <property name="MemberName" />
+                    <property name="CanFocus">True</property>
+                    <property name="IsEditable">True</property>
+                    <property name="InvisibleChar">●</property>
+                  </widget>
+                  <packing>
+                    <property name="Position">0</property>
+                    <property name="AutoSize">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="Gtk.Label" id="remote_port_label">
+                    <property name="MemberName">remote_port_label</property>
+                    <property name="LabelProp" translatable="yes">Port</property>
+                  </widget>
+                  <packing>
+                    <property name="Position">1</property>
+                    <property name="AutoSize">True</property>
+                    <property name="Expand">False</property>
+                    <property name="Fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="Gtk.Entry" id="port_entry">
+                    <property name="MemberName">port_entry</property>
+                    <property name="CanFocus">True</property>
+                    <property name="IsEditable">True</property>
+                    <property name="WidthChars">5</property>
+                    <property name="MaxLength">5</property>
+                    <property name="InvisibleChar">●</property>
+                  </widget>
+                  <packing>
+                    <property name="Position">2</property>
+                    <property name="AutoSize">False</property>
+                    <property name="Expand">False</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="LeftAttach">1</property>
+                <property name="RightAttach">2</property>
+                <property name="AutoSize">True</property>
+                <property name="YOptions">Fill</property>
+                <property name="XExpand">True</property>
+                <property name="XFill">True</property>
+                <property name="XShrink">False</property>
+                <property name="YExpand">False</property>
+                <property name="YFill">True</property>
+                <property name="YShrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Gtk.Label" id="label1">
+                <property name="MemberName" />
+                <property name="Xalign">0</property>
+                <property name="LabelProp" translatable="yes">_Address</property>
+                <property name="UseUnderline">True</property>
+              </widget>
+              <packing>
+                <property name="AutoSize">True</property>
+                <property name="XOptions">Fill</property>
+                <property name="YOptions">Fill</property>
+                <property name="XExpand">False</property>
+                <property name="XFill">True</property>
+                <property name="XShrink">False</property>
+                <property name="YExpand">False</property>
+                <property name="YFill">True</property>
+                <property name="YShrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Gtk.Label" id="label2">
+                <property name="MemberName" />
+                <property name="Xalign">0</property>
+                <property name="LabelProp" translatable="yes">_Password</property>
+                <property name="UseUnderline">True</property>
+              </widget>
+              <packing>
+                <property name="TopAttach">2</property>
+                <property name="BottomAttach">3</property>
+                <property name="AutoSize">True</property>
+                <property name="XOptions">Fill</property>
+                <property name="YOptions">Fill</property>
+                <property name="XExpand">False</property>
+                <property name="XFill">True</property>
+                <property name="XShrink">False</property>
+                <property name="YExpand">False</property>
+                <property name="YFill">True</property>
+                <property name="YShrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Gtk.Label" id="label3">
+                <property name="MemberName" />
+                <property name="Xalign">0</property>
+                <property name="LabelProp" translatable="yes">_Login</property>
+                <property name="UseUnderline">True</property>
+              </widget>
+              <packing>
+                <property name="TopAttach">1</property>
+                <property name="BottomAttach">2</property>
+                <property name="AutoSize">True</property>
+                <property name="XOptions">Fill</property>
+                <property name="YOptions">Fill</property>
+                <property name="XExpand">False</property>
+                <property name="XFill">True</property>
+                <property name="XShrink">False</property>
+                <property name="YExpand">False</property>
+                <property name="YFill">True</property>
+                <property name="YShrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Gtk.Entry" id="password_entry">
+                <property name="MemberName" />
+                <property name="CanFocus">True</property>
+                <property name="IsEditable">True</property>
+                <property name="Visibility">False</property>
+                <property name="InvisibleChar">●</property>
+                <signal name="Changed" handler="OnPasswordEntryChanged" />
+              </widget>
+              <packing>
+                <property name="TopAttach">2</property>
+                <property name="BottomAttach">3</property>
+                <property name="LeftAttach">1</property>
+                <property name="RightAttach">2</property>
+                <property name="AutoSize">True</property>
+                <property name="YOptions">Fill</property>
+                <property name="XExpand">True</property>
+                <property name="XFill">True</property>
+                <property name="XShrink">False</property>
+                <property name="YExpand">False</property>
+                <property name="YFill">True</property>
+                <property name="YShrink">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="Gtk.Entry" id="user_name_entry">
+                <property name="MemberName" />
+                <property name="CanFocus">True</property>
+                <property name="IsEditable">True</property>
+                <property name="InvisibleChar">●</property>
+                <signal name="Changed" handler="OnUserNameEntryChanged" />
+              </widget>
+              <packing>
+                <property name="TopAttach">1</property>
+                <property name="BottomAttach">2</property>
+                <property name="LeftAttach">1</property>
+                <property name="RightAttach">2</property>
+                <property name="AutoSize">True</property>
+                <property name="YOptions">Fill</property>
+                <property name="XExpand">True</property>
+                <property name="XFill">True</property>
+                <property name="XShrink">False</property>
+                <property name="YExpand">False</property>
+                <property name="YFill">True</property>
+                <property name="YShrink">False</property>
+              </packing>
+            </child>
+          </widget>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</stetic-interface>
\ No newline at end of file

=== added directory 'Transmission/lib'
=== added file 'Transmission/lib/Jayrock.Json.dll'
Binary files Transmission/lib/Jayrock.Json.dll	1970-01-01 00:00:00 +0000 and Transmission/lib/Jayrock.Json.dll	2010-07-26 16:47:47 +0000 differ
=== added file 'Transmission/lib/Jayrock.Json.pdb'
Binary files Transmission/lib/Jayrock.Json.pdb	1970-01-01 00:00:00 +0000 and Transmission/lib/Jayrock.Json.pdb	2010-07-26 16:47:47 +0000 differ
=== added directory 'Transmission/src'
=== added directory 'Transmission/src/Config'
=== added file 'Transmission/src/Config/TransmissionConfig.cs'
--- Transmission/src/Config/TransmissionConfig.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/Config/TransmissionConfig.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Mono.Addins;
+
+using Gtk;
+
+using Do.Platform;
+
+namespace Transmission
+{
+
+	[System.ComponentModel.Category("File")]
+	[System.ComponentModel.ToolboxItem(true)]
+	public partial class TransmissionConfig : Gtk.Bin
+	{
+		public static string home_path = Environment.GetFolderPath (Environment.SpecialFolder.Personal); 
+		public static string settings_path = System.IO.Path.Combine (home_path, ".config/transmission/settings.json");
+
+		static IPreferences prefs;
+
+		public TransmissionConfig()
+		{
+			Build();
+			RefreshView();
+		}
+
+		private void RefreshView()
+		{
+			address_entry.Text = Address;
+			port_entry.Text = Port.ToString();
+			user_name_entry.Text = UserName;
+			password_entry.Text = Password;
+		}
+
+		static TransmissionConfig()
+		{
+			prefs = Services.Preferences.Get<TransmissionConfig>();
+		}
+
+		public static string Address
+		{
+			get { return prefs.Get<string>("Address", "127.0.0.1"); }
+			set { prefs.Set<string> ("Address", value); }
+		}
+
+		public static int Port
+		{
+			get { return prefs.Get<int>("Port", TransmissionAPI.DEFAULT_PORT); }
+			set { prefs.Set<int> ("Port", value); }
+		}
+
+		public static string UserName
+		{
+			get { return prefs.Get<string>("UserName", ""); }
+			set { prefs.Set<string> ("UserName", value); }
+		}
+
+		public static string Password
+		{
+			get { return prefs.Get<string>("Password", ""); }
+			set { prefs.Set<string> ("Password", value); }
+		}
+
+		protected virtual void OnAddressEntryChanged (object sender, System.EventArgs e)
+		{
+			Address = address_entry.Text;
+			TransmissionPlugin.ResetConnection();
+		}
+
+		protected virtual void OnUserNameEntryChanged (object sender, System.EventArgs e)
+		{
+			UserName = user_name_entry.Text;
+			TransmissionPlugin.ResetConnection();
+		}
+
+		protected virtual void OnPasswordEntryChanged (object sender, System.EventArgs e)
+		{
+			Password = password_entry.Text;
+			TransmissionPlugin.ResetConnection();
+		}
+
+		protected virtual void OnPortEntryChanged (object sender, System.EventArgs e)
+		{
+			Port = int.Parse(port_entry.Text);
+			TransmissionPlugin.ResetConnection();
+		}
+		
+	}
+}

=== added file 'Transmission/src/ITorrentEntry.cs'
--- Transmission/src/ITorrentEntry.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/ITorrentEntry.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace Transmission {
+
+	public interface ITorrentEntry {
+		// Owner torrent.
+		TorrentItem Torrent { get; }
+
+		// Path on FS.
+		string Path { get; }
+
+		// Get all files under this entry (recursively).
+		// For files return file itself.
+		IEnumerable<TorrentFileItem> GetFiles();
+	}
+}

=== added file 'Transmission/src/JsonCollectionImporter.cs'
--- Transmission/src/JsonCollectionImporter.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/JsonCollectionImporter.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,117 @@
+namespace Transmission
+{
+    #region Imports
+
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics;
+    using Jayrock.Json;
+    using Jayrock.Json.Conversion;
+	using Jayrock.Json.Conversion.Converters;
+
+    #endregion
+
+	/// <summary>
+    /// An abstract base class for importer implementations that can import
+    /// a concrete collection instance from a JSON array.
+    /// </summary>
+
+
+    public abstract class CollectionImporterBase : ImporterBase
+    {
+        private readonly Type _elementType;
+
+
+        public CollectionImporterBase(Type outputType, Type elementType) : 
+            base(outputType)
+        {
+            if (elementType == null) throw new ArgumentNullException("elementType");
+            
+            _elementType = elementType;
+        }
+
+
+        public Type ElementType
+        {
+            get { return _elementType; }
+        }
+
+
+        protected override object ImportFromArray(ImportContext context, JsonReader reader)
+        {
+            if (context == null) throw new ArgumentNullException("context");
+            if (reader == null) throw new ArgumentNullException("reader");
+
+
+            object collection = CreateCollection();
+
+
+            reader.ReadToken(JsonTokenClass.Array);
+
+
+            ImportElements(collection, context, reader);
+
+
+            if (reader.TokenClass != JsonTokenClass.EndArray)
+                throw new Exception("Implementation error.");
+
+
+            reader.Read();
+            return collection;
+        }
+
+
+        protected abstract object CreateCollection();
+        protected abstract void ImportElements(object collection, ImportContext context, JsonReader reader);
+    }
+	
+	
+    /// <summary>
+    /// An importer for importing a collection of elements from a JSON array.
+    /// </summary>
+
+
+    public class CollectionImporter<Collection, Element> : CollectionImporterBase
+        where Collection : ICollection<Element>, new()
+    {
+        public CollectionImporter() :
+            base(typeof(Collection), typeof(Element)) { }
+
+
+        protected override object CreateCollection()
+        {
+            return new Collection();
+        }
+
+
+        protected override void ImportElements(object collection, ImportContext context, JsonReader reader)
+        {
+            if (collection == null) throw new ArgumentNullException("collection");
+            if (context == null) throw new ArgumentNullException("context");
+            if (reader == null) throw new ArgumentNullException("reader");
+
+
+            ImportElements((ICollection<Element>) collection, context, reader);
+        }
+
+
+        private static void ImportElements(ICollection<Element> collection, ImportContext context, JsonReader reader)
+        {
+            Debug.Assert(collection != null);
+            Debug.Assert(context != null);
+            Debug.Assert(reader != null);
+
+
+            while (reader.TokenClass != JsonTokenClass.EndArray)
+                collection.Add((Element) context.Import(typeof(Element), reader));
+        }
+    }
+	
+	/// <summary>
+    /// Imports <see cref="List{T}"/> from a JSON array.
+    /// </summary>
+    
+    public class ListImporter<T> : CollectionImporter<List<T>, T>
+        where T : new() { }
+     
+}
\ No newline at end of file

=== added file 'Transmission/src/TorrentAbstractLimitSpeedAction.cs'
--- Transmission/src/TorrentAbstractLimitSpeedAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentAbstractLimitSpeedAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,80 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+	public abstract class TorrentAbstractLimitSpeedAction: Act {
+
+		public override IEnumerable<Type> SupportedItemTypes {
+			get { yield return typeof (TorrentItem); }
+		}
+
+		public override IEnumerable<Type> SupportedModifierItemTypes {
+			get {
+				yield return typeof (ITextItem);
+				yield return typeof (PredefinedSpeed);
+			}
+		}
+
+		public override bool ModifierItemsOptional {
+			get { return false; }
+		}
+
+		protected abstract PredefinedSpeed GetCurrentSpeedItem(TorrentItem torrent);
+
+		public override IEnumerable<Item> DynamicModifierItemsForItem(Item item) {
+			TorrentItem torrent = (TorrentItem)item;
+
+			yield return new PredefinedSpeed(0, "Unlimited", "Turn download speed limit off");
+			yield return GetCurrentSpeedItem(torrent);
+			foreach (PredefinedSpeed speed in Utils.PredefinedSpeedItems)
+				yield return speed;
+		}
+
+		protected abstract void SetSpeedLimit(TransmissionAPI api, IEnumerable<TorrentItem> torrents, int speed);
+
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			int? speed = null;
+
+			// Get speed item, it can be either ITextItem or PredefinedSpeed.
+			Item modItem = modItems.First();
+			if (modItem is PredefinedSpeed) {
+				speed = ((PredefinedSpeed)modItem).Value;
+			} else {
+				string speed_str = ((ITextItem)modItem).Text;
+
+				try {
+					// Try to parse entered speed value.
+					speed = Utils.ParseSpeed(speed_str);
+
+				} catch (ArgumentException) {
+					Log<TransmissionPlugin>.Debug("Invalid speed string: {0}", speed_str);
+
+					// Show notification about invalid speed value with some hints on
+					// accepted formats.
+					string message = AddinManager.CurrentLocalizer.GetString(
+						"Can't recognize \"{0}\" as speed\nUse values like: 100k, 50 kb, 20m, 10 mib"
+					);
+					Services.Notifications.Notify("Transmission", string.Format(message, speed_str), "transmission");
+				}
+			}
+
+			// If speed is recognized successfully, set speed limit and update item.
+			if (speed.HasValue) {
+				TransmissionAPI api = TransmissionPlugin.getTransmission();
+				IEnumerable<TorrentItem> torrents = items.Cast<TorrentItem>();
+				SetSpeedLimit(api, torrents, speed.Value);
+			}
+
+			yield break;
+		}
+	}
+}

=== added file 'Transmission/src/TorrentDirectoryItem.cs'
--- Transmission/src/TorrentDirectoryItem.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentDirectoryItem.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,64 @@
+
+using System;
+using System.Collections.Generic;
+
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentDirectoryItem: Item, ITorrentEntry {
+
+		private TorrentItem _torrent;
+		private TorrentDirectoryItem _parent;
+		private string _name;
+		private IList<Item> _files;
+
+		public TorrentDirectoryItem(TorrentItem torrent, TorrentDirectoryItem parent, string name) {
+			_torrent = torrent;
+			_parent = parent;
+			_name = name;
+			_files = new List<Item>();
+		}
+
+		public TorrentItem Torrent {
+			get { return _torrent; }
+		}
+
+		public IEnumerable<TorrentFileItem> GetFiles() {
+			foreach (ITorrentEntry entry in _files)
+				foreach (TorrentFileItem file in entry.GetFiles())
+					yield return file;
+		}
+
+		public override string Name {
+			get { return _name; }
+		}
+
+		public override string Description {
+			get { return string.Empty; }
+		}
+
+		public override string Icon {
+			get { return "folder"; }
+		}
+
+		public IList<Item> Files {
+			get { return _files; }
+		}
+
+		public string Path {
+			get {
+				if (_parent != null) {
+					return _parent.Path + '/' + _name;
+				} else {
+					return _name;
+				}
+			}
+		}
+
+		public string Uri {
+			get { return "file://" + Path; }
+		}
+	}
+
+}

=== added file 'Transmission/src/TorrentFileItem.cs'
--- Transmission/src/TorrentFileItem.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentFileItem.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,97 @@
+
+using System;
+using System.Collections.Generic;
+
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentFileItem: Item, ITorrentEntry {
+
+		// Owner torrent.
+		private TorrentItem _torrent;
+		// Position of this file within torrents file list.
+		private int _index;
+
+		private TorrentDirectoryItem _parent;
+		private string _name;
+		private long _size, _downloaded;
+		private bool _wanted;
+		private TransmissionAPI.FilePriority _priority;
+
+		public TorrentFileItem(
+			TorrentItem torrent, int index, TorrentDirectoryItem parent,
+			string name, TransmissionAPI.TorrentFileInfo info
+		) {
+			_torrent = torrent;
+			_index = index;
+
+			_parent = parent;
+			_name = name;
+			_size = info.Length;
+			_downloaded = info.BytesCompleted;
+
+			_wanted = info.Wanted;
+			_priority = info.Priority;
+		}
+
+		public TorrentItem Torrent {
+			get { return _torrent; }
+		}
+
+		public int Index {
+			get { return _index; }
+		}
+
+		public TransmissionAPI.FilePriority Priority {
+			get { return _priority; }
+		}
+
+		public IEnumerable<TorrentFileItem> GetFiles() {
+			yield return this;
+		}
+
+		public override string Name {
+			get { return _name; }
+		}
+
+		public override string Description {
+			get {
+				// I don't use special percentage format string, because it rounds
+				// value and I don't want to get "100%" until file is really downloaded.
+				// High precision isn't needed, because info is mostly out-of-date.
+
+				if (_downloaded == _size)
+					return string.Format("Complete, {0}", Utils.FormatSize(_size));
+
+				else if (_wanted)
+					return string.Format("{0} of {1} complete ({2:0}%)",
+						Utils.FormatSize(_downloaded), Utils.FormatSize(_size),
+						Math.Floor(100.0 * _downloaded / _size)
+					);
+
+				else if (_downloaded != 0)
+					return string.Format("Skipped, {0} of {1} complete ({2:0}%)",
+						Utils.FormatSize(_downloaded),Utils.FormatSize(_size),
+						Math.Floor(100.0 * _downloaded / _size)
+					);
+
+				else
+					return string.Format("Skipped, {0}", Utils.FormatSize(_size));
+			}
+		}
+
+		public override string Icon {
+			get { return "document"; }
+		}
+
+		public string Path {
+			get { return _parent.Path + '/' + _name; }
+		}
+
+		public string Uri {
+			get { return "file://" + Path; }
+		}
+	}
+
+}

=== added file 'Transmission/src/TorrentFileSetPriorityAction.cs'
--- Transmission/src/TorrentFileSetPriorityAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentFileSetPriorityAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,108 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+	public class PriorityItem: Item {
+		private TransmissionAPI.FilePriority _value;
+		private string _name;
+		private string _icon;
+
+		public PriorityItem(TransmissionAPI.FilePriority value, string name, string icon) {
+			_value = value;
+			_name = name;
+			_icon = icon;
+		}
+
+		public override string Name {
+			get { return _name; }
+		}
+
+		public override string Description {
+			get { return ""; }
+		}
+
+		public override string Icon {
+			get { return _icon; }
+		}
+
+		public TransmissionAPI.FilePriority Value {
+			get { return _value; }
+		}
+	}
+
+	public class TorrentFileSetPriorityAction: Act {
+
+		public TorrentFileSetPriorityAction() {
+		}
+
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Set priority"); }
+	    }
+
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Set download priority"); }
+		}
+
+		public override string Icon {
+			get { return "object-flip-vertical"; }
+		}
+
+		public override IEnumerable<Type> SupportedItemTypes {
+			get {
+				yield return typeof (ITorrentEntry);
+			}
+		}
+
+		public override IEnumerable<Type> SupportedModifierItemTypes {
+			get {
+				yield return typeof (PriorityItem);
+			}
+		}
+
+		public override bool ModifierItemsOptional {
+			get { return false; }
+		}
+
+		public override IEnumerable<Item> DynamicModifierItemsForItem(Item item) {
+			yield return new PriorityItem(TransmissionAPI.FilePriority.Low, "Low", "down");
+			yield return new PriorityItem(TransmissionAPI.FilePriority.Normal, "Normal", "forward");
+			yield return new PriorityItem(TransmissionAPI.FilePriority.High, "High", "up");
+		}
+
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			TransmissionAPI.FilePriority priority = (modItems.First() as PriorityItem).Value;
+
+			TransmissionAPI.FileOperation operation = new TransmissionAPI.FileOperation(null, priority);
+
+			// Group torrent entries by torrent.
+			var files_by_torrent = items
+				.Cast<ITorrentEntry>()
+				.GroupBy(
+					item => item.Torrent,
+					(torrent, entries) => new {
+						Torrent = torrent,
+						Files = entries.SelectMany(entry => entry.GetFiles())
+					}
+				);
+
+			TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+			// Expand entries for each torrent into set of torrent file entries.
+			// Perform action for each torrent separately.
+			foreach (var group in files_by_torrent) {
+				var operations = group.Files.ToDictionary(f => f.Index, f => operation);
+				api.SetTorrent(group.Torrent.HashString, null, null, null, null, null, operations);
+			}
+
+			yield break;
+		}
+	}
+}

=== added file 'Transmission/src/TorrentItem.cs'
--- Transmission/src/TorrentItem.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentItem.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,72 @@
+
+using System;
+using System.Collections.Generic;
+
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentItem: Item {
+
+		private string _name;
+		private string _comment;
+		private string _hash_string;
+		private long _size;
+		private TransmissionAPI.TorrentStatus _status;
+		private int _download_speed_limit, _upload_speed_limit;
+		private TorrentDirectoryItem _root;
+
+		public TorrentItem(TransmissionAPI.TorrentInfo info) {
+			_hash_string = info.HashString;
+			_name = info.Name;
+			_comment = info.Comment;
+			_status = info.Status;
+			_size = info.TotalSize;
+			_download_speed_limit = info.DownloadLimit;
+			_upload_speed_limit = info.UploadLimit;
+			_root = new TorrentDirectoryItem(this, null, info.DownloadDir);
+		}
+
+		public override string Name {
+			get { return _name; }
+		}
+
+		public override string Description {
+			get {
+				string status_text = "";
+				switch (_status) {
+				case TransmissionAPI.TorrentStatus.CheckWait: status_text = "Waiting for check"; break;
+				case TransmissionAPI.TorrentStatus.Check:     status_text = "Checking"; break;
+				case TransmissionAPI.TorrentStatus.Download:  status_text = "Downloading"; break;
+				case TransmissionAPI.TorrentStatus.Seed:      status_text = "Seeding"; break;
+				case TransmissionAPI.TorrentStatus.Stopped:   status_text = "Stopped"; break;
+				}
+
+				return string.Format("{0}, {1}", Utils.FormatSize(_size), status_text);
+			}
+		}
+
+		public override string Icon {
+			get { return "transmission"; }
+		}
+
+		public string HashString {
+			get { return _hash_string; }
+		}
+
+		public TorrentDirectoryItem Root {
+			get { return _root; }
+		}
+
+		public int DownloadSpeedLimit {
+			get { return _download_speed_limit; }
+			set { _download_speed_limit = value; }
+		}
+
+		public int UploadSpeedLimit {
+			get { return _upload_speed_limit; }
+			set { _upload_speed_limit = value; }
+		}
+	}
+
+}

=== added file 'Transmission/src/TorrentItemSource.cs'
--- Transmission/src/TorrentItemSource.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentItemSource.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,130 @@
+
+using System;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+using Do.Platform;
+using Do.Platform.Linux;
+
+namespace Transmission {
+
+	public class TorrentItemSource: ItemSource, IConfigurable {
+
+		private List<Item> _torrents = new List<Item>();
+
+		public TorrentItemSource() {
+		}
+
+		public override string Name {
+			get { return "Torrents"; }
+		}
+
+		public override string Description {
+			get { return "Transmission torrent client downloads"; }
+		}
+
+		public override string Icon {
+			get { return "transmission"; }
+		}
+
+		public override void UpdateItems () {
+			Log<TorrentItemSource>.Debug("Updating torrents list");
+
+			// Clear current torrents list.
+			_torrents.Clear();
+
+			TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+			foreach (TransmissionAPI.TorrentInfo t in api.GetAllTorrents()) {
+				Log<TorrentItemSource>.Debug("Torrent: {0}", t.Name);
+
+				TorrentItem torrent = new TorrentItem(t);
+
+				// Transmission returns files as flat list with full names, this map
+				// is used to organize files into hierarchy.
+				// It maps directory path to directory item.
+				Dictionary<string, TorrentDirectoryItem> dirs = new Dictionary<string, TorrentDirectoryItem>();
+				dirs.Add("", torrent.Root);
+
+				int index = 0; // File index within list.
+				foreach (TransmissionAPI.TorrentFileInfo f in t.files) {
+					// Split path and name.
+					int sep_pos = f.Name.LastIndexOf('/');
+
+					string name = f.Name.Substring(sep_pos+1);
+					string path = f.Name.Substring(0, sep_pos == -1 ? 0 : sep_pos);
+					Log<TorrentItemSource>.Debug("File {0} in dir {1}", name, path);
+					TorrentDirectoryItem dir = FindOrCreateDirectory(path, dirs);
+
+					dir.Files.Add(new TorrentFileItem(torrent, index, dir, name, f));
+
+					++index;
+				}
+
+				_torrents.Add(torrent);
+			}
+		}
+
+		private TorrentDirectoryItem FindOrCreateDirectory(
+			string path,
+			Dictionary<string, TorrentDirectoryItem> dirs
+		) {
+			TorrentDirectoryItem dir;
+			dirs.TryGetValue(path, out dir);
+
+			if (dir != null) {
+				// Found already added directory.
+				return dir;
+
+			} else {
+				// Directory doesn't exist, find or add parent one, then add this one.
+				int sep_pos = path.LastIndexOf('/');
+
+				string parent_path = path.Substring(0, sep_pos == -1 ? 0 : sep_pos);
+				TorrentDirectoryItem parent = FindOrCreateDirectory(parent_path, dirs);
+
+				string name = path.Substring(sep_pos+1);
+				dir = new TorrentDirectoryItem(parent.Torrent, parent, name);
+
+				parent.Files.Add(dir);
+				dirs.Add(path, dir);
+
+				return dir;
+			}
+		}
+
+		public override IEnumerable<Item> Items {
+			get { return _torrents; }
+		}
+
+		public override IEnumerable<Item> ChildrenOfItem(Item item) {
+			if (item is TorrentItem) {
+				foreach (Item entry in ((TorrentItem)item).Root.Files)
+					yield return entry;
+
+			} else if (item is TorrentDirectoryItem) {
+				foreach (Item entry in ((TorrentDirectoryItem)item).Files)
+					yield return entry;
+
+			} else {
+				yield break;
+
+			}
+		}
+
+		public override IEnumerable<Type> SupportedItemTypes {
+			get {
+				yield return typeof(TorrentItem);
+				yield return typeof(TorrentDirectoryItem);
+				yield return typeof(TorrentFileItem);
+			}
+		}
+
+		public Gtk.Bin GetConfiguration() {
+			return new TransmissionConfig();
+		}
+
+	}
+}

=== added file 'Transmission/src/TorrentLimitDownloadSpeedAction.cs'
--- Transmission/src/TorrentLimitDownloadSpeedAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentLimitDownloadSpeedAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,47 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentLimitDownloadSpeedAction: TorrentAbstractLimitSpeedAction {
+
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString("Limit download speed"); }
+	    }
+
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString("Set download speed limit"); }
+		}
+
+		public override string Icon {
+			get { return "top"; }
+		}
+
+		protected override PredefinedSpeed GetCurrentSpeedItem(TorrentItem torrent) {
+			int currentSpeed = torrent.DownloadSpeedLimit;
+			return new PredefinedSpeed(
+				currentSpeed,
+				string.Format("Saved: {0}", Utils.FormatSpeed(currentSpeed)),
+				"Use limit from torrent settings"
+			);
+		}
+
+		protected override void SetSpeedLimit(TransmissionAPI api, IEnumerable<TorrentItem> torrents, int speed) {
+			bool limit_speed = (speed != 0);
+			int? limit = (speed == 0 ? (int?)null : speed);
+			api.SetTorrents(torrents.Select(t => t.HashString), null, limit_speed, limit, null, null);
+
+			foreach (TorrentItem torrent in torrents)
+				torrent.DownloadSpeedLimit = speed;
+		}
+
+	}
+}

=== added file 'Transmission/src/TorrentLimitUploadSpeedAction.cs'
--- Transmission/src/TorrentLimitUploadSpeedAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentLimitUploadSpeedAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,47 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentLimitUploadSpeedAction: TorrentAbstractLimitSpeedAction {
+
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Limit upload speed"); }
+	    }
+
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Set upload speed limit"); }
+		}
+
+		public override string Icon {
+			get { return "top"; }
+		}
+
+		protected override PredefinedSpeed GetCurrentSpeedItem(TorrentItem torrent) {
+			int currentSpeed = torrent.UploadSpeedLimit;
+			return new PredefinedSpeed(
+				currentSpeed,
+				string.Format("Saved: {0}", Utils.FormatSpeed(currentSpeed)),
+				"Use limit from torrent settings"
+			);
+		}
+
+		protected override void SetSpeedLimit(TransmissionAPI api, IEnumerable<TorrentItem> torrents, int speed) {
+			bool limit_speed = (speed != 0);
+			int? limit = (speed == 0 ? (int?)null : speed);
+			api.SetTorrents(torrents.Select(t => t.HashString), null, null, null, limit_speed, limit);
+
+			foreach (TorrentItem torrent in torrents)
+				torrent.UploadSpeedLimit = speed;
+		}
+
+	}
+}

=== added file 'Transmission/src/TorrentMarkForDownloadAction.cs'
--- Transmission/src/TorrentMarkForDownloadAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentMarkForDownloadAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,62 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentMarkForDownloadAction: Act {
+
+		public TorrentMarkForDownloadAction() {
+		}
+
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Mark for download"); }
+	    }
+
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Mark file as needed to be downloaded"); }
+		}
+
+		public override string Icon {
+			get { return "add"; }
+		}
+
+		public override IEnumerable<Type> SupportedItemTypes {
+			get {
+				yield return typeof (ITorrentEntry);
+			}
+		}
+
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+			// Operation is common for all files.
+			TransmissionAPI.FileOperation operation = new TransmissionAPI.FileOperation(true, null);
+
+			// Group selected items by owner torrent.
+			var files_by_torrent = items
+				.Cast<ITorrentEntry>()
+				.GroupBy(
+					item => item.Torrent,
+					(torrent, entries) => new {
+						Torrent = torrent,
+						Files = entries.SelectMany(entry => entry.GetFiles())
+					}
+				);
+
+			// Perform action for each torrent separately.
+			foreach (var group in files_by_torrent) {
+				var operations = group.Files.ToDictionary(f => f.Index, f => operation);
+				api.SetTorrent(group.Torrent.HashString, null, null, null, null, null, operations);
+			}
+
+			yield break;
+		}
+	}
+}

=== added file 'Transmission/src/TorrentOperateAction.cs'
--- Transmission/src/TorrentOperateAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentOperateAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,37 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+using Do.Platform;
+
+namespace Transmission {
+
+	public class TorrentOperateAction: Act {
+
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Operate on files"); }
+	    }
+
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Operate on downloaded file"); }
+		}
+
+		public override string Icon {
+			get { return "file"; }
+		}
+
+		public override IEnumerable<Type> SupportedItemTypes {
+			get { yield return typeof (ITorrentEntry); }
+		}
+
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			foreach (Item item in items) {
+				ITorrentEntry entry = (ITorrentEntry)item;
+				yield return Services.UniverseFactory.NewFileItem(entry.Path) as Item;
+			}
+		}
+	}
+}

=== added file 'Transmission/src/TorrentStartAction.cs'
--- Transmission/src/TorrentStartAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentStartAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,42 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+
+namespace Transmission {
+	
+	public class TorrentStartAction: Act {
+		
+		public TorrentStartAction() {
+		}
+		
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Start"); }
+	    }
+		
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Start downloading torrent"); }
+		}
+		
+		public override string Icon {
+			get { return "gtk-media-play"; }
+		}
+			
+		public override IEnumerable<Type> SupportedItemTypes {
+			get { yield return typeof (TorrentItem); }
+		}
+    
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+			var hashes = items.Cast<TorrentItem>().Select(t => t.HashString);
+			api.StartTorrents(hashes);
+			
+			return null;
+		}
+	}
+}

=== added file 'Transmission/src/TorrentStopAction.cs'
--- Transmission/src/TorrentStopAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentStopAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,42 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+
+namespace Transmission {
+	
+	public class TorrentStopAction: Act {
+		
+		public TorrentStopAction() {
+		}
+		
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Stop"); }
+	    }
+		
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Stop downloading torrent"); }
+		}
+		
+		public override string Icon {
+			get { return "gtk-media-pause"; }
+		}
+			
+		public override IEnumerable<Type> SupportedItemTypes {
+			get { yield return typeof (TorrentItem); }
+		}
+    
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+			var hashes = items.Cast<TorrentItem>().Select(t => t.HashString);
+			api.StopTorrents(hashes);
+			
+			return null;
+		}
+	}
+}

=== added file 'Transmission/src/TorrentUnmarkForDownloadAction.cs'
--- Transmission/src/TorrentUnmarkForDownloadAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentUnmarkForDownloadAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,62 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentUnmarkForDownloadAction: Act {
+
+		public TorrentUnmarkForDownloadAction() {
+		}
+
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Unmark for download"); }
+	    }
+
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Unmark file as needed to be downloaded"); }
+		}
+
+		public override string Icon {
+			get { return "remove"; }
+		}
+
+		public override IEnumerable<Type> SupportedItemTypes {
+			get {
+				yield return typeof (ITorrentEntry);
+			}
+		}
+
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+			// Operation is common for all files.
+			TransmissionAPI.FileOperation operation = new TransmissionAPI.FileOperation(false, null);
+
+			// Group selected items by owner torrent.
+			var files_by_torrent = items
+				.Cast<ITorrentEntry>()
+				.GroupBy(
+					item => item.Torrent,
+					(torrent, entries) => new {
+						Torrent = torrent,
+						Files = entries.SelectMany(entry => entry.GetFiles())
+					}
+				);
+
+			// Perform action for each torrent separately.
+			foreach (var group in files_by_torrent) {
+				var operations = group.Files.ToDictionary(f => f.Index, f => operation);
+				api.SetTorrent(group.Torrent.HashString, null, null, null, null, null, operations);
+			}
+
+			yield break;
+		}
+	}
+}

=== added file 'Transmission/src/TorrentVerifyAction.cs'
--- Transmission/src/TorrentVerifyAction.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TorrentVerifyAction.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,42 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+using Mono.Addins;
+
+using Do.Universe;
+
+namespace Transmission {
+
+	public class TorrentVerifyAction: Act {
+
+		public TorrentVerifyAction() {
+		}
+
+	    public override string Name {
+			get { return AddinManager.CurrentLocalizer.GetString ("Verify"); }
+	    }
+
+		public override string Description {
+			get { return AddinManager.CurrentLocalizer.GetString ("Verify torrent"); }
+		}
+
+		public override string Icon {
+			get { return "dialog-question"; }
+		}
+
+		public override IEnumerable<Type> SupportedItemTypes {
+			get { yield return typeof (TorrentItem); }
+		}
+
+		public override IEnumerable<Item> Perform(IEnumerable<Item> items, IEnumerable<Item> modItems) {
+			TransmissionAPI api = TransmissionPlugin.getTransmission();
+
+			var hashes = items.Cast<TorrentItem>().Select(t => t.HashString);
+			api.VerifyTorrents(hashes);
+
+			return null;
+		}
+	}
+}

=== added file 'Transmission/src/TransmissionAPI.cs'
--- Transmission/src/TransmissionAPI.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TransmissionAPI.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,575 @@
+
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Net;
+using System.Text;
+using Jayrock.Json;
+
+namespace Transmission {
+
+	/// <summary>
+	/// Transmission API client.
+	/// Compatible with RPC version 5 to 10 (release version 1.60 to 2.10).
+	/// </summary>
+	public class TransmissionAPI {
+
+		/// <summary>
+		/// File loading priority.
+		/// </summary>
+		public enum FilePriority {
+			Low, Normal, High
+		};
+
+		/// <summary>
+		/// Operation on individual file from torrent.
+		/// </summary>
+		/// <remarks>
+		/// All fields are nullable, <c>null</c> means "don't change current value".
+		/// </remarks>
+		public struct FileOperation {
+			public FileOperation(bool? download, FilePriority? priority) {
+				this.download = download;
+				this.priority = priority;
+			}
+
+			/// <summary>Whether it is needed to download this file</summary>
+			public bool? download;
+
+			/// <summary>Priority relative to other files of the same torrent</summary>
+			public FilePriority? priority;
+		};
+
+		/// <summary>
+		/// Error communicating to Transmission.
+		/// </summary>
+		public class TransmissionAPIError: Exception {
+			public TransmissionAPIError(string message): base(message) {}
+			public TransmissionAPIError(string message, Exception reason): base(message, reason) {}
+		};
+
+		/// <summary>
+		/// Error returned by Transmission.
+		/// </summary>
+		public class TransmissionError: Exception {
+			public TransmissionError(string message): base(message) {
+			}
+		};
+
+		public const int DEFAULT_PORT = 9091;
+		public const string DEFAULT_PATH = "/transmission/rpc";
+		public const string SESSION_HEADER = "X-Transmission-Session-Id";
+
+		private string _url, _username, _password;
+		private string _session_id = "";
+
+		private delegate void ResultReader(JsonReader json);
+
+		/// <summary>Response handler which does nothing</summary>
+		/// <remarks>Prefer using <see cref="Call(string, IDictionary<string, object>)"/> instead</remarks>
+		private void NullHandler(JsonReader json) {}
+
+		/// <summary>
+		/// Create API client.
+		/// </summary>
+		/// <param name="url">Transmission API-RPC URL</param>
+		/// <param name="username">Username for authentication</param>
+		/// <param name="password">Password for authentication</param>
+		/// <remarks>Pass <c>null</c> for both <paramref="username"/> and <paramref="username"/> is
+		/// authentication isn't needed.</remarks>
+		public TransmissionAPI(string url, string username, string password) {
+			_url = url;
+			_username = username;
+			_password = password;
+		}
+
+		/// <summary>Compose request JSON string</summary>
+		protected string ComposeRequest(string method, IDictionary<string, object> arguments) {
+			StringWriter sw = new StringWriter();
+
+			using (JsonWriter json = new JsonTextWriter(sw)) {
+				json.WriteStartObject();
+
+				json.WriteMember("method");
+				json.WriteString(method);
+
+				json.WriteMember("arguments");
+				json.WriteStartObject();
+				foreach (KeyValuePair<string, object> pair in arguments) {
+					string name = pair.Key;
+					object value = pair.Value;
+
+					json.WriteMember(name);
+					Jayrock.Json.Conversion.JsonConvert.Export(value, json);
+				}
+				json.WriteEndObject();
+
+				json.WriteEndObject();
+			}
+
+			return sw.ToString();
+		}
+
+		/// <summary></summary>
+		/// <param name="payload">HTTP POST request content</param>
+		/// <returns>HTTP response content</returns>
+		/// <exception cref="System.Net.WebException"/>
+		protected string PerformRequest(byte[] payload) {
+			// Prepare request.
+			HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_url);
+			request.Method = "POST";
+			request.ContentType = "application/x-www-form-urlencoded";
+			request.Headers[SESSION_HEADER] = _session_id;
+			request.ContentLength = payload.Length;
+
+			// Authenticate if credentials are given.
+			if (_username != null && _password != null) {
+				string auth = Convert.ToBase64String(Encoding.Default.GetBytes(_username + ":" + _password));
+				request.Headers["Authorization"] = "Basic " + auth;
+			}
+
+			// Perform request.
+			using (Stream stream = request.GetRequestStream()) {
+				stream.Write(payload, 0, payload.Length);
+			}
+
+			// Get response.
+			HttpWebResponse response = (HttpWebResponse)request.GetResponse();
+			return new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8).ReadToEnd();
+		}
+
+		/// <summary>
+		/// Call Transmission API method.
+		/// </summary>
+		/// <param name="method">API method name</param>
+		/// <param name="arguments">Arguments passed to API method</param>
+		/// <param name="handler">Function called with value returned by method</param>
+		/// <exception cref="TransmissionError" />
+		/// <exception cref="TransmissionAPIError" />
+		private void Call(string method, IDictionary<string, object> arguments, ResultReader handler) {
+			// Compose request and encode it to UTF-8.
+			string req = ComposeRequest(method, arguments);
+			byte[] reqb = System.Text.Encoding.UTF8.GetBytes(req);
+
+			try {
+
+				string resp = null;
+				try {
+					resp = PerformRequest(reqb);
+
+				} catch (System.Net.WebException err) {
+					HttpWebResponse response = (HttpWebResponse)err.Response;
+
+					// Transmission 1.53 and 1.6 introduced X-Transmission-Session-Id header
+					// in order to protect from CSRF attacks. If you make request without
+					// this header (or your session expire) you'll get 409 response with
+					// this header set. Just copy header to your request and try again.
+					if (
+						err.Status == WebExceptionStatus.ProtocolError &&
+						response.StatusCode == HttpStatusCode.Conflict
+					) {
+						_session_id = response.Headers[SESSION_HEADER];
+						resp = PerformRequest(reqb);
+
+					} else {
+						throw;
+					}
+				}
+
+				JsonReader json_reader = new JsonTextReader(new StringReader(resp));
+
+				json_reader.ReadToken(JsonTokenClass.Object);
+				while (json_reader.TokenClass == JsonTokenClass.Member) {
+					switch (json_reader.ReadMember()) {
+					case "result":
+						string result = json_reader.ReadString();
+						if (result != "success")
+							throw new TransmissionError(result);
+						break;
+					case "tag":
+						json_reader.ReadNumber();
+						break;
+					case "arguments":
+						json_reader.ReadToken(JsonTokenClass.Object);
+						handler(json_reader);
+						json_reader.ReadToken(JsonTokenClass.EndObject);
+						break;
+					default:
+						json_reader.Skip();
+						break;
+					}
+				}
+				json_reader.ReadToken(JsonTokenClass.EndObject);
+				json_reader.ReadToken(JsonTokenClass.EOF);
+
+			} catch (System.Net.WebException err) {
+				throw new TransmissionAPIError("Cannot access Transmission RPC service", err);
+			}
+		}
+
+		/// <summary>Call API method and ignore return value</summary>
+		/// <remarks>This is equivalent to <c>Call(method, arguments, NullHandler)</c>.</remarks>
+		private void Call(string method, IDictionary<string, object> arguments) {
+			Call(method, arguments, NullHandler);
+		}
+
+		/// <summary>
+		/// Start torrents.
+		/// </summary>
+		/// <param name="torrent_hashes">Sequence of torrent's hashes</param>
+		/// <exception cref="TransmissionAPIError" />
+		/// <exception cref="TransmissionError" />
+		public void StartTorrents(IEnumerable<string> torrent_hashes) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+			arguments.Add("ids", torrent_hashes);
+			Call("torrent-start", arguments);
+		}
+
+		/// <summary>
+		/// Start all torrents.
+		/// </summary>
+		/// <exception cref="TransmissionAPIError" />
+		/// <exception cref="TransmissionError" />
+		public void StartAllTorrents() {
+			Call("torrent-start", new Dictionary<string, object>());
+		}
+
+		/// <summary>
+		/// Stop torrents.
+		/// </summary>
+		/// <param name="torrent_hashes">Sequence of torrent's hashes</param>
+		/// <exception cref="TransmissionAPIError" />
+		/// <exception cref="TransmissionError" />
+		public void StopTorrents(IEnumerable<string> torrent_hashes) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+			arguments.Add("ids", torrent_hashes);
+			Call("torrent-stop", arguments);
+		}
+
+		/// <summary>
+		/// Stop all torrents.
+		/// </summary>
+		/// <exception cref="TransmissionAPIError" />
+		/// <exception cref="TransmissionError" />
+		public void StopAllTorrents() {
+			Call("torrent-stop", new Dictionary<string, object>());
+		}
+
+		/// <summary>
+		/// Start torrents verification.
+		/// </summary>
+		/// <param name="torrent_hashes">Sequence of torrent's hashes to verify</param>
+		/// <exception cref="TransmissionAPIError" />
+		/// <exception cref="TransmissionError" />
+		public void VerifyTorrents(IEnumerable<string> torrent_hashes) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+			arguments.Add("ids", torrent_hashes);
+			Call("torrent-verify", arguments);
+		}
+
+		/// <summary>
+		/// Start torrents verification for all torrents.
+		/// </summary>
+		/// <exception cref="TransmissionAPIError" />
+		/// <exception cref="TransmissionError" />
+		public void VerifyAllTorrents() {
+			Call("torrent-verify", new Dictionary<string, object>());
+		}
+
+		/// <summary>
+		/// Set torrents' properties.
+		///
+		/// Not all torrent properties can be changed using this method, because some of them
+		/// are meaningless for torrent group, e.g. file priorities. Use <c>SetTorrent</c> method
+		/// to set such properties.
+		/// </summary>
+		/// <param name="torrent_hashes">Sequence of torrents hashed to modify</param>
+		/// <param name="peer_limit">Maximum number of used peers, if it is <c>null</c> then value won't be changed.</param>
+		/// <param name="limit_download">
+		/// Downloading speed limit switch, if it is <c>true</c>, downloading speed will be limited,
+		/// if it is <c>false</c>, downloading speed isn't limited, if it is <c>null</c>, limit switch won't be changed.
+		/// A <see cref="System.Nullable"/>
+		/// </param>
+		/// <param name="download_speed_limit">
+		/// A <see cref="System.Nullable"/>
+		/// </param>
+		/// <param name="limit_upload">
+		/// A <see cref="System.Nullable"/>
+		/// </param>
+		/// <param name="upload_speed_limit">
+		/// A <see cref="System.Nullable"/>
+		/// </param>
+		public void SetTorrents(IEnumerable<string> torrent_hashes, int? peer_limit, bool? limit_download, int? download_speed_limit, bool? limit_upload, int? upload_speed_limit) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+
+			arguments.Add("ids", torrent_hashes);
+
+			if (peer_limit != null)
+				arguments.Add("peer-limit", peer_limit);
+
+			if (limit_download.HasValue) {
+				if (limit_download.Value) {
+					arguments.Add("downloadLimited", true);
+					if (download_speed_limit.HasValue)
+						arguments.Add("downloadLimit", download_speed_limit.Value);
+				} else {
+					arguments.Add("downloadLimited", false);
+				}
+			}
+
+			if (limit_upload.HasValue) {
+				if (limit_upload.Value) {
+					arguments.Add("uploadLimited", true);
+					if (upload_speed_limit.HasValue)
+						arguments.Add("uploadLimit", upload_speed_limit.Value);
+				} else {
+					arguments.Add("uploadLimited", false);
+				}
+			}
+
+			Call("torrent-set", arguments);
+		}
+
+		public void SetTorrent(string torrent_hash, int? peer_limit, bool? limit_download, int? download_speed_limit, bool? limit_upload, int? upload_speed_limit, IDictionary<int, FileOperation> files) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+
+			arguments.Add("ids", new string[] { torrent_hash });
+
+			if (peer_limit != null)
+				arguments.Add("peer-limit", peer_limit);
+
+			if (limit_download.HasValue) {
+				if (limit_download.Value) {
+					arguments.Add("downloadLimited", true);
+					if (download_speed_limit.HasValue)
+						arguments.Add("downloadLimit", download_speed_limit.Value);
+				} else {
+					arguments.Add("downloadLimited", false);
+				}
+			}
+
+			if (limit_upload.HasValue) {
+				if (limit_upload.Value) {
+					arguments.Add("uploadLimited", true);
+					if (upload_speed_limit.HasValue)
+						arguments.Add("uploadLimit", upload_speed_limit.Value);
+				} else {
+					arguments.Add("uploadLimited", false);
+				}
+			}
+
+			List<int> wanted_files = new List<int>();
+			List<int> unwanted_files = new List<int>();
+			List<int> low_priority_files = new List<int>();
+			List<int> normal_priority_files = new List<int>();
+			List<int> high_priority_files = new List<int>();
+			foreach (KeyValuePair<int, FileOperation> op in files) {
+				int index = op.Key;
+				FileOperation file = op.Value;
+
+				if (file.download.HasValue) {
+					if (file.download.Value) wanted_files.Add(index);
+					else unwanted_files.Add(index);
+				}
+
+				if (file.priority.HasValue) {
+					switch (file.priority.Value) {
+					case FilePriority.Low: low_priority_files.Add(index); break;
+					case FilePriority.Normal: normal_priority_files.Add(index); break;
+					case FilePriority.High: high_priority_files.Add(index); break;
+					}
+				}
+			}
+			if (wanted_files.Count > 0) arguments.Add("files-wanted", wanted_files);
+			if (unwanted_files.Count > 0) arguments.Add("files-unwanted", unwanted_files);
+			if (low_priority_files.Count > 0) arguments.Add("priority-low", low_priority_files);
+			if (normal_priority_files.Count > 0) arguments.Add("priority-normal", normal_priority_files);
+			if (high_priority_files.Count > 0) arguments.Add("priority-high", high_priority_files);
+
+			Call("torrent-set", arguments);
+		}
+
+		/// <summary>
+		/// Torrent status.
+		/// </summary>
+		public enum TorrentStatus {
+			CheckWait = 1, Check = 2, Download = 4, Seed = 8, Stopped = 16
+		};
+
+		public class TorrentInfo {
+			public int Id; // Torrent's unique ID within Transmission.
+			public string Comment;
+			public string HashString;
+			public string Name;
+			public string DownloadDir;
+			public IList<TorrentFileInfo> files;
+			public TorrentStatus Status;
+			public long TotalSize;
+
+			public int DownloadLimit;
+			public bool DownloadLimited;
+			public int UploadLimit;
+			public bool UploadLimited;
+		};
+
+		public class TorrentFileInfo {
+			public string Name;
+			public long Length;
+			public long BytesCompleted;
+			public bool Wanted;
+			public FilePriority Priority;
+		};
+
+		/// <summary>Get information about all torrents.</summary>
+		/// <remarks>This is equivalent to <c>GetTorrents(null)</c>.</remarks>
+		public IEnumerable<TorrentInfo> GetAllTorrents() {
+			return GetTorrents(null);
+		}
+
+		public IEnumerable<TorrentInfo> GetTorrents(IEnumerable<string> torrent_hashes) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+			if (torrent_hashes != null) arguments.Add("ids", torrent_hashes);
+			arguments.Add("fields", new string[] {"comment", "downloadDir", "files", "hashString", "id", "name", "priorities", "status", "totalSize", "wanted", "downloadLimited", "downloadLimit", "uploadLimited", "uploadLimit"});
+
+			List<TorrentInfo> torrents = new List<TorrentInfo>();
+
+			Jayrock.Json.Conversion.ImportContext jsonctx = new Jayrock.Json.Conversion.ImportContext();
+			jsonctx.Register(new ListImporter<int>());
+			jsonctx.Register(new ListImporter<TorrentFileInfo>());
+
+			Call("torrent-get", arguments, delegate(JsonReader json) {
+				while (json.TokenClass == JsonTokenClass.Member) {
+					switch (json.ReadMember()) {
+					case "torrents":
+						json.ReadToken(JsonTokenClass.Array);
+						while (json.TokenClass != JsonTokenClass.EndArray) {
+
+							json.ReadToken(JsonTokenClass.Object);
+
+							TorrentInfo torrent = new TorrentInfo();
+							IList<TorrentFileInfo> files = null;
+							IList<int> wanted = null;
+							IList<int> priorities = null;
+
+							while (json.TokenClass == JsonTokenClass.Member) {
+								switch (json.ReadMember()) {
+								case "comment": torrent.Comment = json.ReadString(); break;
+								case "hashString": torrent.HashString = json.ReadString(); break;
+								case "name": torrent.Name = json.ReadString(); break;
+								case "id": torrent.Id = json.ReadNumber().ToInt32(); break;
+								case "downloadDir": torrent.DownloadDir = json.ReadString(); break;
+								case "status": torrent.Status = (TorrentStatus)json.ReadNumber().ToInt32(); break;
+								case "totalSize": torrent.TotalSize = json.ReadNumber().ToInt64(); break;
+								case "wanted": wanted = (IList<int>)jsonctx.Import(typeof(List<int>), json); break;
+								case "priorities": priorities = (IList<int>)jsonctx.Import(typeof(List<int>), json); break;
+								case "files": files = jsonctx.Import(typeof(List<TorrentFileInfo>), json) as IList<TorrentFileInfo>; break;
+								case "downloadLimited": torrent.DownloadLimited = json.ReadBoolean(); break;
+								case "downloadLimit": torrent.DownloadLimit = json.ReadNumber().ToInt32(); break;
+								case "uploadLimited": torrent.UploadLimited = json.ReadBoolean(); break;
+								case "uploadLimit": torrent.UploadLimit = json.ReadNumber().ToInt32(); break;
+								}
+							}
+
+							for (int i = 0; i < files.Count; ++i) {
+								files[i].Wanted = wanted[i] == 1;
+
+								int prio = priorities[i];
+								if (prio < -1 || prio > 1)
+									throw new TransmissionAPIError(string.Format("Invalid priority value: {0}", prio));
+								files[i].Priority = (FilePriority)(prio+1);
+							}
+							torrent.files = files;
+							torrents.Add(torrent);
+
+							json.ReadToken(JsonTokenClass.EndObject);
+						}
+						json.ReadToken(JsonTokenClass.EndArray);
+						break;
+					default:
+						json.Skip();
+						break;
+					}
+				}
+			});
+
+			return torrents;
+		}
+
+		// Transmission RPC allows to use .torrent file content instead of it's filename, but
+		// this isn't supported.
+		public void AddTorrent(string filename, string download_to, bool paused, int? peer_limit) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+			arguments.Add("filename", filename);
+			arguments.Add("download-dir", download_to);
+			arguments.Add("paused", paused);
+			if (peer_limit.HasValue) arguments.Add("peer-limit", peer_limit.Value);
+			Call("torrent-add", arguments);
+		}
+
+		/// <summary>
+		/// Remove torrents.
+		/// </summary>
+		/// <param name="hashes">Sequence of torrent hashes to remove.</param>
+		/// <param name="delete_files">Whether downloaded files should be deleted or not.</param>
+		public void RemoveTorrent(IEnumerable<string> hashes, bool delete_files) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+			arguments.Add("ids", hashes);
+			arguments.Add("delete-load-data", delete_files);
+			Call("torrent-remove", arguments);
+		}
+
+		/// <summary>
+		/// Set session parameters.
+		/// </summary>
+		/// <param name="download_dir">
+		/// Path to directory to download torrent contents to.
+		/// Pass <c>null</c> to keep old value.
+		/// </param>
+		/// <param name="peer_limit">
+		/// Global limit on number of connected peers. Pass <c>null</c> to keep old value.
+		/// </param>
+		/// <param name="limit_download">
+		/// Global limit on number of connected peers. Pass <c>null</c> to keep old value.
+		/// </param>
+		public void SetSession(
+			string download_dir, int? peer_limit,
+			bool? limit_download, int? download_speed_limit, bool? limit_upload, int? upload_speed_limit
+		) {
+			Dictionary<string, object> arguments = new Dictionary<string, object>();
+
+			if (download_dir != null)
+				arguments.Add("download-dir", download_dir);
+
+			if (peer_limit.HasValue)
+				arguments.Add("peer-limit", peer_limit.Value);
+
+			if (limit_download.HasValue) {
+				if (limit_download.Value) {
+					arguments.Add("downloadLimited", true);
+					if (download_speed_limit.HasValue)
+						arguments.Add("downloadLimit", download_speed_limit.Value);
+				} else {
+					arguments.Add("downloadLimited", false);
+				}
+			}
+
+			if (limit_upload.HasValue) {
+				if (limit_upload.Value) {
+					arguments.Add("uploadLimited", true);
+					if (upload_speed_limit.HasValue)
+						arguments.Add("uploadLimit", upload_speed_limit.Value);
+				} else {
+					arguments.Add("uploadLimited", false);
+				}
+			}
+
+			Call("session-set", arguments);
+		}
+
+		public void GetSession() {
+			Call("session-get", new Dictionary<string, object>());
+		}
+
+	}
+}

=== added file 'Transmission/src/TransmissionPlugin.cs'
--- Transmission/src/TransmissionPlugin.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/TransmissionPlugin.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+
+using Do.Platform;
+
+namespace Transmission {
+
+	public class ConnectionParameters {
+		public ConnectionParameters(string url, string username, string password) {
+			this.url = url;
+			this.username = username;
+			this.password = password;
+		}
+
+		public string url;
+		public string username;
+		public string password;
+	};
+
+	public class TransmissionPlugin {
+		private static TransmissionAPI transmission;
+
+		public static TransmissionAPI getTransmission() {
+			if (transmission == null) {
+				ConnectionParameters p = getTransmissionConnectionParameters();
+				Log<TransmissionPlugin>.Info("Using Transmission on {0}", p.url);
+				Log<TransmissionPlugin>.Debug("Using name, password: {0}:{1}", p.username, p.password);
+				transmission = new TransmissionAPI(p.url, p.username, p.password);
+			}
+			return transmission;
+		}
+
+		public static void ResetConnection() {
+			transmission = null;
+		}
+
+		public static ConnectionParameters getTransmissionConnectionParameters() {
+			string host = TransmissionConfig.Address;
+			int port = TransmissionConfig.Port;
+			string username = TransmissionConfig.UserName;
+			string password = TransmissionConfig.Password;
+
+			string url = string.Format("http://{0}:{1}/transmission/rpc";, host, port);
+			return new ConnectionParameters(url, username, password);
+		}
+
+	};
+
+}
+

=== added file 'Transmission/src/Utils.cs'
--- Transmission/src/Utils.cs	1970-01-01 00:00:00 +0000
+++ Transmission/src/Utils.cs	2010-07-26 16:47:47 +0000
@@ -0,0 +1,111 @@
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+using Do.Platform;
+using Do.Universe;
+
+namespace Transmission {
+	
+	class Utils {
+
+		public static int ParseSpeed(string speed) {
+			Regex regex = new Regex(
+				@"^(\d+)\s*(b|[km]i?b?)$",
+				RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace
+			);
+			Match match = regex.Match(speed);
+
+			if (match.Success) {
+				int number = int.Parse(match.Groups[1].Value);
+				string unit = match.Groups[2].Value.ToLower();
+				int scale = 1;
+
+				if (unit == "" || unit[0] == 'k') scale = 1;
+				else if (unit[0] == 'm') scale = 1024;
+
+				return number * scale;
+
+			} else {
+				throw new ArgumentException("Invalid speed string");
+			}
+		}
+
+		public static string FormatAmount(float amount, string baseFormat, float[] scales, string[] formats) {
+			if (scales.Length != formats.Length)
+				throw new ArgumentException("'scales' and 'formats' arguments must have equal length");
+
+			// The typical scales count is three to five, so don't use binary search,
+			// but just plain reverse loop.
+			for (int i = scales.Length-1; i >= 0; --i) {
+				if (amount >= scales[i])
+					return string.Format(formats[i], amount / scales[i]);
+			}
+
+			return string.Format(baseFormat, amount);
+		}
+
+		// Format speed in KiB/sec into human-readable representation.
+		public static string FormatSpeed(int speed_kbytes_sec) {
+			return FormatAmount(speed_kbytes_sec, "{0} KiB/sec",
+				new float[]  {             1024,         1024*1024},
+				new string[] {"{0:#.#} MiB/sec", "{0:#.#} GiB/sec"}
+			);
+		}
+
+		// Format size in bytes into human-readable representation.
+		public static string FormatSize(long size_bytes) {
+			return FormatAmount(size_bytes, "{0} B",
+				new float[]  {         1024,     1024*1024, 1024*1024*1024},
+				new string[] {"{0:#.#} KiB", "{0:#.#} MiB", "{0:#.##} GiB"}
+			);
+		}
+
+		public readonly static IEnumerable<PredefinedSpeed> PredefinedSpeedItems = new List<PredefinedSpeed>() {
+			new PredefinedSpeed(        10,  "10 KiB/sec", ""),
+			new PredefinedSpeed(        20,  "20 KiB/sec", ""),
+			new PredefinedSpeed(        50,  "50 KiB/sec", ""),
+			new PredefinedSpeed(       100, "100 KiB/sec", ""),
+			new PredefinedSpeed(       200, "200 KiB/sec", ""),
+			new PredefinedSpeed(       500, "500 KiB/sec", ""),
+			new PredefinedSpeed(  1 * 1024,   "1 MiB/sec", ""),
+			new PredefinedSpeed(  2 * 1024,   "2 MiB/sec", ""),
+			new PredefinedSpeed(  5 * 1024,   "5 MiB/sec", ""),
+			new PredefinedSpeed( 10 * 1024,  "10 MiB/sec", ""),
+			new PredefinedSpeed( 20 * 1024,  "20 MiB/sec", ""),
+			new PredefinedSpeed( 50 * 1024,  "50 MiB/sec", ""),
+			new PredefinedSpeed(100 * 1024, "100 MiB/sec", ""),
+		};
+
+	}
+
+	public class PredefinedSpeed: Item {
+		private string _name, _desc;
+		private int _value;
+
+		public PredefinedSpeed(int value, string name, string desc) {
+			_value = value;
+			_name = name;
+			_desc = desc;
+		}
+
+		public override string Name {
+			get { return _name; }
+		}
+		
+		public override string Description {
+			get { return _desc; }
+		}
+
+		public override string Icon {
+			get { return "top"; }
+		}
+
+		public int Value {
+			get { return _value; }
+		}
+	}
+
+}

=== modified file 'configure.ac'
--- configure.ac	2010-03-08 02:43:56 +0000
+++ configure.ac	2010-07-26 16:47:47 +0000
@@ -383,6 +383,8 @@
 Tracker/Resources/TrackerSearch.addin.xml
 Translate/Makefile
 Translate/Resources/Translate.addin.xml
+Transmission/Makefile
+Transmission/Resources/Transmission.addin.xml
 Tomboy/Makefile
 Tomboy/Resources/Tomboy.addin.xml
 Vinagre/Makefile