[SCM] earcandy packaging branch, upstream, updated. upstream/0.5-1-gabb2267

fladi-guest at users.alioth.debian.org fladi-guest at users.alioth.debian.org
Wed Sep 1 10:34:11 UTC 2010


The following commit has been merged in the upstream branch:
commit abb226744082c90aa3c7dcfdff0bc71090e49f44
Author: Fladischer Michael <FladischerMichael at fladi.at>
Date:   Fri Aug 27 21:51:12 2010 +0200

    Imported Upstream version 0.9

diff --git a/.quickly b/.quickly
new file mode 100644
index 0000000..e14aa10
--- /dev/null
+++ b/.quickly
@@ -0,0 +1,3 @@
+project = earcandy
+template = ubuntu-project
+format = 0.2.5
diff --git a/COPYING b/COPYING
deleted file mode 100644
index 1bb399f..0000000
--- a/COPYING
+++ /dev/null
@@ -1,341 +0,0 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
-Public License instead of this License.
-
diff --git a/Copyright b/Copyright
new file mode 100644
index 0000000..659103a
--- /dev/null
+++ b/Copyright
@@ -0,0 +1,3 @@
+# Copyright (C) YYYY <Your Name> <Your E-mail>
+### BEGIN AUTOMATIC LICENSE GENERATION
+### END AUTOMATIC LICENSE GENERATION
diff --git a/README b/README
deleted file mode 100644
index ba3eda7..0000000
--- a/README
+++ /dev/null
@@ -1,4 +0,0 @@
-Ear Candy is a sound manager that controls pulseaudio to create a seamless sound experience
-
-Please note that much of the functionality should eventually make its way into pulseaudio its self
-
diff --git a/bin/earcandy b/bin/earcandy
new file mode 100755
index 0000000..a978b40
--- /dev/null
+++ b/bin/earcandy
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import sys
+import os
+import gtk
+import logging
+
+# Check if we are working in the source tree or from the installed 
+# package and mangle the python path accordingly
+if os.path.dirname(sys.argv[0]) != ".":
+    if sys.argv[0][0] == "/":
+        fullPath = os.path.dirname(sys.argv[0])
+    else:
+        fullPath = os.getcwd() + "/" + os.path.dirname(sys.argv[0])
+else:
+    fullPath = os.getcwd()
+sys.path.insert(0, os.path.dirname(fullPath))
+
+from earcandy.earcandyconfig import getdatapath
+from earcandy.EarCandy import EarCandy
+
+if __name__ == "__main__":
+
+    #run the application
+    logging.basicConfig(level=logging.INFO)
+    
+    gtk.window_set_default_icon_from_file(os.path.join(getdatapath(), 'media', 'icon.png'))
+
+    # Turn on gtk threading
+    gtk.gdk.threads_init()
+
+    ec = EarCandy()
+    ec.run()
+
+    gtk.main()
diff --git a/files/settings.xml b/data/defaults/settings.xml
similarity index 96%
rename from files/settings.xml
rename to data/defaults/settings.xml
index 1c449ce..a4ab937 100644
--- a/files/settings.xml
+++ b/data/defaults/settings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-    <earcandy fade_timer_speed="0.1" follow_new_outputs="True" tray_visible="True" version="0.5">
+    <earcandy fade_timer_speed="0.015" follow_new_outputs="True" tray_visible="True" version="0.6">
     <rules>
     <rule apply_volume_meter_hack="True" category="music" description="File Manager" fade_volume="True" icon_name="system-file-manager" name="totem-audio-preview" rule_re_application="nautilus" rule_re_command=".*\/nautilus" rule_re_window_title="" volume_default="100" volume_mute="-1" window_position_fade="True" />
     <rule apply_volume_meter_hack="True" category="video" description="Totem Movie Player" fade_volume="True" icon_name="totem" name="Totem Movie Player" rule_re_application="totem movie player" rule_re_command=".*\/totem movie player" rule_re_window_title="" volume_default="100" volume_mute="-1" window_position_fade="True"/>
diff --git a/data/media/earcandy.png b/data/media/earcandy.png
new file mode 100644
index 0000000..9b18fb7
Binary files /dev/null and b/data/media/earcandy.png differ
diff --git a/data/media/icon.png b/data/media/icon.png
new file mode 100644
index 0000000..9b18fb7
Binary files /dev/null and b/data/media/icon.png differ
diff --git a/data/ui/AboutEarcandyDialog.ui b/data/ui/AboutEarcandyDialog.ui
new file mode 100644
index 0000000..939e039
--- /dev/null
+++ b/data/ui/AboutEarcandyDialog.ui
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-requires about_earcandy_dialog 1.0 -->
+  <!-- interface-naming-policy project-wide -->
+  <object class="AboutEarcandyDialog" id="about_earcandy_dialog">
+    <property name="border_width">5</property>
+    <property name="icon">../media/icon.png</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <property name="program_name">Earcandy</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <placeholder/>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/files/pulseoptions.glade b/data/ui/EarCandy.ui
similarity index 54%
rename from files/pulseoptions.glade
rename to data/ui/EarCandy.ui
index 72dcda3..ab9cd1b 100644
--- a/files/pulseoptions.glade
+++ b/data/ui/EarCandy.ui
@@ -1,372 +1,363 @@
 <?xml version="1.0"?>
-<glade-interface>
-  <!-- interface-requires gtk+ 2.16 -->
+<interface>
+  <requires lib="gtk+" version="2.16"/>
   <!-- interface-naming-policy toplevel-contextual -->
-  <widget class="GtkDialog" id="dialog_options">
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="value">0.01</property>
+    <property name="lower">0.01</property>
+    <property name="upper">0.20000000000000001</property>
+    <property name="step_increment">0.01</property>
+    <property name="page_increment">0.01</property>
+    <property name="page_size">0.01</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment2">
+    <property name="value">20</property>
+    <property name="upper">110</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">1</property>
+    <property name="page_size">1</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment3">
+    <property name="value">50</property>
+    <property name="upper">110</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment4">
+    <property name="value">50</property>
+    <property name="upper">110</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment5">
+    <property name="value">50</property>
+    <property name="upper">110</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+    <property name="page_size">10</property>
+  </object>
+  <object class="GtkListStore" id="model1">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">[ automatic ]</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkDialog" id="dialog_options">
     <property name="width_request">500</property>
     <property name="height_request">400</property>
     <property name="border_width">5</property>
-    <property name="title" translatable="yes">Ear Candy</property>
+    <property name="title" translatable="yes">Ear Candy - Audio Clients</property>
     <property name="window_position">center-on-parent</property>
     <property name="type_hint">dialog</property>
     <property name="has_separator">False</property>
     <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox1">
+      <object class="GtkVBox" id="dialog-vbox1">
         <property name="visible">True</property>
         <property name="spacing">2</property>
         <child>
-          <widget class="GtkNotebook" id="notebook1">
+          <object class="GtkNotebook" id="notebook1">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <child>
-              <widget class="GtkVBox" id="vbox1">
+              <object class="GtkVBox" id="vbox1">
                 <property name="visible">True</property>
                 <child>
-                  <widget class="GtkHBox" id="hbox1">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkToolbar" id="toolbar1">
-                        <property name="visible">True</property>
-                        <property name="toolbar_style">both</property>
-                        <child>
-                          <widget class="GtkToolButton" id="toolbutton_add">
-                            <property name="visible">True</property>
-                            <property name="stock_id">gtk-add</property>
-                            <signal name="clicked" handler="on_button_add_new_clicked"/>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="homogeneous">True</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkToolButton" id="toolbutton_edit">
-                            <property name="visible">True</property>
-                            <property name="use_underline">True</property>
-                            <property name="stock_id">gtk-edit</property>
-                            <signal name="clicked" handler="on_toolbutton_edit_clicked" after="yes"/>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="homogeneous">True</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkToolButton" id="toolbutton_delete">
-                            <property name="visible">True</property>
-                            <property name="stock_id">gtk-delete</property>
-                            <signal name="clicked" handler="on_toolbutton_delete_clicked"/>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="homogeneous">True</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="position">0</property>
-                  </packing>
+                  <placeholder/>
                 </child>
                 <child>
-                  <widget class="GtkHBox" id="hbox3">
+                  <object class="GtkHBox" id="hbox3">
                     <property name="visible">True</property>
                     <child>
-                      <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                      <object class="GtkScrolledWindow" id="scrolledwindow1">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="hscrollbar_policy">automatic</property>
-                        <property name="vscrollbar_policy">automatic</property>
                         <property name="shadow_type">etched-in</property>
                         <child>
-                          <widget class="GtkTreeView" id="treeview_pulse_clients">
+                          <object class="GtkViewport" id="viewport1">
                             <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="headers_clickable">False</property>
-                            <signal name="cursor_changed" handler="on_treeview_pulse_clients_cursor_changed"/>
-                            <signal name="row_activated" handler="on_treeview_pulse_clients_row_activated"/>
-                          </widget>
+                            <property name="resize_mode">queue</property>
+                            <property name="shadow_type">none</property>
+                            <child>
+                              <object class="GtkVBox" id="vbox_repeater">
+                                <property name="visible">True</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
                         </child>
-                      </widget>
+                      </object>
                       <packing>
                         <property name="padding">4</property>
                         <property name="position">0</property>
                       </packing>
                     </child>
-                  </widget>
+                  </object>
                   <packing>
                     <property name="padding">4</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
-                <child>
-                  <widget class="GtkHBox" id="hbox5">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkAlignment" id="alignment1">
-                        <property name="visible">True</property>
-                        <property name="xalign">1</property>
-                        <property name="xscale">0</property>
-                        <child>
-                          <widget class="GtkLabel" id="label3">
-                            <property name="visible">True</property>
-                            <property name="label" translatable="yes">Display :</property>
-                          </widget>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="padding">4</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkComboBox" id="combobox_view">
-                        <property name="visible">True</property>
-                        <property name="active">0</property>
-                        <property name="items" translatable="yes">Only active rules
-All rules</property>
-                        <signal name="changed" handler="on_combobox_view_changed"/>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
-                        <property name="padding">4</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                  </widget>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="padding">4</property>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-              </widget>
+              </object>
             </child>
-            <child>
-              <widget class="GtkLabel" id="label2">
+            <child type="tab">
+              <object class="GtkLabel" id="label2">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">Application Rules</property>
-              </widget>
+                <property name="label" translatable="yes">Applications</property>
+              </object>
               <packing>
+                <property name="position">1</property>
                 <property name="tab_fill">False</property>
-                <property name="type">tab</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkHBox" id="hbox2">
+              <object class="GtkHBox" id="hbox2">
                 <property name="visible">True</property>
                 <child>
-                  <widget class="GtkTable" id="table3">
+                  <object class="GtkTable" id="table3">
                     <property name="visible">True</property>
-                    <property name="n_rows">7</property>
+                    <property name="n_rows">11</property>
                     <property name="n_columns">2</property>
                     <child>
-                      <widget class="GtkAlignment" id="alignment6">
+                      <object class="GtkAlignment" id="alignment6">
                         <property name="visible">True</property>
                         <property name="xalign">0</property>
                         <property name="xscale">0</property>
                         <child>
-                          <widget class="GtkCheckButton" id="checkbutton_tray">
+                          <object class="GtkCheckButton" id="checkbutton_tray">
                             <property name="label" translatable="yes">Show Icon</property>
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="receives_default">False</property>
                             <property name="draw_indicator">True</property>
                             <signal name="toggled" handler="on_checkbutton_tray_toggled"/>
-                          </widget>
+                          </object>
                         </child>
-                      </widget>
+                      </object>
                       <packing>
                         <property name="left_attach">1</property>
                         <property name="right_attach">2</property>
-                        <property name="top_attach">2</property>
-                        <property name="bottom_attach">3</property>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label10">
+                      <object class="GtkHScale" id="hscale_fade">
                         <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Notification Tray</property>
-                      </widget>
+                        <property name="can_focus">True</property>
+                        <property name="adjustment">adjustment1</property>
+                        <property name="inverted">True</property>
+                        <property name="digits">2</property>
+                        <property name="draw_value">False</property>
+                        <property name="value_pos">right</property>
+                        <signal name="value_changed" handler="on_hscale_fade_value_changed"/>
+                      </object>
                       <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
                         <property name="top_attach">2</property>
                         <property name="bottom_attach">3</property>
-                        <property name="x_options"></property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label6">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Volume Fade Speed</property>
-                      </widget>
+                      <object class="GtkHScale" id="hscale_mute_level">
+                        <property name="can_focus">True</property>
+                        <property name="adjustment">adjustment2</property>
+                        <property name="digits">0</property>
+                        <property name="value_pos">right</property>
+                        <signal name="value_changed" handler="on_hscale_mute_level_value_changed"/>
+                        <signal name="change_value" handler="on_hscale_mute_level_change_value"/>
+                      </object>
                       <packing>
-                        <property name="x_options"></property>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkHScale" id="hscale_fade">
+                      <object class="GtkCheckButton" id="checkbutton_autostart">
+                        <property name="label" translatable="yes">Start on login</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
-                        <property name="adjustment">0.01 0.01 0.20000000000000001 0.01 0.01 0.01</property>
-                        <property name="inverted">True</property>
-                        <property name="digits">2</property>
-                        <property name="draw_value">False</property>
-                        <property name="value_pos">right</property>
-                        <signal name="value_changed" handler="on_hscale_fade_value_changed"/>
-                      </widget>
+                        <property name="receives_default">False</property>
+                        <property name="draw_indicator">True</property>
+                        <signal name="toggled" handler="on_checkbutton_autostart_toggled"/>
+                      </object>
                       <packing>
                         <property name="left_attach">1</property>
                         <property name="right_attach">2</property>
+                        <property name="top_attach">6</property>
+                        <property name="bottom_attach">7</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label9">
-                        <property name="label" translatable="yes">Partial Mute Level</property>
-                      </widget>
+                      <object class="GtkCheckButton" id="checkbutton_mute_phone">
+                        <property name="label" translatable="yes">Include 'phone' category when muting</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="draw_indicator">True</property>
+                        <signal name="toggled" handler="on_checkbutton_mute_phone_toggled"/>
+                      </object>
                       <packing>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
-                        <property name="x_options"></property>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkHScale" id="hscale_mute_level">
+                      <object class="GtkExpander" id="expander1">
                         <property name="can_focus">True</property>
-                        <property name="adjustment">20 0 110 1 1 1</property>
-                        <property name="digits">0</property>
-                        <property name="value_pos">right</property>
-                        <signal name="value_changed" handler="on_hscale_mute_level_value_changed"/>
-                        <signal name="change_value" handler="on_hscale_mute_level_change_value"/>
-                      </widget>
+                        <child>
+                          <object class="GtkScrolledWindow" id="scrolledwindow2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="border_width">4</property>
+                            <property name="hscrollbar_policy">automatic</property>
+                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="shadow_type">etched-in</property>
+                            <child>
+                              <object class="GtkTreeView" id="treeview_plugins">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="label">
+                          <object class="GtkLabel" id="label13">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Application control plugins</property>
+                          </object>
+                        </child>
+                      </object>
                       <packing>
-                        <property name="left_attach">1</property>
                         <property name="right_attach">2</property>
-                        <property name="top_attach">1</property>
-                        <property name="bottom_attach">2</property>
+                        <property name="top_attach">9</property>
+                        <property name="bottom_attach">10</property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment5">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <object class="GtkLabel" id="label6">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Cross Fade Speed</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label11">
+                      <object class="GtkAlignment" id="alignment7">
                         <property name="visible">True</property>
-                        <property name="label" translatable="yes">Output Device</property>
-                      </widget>
+                        <property name="xalign">0</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <object class="GtkLabel" id="label9">
+                            <property name="label" translatable="yes">Partial Mute Level</property>
+                          </object>
+                        </child>
+                      </object>
                       <packing>
                         <property name="top_attach">3</property>
                         <property name="bottom_attach">4</property>
-                        <property name="x_options"></property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkAlignment" id="alignment7">
-                        <property name="visible">True</property>
+                      <object class="GtkAlignment" id="alignment8">
                         <property name="xalign">0</property>
                         <property name="xscale">0</property>
                         <child>
-                          <widget class="GtkCheckButton" id="checkbutton_output">
-                            <property name="label" translatable="yes">Move streams to new output (USB Headsets)</property>
+                          <object class="GtkLabel" id="label5">
                             <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">False</property>
-                            <property name="draw_indicator">True</property>
-                            <signal name="toggled" handler="on_checkbutton_output_toggled"/>
-                          </widget>
+                            <property name="xalign">0.49000000953674316</property>
+                            <property name="label" translatable="yes">Mute</property>
+                          </object>
                         </child>
-                      </widget>
+                      </object>
                       <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">3</property>
-                        <property name="bottom_attach">4</property>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label15">
-                        <property name="label" translatable="yes">Reset volume</property>
-                      </widget>
+                      <object class="GtkAlignment" id="alignment9">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="xscale">0</property>
+                        <child>
+                          <object class="GtkLabel" id="label10">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Notification Tray</property>
+                          </object>
+                        </child>
+                      </object>
                       <packing>
-                        <property name="top_attach">6</property>
-                        <property name="bottom_attach">7</property>
-                        <property name="x_options"></property>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkAlignment" id="alignment3">
+                      <object class="GtkAlignment" id="alignment10">
+                        <property name="visible">True</property>
                         <property name="xalign">0</property>
                         <property name="xscale">0</property>
                         <child>
-                          <widget class="GtkButton" id="button_reset">
+                          <object class="GtkLabel" id="label4">
                             <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">True</property>
-                            <signal name="clicked" handler="on_button_reset_clicked"/>
-                            <child>
-                              <widget class="GtkHBox" id="hbox4">
-                                <property name="visible">True</property>
-                                <child>
-                                  <widget class="GtkImage" id="image1">
-                                    <property name="visible">True</property>
-                                    <property name="stock">gtk-refresh</property>
-                                    <property name="icon-size">2</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="padding">4</property>
-                                    <property name="position">0</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <widget class="GtkLabel" id="label17">
-                                    <property name="visible">True</property>
-                                    <property name="label" translatable="yes">Reset all volume levels</property>
-                                  </widget>
-                                  <packing>
-                                    <property name="padding">4</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </widget>
-                            </child>
-                          </widget>
+                            <property name="label" translatable="yes">Autostart</property>
+                          </object>
                         </child>
-                      </widget>
+                      </object>
                       <packing>
-                        <property name="left_attach">1</property>
-                        <property name="right_attach">2</property>
                         <property name="top_attach">6</property>
                         <property name="bottom_attach">7</property>
                         <property name="y_options"></property>
@@ -375,121 +366,173 @@ All rules</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkLabel" id="label4">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Autostart</property>
-                      </widget>
+                      <object class="GtkFrame" id="frame1">
+                        <property name="label_xalign">0</property>
+                        <child>
+                          <object class="GtkTable" id="table1">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkScrolledWindow" id="scrolledwindow3">
+                                <property name="height_request">50</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="hscrollbar_policy">automatic</property>
+                                <property name="shadow_type">etched-in</property>
+                                <child>
+                                  <object class="GtkTreeView" id="treeview_outputs">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="headers_visible">False</property>
+                                    <property name="reorderable">True</property>
+                                    <signal name="drag_end" handler="on_treeview_outputs_drag_drop"/>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="x_padding">8</property>
+                                <property name="y_padding">8</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                        <child type="label">
+                          <object class="GtkLabel" id="label3">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes"> Output Devices  </property>
+                            <property name="use_markup">True</property>
+                          </object>
+                        </child>
+                      </object>
                       <packing>
-                        <property name="top_attach">4</property>
-                        <property name="bottom_attach">5</property>
-                        <property name="x_options"></property>
-                        <property name="y_options"></property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">10</property>
+                        <property name="bottom_attach">11</property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkCheckButton" id="checkbutton_autostart">
-                        <property name="label" translatable="yes">Start on login</property>
-                        <property name="visible">True</property>
+                      <object class="GtkButton" id="button1">
+                        <property name="label">gtk-revert-to-saved</property>
                         <property name="can_focus">True</property>
-                        <property name="receives_default">False</property>
-                        <property name="draw_indicator">True</property>
-                        <signal name="toggled" handler="on_checkbutton_autostart_toggled"/>
-                      </widget>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="clicked" handler="on_button_reset_clicked"/>
+                      </object>
                       <packing>
                         <property name="left_attach">1</property>
                         <property name="right_attach">2</property>
-                        <property name="top_attach">4</property>
-                        <property name="bottom_attach">5</property>
+                        <property name="top_attach">7</property>
+                        <property name="bottom_attach">8</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkVBox" id="vbox2">
-                        <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
+                      <object class="GtkAlignment" id="alignment1">
+                        <property name="xalign">0</property>
+                        <property name="xscale">0</property>
                         <child>
-                          <widget class="GtkHSeparator" id="hseparator1"/>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">False</property>
-                            <property name="padding">4</property>
-                            <property name="position">0</property>
-                          </packing>
+                          <object class="GtkLabel" id="label7">
+                            <property name="label" translatable="yes">Restore defaults</property>
+                          </object>
                         </child>
-                      </widget>
+                      </object>
                       <packing>
-                        <property name="right_attach">2</property>
-                        <property name="top_attach">5</property>
-                        <property name="bottom_attach">6</property>
+                        <property name="top_attach">7</property>
+                        <property name="bottom_attach">8</property>
                         <property name="y_options"></property>
                         <property name="x_padding">4</property>
                         <property name="y_padding">4</property>
                       </packing>
                     </child>
-                  </widget>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
                   <packing>
                     <property name="padding">4</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="position">1</property>
               </packing>
             </child>
-            <child>
-              <widget class="GtkLabel" id="label1">
+            <child type="tab">
+              <object class="GtkLabel" id="label1">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">Advanced</property>
-              </widget>
+              </object>
               <packing>
                 <property name="position">1</property>
                 <property name="tab_fill">False</property>
-                <property name="type">tab</property>
               </packing>
             </child>
-          </widget>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <placeholder/>
+            </child>
+          </object>
           <packing>
             <property name="position">1</property>
           </packing>
         </child>
         <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area1">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
             <property name="visible">True</property>
             <property name="layout_style">end</property>
             <child>
               <placeholder/>
             </child>
             <child>
-              <widget class="GtkButton" id="button_close">
-                <property name="label" translatable="yes">gtk-close</property>
+              <object class="GtkButton" id="button_close">
+                <property name="label">gtk-close</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
                 <property name="use_stock">True</property>
                 <signal name="clicked" handler="on_close_button_clicked"/>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="position">1</property>
               </packing>
             </child>
-          </widget>
+          </object>
           <packing>
             <property name="expand">False</property>
             <property name="pack_type">end</property>
             <property name="position">0</property>
           </packing>
         </child>
-      </widget>
+      </object>
     </child>
-  </widget>
-  <widget class="GtkDialog" id="dialog_select">
+    <action-widgets>
+      <action-widget response="0">button_close</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkDialog" id="dialog_select">
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Select PulseAudio Stream</property>
     <property name="modal">True</property>
@@ -497,25 +540,25 @@ All rules</property>
     <property name="type_hint">dialog</property>
     <property name="has_separator">False</property>
     <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox2">
+      <object class="GtkVBox" id="dialog-vbox2">
         <property name="visible">True</property>
         <property name="spacing">2</property>
         <child>
-          <widget class="GtkVBox" id="vbox3">
+          <object class="GtkVBox" id="vbox3">
             <property name="visible">True</property>
             <child>
-              <widget class="GtkAlignment" id="alignment3">
+              <object class="GtkAlignment" id="alignment3">
                 <property name="visible">True</property>
                 <property name="xalign">0</property>
                 <property name="xscale">0</property>
                 <child>
-                  <widget class="GtkLabel" id="label16">
+                  <object class="GtkLabel" id="label16">
                     <property name="visible">True</property>
                     <property name="label" translatable="yes">&lt;b&gt;1) Select pulseaudio client&lt;/b&gt;</property>
                     <property name="use_markup">True</property>
-                  </widget>
+                  </object>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -524,10 +567,9 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkComboBox" id="combobox_clients">
+              <object class="GtkComboBox" id="combobox_clients">
                 <property name="visible">True</property>
-                <property name="items" translatable="yes"></property>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -535,11 +577,11 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkLabel" id="label14">
+              <object class="GtkLabel" id="label14">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">&lt;i&gt;For a client to show here it must have played a sound at least once&lt;/i&gt;</property>
                 <property name="use_markup">True</property>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -548,18 +590,18 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkAlignment" id="alignment4">
+              <object class="GtkAlignment" id="alignment4">
                 <property name="visible">True</property>
                 <property name="xalign">0</property>
                 <property name="xscale">0</property>
                 <child>
-                  <widget class="GtkLabel" id="label4">
+                  <object class="GtkLabel" id="label400">
                     <property name="visible">True</property>
                     <property name="label" translatable="yes">&lt;b&gt;2) Select application&lt;/b&gt;</property>
                     <property name="use_markup">True</property>
-                  </widget>
+                  </object>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -568,30 +610,30 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkScrolledWindow" id="scrolledwindow2">
+              <object class="GtkScrolledWindow" id="scrolledwindow200">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="hscrollbar_policy">automatic</property>
                 <property name="vscrollbar_policy">automatic</property>
                 <property name="shadow_type">etched-in</property>
                 <child>
-                  <widget class="GtkTreeView" id="treeview_applications">
+                  <object class="GtkTreeView" id="treeview_applications">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="headers_visible">False</property>
-                  </widget>
+                  </object>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="position">4</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkLabel" id="label8">
+              <object class="GtkLabel" id="label8">
                 <property name="visible">True</property>
                 <property name="label" translatable="yes">&lt;i&gt;For an application to show here it must be open&lt;/i&gt;</property>
                 <property name="use_markup">True</property>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -600,18 +642,18 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkAlignment" id="alignment5">
+              <object class="GtkAlignment" id="alignment500">
                 <property name="visible">True</property>
                 <property name="xalign">0</property>
                 <property name="xscale">0</property>
                 <child>
-                  <widget class="GtkLabel" id="label5">
+                  <object class="GtkLabel" id="label500">
                     <property name="visible">True</property>
                     <property name="label" translatable="yes">&lt;b&gt;3) Select category&lt;/b&gt;</property>
                     <property name="use_markup">True</property>
-                  </widget>
+                  </object>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -620,35 +662,34 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkComboBox" id="combobox_category">
+              <object class="GtkComboBox" id="combobox_category">
                 <property name="visible">True</property>
-                <property name="items" translatable="yes"></property>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="position">7</property>
               </packing>
             </child>
-          </widget>
+          </object>
           <packing>
             <property name="padding">4</property>
             <property name="position">1</property>
           </packing>
         </child>
         <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area2">
+          <object class="GtkHButtonBox" id="dialog-action_area2">
             <property name="visible">True</property>
             <property name="layout_style">end</property>
             <child>
-              <widget class="GtkButton" id="button_cancel">
-                <property name="label" translatable="yes">gtk-cancel</property>
+              <object class="GtkButton" id="button_cancel">
+                <property name="label">gtk-cancel</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
                 <property name="use_stock">True</property>
                 <signal name="clicked" handler="on_button_cancel_clicked"/>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -656,31 +697,35 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkButton" id="button_add">
-                <property name="label" translatable="yes">gtk-add</property>
+              <object class="GtkButton" id="button_add">
+                <property name="label">gtk-add</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
                 <property name="use_stock">True</property>
                 <signal name="clicked" handler="on_button_add_clicked"/>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="position">1</property>
               </packing>
             </child>
-          </widget>
+          </object>
           <packing>
             <property name="expand">False</property>
             <property name="pack_type">end</property>
             <property name="position">0</property>
           </packing>
         </child>
-      </widget>
+      </object>
     </child>
-  </widget>
-  <widget class="GtkWindow" id="popup_volume_control">
+    <action-widgets>
+      <action-widget response="0">button_cancel</action-widget>
+      <action-widget response="0">button_add</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkWindow" id="popup_volume_control">
     <property name="height_request">150</property>
     <property name="can_focus">True</property>
     <property name="has_focus">True</property>
@@ -694,345 +739,400 @@ All rules</property>
     <property name="window_position">center-on-parent</property>
     <property name="type_hint">popup-menu</property>
     <child>
-      <widget class="GtkHBox" id="hbox4">
+      <object class="GtkHBox" id="hbox4">
         <property name="visible">True</property>
         <child>
-          <widget class="GtkVBox" id="vbox5">
+          <object class="GtkVBox" id="vbox5">
             <property name="visible">True</property>
             <property name="orientation">vertical</property>
             <child>
-              <widget class="GtkButton" id="button_suspend">
+              <object class="GtkButton" id="button_mute">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <property name="tooltip" translatable="yes">Lock volume levels</property>
+                <property name="has_tooltip">True</property>
+                <property name="tooltip_text" translatable="yes">Mute sound</property>
+                <signal name="clicked" handler="on_button_mute_clicked"/>
+                <child>
+                  <object class="GtkImage" id="image_mute">
+                    <property name="visible">True</property>
+                    <property name="icon_name">audio-volume-muted</property>
+                    <property name="icon-size">2</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="padding">4</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button_suspend">
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="tooltip_text" translatable="yes">Lock volume levels</property>
                 <signal name="clicked" handler="on_button_suspend_clicked"/>
                 <child>
-                  <widget class="GtkImage" id="image_status">
+                  <object class="GtkImage" id="image_status">
                     <property name="visible">True</property>
                     <property name="stock">gtk-media-pause</property>
                     <property name="icon-size">2</property>
-                  </widget>
+                  </object>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="padding">4</property>
-                <property name="position">0</property>
+                <property name="position">1</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkVScale" id="vscale_volume">
+              <object class="GtkVScale" id="vscale_volume">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="has_default">True</property>
                 <property name="receives_default">True</property>
                 <property name="orientation">vertical</property>
-                <property name="adjustment">50 0 110 1 10 10</property>
+                <property name="adjustment">adjustment3</property>
                 <property name="inverted">True</property>
                 <property name="digits">0</property>
                 <property name="draw_value">False</property>
                 <property name="value_pos">bottom</property>
                 <signal name="value_changed" handler="on_vscale_volume_value_changed"/>
                 <signal name="change_value" handler="on_vscale_volume_change_value"/>
-              </widget>
+              </object>
               <packing>
                 <property name="padding">4</property>
-                <property name="position">1</property>
+                <property name="position">2</property>
               </packing>
             </child>
-          </widget>
+          </object>
           <packing>
             <property name="expand">False</property>
             <property name="padding">4</property>
             <property name="position">0</property>
           </packing>
         </child>
-      </widget>
+      </object>
     </child>
-  </widget>
-  <widget class="GtkWindow" id="window_client_properties">
+  </object>
+  <object class="GtkWindow" id="window_client_properties">
     <property name="resizable">False</property>
     <property name="modal">True</property>
     <property name="window_position">center-on-parent</property>
     <property name="destroy_with_parent">True</property>
     <property name="type_hint">dialog</property>
     <child>
-      <widget class="GtkHBox" id="hbox1">
+      <object class="GtkHBox" id="hbox1">
         <property name="visible">True</property>
         <child>
-          <widget class="GtkVBox" id="vbox1">
+          <object class="GtkVBox" id="vbox100">
             <property name="visible">True</property>
             <property name="orientation">vertical</property>
             <child>
-              <widget class="GtkHBox" id="hbox2">
+              <object class="GtkVBox" id="vbox300">
                 <property name="visible">True</property>
+                <property name="orientation">vertical</property>
                 <child>
-                  <widget class="GtkImage" id="image_icon">
+                  <object class="GtkHBox" id="hbox200">
                     <property name="visible">True</property>
-                    <property name="stock">gtk-directory</property>
-                    <property name="icon-size">6</property>
-                  </widget>
+                    <child>
+                      <object class="GtkImage" id="image_icon">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-directory</property>
+                        <property name="icon-size">6</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="padding">4</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_name">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;big&gt;Banshee&lt;/big&gt;</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="padding">4</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
                   <packing>
-                    <property name="expand">False</property>
+                    <property name="fill">False</property>
                     <property name="padding">4</property>
                     <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkLabel" id="label_name">
+                  <object class="GtkTable" id="table100">
                     <property name="visible">True</property>
-                    <property name="label" translatable="yes">&lt;big&gt;Banshee&lt;/big&gt;</property>
-                    <property name="use_markup">True</property>
-                  </widget>
+                    <property name="n_rows">7</property>
+                    <property name="n_columns">2</property>
+                    <child>
+                      <object class="GtkLabel" id="label_description">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Description</property>
+                      </object>
+                      <packing>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_name2">
+                        <property name="label" translatable="yes">Category</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">3</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="combobox_category0"/>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="entry_description">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="invisible_char">&#x25CF;</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="checkbutton_fix">
+                        <property name="label" translatable="yes">Use volume meter detection</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_name3">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Activity</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">3</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_name4">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Fade</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">3</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="checkbutton_fade_volume">
+                        <property name="label" translatable="yes">Fade volume</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="xalign">0.56999999284744263</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label60">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Output Device</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="combobox_output">
+                        <property name="visible">True</property>
+                        <property name="model">model1</property>
+                        <property name="active">0</property>
+                        <child>
+                          <object class="GtkCellRendererText" id="renderer1"/>
+                          <attributes>
+                            <attribute name="text">0</attribute>
+                          </attributes>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_name1">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Max</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHScale" id="hscale_max_volume">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="adjustment">adjustment4</property>
+                        <property name="digits">0</property>
+                        <property name="value_pos">left</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">5</property>
+                        <property name="bottom_attach">6</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label_name5">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Min</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">6</property>
+                        <property name="bottom_attach">7</property>
+                        <property name="x_options"></property>
+                        <property name="y_options"></property>
+                        <property name="x_padding">4</property>
+                        <property name="y_padding">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHScale" id="hscale_min_volume">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="adjustment">adjustment5</property>
+                        <property name="digits">0</property>
+                        <property name="value_pos">left</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">6</property>
+                        <property name="bottom_attach">7</property>
+                      </packing>
+                    </child>
+                  </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
-                    <property name="padding">4</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
-              </widget>
+              </object>
               <packing>
+                <property name="expand">False</property>
                 <property name="fill">False</property>
-                <property name="padding">4</property>
                 <property name="position">0</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkTable" id="table1">
-                <property name="visible">True</property>
-                <property name="n_rows">7</property>
-                <property name="n_columns">2</property>
-                <child>
-                  <widget class="GtkLabel" id="label_description">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Description</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label_name1">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Max Volume</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkHScale" id="hscale_max_volume">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="adjustment">100 0 110 1 10 10</property>
-                    <property name="digits">0</property>
-                    <property name="value_pos">left</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">2</property>
-                    <property name="bottom_attach">3</property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label_name2">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Category</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">3</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" id="combobox_category">
-                    <property name="visible">True</property>
-                    <property name="items" translatable="yes"></property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">3</property>
-                    <property name="bottom_attach">4</property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkEntry" id="entry_description">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">1</property>
-                    <property name="bottom_attach">2</property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="checkbutton_fix">
-                    <property name="label" translatable="yes">Use volume meter detection</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label_name3">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Fix</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">4</property>
-                    <property name="bottom_attach">5</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">3</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label_name4">
-                    <property name="visible">True</property>
-                    <property name="label" translatable="yes">Fade</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">5</property>
-                    <property name="bottom_attach">6</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">3</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkCheckButton" id="checkbutton_fade_volume">
-                    <property name="label" translatable="yes">Fade volume</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="xalign">0.56999999284744263</property>
-                    <property name="draw_indicator">True</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">5</property>
-                    <property name="bottom_attach">6</property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkLabel" id="label6">
-                    <property name="label" translatable="yes">Output</property>
-                  </widget>
-                  <packing>
-                    <property name="top_attach">6</property>
-                    <property name="bottom_attach">7</property>
-                    <property name="x_options"></property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <widget class="GtkComboBox" id="combobox_output">
-                    <property name="active">0</property>
-                    <property name="items" translatable="yes">[ automatic ]</property>
-                  </widget>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="right_attach">2</property>
-                    <property name="top_attach">6</property>
-                    <property name="bottom_attach">7</property>
-                    <property name="y_options"></property>
-                    <property name="x_padding">4</property>
-                    <property name="y_padding">4</property>
-                  </packing>
-                </child>
-                <child>
-                  <placeholder/>
-                </child>
-                <child>
-                  <placeholder/>
-                </child>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
+              <placeholder/>
             </child>
             <child>
-              <widget class="GtkExpander" id="expander1">
+              <object class="GtkExpander" id="expander10">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <child>
-                  <widget class="GtkVBox" id="vbox2">
+                  <object class="GtkVBox" id="vbox2">
                     <property name="visible">True</property>
                     <property name="orientation">vertical</property>
                     <child>
-                      <widget class="GtkLabel" id="label5">
+                      <object class="GtkLabel" id="label50">
                         <property name="visible">True</property>
                         <property name="label" translatable="yes">&lt;b&gt;Do not edit these values
  unless you know what you are doing&lt;/b&gt;</property>
                         <property name="use_markup">True</property>
                         <property name="justify">center</property>
-                      </widget>
+                      </object>
                       <packing>
                         <property name="padding">4</property>
                         <property name="position">0</property>
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkTable" id="table2">
+                      <object class="GtkTable" id="table2">
                         <property name="visible">True</property>
                         <property name="n_rows">4</property>
                         <property name="n_columns">2</property>
                         <child>
-                          <widget class="GtkLabel" id="label2">
+                          <object class="GtkLabel" id="label20">
                             <property name="visible">True</property>
                             <property name="label" translatable="yes">Window Title</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="x_options"></property>
                             <property name="y_options"></property>
@@ -1041,11 +1141,11 @@ All rules</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkEntry" id="entry_window_title">
+                          <object class="GtkEntry" id="entry_window_title">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="invisible_char">&#x25CF;</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="left_attach">1</property>
                             <property name="right_attach">2</property>
@@ -1055,10 +1155,10 @@ All rules</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkLabel" id="label3">
+                          <object class="GtkLabel" id="label30">
                             <property name="visible">True</property>
                             <property name="label" translatable="yes">Command</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="top_attach">1</property>
                             <property name="bottom_attach">2</property>
@@ -1069,10 +1169,10 @@ All rules</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkLabel" id="label4">
+                          <object class="GtkLabel" id="label40">
                             <property name="visible">True</property>
                             <property name="label" translatable="yes">Application</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="top_attach">2</property>
                             <property name="bottom_attach">3</property>
@@ -1083,11 +1183,11 @@ All rules</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkEntry" id="entry_command">
+                          <object class="GtkEntry" id="entry_command">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="invisible_char">&#x25CF;</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="left_attach">1</property>
                             <property name="right_attach">2</property>
@@ -1099,11 +1199,11 @@ All rules</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkEntry" id="entry_application">
+                          <object class="GtkEntry" id="entry_application">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="invisible_char">&#x25CF;</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="left_attach">1</property>
                             <property name="right_attach">2</property>
@@ -1115,10 +1215,10 @@ All rules</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkLabel" id="label_description1">
+                          <object class="GtkLabel" id="label_description1">
                             <property name="visible">True</property>
                             <property name="label" translatable="yes">Client Name</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="top_attach">3</property>
                             <property name="bottom_attach">4</property>
@@ -1129,11 +1229,11 @@ All rules</property>
                           </packing>
                         </child>
                         <child>
-                          <widget class="GtkEntry" id="entry_name">
+                          <object class="GtkEntry" id="entry_name">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="invisible_char">&#x25CF;</property>
-                          </widget>
+                          </object>
                           <packing>
                             <property name="left_attach">1</property>
                             <property name="right_attach">2</property>
@@ -1144,23 +1244,20 @@ All rules</property>
                             <property name="y_padding">4</property>
                           </packing>
                         </child>
-                      </widget>
+                      </object>
                       <packing>
                         <property name="position">2</property>
                       </packing>
                     </child>
-                  </widget>
+                  </object>
                 </child>
-                <child>
-                  <widget class="GtkLabel" id="label1">
+                <child type="label">
+                  <object class="GtkLabel" id="label101">
                     <property name="visible">True</property>
                     <property name="label" translatable="yes">Advanced client matching rules</property>
-                  </widget>
-                  <packing>
-                    <property name="type">label_item</property>
-                  </packing>
+                  </object>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="padding">4</property>
@@ -1168,46 +1265,46 @@ All rules</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkHBox" id="hbox3">
+              <object class="GtkHBox" id="hbox303">
                 <property name="visible">True</property>
                 <child>
-                  <widget class="GtkAlignment" id="alignment2">
+                  <object class="GtkAlignment" id="alignment2">
                     <property name="visible">True</property>
                     <property name="xalign">1</property>
                     <property name="yalign">1</property>
                     <property name="xscale">0</property>
                     <child>
-                      <widget class="GtkButton" id="button_close">
-                        <property name="label" translatable="yes">gtk-close</property>
+                      <object class="GtkButton" id="button_close01">
+                        <property name="label">gtk-close</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
                         <property name="use_stock">True</property>
                         <signal name="clicked" handler="on_button_close_clicked"/>
                         <signal name="activate" handler="on_button_close_activate"/>
-                      </widget>
+                      </object>
                     </child>
-                  </widget>
+                  </object>
                   <packing>
                     <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
-                  <widget class="GtkAlignment" id="alignment1">
+                  <object class="GtkAlignment" id="alignment101">
                     <property name="visible">True</property>
                     <property name="xalign">1</property>
                     <property name="xscale">0</property>
                     <child>
-                      <widget class="GtkButton" id="button_apply">
-                        <property name="label" translatable="yes">gtk-apply</property>
+                      <object class="GtkButton" id="button_apply">
+                        <property name="label">gtk-apply</property>
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="receives_default">True</property>
                         <property name="use_stock">True</property>
                         <signal name="clicked" handler="on_button_apply_clicked"/>
-                      </widget>
+                      </object>
                     </child>
-                  </widget>
+                  </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">False</property>
@@ -1215,7 +1312,7 @@ All rules</property>
                     <property name="position">2</property>
                   </packing>
                 </child>
-              </widget>
+              </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
@@ -1223,13 +1320,108 @@ All rules</property>
                 <property name="position">3</property>
               </packing>
             </child>
-          </widget>
+          </object>
           <packing>
             <property name="padding">4</property>
             <property name="position">0</property>
           </packing>
         </child>
-      </widget>
+      </object>
+    </child>
+  </object>
+  <object class="GtkWindow" id="window_client_holder">
+    <child>
+      <object class="GtkTable" id="table_clients">
+        <property name="visible">True</property>
+        <property name="n_rows">2</property>
+        <property name="n_columns">4</property>
+        <property name="column_spacing">2</property>
+        <property name="row_spacing">2</property>
+        <child>
+          <object class="GtkImage" id="image_client_icon">
+            <property name="width_request">32</property>
+            <property name="visible">True</property>
+            <property name="stock">gtk-media-play</property>
+            <property name="icon-size">3</property>
+          </object>
+          <packing>
+            <property name="bottom_attach">2</property>
+            <property name="x_options"></property>
+            <property name="x_padding">4</property>
+            <property name="y_padding">4</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkProgressBar" id="progressbar_meter">
+            <property name="height_request">4</property>
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">4</property>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="y_options"></property>
+            <property name="x_padding">4</property>
+            <property name="y_padding">4</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkComboBox" id="combobox_role">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="right_attach">3</property>
+            <property name="x_options"></property>
+            <property name="y_options"></property>
+            <property name="x_padding">4</property>
+            <property name="y_padding">4</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="button_client">
+            <property name="label" translatable="yes">Edit</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="relief">half</property>
+            <signal name="clicked" handler="on_button_client_clicked"/>
+          </object>
+          <packing>
+            <property name="left_attach">3</property>
+            <property name="right_attach">4</property>
+            <property name="x_options"></property>
+            <property name="y_options"></property>
+            <property name="x_padding">4</property>
+            <property name="y_padding">4</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkAlignment" id="alignment303">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <child>
+              <object class="GtkLabel" id="label_client_name">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">label</property>
+                <property name="ellipsize">end</property>
+                <attributes>
+                  <attribute name="gravity" value="west"/>
+                </attributes>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="y_options"></property>
+            <property name="x_padding">4</property>
+            <property name="y_padding">4</property>
+          </packing>
+        </child>
+      </object>
     </child>
-  </widget>
-</glade-interface>
+  </object>
+</interface>
diff --git a/data/ui/EarcandyWindow.ui b/data/ui/EarcandyWindow.ui
new file mode 100644
index 0000000..5176da1
--- /dev/null
+++ b/data/ui/EarcandyWindow.ui
@@ -0,0 +1,226 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-requires earcandy_window 1.0 -->
+  <!-- interface-naming-policy project-wide -->
+  <!-- interface-local-resource-path ../media -->
+  <object class="EarcandyWindow" id="earcandy_window">
+    <property name="width_request">600</property>
+    <property name="height_request">500</property>
+    <property name="title" translatable="yes">Earcandy</property>
+    <property name="icon">../media/icon.png</property>
+    <signal name="destroy" handler="on_destroy"/>
+    <child>
+      <object class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">5</property>
+        <child>
+          <object class="GtkMenuBar" id="menubar1">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkMenuItem" id="menuitem1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_File</property>
+                <property name="use_underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu" id="menu1">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem1">
+                        <property name="label">gtk-new</property>
+                        <property name="visible">True</property>
+                        <property name="use_action_appearance">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <accelerator key="n" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem2">
+                        <property name="label">gtk-open</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem3">
+                        <property name="label">gtk-save</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem4">
+                        <property name="label">gtk-save-as</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+                        <property name="visible">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem5">
+                        <property name="label">gtk-quit</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="activate" handler="quit"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="menuitem2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Edit</property>
+                <property name="use_underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu" id="menu2">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem6">
+                        <property name="label">gtk-cut</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem7">
+                        <property name="label">gtk-copy</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem8">
+                        <property name="label">gtk-paste</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem9">
+                        <property name="label">gtk-delete</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparatorMenuItem" id="separatormenuitem2">
+                        <property name="visible">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem11">
+                        <property name="label">gtk-preferences</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="activate" handler="preferences"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="menuitem3">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_View</property>
+                <property name="use_underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem" id="menuitem4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Help</property>
+                <property name="use_underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu" id="menu3">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkImageMenuItem" id="imagemenuitem10">
+                        <property name="label">gtk-about</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="activate" handler="about"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="xpad">5</property>
+            <property name="ypad">5</property>
+            <property name="label" translatable="yes">Your application has been created!
+
+To start changing this user interface, run 'quickly glade', which will open Glade so you can edit the default windows and dialogs.
+
+To change the behavior and edit the python code, run 'quickly edit', which will bring up a text editor.</property>
+            <property name="wrap">True</property>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="image1">
+            <property name="visible">True</property>
+            <property name="xpad">5</property>
+            <property name="ypad">5</property>
+            <property name="pixbuf">../media/background.png</property>
+          </object>
+          <packing>
+            <property name="padding">15</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkStatusbar" id="statusbar1">
+            <property name="visible">True</property>
+            <property name="spacing">2</property>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="xpad">5</property>
+                <property name="ypad">5</property>
+                <property name="label" translatable="yes">Status Area</property>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/data/ui/about_earcandy_dialog.xml b/data/ui/about_earcandy_dialog.xml
new file mode 100644
index 0000000..c6c1aa6
--- /dev/null
+++ b/data/ui/about_earcandy_dialog.xml
@@ -0,0 +1,9 @@
+<glade-catalog name="about_earcandy_dialog" domain="glade-3" 
+               depends="gtk+" version="1.0">
+  <glade-widget-classes>
+    <glade-widget-class title="About Earcandy Dialog" name="AboutEarcandyDialog" 
+                        generic-name="AboutEarcandyDialog" parent="GtkAboutDialog"
+                        icon-name="widget-gtk-about-dialog"/>
+  </glade-widget-classes>
+
+</glade-catalog>
diff --git a/data/ui/earcandy_window.xml b/data/ui/earcandy_window.xml
new file mode 100644
index 0000000..b8977d8
--- /dev/null
+++ b/data/ui/earcandy_window.xml
@@ -0,0 +1,8 @@
+<glade-catalog name="earcandy_window" domain="glade-3" 
+               depends="gtk+" version="1.0">
+  <glade-widget-classes>
+    <glade-widget-class title="Earcandy Window" name="EarcandyWindow" 
+                        generic-name="EarcandyWindow" parent="GtkWindow"
+                        icon-name="widget-gtk-window"/>
+  </glade-widget-classes>
+</glade-catalog>
diff --git a/ear_candy/Sink.py b/ear_candy/Sink.py
deleted file mode 100644
index db474f9..0000000
--- a/ear_candy/Sink.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Ear Candy - Pulseaduio sound managment tool
-# Copyright (C) 2008 Jason Taylor
-# 
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-import math  
-import time
-          
-class Sink():
-    def __init__(self, index, name, volume, client, channels=1):
-
-        self.index = index
-        self.name = name
-        self.client = client
-        self.volume = volume
-        self.channels = channels
-
-        self.volume_meter = 0
-        self.volume_meter_last_non_zero = 0
-
-        self.__previous_volume = 0
- 
-    def set_volume(self):
-
-        current_volume = round(self.volume[1])
-        step_volume = current_volume
-        result = True
-
-        if current_volume < self.client.volume_target:      
-            step_volume = current_volume + self.client.volume_step
-            if not self.client.fade_volume: step_volume = self.client.volume_target
-        elif current_volume > self.client.volume_target: 
-            step_volume = current_volume - self.client.volume_step
-            if not self.client.fade_volume: step_volume = self.client.volume_target
-        
-        if step_volume > 100:
-            step_volume = 100
-        if step_volume < self.client.volume_step:
-            step_volume = self.client.volume_step
-
-        # we dont want to get stuck in a loop because volumes arn't exactly the same 
-        result = math.fabs(self.__previous_volume - current_volume) >= self.client.volume_step
-
-        if result:
-            for i in range(0, self.channels+1):
-                self.volume[i] = step_volume
-
-            #print "\nAdjust Volume", self.client.name, step_volume
-
-        self.__previous_volume = step_volume
-       
-        return result
-
diff --git a/ear_candy/__init__.py b/ear_candy/__init__.py
deleted file mode 100644
index 4265cc3..0000000
--- a/ear_candy/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#!/usr/bin/env python
diff --git a/ear_candy/ear_candy.py b/ear_candy/ear_candy.py
deleted file mode 100755
index 2b4e74e..0000000
--- a/ear_candy/ear_candy.py
+++ /dev/null
@@ -1,552 +0,0 @@
-#!/usr/bin/env python
-
-# Ear Candy - Pulseaduio sound managment tool
-# Copyright (C) 2008 Jason Taylor
-# 
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-import gtk
-import wnck
-import gobject
-import time
-import datetime
-import re
-import os
-import copy
-import sys
-import gconf
-import time
-import shutil
-import pynotify
-from xml.dom.minidom import *
-
-from window.WindowWatcher import WindowWatcher
-from pulseaudio.PulseAudio import PulseAudio 
-from Client import Client
-from Sink import Sink
-from Threads import threaded
-from TrayIcon import EarCandyStatusIcon
-from EarCandyPrefs import EarCandayPref 
-from VolumeSlider import EarCandyVolumeSlider 
-from EarCandyDBus import EarCandyDBusClient
-
-# Turn on gtk threading
-gtk.gdk.threads_init()
-
-def find_program_file(path):
-    """Finds a program file, for example, a png included with the program.
-    First looks for it in files/ under the parent directory of the parent directory
-    of ear_candy.py
-    Then looks for it in /usr/share/earcandy
-    Returns the path of the file"""
-    if os.path.exists(os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "files",path)):
-        return os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "files",path)
-    else:
-        return os.path.join(sys.prefix, "share/earcandy", path)
-
-gtk.window_set_default_icon_from_file(find_program_file("earsLabel.png"))
-
-class EarCandy():
-    def save(self):
-        path = os.path.dirname(self.config_file)
-        if not os.path.exists( path ):
-            os.makedirs(path)
-
-        # New document
-        doc = Document()
-        ec = doc.createElement("earcandy")
-        doc.appendChild(ec)
-	
-        rules = doc.createElement("rules")
-        ec.appendChild(rules)
-        skip = copy.copy(self.ignore)
-        for client in self.pa_clients.values():
-            if not client.name in skip and client.category:
-                # Creates user element
-                el = doc.createElement("rule")
-                rules.appendChild(el)
-                client.to_xml(el)
-                skip.append(client.name)
-        
-
-        doc.documentElement.setAttribute("fade_timer_speed", str(self.fade_timer_speed))
-        doc.documentElement.setAttribute("mute_level", str(self.mute_level))
-        doc.documentElement.setAttribute("tray_visible", str(self.tray.get_visible()))
-        doc.documentElement.setAttribute("managed_output_name", str(self.managed_output_name))
-        doc.documentElement.setAttribute("follow_new_outputs", str(self.follow_new_outputs))
-        doc.documentElement.setAttribute("version", str(self.version))
-
-        # Record outputs
-        outputs = doc.createElement("outputs")
-        for output in self.pa_outputs.values():
-            # Creates user element
-            el = doc.createElement("output")
-            el.setAttribute("name", output)
-            outputs.appendChild(el)
-        ec.appendChild(outputs)
-
-
-        fp = open(self.config_file,"w")
-        doc.writexml(fp, "    ", "", "\n", "UTF-8")
-        fp.close()
-
-    def load(self):
-        settings_version = 0
-        xml = None
-        doc = None
-
-        # Load the defaults
-        f = open(self.default_config_file, "r")
-        xml = f.read()
-        f.close()
-        default_doc = parseString(xml)
-        default_version = float(default_doc.documentElement.getAttribute("version"))
-
-        # Check for user settings
-        if os.path.exists( self.config_file ):
-            # Load XML
-            try:
-                f = open(self.config_file, "r")
-                xml = f.read()
-                f.close()
-                doc = parseString(xml)
-                if doc.documentElement.hasAttribute("version"):
-                    settings_version = float(doc.documentElement.getAttribute("version"))
-            except:
-                pass
-
-        # Load defaults ?
-        if not doc or default_version > settings_version:
-            doc = default_doc
-            
-        # Load client rules
-        for el in doc.getElementsByTagName("rule"):
-            client = Client(self, "")
-            client.from_xml(el)
-            if client.category:
-                print "Loaded client rules :", client.description
-            self.pa_clients[client.name] = client
-
-        self.fade_timer_speed = float(doc.documentElement.getAttribute("fade_timer_speed"))
-        #self.mute_level = float(doc.documentElement.getAttribute("mute_level"))
-        if doc.documentElement.hasAttribute("tray_visible"):
-            self.tray.set_visible( doc.documentElement.getAttribute("tray_visible") == "True" )
-        if doc.documentElement.hasAttribute("managed_output_name"):
-            self.managed_output_name = doc.documentElement.getAttribute("managed_output_name")
-        if doc.documentElement.hasAttribute("follow_new_outputs"):
-            self.follow_new_outputs = doc.documentElement.getAttribute("follow_new_outputs") == "True"
-
-    def __init__(self):
-        self.version = 0.5
-        self.active = True
-        self.display = {"": "[ unknown ]", "phone" : "Phone (VoIP)", "video" : "Video Player", "music" : "Music Player", "event" : "Notification" }
-        self.ignore = ["EsounD client (UNIX socket client)", "ear-candy", "Native client (UNIX socket client)", "PulseAudio Volume Control"]
-        self.config_file = os.path.expanduser("~/.config/Ear Candy/settings.xml")     
-        self.default_config_file = find_program_file("settings.xml")
-
-        self.pa_clients = {}        # clients by name
-        self.pa_clients_by_id = {}  # clients by id
-        self.pa_sinks = {}
-        self.pa_outputs = {}
-        self.pa_output_descriptions = {}
-        self.client_with_focus = None # client that has a focused window
-        self.last_application = None
-        self.primary_client = None # Client that gets foreground sound regardless of window focus
-        self.managed_output_name = ""
-        self.current_source_name = ""
-        self.reset_all = False
-
-        self.pref = None
-
-        self.fade_timer_speed = 0.1 
-        self.mute_level = 20
-        self.follow_new_outputs = True
-
-        self.priority_stack = { "" : [], "phone" : [], "video" : [], "music" : [] }      
-
-        pass
-
-    def run(self):
-        self.ecb = EarCandyDBusClient(self)
-
-        if self.ecb.is_running():
-            print "Ear candy already running..."
-            self.ecb.show_window()
-            sys.exit(0)
-        else:
-
-            self.tray = EarCandyStatusIcon(self)
-            
-            self.slider = EarCandyVolumeSlider(self)
-
-            self.ecb.start_service()
-            self.load()
-            self.pa = PulseAudio( self.on_new_pa_client, self.on_remove_pa_client, self.on_new_pa_sink, self.on_remove_pa_sink, self.on_new_pa_output, self.on_remove_pa_output, self.on_volume_change, self.pa_volume_meter)
-            self.ww = WindowWatcher(self.on_active_window_change)
-
-            self.select_client_thread()
-            self.apply_volume_thread()
-            
-            gtk.main()
-            self.exit()
-
-    def show_notification(self, title, body, icon):
-        try:
-            pynotify.init( "Ear Candy" )
-            n = pynotify.Notification(title, body, icon)
-            n.show ()
-        except:
-            print "Unable to show notification"
-
-    def set_active(self, active):
-        self.active = active
-        if self.slider: self.slider.update_active_status()
-        self.tray.set_icon()
-
-
-    def open_preferances(self):
-        if not self.pref:
-            self.pref = EarCandayPref(self)
-        self.pref.run()
-
-    def close_preferances(self):
-        if self.pref:
-            self.pref = None
-
-    def get_current_sink_volume(self):
-        self.pa.get_sink_info_by_name(self.managed_output_name)
-
-    @threaded
-    def apply_volume_thread(self):
-        while True:
-            time.sleep(self.fade_timer_speed)
-            if self.active:
-                gobject.idle_add(self.__adjust_volumes)      
-
-    @threaded
-    def select_client_thread(self):
-        while True:
-            time.sleep(0.5)
-            if self.active:
-                gobject.idle_add(self.__set_primary_client)  
-
-    def __adjust_volumes(self):
-
-        # Always update based on active sinks
-        for sink in self.pa_sinks.values():
-            if sink.set_volume():
-                # set pa volume
-                self.pa.set_sink_volume(sink.index, sink.volume, sink.channels)  
-
-
-    def __set_window_stack(self):
-        for client in self.pa_clients.values():
-            # self.pref.update_client( client )
-
-            # Select the primary client and order previous clients
-            for key in self.priority_stack.keys():
-                if client.category == key:
-                    # Add client to category
-                    if not client in self.priority_stack[key]:
-                        if self.client_with_focus == client:
-                            self.priority_stack[key].insert(0, client)
-                        else:
-                            self.priority_stack[key].append(client)
-
-                    # reshuffle if active
-                    elif self.client_with_focus == client and not self.priority_stack[key][0] == client:
-                        self.priority_stack[key].remove(client)
-                        self.priority_stack[key].insert(0, client)
-
-                # If category has changed remove old entry
-                elif client in self.priority_stack[key]:
-                    self.priority_stack[key].remove(client)
-
-    def __set_primary_client(self):
-
-        # Toggle primary status
-        flagFound = False
-        for key in self.priority_stack.keys():
-            if key:
-                for client in self.priority_stack[key]:         
-
-                    if not flagFound and self.active and client.is_active():
-                        # Toggle old client to inactive
-                        if self.primary_client:
-                            self.primary_client.set_primary(False)
-                        self.primary_client = client
-                        client.set_primary(True)
-                        flagFound = True
-                    else:
-                        client.set_primary(False)
-            else:
-                # all clients with no category should be treated as active for volume
-                for client in self.priority_stack[key]:
-                    client.set_primary(True)
-                     
-
-    def on_volume_change(self, level):
-        self.slider.set_volume( level )
-        #print self.managed_output_name, level
-
-    def on_remove_pa_output(self, index):
-        # /desktop/gnome/sound/default_mixer_device
-
-        del( self.pa_outputs[index] )
-        del( self.pa_output_descriptions[index] )
-
-        # fall back to previous value...
-        for value in self.pa_outputs.values():
-            self.set_last_output(value)
-            return
-
-    def set_last_output(self, name):
-        self.managed_output_name = name
-        
-        # Update gconf key that governs multimedia key controls
-        gconf_key =  "/desktop/gnome/sound/default_mixer_device"
-        prefix = "pulsemixer:"
-        client = gconf.client_get_default()
-        value = client.get_string(gconf_key)
-        if not value == prefix + name:
-            print
-            print "== Change gnome default sound device =="
-            client.set_string(gconf_key, prefix + name)
-
-    def on_new_pa_output(self, index, output_name, output_description, startup):
-        self.pa_outputs[index] = output_name
-        self.pa_output_descriptions[index] = output_description
-        self.save()
-       
-        if self.follow_new_outputs and (self.managed_output_name == "" or output_name.lower().count("usb") > 0):
-            # Move all streams to the new output ;)
-            if not startup: 
-                self.show_notification("New sound device detected", "Moving all sound to new device...", "notification-audio-volume-high")
-            self.set_last_output(output_name)
-            self.move_all_sinks()
-
-    def move_all_sinks(self):
-        if self.managed_output_name:
-            for sink in self.pa_sinks.values():
-                if not sink.client.output:
-                    self.pa.move_sink(sink.index, self.managed_output_name)
-
-    def on_new_pa_sink(self, index, name, client_index, volume, sink_index, channels):
-
-        if not self.pa_sinks.has_key(index):
-            print 
-            print "== pa sink input =="
-            print "sink:", index, name
-            if self.pa_clients_by_id.has_key(client_index):
-                client = self.pa_clients_by_id[client_index]
-                print "client:", client.name
-                print "client index:", client_index
-                print "category:", client.category
-                sink = Sink(index, name, volume, client, channels)
-                client.sinks[index] = sink
-                self.pa_sinks[index] = sink
-
-                # insure the sink input is on the correct output..
-                output = None
-                if self.follow_new_outputs and self.managed_output_name: 
-                    output = self.managed_output_name
-                
-                if client.output:
-                    output = client.output
-
-                if output:               
-                    self.pa.move_sink(sink.index, output)
-
-            else:
-                return
-        else:
-            self.pa_sinks[index].volume = volume
-        
-        if self.pref:
-            self.pref.update_client( self.pa_clients_by_id[client_index] )
-        self.on_active_window_change( self.last_application, "pa")
-
-    def pa_volume_meter(self, index, level):
-        if self.pa_sinks.has_key(index):
-            sink = self.pa_sinks[index]
-            sink.volume_meter = level
-            if(level > sink.client.volume_step): sink.volume_meter_last_non_zero = time.mktime(datetime.datetime.now().timetuple())
-
-    def on_remove_pa_sink(self, index):
-        print 
-        print "== pa remove sink input =="
-        print index
-        if self.pa_sinks.has_key(index):
-            client = self.pa_sinks[index].client
-            # Delete entry in store so that volume dosnt stay low :)
-            self.pa.pa_ext_stream_restore_delete( self.pa_sinks[index].name )
-            del( client.sinks[index] )
-            del( self.pa_sinks[index] )
-            if self.pref:
-                self.pref.update_client( client )
-            self.on_active_window_change( self.last_application, "pa")
-        
-    def get_unregistered_clients(self):
-        clients = []
-        # client names to skip from adding to list
-        skip = copy.copy(self.ignore)
-        count = 0
-        for client in self.pa_clients.values():
-            if client.category == "" and not client.icon and not client.name in skip:
-                clients.append(client)
-                skip.append( client.name )
-        return clients
-
-    def on_new_pa_client(self, index, name, pid, proplist):
-        if not pid: pid = -1
-        print 
-        print "== pa sink client =="
-        print "index:", index
-        print "name:", name
-        print proplist
-        # Link all clients with same name into same object
-
-        if not self.pa_clients.has_key(name):
-            client = Client(self, name, int(pid))
-            self.pa_clients[name] = client
-        else:
-            client = self.pa_clients[name]
-            client.pid = int(pid)
-        self.pa_clients_by_id[index] = client
-
-        # Check windows for a match
-        for application in self.ww.applications.values():
-            if self.match_client_to_application(client, application, index): break
-
-    def on_remove_pa_client(self, index):
-        print "== pa remove client =="
-        if self.pa_clients_by_id.has_key(index):
-            print self.pa_clients_by_id[index].name
-            client = self.pa_clients_by_id[index]
-
-            # remove from by ID list
-            del self.pa_clients_by_id[index]
-
-            # Remove from priority array
-            if client in self.priority_stack[client.category]:
-                self.priority_stack[client.category].remove(client)
-
-            # If not category then totaly delete the entry
-            # if not client.category: del self.pa_clients[client.name]
-
-            if self.pref:
-                self.pref.update_client( client )
-                if not client.is_active():
-                    self.pref.remove_client_on_timer( client )
-
-    def on_active_window_change(self, application, state):  #, pid, window_name, x, y, icon, fullscreen
-        if application:
-            # Need to run this if a client is added or removed...
-            if state == "active":
-                """print
-                print "== active window changed =="
-                print "window title:", application.window_name
-                print "application:", application.name
-                print "pid:", application.pid
-                print "gid:", os.getpgid(application.pid)
-                print "command:", application.command
-                print "category:", application.category"""
-
-            # set active / fullscreen flags
-            if self.client_with_focus:
-                self.client_with_focus.has_focus = False
-
-            self.client_with_focus = None
-            for client in self.pa_clients.values():
-                if self.match_client_to_application(client, application, state): break
-
-            self.last_application = application
-
-        self.__set_window_stack()
-
-    def match_client_to_application(self, client, application, index=0):
-        if client.test_focus_window(application.pid, application.window_name, application.command, application.name): 
-
-            client.fullscreen = application.fullscreen  
-            if not client.icon : client.icon = application.icon
-            if not  client.icon_name : client.icon_name = application.icon_name
-            if not  client.description :client.description = application.description
-            client.matched = True
-            #if not state == "open": 
-            self.client_with_focus = client
-
-            if not client.category:
-                client.category = application.category
-            # Balance has no practical use I can think of....
-            # Im going to leave it here in case someone thinks of something
-            #if client.window_position_fade:
-            #    client.balance = ((100 - (application.y / (gtk.gdk.screen_width() /  100))) * 2)  - 100
-            #    print "BALANCE", client.balance
-            #else:
-            #    client.balance = 0
-            if self.pref:
-                self.pref.update_client( client )
-            return True
-        return False
-
-    def clean_client_name(self, name):
-        name = name.strip()
-        alsa_plugin = "ALSA plug-in ["
-        if name.startswith(alsa_plugin):
-            name = name[len(alsa_plugin): -1]
-        return name
-
-    def exit(self):
-        self.set_active(False)
-        # Reset all volumes
-        self.reset_all_volumes()
-        sys.exit(0)
-
-    def reset_all_volumes(self, deleteFile=True):
-        print ""
-        print "Resetting all volume levels..."
-        for sink in self.pa_sinks.values():
-            self.pa.set_sink_volume(sink.index, [100, 100, 100], sink.channels)  
-
-    def set_auto_start(self, flag):
-        filename = "earcandy.desktop"
-        path = os.path.expanduser(os.getenv('XDG_CONFIG_HOME', '~/.config/autostart'))
-        dest = os.path.join(path, filename)
-        src = find_program_file(filename)
-
-        if flag:
-            # insure the autostart path exists
-            if not os.path.exists(path):
-                os.makedirs(path)
-
-            if not os.path.exists( dest ):
-                shutil.copyfile(src, dest)
-        else:
-            if os.path.exists( dest ):
-                os.remove( dest )
-
-    def is_auto_start(self):
-        filename = "earcandy.desktop"
-        dest = os.path.expanduser(os.getenv('XDG_CONFIG_HOME', '~/.config/autostart/' + filename))
-        return os.path.exists(dest)
-
-if __name__ == '__main__':
-
-    os.chdir(os.path.dirname(sys.argv[0]))
-    
-    ec = EarCandy()
-    ec.set_auto_start( True )
-    ec.run()
-    ec.save()
-    
diff --git a/ear_candy/pulseaudio/PulseAudio.py b/ear_candy/pulseaudio/PulseAudio.py
deleted file mode 100644
index 2f7e565..0000000
--- a/ear_candy/pulseaudio/PulseAudio.py
+++ /dev/null
@@ -1,337 +0,0 @@
-# Ear Candy - Pulseaduio sound managment tool
-# Copyright (C) 2008 Jason Taylor
-# 
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-from lib_pulseaudio import *
-import gobject
-import sys 
-import os
-import ctypes
-
-PA_VOLUME_CONVERSION_FACTOR = 655.36
-      
-# A null method that can be given to pulse methods
-def null_cb(a=None, b=None, c=None, d=None):
-    #print "NULL CB"
-    return
-
-class PulseAudio():
-    def __init__(self, new_client_cb, remove_client_cb, new_sink_cb, remove_sink_cb, new_output_cb, remove_pa_output, volume_change_cb, volume_meter_cb):
-        
-        self.sinks = {}
-        self.monitor_sinks = []
-        self.module_stream_restore_argument = ""
-
-        self.new_client_cb = new_client_cb
-        self.new_sink_cb = new_sink_cb
-        self.remove_sink_cb = remove_sink_cb
-        self.remove_client_cb = remove_client_cb
-        self.new_output_cb = new_output_cb
-        self.volume_change_cb = volume_change_cb
-        self.remove_pa_output = remove_pa_output
-        self.volume_meter_cb = volume_meter_cb
-
-        self.pa_mainloop = pa_threaded_mainloop_new();
-        self.pa_mainloop_api = pa_threaded_mainloop_get_api(self.pa_mainloop);
-        
-        self._context = pa_context_new(self.pa_mainloop_api, "ear-candy");
-        self._context_notify_cb = pa_context_notify_cb_t(self.context_notify_cb)
-        pa_context_set_state_callback(self._context, self._context_notify_cb, None);
-        pa_context_connect(self._context, None, 0, None);
-       
-        pa_threaded_mainloop_start(self.pa_mainloop);
-
-    # pulseaudio connection status    
-    def context_notify_cb(self, context, userdata):
-        
-        try:
-            ctc = pa_context_get_state(context)
-            if ctc == PA_CONTEXT_READY:
-                print
-                print "Pulseaudio connection ready..."
-
-                self._null_cb = pa_context_success_cb_t(null_cb)
-                self._pa_context_success_cb = pa_context_success_cb_t(self.pa_context_success_cb)
-                self._pa_stream_request_cb = pa_stream_request_cb_t(self.pa_stream_request_cb)
-                self._pa_stream_notify_cb = pa_stream_notify_cb_t(self.pa_stream_request_cb)
-                self._pa_sink_info_cb = pa_sink_info_cb_t(self.pa_sink_info_cb)
-                self._pa_context_subscribe_cb = pa_context_subscribe_cb_t(self.pa_context_subscribe_cb)
-                self._pa_source_info_cb = pa_source_info_cb_t(self.pa_source_info_cb)
-                self._pa_source_output_info_cb = pa_source_output_info_cb_t(self.pa_source_output_info_cb)
-                self._pa_sink_input_info_list_cb = pa_sink_input_info_cb_t(self.pa_sink_input_info_cb)
-                self._pa_client_info_list_cb = pa_client_info_cb_t(self.pa_client_info_cb)
-                self._pa_module_info_cb = pa_module_info_cb_t(self.pa_module_info_cb)
-                self._pa_context_index_cb = pa_context_index_cb_t(self.pa_context_index_cb) 
-
-                o = pa_context_get_module_info_list(self._context, self._pa_module_info_cb, True)
-                pa_operation_unref(o)
-
-                o = pa_context_get_source_info_list(self._context, self._pa_source_info_cb, True)
-                pa_operation_unref(o)
-
-                o = pa_context_get_client_info_list(self._context, self._pa_client_info_list_cb, None)
-                pa_operation_unref(o)
-                
-                o = pa_context_get_source_output_info_list(self._context, self._pa_source_output_info_cb, None)
-                pa_operation_unref(o)
-
-                o = pa_context_get_sink_info_list(self._context, self._pa_sink_info_cb, None)
-                pa_operation_unref(o)
-
-                o = pa_context_get_sink_input_info_list(self._context, self._pa_sink_input_info_list_cb, True)
-                pa_operation_unref(o)
-
-                pa_context_set_subscribe_callback(self._context, self._pa_context_subscribe_cb, None);
-                o = pa_context_subscribe(self._context, (pa_subscription_mask_t)
-                                               (PA_SUBSCRIPTION_MASK_SINK|
-                                                PA_SUBSCRIPTION_MASK_SOURCE|
-                                                PA_SUBSCRIPTION_MASK_SINK_INPUT|
-                                                PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
-                                                PA_SUBSCRIPTION_MASK_CLIENT|
-                                                PA_SUBSCRIPTION_MASK_SERVER), self._null_cb, None)  
-
-                pa_operation_unref(o)             
-
-            if ctc == PA_CONTEXT_FAILED :
-                self.__print("Connection failed")
-                pa_threaded_mainloop_signal(self.pa_mainloop, 0)
-                sys.exit(1)
-                
-            if ctc == PA_CONTEXT_TERMINATED:
-                self.__print("Connection terminated")
-                pa_threaded_mainloop_signal(self.pa_mainloop, 0)
-                sys.exit(1)
-
-        except Exception, text:
-            self.__print("ERROR context_notify_cb %s" % text)
-
-
-    def pa_context_index_cb(self, context, index, user_data):
-        # Do nothing....
-        return
-
-    def load_module_stream_restore(self):
-        print "Reloading module-stream-restore " 
-        pa_context_load_module(self._context, "module-stream-restore", self.module_stream_restore_argument, self._pa_context_index_cb, None)
-
- 
-    def pa_module_info_cb(self, context, pa_module_info, eol, user_data):
-        if user_data and pa_module_info:
-                      
-            """if pa_module_info.contents.name == "module-stream-restore":
-                print
-                print "Found 'module-stream-restore'... unloading.."  
-                self.module_stream_restore_argument = pa_module_info.contents.argument
-                pa_context_unload_module(context, pa_module_info.contents.index, self._null_cb, None)"""
-        return
-
-    def pa_source_info_cb(self, context, struct, eol, user_data):
-        if eol: return
-
-        if struct:
-            
-            if  struct.contents.monitor_of_sink_name:
-                print 
-                print "== new output device found =="
-                print struct.contents.name
-                print struct.contents.description
-                """print struct.contents.monitor_of_sink_name
-                print struct.contents.driver
-                print struct.contents.sample_spec
-                print struct.contents.channel_map
-                print struct.contents.owner_module
-                print struct.contents.volume
-                print struct.contents.mute
-                print struct.contents.monitor_of_sink
-                print struct.contents.monitor_of_sink_name
-                print struct.contents.flags
-                print struct.contents.index"""
-                
-                gobject.idle_add(self.new_output_cb, struct.contents.index, struct.contents.monitor_of_sink_name, struct.contents.description, user_data)
-                volume = int(pa_cvolume_avg(struct.contents.volume) / PA_VOLUME_CONVERSION_FACTOR)
-                gobject.idle_add(self.volume_change_cb, volume)
-
-    def pa_stream_request_cb(self, stream, length, index):
-
-        # This isnt quite right... maybe not a float.. ?
-        
-        #null_ptr = ctypes.c_void_p()
-        data = POINTER(c_float)()
-        pa_stream_peek(stream, data, ctypes.c_ulong(length)) 
-        v = data[length / 4 -1] * 100
-        if (v < 0):
-            v = 0
-        if (v > 100):
-            v = 100
-        pa_stream_drop(stream)
-
-        self.volume_meter_cb(index, v)
-    
-    def pa_create_monitor_stream_for_sink_input(self, index, monitor_index, name):
-        
-        if not index in self.monitor_sinks:
-            self.monitor_sinks.append(index)
-            # Create new stream
-            ss = pa_sample_spec()
-            ss.channels = 1
-            ss.format = 5
-            ss.rate = 25
-            pa_stream = pa_stream_new(self._context, "Peak detect - " + name, ss, None)
-            
-            pa_stream_set_monitor_stream(pa_stream, index);
-            pa_stream_set_read_callback(pa_stream, self._pa_stream_request_cb, index);
-            pa_stream_set_suspended_callback(pa_stream, self._pa_stream_notify_cb, None);
-
-            attr = pa_buffer_attr()
-            attr.fragsize = 4
-            attr.maxlength = 10
-            attr.tlength = 0
-            attr.prebuf = 0
-            attr.minreq = 0
-
-            pa_stream_connect_record(pa_stream, str(monitor_index), attr, 10752) 
-                    
-    def pa_context_success_cb(self, context, c_int,  user_data):
-        return
-
-    def pa_source_output_info_cb(self, context, struct, c_int, user_data):
-        return
-        
-    def pa_context_subscribe_cb(self, context, event_type, index, user_data):
-
-        try:
-            et = event_type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK
-
-            if et == PA_SUBSCRIPTION_EVENT_CLIENT:
-            
-                if event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK == PA_SUBSCRIPTION_EVENT_REMOVE:
-                    gobject.idle_add(self.remove_client_cb, int(index))
-                else:
-                    o = pa_context_get_client_info(self._context, index, self._pa_client_info_list_cb, None)
-                    pa_operation_unref(o)
-
-            if et == PA_SUBSCRIPTION_EVENT_SINK_INPUT:
-                if event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK == PA_SUBSCRIPTION_EVENT_REMOVE:
-                     gobject.idle_add(self.remove_sink_cb, int(index))
-                     self.monitor_sinks.remove(index)
-                else:
-                    o = pa_context_get_sink_input_info(self._context, int(index), self._pa_sink_input_info_list_cb, True)
-                    pa_operation_unref(o)
-                    
-            if et == PA_SUBSCRIPTION_EVENT_SOURCE:
-                if event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK == PA_SUBSCRIPTION_EVENT_REMOVE:
-                    # Remove output source
-                    self.remove_pa_output( int(index) )
-                else:
-                    o = pa_context_get_source_info_by_index(self._context, int(index), self._pa_source_info_cb, False)
-                    pa_operation_unref(o)
-
-        except Exception, text:
-            self.__print("pa :: ERROR pa_context_subscribe_cb %s" % text)
-    
-    def pa_client_info_cb(self, context, struct, c_int, user_data):
-        try:
-            if struct :
-
-                self.__print("CLIENT")
-                self.__print( pa_proplist_to_string(struct.contents.proplist))
-
-                # Get the client pid so we can match to the x11 window application pid
-                pid = pa_proplist_gets(struct.contents.proplist, "application.process.id")
-                #binary = pa_proplist_gets(struct.contents.proplist, "application.process.binary")
-
-                gobject.idle_add(self.new_client_cb, struct.contents.index, struct.contents.name, pid, pa_proplist_to_string(struct.contents.proplist))
-                
-        except Exception, text:
-            self.__print( "pa :: ERROR pa_client_info_cb %s" % text)    
-
-    def pa_sink_input_info_cb(self, context, struct, index, user_data):
-        if struct and user_data:
-            
-            # TODO: Only do this if app dosnt release pulse streams correctly
-            if float(struct.contents.sink) in self.sinks:
-                self.pa_create_monitor_stream_for_sink_input(int(struct.contents.index), self.sinks[float(struct.contents.sink)], struct.contents.name)
-
-            # here we go...
-            self.__print( "SINK INPUT INFO")
-            self.__print( pa_proplist_to_string(struct.contents.proplist))
-            
-            # Get volume level
-            volume = []
-            volume.append( int(pa_cvolume_avg(struct.contents.volume) / PA_VOLUME_CONVERSION_FACTOR) )
-            for i in range(0, struct.contents.volume.channels):
-                volume.append(int(struct.contents.volume.values[i]) / PA_VOLUME_CONVERSION_FACTOR)
-            
-            gobject.idle_add( self.new_sink_cb, int(struct.contents.index), struct.contents.name, int(struct.contents.client), volume, struct.contents.sink, struct.contents.channel_map.channels)
-
-    # Move a playing stream to a differnt output sink
-    def move_sink(self, sink_index, output_name):
-        self.__print("move_sink")
-        pa_context_move_sink_input_by_name(self._context, sink_index, output_name, self._pa_context_success_cb, None)
-
-    def set_sink_volume_by_name(self, sink_name, volume):
-        self.__print("set_sink_volume_by_name")
-        if volume < 0: volume = 0
-        vol = pa_cvolume()
-        vol.channels = 2 #len(cvolume) - 1
-        v = pa_volume_t * 32
-        vol.values = v (int(volume * PA_VOLUME_CONVERSION_FACTOR), int(volume * PA_VOLUME_CONVERSION_FACTOR))
-
-        o = pa_context_set_sink_volume_by_name(self._context, sink_name, vol, self._null_cb, None)
-        pa_operation_unref(o)
-
-    def set_sink_volume(self, index, cvolume, number_of_channels):
-        self.__print("set_sink_volume")
-        vol = pa_cvolume()
-        vol.channels = number_of_channels #len(cvolume) - 1
-        v = pa_volume_t * 32
-
-        vol.values = v()
-        for i in range(0, number_of_channels):
-            if len(cvolume) > i:
-                vol.values[i] = int(cvolume[i+1] * PA_VOLUME_CONVERSION_FACTOR)
-            else:
-                vol.values[i] = int(cvolume[1] * PA_VOLUME_CONVERSION_FACTOR)
-
-        # Note setting volume causes a trigger of sink_input_info which will gives us back new volume!
-        o = pa_context_set_sink_input_volume(self._context, index, vol, self._null_cb, None) # NOTE: dont pass in any thing here causes a seg fault
-        pa_operation_unref(o)
-
-    def get_sink_info_by_name(self, sink_name):
-        self.__print("get_sink_info_by_name")
-        o = pa_context_get_sink_info_by_name(self._context, sink_name, self._pa_sink_info_cb, False)
-        pa_operation_unref(o)
-
-    def pa_sink_info_cb(self, context, struct, index, data):
-        if struct:
-            # Get volume level
-
-            # Update sink to monitor links
-            self.sinks[ float(struct.contents.index) ] = struct.contents.monitor_source
-            self.__print("pa_sink_info_cb")
-            gobject.idle_add(self.volume_change_cb, int(pa_cvolume_avg(struct.contents.volume) / PA_VOLUME_CONVERSION_FACTOR))
-
-    def pa_ext_stream_restore_delete( self, stream ):
-        pa_ext_stream_restore_delete(self._context, stream, self._pa_context_success_cb, None)
-
-
-
-    def __print(self, text):
-        #print text
-        return
-
-if __name__ == '__main__':
-    c = PulseAudio()
-    
diff --git a/ear_candy/window/WindowWatcher.py b/ear_candy/window/WindowWatcher.py
deleted file mode 100644
index 51c1a78..0000000
--- a/ear_candy/window/WindowWatcher.py
+++ /dev/null
@@ -1,179 +0,0 @@
-#!/usr/bin/env python
-# Ear Candy - Pulseaduio sound managment tool
-# Copyright (C) 2008 Jason Taylor
-# 
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-# based on original code by by RYX (aka Rico Pfaus)
-# http://thegraveyard.org/files/kapager-0.0.3.py
-
-import wnck
-import gobject
-import os
-import gtk
-from DesktopFiles import DesktopFiles
-
-class Application():
-    def __init__(self, pid, command, name, desktop_files, icon):
-        self.command = os.path.basename(command.lower().strip())
-        self.name = name.lower().strip()
-        self.category = ""
-        self.icon = icon
-        self.icon_name = ""
-        self.description = name
-
-        # current application window info
-        self.pid = pid
-        self.window_name = ""
-        self.x = 0
-        self.y = 0
-        self.fullscreen = False
-        
-        self.__match_desktop(desktop_files)
-        return
-
-    def __match_desktop(self,  desktop_files):
-
-        #print "checking desktop files for match....", self.name
-        # Try looking up the application list
-        for a in desktop_files:
-            ex = a.get_exec_array()[0].lower()
-
-            # Check for pulse audio settings first...
-            if a.get("X-PulseAudio-Properties"):
-                self.category = a.get("X-PulseAudio-Properties")
-                break
-
-            if a.get("X-GNOME-Bugzilla-Product") == self.name or ex == self.name or ex == self.command or a.get("Name").lower() == self.name or a.get("Name").lower() == self.command or ex + ".real" == self.command :
-                self.icon_name = a.get("Icon")
-                self.description = a.get("Name")
-                self.category = self.__desktop_categories_to_category( a.get("Categories") )
-                #print "category : " + (self.category or "<unknown>")
-                break
-
-        if self.command == "skype.real":
-            self.category = "phone"
-            #print "category : " + self.category
-            
-
-    def __desktop_categories_to_category(self, categories):
-        if categories:
-            for category in categories:
-                if "Telephony" in categories or "InstantMessaging" in categories:
-                    return "phone"
-                if "Music" in categories:
-                    return "music"
-                if "Video" in categories:
-                    return "video"
-                if "AudioVideo" in categories:
-                    return "music"             
-    
-        return ""
-
-class WindowWatcher():
-    
-    def __init__(self, callback = None, existing=False):
-        self.screen = wnck.screen_get_default()
-        self.screen.connect("active_window_changed", self.active_window_changed)
-        self.screen.connect("window_opened", self.window_opened)
-        self.callback = callback
-        self.current_window = None
-        self.applications = {}
-        self.desktop_files = DesktopFiles().read_all()   
-
-        for win in self.screen.get_windows():
-            self.update("exists", win)
-
-    def send_window_key_press_from_pid(self, pid, key):
-        for win in self.screen.get_windows():
-            app = win.get_application()
-            app_name = app.get_name()
-            if pid == win.get_pid():       
-                
-                break;
-
-
-    def get_command(self, pid):
-        command = str(pid)
-        try:
-            # Try and get command from PID
-            f = open("/proc/%s/cmdline" % pid, "r")
-            command = f.readline()[:-1]
-            f.close()
-        except:
-            pass
-        return command
-
-    def window_opened(self, screen, win):
-        if win and win.get_window_type() != wnck.WINDOW_DOCK: # ignore docks
-            #print            
-            #print "window opened"
-            app = win.get_application ()
-            app_name = app.get_name()
-            pid = win.get_pid()
-            command = self.get_command(pid)
-            category = ""
-
-            if not self.applications.has_key(command):
-                a = Application(pid, command, app_name, self.desktop_files, win.get_icon ())
-                self.applications[command] = a
-            
-            self.update("open", win)
-            win.connect("geometry-changed", self.geometry_changed)
-
-    def active_window_changed(self, screen, old_window):
-        win = screen.get_active_window ()
-        self.update("active", win)        
-
-    def update(self, state, win):
-        if win and win.get_window_type() != wnck.WINDOW_DOCK: # ignore docks
-                
-            #try:
-                app = win.get_application ()
-                app_name = app.get_name()
-                pid = win.get_pid()
-
-                command = self.get_command(pid)
-
-                application = None
-                application = self.applications[command]
-                application.pid = pid
-
-                geom = win.get_geometry()
-                x = float(geom[1])
-                y = float(geom[0])
-                w = float(geom[2])
-                h = float(geom[3])
-
-                if win.get_name():
-                    application.window_name =  win.get_name()
-
-                application.x = x+h/2
-                application.y = y+w/2
-                application.fullscreen = win.is_fullscreen () or win.is_maximized ()
-                application.icon = win.get_icon ()
-
-                
-                if self.callback:
-                    gobject.idle_add(self.callback, application, state)
-            #except:
-            #    print "error reading window", win
-
-    def geometry_changed(self, win):
-        self.update("geo", win)
-
-
-if __name__ == '__main__':
-    ww = WindowWatcher()
-
diff --git a/earcandy.desktop.in b/earcandy.desktop.in
new file mode 100644
index 0000000..1aa0283
--- /dev/null
+++ b/earcandy.desktop.in
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=Earcandy
+Comment=Earcandy application
+Categories=GNOME;AudioVideo;
+Exec=earcandy
+Icon=/usr/share/earcandy/media/icon.png
+Terminal=false
+Type=Application
diff --git a/earcandy/AboutEarcandyDialog.py b/earcandy/AboutEarcandyDialog.py
new file mode 100644
index 0000000..0607548
--- /dev/null
+++ b/earcandy/AboutEarcandyDialog.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+import sys
+import os
+import gtk
+
+from earcandy.earcandyconfig import getdatapath
+
+class AboutEarcandyDialog(gtk.AboutDialog):
+    __gtype_name__ = "AboutEarcandyDialog"
+
+    def __init__(self):
+        """__init__ - This function is typically not called directly.
+        Creation of a AboutEarcandyDialog requires redeading the associated ui
+        file and parsing the ui definition extrenally, 
+        and then calling AboutEarcandyDialog.finish_initializing().
+    
+        Use the convenience function NewAboutEarcandyDialog to create 
+        NewAboutEarcandyDialog objects.
+    
+        """
+        pass
+
+    def finish_initializing(self, builder):
+        """finish_initalizing should be called after parsing the ui definition
+        and creating a AboutEarcandyDialog object with it in order to finish
+        initializing the start of the new AboutEarcandyDialog instance.
+    
+        """
+        #get a reference to the builder and set up the signals
+        self.builder = builder
+        self.builder.connect_signals(self)
+
+        #code for other initialization actions should be added here
+
+def NewAboutEarcandyDialog():
+    """NewAboutEarcandyDialog - returns a fully instantiated
+    AboutEarcandyDialog object. Use this function rather than
+    creating a AboutEarcandyDialog instance directly.
+    
+    """
+
+    #look for the ui file that describes the ui
+    ui_filename = os.path.join(getdatapath(), 'ui', 'AboutEarcandyDialog.ui')
+    if not os.path.exists(ui_filename):
+        ui_filename = None
+
+    builder = gtk.Builder()
+    builder.add_from_file(ui_filename)    
+    dialog = builder.get_object("about_earcandy_dialog")
+    dialog.finish_initializing(builder)
+    return dialog
+
+if __name__ == "__main__":
+    dialog = NewAboutEarcandyDialog()
+    dialog.show()
+    gtk.main()
+
diff --git a/earcandy/Application.py b/earcandy/Application.py
new file mode 100644
index 0000000..c1990f8
--- /dev/null
+++ b/earcandy/Application.py
@@ -0,0 +1,59 @@
+import gtk
+from util.DesktopFiles import DesktopFiles
+
+class Application():
+    def __init__(self, pid, command, name, icon):
+        self.command = os.path.basename(command.lower().strip())
+        self.name = name.lower().strip()
+        self.category = ""
+        self.icon = icon
+        self.icon_name = ""
+        self.description = name
+
+        # current application window info
+        self.pid = pid
+        self.window_name = ""
+        self.x = 0
+        self.y = 0
+        self.fullscreen = False
+
+        self.clients = {}
+        return
+
+    def __match_desktop(self,  desktop_files):
+
+        #print "checking desktop files for match....", self.name
+        # Try looking up the application list
+        for a in desktop_files:
+            ex = a.get_exec_array()[0].lower()
+
+            # Check for pulse audio settings first...
+            if a.get("X-PulseAudio-Properties"):
+                self.category = a.get("X-PulseAudio-Properties")
+                break
+
+            if a.get("X-GNOME-Bugzilla-Product") == self.name or ex == self.name or ex == self.command or a.get("Name").lower() == self.name or a.get("Name").lower() == self.command or ex + ".real" == self.command :
+                self.icon_name = a.get("Icon")
+                self.description = a.get("Name")
+                self.category = self.__desktop_categories_to_category( a.get("Categories") )
+                #print "category : " + (self.category or "<unknown>")
+                break
+
+        if self.command == "skype.real":
+            self.category = "phone"
+            #print "category : " + self.category
+            
+
+    def __desktop_categories_to_category(self, categories):
+        if categories:
+            for category in categories:
+                if "Telephony" in categories or "InstantMessaging" in categories:
+                    return "phone"
+                if "Music" in categories:
+                    return "music"
+                if "Video" in categories:
+                    return "video"
+                if "AudioVideo" in categories:
+                    return "music"             
+    
+        return ""
diff --git a/ear_candy/Client.py b/earcandy/Client.py
similarity index 62%
rename from ear_candy/Client.py
rename to earcandy/Client.py
index 1a3a581..e08794a 100644
--- a/ear_candy/Client.py
+++ b/earcandy/Client.py
@@ -24,41 +24,56 @@ class Client():
         self.core = core
 
         self.name = name
-        self.description = self.core.clean_client_name(name)
-        self.volume_default = 100
-        self.volume_mute = -1
+        self.description = self.clean_client_name(name)
+        self.pid = pid
+        self.role = "" # music | video | phone
+        self.icon = None
+
+        self.volume_max = 100
+        self.volume_min = 0
+        self.volume_target = 100
+        self.volume_step = 2   
+
         self.rule_re_window_title = re.compile("")
         self.rule_re_command = re.compile("")
         self.rule_re_application = re.compile("")
-        self.category = "" # music | video | phone
+        
         self.apply_volume_meter_hack = True
         self.fade_volume = True
-        self.output = ""
-
-        self.pid = pid
-        self.volume_step = 2        
-        self.has_focus = False
-        self.fullscreen = False
-        self.icon = None
-        self.icon_name = ""
-        self.iter = None
-        self.balance = 0
+        self.prefer_sink = ""
 
-        self.volume_target = self.volume_default
 
+        self.dbus_name = ""
+        self.__pause_status = 0       
+        self.application = None
         self.sinks = {}
-
         self.generate_rules()
 
-        # TODO: remove these
-        self.window_position_fade = False
-        self.matched = False
+        self.__status = False
+
+        self.icon_name = None
+
+        self.iter = None
+        self.gtk = None
+
+    def clean_client_name(self, name):
+        name = name.strip()
+        alsa_plugin = "ALSA plug-in ["
+        if name.startswith(alsa_plugin):
+            self.apply_volume_meter_hack = True
+            name = name[len(alsa_plugin): -1]
+        return name
+
+    def get_sink(self):
+        for sink_input in self.sinks.values():
+            return sink_input.sink
+        return -1
 
     def get_volume(self):
         v = 0
         count = 0
         for sink in self.sinks.values():
-            v = v + sink.volume[0]
+            v = v + sink.volume_meter_level
             count = count + 1
         if count > 0: return v / count
         return v
@@ -73,31 +88,19 @@ class Client():
         return v
 
     def is_active(self):
+        for sink in self.sinks.values():
+            if sink.is_active():
+                return True
+        return False
 
-        # no sinks then not active
-        if len(self.sinks.values()) == 0: return False
-
-        timestamp = time.mktime(datetime.datetime.now().timetuple())
-
-        # check meter levels on others
-        if self.apply_volume_meter_hack:
-            for sink in self.sinks.values():
-            
-                if sink.volume_meter > 0: return True
-                
-                # HACK: Check how long its been inactive... and if more than a second count as expired
-                if timestamp - sink.volume_meter_last_non_zero < 1:
-                    return True
-            return False
 
-        return True
 
     def has_rule(self):
         return self.rule_re_command.pattern or self.rule_re_window_title.pattern or self.rule_re_application.pattern
 
     def generate_rules(self, app=None):
         if not app:
-            app = self.core.clean_client_name(self.name).lower()
+            app = self.clean_client_name(self.name).lower()
         else:
             app = app.lower()
 
@@ -110,7 +113,6 @@ class Client():
         # It will fail for things like gstreamer preview and nautilus that have no process link
         # until thats fixed at a lower level we have our trusty regular expressions
         if pid and self.pid and pid == self.pid:
-            #print "Match PID", pid, self.pid
             return True
 
         # Fall back rules if pid matching fails
@@ -133,52 +135,90 @@ class Client():
         return False
 
     def __fade_in(self):
-        self.volume_target = self.volume_default
+        self.volume_target = self.volume_max
 
     def __fade_out(self):
-        if self.volume_mute == -1:
+        if self.volume_min == -1:
             self.volume_target = self.core.mute_level
         else:
-            self.volume_target = self.volume_mute
+            self.volume_target = self.volume_min
 
     def __fade_mute(self):
-        self.volume_target = self.volume_step # if we goto 0 than our volume meter will never register a value
+
+        if self.volume_min == -1:
+            self.volume_target = self.core.mute_level
+        else:
+            self.volume_target = self.volume_min
+
+        # if we goto 0 than our volume meter will never register a value
+        if self.volume_target < self.volume_step:
+            self.volume_target = self.volume_step
     
-    def set_primary(self, value):
-        if value or self.category == "default":
+    def set_status(self, value):
+        if value or self.role == "default":
             self.__fade_in()
+            #if self.__pause_status > 0:
+            #    for plugin in self.plugins:
+            #        if plugin.enabled and not plugin.is_playing():
+            #            if plugin.set_pause(False): 
+            #                self.__pause_status = 0
         else:
             self.__fade_mute()
+            #if self.is_active() and self.__pause_status == 0:                 
+            #    self.__pause_status = 1
+        self.__status = value
+
+    def get_status(self):
+        for sink in self.sinks.values():
+            if sink.get_status():
+                return True
+        return False
+
+
+    # called by sink, check if all sinks are at correct volume
+    def check_volume(self):
+        result = True
+        for sink in self.sinks.values():
+            result = result and sink.volume_check
+
+        if result and self.__pause_status == 1:
+           #for plugin in self.plugins:
+           #     if plugin.enabled and plugin.is_playing():
+           #         plugin.set_pause(True)
+           self.__pause_status = 2
 
     def to_xml(self, el):
 
         # Set attributes to user element
         el.setAttribute("name", self.name)
         el.setAttribute("description", str(self.description))
-        el.setAttribute("volume_default", str(self.volume_default))
-        el.setAttribute("volume_mute", str(self.volume_mute))
+        el.setAttribute("volume_max", str(self.volume_max))
+        el.setAttribute("volume_min", str(self.volume_min))
         el.setAttribute("rule_re_window_title",self.rule_re_window_title.pattern)
         el.setAttribute("rule_re_command",self.rule_re_command.pattern)
         el.setAttribute("rule_re_application",self.rule_re_application.pattern)
-        el.setAttribute("category",self.category)
-        el.setAttribute("window_position_fade", str(self.window_position_fade))
+        el.setAttribute("role",self.role)
         el.setAttribute("icon_name", str(self.icon_name))
         el.setAttribute("apply_volume_meter_hack", str(self.apply_volume_meter_hack))
         el.setAttribute("fade_volume", str(self.fade_volume))
+        el.setAttribute("prefer_sink", self.prefer_sink)
 
-        if self.category == "default":
-            self.category = "event"
+        if self.role == "default":
+            self.role = "event"
 
     def from_xml(self, el):
         if(el.hasAttribute("name")) :                   self.name = el.getAttribute("name")
         if(el.hasAttribute("description")) :            self.description = el.getAttribute("description")
         if(el.hasAttribute("icon_name")) :              self.icon_name = el.getAttribute("icon_name")
-        if(el.hasAttribute("volume_default")) :         self.volume_default = int(el.getAttribute("volume_default"))
-        #self.volume_mute = int(el.getAttribute("volume_mute"))
+        if(el.hasAttribute("volume_max")) :         self.volume_max = int(el.getAttribute("volume_max"))
+        if(el.hasAttribute("volume_min")) :         self.volume_min = int(el.getAttribute("volume_min"))
         if(el.hasAttribute("rule_re_window_title")) :   self.rule_re_window_title = re.compile(el.getAttribute("rule_re_window_title"), re.IGNORECASE)
         if(el.hasAttribute("rule_re_command")) :        self.rule_re_command = re.compile(el.getAttribute("rule_re_command"), re.IGNORECASE)
         if(el.hasAttribute("rule_re_application")) :    self.rule_re_application = re.compile(el.getAttribute("rule_re_application"), re.IGNORECASE)
-        if(el.hasAttribute("category")) :               self.category = el.getAttribute("category")
+        if(el.hasAttribute("role")) :               self.role = el.getAttribute("role")
         if(el.hasAttribute("window_position_fade")) :   self.window_position_fade = el.getAttribute("window_position_fade") == "True"
         if(el.hasAttribute("apply_volume_meter_hack")): self.apply_volume_meter_hack = el.getAttribute("apply_volume_meter_hack") == "True"
         if(el.hasAttribute("fade_volume")):             self.fade_volume = el.getAttribute("fade_volume") == "True"
+        if(el.hasAttribute("prefer_sink")):                  self.prefer_sink = el.getAttribute("prefer_sink")
+
+
diff --git a/earcandy/EarCandy.py b/earcandy/EarCandy.py
new file mode 100644
index 0000000..58c9144
--- /dev/null
+++ b/earcandy/EarCandy.py
@@ -0,0 +1,433 @@
+
+import os
+import copy
+import sys
+import time
+import logging
+import shutil
+from xml.dom.minidom import *
+
+import gconf
+import pynotify
+import gtk
+import gobject
+import pynotify
+
+from util.DesktopFiles import DesktopFiles
+from util.Threads import threaded
+
+from Client import Client
+from Sink import Sink
+from windows.Watcher import Watcher
+from PulseAudio import PulseAudio
+
+from ui.TrayIcon import EarCandyStatusIcon
+from ui.EarCandyPrefs import EarCandayPrefs
+
+from EarCandyDBus import EarCandyDBusClient
+
+from earcandyconfig import getdatapath
+
+log = logging.getLogger('EarCandy')
+log.setLevel(logging.DEBUG)
+
+
+class EarCandy():
+
+    def __init__(self):
+
+        self.version = 0.7
+        self.active = True
+        
+        self.display = {
+                        "": "[ unknown ]", 
+                        "phone" : "Phone (VoIP)", 
+                        "video" : "Video Player", 
+                        "music" : "Music Player", 
+                        "event" : "Notification", 
+                        "game" : "Game" }
+
+        self.ignore = ["EsounD client (UNIX socket client)", 
+                        "ear-candy", 
+                        "Native client (UNIX socket client)", 
+                        "PulseAudio Volume Control"]
+
+        self.config_file = os.path.expanduser("~/.config/Ear Candy/settings.xml")     
+        self.default_config_file = os.path.join(getdatapath(), 'defaults', 'settings.xml')
+
+        self.pa = PulseAudio(self)
+        self.window_watcher = Watcher()
+        self.window_watcher.callback = self.on_active_window_change
+
+        self.prefer_sink = None
+
+        self.pref = None
+
+        pynotify.init('earcandy')
+
+    def notify(self, name, text):
+        return        
+        #n = pynotify.Notification(name, text, None)
+        #n.show()
+
+    def init(self):
+        self.is_mute = False
+        self.mute_phone = False
+
+        self.fade_timer_speed = 0.01 
+        self.mute_level = 20
+        self.follow_new_outputs = True
+
+        self.priority_stack = { "" : [], "phone" : [], "video" : [], "music" : [] , "game" : [] }   
+
+        self.apply_volume_thread_running = False 
+        self.select_client_thread_running = False
+
+    def run(self):
+
+        ecb = EarCandyDBusClient(self)
+        if ecb.is_running():
+            print "Ear candy already running..."
+            ecb.show_window()
+            sys.exit(0)
+
+        ecb.start_service()
+
+        self.init() 
+        self.load()
+        self.pa.connect()
+
+        self.apply_volume_thread()
+        self.select_client_thread()
+
+        self.status_icon = EarCandyStatusIcon(self)
+        self.start()
+
+        self.window_watcher.check_all()
+        
+        
+        #self.open_preferances()
+
+    def exit(self):
+        self.stop()
+        self.pa.disconnect()
+        gtk.main_quit()
+
+
+    def stop(self):
+        self.active = False
+        while self.apply_volume_thread_running or self.select_client_thread_running:
+            time.sleep(0.1)
+
+    def start(self):
+        self.active = True
+        while not self.apply_volume_thread_running or not self.select_client_thread_running:
+            time.sleep(0.1)
+
+    def reset_all(self):
+        # tricky we want to delete all client settings and reload our default xml file
+        self.stop()
+        self.pa.disconnect()
+        self.init()
+        self.load(False)
+        self.pa.connect()
+        self.start()
+        return
+
+    def reset(self):
+        self.stop()
+        self.pa.disconnect()
+        self.init()
+        self.load()
+        self.pa.connect()
+        self.start()
+        return
+
+    def save(self):
+        path = os.path.dirname(self.config_file)
+        if not os.path.exists( path ):
+            os.makedirs(path)
+
+        # New document
+        doc = Document()
+        ec = doc.createElement("earcandy")
+        doc.appendChild(ec)
+	
+        rules = doc.createElement("rules")
+        ec.appendChild(rules)
+        skip = copy.copy(self.ignore)
+        for client in self.pa.clients.values():
+            if not client.name in skip and client.role:
+                # Creates user element
+                el = doc.createElement("rule")
+                rules.appendChild(el)
+                client.to_xml(el)
+                skip.append(client.name)
+        
+        doc.documentElement.setAttribute("fade_timer_speed", str(self.fade_timer_speed))
+        doc.documentElement.setAttribute("mute_level", str(self.mute_level))
+        doc.documentElement.setAttribute("tray_visible", str(self.status_icon.get_visible()))
+        doc.documentElement.setAttribute("prefer_sink", str(self.prefer_sink))
+        doc.documentElement.setAttribute("follow_new_outputs", str(self.follow_new_outputs))
+        doc.documentElement.setAttribute("version", str(self.version))
+        doc.documentElement.setAttribute("mute_phone", str(self.mute_phone))
+
+        # Record outputs
+        outputs = doc.createElement("outputs")
+        for sink in self.pa.sinks.values():
+            # Creates user element
+            el = doc.createElement("output")
+            el.setAttribute("name", sink.name)
+            el.setAttribute("priority", str(sink.priority))
+            outputs.appendChild(el)
+        ec.appendChild(outputs)
+
+        # Plugin status
+        """
+        plugins = doc.createElement("plugins")
+        for plugin in self.plugin_manager.plugins:
+            # Creates user element
+            el = doc.createElement("plugin")
+            el.setAttribute("name", plugin.get_plugin_name())
+            el.setAttribute("enabled", str(plugin.enabled))
+            plugins.appendChild(el)
+        ec.appendChild(plugins)"""
+
+        fp = open(self.config_file,"w")
+        doc.writexml(fp, "    ", "", "\n", "UTF-8")
+        fp.close()
+
+    def load(self, user_settings=True):
+
+        settings_version = 0
+        xml = None
+        doc = None
+
+        # Load the defaults
+        f = open(self.default_config_file, "r")
+        xml = f.read()
+        f.close()
+        default_doc = parseString(xml)
+        default_version = float(default_doc.documentElement.getAttribute("version"))
+
+        # Check for user settings
+        if user_settings and os.path.exists( self.config_file ):
+            # Load XML
+            try:
+                f = open(self.config_file, "r")
+                xml = f.read()
+                f.close()
+                doc = parseString(xml)
+                if doc.documentElement.hasAttribute("version"):
+                    settings_version = float(doc.documentElement.getAttribute("version"))
+            except:
+                pass
+
+        # Load defaults ?
+        if not doc or default_version > settings_version:
+            doc = default_doc
+            
+        # Load client rules
+        for el in doc.getElementsByTagName("rule"):
+            client = Client(self, "")
+            client.from_xml(el)
+            if client.role:
+                log.info("load rule : %s " % client.description)
+            self.pa.clients[client.name] = client
+            #self.plugin_manager.check_client( client )
+
+
+        # Load sink rules
+        for el in doc.getElementsByTagName("output"):
+            sink = Sink(el.getAttribute("name"), int(el.getAttribute("priority")) )
+            self.pa.sinks[sink.name] = sink
+            #self.plugin_manager.check_client( client )
+
+
+        self.fade_timer_speed = float(doc.documentElement.getAttribute("fade_timer_speed"))
+        #self.mute_level = float(doc.documentElement.getAttribute("mute_level"))
+        #if doc.documentElement.hasAttribute("tray_visible"):
+        #    self.status_icon.set_visible( doc.documentElement.getAttribute("tray_visible") == "True" )
+        #if doc.documentElement.hasAttribute("prefer_sink"):
+        #    self.prefer_sink = doc.documentElement.getAttribute("prefer_sink")
+        #if doc.documentElement.hasAttribute("follow_new_outputs"):
+        #    self.follow_new_outputs = doc.documentElement.getAttribute("follow_new_outputs") == "True"
+        #if doc.documentElement.hasAttribute("mute_phone"):
+        #    self.mute_phone = doc.documentElement.getAttribute("mute_phone") == "True"
+
+
+    @threaded
+    def apply_volume_thread(self):
+        while True:
+            #logging.debug(": apply_volume_thread " )
+            if self.active:
+                self.apply_volume_thread_running = True
+                gobject.idle_add(self.__adjust_volumes)      
+            else:
+                self.apply_volume_thread_running = False
+            time.sleep(self.fade_timer_speed)
+
+    @threaded
+    def select_client_thread(self):
+        while True:
+            if self.active:
+                self.select_client_thread_running = True
+                gobject.idle_add(self.set_active_clients)  
+            else:
+                self.select_client_thread_running = False
+            time.sleep(0.5)
+
+    def __adjust_volumes(self):
+        # Always update based on active sinks
+        for sink in self.pa.sink_inputs.values():
+            if sink.set_volume():
+                # set pa volume
+                self.pa.pa_context_set_sink_input_volume(sink.index, sink.volume)  
+
+    def __set_window_stack(self, active_client):
+        for client in self.pa.clients.values():
+
+            # Select the primary client and order previous clients
+            for key in self.priority_stack.keys():
+                if client.role == key:
+                    # Add client to role
+                    if not client in self.priority_stack[key]:
+                        if active_client == client:
+                            self.priority_stack[key].insert(0, client)
+                        else:
+                            self.priority_stack[key].append(client)
+
+                    # reshuffle if active
+                    elif active_client == client and not self.priority_stack[key][0] == client:
+                        self.priority_stack[key].remove(client)
+                        self.priority_stack[key].insert(0, client)
+
+                # If role has changed remove old entry
+                elif client in self.priority_stack[key]:
+                    self.priority_stack[key].remove(client)
+
+    def print_stacks(self):
+        for key in self.priority_stack.keys():
+            if key:
+                text =""
+                for client in self.priority_stack[key]:
+                    text += client.description
+                    text += " (" 
+                    if client.is_active():
+                        text += "*"
+                    else:
+                        text += str(len(client.sinks.values())) 
+                    text += "), "
+                if text: log.debug("stack " + key + " " + text)
+
+    def set_active_clients(self):
+
+        # Based on per sink stacks
+        for sink in self.pa.sinks.values():
+
+            # find the top ranking sink_input for this sink
+            highest_sink_input = None
+            highest_stack_media_role_index = -1   
+            highest_stack_media_role_window_index = -1
+
+            for sink_input in self.pa.sink_inputs.values():
+                status = False
+
+                if sink_input.is_active():
+
+                    # check against media role   
+                    if sink_input.role and sink_input.role in self.priority_stack.keys():
+                    
+                        index = self.priority_stack.keys().index(sink_input.role)
+                        if index <= highest_stack_media_role_index or highest_stack_media_role_index == -1:
+
+                            # check window stack for client
+                            if sink_input.client in self.priority_stack[sink_input.role]:
+                                window_index = self.priority_stack[sink_input.role].index(sink_input.client)
+                                if window_index <= highest_stack_media_role_window_index or highest_stack_media_role_window_index == -1:
+                                    
+                                    # Set previous highest to inactive
+                                    if highest_sink_input:
+                                        highest_sink_input.set_status(False)
+
+                                    # Set new sink to active
+                                    highest_sink_input = sink_input
+                                    highest_stack_media_role_index = index
+                                    highest_stack_media_role_window_index = window_index
+                                    status = True
+                                    
+                    else:
+                        # unknown media role so leave it always on
+                        status = True
+
+                sink_input.set_status(status)
+        #self.print_stacks()
+
+    def on_active_window_change(self, application, state):  #, pid, window_name, x, y, icon, fullscreen
+        if application:
+            if state in ("active","open"):
+                for client in self.pa.clients.values():
+                    if self.match_client_to_application(client, application, state): 
+                        log.info(": window to client match %s => %s" % (application.name, client.description) )
+                        self.__set_window_stack(client)
+                        break
+                
+
+    def match_client_to_application(self, client, application, index=0):
+        if client.test_focus_window(application.pid, application.window_name, application.command, application.name): 
+            client.fullscreen = application.fullscreen  
+            if not client.icon : client.icon = application.icon
+            if not client.icon_name : client.icon_name = application.icon_name
+            if not client.description :client.description = application.description
+            if not client.role: client.role = application.role
+            client.application = application
+
+            return True
+        return False
+
+    def set_auto_start(self, flag):
+        filename = "earcandy.desktop"
+        path = os.path.expanduser(os.getenv('XDG_CONFIG_HOME', '~/.config/autostart'))
+        dest = os.path.join(path, filename)
+        src = "/usr/share/applications/earcandy.desktop"
+
+        if flag:
+            # insure the autostart path exists
+            if not os.path.exists(path):
+                os.makedirs(path)
+
+            if not os.path.exists( dest ):
+                shutil.copyfile(src, dest)
+        else:
+            if os.path.exists( dest ):
+                os.remove( dest )
+
+    def is_auto_start(self):
+        filename = "earcandy.desktop"
+        dest = os.path.expanduser(os.getenv('XDG_CONFIG_HOME', '~/.config/autostart/' + filename))
+        return os.path.exists(dest)
+
+    def open_preferances(self):
+        self.print_stacks()
+        if not self.pref:
+            self.pref = EarCandayPrefs(self)
+        self.pref.run()
+
+    def close_preferances(self):
+        if self.pref:
+            self.pref = None
+
+    def get_current_sink_volume(self):
+        self.pa.get_sink_info_by_name(self.pa.prefer_sink_index)
+
+
+if __name__ == '__main__':
+    logging.basicConfig(level=logging.INFO)
+    import gtk
+    # Turn on gtk threading
+    gtk.gdk.threads_init()
+
+    ec = EarCandy()
+    ec.run()
+
+    gtk.main()
diff --git a/ear_candy/EarCandyDBus.py b/earcandy/EarCandyDBus.py
similarity index 100%
rename from ear_candy/EarCandyDBus.py
rename to earcandy/EarCandyDBus.py
diff --git a/earcandy/PulseAudio.py b/earcandy/PulseAudio.py
new file mode 100644
index 0000000..44d1b24
--- /dev/null
+++ b/earcandy/PulseAudio.py
@@ -0,0 +1,254 @@
+# Ear Candy - Pulseaduio sound managment tool
+# Copyright (C) 2008 Jason Taylor
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+import logging
+import ctypes
+from pulseaudio.PulseAudioConnection import PulseAudioConnection
+from Client import Client
+from SinkInput import SinkInput
+from Sink import Sink
+
+log = logging.getLogger('EarCandyPulseAudio')
+log.setLevel(logging.INFO)
+
+class PulseAudio(PulseAudioConnection):
+    def __init__(self, Core):
+
+        self.core = Core 
+        self.prefer_sink_index = 0       
+        self.prefer_source_index = 0 
+
+        self.sink = {}          # sinks by name
+        self.sinks_by_id = {}   # sinks by id
+        self.clients = {}       # clients by name
+        self.clients_by_id = {} # clients by id
+        self.sink_inputs = {}   # sink inputs by id
+        self.source_outputs = {}   # source outputs by id
+
+        PulseAudioConnection.__init__(self, "EarCandy")
+
+    # CONNECTION
+    def pa_status_ready(self):
+        # list sinks again now that sink_inputs have been loaded
+        # this will trigger auto usb selection
+        self.pa_context_get_sink_info_list()
+        self.pa_context_get_source_info_list()
+        self.pa_context_get_source_output_info_list()
+
+
+    ### DEAL WITH SINKS #################################
+    def pa_sink_info_cb(self, sink, user_data):
+        s = None
+        if not sink.name in self.sinks.keys():
+            s = Sink(sink.name, 0, sink.index)
+            log.info("new sink : %s %s" % (sink.description, sink.index))
+            self.sinks[sink.name] = s
+                      
+        else:
+            s = self.sinks[sink.name]
+
+        self.sinks_by_id[sink.index] = s  
+        s.description = sink.description
+        s.monitor_source = sink.monitor_source
+        s.monitor_source_name = sink.monitor_source_name
+        s.index = sink.index
+        s.volume = sink.volume
+
+        if s.name.lower().count("usb") > 0 or s.name.lower().count("bluetooth") > 0:
+            self.core.notify("New sound device", "Moving sound to new device")
+            log.info("new device detected moving sink inputs : %s " % sink.description)
+
+            self.prefer_sink_index = sink.index
+            self.pa_context_set_default_sink(sink.name)
+            self.move_all_sinks_inputs(sink.index)
+
+        if self.core.pref:
+            self.core.pref.update_output(s, s.priority)
+
+    def pa_sink_remove_cb(self, index):
+        sink = self.sinks_by_id[index]
+        sink.index = 0
+        if self.core.pref:
+            self.core.pref.update_output(sink, False)
+        return
+
+    def move_all_sinks_inputs(self, sink_index):
+        for sink_input in self.sink_inputs.values():
+            self.pa_context_move_sink_input_by_index(sink_input.index, sink_index)
+
+    def move_client_to_sink(self, client):
+        sink_index = self.prefer_sink_index
+        if client.prefer_sink:
+            for sink in self.sinks_by_id.values():
+                if sink.name == client.prefer_sink:
+                    sink_index = sink.index
+                    break
+        for sink_input in client.sinks.values():
+            self.pa_context_move_sink_input_by_index(sink_input.index, sink_index)
+
+    ### DEAL WITH SINKS #################################
+
+
+    ### DEAL WITH SOURCES ###############################
+    def pa_source_info_cb(self, source, user_data, s):
+        if source.name.lower().count("usb") > 0 or source.name.lower().count("bluetooth") > 0:
+            self.core.notify("New microphone", "Moving input to new device")
+            self.prefer_source_index = source.index
+            self.pa_context_set_default_source(source.name)
+            self.move_all_source_outputs(source.index)
+
+    def pa_source_output_info_cb(self, source_output, user_data):
+
+        if self.prefer_source_index > 0 and source_output.source != self.prefer_source_index:
+            self.pa_context_move_source_output_by_index(source_output.index, self.prefer_source_index)
+
+        self.source_outputs[source_output.index] = source_output
+
+        print "SOURCE OUTPUT" ,  source_output.name, source_output.source 
+
+    def move_all_source_outputs(self, source_index):
+        for source_output in self.source_outputs.values():
+            self.pa_context_move_source_output_by_index(source_output.index,  source_index)
+
+    ### DEAL WITH SOURCES ###############################
+
+
+    # CLIENTS    
+    def pa_client_info_cb(self, client, user_data):
+
+        if not self.clients.has_key(client.name):
+            c = Client(self.core, client.name, client.pid)
+            log.info("new client : %s" % c.description)
+            
+            self.clients[client.name] = c
+        else:
+            log.debug("update client : %s" % client.name)
+            c = self.clients[client.name]
+            c.pid = client.pid
+        self.clients_by_id[client.index] = c
+
+    def pa_client_remove_cb(self, index):
+        
+        if self.clients_by_id.has_key(index):
+            client = self.clients_by_id[index]
+            log.debug("remove client : %s" % client.name)
+            del self.clients_by_id[index]
+
+    # SINK INPUTS
+    def pa_sink_input_info_cb(self, sink_input, user_data):
+        target_sink = self.prefer_sink_index
+
+        if not self.sink_inputs.has_key(sink_input.index):
+            s = SinkInput(sink_input.index, sink_input.name, sink_input.volume, sink_input.sink, sink_input.media_role)
+            self.sink_inputs[s.index] = s
+
+            if self.clients_by_id.has_key(sink_input.client):
+                client = self.clients_by_id[sink_input.client]
+                client.sinks[s.index] = s
+                s.client = client
+
+                # Skim client role from 1st stream
+                if not client.role and sink_input.media_role:
+                    client.role = sink_input.media_role
+                    log.info("new client media role : %s %s " % (client.name, sink_input.media_role))
+
+            self.pa_create_monitor_stream_for_sink_input(sink_input.index, self.sinks_by_id[sink_input.sink].monitor_source, sink_input.name, s.client.description)
+
+            log.info("new sink input : %s %s " % (s.client.description, s.name))
+
+        else:
+            log.debug("update sink input : %s %s" % (sink_input.name, sink_input.sink))
+            s = self.sink_inputs[sink_input.index]
+            s.volume = sink_input.volume
+            s.name = sink_input.name
+
+            # Check we have the correct sink
+            if s.client.prefer_sink:
+                for sink in self.sinks_by_id.values():
+                    if sink.name == s.client.prefer_sink:
+                        target_sink = sink.index
+
+            if not s.sink == sink_input.sink:
+                self.pa_create_monitor_stream_for_sink_input(sink_input.index, self.sinks_by_id[sink_input.sink].monitor_source, sink_input.name, s.client.description)   
+                s.sink = sink_input.sink
+
+        if target_sink > 0 and self.sink_inputs[sink_input.index].sink != target_sink:
+            self.pa_context_move_sink_input_by_index(sink_input.index, target_sink)
+
+        # recheck clients to windows
+        self.core.window_watcher.check_all()
+
+    def pa_sink_input_remove_cb(self, index):
+
+        if self.sink_inputs.has_key(index):
+            sink = self.sink_inputs[index]
+            log.debug("remove sink input : %s" % sink.name)
+            
+            del( sink.client.sinks[index] )
+            del( self.sink_inputs[index] )
+
+            # reset volume in pa
+            v = []
+            for i in range(0, len(sink.volume)):
+                v.append(100)
+            self.pa_context_set_sink_input_volume(index, v)
+
+            if self.core.pref:
+                self.core.pref.update_client( sink.client )
+
+
+    # VOLUME METER FOR SINK INPUT
+    def pa_stream_request_cb(self, meter_level, user_data):
+        if user_data in self.sink_inputs.keys():
+            self.sink_inputs[user_data].set_meter( meter_level )
+            log.debug("pa_stream_request_cb : %s %s" % (meter_level, user_data))
+            if self.core.pref:
+                self.core.pref.update_client( self.sink_inputs[user_data].client )
+
+    def pa_create_monitor_stream_for_sink_input(self, index, monitor_index, name, description):
+        
+        log.debug("pa_create_monitor_stream_for_sink_input : %s" % index)
+
+        # Create new stream
+        ss = self.pa_sample_spec()
+        ss.channels = 1
+        ss.format = 5
+        ss.rate = 25
+        pa_stream = self.pa_stream_new(description, ss)
+        
+        self.pa_stream_set_monitor_stream(pa_stream, index);
+        self.pa_stream_set_read_callback(pa_stream, index);
+        self.pa_stream_set_suspended_callback(pa_stream);
+
+        attr = self.pa_buffer_attr()
+        attr.fragsize = 4
+        attr.maxlength = 10
+        attr.tlength = 0
+        attr.prebuf = 0
+        attr.minreq = 0
+
+        self.pa_stream_connect_record(pa_stream, str(monitor_index), attr, 10752) 
+
+
+if __name__ == '__main__':
+
+    import gtk
+    # Turn on gtk threading
+    gtk.gdk.threads_init()
+
+    pa = PulseAudio("Test")
+    pa.connect()
+
+    gtk.main()
diff --git a/runner/ear_candy b/earcandy/Sink.py
old mode 100755
new mode 100644
similarity index 64%
rename from runner/ear_candy
rename to earcandy/Sink.py
index 7faf7a4..340b392
--- a/runner/ear_candy
+++ b/earcandy/Sink.py
@@ -1,6 +1,5 @@
-#!/usr/bin/env python
 # Ear Candy - Pulseaduio sound managment tool
-# Copyright (C) 2008 Jason Taylor, Michael Budde
+# Copyright (C) 2008 Jason Taylor
 # 
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -14,10 +13,20 @@
 # 
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
-import ear_candy
-import subprocess
-import os, sys
 
-if __name__ == "__main__":
-    path = os.path.join(os.path.dirname(ear_candy.__file__), "ear_candy.py")
-    subprocess.call([sys.executable, path] + sys.argv[1:])
+import math  
+import time
+import datetime 
+         
+class Sink():
+    def __init__(self, name, priority=0, index=0):
+
+        self.name = name
+        self.priority = priority
+
+        
+        self.index = index
+        self.monitor_source = 0
+        self.monitor_source_name = ""
+        self.description = name
+        self.volume = None
diff --git a/earcandy/SinkInput.py b/earcandy/SinkInput.py
new file mode 100644
index 0000000..78d7a2a
--- /dev/null
+++ b/earcandy/SinkInput.py
@@ -0,0 +1,144 @@
+# Ear Candy - Pulseaduio sound managment tool
+# Copyright (C) 2008 Jason Taylor
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import math  
+import time
+import datetime 
+         
+class SinkInput():
+    def __init__(self, index, name, volume, sink, role):
+
+        self.index = index
+        self.name = name
+        self.client = None
+        self.volume = volume
+        self.sink = sink
+        self.__role = role
+
+        self.volume_meter_level = 0
+        self.volume_meter_last_non_zero_timestamp = time.mktime(datetime.datetime.min.timetuple())
+
+        self.volume_target = 100
+
+        self.__previous_volume = 0
+
+        self.volume_check = True
+        self.__status = False
+
+
+    def get_media_role(self):
+        if not self.__role: return self.client.role
+        return self.__role
+
+    def set_media_role(self, role):
+        self.__role = role
+
+    role = property(get_media_role, set_media_role)
+
+ 
+    def set_meter(self, meter_level):
+        timestamp = math.floor(time.mktime(datetime.datetime.now().timetuple()))
+        if(meter_level > 0):
+            self.volume_meter_last_non_zero_timestamp = timestamp
+        self.volume_meter_level = meter_level
+
+    def is_active(self):
+        timestamp = math.floor(time.mktime(datetime.datetime.now().timetuple()))
+        compare = timestamp - self.volume_meter_last_non_zero_timestamp
+        if compare <= 1:
+            return True
+        return False
+
+
+
+    ## new per sink settings ##
+    def set_volume(self):
+
+        current_volume = round(self.volume[0])
+        step_volume = current_volume
+        result = True
+
+        if current_volume < self.volume_target:      
+            step_volume = current_volume + self.client.volume_step
+            if not self.client.fade_volume: step_volume = self.volume_target
+        elif current_volume > self.volume_target: 
+            step_volume = current_volume - self.client.volume_step
+            if not self.client.fade_volume: step_volume = self.volume_target
+        
+        if step_volume > 100:
+            step_volume = 100
+        if step_volume < self.client.volume_step:
+            step_volume = self.client.volume_step
+
+        # we dont want to get stuck in a loop because volumes arn't exactly the same 
+        result = math.fabs(self.__previous_volume - current_volume) >= self.client.volume_step
+        volume_check = math.fabs(self.client.volume_target - current_volume) < self.client.volume_step
+
+        if result:
+            for i in range(0, len(self.volume)):
+                self.volume[i] = step_volume
+
+            #print "\nAdjust Volume", self.client.name, step_volume
+        
+        if volume_check and not self.volume_check:
+            self.volume_check = volume_check
+            self.client.check_volume()
+
+        self.volume_check = volume_check
+
+        self.__previous_volume = step_volume       
+        return result
+
+    def get_volume_meter(self):
+        return sink.volume_meter_level
+
+    def __fade_in(self):
+        self.volume_target = self.client.volume_max
+
+    def __fade_out(self):
+        if self.client.volume_min == -1:
+            self.volume_target = self.client.core.mute_level
+        else:
+            self.volume_target = self.client.volume_min
+
+    def __fade_mute(self):
+        if self.client.volume_min == -1:
+            self.volume_target = self.client.core.mute_level
+        else:
+            self.volume_target = self.client.volume_min
+
+        # if we goto 0 than our volume meter will never register a value
+        if self.volume_target < self.client.volume_step:
+            self.volume_target = self.client.volume_step
+    
+    def set_status(self, value):
+        
+        if value or self.role == "default":
+            self.__fade_in()
+        else:
+            self.__fade_mute()
+
+        self.__status = value
+
+    def get_status(self):
+        return self.__status
+
+
+
+
+
+
+
diff --git a/ear_candy/pulseaudio/__init__.py b/earcandy/__init__.py
similarity index 100%
copy from ear_candy/pulseaudio/__init__.py
copy to earcandy/__init__.py
diff --git a/earcandy/earcandyconfig.py b/earcandy/earcandyconfig.py
new file mode 100644
index 0000000..c1a4778
--- /dev/null
+++ b/earcandy/earcandyconfig.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+# THIS IS Earcandy CONFIGURATION FILE
+# YOU CAN PUT THERE SOME GLOBAL VALUE
+# Do not touch until you know what you're doing.
+# you're warned :)
+
+# where your project will head for your data (for instance, images and ui files)
+# by default, this is ../data, relative your trunk layout
+__earcandy_data_directory__ = '../data/'
+
+
+import os
+
+class project_path_not_found(Exception):
+    pass
+
+def getdatapath():
+    """Retrieve earcandy data path
+
+    This path is by default <earcandy_lib_path>/../data/ in trunk
+    and /usr/share/earcandy in an installed version but this path
+    is specified at installation time.
+    """
+
+    # get pathname absolute or relative
+    if __earcandy_data_directory__.startswith('/'):
+        pathname = __earcandy_data_directory__
+    else:
+        pathname = os.path.dirname(__file__) + '/' + __earcandy_data_directory__
+
+    abs_data_path = os.path.abspath(pathname)
+    if os.path.exists(abs_data_path):
+        return abs_data_path
+    else:
+        raise project_path_not_found
+
diff --git a/ear_candy/window/__init__.py b/earcandy/plugins/banshee/__init__.py
similarity index 100%
rename from ear_candy/window/__init__.py
rename to earcandy/plugins/banshee/__init__.py
diff --git a/earcandy/plugins/banshee/plugin.py b/earcandy/plugins/banshee/plugin.py
new file mode 100644
index 0000000..ff3f02f
--- /dev/null
+++ b/earcandy/plugins/banshee/plugin.py
@@ -0,0 +1,57 @@
+# Ear Candy - Pulseaduio sound managment tool
+# Copyright (C) 2009  Jason Taylor
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from PluginBase import PluginBaseObject
+
+import dbus
+
+class BansheeControlPlugin(PluginBaseObject):
+
+    def __init__(self, core, path):
+        PluginBaseObject(core, path) 
+        self.bus = dbus.SessionBus()
+        return
+
+    def get_client_name(self):
+        return "Banshee"
+
+    def get_plugin_name(self):
+        return "Banshee pause control (DBUS)"
+
+    # Is application playing
+    def is_playing(self):
+        banshee = self.__get_application()
+        if banshee:        
+            return not banshee.GetCurrentState() == "paused"
+        return False
+
+    # Set application paused status
+    def set_pause(self, status):
+        banshee = self.__get_application()
+        if banshee:
+            if status:
+                banshee.Pause()
+            else:
+                banshee.Play()
+            return True
+        return False
+
+    def __get_application(self):
+        return self.bus.get_object("org.bansheeproject.Banshee", "/org/bansheeproject/Banshee/PlayerEngine")
+
+
+def register_plugin( core, path ):
+    return BansheeControlPlugin( core, path )
diff --git a/ear_candy/pulseaudio/__init__.py b/earcandy/plugins/rhythmbox/__init__.py
similarity index 100%
copy from ear_candy/pulseaudio/__init__.py
copy to earcandy/plugins/rhythmbox/__init__.py
diff --git a/earcandy/plugins/rhythmbox/plugin.py b/earcandy/plugins/rhythmbox/plugin.py
new file mode 100644
index 0000000..97d4526
--- /dev/null
+++ b/earcandy/plugins/rhythmbox/plugin.py
@@ -0,0 +1,54 @@
+# Ear Candy - Pulseaduio sound managment tool
+# Copyright (C) 2009  Jason Taylor
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from PluginBase import PluginBaseObject
+
+import dbus
+
+class RhythmboxControlPlugin(PluginBaseObject):
+
+    def __init__(self, core, path):
+        PluginBaseObject(core, path) 
+        self.bus = dbus.SessionBus()
+        return
+
+    # Client this plugin applies to
+    def get_client_name(self):
+        return "Rhythmbox"
+
+    def get_plugin_name(self):
+        return "Rhythmbox pause control (DBUS)"
+
+    def __get_application(self):
+        return self.bus.get_object("org.gnome.Rhythmbox", "/org/gnome/Rhythmbox/Player")
+
+    # Is application paused
+    def is_playing(self):
+        rb = self.__get_application()
+        if rb:  
+            return rb.getPlaying()
+        return False
+
+    # Set application paused status
+    def set_pause(self, status):
+        rb = self.__get_application()
+        if rb:
+            rb.playPause(status)
+            return True
+        return False
+
+def register_plugin( core, path ):
+    return RhythmboxControlPlugin( core, path )
diff --git a/earcandy/pulseaudio/Client.py b/earcandy/pulseaudio/Client.py
new file mode 100644
index 0000000..f623988
--- /dev/null
+++ b/earcandy/pulseaudio/Client.py
@@ -0,0 +1,12 @@
+
+class Client():
+    def __init__(self, pa, contents):
+        
+        self.name = str(contents.name)
+        self.index = int(contents.index)
+        self.pid = float(pa.pa_proplist_gets(contents.proplist, "application.process.id") or -1)
+
+        # Almost no applications use these yet :(
+        self.binary = pa.pa_proplist_gets(contents.proplist, "application.process.binary") or None
+        self.xid = pa.pa_proplist_gets(contents.proplist, "window.x11.xid") or -1
+        self.window_id = pa.pa_proplist_gets(contents.proplist, "window.id") or -1
diff --git a/earcandy/pulseaudio/Crash.py b/earcandy/pulseaudio/Crash.py
new file mode 100644
index 0000000..98239eb
--- /dev/null
+++ b/earcandy/pulseaudio/Crash.py
@@ -0,0 +1,24 @@
+
+
+from PulseAudioConnection import PulseAudioConnection
+
+
+class PulseAudio(PulseAudioConnection):
+    def __init__(self, Name):
+
+        PulseAudioConnection.__init__(self, Name)
+
+    def pa_sink_input_info_cb(self, sink_input, user_data):
+        #print self.convert_simple_volume_to_pa_volume(sink_input.volume).values[0]
+        self.pa_context_set_sink_input_volume(sink_input.index, sink_input.volume  )
+        return
+
+if __name__ == '__main__':
+    import gtk
+    # Turn on gtk threading
+    gtk.gdk.threads_init()
+
+    pa = PulseAudio("Test")
+    pa.connect()
+
+    gtk.main()
diff --git a/earcandy/pulseaudio/PulseAudioConnection.py b/earcandy/pulseaudio/PulseAudioConnection.py
new file mode 100644
index 0000000..d05e512
--- /dev/null
+++ b/earcandy/pulseaudio/PulseAudioConnection.py
@@ -0,0 +1,363 @@
+# Ear Candy - Pulseaduio sound managment tool
+# Copyright (C) 2008 Jason Taylor
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from lib_pulseaudio import *
+import gobject
+import ctypes
+import logging
+from Client import Client
+from SinkInput import SinkInput
+from Sink import Sink
+from SourceOutput import SourceOutput
+from Source import Source
+PA_VOLUME_CONVERSION_FACTOR = 655.36
+
+log = logging.getLogger('PulseAudioConnection')
+log.setLevel(logging.DEBUG)
+
+# A null method that can be given to pulse methods
+def null_cb(a=None, b=None, c=None, d=None):
+    #print "NULL CB"
+    return
+
+class PulseAudioConnection():
+
+    def __init__(self, Name="Simple Pulse Audio Connection"):
+        
+        self.Name = Name
+
+        self.sinks = {}
+        self.monitor_sinks = []
+        self.module_stream_restore_argument = ""
+
+    def connect(self):
+
+        self.__mainloop = pa_threaded_mainloop_new()
+        self.__mainloop_api = pa_threaded_mainloop_get_api(self.__mainloop)
+
+        self._context = pa_context_new(self.__mainloop_api, self.Name )   
+
+        self._context_notify_cb = pa_context_notify_cb_t(self.context_notify_cb) 
+        pa_context_set_state_callback(self._context, self._context_notify_cb, None)   
+
+        pa_context_connect(self._context, None, 0, None);
+        pa_threaded_mainloop_start(self.__mainloop)
+
+    def disconnect(self):
+        pa_context_disconnect(self._context)
+
+    # pulseaudio connection status    
+    def context_notify_cb(self, context, userdata):
+        
+        try:
+            ctc = pa_context_get_state(context)
+            if ctc == PA_CONTEXT_READY:
+                log.debug("connection ready")
+
+                self._null_cb = pa_context_success_cb_t(null_cb)
+                self._pa_context_success_cb = pa_context_success_cb_t(self.__pa_context_success_cb)
+                self._pa_stream_request_cb = pa_stream_request_cb_t(self.__pa_stream_request_cb)
+                self._pa_stream_notify_cb = pa_stream_notify_cb_t(self.__pa_stream_request_cb)
+                self._pa_sink_info_cb = pa_sink_info_cb_t(self.__pa_sink_info_cb)
+                self._pa_context_subscribe_cb = pa_context_subscribe_cb_t(self.__pa_context_subscribe_cb)
+                self._pa_source_info_cb = pa_source_info_cb_t(self.__pa_source_info_cb)
+                self._pa_source_output_info_cb = pa_source_output_info_cb_t(self.__pa_source_output_info_cb)
+                self._pa_sink_input_info_list_cb = pa_sink_input_info_cb_t(self.__pa_sink_input_info_cb)
+                self._pa_client_info_list_cb = pa_client_info_cb_t(self.__pa_client_info_cb)
+                self._pa_module_info_cb = pa_module_info_cb_t(self.__pa_module_info_cb)
+                self._pa_context_index_cb = pa_context_index_cb_t(self.__pa_context_index_cb) 
+
+                o = pa_context_get_module_info_list(self._context, self._pa_module_info_cb, True)
+                pa_operation_unref(o)
+
+                o = pa_context_get_source_info_list(self._context, self._pa_source_info_cb, True)
+                pa_operation_unref(o)
+
+                o = pa_context_get_sink_info_list(self._context, self._pa_sink_info_cb, True)
+                pa_operation_unref(o)
+
+                o = pa_context_get_client_info_list(self._context, self._pa_client_info_list_cb, None)
+                pa_operation_unref(o)
+                
+                o = pa_context_get_source_output_info_list(self._context, self._pa_source_output_info_cb, None)
+                pa_operation_unref(o)
+
+                o = pa_context_get_sink_input_info_list(self._context, self._pa_sink_input_info_list_cb, True)
+                pa_operation_unref(o)
+
+                pa_context_set_subscribe_callback(self._context, self._pa_context_subscribe_cb, None);
+                o = pa_context_subscribe(self._context, (pa_subscription_mask_t)
+                                               (PA_SUBSCRIPTION_MASK_SINK |
+                                                PA_SUBSCRIPTION_MASK_SOURCE |
+                                                PA_SUBSCRIPTION_MASK_SINK_INPUT |
+                                                PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT |
+                                                PA_SUBSCRIPTION_MASK_CLIENT |
+                                                PA_SUBSCRIPTION_MASK_SERVER), self._null_cb, None)  
+
+                pa_operation_unref(o)     
+                gobject.idle_add(self.pa_status_ready)        
+
+            if ctc == PA_CONTEXT_FAILED :
+                log.debug("connection failed")
+                pa_threaded_mainloop_signal(self.__mainloop, 0)
+                
+            if ctc == PA_CONTEXT_TERMINATED:
+                log.debug("connection terminated")
+                pa_threaded_mainloop_signal(self.__mainloop, 0)
+
+        except Exception, text:
+            log.exception("context_notify_cb %s" % text)
+
+    def __pa_context_index_cb(self, context, index, user_data):
+        log.debug("__pa_context_index_cb")
+        gobject.idle_add(self.pa_context_index_cb,  index, user_data)
+        return
+
+    def __pa_module_info_cb(self, context, struct, eol, user_data):
+        
+        if struct:
+            log.debug("__pa_module_info_cb : %s" % struct.contents.name)
+            gobject.idle_add(self.pa_module_info_cb, struct.contents, eol, user_data)
+
+    def __pa_source_info_cb(self, context, struct, eol, user_data):
+        if struct:
+            source = Source(self, struct.contents)
+            log.debug("__pa_source_info_cb : %s" % struct.contents.name)
+            gobject.idle_add(self.pa_source_info_cb, source, eol, user_data)
+
+    def __pa_source_output_info_cb(self, context, struct, c_int, user_data):
+            if struct:
+                source_output = SourceOutput(self, struct.contents)
+                log.debug("__pa_source_output_info_cb : %s" % struct.contents.name)
+                gobject.idle_add(self.pa_source_output_info_cb, source_output, user_data)
+
+    def __pa_context_success_cb(self, context, c_int, user_data):
+        log.debug("__pa_source_output_info_cb")
+        gobject.idle_add(self.pa_context_success_cb, c_int, user_data)
+
+    def __pa_stream_request_cb(self, stream, length, user_data):
+        data = self.pa_stream_peek(stream, length)
+        v = data[length / 4 -1] * 100
+        if (v < 0):
+            v = 0
+        if (v > 100):
+            v = 100
+        self.pa_stream_drop(stream)
+
+        #log.debug("__pa_stream_request_cb")
+        gobject.idle_add(self.pa_stream_request_cb, v, user_data)
+
+    def __pa_context_subscribe_cb(self, context, event_type, index, user_data):
+
+        try:
+            et = event_type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK
+
+            if et == PA_SUBSCRIPTION_EVENT_CLIENT:
+                
+                if event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK == PA_SUBSCRIPTION_EVENT_REMOVE:
+                    log.debug("PA_SUBSCRIPTION_EVENT_CLIENT | PA_SUBSCRIPTION_EVENT_REMOVE : %d" % index)
+                    gobject.idle_add(self.pa_client_remove_cb, int(index))
+                else:
+                    o = pa_context_get_client_info(self._context, index, self._pa_client_info_list_cb, None)
+                    pa_operation_unref(o)
+
+            if et == PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+                if event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK == PA_SUBSCRIPTION_EVENT_REMOVE:
+                    log.debug("PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_REMOVE : %d" % index)
+                    gobject.idle_add(self.pa_sink_input_remove_cb, int(index))
+                else:
+                    o = pa_context_get_sink_input_info(self._context, int(index), self._pa_sink_input_info_list_cb, True)
+                    pa_operation_unref(o)
+                    
+            if et == PA_SUBSCRIPTION_EVENT_SINK:
+                if event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK == PA_SUBSCRIPTION_EVENT_REMOVE:
+                    log.debug("PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE : %d" % index)
+                    gobject.idle_add(self.pa_sink_remove_cb, int(index))
+                else:
+                    o = pa_context_get_sink_info_by_index(self._context, int(index), self._pa_sink_info_cb, False)
+                    pa_operation_unref(o)
+
+            if et == PA_SUBSCRIPTION_EVENT_SOURCE:
+                if event_type & PA_SUBSCRIPTION_EVENT_TYPE_MASK == PA_SUBSCRIPTION_EVENT_REMOVE:
+                    log.debug("PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE : %d" % index)
+                    gobject.idle_add(self.pa_source_remove_cb, int(index))
+                else:
+                    o = pa_context_get_source_info_by_index(self._context, int(index), self._pa_source_info_cb, False)
+                    pa_operation_unref(o)
+
+
+        except Exception, text:
+            log.exception("PA ERROR pa_context_subscribe_cb %s" % text)
+    
+    def __pa_client_info_cb(self, context, struct, c_int, user_data):
+        if struct:
+            client = Client(self, struct.contents)
+            log.debug("__pa_client_info_cb : %s" % struct.contents.name)
+            gobject.idle_add(self.pa_client_info_cb, client, user_data)
+   
+    def __pa_sink_input_info_cb(self, context, struct, index, user_data):
+        if struct:
+            sink_input = SinkInput(self, struct.contents)
+            log.debug("__pa_sink_input_info_cb : %s" % struct.contents.name)
+            gobject.idle_add( self.pa_sink_input_info_cb, sink_input, user_data)
+
+    def __pa_sink_info_cb(self, context, struct, index, user_data):
+        if struct:
+            sink = Sink(self, struct.contents)
+            log.debug("__pa_sink_info_cb : %s" % struct.contents.name)
+            gobject.idle_add( self.pa_sink_info_cb, sink, user_data)
+
+    # EVENTS
+    def pa_client_info_cb(self, client, user_data):
+        return
+    def pa_client_remove_cb(self, index):
+        return
+
+    def pa_sink_input_info_cb(self, sink_input, user_data):
+        return
+    def pa_sink_input_remove_cb(self, index):
+        return
+
+    def pa_sink_info_cb(self, sink, user_data):
+        return
+    def pa_sink_remove_cb(self, index):
+        return
+
+    def pa_source_info_cb(self, contents, eol, user_data):
+        return
+    def pa_source_remove_cb(self, index):
+        return
+
+    def pa_module_info_cb(self, contents, eol, user_data):
+        return
+    def pa_source_output_info_cb(self, contents, index, user_data):
+        return
+    def pa_stream_request_cb(self, meter, index):
+        return
+    def pa_context_success_cb(self, c_int, user_data):
+        return
+    def pa_context_index_cb(self, index, user_data):
+        return
+
+    def pa_status_ready(self):
+        return
+
+
+    def pa_buffer_attr(self):
+        return pa_buffer_attr()
+    def pa_sample_spec(self):
+        return pa_sample_spec()
+    def pa_stream_new(self, name, sample_spec):
+        return pa_stream_new(self._context, name, sample_spec, None)
+    def pa_stream_set_monitor_stream(self, pa_stream, index):
+        return pa_stream_set_monitor_stream(pa_stream, index)
+    def pa_stream_set_read_callback(self, pa_stream, index):
+        return pa_stream_set_read_callback(pa_stream, self._pa_stream_request_cb, index)
+    def pa_stream_set_suspended_callback(self, pa_stream):
+        return pa_stream_set_suspended_callback(pa_stream, self._pa_stream_notify_cb, "DOOM")
+    def pa_stream_connect_record(self, pa_stream, monitor_index, attr, size):
+        return pa_stream_connect_record(pa_stream, monitor_index, attr, size) 
+    def pa_stream_peek(self, stream, length):
+        data = POINTER(c_float)()
+        pa_stream_peek(stream, data, ctypes.c_ulong(length))
+        return data
+    def pa_stream_drop(self, stream):
+        pa_stream_drop(stream)
+
+
+    # METHODS
+    def pa_context_get_sink_info_list(self):
+        o = pa_context_get_sink_info_list(self._context, self._pa_sink_info_cb, True)
+        pa_operation_unref(o)
+
+    def pa_context_get_source_output_info_list(self):
+        o = pa_context_get_source_output_info_list(self._context, self._pa_source_output_info_cb, True)
+        pa_operation_unref(o)
+
+    def pa_context_get_source_info_list(self):
+        o = pa_context_get_source_info_list(self._context, self._pa_source_info_cb, True)
+        pa_operation_unref(o)
+
+    def pa_proplist_gets(self, proplist, name):
+        return pa_proplist_gets(proplist, name)
+
+    def convert_simple_volume_to_pa_volume(self, volume):
+        vol = pa_cvolume()
+        vol.channels = len(volume)
+        v = pa_volume_t * 32
+        vol.values = v()
+        for i in range(0, len(volume)):
+            if i == 32: return vol
+            channel_volume = volume[i]
+            if channel_volume < 0: channel_volume = 0
+            if channel_volume > 100: channel_volume = 100
+            vol.values[i] = int(channel_volume * PA_VOLUME_CONVERSION_FACTOR)
+        return vol
+
+    def convert_pa_volume_to_simple_volume(self, volume):
+        vol = []
+        for i in range(0, volume.channels):
+            vol.append( int(volume.values[i] / PA_VOLUME_CONVERSION_FACTOR) )
+        return vol
+
+    def pa_context_move_sink_input_by_index(self, sink_input_index, sink_index):
+        pa_context_move_sink_input_by_index(self._context, sink_input_index, sink_index, self._pa_context_success_cb, None)
+
+    def pa_context_move_sink_input_by_name(self, sink_index, output_name):
+        pa_context_move_sink_input_by_name(self._context, sink_index, output_name, self._pa_context_success_cb, None)
+
+    def pa_context_set_sink_volume_by_name(self, sink_name, volume):       
+        vol = self.convert_simple_volume_to_pa_volume(volume)
+        o = pa_context_set_sink_volume_by_name(self._context, sink_name, vol, self._null_cb, None)
+        pa_operation_unref(o)
+
+    def pa_context_set_sink_input_volume(self, index, volume):
+        log.debug("pa_context_set_sink_input_volume : %s" % index)
+        vol = self.convert_simple_volume_to_pa_volume(volume)
+        o = pa_context_set_sink_input_volume(self._context, index, vol, self._null_cb, None) # NOTE: dont pass in any thing here causes a seg fault
+        pa_operation_unref(o)
+
+    def pa_context_get_sink_info_by_name(self, sink_name):
+        o = pa_context_get_sink_info_by_name(self._context, sink_name, self._pa_sink_info_cb, False)
+        pa_operation_unref(o)
+
+    def pa_context_get_source_info_by_name(self, source_name):
+        o = pa_context_get_source_info_by_name(self._context, source_name, self._pa_source_info_cb, False)
+        pa_operation_unref(o)
+
+    def pa_context_set_default_sink(self, sink_name):
+        o = pa_context_set_default_sink(self._context, sink_name, self._pa_context_success_cb, False)
+        pa_operation_unref(o)
+
+    def pa_context_move_source_output_by_index(self, source_output_index, source_index):
+        pa_context_move_source_output_by_index(self._context, source_output_index, source_index, self._pa_context_success_cb, None)
+
+    def pa_context_set_default_source(self, source_name):
+        o = pa_context_set_default_source(self._context, source_name, self._pa_context_success_cb, False)
+        pa_operation_unref(o)
+
+if __name__ == '__main__':
+
+    log.basicConfig(level=log.CRITICAL)
+    import gtk
+    # Turn on gtk threading
+    gtk.gdk.threads_init()
+
+    pac = PulseAudioConnection("Test")
+    pac.connect()
+
+    gtk.main()
+    
diff --git a/earcandy/pulseaudio/Sink.py b/earcandy/pulseaudio/Sink.py
new file mode 100644
index 0000000..61969e1
--- /dev/null
+++ b/earcandy/pulseaudio/Sink.py
@@ -0,0 +1,11 @@
+
+class Sink():
+    def __init__(self, pa, contents):
+        
+        self.name = str(contents.name)
+        self.index = int(contents.index)
+        self.volume = pa.convert_pa_volume_to_simple_volume(contents.volume)
+        self.description = pa.pa_proplist_gets(contents.proplist, "device.description")
+
+        self.monitor_source = int(contents.monitor_source)
+        self.monitor_source_name = str(contents.monitor_source_name)
diff --git a/earcandy/pulseaudio/SinkInput.py b/earcandy/pulseaudio/SinkInput.py
new file mode 100644
index 0000000..2198781
--- /dev/null
+++ b/earcandy/pulseaudio/SinkInput.py
@@ -0,0 +1,16 @@
+
+class SinkInput():
+    def __init__(self, pa, contents):
+        
+        self.name = str(contents.name)
+        self.index = int(contents.index)
+        self.volume = pa.convert_pa_volume_to_simple_volume(contents.volume)
+
+        self.client = int(contents.client)
+        self.sink = int(contents.sink)
+
+        self.media_role = pa.pa_proplist_gets(contents.proplist, "media.role") or None
+        self.media_name = pa.pa_proplist_gets(contents.proplist, "media.name") or None
+        self.media_title = pa.pa_proplist_gets(contents.proplist, "media.title") or None
+        self.media_artist = pa.pa_proplist_gets(contents.proplist, "media.artist") or None
+        self.media_icon_name = pa.pa_proplist_gets(contents.proplist, "media.icon_name") or None
diff --git a/earcandy/pulseaudio/Source.py b/earcandy/pulseaudio/Source.py
new file mode 100644
index 0000000..4bbb1f2
--- /dev/null
+++ b/earcandy/pulseaudio/Source.py
@@ -0,0 +1,9 @@
+
+class Source():
+    def __init__(self, pa, contents):
+        
+        self.name = str(contents.name)
+        self.index = int(contents.index)
+        #self.volume = pa.convert_pa_volume_to_simple_volume(contents.volume)
+        #self.description = pa.pa_proplist_gets(contents.proplist, "device.description")
+
diff --git a/earcandy/pulseaudio/SourceOutput.py b/earcandy/pulseaudio/SourceOutput.py
new file mode 100644
index 0000000..47126fe
--- /dev/null
+++ b/earcandy/pulseaudio/SourceOutput.py
@@ -0,0 +1,12 @@
+
+class SourceOutput():
+    def __init__(self, pa, contents):
+        
+        self.name = str(contents.name)
+        self.index = int(contents.index)
+        #self.volume = pa.convert_pa_volume_to_simple_volume(contents.volume)
+
+        self.client = int(contents.client)
+        self.source = int(contents.source)
+
+
diff --git a/ear_candy/pulseaudio/__init__.py b/earcandy/pulseaudio/__init__.py
similarity index 100%
copy from ear_candy/pulseaudio/__init__.py
copy to earcandy/pulseaudio/__init__.py
diff --git a/ear_candy/pulseaudio/lib.py b/earcandy/pulseaudio/lib.py
similarity index 95%
rename from ear_candy/pulseaudio/lib.py
rename to earcandy/pulseaudio/lib.py
index 37eb3f8..35b020d 100644
--- a/ear_candy/pulseaudio/lib.py
+++ b/earcandy/pulseaudio/lib.py
@@ -32,13 +32,15 @@
 import os
 import re
 import sys
-
+import logging
 import ctypes
 import ctypes.util
 
 _debug_lib = False
 _debug_trace = False
 
+logging.basicConfig(level=logging.DEBUG)
+
 class LibraryLoader(object):
     def load_library(self, *names, **kwargs):
         '''Find and load a library.  
@@ -64,10 +66,7 @@ class LibraryLoader(object):
         for name in platform_names:
             try:
                 lib = ctypes.cdll.LoadLibrary(name)
-                #if _debug_lib:
-                print "Loaded library :", name
-                #if _debug_trace:
-                #    lib = _TraceLibrary(lib)
+                logging.debug("lib :: Loaded library : %s" % name)
                 return lib
             except OSError:
                 path = self.find_library(name)
diff --git a/ear_candy/pulseaudio/lib_pulseaudio.py b/earcandy/pulseaudio/lib_pulseaudio.py
similarity index 99%
rename from ear_candy/pulseaudio/lib_pulseaudio.py
rename to earcandy/pulseaudio/lib_pulseaudio.py
index 6824457..bdf9e9d 100644
--- a/ear_candy/pulseaudio/lib_pulseaudio.py
+++ b/earcandy/pulseaudio/lib_pulseaudio.py
@@ -1928,7 +1928,7 @@ pa_proplist_to_string.argtypes = [
 
 pa_ext_stream_restore_delete = _lib.pa_ext_stream_restore_delete
 pa_ext_stream_restore_delete.restype = pa_operation
-pa_ext_stream_restore_delete.argtypes = [POINTER(pa_context), c_char_p, pa_context_success_cb_t, POINTER(None)]
+pa_ext_stream_restore_delete.argtypes = [POINTER(pa_context), POINTER(c_char_p), pa_context_success_cb_t, POINTER(None)]
 
 
 
diff --git a/earcandy/ui/Client.py b/earcandy/ui/Client.py
new file mode 100644
index 0000000..5f72dba
--- /dev/null
+++ b/earcandy/ui/Client.py
@@ -0,0 +1,85 @@
+
+import sys
+import gtk
+import time
+import gobject
+import os
+from earcandy.earcandyconfig import getdatapath
+from earcandy.util.Threads import threaded
+
+class Client:
+        def __init__(self, client, core):
+            self.core = core
+            self.client = client
+
+            glade = os.path.join(getdatapath(), 'ui', 'EarCandy.ui')
+
+            # Get glade file XML
+            f = open( glade ,"r")
+            xml =  f.read()
+            f.close()
+        
+            # Remember you will need to recreate tree everytime the window loads
+            #wtree = gtk.glade.xml_new_from_buffer(xml, len(xml), "table_clients")
+
+            wtree = gtk.Builder()
+            wtree.add_from_string(xml)
+
+            self.gtk = wtree.get_object("table_clients") 
+            self.gtk_progressbar_meter = wtree.get_object("progressbar_meter")   
+            self.gtk_label = wtree.get_object("label_client_name")  
+            self.gtk_icon = wtree.get_object("image_client_icon") 
+            self.gtk_role_combobox = wtree.get_object("combobox_role") 
+            self.button_client = wtree.get_object("button_client") 
+
+            store = gtk.ListStore(gobject.TYPE_STRING)
+            self.gtk_role_combobox.set_model(store)
+            cell = gtk.CellRendererText()
+            self.gtk_role_combobox.pack_start(cell, True)
+            self.gtk_role_combobox.add_attribute(cell, 'text', 0)
+
+            for key in self.core.display.keys():
+                self.gtk_role_combobox.append_text(self.core.display[key])
+            self.gtk_role_combobox.set_active(0)
+
+            #client.gtk_role_combobox.set_model(self.cb_model)
+            self.gtk_role_combobox.connect("changed", self.on_role_changed)
+            self.button_client.connect("clicked", self.on_clicked)
+
+            style = gtk.Style()
+            self.gtk_label.modify_fg(gtk.STATE_NORMAL, style.fg[gtk.STATE_NORMAL])
+
+            self.__hide_timer = False
+
+            self.gtk.unparent()
+
+        def on_clicked(self, widget):
+            self.core.pref.open_client_properties(self.client)
+
+
+        def on_role_changed(self, widget):
+            #self.client.role = ""
+            for key in self.core.display.keys():
+                if self.core.display[key] == widget.get_active_text():
+                    self.client.role = key
+            self.core.save()
+            return
+
+        def set_status(self, status):
+            if not status:
+                if not self.__hide_timer:
+                    self.__hide_timer = True
+                    self.remove()
+            else:
+                self.__hide_timer = False
+
+        @threaded
+        def remove(self):
+            count = 1
+            while count < 60:
+                if not self.__hide_timer: return
+                time.sleep(1)
+                count = count + 1
+            if not self.client.is_active():
+                self.gtk.hide()
+
diff --git a/ear_candy/EarCandyAppSelect.py b/earcandy/ui/EarCandyAppSelect.py
similarity index 95%
rename from ear_candy/EarCandyAppSelect.py
rename to earcandy/ui/EarCandyAppSelect.py
index 4a0b4eb..3dee445 100644
--- a/ear_candy/EarCandyAppSelect.py
+++ b/earcandy/ui/EarCandyAppSelect.py
@@ -15,22 +15,13 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import sys
-try:
-     import pygtk
-     pygtk.require("2.0")
-except:
-      pass
-try:
-    import gtk
-    import gtk.glade
-except:
-    sys.exit(1)
+
+import gtk
 
 import re
 import copy
 
 from glade_window import GladeWindow
-from window.WindowWatcher import WindowWatcher
 from Client import Client
 
 class EarCandyAppSelect(GladeWindow):
diff --git a/ear_candy/EarCandyClientProperties.py b/earcandy/ui/EarCandyClientProperties.py
similarity index 56%
rename from ear_candy/EarCandyClientProperties.py
rename to earcandy/ui/EarCandyClientProperties.py
index b7965ec..150daad 100644
--- a/ear_candy/EarCandyClientProperties.py
+++ b/earcandy/ui/EarCandyClientProperties.py
@@ -15,23 +15,11 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import sys
-try:
-     import pygtk
-     pygtk.require("2.0")
-except:
-      pass
-try:
-    import gtk
-    import gtk.glade
-except:
-    sys.exit(1)
-
+import gobject
+import gtk
 import re
 import copy
-
 from glade_window import GladeWindow
-from window.WindowWatcher import WindowWatcher
-
 from Client import Client
 
 class EarCandyClientProperties(GladeWindow):
@@ -41,60 +29,67 @@ class EarCandyClientProperties(GladeWindow):
         
         GladeWindow.__init__(self, "pulseoptions.glade", "window_client_properties", parent)
         
-        self.entry_description = self.wtree.get_widget("entry_description")
-        self.image_icon = self.wtree.get_widget("image_icon")
-        self.hscale_max_volume = self.wtree.get_widget("hscale_max_volume")
-        self.entry_window_title = self.wtree.get_widget("entry_window_title")
-        self.entry_command = self.wtree.get_widget("entry_command")
-        self.entry_application = self.wtree.get_widget("entry_application")
-        self.label_name = self.wtree.get_widget("label_name")
-        self.entry_name = self.wtree.get_widget("entry_name")
-        self.combobox_category = self.wtree.get_widget("combobox_category")
-        self.checkbutton_fix = self.wtree.get_widget("checkbutton_fix")
-        self.checkbutton_fade_volume = self.wtree.get_widget("checkbutton_fade_volume")
-        self.combobox_output = self.wtree.get_widget("combobox_output")
+        self.entry_description = self.wtree.get_object("entry_description")
+        self.image_icon = self.wtree.get_object("image_icon")
+        self.hscale_max_volume = self.wtree.get_object("hscale_max_volume")
+        self.hscale_min_volume = self.wtree.get_object("hscale_min_volume")
+        self.entry_window_title = self.wtree.get_object("entry_window_title")
+        self.entry_command = self.wtree.get_object("entry_command")
+        self.entry_application = self.wtree.get_object("entry_application")
+        self.label_name = self.wtree.get_object("label_name")
+        self.entry_name = self.wtree.get_object("entry_name")
+        self.combobox_category = self.wtree.get_object("combobox_category")
+        self.checkbutton_fix = self.wtree.get_object("checkbutton_fix")
+        self.checkbutton_fade_volume = self.wtree.get_object("checkbutton_fade_volume")
+        self.combobox_output = self.wtree.get_object("combobox_output")
 
         signals = {
             "on_button_close_clicked" : self.on_button_close_clicked,
             "on_button_apply_clicked" : self.on_button_apply_clicked
         }
-        self.wtree.signal_autoconnect(signals)
+        self.wtree.connect_signals(signals)
 
         self.entry_description.set_text(client.description)
 
         self.window.set_title("Ear Candy : " + client.description)
-
-        self.image_icon.set_from_pixbuf(client.icon)
-        
-        self.hscale_max_volume.set_value( client.volume_default )
-
+        self.image_icon.set_from_pixbuf(client.icon)      
+        self.hscale_max_volume.set_value( client.volume_max )
+        self.hscale_min_volume.set_value( client.volume_min )
         self.entry_window_title.set_text( client.rule_re_window_title.pattern )
-        
         self.entry_command.set_text( client.rule_re_command.pattern )
-
         self.entry_application.set_text( client.rule_re_application.pattern )
-
         self.label_name.set_markup("<b><big>" + client.description + "</big></b>")
-
         self.entry_name.set_text(client.name)
-
         self.checkbutton_fix.set_active( client.apply_volume_meter_hack )
-
         self.checkbutton_fade_volume.set_active( client.fade_volume )
 
+
+        #store = gtk.ListStore(gobject.TYPE_STRING)
+        #self.combobox_output.set_model(store)
+        #cell = gtk.CellRendererText()
+        #self.combobox_output.pack_start(cell, True)
+        #self.combobox_output.add_attribute(cell, 'text', 0)
+
+        store = gtk.ListStore(gobject.TYPE_STRING)
+        self.combobox_category.set_model(store)
+        cell = gtk.CellRendererText()
+        self.combobox_category.pack_start(cell, True)
+        self.combobox_category.add_attribute(cell, 'text', 0)
+
+        
         count = 0
         for value in self.core.display.values():
             self.combobox_category.append_text(value)
-            if self.core.display[client.category] == value: self.combobox_category.set_active(count)
+            if self.core.display[client.role] == value: self.combobox_category.set_active(count)
             count = count + 1
         
         self.client = client
 
         count = 1
         self.combobox_output.set_active(0 )
-        for key in self.core.pa_output_descriptions.keys():
-            self.combobox_output.append_text(self.core.pa_output_descriptions[key])
-            if self.core.pa_outputs[key] == client.output: 
+        for key in self.core.pa.sinks.keys():
+            self.combobox_output.append_text(self.core.pa.sinks[key].description)
+            if self.core.pa.sinks[key].name == client.prefer_sink: 
                 self.combobox_output.set_active(count)
             count = count + 1
 
@@ -103,7 +98,8 @@ class EarCandyClientProperties(GladeWindow):
 
     def on_button_apply_clicked(self, widget):
         self.client.description = self.entry_description.get_text()
-        self.client.volume_default = int(self.hscale_max_volume.get_value())
+        self.client.volume_max = int(self.hscale_max_volume.get_value())
+        self.client.volume_min = int(self.hscale_min_volume.get_value())
         self.client.rule_re_window_title = re.compile( self.entry_window_title.get_text() )
         self.client.rule_re_command = re.compile( self.entry_command.get_text() )
         self.client.rule_re_application = re.compile( self.entry_application.get_text() )
@@ -111,14 +107,16 @@ class EarCandyClientProperties(GladeWindow):
         self.client.apply_volume_meter_hack = self.checkbutton_fix.get_active()
         self.client.fade_volume = self.checkbutton_fade_volume.get_active()
 
-        self.client.output = ""
-        for key in self.core.pa_output_descriptions.keys():
-            if self.core.pa_output_descriptions[key] == self.combobox_output.get_active_text():
-                self.client.output = self.core.pa_outputs[key]
+        output = ""
+        self.client.prefer_sink = ""
+        for key in self.core.pa.sinks.keys():
+            if self.core.pa.sinks[key].description == self.combobox_output.get_active_text():
+                self.client.prefer_sink = self.core.pa.sinks[key].name
+        self.core.pa.move_client_to_sink(self.client)
 
-        self.client.category = ""
+        self.client.role = ""
         for key in self.core.display.keys():
             if self.core.display[key] == self.combobox_category.get_active_text():
-                self.client.category = key
+                self.client.role = key
 
         self.stop()
diff --git a/ear_candy/EarCandyPrefs.py b/earcandy/ui/EarCandyPrefs.py
similarity index 56%
rename from ear_candy/EarCandyPrefs.py
rename to earcandy/ui/EarCandyPrefs.py
index 853f987..9b89867 100644
--- a/ear_candy/EarCandyPrefs.py
+++ b/earcandy/ui/EarCandyPrefs.py
@@ -19,24 +19,15 @@ import sys
 import gobject
 import re
 import time
-try:
-     import pygtk
-     pygtk.require("2.0")
-except:
-      pass
-try:
-    import gtk
-    import gtk.glade
-except:
-    sys.exit(1)
-
-
-from Threads import threaded
+import gtk
+
+from earcandy.util.Threads import threaded
 from glade_window import GladeWindow
 from EarCandyAppSelect import EarCandyAppSelect
 from EarCandyClientProperties import EarCandyClientProperties
+from Client import Client
 
-class EarCandayPref(GladeWindow):
+class EarCandayPrefs(GladeWindow):
     def __init__(self, core):
         
         self.core = core
@@ -45,29 +36,35 @@ class EarCandayPref(GladeWindow):
 
         GladeWindow.__init__(self, "pulseoptions.glade", "dialog_options")
 
-        self.treeview_pulse_clients = self.wtree.get_widget("treeview_pulse_clients")
-        self.label_pulse_client = self.wtree.get_widget("label_pulse_client")
-        self.entry_pulse_client_description = self.wtree.get_widget("entry_pulse_client_description")
-        self.comboboxentry_window_match = self.wtree.get_widget("comboboxentry_window_match")
-        self.radiobutton_do_not_mute_others = self.wtree.get_widget("radiobutton_do_not_mute_others")
-        self.radiobutton_mute_others_onfocus = self.wtree.get_widget("radiobutton_mute_others_onfocus")
-        self.radiobutton_mute_others_focus = self.wtree.get_widget("radiobutton_mute_others_focus")
-        self.checkbutton_mute_onblur = self.wtree.get_widget("checkbutton_mute_onblur")
-        self.checkbutton_do_not_mute = self.wtree.get_widget("checkbutton_do_not_mute")
-        self.vscale_volume = self.wtree.get_widget("vscale_volume")
-        self.vscale_mute = self.wtree.get_widget("vscale_mute")
-        self.combobox_profile = self.wtree.get_widget("combobox_profile")
-        self.entry_application = self.wtree.get_widget("entry_application")
-        self.entry_command = self.wtree.get_widget("entry_command")
-        self.entry_window_title = self.wtree.get_widget("entry_window_title")
-        self.checkbutton_channel_window = self.wtree.get_widget("checkbutton_channel_window")
-        self.label_client = self.wtree.get_widget("label_client")
-        self.hscale_fade = self.wtree.get_widget("hscale_fade")
-        self.hscale_mute_level = self.wtree.get_widget("hscale_mute_level")
-        self.checkbutton_tray = self.wtree.get_widget("checkbutton_tray")
-        self.checkbutton_output = self.wtree.get_widget("checkbutton_output")
-        self.combobox_view = self.wtree.get_widget("combobox_view")
-        self.checkbutton_autostart = self.wtree.get_widget("checkbutton_autostart")
+        self.treeview_pulse_clients = self.wtree.get_object("treeview_pulse_clients")
+        self.label_pulse_client = self.wtree.get_object("label_pulse_client")
+        self.entry_pulse_client_description = self.wtree.get_object("entry_pulse_client_description")
+        self.comboboxentry_window_match = self.wtree.get_object("comboboxentry_window_match")
+        self.radiobutton_do_not_mute_others = self.wtree.get_object("radiobutton_do_not_mute_others")
+        self.radiobutton_mute_others_onfocus = self.wtree.get_object("radiobutton_mute_others_onfocus")
+        self.radiobutton_mute_others_focus = self.wtree.get_object("radiobutton_mute_others_focus")
+        self.checkbutton_mute_onblur = self.wtree.get_object("checkbutton_mute_onblur")
+        self.checkbutton_do_not_mute = self.wtree.get_object("checkbutton_do_not_mute")
+        self.vscale_volume = self.wtree.get_object("vscale_volume")
+        self.vscale_mute = self.wtree.get_object("vscale_mute")
+        self.combobox_profile = self.wtree.get_object("combobox_profile")
+        self.entry_application = self.wtree.get_object("entry_application")
+        self.entry_command = self.wtree.get_object("entry_command")
+        self.entry_window_title = self.wtree.get_object("entry_window_title")
+        self.checkbutton_channel_window = self.wtree.get_object("checkbutton_channel_window")
+        self.label_client = self.wtree.get_object("label_client")
+        self.hscale_fade = self.wtree.get_object("hscale_fade")
+        self.hscale_mute_level = self.wtree.get_object("hscale_mute_level")
+        self.checkbutton_tray = self.wtree.get_object("checkbutton_tray")
+        self.checkbutton_output = self.wtree.get_object("checkbutton_output")
+        self.combobox_view = self.wtree.get_object("combobox_view")
+        self.checkbutton_autostart = self.wtree.get_object("checkbutton_autostart")
+        self.treeview_plugins = self.wtree.get_object("treeview_plugins")
+        self.checkbutton_mute_phone = self.wtree.get_object("checkbutton_mute_phone")
+        self.combobox_sink = self.wtree.get_object("combobox_sink")
+        self.vbox_repeater = self.wtree.get_object("vbox_repeater")
+        self.table_clients = self.wtree.get_object("table_clients")
+        self.treeview_outputs = self.wtree.get_object("treeview_outputs")
 
         signals = {
             "on_entry_pulse_client_description_changed" : self.on_entry_pulse_client_description_changed,
@@ -75,8 +72,8 @@ class EarCandayPref(GladeWindow):
             "on_entry_application_changed" : self.on_entry_application_changed,
             "on_entry_command_changed" : self.on_entry_command_changed,
             "on_entry_window_title_changed" : self.on_entry_window_title_changed,
-            "on_vscale_volume_change_value" : self.on_vscale_volume_change_value,
-            "on_vscale_mute_change_value" : self.on_vscale_mute_change_value,
+            #"on_vscale_volume_change_value" : self.on_vscale_volume_change_value,
+            #"on_vscale_mute_change_value" : self.on_vscale_mute_change_value,
             "on_checkbutton_channel_window_toggled" : self.on_checkbutton_channel_window_toggled,
 
             "on_treeview_pulse_clients_cursor_changed" : self.on_treeview_pulse_clients_cursor_changed,
@@ -99,81 +96,90 @@ class EarCandayPref(GladeWindow):
 
             "on_button_reset_clicked" : self.on_button_reset_clicked,
 
-            "on_checkbutton_autostart_toggled" : self.on_checkbutton_autostart_toggled
-        }
-        self.wtree.signal_autoconnect(signals)
-                
-        # Setup tree
+            "on_checkbutton_autostart_toggled" : self.on_checkbutton_autostart_toggled,
 
+            "on_checkbutton_mute_phone_toggled" : self.on_checkbutton_mute_phone_toggled,
 
-        column = gtk.TreeViewColumn((''))
-        column.set_spacing(4)
-        cell = gtk.CellRendererPixbuf()
-        column.pack_start(cell, False)
-        column.set_attributes(cell, pixbuf=0)
-        self.treeview_pulse_clients.append_column(column)
+            "on_combobox_sink_changed" : self.on_combobox_sink_changed,
 
-        column_label = gtk.TreeViewColumn(('Application'))
-        cell = gtk.CellRendererText()
-        column_label.pack_start(cell, True)
-        column_label.set_attributes(cell, markup=1)
-        self.treeview_pulse_clients.append_column(column_label)
-        column_label.set_expand(True)
+            "on_treeview_outputs_drag_drop" : self.on_treeview_outputs_drag_drop
+        }
+        self.wtree.connect_signals(signals)
 
         #list store for cell renderer
         self.cb_model = gtk.ListStore(str)
         for key in self.core.display.keys():
             self.cb_model.append([self.core.display[key]])
 
-        cell = gtk.CellRendererCombo()
-        cell.connect('edited', self.on_client_category_changed)
-        column = gtk.TreeViewColumn('Category', cell)
-        cell.set_property("model",self.cb_model)
-        cell.set_property('text-column', 0)
-        cell.set_property('editable', True)
-        cell.connect('editing-started', self.cell_edit_start)
-        column.set_attributes(cell, text = 2)
-        column.set_expand(False)
-        self.treeview_pulse_clients.append_column(column)
-
-        """column = gtk.TreeViewColumn('Fix')
-        cell = gtk.CellRendererToggle()
-        cell.set_property('activatable', True)
-        cell.connect( 'toggled', self.on_client_fade_toggled)
-        column.pack_start(cell, False)
-        column.set_attributes(cell, active=3)
-        self.treeview_pulse_clients.append_column(column)"""
-
         self.last_popup = None
         self.last_path = None
         #self.update_treeview()
-        self.combobox_view.set_active(0)
 
         self.checkbutton_autostart.set_active( self.core.is_auto_start() )
+        self.checkbutton_mute_phone.set_active( self.core.mute_phone )
+
+
+        column_label = gtk.TreeViewColumn(('Source'))
+        cell = gtk.CellRendererText()
+        column_label.pack_start(cell, True)
+        column_label.set_attributes(cell, markup=1)
+        self.treeview_outputs.append_column(column_label)
+        column_label.set_expand(True)
+        self.store = gtk.ListStore(str, str, object)
+        self.treeview_outputs.set_model(self.store) 
+
+        self.clients = {}
+
+        # fix color : Fix this for dark themes....
+        style = gtk.Style()
+        viewport1 = self.wtree.get_object("viewport1")
+        viewport1.modify_bg(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
+
+
+    def on_treeview_outputs_drag_drop(self, widget, b):
+        self.update_ouput_order()
+
+
+    def on_combobox_sink_changed(self, widget):
+        
+        return
+
+    def on_treeview_plugins_toggled(self, cell, path):
+        model =  self.treeview_plugins.get_model()
+        iter = model.get_iter((int(path),))
+        if not iter: return
+
+        model[path][0] = not model[path][0]
+        model[path][2].enabled = model[path][0]
+
+    def on_checkbutton_mute_phone_toggled(self, widget):
+        self.core.mute_phone = not self.core.mute_phone
 
     def on_checkbutton_autostart_toggled(self, widget):
         self.core.set_auto_start( widget.get_active() )
 
     def on_button_reset_clicked(self, widget):
 
-        md = gtk.MessageDialog(self.window, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_YES_NO, message_format="Are you sure you want to reset all volume levels?") 
+        md = gtk.MessageDialog(self.window, flags=0, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_YES_NO, message_format="Are you sure you want to reset all your settings?") 
         result = md.run()
         md.destroy()
         if result == gtk.RESPONSE_YES:
-            self.core.reset_all_volumes(False)
+            self.window.destroy()
+            self.core.reset_all()
+            self.core.pref = None
+            self.core.open_preferances()
 
     def cell_edit_start(self,  editable, control, path):
         self.last_popup = control
         self.last_path = path
 
     def on_combobox_view_changed(self, widget):
-        self.update_treeview()
+        return
+        #self.update_treeview()
 
 
     def on_checkbutton_tray_toggled(self, widget):
-        self.core.tray.set_visible( widget.get_active() )
-        if widget.get_active():
-            self.core.move_all_sinks()
+        self.core.status_icon.set_visible( widget.get_active() )
 
     def on_checkbutton_output_toggled(self, widget):
         self.core.follow_new_outputs = widget.get_active()
@@ -195,14 +201,14 @@ class EarCandayPref(GladeWindow):
     def on_hscale_fade_value_changed(self, widget):
         self.core.fade_timer_speed = self.hscale_fade.get_value()
 
-    def on_client_category_changed(self, cell, row, newText):
+    def on_client_role_changed(self, cell, row, newText):
         model, iter =  self.treeview_pulse_clients.get_selection().get_selected()
         if not iter: return
         self.selected_client = model.get_value(iter, 5)
 
         for key in self.core.display.keys():
             if self.core.display[key] == newText:
-                self.selected_client.category = key
+                self.selected_client.role = key
                 self.store.set(iter,
                     2,  newText
                 )
@@ -216,7 +222,7 @@ class EarCandayPref(GladeWindow):
         result = md.run()
         md.destroy()
         if result == gtk.RESPONSE_YES:
-            self.selected_client.category = ""
+            self.selected_client.role = ""
             self.selected_client.iter = None
             self.store.remove(iter)
         self.core.save()
@@ -226,11 +232,14 @@ class EarCandayPref(GladeWindow):
         model, iter =  self.treeview_pulse_clients.get_selection().get_selected()
         if not iter: return
         self.selected_client = model.get_value(iter, 5)
-        print self.selected_client
         
-        eccp = EarCandyClientProperties(self.core, self.selected_client, self.window)
+
+
+
+    def open_client_properties(self, client):
+        eccp = EarCandyClientProperties(self.core, client, self.window)
         eccp.run()
-        self.update_client(self.selected_client)
+        self.update_client(client)
         self.core.save()
 
     def on_button_add_new_clicked(self, widget):
@@ -261,7 +270,7 @@ class EarCandayPref(GladeWindow):
     def on_combobox_profile_changed(self, widget):
         for key in self.vals.keys():
             if self.vals[key] == self.combobox_profile.get_active():
-                self.current_client.category = key
+                self.current_client.role = key
                 self.core.save()
 
     def on_vscale_volume_change_value(self, widget, a, b):
@@ -306,68 +315,120 @@ class EarCandayPref(GladeWindow):
 
     def __get_title(self, client):
         text = client.name
-        if client.description:
-            text = client.description
-        return text
+        if client.is_active() and client.get_status():
+            text = "<b>" + client.description + "</b>"
+            for sink in client.sinks.values():
+                text += "\n<small><i>" + sink.name + "</i></small>"
+            return text
+
+        elif not client.is_active():
+            return "<i>" + client.description + "</i>"
+
+        return client.description
 
     def update_treeview(self):
-        for client in self.core.pa_clients.values():
+        for client in self.core.pa.clients.values():
             client.iter = None
 
-        self.store = gtk.ListStore(gtk.gdk.Pixbuf, str, str, bool, int, object)
+        self.store = gtk.ListStore(gtk.gdk.Pixbuf, str, str, bool, int, object, str)
         self.treeview_pulse_clients.set_model(self.store) 
 
         icon_theme = gtk.icon_theme_get_default()
         icon = icon_theme.lookup_icon("audio-volume-medium", 32, 0).load_icon()
-        for client in self.core.pa_clients.values():
-            if client.category and not (client.name in self.core.ignore):
+        for client in self.core.pa.clients.values():
+            if not (client.name in self.core.ignore):
                 if client.is_active() or self.combobox_view.get_active() == 1:
-                    client.iter = self.store.append(([client.icon or icon, self.__get_title(client), self.core.display[client.category], client.apply_volume_meter_hack, 50, client])) 
+                    client.iter = self.store.append(([client.icon or icon, self.__get_title(client), self.core.display[client.role], client.apply_volume_meter_hack, 0, client, client.description])) 
 
-        self.store.set_sort_column_id(1, gtk.SORT_ASCENDING)  
+        self.store.set_sort_column_id(6, gtk.SORT_ASCENDING)  
 
     def update_client(self, client):
 
         # load saved icon
-        if not client.icon and client.icon_name:
-            print "load client icon", client.icon_name
-            icon_theme = gtk.icon_theme_get_default()
-            try:
-                client.icon = icon_theme.load_icon(client.icon_name, 32, 0)
-            except:
-                client.icon = icon_theme.load_icon("audio-volume-medium", 32, 0)
-
-        if client.iter:
-            if client.is_active():
-                self.store.set(client.iter,
-                    0, client.icon,
-                    1,  self.__get_title(client),
-                    4, client.get_volume()
-                )
-                if client.category in self.core.display.keys():
-                    self.store.set(client.iter,
-                        2,  self.core.display[client.category]
-                    )
-            else:
-                pass
-                # This needs to be on a timer
-                #self.store.remove(client.iter)
-                #client.iter = None
-        else:
-            if client.has_rule() and not client.name in self.core.ignore:
+        if not client.icon:
+            if client.icon_name:
+                print "load client icon", client.icon_name
                 icon_theme = gtk.icon_theme_get_default()
-                icon = icon_theme.lookup_icon("audio-volume-medium", 32, 0).load_icon()
-                client.iter = self.store.append(([client.icon or icon, self.__get_title(client), self.core.display[client.category], client.apply_volume_meter_hack, client.get_volume(), client])) 
+                try:
+                    client.icon = icon_theme.load_icon(client.icon_name, 32, 0)
+                except:
+                    client.icon = icon_theme.load_icon("audio-volume-medium", 32, 0)
+            elif client.application and client.application.icon_name:
+                icon_theme = gtk.icon_theme_get_default()
+                try:
+                    client.icon = icon_theme.load_icon(client.application.icon_name, 32, 0)
+                except:
+                    client.icon = icon_theme.load_icon("audio-volume-medium", 32, 0)
+        
+        if not self.clients.has_key(client.name):
+
+            client_gtk = Client(client, self.core)
+            self.clients[client.name] = client_gtk
+            self.vbox_repeater.pack_start(client_gtk.gtk, expand=False, fill=False, padding=0)
+            client_gtk.gtk.show()
+
+            count = 0 
+            for key in self.core.display.keys():
+                if key == client.role: 
+                    client_gtk.gtk_role_combobox.set_active(count)
+                    break
+                count = count + 1
+        
+        else:
+            client_gtk = self.clients[client.name]
+
+        client_gtk.set_status(client.is_active())
 
-        self.store.set_sort_column_id(1,gtk.SORT_ASCENDING)
+        client_gtk.gtk_progressbar_meter.set_fraction(client.get_volume() / 100)
+        client_gtk.gtk_label.set_markup(self.__get_title(client))
+        if client.icon: client_gtk.gtk_icon.set_from_pixbuf(client.icon)
+
+        for key in self.core.display.keys():
+            self.cb_model.append([self.core.display[key]])
+
+        client_gtk.gtk.show()
+
+    def update_output(self, output, priority):
+        itr = self.store.get_iter_first()
+        while itr:
+            o = self.store.get_value(itr, 2)
+            if o.name == output.name:
+                if not output.index:
+                    self.store.remove(itr)
+                    self.update_ouput_order()
+                return
+            itr = self.store.iter_next(itr)
+
+        self.store.insert(priority, ([output.name, output.description, output]))
+
+        self.update_ouput_order()
+
+    def update_ouput_order(self):
+
+        itr = self.store.get_iter_first()
+        count = 1
+        flagCurrent = False
+        while itr:
+            output = self.store.get_value(itr, 2)
+            output.priority = count
+            if count == 1:
+                self.store.set_value(itr, 1, "<b>" + output.description + "</b>")
+            else:
+                self.store.set_value(itr, 1, output.description)
+
+            itr = self.store.iter_next(itr)
+            
+            count = count + 1
+
+        self.core.pa.set_default_prefered_device()
 
     def run(self):
         self.window.show()  
         self.hscale_mute_level.set_value(self.core.mute_level)
         self.hscale_fade.set_value(self.core.fade_timer_speed)
-        self.checkbutton_tray.set_active( self.core.tray.get_visible() )
-        self.checkbutton_output.set_active( self.core.follow_new_outputs )
+        self.checkbutton_tray.set_active( self.core.status_icon.get_visible() )
         self.window.present()
+        self.core.pa.pa_context_get_sink_info_list()
         return self.return_value
 
     def stop(self):
@@ -375,18 +436,6 @@ class EarCandayPref(GladeWindow):
         self.core.close_preferances()
         self.window.destroy()
     
-    @threaded
-    def remove_client_on_timer(self, client):
-        #time.sleep(60)
-        #gobject.idle_add(self.remove_client, client) 
-        return
-    
-    def remove_client(self, client):
-        if not client.is_active() and client.iter :
-            self.store.remove(client.iter)
-            client.iter = None
-
-
 
 
 
diff --git a/ear_candy/TrayIcon.py b/earcandy/ui/TrayIcon.py
similarity index 66%
rename from ear_candy/TrayIcon.py
rename to earcandy/ui/TrayIcon.py
index 44a8868..5d89f36 100644
--- a/ear_candy/TrayIcon.py
+++ b/earcandy/ui/TrayIcon.py
@@ -19,16 +19,7 @@ import os
 import sys
 import gtk
 
-def find_program_file(path):
-    """Finds a program file, for example, a png included with the program.
-    First looks for it in files/ under the parent directory of the parent directory
-    of ear_candy.py
-    Then looks for it in /usr/share/earcandy
-    Returns the path of the file"""
-    if os.path.exists(os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "files",path)):
-        return os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "files",path)
-    else:
-        return os.path.join(sys.prefix, "share/earcandy", path)
+from earcandy.earcandyconfig import getdatapath
 
 class EarCandyStatusIcon(gtk.StatusIcon):
     def __init__(self, core):
@@ -39,6 +30,7 @@ class EarCandyStatusIcon(gtk.StatusIcon):
             <ui>
              <menubar name="Menubar">
               <menu action="Menu">
+               <separator/>
                <menuitem action="Preferences"/>
                <menuitem action="About"/>
                <separator/>
@@ -49,7 +41,10 @@ class EarCandyStatusIcon(gtk.StatusIcon):
         '''
         actions = [
             ('Menu',  None, 'Menu'),
+            ('Mute', "stock_volume-mute", '_Mute...', None, 'Mute sound', self.on_preferences),
+            ('Lock', "stock_lock", '_Lock...', None, 'Lock volume levels', self.on_preferences),
             ('Preferences', gtk.STOCK_PREFERENCES, '_Preferences...', None, 'Edit Preferences', self.on_preferences),
+
             ('About', gtk.STOCK_ABOUT, '_About...', None, 'About Ear Candy', self.on_about),
             ('Exit', gtk.STOCK_STOP, '_Exit...', None, 'Exit Ear Candy', self.stop)]
         ag = gtk.ActionGroup('Actions')
@@ -65,38 +60,31 @@ class EarCandyStatusIcon(gtk.StatusIcon):
         self.connect('activate', self.on_activate)
         self.connect('popup-menu', self.on_popup_menu)
 
+
     def on_activate(self, data):
 
-        if not self.core.slider.active:
-            self.core.slider.window.set_position(gtk.WIN_POS_MOUSE)
-            screen, rect, orin = self.get_geometry()           
-            x, y = self.core.slider.window.get_position()
-            if rect.y - rect.height/2 > y - rect.height:
-                self.core.slider.window.move(x, y + rect.height)
-            self.core.slider.run()
+        self.core.open_preferances()
             
     def on_preferences(self, widget):
         self.core.open_preferances()
 
-    def on_popup_menu(self, status, button, time):
+    def on_reset(self, widget):
+        self.core.reset()
+
+    def on_popup_menu(self, status, button=None, time=0):
         self.menu.popup(None, None, None, button, time)
 
     def on_about(self, data):
         dialog = gtk.AboutDialog()
         dialog.set_name('Ear Candy')
         dialog.set_version(str(self.core.version))
-        dialog.set_comments('An pulseaudio / X.org sound manager')
-        dialog.set_website('http://edge.launchpad.net/eyecandy')
+        dialog.set_comments('An pulseaudio / X.org sound volume manager')
+        dialog.set_website('https://launchpad.net/earcandy')
         dialog.run()
         dialog.destroy()
     
-
     def set_icon(self):
-        if self.core.active:
-            icon_path = find_program_file("earsLabel.png")
-        else:
-            icon_path = find_program_file("earsLabelOff.png")
-        self.set_from_file( icon_path )
+        self.set_from_file( os.path.join(getdatapath(), 'media', 'icon.png') )
 
     def stop(self, data=None):
         self.core.exit()
diff --git a/ear_candy/VolumeSlider.py b/earcandy/ui/VolumeSlider.py
similarity index 84%
rename from ear_candy/VolumeSlider.py
rename to earcandy/ui/VolumeSlider.py
index 05df249..8579a82 100644
--- a/ear_candy/VolumeSlider.py
+++ b/earcandy/ui/VolumeSlider.py
@@ -15,23 +15,14 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import sys, os
-try:
-     import pygtk
-     pygtk.require("2.0")
-except:
-      pass
-try:
-    import gtk
-    import gtk.glade
-except:
-    sys.exit(1)
+
+import gtk
+import gtk.glade
 
 import re
 import copy
 
 from glade_window import GladeWindow
-from window.WindowWatcher import WindowWatcher
-from Client import Client
 
 class EarCandyVolumeSlider(GladeWindow):
     def __init__(self, core):
@@ -41,12 +32,13 @@ class EarCandyVolumeSlider(GladeWindow):
         
         self.vscale_volume = self.wtree.get_widget("vscale_volume")
         self.image_status = self.wtree.get_widget("image_status")
+        self.image_mute = self.wtree.get_widget("image_mute")
 
         signals = {
           "on_popup_volume_control_focus_out_event" : self.on_popup_volume_control_focus_out_event,
           "on_vscale_volume_change_value" : self.on_vscale_volume_change_value,
-          "on_button_suspend_clicked" : self.on_button_suspend_clicked
-
+          "on_button_suspend_clicked" : self.on_button_suspend_clicked,
+          "on_button_mute_clicked" : self.on_button_mute_clicked
         }
 
         self.wtree.signal_autoconnect(signals)
@@ -58,11 +50,21 @@ class EarCandyVolumeSlider(GladeWindow):
         #    self.combobox_client_category.append_text( val )
         self.active = False
 
+    def on_button_mute_clicked(self, widget):
+        self.core.set_mute( not self.core.is_mute )
+        self.stop()
+
     def on_button_suspend_clicked(self, widget):
 
         self.core.set_active( not self.core.active )
         self.stop()
 
+    def update_mute_status(self):
+        if self.core.is_mute:
+            self.image_mute.set_from_file( self.core.find_program_file("volume-magic.png") )
+        else:
+            self.image_mute.set_from_file( self.core.find_program_file("volume-muted.png") ) 
+
     def update_active_status(self):
         if self.core.active:
             self.image_status.set_from_stock("gtk-media-pause", 4)
@@ -85,7 +87,7 @@ class EarCandyVolumeSlider(GladeWindow):
 
     def run(self):
         self.active = True
-        self.core.get_current_sink_volume()
+        #self.core.get_current_sink_volume()
         self.window.present()
         gtk.gdk.keyboard_grab(self.window.window, True)
         gtk.gdk.pointer_grab(self.window.window, True,
diff --git a/ear_candy/pulseaudio/__init__.py b/earcandy/ui/__init__.py
similarity index 100%
copy from ear_candy/pulseaudio/__init__.py
copy to earcandy/ui/__init__.py
diff --git a/ear_candy/glade_window.py b/earcandy/ui/glade_window.py
similarity index 59%
rename from ear_candy/glade_window.py
rename to earcandy/ui/glade_window.py
index 374a283..691e9b2 100644
--- a/ear_candy/glade_window.py
+++ b/earcandy/ui/glade_window.py
@@ -15,38 +15,25 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import sys
-try:
-     import pygtk
-     pygtk.require("2.0")
-except:
-      pass
-try:
-    import gtk
-    import gtk.glade
-except:
-    sys.exit(1)
-
+import gtk
 import os, sys
 
+from earcandy.earcandyconfig import getdatapath
 
-def find_program_file(path):
-    """Finds a program file, for example, a png included with the program.
-    First looks for it in files/ under the parent directory of the parent directory
-    of ear_candy.py
-    Then looks for it in /usr/share/earcandy
-    Returns the path of the file"""
-    if os.path.exists(os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "files",path)):
-        return os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "files",path)
-    else:
-        return os.path.join(sys.prefix, "share/earcandy", path)
-    
 class GladeWindow():
 
     def __init__(self, glade_file, window_name, parent=None):
         self.return_value = None
         self.parent = parent
-        self.wtree = gtk.glade.XML(find_program_file(glade_file))
-        self.window = self.wtree.get_widget(window_name)
+
+        ui_filename = os.path.join(getdatapath(), 'ui', 'EarCandy.ui')
+        if not os.path.exists(ui_filename):
+            ui_filename = None
+
+        self.wtree = gtk.Builder()
+        self.wtree.add_from_file(ui_filename)
+
+        self.window = self.wtree.get_object(window_name)
         self.window.connect("destroy", self.on_destroy)
         if parent: self.window.set_transient_for(parent)
         
diff --git a/ear_candy/DesktopFiles.py b/earcandy/util/DesktopFiles.py
similarity index 100%
rename from ear_candy/DesktopFiles.py
rename to earcandy/util/DesktopFiles.py
diff --git a/ear_candy/Freedesktop.py b/earcandy/util/Freedesktop.py
similarity index 100%
rename from ear_candy/Freedesktop.py
rename to earcandy/util/Freedesktop.py
diff --git a/ear_candy/Threads.py b/earcandy/util/Threads.py
similarity index 100%
rename from ear_candy/Threads.py
rename to earcandy/util/Threads.py
diff --git a/ear_candy/pulseaudio/__init__.py b/earcandy/util/__init__.py
similarity index 100%
copy from ear_candy/pulseaudio/__init__.py
copy to earcandy/util/__init__.py
diff --git a/earcandy/windows/Application.py b/earcandy/windows/Application.py
new file mode 100644
index 0000000..49b8db4
--- /dev/null
+++ b/earcandy/windows/Application.py
@@ -0,0 +1,69 @@
+
+import os
+import gtk
+from earcandy.util.DesktopFiles import DesktopFiles
+import logging
+
+log = logging.getLogger('WindowApplication')
+log.setLevel(logging.WARNING)
+
+class Application():
+    def __init__(self, pid, command, name, desktop_files, icon):
+        self.command = os.path.basename(command.lower().strip())
+        self.name = name.lower().strip()
+        self.role = ""
+        self.icon = icon
+        self.icon_name = ""
+        self.description = name
+
+        # current application window info
+        self.pid = pid
+        self.window_name = ""
+        self.x = 0
+        self.y = 0
+        self.fullscreen = False
+        
+        self.__match_desktop(desktop_files)
+        return
+
+    def __match_desktop(self,  desktop_files):
+
+        #print "checking desktop files for match....", self.name
+        # Try looking up the application list
+        for a in desktop_files:
+            ex = a.get_exec_array()[0].lower()
+
+            # Check for pulse audio settings first...
+            if a.get("X-PulseAudio-Properties"):
+                self.role = a.get("X-PulseAudio-Properties")
+                break
+
+            if a.get("Categories") and len(a.get("Categories")) == 1 and a.get("Categories")[0] == "Core":
+                # skip entries that are core only
+                pass
+            else:
+                if a.get("X-GNOME-Bugzilla-Product") == self.name or ex == self.name or ex == self.command or a.get("Name").lower() == self.name or a.get("Name").lower() == self.command or ex + ".real" == self.command :
+                    self.icon_name = a.get("Icon")
+                    self.description = a.get("Name")
+                    self.role = self.__desktop_categories_to_role( a.get("Categories") )
+                    log.debug("WW :: desktop file : %s" % a.filename)
+                    break
+
+        if self.command == "skype.real":
+            self.role = "phone"
+        
+        log.debug("WW :: role : %s" % self.role)
+
+    def __desktop_categories_to_role(self, categories):
+        if categories:
+            for role in categories:
+                if "Telephony" in categories or "InstantMessaging" in categories:
+                    return "phone"
+                if "Music" in categories:
+                    return "music"
+                if "Video" in categories:
+                    return "video"
+                if "AudioVideo" in categories:
+                    return "music"             
+    
+        return ""
diff --git a/earcandy/windows/Watcher.py b/earcandy/windows/Watcher.py
new file mode 100644
index 0000000..49b563e
--- /dev/null
+++ b/earcandy/windows/Watcher.py
@@ -0,0 +1,112 @@
+import wnck
+import gobject
+import logging
+from earcandy.util.DesktopFiles import DesktopFiles
+from Application import Application
+
+log = logging.getLogger('WindowWatcher')
+log.setLevel(logging.WARNING)
+
+class Watcher():
+    
+    def __init__(self):
+        self.screen = wnck.screen_get_default()
+        self.screen.connect("active_window_changed", self.active_window_changed)
+        self.screen.connect("window_opened", self.window_opened)
+
+        self.desktop_files = DesktopFiles().read_all()
+        self.current_window = None
+        self.applications = {}
+
+        self.callback = None
+
+
+
+
+    def check_all(self):
+        for win in self.screen.get_windows():
+            self.update("exists", win) 
+
+    def get_command(self, pid):
+        command = str(pid)
+        try:
+            # Try and get command from PID
+            f = open("/proc/%s/cmdline" % pid, "r")
+            command = f.readline()[:-1]
+            f.close()
+        except:
+            pass
+        return command
+
+    def window_opened(self, screen, win):
+        if win and win.get_window_type() != wnck.WINDOW_DOCK: # ignore docks
+           
+            app = win.get_application ()
+            app_name = app.get_name()
+            
+            pid = win.get_pid()
+            command = self.get_command(pid)
+            category = ""
+
+            if not self.applications.has_key(command):
+                a = Application(pid, command, app_name, self.desktop_files, win.get_icon ())
+                self.applications[command] = a
+            
+            log.debug("WW :: window opened : %s" % app_name)
+            self.update("open", win)
+            #win.connect("geometry-changed", self.geometry_changed)
+           
+    def active_window_changed(self, screen, old_window):
+        #print screen
+        win = screen.get_active_window ()
+        if win:
+            log.debug("WW :: active window changed : %s" % win.get_name())
+            self.update("active", win)    
+    
+
+    def update(self, state, win):
+        if win and win.get_window_type() != wnck.WINDOW_DOCK: # ignore docks
+                
+            #try:
+                app = win.get_application ()
+                app_name = app.get_name()
+                pid = win.get_pid()
+
+                command = self.get_command(pid)
+
+                application = None
+                application = self.applications[command]
+                application.pid = pid
+
+                geom = win.get_geometry()
+                x = float(geom[1])
+                y = float(geom[0])
+                w = float(geom[2])
+                h = float(geom[3])
+
+                if win.get_name():
+                    application.window_name =  win.get_name()
+
+                #application.x = x+h/2
+                #application.y = y+w/2
+                #application.fullscreen = win.is_fullscreen () or win.is_maximized ()
+                #application.icon = win.get_icon ()
+                log.debug("WW :: application : %s" % application.description)
+                
+                if self.callback:
+                    gobject.idle_add(self.callback, application, state)
+            #except:
+            #    print "error reading window", win
+
+    def geometry_changed(self, win):
+        self.update("geo", win)
+
+
+if __name__ == '__main__':
+    logging.basicConfig(level=log.debug)
+    import gtk
+    # Turn on gtk threading
+    gtk.gdk.threads_init()
+    ww = Watcher()
+    gtk.main()
+
diff --git a/ear_candy/pulseaudio/__init__.py b/earcandy/windows/__init__.py
similarity index 100%
rename from ear_candy/pulseaudio/__init__.py
rename to earcandy/windows/__init__.py
diff --git a/files/earcandy.desktop b/files/earcandy.desktop
deleted file mode 100644
index 89c8497..0000000
--- a/files/earcandy.desktop
+++ /dev/null
@@ -1,9 +0,0 @@
-[Desktop Entry]
-Name=Ear Candy
-Comment=Gracefully fade applications volume in and out
-Terminal=false
-Icon=earsLabel
-Type=Application
-Exec=earcandy
-Categories=Utility;
-StartupNotify=true
diff --git a/files/earsLabel.png b/files/earsLabel.png
deleted file mode 100644
index b8ecce8..0000000
Binary files a/files/earsLabel.png and /dev/null differ
diff --git a/files/earsLabelOff.png b/files/earsLabelOff.png
deleted file mode 100644
index 2395f7b..0000000
Binary files a/files/earsLabelOff.png and /dev/null differ
diff --git a/po/earcandy.pot b/po/earcandy.pot
new file mode 100644
index 0000000..4434017
--- /dev/null
+++ b/po/earcandy.pot
@@ -0,0 +1,219 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-04-15 22:38+1200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: LANGUAGE <LL at li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../data/ui/EarcandyWindow.ui.h:1
+msgid "Earcandy"
+msgstr ""
+
+#: ../data/ui/EarcandyWindow.ui.h:2
+msgid "Status Area"
+msgstr ""
+
+#: ../data/ui/EarcandyWindow.ui.h:3
+msgid ""
+"Your application has been created!\n"
+"\n"
+"To start changing this user interface, run 'quickly glade', which will open "
+"Glade so you can edit the default windows and dialogs.\n"
+"\n"
+"To change the behavior and edit the python code, run 'quickly edit', which "
+"will bring up a text editor."
+msgstr ""
+
+#: ../data/ui/EarcandyWindow.ui.h:8
+msgid "_Edit"
+msgstr ""
+
+#: ../data/ui/EarcandyWindow.ui.h:9
+msgid "_File"
+msgstr ""
+
+#: ../data/ui/EarcandyWindow.ui.h:10
+msgid "_Help"
+msgstr ""
+
+#: ../data/ui/EarcandyWindow.ui.h:11
+msgid "_View"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:1
+msgid " Output Devices  "
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:2
+msgid "<b>1) Select pulseaudio client</b>"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:3
+msgid "<b>2) Select application</b>"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:4
+msgid "<b>3) Select category</b>"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:5
+msgid ""
+"<b>Do not edit these values\n"
+" unless you know what you are doing</b>"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:7
+msgid "<big>Banshee</big>"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:8
+msgid ""
+"<i>For a client to show here it must have played a sound at least once</i>"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:9
+msgid "<i>For an application to show here it must be open</i>"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:10
+msgid "Activity"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:11
+msgid "Advanced"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:12
+msgid "Advanced client matching rules"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:13
+msgid "Application"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:14
+msgid "Application control plugins"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:15
+msgid "Applications"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:16
+msgid "Autostart"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:17
+msgid "Category"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:18
+msgid "Client Name"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:19
+msgid "Command"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:20
+msgid "Cross Fade Speed"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:21
+msgid "Description"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:22
+msgid "Ear Candy - Audio Clients"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:23
+msgid "Edit"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:24
+msgid "Fade"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:25
+msgid "Fade volume"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:26
+msgid "Include 'phone' category when muting"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:27
+msgid "Lock volume levels"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:28
+msgid "Max"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:29
+msgid "Min"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:30
+msgid "Mute"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:31
+msgid "Mute sound"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:32
+msgid "Notification Tray"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:33
+msgid "Output Device"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:34
+msgid "Partial Mute Level"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:35
+msgid "Restore defaults"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:36
+msgid "Select PulseAudio Stream"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:37
+msgid "Show Icon"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:38
+msgid "Start on login"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:39
+msgid "Use volume meter detection"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:40
+msgid "Window Title"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:41
+msgid "[ automatic ]"
+msgstr ""
+
+#: ../data/ui/EarCandy.ui.h:42
+msgid "label"
+msgstr ""
diff --git a/setup.py b/setup.py
index f6a61a2..81fdc9b 100644
--- a/setup.py
+++ b/setup.py
@@ -1,47 +1,94 @@
 #!/usr/bin/env python
-# author: David D Lowe
-# year: 2009
-# this file has been released into the public domain
-
-from distutils.core import setup
-import os, sys
-import glob
-
-
-def main():
-    
-    data_files = glob.glob("files/*")  # data_files a list of strings
-    # of the relative paths of all non-py files that should be included
-    # ex: data_files is ['files/pulseoptions.glade', 'settings.xml', ...]
-    
-    setup( name="earcandy", # this should start with a lowercase letter 
-        #so that it can be used as a debian package name later on
-    version="0.4", # string, version of your program, not python version
-    description="A sound level manager that nicely fades applications in and out based on their profile and window focus", # short
-    author="KillerKiwi",
-    author_email="killerkiwi2005 at gmail.com",
-    url="https://launchpad.net/earcandy", # home page for end-users
-    license="unknown",
-    packages=["ear_candy", "ear_candy.pulseaudio", "ear_candy.window"], # python packages, not debian packages
-    data_files=[('share/earcandy', data_files),
-      ('/usr/share/applications', ['files/earcandy.desktop']),
-      ('/usr/share/pixmaps', ['files/earsLabel.png'])], # data_files is a list of tuples
-            # each tuple contaning an installation path and a list of data files
-    scripts=["runner/ear_candy"], # the script that should be run and installed in /usr/bin
-    classifiers=["Development Status :: 5 - Production/Stable", "Intended Audience :: End Users/Desktop", "License :: unknown", "Operating System :: POSIX :: Linux"],
-        # a bunch of optional tags, a list of classifiers can be found at http://pypi.python.org/pypi?:action=list_classifiers
-    long_description="""A sound level manager that nicely fades applications in and out based on there profile and window focus
-
-What works now :
- - All volume adjustments are fades
- - Fade out music/video players on skype call
- - Fade to music player with focus when more than one
- - Fade out music player when video playing
- - Push sound to USB headsets on plugin
- - Categories to assign to clients
- - Sniffs desktop files to guess application category ... music/video/VoIP
- - Simplified pref UI for creating rules
- - Volume sniffing to fix youtube video issue""")
-
-if __name__ == "__main__":
-    main()  
+# -*- coding: utf-8 -*-
+### BEGIN LICENSE
+# This file is in the public domain
+### END LICENSE
+
+###################### DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ######################
+
+try:
+    import DistUtilsExtra.auto
+except ImportError:
+    import sys
+    print >> sys.stderr, 'To build earcandy you need https://launchpad.net/python-distutils-extra'
+    sys.exit(1)
+
+assert DistUtilsExtra.auto.__version__ >= '2.10', 'needs DistUtilsExtra.auto >= 2.10'
+import os
+
+
+def update_data_path(prefix, oldvalue=None):
+
+    try:
+        fin = file('earcandy/earcandyconfig.py', 'r')
+        fout = file(fin.name + '.new', 'w')
+
+        for line in fin:            
+            fields = line.split(' = ') # Separate variable from value
+            if fields[0] == '__earcandy_data_directory__':
+                # update to prefix, store oldvalue
+                if not oldvalue:
+                    oldvalue = fields[1]
+                    line = "%s = '%s'\n" % (fields[0], prefix)
+                else: # restore oldvalue
+                    line = "%s = %s" % (fields[0], oldvalue)
+            fout.write(line)
+
+        fout.flush()
+        fout.close()
+        fin.close()
+        os.rename(fout.name, fin.name)
+    except (OSError, IOError), e:
+        print ("ERROR: Can't find earcandy/earcandyconfig.py")
+        sys.exit(1)
+    return oldvalue
+
+
+def update_desktop_file(datadir):
+
+    try:
+        fin = file('earcandy.desktop.in', 'r')
+        fout = file(fin.name + '.new', 'w')
+
+        for line in fin:            
+            if 'Icon=' in line:
+                line = "Icon=%s\n" % (datadir + 'media/icon.png')
+            fout.write(line)
+        fout.flush()
+        fout.close()
+        fin.close()
+        os.rename(fout.name, fin.name)
+    except (OSError, IOError), e:
+        print ("ERROR: Can't find earcandy.desktop.in")
+        sys.exit(1)
+
+
+class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto):
+    def run(self):
+        if self.root or self.home:
+            print "WARNING: You don't use a standard --prefix installation, take care that you eventually " \
+            "need to update quickly/quicklyconfig.py file to adjust __quickly_data_directory__. You can " \
+            "ignore this warning if you are packaging and uses --prefix."
+        previous_value = update_data_path(self.prefix + '/share/earcandy/')
+        update_desktop_file(self.prefix + '/share/earcandy/')
+        DistUtilsExtra.auto.install_auto.run(self)
+        update_data_path(self.prefix, previous_value)
+
+
+        
+##################################################################################
+###################### YOU SHOULD MODIFY ONLY WHAT IS BELOW ######################
+##################################################################################
+
+DistUtilsExtra.auto.setup(
+    name='earcandy',
+    version='0.9',
+    license='GPL v2',
+    author='Jason Taylor',
+    author_email='killerkiwi2005 at gmail.com',
+    description='Automatic pulseaudio volume control',
+    long_description='A sound level manager that nicely fades applications in and out based on their media role and window focus',
+    url='https://launchpad.net/earcandy',
+    cmdclass={'install': InstallAndUpdateDataDirectory}
+    )
+

-- 
earcandy packaging



More information about the pkg-multimedia-commits mailing list