summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--AUTHORS20
-rw-r--r--COPYING699
-rw-r--r--INSTALL59
-rw-r--r--NEWS89
-rw-r--r--README7
-rw-r--r--doc/patchage.127
-rw-r--r--icons/128x128/patchage.pngbin0 -> 10307 bytes
-rw-r--r--icons/16x16/patchage.pngbin0 -> 811 bytes
-rw-r--r--icons/16x16/patchage.svg562
-rw-r--r--icons/22x22/patchage.pngbin0 -> 1141 bytes
-rw-r--r--icons/22x22/patchage.svg349
-rw-r--r--icons/24x24/patchage.pngbin0 -> 1201 bytes
-rw-r--r--icons/256x256/patchage.pngbin0 -> 21512 bytes
-rw-r--r--icons/32x32/patchage.pngbin0 -> 1824 bytes
-rw-r--r--icons/32x32/patchage.svg1082
-rw-r--r--icons/48x48/patchage.pngbin0 -> 2687 bytes
-rw-r--r--icons/48x48/patchage.svg595
-rw-r--r--icons/scalable/patchage.svg595
-rw-r--r--osx/Info.plist.in45
-rw-r--r--osx/Patchage.icnsbin0 -> 177383 bytes
-rwxr-xr-xosx/bundleify.sh79
-rw-r--r--osx/gtkrc251
-rw-r--r--osx/loaders.cache11
-rw-r--r--osx/pango.modules2
-rw-r--r--osx/pangorc2
-rw-r--r--patchage.desktop.in9
-rw-r--r--src/AlsaDriver.cpp585
-rw-r--r--src/AlsaDriver.hpp116
-rw-r--r--src/Configuration.cpp333
-rw-r--r--src/Configuration.hpp109
-rw-r--r--src/Driver.hpp55
-rw-r--r--src/JackDbusDriver.cpp1048
-rw-r--r--src/JackDbusDriver.hpp161
-rw-r--r--src/JackDriver.cpp588
-rw-r--r--src/JackDriver.hpp109
-rw-r--r--src/Legend.hpp71
-rw-r--r--src/Patchage.cpp1078
-rw-r--r--src/Patchage.hpp212
-rw-r--r--src/PatchageCanvas.cpp338
-rw-r--r--src/PatchageCanvas.hpp85
-rw-r--r--src/PatchageEvent.cpp110
-rw-r--r--src/PatchageEvent.hpp87
-rw-r--r--src/PatchageModule.cpp157
-rw-r--r--src/PatchageModule.hpp67
-rw-r--r--src/PatchagePort.hpp104
-rw-r--r--src/PortID.hpp120
-rw-r--r--src/Queue.hpp131
-rw-r--r--src/UIFile.hpp66
-rw-r--r--src/Widget.hpp46
-rw-r--r--src/binary_location.h54
-rw-r--r--src/jackey.h72
-rw-r--r--src/main.cpp93
-rw-r--r--src/patchage.gladep9
l---------src/patchage.svg1
-rw-r--r--src/patchage.ui1260
-rwxr-xr-xwaf171
-rw-r--r--waflib/.gitignore (renamed from .gitignore)0
-rw-r--r--waflib/Build.py (renamed from Build.py)0
-rw-r--r--waflib/COPYING25
-rw-r--r--waflib/ConfigSet.py (renamed from ConfigSet.py)0
-rw-r--r--waflib/Configure.py (renamed from Configure.py)0
-rw-r--r--waflib/Context.py (renamed from Context.py)0
-rw-r--r--waflib/Errors.py (renamed from Errors.py)0
-rw-r--r--waflib/Logs.py (renamed from Logs.py)0
-rw-r--r--waflib/Node.py (renamed from Node.py)0
-rw-r--r--waflib/Options.py (renamed from Options.py)0
-rw-r--r--waflib/README.md (renamed from README.md)0
-rw-r--r--waflib/Runner.py (renamed from Runner.py)0
-rw-r--r--waflib/Scripting.py (renamed from Scripting.py)0
-rw-r--r--waflib/Task.py (renamed from Task.py)0
-rw-r--r--waflib/TaskGen.py (renamed from TaskGen.py)0
-rw-r--r--waflib/Tools/__init__.py (renamed from Tools/__init__.py)0
-rw-r--r--waflib/Tools/ar.py (renamed from Tools/ar.py)0
-rw-r--r--waflib/Tools/asm.py (renamed from Tools/asm.py)0
-rw-r--r--waflib/Tools/bison.py (renamed from Tools/bison.py)0
-rw-r--r--waflib/Tools/c.py (renamed from Tools/c.py)0
-rw-r--r--waflib/Tools/c_aliases.py (renamed from Tools/c_aliases.py)0
-rw-r--r--waflib/Tools/c_config.py (renamed from Tools/c_config.py)0
-rw-r--r--waflib/Tools/c_osx.py (renamed from Tools/c_osx.py)0
-rw-r--r--waflib/Tools/c_preproc.py (renamed from Tools/c_preproc.py)0
-rw-r--r--waflib/Tools/c_tests.py (renamed from Tools/c_tests.py)0
-rw-r--r--waflib/Tools/ccroot.py (renamed from Tools/ccroot.py)0
-rw-r--r--waflib/Tools/clang.py (renamed from Tools/clang.py)0
-rw-r--r--waflib/Tools/clangxx.py (renamed from Tools/clangxx.py)0
-rw-r--r--waflib/Tools/compiler_c.py (renamed from Tools/compiler_c.py)0
-rw-r--r--waflib/Tools/compiler_cxx.py (renamed from Tools/compiler_cxx.py)0
-rw-r--r--waflib/Tools/compiler_d.py (renamed from Tools/compiler_d.py)0
-rw-r--r--waflib/Tools/compiler_fc.py (renamed from Tools/compiler_fc.py)0
-rw-r--r--waflib/Tools/cs.py (renamed from Tools/cs.py)0
-rw-r--r--waflib/Tools/cxx.py (renamed from Tools/cxx.py)0
-rw-r--r--waflib/Tools/d.py (renamed from Tools/d.py)0
-rw-r--r--waflib/Tools/d_config.py (renamed from Tools/d_config.py)0
-rw-r--r--waflib/Tools/d_scan.py (renamed from Tools/d_scan.py)0
-rw-r--r--waflib/Tools/dbus.py (renamed from Tools/dbus.py)0
-rw-r--r--waflib/Tools/dmd.py (renamed from Tools/dmd.py)0
-rw-r--r--waflib/Tools/errcheck.py (renamed from Tools/errcheck.py)0
-rw-r--r--waflib/Tools/fc.py (renamed from Tools/fc.py)0
-rw-r--r--waflib/Tools/fc_config.py (renamed from Tools/fc_config.py)0
-rw-r--r--waflib/Tools/fc_scan.py (renamed from Tools/fc_scan.py)0
-rw-r--r--waflib/Tools/flex.py (renamed from Tools/flex.py)0
-rw-r--r--waflib/Tools/g95.py (renamed from Tools/g95.py)0
-rw-r--r--waflib/Tools/gas.py (renamed from Tools/gas.py)0
-rw-r--r--waflib/Tools/gcc.py (renamed from Tools/gcc.py)0
-rw-r--r--waflib/Tools/gdc.py (renamed from Tools/gdc.py)0
-rw-r--r--waflib/Tools/gfortran.py (renamed from Tools/gfortran.py)0
-rw-r--r--waflib/Tools/glib2.py (renamed from Tools/glib2.py)0
-rw-r--r--waflib/Tools/gnu_dirs.py (renamed from Tools/gnu_dirs.py)0
-rw-r--r--waflib/Tools/gxx.py (renamed from Tools/gxx.py)0
-rw-r--r--waflib/Tools/icc.py (renamed from Tools/icc.py)0
-rw-r--r--waflib/Tools/icpc.py (renamed from Tools/icpc.py)0
-rw-r--r--waflib/Tools/ifort.py (renamed from Tools/ifort.py)0
-rw-r--r--waflib/Tools/intltool.py (renamed from Tools/intltool.py)0
-rw-r--r--waflib/Tools/irixcc.py (renamed from Tools/irixcc.py)0
-rw-r--r--waflib/Tools/javaw.py (renamed from Tools/javaw.py)0
-rw-r--r--waflib/Tools/ldc2.py (renamed from Tools/ldc2.py)0
-rw-r--r--waflib/Tools/lua.py (renamed from Tools/lua.py)0
-rw-r--r--waflib/Tools/md5_tstamp.py (renamed from Tools/md5_tstamp.py)0
-rw-r--r--waflib/Tools/msvc.py (renamed from Tools/msvc.py)0
-rw-r--r--waflib/Tools/nasm.py (renamed from Tools/nasm.py)0
-rw-r--r--waflib/Tools/nobuild.py (renamed from Tools/nobuild.py)0
-rw-r--r--waflib/Tools/perl.py (renamed from Tools/perl.py)0
-rw-r--r--waflib/Tools/python.py (renamed from Tools/python.py)0
-rw-r--r--waflib/Tools/qt5.py (renamed from Tools/qt5.py)0
-rw-r--r--waflib/Tools/ruby.py (renamed from Tools/ruby.py)0
-rw-r--r--waflib/Tools/suncc.py (renamed from Tools/suncc.py)0
-rw-r--r--waflib/Tools/suncxx.py (renamed from Tools/suncxx.py)0
-rw-r--r--waflib/Tools/tex.py (renamed from Tools/tex.py)0
-rw-r--r--waflib/Tools/vala.py (renamed from Tools/vala.py)0
-rw-r--r--waflib/Tools/waf_unit_test.py (renamed from Tools/waf_unit_test.py)0
-rw-r--r--waflib/Tools/winres.py (renamed from Tools/winres.py)0
-rw-r--r--waflib/Tools/xlc.py (renamed from Tools/xlc.py)0
-rw-r--r--waflib/Tools/xlcxx.py (renamed from Tools/xlcxx.py)0
-rw-r--r--waflib/Utils.py (renamed from Utils.py)0
-rw-r--r--waflib/__init__.py (renamed from __init__.py)0
-rw-r--r--waflib/ansiterm.py (renamed from ansiterm.py)0
-rw-r--r--waflib/extras/__init__.py (renamed from extras/__init__.py)0
-rw-r--r--waflib/extras/autowaf.py (renamed from extras/autowaf.py)0
-rw-r--r--waflib/extras/batched_cc.py (renamed from extras/batched_cc.py)0
-rw-r--r--waflib/extras/biber.py (renamed from extras/biber.py)0
-rw-r--r--waflib/extras/bjam.py (renamed from extras/bjam.py)0
-rw-r--r--waflib/extras/blender.py (renamed from extras/blender.py)0
-rw-r--r--waflib/extras/boo.py (renamed from extras/boo.py)0
-rw-r--r--waflib/extras/boost.py (renamed from extras/boost.py)0
-rw-r--r--waflib/extras/build_file_tracker.py (renamed from extras/build_file_tracker.py)0
-rw-r--r--waflib/extras/build_logs.py (renamed from extras/build_logs.py)0
-rw-r--r--waflib/extras/buildcopy.py (renamed from extras/buildcopy.py)0
-rw-r--r--waflib/extras/c_bgxlc.py (renamed from extras/c_bgxlc.py)0
-rw-r--r--waflib/extras/c_dumbpreproc.py (renamed from extras/c_dumbpreproc.py)0
-rw-r--r--waflib/extras/c_emscripten.py (renamed from extras/c_emscripten.py)0
-rw-r--r--waflib/extras/c_nec.py (renamed from extras/c_nec.py)0
-rw-r--r--waflib/extras/cabal.py (renamed from extras/cabal.py)0
-rw-r--r--waflib/extras/cfg_altoptions.py (renamed from extras/cfg_altoptions.py)0
-rw-r--r--waflib/extras/clang_compilation_database.py (renamed from extras/clang_compilation_database.py)0
-rw-r--r--waflib/extras/codelite.py (renamed from extras/codelite.py)0
-rw-r--r--waflib/extras/color_gcc.py (renamed from extras/color_gcc.py)0
-rw-r--r--waflib/extras/color_rvct.py (renamed from extras/color_rvct.py)0
-rw-r--r--waflib/extras/compat15.py (renamed from extras/compat15.py)0
-rw-r--r--waflib/extras/cppcheck.py (renamed from extras/cppcheck.py)0
-rw-r--r--waflib/extras/cpplint.py (renamed from extras/cpplint.py)0
-rw-r--r--waflib/extras/cross_gnu.py (renamed from extras/cross_gnu.py)0
-rw-r--r--waflib/extras/cython.py (renamed from extras/cython.py)0
-rw-r--r--waflib/extras/dcc.py (renamed from extras/dcc.py)0
-rw-r--r--waflib/extras/distnet.py (renamed from extras/distnet.py)0
-rw-r--r--waflib/extras/doxygen.py (renamed from extras/doxygen.py)0
-rw-r--r--waflib/extras/dpapi.py (renamed from extras/dpapi.py)0
-rw-r--r--waflib/extras/eclipse.py (renamed from extras/eclipse.py)0
-rw-r--r--waflib/extras/erlang.py (renamed from extras/erlang.py)0
-rw-r--r--waflib/extras/fast_partial.py (renamed from extras/fast_partial.py)0
-rw-r--r--waflib/extras/fc_bgxlf.py (renamed from extras/fc_bgxlf.py)0
-rw-r--r--waflib/extras/fc_cray.py (renamed from extras/fc_cray.py)0
-rw-r--r--waflib/extras/fc_nag.py (renamed from extras/fc_nag.py)0
-rw-r--r--waflib/extras/fc_nec.py (renamed from extras/fc_nec.py)0
-rw-r--r--waflib/extras/fc_open64.py (renamed from extras/fc_open64.py)0
-rw-r--r--waflib/extras/fc_pgfortran.py (renamed from extras/fc_pgfortran.py)0
-rw-r--r--waflib/extras/fc_solstudio.py (renamed from extras/fc_solstudio.py)0
-rw-r--r--waflib/extras/fc_xlf.py (renamed from extras/fc_xlf.py)0
-rw-r--r--waflib/extras/file_to_object.py (renamed from extras/file_to_object.py)0
-rw-r--r--waflib/extras/fluid.py (renamed from extras/fluid.py)0
-rw-r--r--waflib/extras/freeimage.py (renamed from extras/freeimage.py)0
-rw-r--r--waflib/extras/fsb.py (renamed from extras/fsb.py)0
-rw-r--r--waflib/extras/fsc.py (renamed from extras/fsc.py)0
-rw-r--r--waflib/extras/gccdeps.py (renamed from extras/gccdeps.py)0
-rw-r--r--waflib/extras/gdbus.py (renamed from extras/gdbus.py)0
-rw-r--r--waflib/extras/gob2.py (renamed from extras/gob2.py)0
-rw-r--r--waflib/extras/halide.py (renamed from extras/halide.py)0
-rwxr-xr-xwaflib/extras/javatest.py (renamed from extras/javatest.py)0
-rw-r--r--waflib/extras/kde4.py (renamed from extras/kde4.py)0
-rw-r--r--waflib/extras/local_rpath.py (renamed from extras/local_rpath.py)0
-rw-r--r--waflib/extras/lv2.py (renamed from extras/lv2.py)0
-rw-r--r--waflib/extras/make.py (renamed from extras/make.py)0
-rw-r--r--waflib/extras/midl.py (renamed from extras/midl.py)0
-rw-r--r--waflib/extras/msvcdeps.py (renamed from extras/msvcdeps.py)0
-rw-r--r--waflib/extras/msvs.py (renamed from extras/msvs.py)0
-rw-r--r--waflib/extras/netcache_client.py (renamed from extras/netcache_client.py)0
-rw-r--r--waflib/extras/objcopy.py (renamed from extras/objcopy.py)0
-rw-r--r--waflib/extras/ocaml.py (renamed from extras/ocaml.py)0
-rw-r--r--waflib/extras/package.py (renamed from extras/package.py)0
-rw-r--r--waflib/extras/parallel_debug.py (renamed from extras/parallel_debug.py)0
-rw-r--r--waflib/extras/pch.py (renamed from extras/pch.py)0
-rw-r--r--waflib/extras/pep8.py (renamed from extras/pep8.py)0
-rw-r--r--waflib/extras/pgicc.py (renamed from extras/pgicc.py)0
-rw-r--r--waflib/extras/pgicxx.py (renamed from extras/pgicxx.py)0
-rw-r--r--waflib/extras/proc.py (renamed from extras/proc.py)0
-rw-r--r--waflib/extras/protoc.py (renamed from extras/protoc.py)0
-rw-r--r--waflib/extras/pyqt5.py (renamed from extras/pyqt5.py)0
-rw-r--r--waflib/extras/pytest.py (renamed from extras/pytest.py)0
-rw-r--r--waflib/extras/qnxnto.py (renamed from extras/qnxnto.py)0
-rw-r--r--waflib/extras/qt4.py (renamed from extras/qt4.py)0
-rw-r--r--waflib/extras/relocation.py (renamed from extras/relocation.py)0
-rw-r--r--waflib/extras/remote.py (renamed from extras/remote.py)0
-rw-r--r--waflib/extras/resx.py (renamed from extras/resx.py)0
-rw-r--r--waflib/extras/review.py (renamed from extras/review.py)0
-rw-r--r--waflib/extras/rst.py (renamed from extras/rst.py)0
-rw-r--r--waflib/extras/run_do_script.py (renamed from extras/run_do_script.py)0
-rw-r--r--waflib/extras/run_m_script.py (renamed from extras/run_m_script.py)0
-rw-r--r--waflib/extras/run_py_script.py (renamed from extras/run_py_script.py)0
-rw-r--r--waflib/extras/run_r_script.py (renamed from extras/run_r_script.py)0
-rw-r--r--waflib/extras/sas.py (renamed from extras/sas.py)0
-rw-r--r--waflib/extras/satellite_assembly.py (renamed from extras/satellite_assembly.py)0
-rw-r--r--waflib/extras/scala.py (renamed from extras/scala.py)0
-rw-r--r--waflib/extras/slow_qt4.py (renamed from extras/slow_qt4.py)0
-rw-r--r--waflib/extras/softlink_libs.py (renamed from extras/softlink_libs.py)0
-rw-r--r--waflib/extras/stale.py (renamed from extras/stale.py)0
-rw-r--r--waflib/extras/stracedeps.py (renamed from extras/stracedeps.py)0
-rw-r--r--waflib/extras/swig.py (renamed from extras/swig.py)0
-rw-r--r--waflib/extras/syms.py (renamed from extras/syms.py)0
-rw-r--r--waflib/extras/ticgt.py (renamed from extras/ticgt.py)0
-rw-r--r--waflib/extras/unity.py (renamed from extras/unity.py)0
-rw-r--r--waflib/extras/use_config.py (renamed from extras/use_config.py)0
-rw-r--r--waflib/extras/valadoc.py (renamed from extras/valadoc.py)0
-rw-r--r--waflib/extras/waf_xattr.py (renamed from extras/waf_xattr.py)0
-rw-r--r--waflib/extras/why.py (renamed from extras/why.py)0
-rw-r--r--waflib/extras/win32_opts.py (renamed from extras/win32_opts.py)0
-rw-r--r--waflib/extras/wix.py (renamed from extras/wix.py)0
-rw-r--r--waflib/extras/xcode6.py (renamed from extras/xcode6.py)0
-rw-r--r--waflib/fixpy2.py (renamed from fixpy2.py)0
-rwxr-xr-xwaflib/processor.py (renamed from processor.py)0
-rwxr-xr-xwaflib/waf16
-rw-r--r--wscript226
240 files changed, 12153 insertions, 34 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..f063da3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+waf binary \ No newline at end of file
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..3d55834
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,20 @@
+Author:
+ David Robillard <d@drobilla.net>
+
+Jack D-BUS support:
+ Nedko Arnaudov <nedko@arnaudov.name>
+
+Icons:
+ Lapo Calamandrei
+
+Additional thanks to:
+ Alessandro Cominu (comix)
+ Esben Stien (b0ef)
+ Lars Luthman (larsl)
+ Robert Ham (rah)
+ Steve Harris (swh)
+ Thorsten Wilms (thorwil)
+ Hanspeter Portner (ventosus)
+ Thomas Brand (tom_)
+
+ ... and others for influence, ideas, bug reports, etc.
diff --git a/COPYING b/COPYING
index a4147d2..94a9ed0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,25 +1,674 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU 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 Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..623cddd
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,59 @@
+Installation Instructions
+=========================
+
+Basic Installation
+------------------
+
+Building this software requires only Python. To install with default options:
+
+ ./waf configure
+ ./waf
+ ./waf install
+
+You may need to become root for the install stage, for example:
+
+ sudo ./waf install
+
+Configuration Options
+---------------------
+
+All supported options can be viewed using the command:
+
+ ./waf --help
+
+Most options only need to be passed during the configure stage, for example:
+
+ ./waf configure --prefix=/usr
+ ./waf
+ ./waf install
+
+Compiler Configuration
+----------------------
+
+Several standard environment variables can be used to control how compilers are
+invoked:
+
+ * CC: Path to C compiler
+ * CFLAGS: C compiler options
+ * CXX: Path to C++ compiler
+ * CXXFLAGS: C++ compiler options
+ * CPPFLAGS: C preprocessor options
+ * LINKFLAGS: Linker options
+
+Installation Directories
+------------------------
+
+The --prefix option (or the PREFIX environment variable) can be used to change
+the prefix which all files are installed under. There are also several options
+allowing for more fine-tuned control, see the --help output for details.
+
+Packaging
+---------
+
+Everything can be installed to a specific root directory by passing a --destdir
+option to the install stage (or setting the DESTDIR environment variable),
+which adds a prefix to all install paths. For example:
+
+ ./waf configure --prefix=/usr
+ ./waf
+ ./waf install --destdir=/tmp/package
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..7d91a7a
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,89 @@
+patchage (1.0.1) unstable;
+
+ * Support Jack CV and OSC via metadata
+ * Add support for exporting canvas as PDF
+ * Save window size and position when closed via window manager
+ * Order ports deterministically
+ * Bring back Jack buffer size selector
+ * Style messages pane to match canvas
+ * Don't install 512x512 icons
+ * Restore messages pane visibility and height
+ * Configure based on compiler target OS for cross-compilation
+ * Fix compilation with Jack DBus
+ * Upgrade to waf 1.8.14
+
+ -- David Robillard <d@drobilla.net> Fri, 14 Oct 2016 19:06:17 -0400
+
+patchage (1.0.0) stable;
+
+ * Allow removing connections by selecting their handle and pressing delete
+ * Remove Raul dependency
+ * Switch from FlowCanvas to Ganv (much improved looks and performance)
+ * Remove LASH support and simplify UI
+ * Fix font configuration on OSX
+ * Use Mac style key bindings on OSX
+ * Integrate with Mac menu bar on OSX
+ * Support for DOT export for rendering with GraphViz
+ * Use XDG_CONFIG_HOME instead of ~/.patchagerc
+ * Make port colours configurable
+ * Support port pretty names via new Jack metadata API
+
+ -- David Robillard <d@drobilla.net> Sun, 27 Apr 2014 23:46:10 -0400
+
+patchage (0.5.0) stable;
+
+ * Auto-arrange interface modules sanely (align corresponding inputs/outputs)
+ * Add -J (--no-jack) command line option
+ * Add proper --help output and man page
+ * Improve performance (dramatically for large setups)
+ * Fancy console info/warning/error logging
+ * Fix minor memory leaks and reduce memory consumption
+ * Fix handling of ALSA duplex ports
+ * Hide "split" module menu item when it is useless
+ * Fix Jack D-Bus support
+ * Mac OS X .app bundle port
+ * Bump FlowCanvas dependency to 0.7.0
+ * Add more/larger icons
+ * Add missing COPYING file to distribution
+ * Build system and code quality improvements
+
+ -- David Robillard <d@drobilla.net> Tue, 11 Jan 2011 17:42:07 -0500
+
+patchage (0.4.5) stable;
+
+ * Install SVG icon
+ * Fix compilation without Jack
+ * Improve performance when dragging modules
+ * Bump FlowCanvas dependency to 0.6.0
+ * Upgrade to waf 1.5.18
+
+ -- David Robillard <d@drobilla.net> Fri, 03 Sep 2010 20:24:36 -0400
+
+patchage (0.4.4) stable;
+
+ * Fix incorrect icon install paths
+
+ -- David Robillard <d@drobilla.net> Wed, 09 Dec 2009 10:17:37 -0500
+
+patchage (0.4.3) stable;
+
+ * Switch to waf build system
+ * Fix compilation with GCC 4.4
+ * Better ALSA support
+ * Massive performance improvements when ALSA is enabled
+ * Center on startup
+
+ -- David Robillard <d@drobilla.net> Tue, 08 Dec 2009 21:13:37 -0500
+
+patchage (0.4.2) stable;
+
+ * LASH support via D-BUS from ladi-patchage branch
+ * Remove old LASH support via liblash
+
+ -- David Robillard <d@drobilla.net> Tue, 09 Sep 2008 15:41:04 -0400
+
+patchage (0.4.1) stable;
+
+ * Initial release
+
+ -- David Robillard <d@drobilla.net> Sun, 06 Jul 2008 17:19:55 -0400
diff --git a/README b/README
new file mode 100644
index 0000000..33af97f
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+Patchage
+========
+
+Patchage is a modular patch bay for Jack and ALSA based audio/MIDI systems.
+For more information, see <http://drobilla.net/software/patchage>.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/doc/patchage.1 b/doc/patchage.1
new file mode 100644
index 0000000..07cdabe
--- /dev/null
+++ b/doc/patchage.1
@@ -0,0 +1,27 @@
+.TH PATCHAGE 1 "15 Dec 2010"
+
+.SH NAME
+.B patchage \- Graphically connect JACK and ALSA Audio/MIDI ports
+
+.SH SYNOPSIS
+.B Patchage
+provides a graphical interface to connect Jack/Alsa Audio/MIDI inputs
+and outputs. Each application is represented on a canvas as a "module"
+with inputs on the left and outputs on the right. Modules can be arranged
+manually or automatically to have a clear display of the current setup.
+
+.SH OPTIONS
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print the command line options.
+
+.TP
+\fB\-A\fR, \fB\-\-no\-alsa\fR
+Do not automatically attach to ALSA.
+
+.TP
+\fB\-J\fR, \fB\-\-no\-jack\fR
+Do not automatically attach to JACK.
+
+.SH AUTHOR
+Patchage was written by David Robillard <d@drobilla.net>
diff --git a/icons/128x128/patchage.png b/icons/128x128/patchage.png
new file mode 100644
index 0000000..8082502
--- /dev/null
+++ b/icons/128x128/patchage.png
Binary files differ
diff --git a/icons/16x16/patchage.png b/icons/16x16/patchage.png
new file mode 100644
index 0000000..cf8b853
--- /dev/null
+++ b/icons/16x16/patchage.png
Binary files differ
diff --git a/icons/16x16/patchage.svg b/icons/16x16/patchage.svg
new file mode 100644
index 0000000..f4684a7
--- /dev/null
+++ b/icons/16x16/patchage.svg
@@ -0,0 +1,562 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ id="svg7854"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/lapo/Scrivania/patchage/16x16"
+ sodipodi:docname="patchage.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs7856">
+ <linearGradient
+ id="linearGradient2785"
+ inkscape:collect="always">
+ <stop
+ id="stop2787"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2789"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2779"
+ inkscape:collect="always">
+ <stop
+ id="stop2781"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1" />
+ <stop
+ id="stop2783"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2773"
+ inkscape:collect="always">
+ <stop
+ id="stop2775"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2777"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2767"
+ inkscape:collect="always">
+ <stop
+ id="stop2769"
+ offset="0"
+ style="stop-color:#4e9a06;stop-opacity:1" />
+ <stop
+ id="stop2771"
+ offset="1"
+ style="stop-color:#4e9a06;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2761"
+ inkscape:collect="always">
+ <stop
+ id="stop2763"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1" />
+ <stop
+ id="stop2765"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2755"
+ inkscape:collect="always">
+ <stop
+ id="stop2757"
+ offset="0"
+ style="stop-color:#4e9a06;stop-opacity:1" />
+ <stop
+ id="stop2759"
+ offset="1"
+ style="stop-color:#4e9a06;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2749"
+ inkscape:collect="always">
+ <stop
+ id="stop2751"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1" />
+ <stop
+ id="stop2753"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2743"
+ inkscape:collect="always">
+ <stop
+ id="stop2745"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2747"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4225">
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="0"
+ id="stop4227" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop4229" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4179">
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:1;"
+ offset="0"
+ id="stop4181" />
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:0;"
+ offset="1"
+ id="stop4183" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4171">
+ <stop
+ style="stop-color:#888a85;stop-opacity:1;"
+ offset="0"
+ id="stop4173" />
+ <stop
+ style="stop-color:#888a85;stop-opacity:0;"
+ offset="1"
+ id="stop4175" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4053">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop4055" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop4057" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3648">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3650" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop3652" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3529">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3531" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3533" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3354">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop3356" />
+ <stop
+ id="stop3362"
+ offset="0.3253012"
+ style="stop-color:#6e706c;stop-opacity:1;" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1"
+ offset="1"
+ id="stop3358" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(48,3)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(48,-1)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(0,4)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,0,0)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2785"
+ id="radialGradient5350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.6485415,0,0,1.0712181,-74.997522,74.840854)"
+ cx="23.125"
+ cy="-2.4186027"
+ fx="23.125"
+ fy="-2.4186027"
+ r="13.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3648"
+ id="radialGradient5353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.9868421,0,0,1.1196908,12.080591,63.193551)"
+ cx="-21.745861"
+ cy="13.112056"
+ fx="-21.745861"
+ fy="13.112056"
+ r="9.500001" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2773"
+ id="radialGradient4051"
+ cx="8.4375"
+ cy="3.0789473"
+ fx="8.4375"
+ fy="3.0789473"
+ r="8"
+ gradientTransform="matrix(1.8518262,0,0,1.0938616,-67.187283,28.507058)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4053"
+ id="radialGradient4059"
+ cx="11.25"
+ cy="6.0882354"
+ fx="11.25"
+ fy="6.0882354"
+ r="6"
+ gradientTransform="matrix(1.6565447,0,0,1.1458807,-67.386127,27.273609)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4179"
+ id="linearGradient4207"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-35,16)"
+ x1="2"
+ y1="10.53125"
+ x2="-0.00024412572"
+ y2="10.53125" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4171"
+ id="linearGradient4209"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-35,16)"
+ x1="1.46875"
+ y1="9.46875"
+ x2="0.029891947"
+ y2="9.46875" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2761"
+ id="linearGradient4211"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-39,16)"
+ x1="1.6875"
+ y1="12.5"
+ x2="0.0625"
+ y2="12.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2767"
+ id="linearGradient4213"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-39,16)"
+ x1="1.4375"
+ y1="13.5"
+ x2="-0.033446059"
+ y2="13.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2743"
+ id="radialGradient4215"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.9174614,0,0,1.044681,-34.816672,-1.1654575)"
+ cx="5.25"
+ cy="3.8676469"
+ fx="5.25"
+ fy="3.8676469"
+ r="7" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4179"
+ id="linearGradient4217"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="2"
+ y1="10.53125"
+ x2="-0.00024412572"
+ y2="10.53125" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4171"
+ id="linearGradient4219"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="1.46875"
+ y1="9.46875"
+ x2="0.029891947"
+ y2="9.46875" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2749"
+ id="linearGradient4221"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="1.6875"
+ y1="12.5"
+ x2="0.0625"
+ y2="12.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2755"
+ id="linearGradient4223"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="1.4375"
+ y1="13.5"
+ x2="-0.033446059"
+ y2="13.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4225"
+ id="linearGradient4231"
+ x1="-47.747299"
+ y1="42.6875"
+ x2="-54.256195"
+ y2="42.6875"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2779"
+ id="linearGradient4239"
+ x1="-47.049999"
+ y1="44.505241"
+ x2="-61.469978"
+ y2="44.505241"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#787878"
+ borderopacity="1"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="54.456554"
+ inkscape:cy="-37.601826"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="16px"
+ height="16px"
+ inkscape:showpageshadow="false"
+ inkscape:window-width="872"
+ inkscape:window-height="751"
+ inkscape:window-x="517"
+ inkscape:window-y="133"
+ showgrid="false"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px"
+ gridempspacing="2"
+ inkscape:grid-points="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ showborder="false" />
+ <metadata
+ id="metadata7859">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Lapo Calamandrei</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:source />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+ <dc:title>Patchage</dc:title>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>patches</rdf:li>
+ <rdf:li>audio</rdf:li>
+ <rdf:li>cables</rdf:li>
+ <rdf:li>jacks</rdf:li>
+ <rdf:li>jack</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/SourceCode" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <g
+ id="g4481"
+ style="opacity:0.2"
+ transform="matrix(2,0,0,2,-394,6.000001)" />
+ <g
+ id="g4199"
+ transform="translate(30,0)">
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path4187"
+ d="M -23.5,11 L -23.5,16.5 L -25.5,16.5 L -25.5,11"
+ style="color:#000000;fill:url(#linearGradient4207);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4209);stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccccssccccsc"
+ id="path4122"
+ d="M -21,2.5 C -23.474439,2.5 -25.499999,4.5255603 -25.499999,7 C -25.499999,7 -25.5,13.5 -25.5,13.5 L -23.491072,13.5 C -23.491072,13.5 -23.491071,7 -23.491071,7 C -23.491071,5.5950118 -22.404987,4.5089286 -21,4.5089286 C -19.595013,4.5089286 -18.508929,5.5950118 -18.508929,7 C -18.508929,8.4049883 -19.595013,9.4910715 -21,9.4910715 C -21,9.4910715 -23.5,9.4910715 -23.5,9.4910715 L -23.5,11.5 C -23.5,11.5 -21,11.5 -21,11.5 C -18.525561,11.5 -16.5,9.4744398 -16.5,7 C -16.5,4.5255603 -18.525561,2.5 -21,2.5 z "
+ style="color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path4189"
+ d="M -25.5,10 L -25.5,16.5 L -27.5,16.5 L -27.5,10"
+ style="color:#000000;fill:url(#linearGradient4211);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4213);stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccccssccccsc"
+ id="path4120"
+ d="M -21,0.5 C -24.571302,0.50000003 -27.5,3.4293124 -27.5,7 C -27.5,7 -27.5,13.5 -27.5,13.5 L -25.5,13.5 C -25.5,13.5 -25.5,7 -25.5,7 C -25.5,4.5224862 -23.478823,2.5 -21,2.5 C -18.521177,2.5 -16.5,4.5224863 -16.5,7 C -16.5,9.4775136 -18.521176,11.5 -21,11.5 C -21,11.5 -25.5,11.5 -25.5,11.5 L -25.5,13.5 C -25.5,13.5 -21,13.5 -21,13.5 C -17.428698,13.5 -14.5,10.570687 -14.5,7 C -14.5,3.4293124 -17.428698,0.5 -21,0.5 z "
+ style="color:#000000;fill:url(#radialGradient4215);fill-opacity:1;fill-rule:evenodd;stroke:#4e9a06;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="rect4137"
+ d="M -22,11.5 L -30.5,11.5 L -30.5,9.5 L -22,9.5"
+ style="color:#000000;fill:url(#linearGradient4217);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4219);stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="rect4135"
+ d="M -22,13.5 L -30.5,13.5 L -30.5,11.5 L -22,11.5"
+ style="color:#000000;fill:url(#linearGradient4221);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4223);stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ </g>
+</svg>
diff --git a/icons/22x22/patchage.png b/icons/22x22/patchage.png
new file mode 100644
index 0000000..8188d31
--- /dev/null
+++ b/icons/22x22/patchage.png
Binary files differ
diff --git a/icons/22x22/patchage.svg b/icons/22x22/patchage.svg
new file mode 100644
index 0000000..819ae1f
--- /dev/null
+++ b/icons/22x22/patchage.svg
@@ -0,0 +1,349 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="22"
+ height="22"
+ id="svg7854"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/lapo/Scrivania/patchage/22x22"
+ sodipodi:docname="patchage.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs7856">
+ <linearGradient
+ id="linearGradient2779"
+ inkscape:collect="always">
+ <stop
+ id="stop2781"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1" />
+ <stop
+ id="stop2783"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2773"
+ inkscape:collect="always">
+ <stop
+ id="stop2775"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2777"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4225">
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="0"
+ id="stop4227" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop4229" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4053">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop4055" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop4057" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2773"
+ id="radialGradient4051"
+ cx="8.4375"
+ cy="3.0789473"
+ fx="8.4375"
+ fy="3.0789473"
+ r="8"
+ gradientTransform="matrix(1.8518262,0,0,1.0938616,-67.187283,28.507058)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4053"
+ id="radialGradient4059"
+ cx="11.25"
+ cy="6.0882354"
+ fx="11.25"
+ fy="6.0882354"
+ r="6"
+ gradientTransform="matrix(1.6565447,0,0,1.1458807,-67.386127,27.273609)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4225"
+ id="linearGradient4231"
+ x1="-47.747299"
+ y1="42.6875"
+ x2="-54.256195"
+ y2="42.6875"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2779"
+ id="linearGradient4239"
+ x1="-47.049999"
+ y1="44.505241"
+ x2="-61.469978"
+ y2="44.505241"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#787878"
+ borderopacity="1"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="9.4874329"
+ inkscape:cy="5.9224321"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="22px"
+ height="22px"
+ inkscape:showpageshadow="false"
+ inkscape:window-width="872"
+ inkscape:window-height="751"
+ inkscape:window-x="517"
+ inkscape:window-y="133"
+ showgrid="false"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px"
+ gridempspacing="2"
+ inkscape:grid-points="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ showborder="false" />
+ <metadata
+ id="metadata7859">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Lapo Calamandrei</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:source />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+ <dc:title>Patchage</dc:title>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>patches</rdf:li>
+ <rdf:li>audio</rdf:li>
+ <rdf:li>cables</rdf:li>
+ <rdf:li>jacks</rdf:li>
+ <rdf:li>jack</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/SourceCode" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <g
+ id="g4481"
+ style="opacity:0.2"
+ transform="matrix(2,0,0,2,-394,6.000001)" />
+ <g
+ id="g3234"
+ style="opacity:0.2">
+ <g
+ style="opacity:1"
+ transform="translate(0,1)"
+ id="g2250">
+ <path
+ transform="translate(60,-30)"
+ sodipodi:nodetypes="cccccssccccsc"
+ id="path2246"
+ d="M -46,32.5 C -49.02789,32.5 -51.5,34.97211 -51.5,38 C -51.5,38 -51.5,48.5 -51.5,48.5 L -49.5,48.5 C -49.5,48.5 -49.5,38 -49.5,38 C -49.5,36.048907 -47.951092,34.5 -46,34.5 C -44.048908,34.5 -42.5,36.048907 -42.5,38 C -42.5,39.951093 -44.048908,41.5 -46,41.5 C -46,41.5 -48.5,41.5 -48.5,41.5 L -48.5,43.5 C -48.5,43.5 -46,43.5 -46,43.5 C -42.97211,43.5 -40.5,41.02789 -40.5,38 C -40.5,34.97211 -42.97211,32.5 -46,32.5 z "
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ transform="translate(60,-30)"
+ sodipodi:nodetypes="cccccssccccsc"
+ id="path2248"
+ d="M -46,30.5 C -50.121819,30.5 -53.5,33.878182 -53.5,38 C -53.5,38 -53.5,48.5 -53.5,48.5 L -51.5,48.5 C -51.5,48.5 -51.5,38 -51.5,38 C -51.5,34.967693 -49.032307,32.5 -46,32.5 C -42.967693,32.5 -40.5,34.967693 -40.5,38 C -40.5,41.032307 -42.967692,43.5 -46,43.5 C -46,43.5 -48.5,43.5 -48.5,43.5 L -48.5,45.5 C -48.5,45.5 -46,45.5 -46,45.5 C -41.878182,45.5 -38.5,42.121818 -38.5,38 C -38.5,33.878182 -41.878181,30.5 -46,30.5 z "
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <path
+ id="rect3227"
+ d="M 1.5,14.5 C 0.948,14.5 0.5,14.948 0.5,15.5 C 0.5,16.052 0.948,16.5 1.5,16.5 C 2.052,16.5 2.5,16.052 2.5,15.5 C 2.5,14.948 2.052,14.5 1.5,14.5 z M 2.5,15.5 L 2.5,16.5 L 4.5,16.5 L 7.5,16.5 L 7.5,14.5 L 4.5,14.5 L 2.5,14.5 L 2.5,15.5 z "
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g4249"
+ transform="translate(60,-30)">
+ <path
+ sodipodi:nodetypes="cccccssccccsc"
+ id="path3981"
+ d="M -46,32.5 C -49.02789,32.5 -51.5,34.97211 -51.5,38 C -51.5,38 -51.5,48.5 -51.5,48.5 L -49.5,48.5 C -49.5,48.5 -49.5,38 -49.5,38 C -49.5,36.048907 -47.951092,34.5 -46,34.5 C -44.048908,34.5 -42.5,36.048907 -42.5,38 C -42.5,39.951093 -44.048908,41.5 -46,41.5 C -46,41.5 -48.5,41.5 -48.5,41.5 L -48.5,43.5 C -48.5,43.5 -46,43.5 -46,43.5 C -42.97211,43.5 -40.5,41.02789 -40.5,38 C -40.5,34.97211 -42.97211,32.5 -46,32.5 z "
+ style="color:#000000;fill:url(#radialGradient4059);fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <g
+ transform="matrix(0,-1,1,0,-65,52)"
+ id="g4035">
+ <rect
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect4031"
+ width="2"
+ height="2"
+ x="2.5"
+ y="13.5" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.25;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path4033"
+ sodipodi:cx="1.75"
+ sodipodi:cy="12.25"
+ sodipodi:rx="1.25"
+ sodipodi:ry="1.25"
+ d="M 3 12.25 A 1.25 1.25 0 1 1 0.5,12.25 A 1.25 1.25 0 1 1 3 12.25 z"
+ transform="matrix(0.8,0,0,0.8,0.1,4.7)" />
+ </g>
+ <path
+ sodipodi:nodetypes="cccccssccccsc"
+ id="path3973"
+ d="M -46,30.5 C -50.121819,30.5 -53.5,33.878182 -53.5,38 C -53.5,38 -53.5,48.5 -53.5,48.5 L -51.5,48.5 C -51.5,48.5 -51.5,38 -51.5,38 C -51.5,34.967693 -49.032307,32.5 -46,32.5 C -42.967693,32.5 -40.5,34.967693 -40.5,38 C -40.5,41.032307 -42.967692,43.5 -46,43.5 C -46,43.5 -48.5,43.5 -48.5,43.5 L -48.5,45.5 C -48.5,45.5 -46,45.5 -46,45.5 C -41.878182,45.5 -38.5,42.121818 -38.5,38 C -38.5,33.878182 -41.878181,30.5 -46,30.5 z "
+ style="color:#000000;fill:url(#radialGradient4051);fill-opacity:1;fill-rule:evenodd;stroke:#4e9a06;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <g
+ transform="matrix(0,-1,1,0,-67,52)"
+ id="g4039">
+ <rect
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect4041"
+ width="2"
+ height="2"
+ x="2.5"
+ y="13.5" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.25;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path4043"
+ sodipodi:cx="1.75"
+ sodipodi:cy="12.25"
+ sodipodi:rx="1.25"
+ sodipodi:ry="1.25"
+ d="M 3 12.25 A 1.25 1.25 0 1 1 0.5,12.25 A 1.25 1.25 0 1 1 3 12.25 z"
+ transform="matrix(0.8,0,0,0.8,0.1,4.7)" />
+ </g>
+ <rect
+ y="45.5"
+ x="-53.5"
+ height="2"
+ width="2"
+ id="rect4019"
+ style="opacity:1;color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="rect4009"
+ d="M -47.5,43.5 L -56.5,43.5 L -56.5,41.5 L -47.5,41.5"
+ style="color:#000000;fill:url(#linearGradient4231);fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ y="45.5"
+ x="-51.5"
+ height="2"
+ width="2"
+ id="rect4021"
+ style="opacity:1;color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path4012"
+ d="M -46.5,45.5 L -56.5,45.5 L -56.5,43.5 L -46.5,43.5"
+ style="color:#000000;fill:url(#linearGradient4239);fill-opacity:1;fill-rule:evenodd;stroke:#4e9a06;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ y="41.5"
+ x="-57.5"
+ height="2"
+ width="2"
+ id="rect4025"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ y="43.5"
+ x="-57.5"
+ height="2"
+ width="2"
+ id="rect4027"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ y="41.5"
+ x="-55.500004"
+ height="2"
+ width="3"
+ id="rect4014"
+ style="opacity:1;color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ transform="matrix(0.8,0,0,0.8,-59.9,34.7)"
+ d="M 3 12.25 A 1.25 1.25 0 1 1 0.5,12.25 A 1.25 1.25 0 1 1 3 12.25 z"
+ sodipodi:ry="1.25"
+ sodipodi:rx="1.25"
+ sodipodi:cy="12.25"
+ sodipodi:cx="1.75"
+ id="path4029"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.25;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:type="arc" />
+ <rect
+ y="43.5"
+ x="-55.500004"
+ height="2"
+ width="3"
+ id="rect4017"
+ style="opacity:1;color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ transform="matrix(0.8,0,0,0.8,-59.9,32.7)"
+ d="M 3 12.25 A 1.25 1.25 0 1 1 0.5,12.25 A 1.25 1.25 0 1 1 3 12.25 z"
+ sodipodi:ry="1.25"
+ sodipodi:rx="1.25"
+ sodipodi:cy="12.25"
+ sodipodi:cx="1.75"
+ id="path4023"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.25;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:type="arc" />
+ </g>
+ </g>
+</svg>
diff --git a/icons/24x24/patchage.png b/icons/24x24/patchage.png
new file mode 100644
index 0000000..96d6a5f
--- /dev/null
+++ b/icons/24x24/patchage.png
Binary files differ
diff --git a/icons/256x256/patchage.png b/icons/256x256/patchage.png
new file mode 100644
index 0000000..85070ff
--- /dev/null
+++ b/icons/256x256/patchage.png
Binary files differ
diff --git a/icons/32x32/patchage.png b/icons/32x32/patchage.png
new file mode 100644
index 0000000..6a48838
--- /dev/null
+++ b/icons/32x32/patchage.png
Binary files differ
diff --git a/icons/32x32/patchage.svg b/icons/32x32/patchage.svg
new file mode 100644
index 0000000..6cb4d44
--- /dev/null
+++ b/icons/32x32/patchage.svg
@@ -0,0 +1,1082 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32"
+ height="32"
+ id="svg7854"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/lapo/Scrivania/patchage/32x32"
+ sodipodi:docname="patchage.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs7856">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3717">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3719" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop3721" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3591">
+ <stop
+ style="stop-color:#73d216;stop-opacity:1;"
+ offset="0"
+ id="stop3593" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop3595" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3583">
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1;"
+ offset="0"
+ id="stop3585" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop3587" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3567">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3569" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop3571" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3559">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3561" />
+ <stop
+ style="stop-color:#73d216;stop-opacity:1"
+ offset="1"
+ id="stop3563" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2785"
+ inkscape:collect="always">
+ <stop
+ id="stop2787"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2789"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2779"
+ inkscape:collect="always">
+ <stop
+ id="stop2781"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1" />
+ <stop
+ id="stop2783"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2773"
+ inkscape:collect="always">
+ <stop
+ id="stop2775"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2777"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2767"
+ inkscape:collect="always">
+ <stop
+ id="stop2769"
+ offset="0"
+ style="stop-color:#4e9a06;stop-opacity:1" />
+ <stop
+ id="stop2771"
+ offset="1"
+ style="stop-color:#4e9a06;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2761"
+ inkscape:collect="always">
+ <stop
+ id="stop2763"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1" />
+ <stop
+ id="stop2765"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2755"
+ inkscape:collect="always">
+ <stop
+ id="stop2757"
+ offset="0"
+ style="stop-color:#4e9a06;stop-opacity:1" />
+ <stop
+ id="stop2759"
+ offset="1"
+ style="stop-color:#4e9a06;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2749"
+ inkscape:collect="always">
+ <stop
+ id="stop2751"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1" />
+ <stop
+ id="stop2753"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2743"
+ inkscape:collect="always">
+ <stop
+ id="stop2745"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2747"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4225">
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="0"
+ id="stop4227" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop4229" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4179">
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:1;"
+ offset="0"
+ id="stop4181" />
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:0;"
+ offset="1"
+ id="stop4183" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4171">
+ <stop
+ style="stop-color:#888a85;stop-opacity:1;"
+ offset="0"
+ id="stop4173" />
+ <stop
+ style="stop-color:#888a85;stop-opacity:0;"
+ offset="1"
+ id="stop4175" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4053">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop4055" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop4057" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3648">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3650" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop3652" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3529">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3531" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3533" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3354">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop3356" />
+ <stop
+ id="stop3362"
+ offset="0.3253012"
+ style="stop-color:#6e706c;stop-opacity:1;" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1"
+ offset="1"
+ id="stop3358" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(48,3)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(48,-1)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(0,4)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient4921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,0,0)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient4925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2785"
+ id="radialGradient5350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.6485415,0,0,1.0712181,-74.997522,74.840854)"
+ cx="23.125"
+ cy="-2.4186027"
+ fx="23.125"
+ fy="-2.4186027"
+ r="13.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3648"
+ id="radialGradient5353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.9868421,0,0,1.1196908,12.080591,63.193551)"
+ cx="-21.745861"
+ cy="13.112056"
+ fx="-21.745861"
+ fy="13.112056"
+ r="9.500001" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2773"
+ id="radialGradient4051"
+ cx="8.4375"
+ cy="3.0789473"
+ fx="8.4375"
+ fy="3.0789473"
+ r="8"
+ gradientTransform="matrix(1.8518262,0,0,1.0938616,-67.187283,28.507058)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4053"
+ id="radialGradient4059"
+ cx="11.25"
+ cy="6.0882354"
+ fx="11.25"
+ fy="6.0882354"
+ r="6"
+ gradientTransform="matrix(1.6565447,0,0,1.1458807,-67.386127,27.273609)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4179"
+ id="linearGradient4207"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-35,16)"
+ x1="2"
+ y1="10.53125"
+ x2="-0.00024412572"
+ y2="10.53125" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4171"
+ id="linearGradient4209"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-35,16)"
+ x1="1.46875"
+ y1="9.46875"
+ x2="0.029891947"
+ y2="9.46875" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2761"
+ id="linearGradient4211"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-39,16)"
+ x1="1.6875"
+ y1="12.5"
+ x2="0.0625"
+ y2="12.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2767"
+ id="linearGradient4213"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-1,1,0,-39,16)"
+ x1="1.4375"
+ y1="13.5"
+ x2="-0.033446059"
+ y2="13.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2743"
+ id="radialGradient4215"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.9174614,0,0,1.044681,-34.816672,-1.1654575)"
+ cx="5.25"
+ cy="3.8676469"
+ fx="5.25"
+ fy="3.8676469"
+ r="7" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4179"
+ id="linearGradient4217"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="2"
+ y1="10.53125"
+ x2="-0.00024412572"
+ y2="10.53125" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4171"
+ id="linearGradient4219"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="1.46875"
+ y1="9.46875"
+ x2="0.029891947"
+ y2="9.46875" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2749"
+ id="linearGradient4221"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="1.6875"
+ y1="12.5"
+ x2="0.0625"
+ y2="12.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2755"
+ id="linearGradient4223"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-30,0)"
+ x1="1.4375"
+ y1="13.5"
+ x2="-0.033446059"
+ y2="13.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4225"
+ id="linearGradient4231"
+ x1="-47.747299"
+ y1="42.6875"
+ x2="-54.256195"
+ y2="42.6875"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2779"
+ id="linearGradient4239"
+ x1="-47.049999"
+ y1="44.505241"
+ x2="-61.469978"
+ y2="44.505241"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2484"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-0.625,1,0,4,0.1875002)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2486"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-0.625,1,0,0,0.1875002)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2488"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2490"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2536"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.625,0,0,1,30.8125,-5)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2539"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.625,0,0,1,30.8125,-1)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2549"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(3.2860825,0,0,0.6554676,-19.185931,9.269849)"
+ cx="8.2330503"
+ cy="16.894657"
+ fx="8.2330503"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2553"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(3.2860825,0,0,0.6554676,-19.185931,9.269849)"
+ cx="8.2330503"
+ cy="16.894657"
+ fx="8.2330503"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3559"
+ id="radialGradient3565"
+ cx="16.836842"
+ cy="2.8636277"
+ fx="16.836842"
+ fy="2.8636277"
+ r="9.996094"
+ gradientTransform="matrix(2.3264074,0,0,1.0996483,-22.332512,-1.0957416)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3567"
+ id="radialGradient3573"
+ cx="19.633221"
+ cy="8.4071264"
+ fx="19.633221"
+ fy="8.4071264"
+ r="6.0016825"
+ gradientTransform="matrix(3.115365,0,0,1.4997617,-41.531427,-6.4924985)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3583"
+ id="linearGradient3589"
+ x1="19.25"
+ y1="14.5625"
+ x2="6.3123488"
+ y2="14.5625"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3591"
+ id="linearGradient3597"
+ x1="18.9375"
+ y1="18.5625"
+ x2="3.4938018"
+ y2="18.5625"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3717"
+ id="radialGradient3723"
+ cx="13.722291"
+ cy="29.083185"
+ fx="13.722291"
+ fy="29.083185"
+ r="1.7456698"
+ gradientTransform="matrix(1,0,0,1.6708861,0,-19.511505)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3717"
+ id="radialGradient3727"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1,0,0,1.6708861,0,-19.511505)"
+ cx="13.722291"
+ cy="29.083185"
+ fx="13.722291"
+ fy="29.083185"
+ r="1.7456698" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#787878"
+ borderopacity="1"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="21.182126"
+ inkscape:cy="1.7497561"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="32px"
+ height="32px"
+ inkscape:showpageshadow="false"
+ inkscape:window-width="872"
+ inkscape:window-height="751"
+ inkscape:window-x="517"
+ inkscape:window-y="133"
+ showgrid="false"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px"
+ gridempspacing="2"
+ inkscape:grid-points="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ showborder="false" />
+ <metadata
+ id="metadata7859">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Lapo Calamandrei</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:source />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+ <dc:title>Patchage</dc:title>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>patches</rdf:li>
+ <rdf:li>audio</rdf:li>
+ <rdf:li>cables</rdf:li>
+ <rdf:li>jacks</rdf:li>
+ <rdf:li>jack</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/SourceCode" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <g
+ id="g4481"
+ style="opacity:0.2"
+ transform="matrix(2,0,0,2,-394,6.000001)" />
+ <g
+ id="g2518"
+ style="opacity:0.2"
+ transform="matrix(0,1,-1,0,46,-5)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.15;color:#000000;fill:url(#radialGradient3727);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.93425632;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path3725"
+ sodipodi:cx="13.722291"
+ sodipodi:cy="29.083185"
+ sodipodi:rx="1.7456698"
+ sodipodi:ry="2.9168155"
+ d="M 15.467961 29.083185 A 1.7456698 2.9168155 0 1 1 11.976621,29.083185 A 1.7456698 2.9168155 0 1 1 15.467961 29.083185 z"
+ transform="matrix(1.4321149,0,0,1.1856793,-2.1518965,-5.4417382)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.15;color:#000000;fill:url(#radialGradient3723);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.93425632;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path3707"
+ sodipodi:cx="13.722291"
+ sodipodi:cy="29.083185"
+ sodipodi:rx="1.7456698"
+ sodipodi:ry="2.9168155"
+ d="M 15.467961 29.083185 A 1.7456698 2.9168155 0 1 1 11.976621,29.083185 A 1.7456698 2.9168155 0 1 1 15.467961 29.083185 z"
+ transform="matrix(1.4321149,0,0,1.1856793,-6.1518965,-5.4417382)" />
+ <g
+ id="g3693"
+ transform="translate(0,1)"
+ style="opacity:0.15">
+ <path
+ id="path3599"
+ d="M 22,0.50390625 C 16.766937,0.50390625 12.503906,4.7669372 12.503906,10 C 12.503906,10 12.503906,21.488281 12.503906,21.488281 L 14.496094,21.488281 C 14.496094,21.488281 14.496094,10 14.496094,10 C 14.496094,5.8490628 17.849063,2.4960938 22,2.4960938 C 26.150937,2.4960938 29.503906,5.8490628 29.503906,10 C 29.503906,14.150937 26.150937,17.503906 22,17.503906 C 22,17.503906 16.488281,17.503906 16.488281,17.503906 L 16.488281,19.496094 C 16.488281,19.496094 22,19.496094 22,19.496094 C 27.233063,19.496094 31.496094,15.233063 31.496094,10 C 31.496094,4.7669372 27.233063,0.50390625 22,0.50390625 z "
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ id="path3601"
+ d="M 22.008222,4.4900948 C 18.958565,4.4900948 16.506539,6.9421211 16.506539,9.9917773 C 16.506539,9.9917773 16.506539,19.247385 16.506539,21.492282 L 18.495099,21.492282 C 18.495099,19.247385 18.495099,9.9917773 18.495099,9.9917773 C 18.495099,7.9867099 20.003154,6.4786547 22.008222,6.4786547 C 24.013289,6.4786547 25.521344,7.9867099 25.521344,9.9917773 C 25.521344,11.996845 24.013289,13.5049 22.008222,13.5049 C 22.008222,13.5049 19.489379,13.5049 19.489379,13.5049 L 19.489379,15.49346 C 19.489379,15.49346 22.008222,15.49346 22.008222,15.49346 C 25.057878,15.49346 27.509904,13.041434 27.509904,9.9917773 C 27.509904,6.9421211 25.057878,4.4900948 22.008222,4.4900948 z "
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path3603"
+ d="M 21.5,15.5 L 10.5,15.5 L 10.5,13.5 L 21.5,13.5"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="path3605"
+ d="M 21.5,19.5 L 10.5,19.5 L 10.5,17.5 L 21.5,17.5"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 15.5,26.499999 L 15.5,22.467322 C 15.5,21.931425 16.190281,21.499999 17.047716,21.499999 L 17.952284,21.499999 C 18.809719,21.499999 19.5,21.931425 19.5,22.467322 L 19.5,26.499999 L 15.5,26.499999 z "
+ id="path3607"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 11.5,26.499999 L 11.5,22.467322 C 11.5,21.931425 12.190281,21.499999 13.047716,21.499999 L 13.952284,21.499999 C 14.809719,21.499999 15.5,21.931425 15.5,22.467322 L 15.5,26.499999 L 11.5,26.499999 z "
+ id="path3609"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ ry="1"
+ rx="1"
+ y="17.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect3619"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="17.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect3621"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="13.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect3623"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="13.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect3625"
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 5.500001,16.5 L 9.532678,16.5 C 10.068575,16.5 10.500001,17.190281 10.500001,18.047716 L 10.500001,18.952284 C 10.500001,19.809719 10.068575,20.5 9.532678,20.5 L 5.500001,20.5 L 5.500001,16.5 z "
+ id="path3627"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 5.500001,12.5 L 9.532678,12.5 C 10.068575,12.5 10.500001,13.190281 10.500001,14.047716 L 10.500001,14.952284 C 10.500001,15.809719 10.068575,16.5 9.532678,16.5 L 5.500001,16.5 L 5.500001,12.5 z "
+ id="path3629"
+ sodipodi:nodetypes="ccccccc" />
+ </g>
+ <path
+ style="opacity:1;color:#000000;fill:url(#radialGradient3565);fill-opacity:1.0;fill-rule:evenodd;stroke:#4e9a06;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 22,0.50390625 C 16.766937,0.50390625 12.503906,4.7669372 12.503906,10 C 12.503906,10 12.503906,21.488281 12.503906,21.488281 L 14.496094,21.488281 C 14.496094,21.488281 14.496094,10 14.496094,10 C 14.496094,5.8490628 17.849063,2.4960938 22,2.4960938 C 26.150937,2.4960938 29.503906,5.8490628 29.503906,10 C 29.503906,14.150937 26.150937,17.503906 22,17.503906 C 22,17.503906 16.488281,17.503906 16.488281,17.503906 L 16.488281,19.496094 C 16.488281,19.496094 22,19.496094 22,19.496094 C 27.233063,19.496094 31.496094,15.233063 31.496094,10 C 31.496094,4.7669372 27.233063,0.50390625 22,0.50390625 z "
+ id="path2569" />
+ <path
+ style="opacity:1;color:#000000;fill:url(#radialGradient3573);fill-opacity:1.0;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 22.008222,4.4900948 C 18.958565,4.4900948 16.506539,6.9421211 16.506539,9.9917773 C 16.506539,9.9917773 16.506539,19.247385 16.506539,21.492282 L 18.495099,21.492282 C 18.495099,19.247385 18.495099,9.9917773 18.495099,9.9917773 C 18.495099,7.9867099 20.003154,6.4786547 22.008222,6.4786547 C 24.013289,6.4786547 25.521344,7.9867099 25.521344,9.9917773 C 25.521344,11.996845 24.013289,13.5049 22.008222,13.5049 C 22.008222,13.5049 19.489379,13.5049 19.489379,13.5049 L 19.489379,15.49346 C 19.489379,15.49346 22.008222,15.49346 22.008222,15.49346 C 25.057878,15.49346 27.509904,13.041434 27.509904,9.9917773 C 27.509904,6.9421211 25.057878,4.4900948 22.008222,4.4900948 z "
+ id="path2574" />
+ <path
+ style="color:#000000;fill:url(#linearGradient3589);fill-opacity:1.0;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 21.5,15.5 L 10.5,15.5 L 10.5,13.5 L 21.5,13.5"
+ id="rect3550"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="color:#000000;fill:url(#linearGradient3597);fill-opacity:1.0;fill-rule:evenodd;stroke:#4e9a06;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 21.5,19.5 L 10.5,19.5 L 10.5,17.5 L 21.5,17.5"
+ id="path3557"
+ sodipodi:nodetypes="cccc" />
+ <g
+ id="g2467"
+ transform="translate(-9,1)">
+ <g
+ style="stroke:#888a85"
+ transform="matrix(0,-1,1,0,1.9999999,31)"
+ id="g2336">
+ <rect
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2338"
+ width="6"
+ height="2"
+ x="2.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ <rect
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2340"
+ width="2"
+ height="2"
+ x="0.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ </g>
+ <g
+ style="stroke:#888a85"
+ transform="matrix(0,-1,1,0,-2.0000001,31)"
+ id="g2342">
+ <rect
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2344"
+ width="6"
+ height="2"
+ x="2.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ <rect
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2346"
+ width="2"
+ height="2"
+ x="0.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ </g>
+ <rect
+ transform="matrix(0,-1,1,0,0,0)"
+ ry="0"
+ rx="0"
+ y="25.5"
+ x="-24.5"
+ height="2"
+ width="5"
+ id="rect2348"
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ transform="matrix(0,-1,1,0,0,0)"
+ ry="0"
+ rx="0"
+ y="21.5"
+ x="-24.5"
+ height="2"
+ width="5"
+ id="rect2350"
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ id="path2352"
+ d="M 24.5,25.499999 L 24.5,21.467322 C 24.5,20.931425 25.190281,20.499999 26.047716,20.499999 L 26.952284,20.499999 C 27.809719,20.499999 28.5,20.931425 28.5,21.467322 L 28.5,25.499999 L 24.5,25.499999 z "
+ style="color:#000000;fill:url(#linearGradient2484);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ id="path2354"
+ d="M 20.5,25.499999 L 20.5,21.467322 C 20.5,20.931425 21.190281,20.499999 22.047716,20.499999 L 22.952284,20.499999 C 23.809719,20.499999 24.5,20.931425 24.5,21.467322 L 24.5,25.499999 L 20.5,25.499999 z "
+ style="color:#000000;fill:url(#linearGradient2486);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ transform="matrix(0,-0.5,1,0,1,28.75)"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ id="path2356"
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2488);stroke-width:1.41421366;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ inkscape:radius="-1"
+ sodipodi:type="inkscape:offset" />
+ <path
+ transform="matrix(0,-0.5,1,0,5,28.75)"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ id="path2358"
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2490);stroke-width:1.41421366;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ inkscape:radius="-1"
+ sodipodi:type="inkscape:offset" />
+ <g
+ transform="translate(0,-15)"
+ style="opacity:0.2"
+ id="g2360">
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2362"
+ width="3"
+ height="1"
+ x="21"
+ y="41" />
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2364"
+ width="3"
+ height="1"
+ x="25"
+ y="41" />
+ </g>
+ </g>
+ <g
+ id="g2555"
+ transform="translate(0,-3)">
+ <g
+ style="stroke:#888a85"
+ transform="translate(0,-3.0000001)"
+ id="g2494">
+ <rect
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2496"
+ width="6"
+ height="2"
+ x="2.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ <rect
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2498"
+ width="2"
+ height="2"
+ x="0.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ </g>
+ <g
+ style="stroke:#888a85"
+ transform="translate(0,-7.0000001)"
+ id="g2500">
+ <rect
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2502"
+ width="6"
+ height="2"
+ x="2.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ <rect
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2504"
+ width="2"
+ height="2"
+ x="0.5"
+ y="23.5"
+ rx="1"
+ ry="1" />
+ </g>
+ <rect
+ ry="0"
+ rx="0"
+ y="20.5"
+ x="6.5"
+ height="2"
+ width="5"
+ id="rect2506"
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="0"
+ rx="0"
+ y="16.5"
+ x="6.5"
+ height="2"
+ width="5"
+ id="rect2508"
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ id="path2510"
+ d="M 5.500001,19.5 L 9.532678,19.5 C 10.068575,19.5 10.500001,20.190281 10.500001,21.047716 L 10.500001,21.952284 C 10.500001,22.809719 10.068575,23.5 9.532678,23.5 L 5.500001,23.5 L 5.500001,19.5 z "
+ style="color:#000000;fill:url(#linearGradient2539);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ sodipodi:nodetypes="ccccccc"
+ id="path2512"
+ d="M 5.500001,15.5 L 9.532678,15.5 C 10.068575,15.5 10.500001,16.190281 10.500001,17.047716 L 10.500001,17.952284 C 10.500001,18.809719 10.068575,19.5 9.532678,19.5 L 5.500001,19.5 L 5.500001,15.5 z "
+ style="color:#000000;fill:url(#linearGradient2536);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <path
+ transform="matrix(0.5,0,0,1,2.25,0)"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ id="path2516"
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2549);stroke-width:1.41421366;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ inkscape:radius="-1"
+ sodipodi:type="inkscape:offset" />
+ <path
+ transform="matrix(0.5,0,0,1,2.25,-4)"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ id="path2551"
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2553);stroke-width:1.41421366;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ inkscape:radius="-1"
+ sodipodi:type="inkscape:offset" />
+ </g>
+ <g
+ id="g3579"
+ style="opacity:0.2">
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3575"
+ width="3"
+ height="1"
+ x="12"
+ y="16" />
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3577"
+ width="3"
+ height="1"
+ x="16"
+ y="16" />
+ </g>
+ </g>
+</svg>
diff --git a/icons/48x48/patchage.png b/icons/48x48/patchage.png
new file mode 100644
index 0000000..43d863d
--- /dev/null
+++ b/icons/48x48/patchage.png
Binary files differ
diff --git a/icons/48x48/patchage.svg b/icons/48x48/patchage.svg
new file mode 100644
index 0000000..cf2c26c
--- /dev/null
+++ b/icons/48x48/patchage.svg
@@ -0,0 +1,595 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48"
+ height="48"
+ id="svg7854"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/lapo/Scrivania/patchage/48x48"
+ sodipodi:docname="patchage.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs7856">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3469">
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1;"
+ offset="0"
+ id="stop3471" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop3473" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3461">
+ <stop
+ style="stop-color:#73d216;stop-opacity:1;"
+ offset="0"
+ id="stop3463" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop3465" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2785"
+ inkscape:collect="always">
+ <stop
+ id="stop2787"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2789"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3648">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3650" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop3652" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3529">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3531" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3533" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3354">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop3356" />
+ <stop
+ id="stop3362"
+ offset="0.3253012"
+ style="stop-color:#6e706c;stop-opacity:1;" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1"
+ offset="1"
+ id="stop3358" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2785"
+ id="radialGradient2424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.6485415,0,0,1.0712181,-14.997522,4.840854)"
+ cx="23.125"
+ cy="-2.4186027"
+ fx="23.125"
+ fy="-2.4186027"
+ r="13.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3648"
+ id="radialGradient2427"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.9868421,0,0,1.1196908,72.080591,-6.806449)"
+ cx="-21.745861"
+ cy="13.112056"
+ fx="-21.745861"
+ fy="13.112056"
+ r="9.500001" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2440"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.875,0,0,1,42.9375,-1)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2444"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.875,0,0,1,42.9375,3)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2453"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2455"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2471"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-0.875,1,0,0,5.0625)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2474"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-0.875,1,0,4,5.0625)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2484"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2486"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3461"
+ id="linearGradient3467"
+ x1="29.5"
+ y1="25.4375"
+ x2="-1.3127575"
+ y2="25.4375"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3469"
+ id="linearGradient3475"
+ x1="30.375"
+ y1="21.5"
+ x2="7.625"
+ y2="21.5"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#787878"
+ borderopacity="1"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="29.936922"
+ inkscape:cy="46.491641"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="48px"
+ height="48px"
+ inkscape:showpageshadow="false"
+ inkscape:window-width="872"
+ inkscape:window-height="751"
+ inkscape:window-x="517"
+ inkscape:window-y="133"
+ showgrid="false"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px"
+ gridempspacing="2"
+ inkscape:grid-points="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ showborder="false" />
+ <metadata
+ id="metadata7859">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Lapo Calamandrei</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:source />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+ <dc:title>Patchage</dc:title>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>patches</rdf:li>
+ <rdf:li>audio</rdf:li>
+ <rdf:li>cables</rdf:li>
+ <rdf:li>jacks</rdf:li>
+ <rdf:li>jack</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/SourceCode" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <path
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;opacity:0.15"
+ d="M 34.5,1.5 C 27.29932,1.5 21.5,7.29932 21.5,14.5 C 21.5,14.5 21.5,19.270322 21.5,21.5 L 14.40625,21.5 C 14.210438,20.924291 13.728872,20.5 13.15625,20.5 L 7.5,20.5 L 7.5,21.5 L 3.5,21.5 C 2.946,21.5 2.5,21.946 2.5,22.5 C 2.5,23.054 2.946,23.5 3.5,23.5 L 7.5,23.5 L 7.5,24.5 L 7.5,25.5 L 3.5,25.5 C 2.946,25.5 2.5,25.946 2.5,26.5 C 2.5,27.054 2.946,27.5 3.5,27.5 L 7.5,27.5 L 7.5,28.5 L 13.15625,28.5 C 13.728872,28.5 14.210438,28.075709 14.40625,27.5 L 21.5,27.5 C 21.5,30.649407 21.5,31.638905 21.5,34.59375 C 20.924291,34.789562 20.5,35.271128 20.5,35.84375 L 20.5,41.5 L 21.5,41.5 L 21.5,44.5 C 21.5,45.054 21.946,45.5 22.5,45.5 C 23.054,45.5 23.5,45.054 23.5,44.5 L 23.5,41.5 L 24.5,41.5 L 25.5,41.5 L 25.5,44.5 C 25.5,45.054 25.946,45.5 26.5,45.5 C 27.054,45.5 27.5,45.054 27.5,44.5 L 27.5,41.5 L 28.5,41.5 L 28.5,35.84375 C 28.5,35.271128 28.075709,34.789562 27.5,34.59375 C 27.5,33.027561 27.5,30.986531 27.5,27.5 L 32.5,27.5 C 32.795145,27.5 34.5,27.5 34.5,27.5 C 41.700681,27.5 47.5,21.700681 47.5,14.5 C 47.500001,7.29932 41.70068,1.5 34.5,1.5 z M 26.5,45.5 C 25.946,45.5 25.5,45.946 25.5,46.5 C 25.5,47.054 25.946,47.5 26.5,47.5 C 27.054,47.5 27.5,47.054 27.5,46.5 C 27.5,45.946 27.054,45.5 26.5,45.5 z M 22.5,45.5 C 21.946,45.5 21.5,45.946 21.5,46.5 C 21.5,47.054 21.946,47.5 22.5,47.5 C 23.054,47.5 23.5,47.054 23.5,46.5 C 23.5,45.946 23.054,45.5 22.5,45.5 z M 2.5,26.5 C 2.5,25.946 2.054,25.5 1.5,25.5 C 0.946,25.5 0.5,25.946 0.5,26.5 C 0.5,27.054 0.946,27.5 1.5,27.5 C 2.054,27.5 2.5,27.054 2.5,26.5 z M 2.5,22.5 C 2.5,21.946 2.054,21.5 1.5,21.5 C 0.946,21.5 0.5,21.946 0.5,22.5 C 0.5,23.054 0.946,23.5 1.5,23.5 C 2.054,23.5 2.5,23.054 2.5,22.5 z M 34.5,3.5 C 40.634313,3.5 45.5,8.365688 45.5,14.5 C 45.500001,20.634313 40.634312,25.5 34.5,25.5 C 34.5,25.5 32.795145,25.5 32.5,25.5 L 27.5,25.5 C 27.5,24.50189 27.5,24.452741 27.5,23.5 L 32.5,23.5 C 33.46,23.5 34.5,23.5 34.5,23.5 C 39.457063,23.5 43.5,19.457062 43.5,14.5 C 43.500001,9.542937 39.457063,5.5 34.5,5.5 C 29.542938,5.5 25.5,9.542937 25.5,14.5 C 25.5,14.5 25.5,18.535917 25.5,21.5 L 23.5,21.5 C 23.5,19.270322 23.5,14.5 23.5,14.5 C 23.5,8.365688 28.365687,3.5 34.5,3.5 z M 34.5,7.5 C 38.374938,7.5 41.5,10.625062 41.5,14.5 C 41.500001,18.374937 38.374938,21.5 34.5,21.5 C 34.5,21.5 33.46,21.5 32.5,21.5 L 27.5,21.5 C 27.5,18.535917 27.5,14.5 27.5,14.5 C 27.5,10.625062 30.625063,7.5 34.5,7.5 z M 14.40625,23.5 L 21.5,23.5 C 21.5,24.250953 21.5,24.665418 21.5,25.5 L 14.40625,25.5 C 14.210438,24.924291 13.728872,24.5 13.15625,24.5 C 13.728872,24.5 14.210438,24.075709 14.40625,23.5 z M 23.5,23.5 L 25.5,23.5 C 25.5,24.452741 25.5,24.50189 25.5,25.5 L 23.5,25.5 C 23.5,24.665418 23.5,24.250953 23.5,23.5 z M 23.5,27.5 L 25.5,27.5 C 25.5,30.986531 25.5,33.027561 25.5,34.59375 C 24.924291,34.789562 24.5,35.271128 24.5,35.84375 C 24.5,35.271128 24.075709,34.789562 23.5,34.59375 C 23.5,31.638905 23.5,30.649407 23.5,27.5 z "
+ id="path3477"
+ sodipodi:nodetypes="ccccccccscccccscccccccccccscccccsccccccccsccsssccsssccsssccsssccsccccccssccccccscccccccccscccccccccscc" />
+ <path
+ style="color:#000000;fill:url(#radialGradient2427);fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 34.5,4.499999 C 29.542938,4.499999 25.5,8.542937 25.5,13.5 C 25.5,13.5 25.5,36.5 25.5,36.5 L 27.5,36.5 C 27.5,36.5 27.5,13.5 27.5,13.5 C 27.5,9.625062 30.625063,6.499999 34.5,6.499999 C 38.374938,6.499999 41.500001,9.625062 41.500001,13.5 C 41.500001,17.374937 38.374938,20.5 34.5,20.5 C 34.5,20.5 29.5,20.5 29.5,20.5 L 29.5,22.5 C 29.5,22.5 34.5,22.5 34.5,22.5 C 39.457063,22.5 43.500001,18.457062 43.500001,13.5 C 43.500001,8.542937 39.457063,4.499999 34.5,4.499999 z "
+ id="path2579"
+ sodipodi:nodetypes="cccccssccccsc" />
+ <path
+ style="color:#000000;fill:url(#radialGradient2424);fill-opacity:1;fill-rule:evenodd;stroke:#4e9a06;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 34.500001,0.500001 C 27.29932,0.500001 21.5,6.299321 21.5,13.500001 C 21.5,13.500001 21.5,34.91358 21.5,40.5 L 23.5,40.5 C 23.5,34.91358 23.5,13.500001 23.5,13.500001 C 23.5,7.365689 28.365688,2.500001 34.500001,2.500001 C 40.634313,2.500001 45.500001,7.365689 45.500001,13.500001 C 45.500001,19.634314 40.634313,24.500001 34.500001,24.500001 C 34.500001,24.500001 33.086419,24.500001 27.5,24.500001 L 27.5,26.500002 C 33.086419,26.500002 34.500001,26.500002 34.500001,26.500002 C 41.700681,26.500002 47.500001,20.700682 47.500001,13.500001 C 47.500001,6.299321 41.700681,0.500001 34.500001,0.500001 z "
+ id="path2581" />
+ <path
+ style="color:#000000;fill:url(#linearGradient3475);fill-opacity:1.0;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 32.5,22.5 L 7.5,22.5 L 7.5,20.5 L 32.5,20.5"
+ id="path2583"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="color:#000000;fill:url(#linearGradient3467);fill-opacity:1.0;fill-rule:evenodd;stroke:#4e9a06;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 32.5,26.5 L 10.5,26.5 L 10.5,24.5 L 32.5,24.5"
+ id="path2585"
+ sodipodi:nodetypes="cccc" />
+ <g
+ id="g2589"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="20.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2591"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="20.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2593"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g2595"
+ transform="translate(0,1)"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2597"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2599"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2601"
+ width="5"
+ height="2"
+ x="13.5"
+ y="24.5"
+ rx="0"
+ ry="0" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2444);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 7.5,23.5 L 13.145748,23.5 C 13.896004,23.5 14.5,24.190281 14.5,25.047716 L 14.5,25.952284 C 14.5,26.809719 13.896004,27.5 13.145748,27.5 L 7.5,27.5 L 7.5,23.5 z "
+ id="path2603"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2605"
+ width="5"
+ height="2"
+ x="13.5"
+ y="20.5"
+ rx="0"
+ ry="0" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2440);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 7.5,19.5 L 13.145748,19.5 C 13.896004,19.5 14.5,20.190281 14.5,21.047716 L 14.5,21.952284 C 14.5,22.809719 13.896004,23.5 13.145748,23.5 L 7.5,23.5 L 7.5,19.5 z "
+ id="path2607"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2455);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2609"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0.8333333,0,0,1,1.4166667,0)" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2453);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2611"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0.8333333,0,0,1,1.4166667,4)" />
+ <g
+ id="g2615"
+ transform="matrix(0,-1,1,0,1.9999999,48)"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2617"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2619"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g2621"
+ transform="matrix(0,-1,1,0,-2.0000001,48)"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2623"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2625"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2627"
+ width="5"
+ height="2"
+ x="-34.5"
+ y="25.5"
+ rx="0"
+ ry="0"
+ transform="matrix(0,-1,1,0,0,0)" />
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2629"
+ width="5"
+ height="2"
+ x="-34.5"
+ y="21.5"
+ rx="0"
+ ry="0"
+ transform="matrix(0,-1,1,0,0,0)" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2474);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 24.5,40.5 L 24.5,34.854252 C 24.5,34.103996 25.190281,33.5 26.047716,33.5 L 26.952284,33.5 C 27.809719,33.5 28.5,34.103996 28.5,34.854252 L 28.5,40.5 L 24.5,40.5 z "
+ id="path2631"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2471);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 20.5,40.5 L 20.5,34.854252 C 20.5,34.103996 21.190281,33.5 22.047716,33.5 L 22.952284,33.5 C 23.809719,33.5 24.5,34.103996 24.5,34.854252 L 24.5,40.5 L 20.5,40.5 z "
+ id="path2633"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2484);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2635"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0,-0.8333333,1,0,1,46.583333)" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2486);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2637"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0,-0.8333333,1,0,5,46.583333)" />
+ <g
+ id="g2639"
+ style="opacity:0.2">
+ <rect
+ y="41"
+ x="21"
+ height="1"
+ width="3"
+ id="rect2641"
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ y="41"
+ x="25"
+ height="1"
+ width="3"
+ id="rect2643"
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g2645">
+ <g
+ transform="translate(0,-14)"
+ style="opacity:0.2"
+ id="g2647">
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2649"
+ width="3"
+ height="1"
+ x="21"
+ y="41" />
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2651"
+ width="3"
+ height="1"
+ x="25"
+ y="41" />
+ </g>
+ <g
+ transform="translate(0,-18)"
+ style="opacity:0.2"
+ id="g2653">
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2655"
+ width="3"
+ height="1"
+ x="21"
+ y="41" />
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2657"
+ width="3"
+ height="1"
+ x="25"
+ y="41" />
+ </g>
+ </g>
+ <g
+ id="g4481"
+ style="opacity:0.2"
+ transform="matrix(2,0,0,2,-394,6.000001)" />
+ <g
+ id="g2518"
+ style="opacity:0.2"
+ transform="matrix(0,1,-1,0,46,-5)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2488"
+ sodipodi:cx="3.5"
+ sodipodi:cy="21.5"
+ sodipodi:rx="0.5"
+ sodipodi:ry="0.5"
+ d="M 4 21.5 A 0.5 0.5 0 1 1 3,21.5 A 0.5 0.5 0 1 1 4 21.5 z"
+ transform="matrix(1.5,0,0,1.5,-2,-11)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path3459"
+ sodipodi:cx="3.5"
+ sodipodi:cy="21.5"
+ sodipodi:rx="0.5"
+ sodipodi:ry="0.5"
+ d="M 4 21.5 A 0.5 0.5 0 1 1 3,21.5 A 0.5 0.5 0 1 1 4 21.5 z"
+ transform="matrix(1.5,0,0,1.5,-2,-7)" />
+ </g>
+</svg>
diff --git a/icons/scalable/patchage.svg b/icons/scalable/patchage.svg
new file mode 100644
index 0000000..cf2c26c
--- /dev/null
+++ b/icons/scalable/patchage.svg
@@ -0,0 +1,595 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48"
+ height="48"
+ id="svg7854"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/lapo/Scrivania/patchage/48x48"
+ sodipodi:docname="patchage.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs7856">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3469">
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1;"
+ offset="0"
+ id="stop3471" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop3473" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3461">
+ <stop
+ style="stop-color:#73d216;stop-opacity:1;"
+ offset="0"
+ id="stop3463" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="1"
+ id="stop3465" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2785"
+ inkscape:collect="always">
+ <stop
+ id="stop2787"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop2789"
+ offset="1"
+ style="stop-color:#73d216;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3648">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3650" />
+ <stop
+ style="stop-color:#d3d7cf;stop-opacity:1"
+ offset="1"
+ id="stop3652" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3529">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3531" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3533" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3354">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop3356" />
+ <stop
+ id="stop3362"
+ offset="0.3253012"
+ style="stop-color:#6e706c;stop-opacity:1;" />
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1"
+ offset="1"
+ id="stop3358" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2785"
+ id="radialGradient2424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.6485415,0,0,1.0712181,-14.997522,4.840854)"
+ cx="23.125"
+ cy="-2.4186027"
+ fx="23.125"
+ fy="-2.4186027"
+ r="13.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3648"
+ id="radialGradient2427"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.9868421,0,0,1.1196908,72.080591,-6.806449)"
+ cx="-21.745861"
+ cy="13.112056"
+ fx="-21.745861"
+ fy="13.112056"
+ r="9.500001" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2440"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.875,0,0,1,42.9375,-1)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2444"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.875,0,0,1,42.9375,3)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2453"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2455"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.5290563,0,0,0.9612391,-12.71028,2.6976939)"
+ cx="8.3125"
+ cy="18.000002"
+ fx="8.3125"
+ fy="18.000002"
+ r="3.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2471"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-0.875,1,0,0,5.0625)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3354"
+ id="linearGradient2474"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,-0.875,1,0,4,5.0625)"
+ x1="-35.430172"
+ y1="21.307753"
+ x2="-35.430172"
+ y2="23.565876" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2484"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3529"
+ id="radialGradient2486"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,0.9174534,-1.6491259,0,42.017667,11.034824)"
+ cx="10.146483"
+ cy="16.894657"
+ fx="10.146483"
+ fy="16.894657"
+ r="3.5" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3461"
+ id="linearGradient3467"
+ x1="29.5"
+ y1="25.4375"
+ x2="-1.3127575"
+ y2="25.4375"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3469"
+ id="linearGradient3475"
+ x1="30.375"
+ y1="21.5"
+ x2="7.625"
+ y2="21.5"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#787878"
+ borderopacity="1"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="29.936922"
+ inkscape:cy="46.491641"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="48px"
+ height="48px"
+ inkscape:showpageshadow="false"
+ inkscape:window-width="872"
+ inkscape:window-height="751"
+ inkscape:window-x="517"
+ inkscape:window-y="133"
+ showgrid="false"
+ gridspacingx="0.5px"
+ gridspacingy="0.5px"
+ gridempspacing="2"
+ inkscape:grid-points="true"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ showborder="false" />
+ <metadata
+ id="metadata7859">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>Lapo Calamandrei</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:source />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
+ <dc:title>Patchage</dc:title>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>patches</rdf:li>
+ <rdf:li>audio</rdf:li>
+ <rdf:li>cables</rdf:li>
+ <rdf:li>jacks</rdf:li>
+ <rdf:li>jack</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/Notice" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/ShareAlike" />
+ <cc:requires
+ rdf:resource="http://web.resource.org/cc/SourceCode" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <path
+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;opacity:0.15"
+ d="M 34.5,1.5 C 27.29932,1.5 21.5,7.29932 21.5,14.5 C 21.5,14.5 21.5,19.270322 21.5,21.5 L 14.40625,21.5 C 14.210438,20.924291 13.728872,20.5 13.15625,20.5 L 7.5,20.5 L 7.5,21.5 L 3.5,21.5 C 2.946,21.5 2.5,21.946 2.5,22.5 C 2.5,23.054 2.946,23.5 3.5,23.5 L 7.5,23.5 L 7.5,24.5 L 7.5,25.5 L 3.5,25.5 C 2.946,25.5 2.5,25.946 2.5,26.5 C 2.5,27.054 2.946,27.5 3.5,27.5 L 7.5,27.5 L 7.5,28.5 L 13.15625,28.5 C 13.728872,28.5 14.210438,28.075709 14.40625,27.5 L 21.5,27.5 C 21.5,30.649407 21.5,31.638905 21.5,34.59375 C 20.924291,34.789562 20.5,35.271128 20.5,35.84375 L 20.5,41.5 L 21.5,41.5 L 21.5,44.5 C 21.5,45.054 21.946,45.5 22.5,45.5 C 23.054,45.5 23.5,45.054 23.5,44.5 L 23.5,41.5 L 24.5,41.5 L 25.5,41.5 L 25.5,44.5 C 25.5,45.054 25.946,45.5 26.5,45.5 C 27.054,45.5 27.5,45.054 27.5,44.5 L 27.5,41.5 L 28.5,41.5 L 28.5,35.84375 C 28.5,35.271128 28.075709,34.789562 27.5,34.59375 C 27.5,33.027561 27.5,30.986531 27.5,27.5 L 32.5,27.5 C 32.795145,27.5 34.5,27.5 34.5,27.5 C 41.700681,27.5 47.5,21.700681 47.5,14.5 C 47.500001,7.29932 41.70068,1.5 34.5,1.5 z M 26.5,45.5 C 25.946,45.5 25.5,45.946 25.5,46.5 C 25.5,47.054 25.946,47.5 26.5,47.5 C 27.054,47.5 27.5,47.054 27.5,46.5 C 27.5,45.946 27.054,45.5 26.5,45.5 z M 22.5,45.5 C 21.946,45.5 21.5,45.946 21.5,46.5 C 21.5,47.054 21.946,47.5 22.5,47.5 C 23.054,47.5 23.5,47.054 23.5,46.5 C 23.5,45.946 23.054,45.5 22.5,45.5 z M 2.5,26.5 C 2.5,25.946 2.054,25.5 1.5,25.5 C 0.946,25.5 0.5,25.946 0.5,26.5 C 0.5,27.054 0.946,27.5 1.5,27.5 C 2.054,27.5 2.5,27.054 2.5,26.5 z M 2.5,22.5 C 2.5,21.946 2.054,21.5 1.5,21.5 C 0.946,21.5 0.5,21.946 0.5,22.5 C 0.5,23.054 0.946,23.5 1.5,23.5 C 2.054,23.5 2.5,23.054 2.5,22.5 z M 34.5,3.5 C 40.634313,3.5 45.5,8.365688 45.5,14.5 C 45.500001,20.634313 40.634312,25.5 34.5,25.5 C 34.5,25.5 32.795145,25.5 32.5,25.5 L 27.5,25.5 C 27.5,24.50189 27.5,24.452741 27.5,23.5 L 32.5,23.5 C 33.46,23.5 34.5,23.5 34.5,23.5 C 39.457063,23.5 43.5,19.457062 43.5,14.5 C 43.500001,9.542937 39.457063,5.5 34.5,5.5 C 29.542938,5.5 25.5,9.542937 25.5,14.5 C 25.5,14.5 25.5,18.535917 25.5,21.5 L 23.5,21.5 C 23.5,19.270322 23.5,14.5 23.5,14.5 C 23.5,8.365688 28.365687,3.5 34.5,3.5 z M 34.5,7.5 C 38.374938,7.5 41.5,10.625062 41.5,14.5 C 41.500001,18.374937 38.374938,21.5 34.5,21.5 C 34.5,21.5 33.46,21.5 32.5,21.5 L 27.5,21.5 C 27.5,18.535917 27.5,14.5 27.5,14.5 C 27.5,10.625062 30.625063,7.5 34.5,7.5 z M 14.40625,23.5 L 21.5,23.5 C 21.5,24.250953 21.5,24.665418 21.5,25.5 L 14.40625,25.5 C 14.210438,24.924291 13.728872,24.5 13.15625,24.5 C 13.728872,24.5 14.210438,24.075709 14.40625,23.5 z M 23.5,23.5 L 25.5,23.5 C 25.5,24.452741 25.5,24.50189 25.5,25.5 L 23.5,25.5 C 23.5,24.665418 23.5,24.250953 23.5,23.5 z M 23.5,27.5 L 25.5,27.5 C 25.5,30.986531 25.5,33.027561 25.5,34.59375 C 24.924291,34.789562 24.5,35.271128 24.5,35.84375 C 24.5,35.271128 24.075709,34.789562 23.5,34.59375 C 23.5,31.638905 23.5,30.649407 23.5,27.5 z "
+ id="path3477"
+ sodipodi:nodetypes="ccccccccscccccscccccccccccscccccsccccccccsccsssccsssccsssccsssccsccccccssccccccscccccccccscccccccccscc" />
+ <path
+ style="color:#000000;fill:url(#radialGradient2427);fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 34.5,4.499999 C 29.542938,4.499999 25.5,8.542937 25.5,13.5 C 25.5,13.5 25.5,36.5 25.5,36.5 L 27.5,36.5 C 27.5,36.5 27.5,13.5 27.5,13.5 C 27.5,9.625062 30.625063,6.499999 34.5,6.499999 C 38.374938,6.499999 41.500001,9.625062 41.500001,13.5 C 41.500001,17.374937 38.374938,20.5 34.5,20.5 C 34.5,20.5 29.5,20.5 29.5,20.5 L 29.5,22.5 C 29.5,22.5 34.5,22.5 34.5,22.5 C 39.457063,22.5 43.500001,18.457062 43.500001,13.5 C 43.500001,8.542937 39.457063,4.499999 34.5,4.499999 z "
+ id="path2579"
+ sodipodi:nodetypes="cccccssccccsc" />
+ <path
+ style="color:#000000;fill:url(#radialGradient2424);fill-opacity:1;fill-rule:evenodd;stroke:#4e9a06;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 34.500001,0.500001 C 27.29932,0.500001 21.5,6.299321 21.5,13.500001 C 21.5,13.500001 21.5,34.91358 21.5,40.5 L 23.5,40.5 C 23.5,34.91358 23.5,13.500001 23.5,13.500001 C 23.5,7.365689 28.365688,2.500001 34.500001,2.500001 C 40.634313,2.500001 45.500001,7.365689 45.500001,13.500001 C 45.500001,19.634314 40.634313,24.500001 34.500001,24.500001 C 34.500001,24.500001 33.086419,24.500001 27.5,24.500001 L 27.5,26.500002 C 33.086419,26.500002 34.500001,26.500002 34.500001,26.500002 C 41.700681,26.500002 47.500001,20.700682 47.500001,13.500001 C 47.500001,6.299321 41.700681,0.500001 34.500001,0.500001 z "
+ id="path2581" />
+ <path
+ style="color:#000000;fill:url(#linearGradient3475);fill-opacity:1.0;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 32.5,22.5 L 7.5,22.5 L 7.5,20.5 L 32.5,20.5"
+ id="path2583"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="color:#000000;fill:url(#linearGradient3467);fill-opacity:1.0;fill-rule:evenodd;stroke:#4e9a06;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 32.5,26.5 L 10.5,26.5 L 10.5,24.5 L 32.5,24.5"
+ id="path2585"
+ sodipodi:nodetypes="cccc" />
+ <g
+ id="g2589"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="20.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2591"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="20.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2593"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g2595"
+ transform="translate(0,1)"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2597"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2599"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2601"
+ width="5"
+ height="2"
+ x="13.5"
+ y="24.5"
+ rx="0"
+ ry="0" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2444);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 7.5,23.5 L 13.145748,23.5 C 13.896004,23.5 14.5,24.190281 14.5,25.047716 L 14.5,25.952284 C 14.5,26.809719 13.896004,27.5 13.145748,27.5 L 7.5,27.5 L 7.5,23.5 z "
+ id="path2603"
+ sodipodi:nodetypes="ccccccc" />
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2605"
+ width="5"
+ height="2"
+ x="13.5"
+ y="20.5"
+ rx="0"
+ ry="0" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2440);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 7.5,19.5 L 13.145748,19.5 C 13.896004,19.5 14.5,20.190281 14.5,21.047716 L 14.5,21.952284 C 14.5,22.809719 13.896004,23.5 13.145748,23.5 L 7.5,23.5 L 7.5,19.5 z "
+ id="path2607"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2455);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2609"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0.8333333,0,0,1,1.4166667,0)" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2453);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2611"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0.8333333,0,0,1,1.4166667,4)" />
+ <g
+ id="g2615"
+ transform="matrix(0,-1,1,0,1.9999999,48)"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2617"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2619"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g2621"
+ transform="matrix(0,-1,1,0,-2.0000001,48)"
+ style="stroke:#888a85">
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="2.5"
+ height="2"
+ width="6"
+ id="rect2623"
+ style="opacity:1;color:#000000;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000012;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ ry="1"
+ rx="1"
+ y="23.5"
+ x="0.5"
+ height="2"
+ width="2"
+ id="rect2625"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2627"
+ width="5"
+ height="2"
+ x="-34.5"
+ y="25.5"
+ rx="0"
+ ry="0"
+ transform="matrix(0,-1,1,0,0,0)" />
+ <rect
+ style="color:#000000;fill:#555753;fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2629"
+ width="5"
+ height="2"
+ x="-34.5"
+ y="21.5"
+ rx="0"
+ ry="0"
+ transform="matrix(0,-1,1,0,0,0)" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2474);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 24.5,40.5 L 24.5,34.854252 C 24.5,34.103996 25.190281,33.5 26.047716,33.5 L 26.952284,33.5 C 27.809719,33.5 28.5,34.103996 28.5,34.854252 L 28.5,40.5 L 24.5,40.5 z "
+ id="path2631"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="color:#000000;fill:url(#linearGradient2471);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="M 20.5,40.5 L 20.5,34.854252 C 20.5,34.103996 21.190281,33.5 22.047716,33.5 L 22.952284,33.5 C 23.809719,33.5 24.5,34.103996 24.5,34.854252 L 24.5,40.5 L 20.5,40.5 z "
+ id="path2633"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2484);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2635"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0,-0.8333333,1,0,1,46.583333)" />
+ <path
+ sodipodi:type="inkscape:offset"
+ inkscape:radius="-1"
+ inkscape:original="M 7.5 19.5 L 7.5 23.5 L 13.9375 23.5 C 14.794935 23.5 15.5 22.794935 15.5 21.9375 L 15.5 21.0625 C 15.5 20.205065 14.794935 19.5 13.9375 19.5 L 7.5 19.5 z "
+ style="opacity:0.2;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient2486);stroke-width:1.09544516;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2637"
+ d="M 8.5,20.5 L 8.5,22.5 L 13.9375,22.5 C 14.246738,22.5 14.5,22.246738 14.5,21.9375 L 14.5,21.0625 C 14.5,20.753262 14.246738,20.5 13.9375,20.5 L 8.5,20.5 z "
+ transform="matrix(0,-0.8333333,1,0,5,46.583333)" />
+ <g
+ id="g2639"
+ style="opacity:0.2">
+ <rect
+ y="41"
+ x="21"
+ height="1"
+ width="3"
+ id="rect2641"
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <rect
+ y="41"
+ x="25"
+ height="1"
+ width="3"
+ id="rect2643"
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ </g>
+ <g
+ id="g2645">
+ <g
+ transform="translate(0,-14)"
+ style="opacity:0.2"
+ id="g2647">
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2649"
+ width="3"
+ height="1"
+ x="21"
+ y="41" />
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2651"
+ width="3"
+ height="1"
+ x="25"
+ y="41" />
+ </g>
+ <g
+ transform="translate(0,-18)"
+ style="opacity:0.2"
+ id="g2653">
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2655"
+ width="3"
+ height="1"
+ x="21"
+ y="41" />
+ <rect
+ style="opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2657"
+ width="3"
+ height="1"
+ x="25"
+ y="41" />
+ </g>
+ </g>
+ <g
+ id="g4481"
+ style="opacity:0.2"
+ transform="matrix(2,0,0,2,-394,6.000001)" />
+ <g
+ id="g2518"
+ style="opacity:0.2"
+ transform="matrix(0,1,-1,0,46,-5)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path2488"
+ sodipodi:cx="3.5"
+ sodipodi:cy="21.5"
+ sodipodi:rx="0.5"
+ sodipodi:ry="0.5"
+ d="M 4 21.5 A 0.5 0.5 0 1 1 3,21.5 A 0.5 0.5 0 1 1 4 21.5 z"
+ transform="matrix(1.5,0,0,1.5,-2,-11)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path3459"
+ sodipodi:cx="3.5"
+ sodipodi:cy="21.5"
+ sodipodi:rx="0.5"
+ sodipodi:ry="0.5"
+ d="M 4 21.5 A 0.5 0.5 0 1 1 3,21.5 A 0.5 0.5 0 1 1 4 21.5 z"
+ transform="matrix(1.5,0,0,1.5,-2,-7)" />
+ </g>
+</svg>
diff --git a/osx/Info.plist.in b/osx/Info.plist.in
new file mode 100644
index 0000000..0d8714b
--- /dev/null
+++ b/osx/Info.plist.in
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDisplayName</key>
+ <string>Patchage</string>
+ <key>CFBundleExecutable</key>
+ <string>patchage</string>
+ <key>CFBundleGetInfoString</key>
+ <string>@PATCHAGE_VERSION@, Copyright © 2014 David Robillard</string>
+ <key>CFBundleIconFile</key>
+ <string>Patchage</string>
+ <key>CFBundleIconFile</key>
+ <string>Patchage.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>net.drobilla.Patchage</string>
+ <key>CFBundleName</key>
+ <string>Patchage</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@PATCHAGE_VERSION@</string>
+ <key>CFBundleSignature</key>
+ <string>patc</string>
+ <key>LSEnvironment</key>
+ <dict>
+ <key>DYLD_LIBRARY_PATH</key>
+ <string>lib</string>
+ <key>GTK_PATH</key>
+ <string>lib</string>
+ <key>GTK_DATA_PREFIX</key>
+ <string>Resources</string>
+ <key>XDG_DATA_DIRS</key>
+ <string>Resources</string>
+ <key>GDK_PIXBUF_MODULE_FILE</key>
+ <string>Resources/loaders.cache</string>
+ <key>FONTCONFIG_FILE</key>
+ <string>Resources/fonts.conf</string>
+ <key>PANGO_RC_FILE</key>
+ <string>Resources/pangorc</string>
+ </dict>
+</dict>
+</plist>
diff --git a/osx/Patchage.icns b/osx/Patchage.icns
new file mode 100644
index 0000000..83364a2
--- /dev/null
+++ b/osx/Patchage.icns
Binary files differ
diff --git a/osx/bundleify.sh b/osx/bundleify.sh
new file mode 100755
index 0000000..55ff57d
--- /dev/null
+++ b/osx/bundleify.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+if [ "$#" != 3 ]; then
+ echo "USAGE: $0 LIB_PREFIX BUNDLE EXE";
+ exit 1;
+fi
+
+prefix=$1
+bundle=$2
+exe=$3
+
+mkdir -p "$bundle/Contents/lib"
+
+# Replace Control with Command in key bindings
+sed -i '' 's/GDK_CONTROL_MASK/GDK_META_MASK/' $bundle/Contents/patchage.ui
+
+# Copy font configuration files
+cp $prefix/etc/fonts/fonts.conf $bundle/Contents/Resources
+
+# Copy GTK and pango modules
+mkdir -p "$bundle/Contents/lib/modules"
+mkdir -p "$bundle/Contents/lib/gtk-2.0/engines"
+cp $prefix/lib/gtk-2.0/2.10.0/engines/libquartz.so $bundle/Contents/lib/gtk-2.0/engines
+cp $(find /usr/local/Cellar/pango -name '*basic-coretext*') $bundle/Contents/lib/modules
+
+# Copy GdkPixbuf loaders
+mkdir -p $bundle/Contents/lib/gdk-pixbuf-2.0/2.10.0/loaders/
+for fmt in icns png; do
+ cp $prefix/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-$fmt.so \
+ $bundle/Contents/lib/gdk-pixbuf-2.0/2.10.0/loaders/;
+done
+
+chmod -R 755 $bundle/Contents/lib/*
+
+# Copy libraries depended on by the executable to bundle
+libs="`otool -L $exe | grep '\.dylib\|\.so' | grep '/User\|/usr/local' | sed 's/(.*//'`"
+for l in $libs; do
+ cp $l $bundle/Contents/lib/;
+done
+chmod 755 $bundle/Contents/lib/*
+
+# ... recursively
+while true; do
+ newlibs=$libs
+
+ # Copy all libraries this library depends on to bundle
+ for l in $(find $bundle -name '*.dylib' -or -name '*.so'); do
+ reclibs="`otool -L $l | grep '\.dylib\|\.so' | grep '/User\|/usr/local' | sed 's/(.*//'`"
+ for rl in $reclibs; do
+ cp $rl $bundle/Contents/lib/;
+ done
+ chmod 755 $bundle/Contents/lib/*
+ newlibs=$(echo "$newlibs"; echo "$reclibs")
+ done
+
+ # Exit once we haven't added any new libraries
+ newlibs=$(echo "$newlibs" | sort | uniq)
+ if [ "$newlibs" = "$libs" ]; then
+ break;
+ fi
+ libs=$newlibs
+done
+
+echo "Bundled libraries:"
+echo "$libs"
+
+for l in $libs; do
+ lname=`echo $l | sed 's/.*\///'`
+ lid="@executable_path/lib/$lname"
+ lpath="$bundle/Contents/lib/$lname"
+ install_name_tool -id $lid $lpath
+ install_name_tool -change $l $lid $exe
+ for j in `find $bundle -name '*.so' -or -name '*.dylib'`; do
+ install_name_tool -change $l $lid $j
+ done;
+done
+
+echo "External library references:"
+otool -L $exe `find $bundle -name '*.so' -or -name '*.dylib'` | grep -v ':' | grep -v '@executable_path' | sort | uniq
diff --git a/osx/gtkrc b/osx/gtkrc
new file mode 100644
index 0000000..412de42
--- /dev/null
+++ b/osx/gtkrc
@@ -0,0 +1,251 @@
+gtk-color-scheme =
+"bg_color: #f5f5f5
+fg_color: #000
+base_color: #fff
+text_color: #000
+selected_bg_color: #0066FF
+selected_fg_color: #fff
+tooltip_bg_color: #fafaba
+tooltip_fg_color: #000"
+
+gtk-font-name = "Lucida Grande 12"
+gtk-icon-theme-name = "Tango"
+gtk-menu-popup-delay = 1
+gtk-button-images = 0
+gtk-menu-images = 0
+gtk-toolbar-style = 0
+gtk-enable-mnemonics = 0
+gtk-icon-sizes = "gtk-small-toolbar=16,16:gtk-large-toolbar=16,16"
+gtk-toolbar-icon-size = small-toolbar
+gtk-error-bell = 0
+gtk-show-input-method-menu = 0
+
+style "default"
+{
+ bg[NORMAL] = @bg_color
+ bg[PRELIGHT] = @bg_color
+ bg[SELECTED] = @selected_bg_color
+ bg[INSENSITIVE] = @bg_color
+ bg[ACTIVE] = @bg_color
+
+ fg[NORMAL] = @fg_color
+ fg[PRELIGHT] = @fg_color
+ fg[SELECTED] = @selected_fg_color
+ fg[INSENSITIVE] = darker (@bg_color)
+ fg[ACTIVE] = @fg_color
+
+ text[NORMAL] = @text_color
+ text[PRELIGHT] = @text_color
+ text[SELECTED] = @selected_fg_color
+ text[INSENSITIVE] = darker (@bg_color)
+ text[ACTIVE] = @text_color
+
+ base[NORMAL] = @base_color
+ base[PRELIGHT] = @selected_bg_color
+ base[SELECTED] = @selected_bg_color
+ base[INSENSITIVE] = @bg_color
+ base[ACTIVE] = shade (1.3, @selected_bg_color)
+
+ GtkWidget::interior-focus = 1
+ GtkWidget::new-tooltip-style = 1
+ GtkWidget::focus-line-width = 0
+ GtkWidget::focus-padding = 0
+ GtkButton::inner-border = { 8, 8, 0, 0 }
+ GtkButton::default-border = { 0, 0, 0, 0 }
+ GtkButton::child-displacement-x = 0
+ GtkButton::child-displacement-y = 0
+ GtkCheckButton::indicator-spacing = 3
+ GtkSpinButton::shadow-type = out
+ GtkButtonBox::child-min-width = 70
+ GtkButtonBox::child-min-height = 22
+ GtkComboBox::appears-as-list = 0
+ GtkComboBox::focus-on-click = 0
+ GtkNotebook::tab-curvature = 4
+ GtkNotebook::tab-overlap = 0
+ GtkTreeView::allow-rules = 1
+ GtkTreeView::expander-size = 14
+ GtkToolbar::internal-padding = 2
+ GtkExpander::expander-size = 14
+ GtkScrolledWindow::scrollbar-spacing = 0
+ GtkMenuItem::horizontal-padding = 8
+ GtkMenu::vertical-padding = 4
+ GtkMenuItem::horizontal-padding = 9
+ GtkMenuItem::toggle-spacing = 0
+ GtkSeparatorMenuItem::horizontal-padding = 2
+ GtkSeparatorMenuItem::horizontal-padding = 2
+ GtkScrollbar::min-slider-length = 10
+ GtkScrollbar::has-forward-stepper = 1
+ GtkScrollbar::has-backward-stepper = 0
+ GtkScrollbar::has-secondary-forward-stepper = 0
+ GtkScrollbar::has-secondary-backward-stepper = 1
+ GtkRange::trough-border = 0
+ GtkRange::stepper-spacing = 0
+ GtkRange::stepper-size = 14
+ GtkRange::trough-under-steppers = 1
+
+ engine "quartz"
+ {
+ buttontype = "aqua"
+ }
+}
+
+style "wider"
+{
+ xthickness = 3
+ ythickness = 3
+}
+
+style "scrolled-window"
+{
+ xthickness = 1
+ ythickness = 1
+}
+
+style "menu"
+{
+ xthickness = 0
+ ythickness = 0
+ font_name = "Lucida Grande 14"
+
+ bg[PRELIGHT] = @selected_fg_color
+ fg[PRELIGHT] = @selected_fg_color
+ text[PRELIGHT] = @selected_fg_color
+}
+
+style "menu-item" = "menu"
+{
+ ythickness = 2
+ GtkWidget::draw-border = { 0, 0, 0, 2 }
+}
+
+style "menu-separator" = "menu-item"
+{
+ ythickness = 3
+}
+
+style "menu-bar"
+{
+ ythickness = 0
+ xthickness = 0
+}
+
+style "treeview"
+{
+ GtkTreeView::odd-row-color = "#f5f5f5"
+
+ base[SELECTED] = "#2b5dcd"
+ base[ACTIVE] = "#cacaca"
+ text[SELECTED] = "#FFF"
+ text[ACTIVE] = "#000"
+
+ font_name = "Lucida Grande 12"
+
+ GtkWidget::focus-line-width = 0
+ GtkWidget::draw-border = { 1, 1, 1, 1 }
+ GtkButton::inner-border = { 3, 3, 1, 3 }
+}
+
+style "tree-header"
+{
+ ythickness = 0
+ bg[NORMAL] = "#f2f2f2"
+
+ font_name = "Lucida Grande 11"
+}
+
+style "tooltip"
+{
+ xthickness = 5
+ ythickness = 5
+
+ fg[NORMAL] = @tooltip_fg_color
+ bg[NORMAL] = @tooltip_bg_color
+}
+
+style "scrollbar"
+{
+ GtkScrollbar::has-forward-stepper = 0
+ GtkScrollbar::has-backward-stepper = 0
+ GtkRange::slider-width = 15
+ GtkRange::trough-border = 0
+ GtkRange::trough-side-details = 0
+}
+
+style "button"
+{
+ xthickness = 2
+ ythickness = 2
+
+ bg[NORMAL] = @bg_color
+ bg[PRELIGHT] = shade(1.1, @bg_color)
+ bg[ACTIVE] = @bg_color
+ bg[INSENSITIVE] = @bg_color
+
+ GtkWidget::draw-border = { 2, 2, 2, 2 }
+ GtkWidget::focus-line-width = 1
+}
+
+style "combo-box"
+{
+ fg[NORMAL] = "#616161"
+ #xthickness = 2
+ #ythickness = 2
+}
+
+style "combo-box-button"
+{
+ xthickness = 6
+ ythickness = 2
+
+ bg[NORMAL] = @base_color
+ bg[PRELIGHT] = shade(1.1, @base_color)
+ bg[ACTIVE] = @base_color
+ bg[INSENSITIVE] = @base_color
+
+ GtkWidget::focus-padding = 0
+}
+
+style "combo-box-label" = "combo-box"
+{
+ fg[NORMAL] = "#595959"
+}
+
+style "small-font"
+{
+ font_name = "Lucida Grande 11"
+}
+
+style "mini-font"
+{
+ font_name = "Lucida Grande 9"
+}
+
+style "entry"
+{
+ xthickness = 2
+ ythickness = 2
+ GtkEntry::inner-border = { 2, 2, 2, 2 }
+ base[INSENSITIVE] = shade(1.02, @bg_color)
+}
+
+class "GtkWidget" style "default"
+class "GtkMenuBar" style "menu-bar"
+class "GtkScrolledWindow" style "scrolled-window"
+class "GtkFrame" style "wider"
+class "GtkScrollbar" style "scrollbar"
+class "GtkEntry" style "entry"
+class "GtkButton" style "button"
+
+widget "gtk-tooltip*" style "tooltip"
+widget "*small-font*" style "small-font"
+widget "*mini-font*" style "mini-font"
+
+widget_class "*<GtkMenu>*" style "menu"
+widget_class "*<GtkMenuItem>*" style "menu-item"
+widget_class "*<GtkSeparatorMenuItem>" style "menu-separator"
+/*widget "*.gtk-combobox-popup-menu*" style "combo-box-menu"*/
+widget_class "*.<GtkTreeView>*" style "treeview"
+widget_class "*.GtkTreeView.GtkButton" style "tree-header"
+widget_class "*.<GtkComboBox>.*" style "combo-box"
+widget_class "*.<GtkComboBoxText>.<GtkToggleButton>" style "combo-box-button"
+widget_class "*.<GtkComboBoxEntry>.<GtkToggleButton>" style "combo-box-button"
diff --git a/osx/loaders.cache b/osx/loaders.cache
new file mode 100644
index 0000000..19e9a29
--- /dev/null
+++ b/osx/loaders.cache
@@ -0,0 +1,11 @@
+"@executable_path/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-icns.so"
+"icns" 4 "gdk-pixbuf" "The ICNS image format" "GPL"
+"image/x-icns" ""
+"icns" ""
+"icns" "" 100
+
+"@executable_path/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-png.so"
+"png" 5 "gdk-pixbuf" "The PNG image format" "LGPL"
+"image/png" ""
+"png" ""
+"\211PNG\r\n\032\n" "" 100
diff --git a/osx/pango.modules b/osx/pango.modules
new file mode 100644
index 0000000..d981ee2
--- /dev/null
+++ b/osx/pango.modules
@@ -0,0 +1,2 @@
+lib/modules/pango-basic-coretext.so BasicScriptEngineCoreText PangoEngineShape PangoRenderCoreText common:
+lib/modules/pango-basic-fc.so BasicScriptEngineFc PangoEngineShape PangoRenderFc common:
diff --git a/osx/pangorc b/osx/pangorc
new file mode 100644
index 0000000..ce582e3
--- /dev/null
+++ b/osx/pangorc
@@ -0,0 +1,2 @@
+[Pango]
+ModuleFiles = ./Resources/pango.modules
diff --git a/patchage.desktop.in b/patchage.desktop.in
new file mode 100644
index 0000000..b2f2809
--- /dev/null
+++ b/patchage.desktop.in
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=@APP_HUMAN_NAME@
+Comment=Connect audio and MIDI applications together and manage audio sessions
+Comment[fr]=Connecter des applications audio et MIDI entre elles, et gérer les sessions audio
+Exec=@BINDIR@/@APP_INSTALL_NAME@
+Terminal=false
+Icon=@APP_INSTALL_NAME@
+Type=Application
+Categories=AudioVideo;Audio;
diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp
new file mode 100644
index 0000000..1ebd12d
--- /dev/null
+++ b/src/AlsaDriver.cpp
@@ -0,0 +1,585 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+#include <set>
+#include <string>
+#include <utility>
+
+#include <boost/format.hpp>
+
+#include "AlsaDriver.hpp"
+#include "Patchage.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageModule.hpp"
+#include "PatchagePort.hpp"
+
+using std::endl;
+using std::string;
+using boost::format;
+
+AlsaDriver::AlsaDriver(Patchage* app)
+ : _app(app)
+ , _seq(NULL)
+{
+}
+
+AlsaDriver::~AlsaDriver()
+{
+ detach();
+}
+
+/** Attach to ALSA. */
+void
+AlsaDriver::attach(bool /*launch_daemon*/)
+{
+ int ret = snd_seq_open(&_seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
+ if (ret) {
+ _app->error_msg("Alsa: Unable to attach.");
+ _seq = NULL;
+ } else {
+ _app->info_msg("Alsa: Attached.");
+
+ snd_seq_set_client_name(_seq, "Patchage");
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 50000);
+
+ ret = pthread_create(&_refresh_thread, &attr, &AlsaDriver::refresh_main, this);
+ if (ret)
+ _app->error_msg("Alsa: Failed to start refresh thread.");
+
+ signal_attached.emit();
+ }
+}
+
+void
+AlsaDriver::detach()
+{
+ if (_seq) {
+ pthread_cancel(_refresh_thread);
+ pthread_join(_refresh_thread, NULL);
+ snd_seq_close(_seq);
+ _seq = NULL;
+ signal_detached.emit();
+ _app->info_msg("Alsa: Detached.");
+ }
+}
+
+static bool
+is_alsa_port(const PatchagePort* port)
+{
+ return port->type() == ALSA_MIDI;
+}
+
+/** Destroy all JACK (canvas) ports.
+ */
+void
+AlsaDriver::destroy_all()
+{
+ _app->canvas()->remove_ports(is_alsa_port);
+ _modules.clear();
+ _port_addrs.clear();
+}
+
+/** Refresh all Alsa Midi ports and connections.
+ */
+void
+AlsaDriver::refresh()
+{
+ if (!is_attached())
+ return;
+
+ assert(_seq);
+
+ _modules.clear();
+ _ignored.clear();
+ _port_addrs.clear();
+
+ snd_seq_client_info_t* cinfo;
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_client_info_set_client(cinfo, -1);
+
+ snd_seq_port_info_t* pinfo;
+ snd_seq_port_info_alloca(&pinfo);
+
+ PatchageModule* parent = NULL;
+ PatchagePort* port = NULL;
+
+ // Create port views
+ while (snd_seq_query_next_client(_seq, cinfo) >= 0) {
+ snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
+ snd_seq_port_info_set_port(pinfo, -1);
+ while (snd_seq_query_next_port(_seq, pinfo) >= 0) {
+ const snd_seq_addr_t& addr = *snd_seq_port_info_get_addr(pinfo);
+ if (ignore(addr)) {
+ continue;
+ }
+
+ create_port_view_internal(_app, addr, parent, port);
+ }
+ }
+
+ // Create connections
+ snd_seq_client_info_set_client(cinfo, -1);
+ while (snd_seq_query_next_client(_seq, cinfo) >= 0) {
+ snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
+ snd_seq_port_info_set_port(pinfo, -1);
+ while (snd_seq_query_next_port(_seq, pinfo) >= 0) {
+ const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(pinfo);
+ if (ignore(*addr)) {
+ continue;
+ }
+
+ PatchagePort* port = _app->canvas()->find_port(PortID(*addr, false));
+ if (!port) {
+ continue;
+ }
+
+ snd_seq_query_subscribe_t* subsinfo;
+ snd_seq_query_subscribe_alloca(&subsinfo);
+ snd_seq_query_subscribe_set_root(subsinfo, addr);
+ snd_seq_query_subscribe_set_index(subsinfo, 0);
+ while (!snd_seq_query_port_subscribers(_seq, subsinfo)) {
+ const snd_seq_addr_t* addr2 = snd_seq_query_subscribe_get_addr(subsinfo);
+ if (addr2) {
+ const PortID id2(*addr2, true);
+ PatchagePort* port2 = _app->canvas()->find_port(id2);
+ if (port2 && !_app->canvas()->get_edge(port, port2)) {
+ _app->canvas()->make_connection(port, port2);
+ }
+ }
+
+ snd_seq_query_subscribe_set_index(
+ subsinfo, snd_seq_query_subscribe_get_index(subsinfo) + 1);
+ }
+ }
+ }
+}
+
+PatchagePort*
+AlsaDriver::create_port_view(Patchage* patchage,
+ const PortID& id)
+{
+ PatchageModule* parent = NULL;
+ PatchagePort* port = NULL;
+ create_port_view_internal(patchage, id.id.alsa_addr, parent, port);
+ return port;
+}
+
+PatchageModule*
+AlsaDriver::find_module(uint8_t client_id, ModuleType type)
+{
+ const Modules::const_iterator i = _modules.find(client_id);
+ if (i == _modules.end())
+ return NULL;
+
+ PatchageModule* io_module = NULL;
+ for (Modules::const_iterator j = i;
+ j != _modules.end() && j->first == client_id;
+ ++j) {
+ if (j->second->type() == type) {
+ return j->second;
+ } else if (j->second->type() == InputOutput) {
+ io_module = j->second;
+ }
+ }
+
+ // Return InputOutput module for Input or Output, or NULL if not found
+ return io_module;
+}
+
+PatchageModule*
+AlsaDriver::find_or_create_module(
+ Patchage* patchage,
+ uint8_t client_id,
+ const std::string& client_name,
+ ModuleType type)
+{
+ PatchageModule* m = find_module(client_id, type);
+ if (!m) {
+ m = new PatchageModule(patchage, client_name, type);
+ m->load_location();
+ _app->canvas()->add_module(client_name, m);
+ _modules.insert(std::make_pair(client_id, m));
+ }
+ return m;
+}
+
+void
+AlsaDriver::create_port_view_internal(
+ Patchage* patchage,
+ snd_seq_addr_t addr,
+ PatchageModule*& m,
+ PatchagePort*& port)
+{
+ if (ignore(addr))
+ return;
+
+ snd_seq_client_info_t* cinfo;
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_client_info_set_client(cinfo, addr.client);
+ snd_seq_get_any_client_info(_seq, addr.client, cinfo);
+
+ snd_seq_port_info_t* pinfo;
+ snd_seq_port_info_alloca(&pinfo);
+ snd_seq_port_info_set_client(pinfo, addr.client);
+ snd_seq_port_info_set_port(pinfo, addr.port);
+ snd_seq_get_any_port_info(_seq, addr.client, addr.port, pinfo);
+
+ const string client_name = snd_seq_client_info_get_name(cinfo);
+ const string port_name = snd_seq_port_info_get_name(pinfo);
+ bool is_input = false;
+ bool is_duplex = false;
+ bool is_application = true;
+
+ int caps = snd_seq_port_info_get_capability(pinfo);
+ int type = snd_seq_port_info_get_type(pinfo);
+
+ // Figure out direction
+ if ((caps & SND_SEQ_PORT_CAP_READ) && (caps & SND_SEQ_PORT_CAP_WRITE))
+ is_duplex = true;
+ else if (caps & SND_SEQ_PORT_CAP_READ)
+ is_input = false;
+ else if (caps & SND_SEQ_PORT_CAP_WRITE)
+ is_input = true;
+
+ is_application = (type & SND_SEQ_PORT_TYPE_APPLICATION);
+
+ // Because there would be name conflicts, we must force a split if (stupid)
+ // alsa duplex ports are present on the client
+ bool split = false;
+ if (is_duplex) {
+ split = true;
+ if (!_app->conf()->get_module_split(client_name, !is_application)) {
+ _app->conf()->set_module_split(client_name, true);
+ }
+ } else {
+ split = _app->conf()->get_module_split(client_name, !is_application);
+ }
+
+ /*cout << "ALSA PORT: " << client_name << " : " << port_name
+ << " is_application = " << is_application
+ << " is_duplex = " << is_duplex
+ << " split = " << split << endl;*/
+
+ if (!split) {
+ m = find_or_create_module(_app, addr.client, client_name, InputOutput);
+ if (!m->get_port(port_name)) {
+ port = create_port(*m, port_name, is_input, addr);
+ port->show();
+ }
+
+ } else { // split
+ ModuleType type = ((is_input) ? Input : Output);
+ m = find_or_create_module(_app, addr.client, client_name, type);
+ if (!m->get_port(port_name)) {
+ port = create_port(*m, port_name, is_input, addr);
+ port->show();
+ }
+
+ if (is_duplex) {
+ type = ((!is_input) ? Input : Output);
+ m = find_or_create_module(_app, addr.client, client_name, type);
+ if (!m->get_port(port_name)) {
+ port = create_port(*m, port_name, !is_input, addr);
+ port->show();
+ }
+ }
+ }
+}
+
+PatchagePort*
+AlsaDriver::create_port(PatchageModule& parent,
+ const string& name, bool is_input, snd_seq_addr_t addr)
+{
+ PatchagePort* ret = new PatchagePort(
+ parent, ALSA_MIDI, name, "", is_input,
+ _app->conf()->get_port_color(ALSA_MIDI),
+ _app->show_human_names());
+
+ dynamic_cast<PatchageCanvas*>(parent.canvas())->index_port(
+ PortID(addr, is_input), ret);
+
+ _app->canvas()->index_port(PortID(addr, is_input), ret);
+ _port_addrs.insert(std::make_pair(ret, PortID(addr, is_input)));
+ return ret;
+}
+
+bool
+AlsaDriver::ignore(const snd_seq_addr_t& addr, bool add)
+{
+ if (_ignored.find(addr) != _ignored.end())
+ return true;
+
+ if (!add)
+ return false;
+
+ snd_seq_client_info_t* cinfo;
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_client_info_set_client(cinfo, addr.client);
+ snd_seq_get_any_client_info(_seq, addr.client, cinfo);
+
+ snd_seq_port_info_t* pinfo;
+ snd_seq_port_info_alloca(&pinfo);
+ snd_seq_port_info_set_client(pinfo, addr.client);
+ snd_seq_port_info_set_port(pinfo, addr.port);
+ snd_seq_get_any_port_info(_seq, addr.client, addr.port, pinfo);
+
+ const int type = snd_seq_port_info_get_type(pinfo);
+ const int caps = snd_seq_port_info_get_capability(pinfo);
+
+ if (caps & SND_SEQ_PORT_CAP_NO_EXPORT) {
+ _ignored.insert(addr);
+ return true;
+ } else if ( !( (caps & SND_SEQ_PORT_CAP_READ)
+ || (caps & SND_SEQ_PORT_CAP_WRITE)
+ || (caps & SND_SEQ_PORT_CAP_DUPLEX))) {
+ _ignored.insert(addr);
+ return true;
+ } else if ((snd_seq_client_info_get_type(cinfo) != SND_SEQ_USER_CLIENT)
+ && ((type == SND_SEQ_PORT_SYSTEM_TIMER
+ || type == SND_SEQ_PORT_SYSTEM_ANNOUNCE))) {
+ _ignored.insert(addr);
+ return true;
+ }
+
+ return false;
+}
+
+/** Connects two Alsa Midi ports.
+ *
+ * \return Whether connection succeeded.
+ */
+bool
+AlsaDriver::connect(PatchagePort* src_port,
+ PatchagePort* dst_port)
+{
+ PortAddrs::const_iterator s = _port_addrs.find(src_port);
+ PortAddrs::const_iterator d = _port_addrs.find(dst_port);
+
+ if (s == _port_addrs.end() || d == _port_addrs.end()) {
+ _app->error_msg("Alsa: Attempt to connect port with no address.");
+ return false;
+ }
+
+ const PortID src = s->second;
+ const PortID dst = d->second;
+
+ if (src.id.alsa_addr.client == dst.id.alsa_addr.client
+ && src.id.alsa_addr.port == dst.id.alsa_addr.port) {
+ _app->warning_msg("Alsa: Refusing to connect port to itself.");
+ return false;
+ }
+
+ bool result = true;
+
+ snd_seq_port_subscribe_t* subs;
+ snd_seq_port_subscribe_malloc(&subs);
+ snd_seq_port_subscribe_set_sender(subs, &src.id.alsa_addr);
+ snd_seq_port_subscribe_set_dest(subs, &dst.id.alsa_addr);
+ snd_seq_port_subscribe_set_exclusive(subs, 0);
+ snd_seq_port_subscribe_set_time_update(subs, 0);
+ snd_seq_port_subscribe_set_time_real(subs, 0);
+
+ // Already connected (shouldn't happen)
+ if (!snd_seq_get_port_subscription(_seq, subs)) {
+ _app->error_msg("Alsa: Attempt to double subscribe ports.");
+ result = false;
+ }
+
+ int ret = snd_seq_subscribe_port(_seq, subs);
+ if (ret < 0) {
+ _app->error_msg((format("Alsa: Subscription failed (%1%).")
+ % snd_strerror(ret)).str());
+ result = false;
+ }
+
+ if (result)
+ _app->info_msg(string("Alsa: Connected ")
+ + src_port->full_name() + " => " + dst_port->full_name());
+ else
+ _app->error_msg(string("Alsa: Unable to connect ")
+ + src_port->full_name() + " => " + dst_port->full_name());
+
+ return (!result);
+}
+
+/** Disconnects two Alsa Midi ports.
+ *
+ * \return Whether disconnection succeeded.
+ */
+bool
+AlsaDriver::disconnect(PatchagePort* src_port,
+ PatchagePort* dst_port)
+{
+ PortAddrs::const_iterator s = _port_addrs.find(src_port);
+ PortAddrs::const_iterator d = _port_addrs.find(dst_port);
+
+ if (s == _port_addrs.end() || d == _port_addrs.end()) {
+ _app->error_msg("Alsa: Attempt to connect port with no address");
+ return false;
+ }
+
+ const PortID src = s->second;
+ const PortID dst = d->second;
+
+ snd_seq_port_subscribe_t* subs;
+ snd_seq_port_subscribe_malloc(&subs);
+ snd_seq_port_subscribe_set_sender(subs, &src.id.alsa_addr);
+ snd_seq_port_subscribe_set_dest(subs, &dst.id.alsa_addr);
+ snd_seq_port_subscribe_set_exclusive(subs, 0);
+ snd_seq_port_subscribe_set_time_update(subs, 0);
+ snd_seq_port_subscribe_set_time_real(subs, 0);
+
+ // Not connected (shouldn't happen)
+ if (snd_seq_get_port_subscription(_seq, subs) != 0) {
+ _app->error_msg("Alsa: Attempt to unsubscribe ports that are not subscribed.");
+ return false;
+ }
+
+ int ret = snd_seq_unsubscribe_port(_seq, subs);
+ if (ret < 0) {
+ _app->error_msg(string("Alsa: Unable to disconnect ")
+ + src_port->full_name() + " => " + dst_port->full_name()
+ + "(" + snd_strerror(ret) + ")");
+ return false;
+ }
+
+ _app->info_msg(string("Alsa: Disconnected ")
+ + src_port->full_name() + " => " + dst_port->full_name());
+
+ return true;
+}
+
+bool
+AlsaDriver::create_refresh_port()
+{
+ snd_seq_port_info_t* port_info;
+ snd_seq_port_info_alloca(&port_info);
+ snd_seq_port_info_set_name(port_info, "System Announcement Reciever");
+ snd_seq_port_info_set_type(port_info, SND_SEQ_PORT_TYPE_APPLICATION);
+ snd_seq_port_info_set_capability(port_info,
+ SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE|SND_SEQ_PORT_CAP_NO_EXPORT);
+
+ int ret = snd_seq_create_port(_seq, port_info);
+ if (ret) {
+ _app->error_msg((format("Alsa: Error creating port (%1%): ")
+ % snd_strerror(ret)).str());
+ return false;
+ }
+
+ // Subscribe the port to the system announcer
+ ret = snd_seq_connect_from(_seq,
+ snd_seq_port_info_get_port(port_info),
+ SND_SEQ_CLIENT_SYSTEM,
+ SND_SEQ_PORT_SYSTEM_ANNOUNCE);
+ if (ret) {
+ _app->error_msg((format("Alsa: Failed to connect to system announce port (%1%)")
+ % snd_strerror(ret)).str());
+ return false;
+ }
+
+ return true;
+}
+
+void*
+AlsaDriver::refresh_main(void* me)
+{
+ AlsaDriver* ad = (AlsaDriver*)me;
+ ad->_refresh_main();
+ return NULL;
+}
+
+void
+AlsaDriver::_refresh_main()
+{
+ if (!create_refresh_port()) {
+ _app->error_msg("Alsa: Could not create listen port, auto-refresh disabled.");
+ return;
+ }
+
+ int caps = 0;
+
+ snd_seq_client_info_t* cinfo;
+ snd_seq_client_info_alloca(&cinfo);
+
+ snd_seq_port_info_t* pinfo;
+ snd_seq_port_info_alloca(&pinfo);
+
+ snd_seq_event_t* ev;
+ while (snd_seq_event_input(_seq, &ev) > 0) {
+ assert(ev);
+
+ Glib::Mutex::Lock lock(_events_mutex);
+
+ switch (ev->type) {
+ case SND_SEQ_EVENT_PORT_SUBSCRIBED:
+ if (!ignore(ev->data.connect.sender) && !ignore(ev->data.connect.dest))
+ _events.push(PatchageEvent(PatchageEvent::CONNECTION,
+ ev->data.connect.sender, ev->data.connect.dest));
+ break;
+ case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
+ if (!ignore(ev->data.connect.sender) && !ignore(ev->data.connect.dest))
+ _events.push(PatchageEvent(PatchageEvent::DISCONNECTION,
+ ev->data.connect.sender, ev->data.connect.dest));
+ break;
+ case SND_SEQ_EVENT_PORT_START:
+ snd_seq_get_any_client_info(_seq, ev->data.addr.client, cinfo);
+ snd_seq_get_any_port_info(_seq, ev->data.addr.client, ev->data.addr.port, pinfo);
+ caps = snd_seq_port_info_get_capability(pinfo);
+
+ if (!ignore(ev->data.addr))
+ _events.push(PatchageEvent(PatchageEvent::PORT_CREATION,
+ PortID(ev->data.addr, (caps & SND_SEQ_PORT_CAP_READ))));
+ break;
+ case SND_SEQ_EVENT_PORT_EXIT:
+ if (!ignore(ev->data.addr, false)) {
+ // Note: getting caps at this point does not work
+ // Delete both inputs and outputs (in case this is a duplex port)
+ _events.push(PatchageEvent(PatchageEvent::PORT_DESTRUCTION,
+ PortID(ev->data.addr, true)));
+ _events.push(PatchageEvent(PatchageEvent::PORT_DESTRUCTION,
+ PortID(ev->data.addr, false)));
+ _port_addrs.erase(_app->canvas()->find_port(
+ PortID(ev->data.addr, false)));
+ _port_addrs.erase(_app->canvas()->find_port(
+ PortID(ev->data.addr, true)));
+ }
+ break;
+ case SND_SEQ_EVENT_CLIENT_CHANGE:
+ case SND_SEQ_EVENT_CLIENT_EXIT:
+ case SND_SEQ_EVENT_CLIENT_START:
+ case SND_SEQ_EVENT_PORT_CHANGE:
+ case SND_SEQ_EVENT_RESET:
+ default:
+ //_events.push(PatchageEvent(PatchageEvent::REFRESH));
+ break;
+ }
+ }
+}
+
+void
+AlsaDriver::process_events(Patchage* app)
+{
+ Glib::Mutex::Lock lock(_events_mutex);
+ while (!_events.empty()) {
+ PatchageEvent& ev = _events.front();
+ ev.execute(app);
+ _events.pop();
+ }
+}
diff --git a/src/AlsaDriver.hpp b/src/AlsaDriver.hpp
new file mode 100644
index 0000000..8bf837a
--- /dev/null
+++ b/src/AlsaDriver.hpp
@@ -0,0 +1,116 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_ALSADRIVER_HPP
+#define PATCHAGE_ALSADRIVER_HPP
+
+#include <queue>
+#include <set>
+#include <string>
+#include <map>
+
+#include <alsa/asoundlib.h>
+#include <pthread.h>
+
+#include "Driver.hpp"
+#include "PatchageModule.hpp"
+
+class Patchage;
+class PatchagePort;
+
+/** Handles all externally driven functionality, registering ports etc.
+ */
+class AlsaDriver : public Driver
+{
+public:
+ explicit AlsaDriver(Patchage* app);
+ ~AlsaDriver();
+
+ void attach(bool launch_daemon = false);
+ void detach();
+
+ bool is_attached() const { return (_seq != NULL); }
+
+ void refresh();
+ void destroy_all();
+
+ PatchagePort* create_port_view(
+ Patchage* patchage,
+ const PortID& id);
+
+ bool connect(PatchagePort* src_port,
+ PatchagePort* dst_port);
+
+ bool disconnect(PatchagePort* src_port,
+ PatchagePort* dst_port);
+
+ void print_addr(snd_seq_addr_t addr);
+
+ void process_events(Patchage* app);
+
+private:
+ bool create_refresh_port();
+ static void* refresh_main(void* me);
+ void _refresh_main();
+
+ PatchageModule* find_module(uint8_t client_id, ModuleType type);
+
+ PatchageModule*
+ find_or_create_module(
+ Patchage* patchage,
+ uint8_t client_id,
+ const std::string& client_name,
+ ModuleType type);
+
+ void
+ create_port_view_internal(
+ Patchage* patchage,
+ snd_seq_addr_t addr,
+ PatchageModule*& parent,
+ PatchagePort*& port);
+
+ PatchagePort* create_port(
+ PatchageModule& parent,
+ const std::string& name,
+ bool is_input,
+ snd_seq_addr_t addr);
+
+ Patchage* _app;
+ snd_seq_t* _seq;
+ pthread_t _refresh_thread;
+
+ Glib::Mutex _events_mutex;
+ std::queue<PatchageEvent> _events;
+
+ struct SeqAddrComparator {
+ bool operator() (const snd_seq_addr_t& a, const snd_seq_addr_t& b) const {
+ return ((a.client < b.client) || ((a.client == b.client) && a.port < b.port));
+ }
+ };
+
+ typedef std::set<snd_seq_addr_t, SeqAddrComparator> Ignored;
+ Ignored _ignored;
+
+ typedef std::multimap<uint8_t, PatchageModule*> Modules;
+ Modules _modules;
+
+ typedef std::map<PatchagePort*, PortID> PortAddrs;
+ PortAddrs _port_addrs;
+
+ bool ignore(const snd_seq_addr_t& addr, bool add=true);
+};
+
+#endif // PATCHAGE_ALSADRIVER_HPP
diff --git a/src/Configuration.cpp b/src/Configuration.cpp
new file mode 100644
index 0000000..d9537c0
--- /dev/null
+++ b/src/Configuration.cpp
@@ -0,0 +1,333 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <ios>
+#include <iostream>
+#include <limits>
+#include <stdexcept>
+#include <vector>
+
+#include "Configuration.hpp"
+#include "Patchage.hpp"
+
+static const char* port_type_names[N_PORT_TYPES] = {
+ "JACK_AUDIO",
+ "JACK_MIDI",
+ "ALSA_MIDI",
+ "JACK_OSC",
+ "JACK_CV"
+};
+
+Configuration::Configuration()
+ : _window_location(0, 0)
+ , _window_size(640, 480)
+ , _zoom(1.0)
+ , _font_size(12.0)
+ , _messages_height(0)
+ , _show_toolbar(true)
+ , _show_messages(false)
+ , _sort_ports(true)
+{
+#ifdef PATCHAGE_USE_LIGHT_THEME
+ _port_colors[JACK_AUDIO] = _default_port_colors[JACK_AUDIO] = 0xA4BC8CFF;
+ _port_colors[JACK_MIDI] = _default_port_colors[JACK_MIDI] = 0xC89595FF;
+ _port_colors[ALSA_MIDI] = _default_port_colors[ALSA_MIDI] = 0x8F7198FF;
+ _port_colors[JACK_OSC] = _default_port_colors[JACK_OSC] = 0x7E8EAAFF;
+ _port_colors[JACK_CV] = _default_port_colors[JACK_CV] = 0x83AFABFF;
+#else
+ _port_colors[JACK_AUDIO] = _default_port_colors[JACK_AUDIO] = 0x3E5E00FF;
+ _port_colors[JACK_MIDI] = _default_port_colors[JACK_MIDI] = 0x650300FF;
+ _port_colors[ALSA_MIDI] = _default_port_colors[ALSA_MIDI] = 0x2D0043FF;
+ _port_colors[JACK_OSC] = _default_port_colors[JACK_OSC] = 0x4100FEFF;
+ _port_colors[JACK_CV] = _default_port_colors[JACK_CV] = 0x005E4EFF;
+#endif
+}
+
+bool
+Configuration::get_module_location(const std::string& name, ModuleType type, Coord& loc)
+{
+ std::map<std::string, ModuleSettings>::const_iterator i = _module_settings.find(name);
+ if (i == _module_settings.end()) {
+ return false;
+ }
+
+ const ModuleSettings& settings = (*i).second;
+ if (type == Input && settings.input_location) {
+ loc = *settings.input_location;
+ } else if (type == Output && settings.output_location) {
+ loc = *settings.output_location;
+ } else if (type == InputOutput && settings.inout_location) {
+ loc = *settings.inout_location;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void
+Configuration::set_module_location(const std::string& name, ModuleType type, Coord loc)
+{
+ std::map<std::string, ModuleSettings>::iterator i = _module_settings.find(name);
+ if (i == _module_settings.end()) {
+ i = _module_settings.insert(
+ std::make_pair(name, ModuleSettings(type != InputOutput))).first;
+ }
+
+ ModuleSettings& settings = (*i).second;
+ switch (type) {
+ case Input:
+ settings.input_location = loc;
+ break;
+ case Output:
+ settings.output_location = loc;
+ break;
+ case InputOutput:
+ settings.inout_location = loc;
+ break;
+ default:
+ break; // shouldn't reach here
+ }
+}
+
+/** Returns whether or not this module should be split.
+ *
+ * If nothing is known about the given module, `default_val` is returned (this is
+ * to allow driver's to request terminal ports get split by default).
+ */
+bool
+Configuration::get_module_split(const std::string& name, bool default_val) const
+{
+ std::map<std::string, ModuleSettings>::const_iterator i = _module_settings.find(name);
+ if (i == _module_settings.end()) {
+ return default_val;
+ }
+
+ return (*i).second.split;
+}
+
+void
+Configuration::set_module_split(const std::string& name, bool split)
+{
+ _module_settings[name].split = split;
+}
+
+/** Return a vector of filenames in descending order by preference. */
+static std::vector<std::string>
+get_filenames()
+{
+ std::vector<std::string> filenames;
+ std::string prefix;
+
+ const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
+ const char* home = getenv("HOME");
+
+ // XDG spec
+ if (xdg_config_home) {
+ filenames.push_back(std::string(xdg_config_home) + "/patchagerc");
+ } else if (home) {
+ filenames.push_back(std::string(home) + "/.config/patchagerc");
+ }
+
+ // Old location
+ if (home) {
+ filenames.push_back(std::string(home) + "/.patchagerc");
+ }
+
+ // Current directory (bundle or last-ditch effort)
+ filenames.push_back("patchagerc");
+
+ return filenames;
+}
+
+void
+Configuration::load()
+{
+ // Try to find a readable configuration file
+ const std::vector<std::string> filenames = get_filenames();
+ std::ifstream file;
+ for (size_t i = 0; i < filenames.size(); ++i) {
+ file.open(filenames[i].c_str(), std::ios::in);
+ if (file.good()) {
+ std::cout << "Loading configuration from " << filenames[i] << std::endl;
+ break;
+ }
+ }
+
+ if (!file.good()) {
+ std::cout << "No configuration file present" << std::endl;
+ return;
+ }
+
+ _module_settings.clear();
+ while (file.good()) {
+ std::string key;
+ if (file.peek() == '\"') {
+ /* Old versions omitted the module_position key and listed
+ positions starting with module name in quotes. */
+ key = "module_position";
+ } else {
+ file >> key;
+ }
+
+ if (key == "window_location") {
+ file >> _window_location.x >> _window_location.y;
+ } else if (key == "window_size") {
+ file >> _window_size.x >> _window_size.y;
+ } else if (key == "zoom_level") {
+ file >> _zoom;
+ } else if (key == "font_size") {
+ file >> _font_size;
+ } else if (key == "show_toolbar") {
+ file >> _show_toolbar;
+ } else if (key == "sprung_layout") {
+ file >> _sprung_layout;
+ } else if (key == "show_messages") {
+ file >> _show_messages;
+ } else if (key == "sort_ports") {
+ file >> _sort_ports;
+ } else if (key == "messages_height") {
+ file >> _messages_height;
+ } else if (key == "port_color") {
+ std::string type_name;
+ uint32_t rgba;
+ file >> type_name;
+ file.ignore(1, '#');
+ file >> std::hex >> std::uppercase;
+ file >> rgba;
+ file >> std::dec >> std::nouppercase;
+
+ bool found = false;
+ for (int i = 0; i < N_PORT_TYPES; ++i) {
+ if (type_name == port_type_names[i]) {
+ _port_colors[i] = rgba;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ std::cerr << "error: color for unknown port type `"
+ << type_name << "'" << std::endl;
+ }
+ } else if (key == "module_position" || key[0] == '\"') {
+ Coord loc;
+ std::string name;
+ file.ignore(1, '\"');
+ std::getline(file, name, '\"');
+
+ ModuleType type;
+ std::string type_str;
+ file >> type_str;
+ if (type_str == "input") {
+ type = Input;
+ } else if (type_str == "output") {
+ type = Output;
+ } else if (type_str == "inputoutput") {
+ type = InputOutput;
+ } else {
+ std::cerr << "error: bad position type `" << type_str
+ << "' for module `" << name << "'" << std::endl;
+ file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+ continue;
+ }
+
+ file >> loc.x;
+ file >> loc.y;
+
+ set_module_location(name, type, loc);
+ } else {
+ std::cerr << "warning: unknown configuration key `" << key << "'"
+ << std::endl;
+ file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+ }
+
+ // Skip trailing whitespace, including newline
+ while (file.good() && isspace(file.peek())) {
+ file.ignore(1);
+ }
+ }
+
+ file.close();
+}
+
+static inline void
+write_module_position(std::ofstream& os,
+ const std::string& name,
+ const char* type,
+ const Coord& loc)
+{
+ os << "module_position \"" << name << "\""
+ << " " << type << " " << loc.x << " " << loc.y << std::endl;
+}
+
+void
+Configuration::save()
+{
+ // Try to find a writable configuration file
+ const std::vector<std::string> filenames = get_filenames();
+ std::ofstream file;
+ for (size_t i = 0; i < filenames.size(); ++i) {
+ file.open(filenames[i].c_str(), std::ios::out);
+ if (file.good()) {
+ std::cout << "Writing configuration to " << filenames[i] << std::endl;
+ break;
+ }
+ }
+
+ if (!file.good()) {
+ std::cout << "Unable to open configuration file to write" << std::endl;
+ return;
+ }
+
+ file << "window_location " << _window_location.x << " " << _window_location.y << std::endl;
+ file << "window_size " << _window_size.x << " " << _window_size.y << std::endl;
+ file << "zoom_level " << _zoom << std::endl;
+ file << "font_size " << _font_size << std::endl;
+ file << "show_toolbar " << _show_toolbar << std::endl;
+ file << "sprung_layout " << _sprung_layout << std::endl;
+ file << "show_messages " << _show_messages << std::endl;
+ file << "sort_ports " << _sort_ports << std::endl;
+ file << "messages_height " << _messages_height << std::endl;
+
+ file << std::hex << std::uppercase;
+ for (int i = 0; i < N_PORT_TYPES; ++i) {
+ if (_port_colors[i] != _default_port_colors[i]) {
+ file << "port_color " << port_type_names[i] << " " << _port_colors[i] << std::endl;
+ }
+ }
+ file << std::dec << std::nouppercase;
+
+ for (std::map<std::string, ModuleSettings>::iterator i = _module_settings.begin();
+ i != _module_settings.end(); ++i) {
+ const ModuleSettings& settings = (*i).second;
+ const std::string& name = (*i).first;
+
+ if (settings.split) {
+ if (settings.input_location && settings.output_location) {
+ write_module_position(file, name, "input", *settings.input_location);
+ write_module_position(file, name, "output", *settings.output_location);
+ }
+ } else if (settings.inout_location) {
+ write_module_position(file, name, "inputoutput", *settings.inout_location);
+ }
+ }
+
+ file.close();
+}
diff --git a/src/Configuration.hpp b/src/Configuration.hpp
new file mode 100644
index 0000000..127a4a8
--- /dev/null
+++ b/src/Configuration.hpp
@@ -0,0 +1,109 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_CONFIGURATION_HPP
+#define PATCHAGE_CONFIGURATION_HPP
+
+#include <stdint.h>
+
+#include <string>
+#include <list>
+#include <map>
+
+#include <boost/optional.hpp>
+
+enum ModuleType { Input, Output, InputOutput };
+
+enum PortType { JACK_AUDIO, JACK_MIDI, ALSA_MIDI, JACK_OSC, JACK_CV };
+
+#define N_PORT_TYPES 5
+
+struct Coord {
+ Coord(double x_=0, double y_=0) : x(x_), y(y_) {}
+ double x;
+ double y;
+};
+
+class Configuration
+{
+public:
+ Configuration();
+
+ void load();
+ void save();
+
+ bool get_module_location(const std::string& name, ModuleType type, Coord& loc);
+ void set_module_location(const std::string& name, ModuleType type, Coord loc);
+
+ void set_module_split(const std::string& name, bool split);
+ bool get_module_split(const std::string& name, bool default_val) const;
+
+ float get_zoom() const { return _zoom; }
+ void set_zoom(float zoom) { _zoom = zoom; }
+ float get_font_size() const { return _font_size; }
+ void set_font_size(float font_size) { _font_size = font_size; }
+
+ float get_show_toolbar() const { return _show_toolbar; }
+ void set_show_toolbar(float show_toolbar) { _show_toolbar = show_toolbar; }
+
+ float get_sprung_layout() const { return _sprung_layout; }
+ void set_sprung_layout(float sprung_layout) { _sprung_layout = sprung_layout; }
+
+ bool get_show_messages() const { return _show_messages; }
+ void set_show_messages(bool show_messages) { _show_messages = show_messages; }
+
+ bool get_sort_ports() const { return _sort_ports; }
+ void set_sort_ports(bool sort_ports) { _sort_ports = sort_ports; }
+
+ int get_messages_height() const { return _messages_height; }
+ void set_messages_height(int height) { _messages_height = height; }
+
+ uint32_t get_port_color(PortType type) const { return _port_colors[type]; }
+ void set_port_color(PortType type, uint32_t rgba) {
+ _port_colors[type] = rgba;
+ }
+
+ Coord get_window_location() { return _window_location; }
+ void set_window_location(Coord loc) { _window_location = loc; }
+ Coord get_window_size() { return _window_size; }
+ void set_window_size(Coord size) { _window_size = size; }
+
+private:
+ struct ModuleSettings {
+ ModuleSettings(bool s=false) : split(s) {}
+ boost::optional<Coord> input_location;
+ boost::optional<Coord> output_location;
+ boost::optional<Coord> inout_location;
+ bool split;
+ };
+
+ std::map<std::string, ModuleSettings> _module_settings;
+
+ uint32_t _default_port_colors[N_PORT_TYPES];
+ uint32_t _port_colors[N_PORT_TYPES];
+
+ Coord _window_location;
+ Coord _window_size;
+ float _zoom;
+ float _font_size;
+ int _messages_height;
+ bool _show_toolbar;
+ bool _sprung_layout;
+ bool _show_messages;
+ bool _sort_ports;
+};
+
+#endif // PATCHAGE_CONFIGURATION_HPP
diff --git a/src/Driver.hpp b/src/Driver.hpp
new file mode 100644
index 0000000..3837382
--- /dev/null
+++ b/src/Driver.hpp
@@ -0,0 +1,55 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_DRIVER_HPP
+#define PATCHAGE_DRIVER_HPP
+
+#include <boost/shared_ptr.hpp>
+#include <sigc++/sigc++.h>
+
+#include "PatchageEvent.hpp"
+
+class PatchagePort;
+class PatchageCanvas;
+
+/** Trival driver base class */
+class Driver {
+public:
+ virtual ~Driver() {}
+
+ virtual void process_events(Patchage* app) = 0;
+
+ virtual void attach(bool launch_daemon) = 0;
+ virtual void detach() = 0;
+ virtual bool is_attached() const = 0;
+
+ virtual void refresh() = 0;
+ virtual void destroy_all() {}
+
+ virtual PatchagePort* create_port_view(Patchage* patchage,
+ const PortID& id) = 0;
+
+ virtual bool connect(PatchagePort* src_port,
+ PatchagePort* dst_port) = 0;
+
+ virtual bool disconnect(PatchagePort* src_port,
+ PatchagePort* dst_port) = 0;
+
+ sigc::signal<void> signal_attached;
+ sigc::signal<void> signal_detached;
+};
+
+#endif // PATCHAGE_DRIVER_HPP
diff --git a/src/JackDbusDriver.cpp b/src/JackDbusDriver.cpp
new file mode 100644
index 0000000..7953051
--- /dev/null
+++ b/src/JackDbusDriver.cpp
@@ -0,0 +1,1048 @@
+/* This file is part of Patchage.
+ * Copyright 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+#include <cstring>
+#include <string>
+#include <set>
+
+#include "patchage_config.h"
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <boost/format.hpp>
+
+#include "Driver.hpp"
+#include "JackDbusDriver.hpp"
+#include "Patchage.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageEvent.hpp"
+#include "PatchageModule.hpp"
+
+#define JACKDBUS_SERVICE "org.jackaudio.service"
+#define JACKDBUS_OBJECT "/org/jackaudio/Controller"
+#define JACKDBUS_IFACE_CONTROL "org.jackaudio.JackControl"
+#define JACKDBUS_IFACE_PATCHBAY "org.jackaudio.JackPatchbay"
+
+#define JACKDBUS_CALL_DEFAULT_TIMEOUT 1000 // in milliseconds
+
+#define JACKDBUS_PORT_FLAG_INPUT 0x00000001
+#define JACKDBUS_PORT_FLAG_OUTPUT 0x00000002
+#define JACKDBUS_PORT_FLAG_PHYSICAL 0x00000004
+#define JACKDBUS_PORT_FLAG_CAN_MONITOR 0x00000008
+#define JACKDBUS_PORT_FLAG_TERMINAL 0x00000010
+
+#define JACKDBUS_PORT_TYPE_AUDIO 0
+#define JACKDBUS_PORT_TYPE_MIDI 1
+
+//#define USE_FULL_REFRESH
+
+JackDriver::JackDriver(Patchage* app)
+ : _app(app)
+ , _dbus_connection(0)
+ , _max_dsp_load(0)
+ , _server_responding(false)
+ , _server_started(false)
+ , _graph_version(0)
+{
+ dbus_error_init(&_dbus_error);
+}
+
+JackDriver::~JackDriver()
+{
+ if (_dbus_connection) {
+ dbus_connection_flush(_dbus_connection);
+ }
+
+ if (dbus_error_is_set(&_dbus_error)) {
+ dbus_error_free(&_dbus_error);
+ }
+}
+
+static bool
+is_jack_port(const PatchagePort* port)
+{
+ return port->type() == JACK_AUDIO || port->type() == JACK_MIDI;
+}
+
+/** Destroy all JACK (canvas) ports.
+ */
+void
+JackDriver::destroy_all()
+{
+ _app->canvas()->remove_ports(is_jack_port);
+}
+
+void
+JackDriver::update_attached()
+{
+ bool was_attached = _server_started;
+ _server_started = is_started();
+
+ if (!_server_responding) {
+ if (was_attached) {
+ signal_detached.emit();
+ }
+ return;
+ }
+
+ if (_server_started && !was_attached) {
+ signal_attached.emit();
+ return;
+ }
+
+ if (!_server_started && was_attached) {
+ signal_detached.emit();
+ return;
+ }
+}
+
+void
+JackDriver::on_jack_appeared()
+{
+ info_msg("JACK appeared.");
+ update_attached();
+}
+
+void
+JackDriver::on_jack_disappeared()
+{
+ info_msg("JACK disappeared.");
+
+ // we are not calling update_attached() here, because it will activate jackdbus
+
+ _server_responding = false;
+
+ if (_server_started) {
+ signal_detached.emit();
+ }
+
+ _server_started = false;
+}
+
+/** Handle signals we have subscribed for in attach(). */
+DBusHandlerResult
+JackDriver::dbus_message_hook(DBusConnection* connection,
+ DBusMessage* message,
+ void* jack_driver)
+{
+ const char* client2_name;
+ const char* client_name;
+ const char* new_owner;
+ const char* object_name;
+ const char* old_owner;
+ const char* port2_name;
+ const char* port_name;
+ dbus_uint32_t port_flags;
+ dbus_uint32_t port_type;
+ dbus_uint64_t client2_id;
+ dbus_uint64_t client_id;
+ dbus_uint64_t connection_id;
+ dbus_uint64_t new_graph_version;
+ dbus_uint64_t port2_id;
+ dbus_uint64_t port_id;
+
+ assert(jack_driver);
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ assert(me->_dbus_connection);
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+ if (!dbus_message_get_args( message, &me->_dbus_error,
+ DBUS_TYPE_STRING, &object_name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract "
+ "NameOwnerChanged signal arguments (%s)") % me->_dbus_error.message));
+ dbus_error_free(&me->_dbus_error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (old_owner[0] == '\0') {
+ me->on_jack_appeared();
+ } else if (new_owner[0] == '\0') {
+ me->on_jack_disappeared();
+ }
+ }
+
+#if defined(USE_FULL_REFRESH)
+ if (dbus_message_is_signal(message, JACKDBUS_IFACE_PATCHBAY, "GraphChanged")) {
+ if (!dbus_message_get_args(message, &me->_dbus_error,
+ DBUS_TYPE_UINT64, &new_graph_version,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract "
+ "GraphChanged signal arguments (%s)") % me->_dbus_error.message));
+ dbus_error_free(&me->_dbus_error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (!me->_server_started) {
+ me->_server_started = true;
+ me->signal_attached.emit();
+ }
+
+ if (new_graph_version > me->_graph_version) {
+ me->refresh_internal(false);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+#else
+// if (dbus_message_is_signal(message, JACKDBUS_IFACE_PATCHBAY, "ClientAppeared")) {
+// me->info_msg("ClientAppeared");
+// return DBUS_HANDLER_RESULT_HANDLED;
+// }
+
+// if (dbus_message_is_signal(message, JACKDBUS_IFACE_PATCHBAY, "ClientDisappeared")) {
+// me->info_msg("ClientDisappeared");
+// return DBUS_HANDLER_RESULT_HANDLED;
+// }
+
+ if (dbus_message_is_signal(message, JACKDBUS_IFACE_PATCHBAY, "PortAppeared")) {
+ if (!dbus_message_get_args( message, &me->_dbus_error,
+ DBUS_TYPE_UINT64, &new_graph_version,
+ DBUS_TYPE_UINT64, &client_id,
+ DBUS_TYPE_STRING, &client_name,
+ DBUS_TYPE_UINT64, &port_id,
+ DBUS_TYPE_STRING, &port_name,
+ DBUS_TYPE_UINT32, &port_flags,
+ DBUS_TYPE_UINT32, &port_type,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract "
+ "PortAppeared signal arguments (%s)") % me->_dbus_error.message));
+ dbus_error_free(&me->_dbus_error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ //me->info_msg(str(boost::format("PortAppeared, %s(%llu):%s(%llu), %lu, %lu") % client_name % client_id % port_name % port_id % port_flags % port_type));
+
+ if (!me->_server_started) {
+ me->_server_started = true;
+ me->signal_attached.emit();
+ }
+
+ me->add_port(client_id, client_name, port_id, port_name, port_flags, port_type);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, JACKDBUS_IFACE_PATCHBAY, "PortDisappeared")) {
+ if (!dbus_message_get_args( message, &me->_dbus_error,
+ DBUS_TYPE_UINT64, &new_graph_version,
+ DBUS_TYPE_UINT64, &client_id,
+ DBUS_TYPE_STRING, &client_name,
+ DBUS_TYPE_UINT64, &port_id,
+ DBUS_TYPE_STRING, &port_name,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract "
+ "PortDisappeared signal arguments (%s)") % me->_dbus_error.message));
+ dbus_error_free(&me->_dbus_error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ //me->info_msg(str(boost::format("PortDisappeared, %s(%llu):%s(%llu)") % client_name % client_id % port_name % port_id));
+
+ if (!me->_server_started) {
+ me->_server_started = true;
+ me->signal_attached.emit();
+ }
+
+ me->remove_port(client_id, client_name, port_id, port_name);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, JACKDBUS_IFACE_PATCHBAY, "PortsConnected")) {
+ if (!dbus_message_get_args(message, &me->_dbus_error,
+ DBUS_TYPE_UINT64, &new_graph_version,
+ DBUS_TYPE_UINT64, &client_id,
+ DBUS_TYPE_STRING, &client_name,
+ DBUS_TYPE_UINT64, &port_id,
+ DBUS_TYPE_STRING, &port_name,
+ DBUS_TYPE_UINT64, &client2_id,
+ DBUS_TYPE_STRING, &client2_name,
+ DBUS_TYPE_UINT64, &port2_id,
+ DBUS_TYPE_STRING, &port2_name,
+ DBUS_TYPE_UINT64, &connection_id,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract "
+ "PortsConnected signal arguments (%s)") % me->_dbus_error.message));
+ dbus_error_free(&me->_dbus_error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (!me->_server_started) {
+ me->_server_started = true;
+ me->signal_attached.emit();
+ }
+
+ me->connect_ports(
+ connection_id,
+ client_id, client_name,
+ port_id, port_name,
+ client2_id, client2_name,
+ port2_id, port2_name);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (dbus_message_is_signal(message, JACKDBUS_IFACE_PATCHBAY, "PortsDisconnected")) {
+ if (!dbus_message_get_args(message, &me->_dbus_error,
+ DBUS_TYPE_UINT64, &new_graph_version,
+ DBUS_TYPE_UINT64, &client_id,
+ DBUS_TYPE_STRING, &client_name,
+ DBUS_TYPE_UINT64, &port_id,
+ DBUS_TYPE_STRING, &port_name,
+ DBUS_TYPE_UINT64, &client2_id,
+ DBUS_TYPE_STRING, &client2_name,
+ DBUS_TYPE_UINT64, &port2_id,
+ DBUS_TYPE_STRING, &port2_name,
+ DBUS_TYPE_UINT64, &connection_id,
+ DBUS_TYPE_INVALID)) {
+ me->error_msg(str(boost::format("dbus_message_get_args() failed to extract "
+ "PortsConnected signal arguments (%s)") % me->_dbus_error.message));
+ dbus_error_free(&me->_dbus_error);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (!me->_server_started) {
+ me->_server_started = true;
+ me->signal_attached.emit();
+ }
+
+ me->disconnect_ports(
+ connection_id,
+ client_id, client_name,
+ port_id, port_name,
+ client2_id, client2_name,
+ port2_id, port2_name);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+#endif
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+bool
+JackDriver::call(
+ bool response_expected,
+ const char* iface,
+ const char* method,
+ DBusMessage** reply_ptr_ptr,
+ int in_type, ...)
+{
+ DBusMessage* request_ptr;
+ DBusMessage* reply_ptr;
+ va_list ap;
+
+ request_ptr = dbus_message_new_method_call(
+ JACKDBUS_SERVICE,
+ JACKDBUS_OBJECT,
+ iface,
+ method);
+ if (!request_ptr) {
+ throw std::runtime_error("dbus_message_new_method_call() returned 0");
+ }
+
+ va_start(ap, in_type);
+
+ dbus_message_append_args_valist(request_ptr, in_type, ap);
+
+ va_end(ap);
+
+ // send message and get a handle for a reply
+ reply_ptr = dbus_connection_send_with_reply_and_block(_dbus_connection, request_ptr,
+ JACKDBUS_CALL_DEFAULT_TIMEOUT, &_dbus_error);
+
+ dbus_message_unref(request_ptr);
+
+ if (!reply_ptr) {
+ if (response_expected) {
+ error_msg(str(boost::format("no reply from server when calling method '%s'"
+ ", error is '%s'") % method % _dbus_error.message));
+ }
+ _server_responding = false;
+ dbus_error_free(&_dbus_error);
+ } else {
+ _server_responding = true;
+ *reply_ptr_ptr = reply_ptr;
+ }
+
+ return reply_ptr;
+}
+
+bool
+JackDriver::is_started()
+{
+ DBusMessage* reply_ptr;
+ dbus_bool_t started;
+
+ if (!call(false, JACKDBUS_IFACE_CONTROL, "IsStarted", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return false;
+ }
+
+ if (!dbus_message_get_args(reply_ptr, &_dbus_error,
+ DBUS_TYPE_BOOLEAN, &started,
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply_ptr);
+ dbus_error_free(&_dbus_error);
+ error_msg("decoding reply of IsStarted failed.");
+ return false;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ return started;
+}
+
+void
+JackDriver::start_server()
+{
+ DBusMessage* reply_ptr;
+
+ if (!call(false, JACKDBUS_IFACE_CONTROL, "StartServer", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ update_attached();
+}
+
+void
+JackDriver::stop_server()
+{
+ DBusMessage* reply_ptr;
+
+ if (!call(false, JACKDBUS_IFACE_CONTROL, "StopServer", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ if (!_server_started) {
+ _server_started = false;
+ signal_detached.emit();
+ }
+}
+
+void
+JackDriver::attach(bool launch_daemon)
+{
+ // Connect to the bus
+ _dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &_dbus_error);
+ if (dbus_error_is_set(&_dbus_error)) {
+ error_msg("dbus_bus_get() failed");
+ error_msg(_dbus_error.message);
+ dbus_error_free(&_dbus_error);
+ return;
+ }
+
+ dbus_connection_setup_with_g_main(_dbus_connection, NULL);
+
+ dbus_bus_add_match(_dbus_connection, "type='signal',interface='" DBUS_INTERFACE_DBUS "',member=NameOwnerChanged,arg0='org.jackaudio.service'", NULL);
+#if defined(USE_FULL_REFRESH)
+ dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=GraphChanged", NULL);
+#else
+ // dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=ClientAppeared", NULL);
+ // dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=ClientDisappeared", NULL);
+ dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortAppeared", NULL);
+ dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortDisappeared", NULL);
+ dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortsConnected", NULL);
+ dbus_bus_add_match(_dbus_connection, "type='signal',interface='" JACKDBUS_IFACE_PATCHBAY "',member=PortsDisconnected", NULL);
+#endif
+ dbus_connection_add_filter(_dbus_connection, dbus_message_hook, this, NULL);
+
+ update_attached();
+
+ if (!_server_responding) {
+ return;
+ }
+
+ if (launch_daemon) {
+ start_server();
+ }
+}
+
+void
+JackDriver::detach()
+{
+ stop_server();
+}
+
+bool
+JackDriver::is_attached() const
+{
+ return _dbus_connection && _server_responding;
+}
+
+void
+JackDriver::add_port(PatchageModule* module,
+ PortType type,
+ const std::string& name,
+ bool is_input)
+{
+ if (module->get_port(name)) {
+ return;
+ }
+
+ new PatchagePort(
+ *module,
+ type,
+ name,
+ "", // TODO: pretty name
+ is_input,
+ _app->conf()->get_port_color(type),
+ _app->show_human_names());
+}
+
+void
+JackDriver::add_port(dbus_uint64_t client_id,
+ const char* client_name,
+ dbus_uint64_t port_id,
+ const char* port_name,
+ dbus_uint32_t port_flags,
+ dbus_uint32_t port_type)
+{
+ PortType local_port_type;
+
+ switch (port_type) {
+ case JACKDBUS_PORT_TYPE_AUDIO:
+ local_port_type = JACK_AUDIO;
+ break;
+ case JACKDBUS_PORT_TYPE_MIDI:
+ local_port_type = JACK_MIDI;
+ break;
+ default:
+ error_msg("Unknown JACK D-Bus port type");
+ return;
+ }
+
+ ModuleType type = InputOutput;
+ if (_app->conf()->get_module_split(client_name, port_flags & JACKDBUS_PORT_FLAG_TERMINAL)) {
+ if (port_flags & JACKDBUS_PORT_FLAG_INPUT) {
+ type = Input;
+ } else {
+ type = Output;
+ }
+ }
+
+ PatchageModule* module = find_or_create_module(type, client_name);
+
+ add_port(module, local_port_type, port_name, port_flags & JACKDBUS_PORT_FLAG_INPUT);
+}
+
+void
+JackDriver::remove_port(dbus_uint64_t client_id,
+ const char* client_name,
+ dbus_uint64_t port_id,
+ const char* port_name)
+{
+ PatchagePort* port = _app->canvas()->find_port_by_name(client_name, port_name);
+ if (!port) {
+ error_msg("Unable to remove unknown port");
+ return;
+ }
+
+ PatchageModule* module = dynamic_cast<PatchageModule*>(port->get_module());
+
+ delete port;
+
+ // No empty modules (for now)
+ if (module->num_ports() == 0) {
+ delete module;
+ }
+
+ if (_app->canvas()->empty()) {
+ if (_server_started) {
+ signal_detached.emit();
+ }
+
+ _server_started = false;
+ }
+}
+
+PatchageModule*
+JackDriver::find_or_create_module(
+ ModuleType type,
+ const std::string& name)
+{
+ PatchageModule* module = _app->canvas()->find_module(name, type);
+
+ if (!module) {
+ module = new PatchageModule(_app, name, type);
+ module->load_location();
+ _app->canvas()->add_module(name, module);
+ }
+
+ return module;
+}
+
+void
+JackDriver::connect_ports(dbus_uint64_t connection_id,
+ dbus_uint64_t client1_id,
+ const char* client1_name,
+ dbus_uint64_t port1_id,
+ const char* port1_name,
+ dbus_uint64_t client2_id,
+ const char* client2_name,
+ dbus_uint64_t port2_id,
+ const char* port2_name)
+{
+ PatchagePort* port1 = _app->canvas()->find_port_by_name(client1_name, port1_name);
+ if (!port1) {
+ error_msg((std::string)"Unable to connect unknown port '" + port1_name + "' of client '" + client1_name + "'");
+ return;
+ }
+
+ PatchagePort* port2 = _app->canvas()->find_port_by_name(client2_name, port2_name);
+ if (!port2) {
+ error_msg((std::string)"Unable to connect unknown port '" + port2_name + "' of client '" + client2_name + "'");
+ return;
+ }
+
+ _app->canvas()->connect(port1, port2);
+}
+
+void
+JackDriver::disconnect_ports(dbus_uint64_t connection_id,
+ dbus_uint64_t client1_id,
+ const char* client1_name,
+ dbus_uint64_t port1_id,
+ const char* port1_name,
+ dbus_uint64_t client2_id,
+ const char* client2_name,
+ dbus_uint64_t port2_id,
+ const char* port2_name)
+{
+ PatchagePort* port1 = _app->canvas()->find_port_by_name(client1_name, port1_name);
+ if (!port1) {
+ error_msg((std::string)"Unable to disconnect unknown port '" + port1_name + "' of client '" + client1_name + "'");
+ return;
+ }
+
+ PatchagePort* port2 = _app->canvas()->find_port_by_name(client2_name, port2_name);
+ if (!port2) {
+ error_msg((std::string)"Unable to disconnect unknown port '" + port2_name + "' of client '" + client2_name + "'");
+ return;
+ }
+
+ _app->canvas()->disconnect(port1, port2);
+}
+
+void
+JackDriver::refresh_internal(bool force)
+{
+ DBusMessage* reply_ptr;
+ DBusMessageIter iter;
+ dbus_uint64_t version;
+ const char* reply_signature;
+ DBusMessageIter clients_array_iter;
+ DBusMessageIter client_struct_iter;
+ DBusMessageIter ports_array_iter;
+ DBusMessageIter port_struct_iter;
+ DBusMessageIter connections_array_iter;
+ DBusMessageIter connection_struct_iter;
+ dbus_uint64_t client_id;
+ const char* client_name;
+ dbus_uint64_t port_id;
+ const char* port_name;
+ dbus_uint32_t port_flags;
+ dbus_uint32_t port_type;
+ dbus_uint64_t client2_id;
+ const char* client2_name;
+ dbus_uint64_t port2_id;
+ const char* port2_name;
+ dbus_uint64_t connection_id;
+
+ if (force) {
+ version = 0; // workaround module split/join stupidity
+ } else {
+ version = _graph_version;
+ }
+
+ if (!call(true, JACKDBUS_IFACE_PATCHBAY, "GetGraph", &reply_ptr, DBUS_TYPE_UINT64, &version, DBUS_TYPE_INVALID)) {
+ error_msg("GetGraph() failed.");
+ return;
+ }
+
+ reply_signature = dbus_message_get_signature(reply_ptr);
+
+ if (strcmp(reply_signature, "ta(tsa(tsuu))a(tstststst)") != 0) {
+ error_msg((std::string)"GetGraph() reply signature mismatch. " + reply_signature);
+ goto unref;
+ }
+
+ dbus_message_iter_init(reply_ptr, &iter);
+
+ //info_msg((string)"version " + (char)dbus_message_iter_get_arg_type(&iter));
+ dbus_message_iter_get_basic(&iter, &version);
+ dbus_message_iter_next(&iter);
+
+ if (!force && version <= _graph_version) {
+ goto unref;
+ }
+
+ destroy_all();
+
+ //info_msg(str(boost::format("got new graph version %llu") % version));
+ _graph_version = version;
+
+ //info_msg((string)"clients " + (char)dbus_message_iter_get_arg_type(&iter));
+
+ for (dbus_message_iter_recurse(&iter, &clients_array_iter);
+ dbus_message_iter_get_arg_type(&clients_array_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&clients_array_iter)) {
+ //info_msg((string)"a client " + (char)dbus_message_iter_get_arg_type(&clients_array_iter));
+ dbus_message_iter_recurse(&clients_array_iter, &client_struct_iter);
+
+ dbus_message_iter_get_basic(&client_struct_iter, &client_id);
+ dbus_message_iter_next(&client_struct_iter);
+
+ dbus_message_iter_get_basic(&client_struct_iter, &client_name);
+ dbus_message_iter_next(&client_struct_iter);
+
+ //info_msg((string)"client '" + client_name + "'");
+
+ for (dbus_message_iter_recurse(&client_struct_iter, &ports_array_iter);
+ dbus_message_iter_get_arg_type(&ports_array_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&ports_array_iter)) {
+ //info_msg((string)"a port " + (char)dbus_message_iter_get_arg_type(&ports_array_iter));
+ dbus_message_iter_recurse(&ports_array_iter, &port_struct_iter);
+
+ dbus_message_iter_get_basic(&port_struct_iter, &port_id);
+ dbus_message_iter_next(&port_struct_iter);
+
+ dbus_message_iter_get_basic(&port_struct_iter, &port_name);
+ dbus_message_iter_next(&port_struct_iter);
+
+ dbus_message_iter_get_basic(&port_struct_iter, &port_flags);
+ dbus_message_iter_next(&port_struct_iter);
+
+ dbus_message_iter_get_basic(&port_struct_iter, &port_type);
+ dbus_message_iter_next(&port_struct_iter);
+
+ //info_msg((string)"port: " + port_name);
+
+ add_port(client_id, client_name, port_id, port_name, port_flags, port_type);
+ }
+
+ dbus_message_iter_next(&client_struct_iter);
+ }
+
+ dbus_message_iter_next(&iter);
+
+ for (dbus_message_iter_recurse(&iter, &connections_array_iter);
+ dbus_message_iter_get_arg_type(&connections_array_iter) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&connections_array_iter)) {
+ //info_msg((string)"a connection " + (char)dbus_message_iter_get_arg_type(&connections_array_iter));
+ dbus_message_iter_recurse(&connections_array_iter, &connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &client_id);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &client_name);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &port_id);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &port_name);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &client2_id);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &client2_name);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &port2_id);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &port2_name);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ dbus_message_iter_get_basic(&connection_struct_iter, &connection_id);
+ dbus_message_iter_next(&connection_struct_iter);
+
+ //info_msg(str(boost::format("connection(%llu) %s(%llu):%s(%llu) <-> %s(%llu):%s(%llu)") %
+ // connection_id %
+ // client_name %
+ // client_id %
+ // port_name %
+ // port_id %
+ // client2_name %
+ // client2_id %
+ // port2_name %
+ // port2_id));
+
+ connect_ports(
+ connection_id,
+ client_id, client_name,
+ port_id, port_name,
+ client2_id, client2_name,
+ port2_id, port2_name);
+ }
+
+unref:
+ dbus_message_unref(reply_ptr);
+}
+
+void
+JackDriver::refresh()
+{
+ refresh_internal(true);
+}
+
+bool
+JackDriver::connect(PatchagePort* src,
+ PatchagePort* dst)
+{
+ const char* client1_name = src->get_module()->get_label();
+ const char* port1_name = src->get_label();
+ const char* client2_name = dst->get_module()->get_label();
+ const char* port2_name = dst->get_label();
+
+ DBusMessage* reply_ptr;
+ if (!call(true, JACKDBUS_IFACE_PATCHBAY, "ConnectPortsByName", &reply_ptr,
+ DBUS_TYPE_STRING, &client1_name,
+ DBUS_TYPE_STRING, &port1_name,
+ DBUS_TYPE_STRING, &client2_name,
+ DBUS_TYPE_STRING, &port2_name,
+ DBUS_TYPE_INVALID)) {
+ error_msg("ConnectPortsByName() failed.");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+JackDriver::disconnect(PatchagePort* src,
+ PatchagePort* dst)
+{
+ const char* client1_name = src->get_module()->get_label();
+ const char* port1_name = src->get_label();
+ const char* client2_name = dst->get_module()->get_label();
+ const char* port2_name = dst->get_label();
+
+ DBusMessage* reply_ptr;
+ if (!call(true, JACKDBUS_IFACE_PATCHBAY, "DisconnectPortsByName", &reply_ptr,
+ DBUS_TYPE_STRING, &client1_name,
+ DBUS_TYPE_STRING, &port1_name,
+ DBUS_TYPE_STRING, &client2_name,
+ DBUS_TYPE_STRING, &port2_name,
+ DBUS_TYPE_INVALID)) {
+ error_msg("DisconnectPortsByName() failed.");
+ return false;
+ }
+
+ return true;
+}
+
+jack_nframes_t
+JackDriver::buffer_size()
+{
+ DBusMessage* reply_ptr;
+ dbus_uint32_t buffer_size;
+
+ if (_server_responding && !_server_started) {
+ goto fail;
+ }
+
+ if (!call(true, JACKDBUS_IFACE_CONTROL, "GetBufferSize", &reply_ptr, DBUS_TYPE_INVALID)) {
+ goto fail;
+ }
+
+ if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_UINT32, &buffer_size, DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply_ptr);
+ dbus_error_free(&_dbus_error);
+ error_msg("decoding reply of GetBufferSize failed.");
+ goto fail;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ return buffer_size;
+
+fail:
+ return 4096; // something fake, patchage needs it to match combobox value
+}
+
+bool
+JackDriver::set_buffer_size(jack_nframes_t size)
+{
+ DBusMessage* reply_ptr;
+ dbus_uint32_t buffer_size;
+
+ buffer_size = size;
+
+ if (!call(true, JACKDBUS_IFACE_CONTROL, "SetBufferSize", &reply_ptr, DBUS_TYPE_UINT32, &buffer_size, DBUS_TYPE_INVALID)) {
+ return false;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ return true;
+}
+
+float
+JackDriver::sample_rate()
+{
+ DBusMessage* reply_ptr;
+ double sample_rate;
+
+ if (!call(true, JACKDBUS_IFACE_CONTROL, "GetSampleRate", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return false;
+ }
+
+ if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_DOUBLE, &sample_rate, DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply_ptr);
+ dbus_error_free(&_dbus_error);
+ error_msg("decoding reply of GetSampleRate failed.");
+ return false;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ return sample_rate;
+}
+
+bool
+JackDriver::is_realtime() const
+{
+ DBusMessage* reply_ptr;
+ dbus_bool_t realtime;
+
+ JackDriver* me = const_cast<JackDriver*>(this);
+ if (!me->call(true, JACKDBUS_IFACE_CONTROL, "IsRealtime",
+ &reply_ptr, DBUS_TYPE_INVALID)) {
+ return false;
+ }
+
+ if (!dbus_message_get_args(reply_ptr, &me->_dbus_error, DBUS_TYPE_BOOLEAN,
+ &realtime, DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply_ptr);
+ dbus_error_free(&me->_dbus_error);
+ error_msg("decoding reply of IsRealtime failed.");
+ return false;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ return realtime;
+}
+
+size_t
+JackDriver::get_xruns()
+{
+ DBusMessage* reply_ptr;
+ dbus_uint32_t xruns;
+
+ if (_server_responding && !_server_started) {
+ return 0;
+ }
+
+ if (!call(true, JACKDBUS_IFACE_CONTROL, "GetXruns", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return 0;
+ }
+
+ if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_UINT32, &xruns, DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply_ptr);
+ dbus_error_free(&_dbus_error);
+ error_msg("decoding reply of GetXruns failed.");
+ return 0;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ return xruns;
+}
+
+void
+JackDriver::reset_xruns()
+{
+ DBusMessage* reply_ptr;
+
+ if (!call(true, JACKDBUS_IFACE_CONTROL, "ResetXruns", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return;
+ }
+
+ dbus_message_unref(reply_ptr);
+}
+
+float
+JackDriver::get_max_dsp_load()
+{
+ DBusMessage* reply_ptr;
+ double load;
+
+ if (_server_responding && !_server_started) {
+ return 0.0;
+ }
+
+ if (!call(true, JACKDBUS_IFACE_CONTROL, "GetLoad", &reply_ptr, DBUS_TYPE_INVALID)) {
+ return 0.0;
+ }
+
+ if (!dbus_message_get_args(reply_ptr, &_dbus_error, DBUS_TYPE_DOUBLE, &load, DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply_ptr);
+ dbus_error_free(&_dbus_error);
+ error_msg("decoding reply of GetLoad failed.");
+ return 0.0;
+ }
+
+ dbus_message_unref(reply_ptr);
+
+ load /= 100.0; // convert from percent to [0..1]
+
+ if (load > _max_dsp_load) {
+ _max_dsp_load = load;
+ }
+
+ return _max_dsp_load;
+}
+
+
+void
+JackDriver::reset_max_dsp_load()
+{
+ _max_dsp_load = 0.0;
+}
+
+PatchagePort*
+JackDriver::create_port_view(Patchage* patchage,
+ const PortID& id)
+{
+ assert(false); // we dont use events at all
+ return NULL;
+}
+
+void
+JackDriver::error_msg(const std::string& msg) const
+{
+ _app->error_msg((std::string)"Jack: " + msg);
+}
+
+void
+JackDriver::info_msg(const std::string& msg) const
+{
+ _app->info_msg((std::string)"Jack: " + msg);
+}
diff --git a/src/JackDbusDriver.hpp b/src/JackDbusDriver.hpp
new file mode 100644
index 0000000..69cc0a5
--- /dev/null
+++ b/src/JackDbusDriver.hpp
@@ -0,0 +1,161 @@
+/* This file is part of Patchage.
+ * Copyright 2008 Nedko Arnaudov <nedko@arnaudov.name>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_JACKDBUSDRIVER_HPP
+#define PATCHAGE_JACKDBUSDRIVER_HPP
+
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <jack/jack.h>
+#include <jack/statistics.h>
+#include <glibmm/thread.h>
+#include <dbus/dbus.h>
+#include "Driver.hpp"
+#include "Patchage.hpp"
+#include "PatchageModule.hpp"
+
+class PatchageEvent;
+class PatchageCanvas;
+class PatchagePort;
+
+class JackDriver : public Driver
+{
+public:
+ explicit JackDriver(Patchage* app);
+ ~JackDriver();
+
+ void attach(bool launch_daemon);
+ void detach();
+
+ bool is_attached() const;
+ bool is_realtime() const;
+
+ void refresh();
+ void destroy_all();
+
+ bool connect(
+ PatchagePort* src,
+ PatchagePort* dst);
+
+ bool disconnect(
+ PatchagePort* src,
+ PatchagePort* dst);
+
+ size_t get_xruns();
+ void reset_xruns();
+ float get_max_dsp_load();
+ void reset_max_dsp_load();
+
+ float sample_rate();
+ jack_nframes_t buffer_size();
+ bool set_buffer_size(jack_nframes_t size);
+
+ void process_events(Patchage* app) {}
+
+ PatchagePort* create_port_view(
+ Patchage* patchage,
+ const PortID& ref);
+
+private:
+ void error_msg(const std::string& msg) const;
+ void info_msg(const std::string& msg) const;
+
+ PatchageModule* find_or_create_module(
+ ModuleType type,
+ const std::string& name);
+
+ void add_port(
+ PatchageModule* module,
+ PortType type,
+ const std::string& name,
+ bool is_input);
+
+ void add_port(
+ dbus_uint64_t client_id,
+ const char* client_name,
+ dbus_uint64_t port_id,
+ const char* port_name,
+ dbus_uint32_t port_flags,
+ dbus_uint32_t port_type);
+
+ void remove_port(
+ dbus_uint64_t client_id,
+ const char* client_name,
+ dbus_uint64_t port_id,
+ const char* port_name);
+
+ void connect_ports(
+ dbus_uint64_t connection_id,
+ dbus_uint64_t client1_id,
+ const char* client1_name,
+ dbus_uint64_t port1_id,
+ const char* port1_name,
+ dbus_uint64_t client2_id,
+ const char* client2_name,
+ dbus_uint64_t port2_id,
+ const char* port2_name);
+
+ void disconnect_ports(
+ dbus_uint64_t connection_id,
+ dbus_uint64_t client1_id,
+ const char* client1_name,
+ dbus_uint64_t port1_id,
+ const char* port1_name,
+ dbus_uint64_t client2_id,
+ const char* client2_name,
+ dbus_uint64_t port2_id,
+ const char* port2_name);
+
+ bool call(
+ bool response_expected,
+ const char* iface,
+ const char* method,
+ DBusMessage** reply_ptr_ptr,
+ int in_type,
+ ...);
+
+ void update_attached();
+
+ bool is_started();
+
+ void start_server();
+
+ void stop_server();
+
+ void refresh_internal(bool force);
+
+ static DBusHandlerResult dbus_message_hook(
+ DBusConnection *connection,
+ DBusMessage *message,
+ void *me);
+
+ void on_jack_appeared();
+
+ void on_jack_disappeared();
+
+private:
+ Patchage* _app;
+ DBusError _dbus_error;
+ DBusConnection* _dbus_connection;
+ float _max_dsp_load;
+
+ bool _server_responding;
+ bool _server_started;
+
+ dbus_uint64_t _graph_version;
+};
+
+#endif // PATCHAGE_JACKDBUSDRIVER_HPP
diff --git a/src/JackDriver.cpp b/src/JackDriver.cpp
new file mode 100644
index 0000000..5daedae
--- /dev/null
+++ b/src/JackDriver.cpp
@@ -0,0 +1,588 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cassert>
+#include <cstring>
+#include <set>
+#include <string>
+
+#include <boost/format.hpp>
+
+#include <jack/jack.h>
+#include <jack/statistics.h>
+
+#include "JackDriver.hpp"
+#include "Patchage.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageEvent.hpp"
+#include "PatchageModule.hpp"
+#include "Queue.hpp"
+#include "patchage_config.h"
+#ifdef HAVE_JACK_METADATA
+#include <jack/metadata.h>
+#include "jackey.h"
+#endif
+
+using std::endl;
+using std::string;
+using boost::format;
+
+JackDriver::JackDriver(Patchage* app)
+ : _app(app)
+ , _client(NULL)
+ , _events(128)
+ , _xruns(0)
+ , _xrun_delay(0)
+ , _is_activated(false)
+{
+ _last_pos.frame = 0;
+ _last_pos.valid = (jack_position_bits_t)0;
+}
+
+JackDriver::~JackDriver()
+{
+ detach();
+}
+
+/** Connect to Jack.
+ */
+void
+JackDriver::attach(bool launch_daemon)
+{
+ // Already connected
+ if (_client)
+ return;
+
+ jack_options_t options = (!launch_daemon) ? JackNoStartServer : JackNullOption;
+ _client = jack_client_open("Patchage", options, NULL);
+ if (_client == NULL) {
+ _app->error_msg("Jack: Unable to create client.");
+ _is_activated = false;
+ } else {
+ jack_client_t* const client = _client;
+
+ jack_on_shutdown(client, jack_shutdown_cb, this);
+ jack_set_client_registration_callback(client, jack_client_registration_cb, this);
+ jack_set_port_registration_callback(client, jack_port_registration_cb, this);
+ jack_set_port_connect_callback(client, jack_port_connect_cb, this);
+ jack_set_xrun_callback(client, jack_xrun_cb, this);
+
+ _buffer_size = jack_get_buffer_size(client);
+
+ if (!jack_activate(client)) {
+ _is_activated = true;
+ signal_attached.emit();
+ std::stringstream ss;
+ _app->info_msg("Jack: Attached.");
+ } else {
+ _app->error_msg("Jack: Client activation failed.");
+ _is_activated = false;
+ }
+ }
+}
+
+void
+JackDriver::detach()
+{
+ Glib::Mutex::Lock lock(_shutdown_mutex);
+ if (_client) {
+ jack_deactivate(_client);
+ jack_client_close(_client);
+ _client = NULL;
+ }
+ _is_activated = false;
+ signal_detached.emit();
+ _app->info_msg("Jack: Detached.");
+}
+
+static bool
+is_jack_port(const PatchagePort* port)
+{
+ return (port->type() == JACK_AUDIO ||
+ port->type() == JACK_MIDI ||
+ port->type() == JACK_OSC ||
+ port->type() == JACK_CV);
+}
+
+/** Destroy all JACK (canvas) ports.
+ */
+void
+JackDriver::destroy_all()
+{
+ if (_app->canvas()) {
+ _app->canvas()->remove_ports(is_jack_port);
+ }
+}
+
+PatchagePort*
+JackDriver::create_port_view(Patchage* patchage,
+ const PortID& id)
+{
+ assert(id.type == PortID::JACK_ID);
+
+ jack_port_t* jack_port = jack_port_by_id(_client, id.id.jack_id);
+ if (!jack_port) {
+ _app->error_msg((format("Jack: Failed to find port with ID `%1%'.")
+ % id).str());;
+ return NULL;
+ }
+
+ const int jack_flags = jack_port_flags(jack_port);
+
+ string module_name, port_name;
+ port_names(id, module_name, port_name);
+
+ ModuleType type = InputOutput;
+ if (_app->conf()->get_module_split(
+ module_name, (jack_flags & JackPortIsTerminal))) {
+ if (jack_flags & JackPortIsInput) {
+ type = Input;
+ } else {
+ type = Output;
+ }
+ }
+
+ PatchageModule* parent = _app->canvas()->find_module(module_name, type);
+ if (!parent) {
+ parent = new PatchageModule(patchage, module_name, type);
+ parent->load_location();
+ patchage->canvas()->add_module(module_name, parent);
+ }
+
+ if (parent->get_port(port_name)) {
+ _app->error_msg((format("Jack: Module `%1%' already has port `%2%'.")
+ % module_name % port_name).str());
+ return NULL;
+ }
+
+ PatchagePort* port = create_port(*parent, jack_port, id);
+ port->show();
+ if (port->is_input()) {
+ parent->set_is_source(false);
+ }
+
+ return port;
+}
+
+#ifdef HAVE_JACK_METADATA
+static std::string
+get_property(jack_uuid_t subject, const char* key)
+{
+ std::string result;
+
+ char* value = NULL;
+ char* datatype = NULL;
+ if (!jack_get_property(subject, key, &value, &datatype)) {
+ result = value;
+ }
+ jack_free(datatype);
+ jack_free(value);
+
+ return result;
+}
+#endif
+
+PatchagePort*
+JackDriver::create_port(PatchageModule& parent, jack_port_t* port, PortID id)
+{
+ if (!port) {
+ return NULL;
+ }
+
+ std::string label;
+ boost::optional<int> order;
+
+#ifdef HAVE_JACK_METADATA
+ const jack_uuid_t uuid = jack_port_uuid(port);
+ if (_app->conf()->get_sort_ports()) {
+ const std::string order_str = get_property(uuid, JACKEY_ORDER);
+ label = get_property(uuid, JACK_METADATA_PRETTY_NAME);
+ if (!order_str.empty()) {
+ order = atoi(order_str.c_str());
+ }
+ }
+#endif
+
+ const char* const type_str = jack_port_type(port);
+ PortType port_type;
+ if (!strcmp(type_str, JACK_DEFAULT_AUDIO_TYPE)) {
+ port_type = JACK_AUDIO;
+#ifdef HAVE_JACK_METADATA
+ if (get_property(uuid, JACKEY_SIGNAL_TYPE) == "CV") {
+ port_type = JACK_CV;
+ }
+#endif
+ } else if (!strcmp(type_str, JACK_DEFAULT_MIDI_TYPE)) {
+ port_type = JACK_MIDI;
+#ifdef HAVE_JACK_METADATA
+ if (get_property(uuid, JACKEY_EVENT_TYPES) == "OSC") {
+ port_type = JACK_OSC;
+ }
+#endif
+ } else {
+ _app->warning_msg((format("Jack: Port `%1%' has unknown type `%2%'.")
+ % jack_port_name(port) % type_str).str());
+ return NULL;
+ }
+
+ PatchagePort* ret(
+ new PatchagePort(parent, port_type, jack_port_short_name(port),
+ label,
+ (jack_port_flags(port) & JackPortIsInput),
+ _app->conf()->get_port_color(port_type),
+ _app->show_human_names(),
+ order));
+
+ if (id.type != PortID::NULL_PORT_ID) {
+ dynamic_cast<PatchageCanvas*>(parent.canvas())->index_port(id, ret);
+ }
+
+ return ret;
+}
+
+void
+JackDriver::shutdown()
+{
+ signal_detached.emit();
+}
+
+/** Refresh all Jack audio ports/connections.
+ * To be called from GTK thread only.
+ */
+void
+JackDriver::refresh()
+{
+ const char** ports;
+ jack_port_t* port;
+
+ // Jack can take _client away from us at any time throughout here :/
+ // Shortest locks possible is the best solution I can figure out
+
+ Glib::Mutex::Lock lock(_shutdown_mutex);
+
+ if (_client == NULL) {
+ shutdown();
+ return;
+ }
+
+ ports = jack_get_ports(_client, NULL, NULL, 0); // get all existing ports
+
+ if (!ports) {
+ return;
+ }
+
+ string client1_name;
+ string port1_name;
+ string client2_name;
+ string port2_name;
+ size_t colon;
+
+ // Add all ports
+ for (int i = 0; ports[i]; ++i) {
+ port = jack_port_by_name(_client, ports[i]);
+
+ client1_name = ports[i];
+ client1_name = client1_name.substr(0, client1_name.find(":"));
+
+ ModuleType type = InputOutput;
+ if (_app->conf()->get_module_split(
+ client1_name,
+ (jack_port_flags(port) & JackPortIsTerminal))) {
+ if (jack_port_flags(port) & JackPortIsInput) {
+ type = Input;
+ } else {
+ type = Output;
+ }
+ }
+
+ PatchageModule* m = _app->canvas()->find_module(client1_name, type);
+
+ if (!m) {
+ m = new PatchageModule(_app, client1_name, type);
+ m->load_location();
+ _app->canvas()->add_module(client1_name, m);
+ }
+
+ if (!m->get_port(jack_port_short_name(port)))
+ create_port(*m, port, PortID());
+ }
+
+ // Add all connections
+ for (int i = 0; ports[i]; ++i) {
+ port = jack_port_by_name(_client, ports[i]);
+ const char** connected_ports = jack_port_get_all_connections(_client, port);
+
+ client1_name = ports[i];
+ colon = client1_name.find(':');
+ port1_name = client1_name.substr(colon + 1);
+ client1_name = client1_name.substr(0, colon);
+
+ const ModuleType port1_type = (jack_port_flags(port) & JackPortIsInput)
+ ? Input : Output;
+
+ PatchageModule* client1_module
+ = _app->canvas()->find_module(client1_name, port1_type);
+
+ if (connected_ports) {
+ for (int j = 0; connected_ports[j]; ++j) {
+
+ client2_name = connected_ports[j];
+ colon = client2_name.find(':');
+ port2_name = client2_name.substr(colon+1);
+ client2_name = client2_name.substr(0, colon);
+
+ const ModuleType port2_type = (port1_type == Input) ? Output : Input;
+
+ PatchageModule* client2_module
+ = _app->canvas()->find_module(client2_name, port2_type);
+
+ Ganv::Port* port1 = client1_module->get_port(port1_name);
+ Ganv::Port* port2 = client2_module->get_port(port2_name);
+
+ if (!port1 || !port2)
+ continue;
+
+ Ganv::Port* src = NULL;
+ Ganv::Port* dst = NULL;
+
+ if (port1->is_output() && port2->is_input()) {
+ src = port1;
+ dst = port2;
+ } else {
+ src = port2;
+ dst = port1;
+ }
+
+ if (src && dst && !_app->canvas()->get_edge(src, dst))
+ _app->canvas()->make_connection(src, dst);
+ }
+
+ jack_free(connected_ports);
+ }
+ }
+
+ jack_free(ports);
+}
+
+bool
+JackDriver::port_names(const PortID& id,
+ string& module_name,
+ string& port_name)
+{
+ jack_port_t* jack_port = NULL;
+
+ if (id.type == PortID::JACK_ID)
+ jack_port = jack_port_by_id(_client, id.id.jack_id);
+
+ if (!jack_port) {
+ module_name.clear();
+ port_name.clear();
+ return false;
+ }
+
+ const string full_name = jack_port_name(jack_port);
+
+ module_name = full_name.substr(0, full_name.find(":"));
+ port_name = full_name.substr(full_name.find(":")+1);
+
+ return true;
+}
+
+/** Connects two Jack audio ports.
+ * To be called from GTK thread only.
+ * \return Whether connection succeeded.
+ */
+bool
+JackDriver::connect(PatchagePort* src_port,
+ PatchagePort* dst_port)
+{
+ if (_client == NULL)
+ return false;
+
+ int result = jack_connect(_client, src_port->full_name().c_str(), dst_port->full_name().c_str());
+
+ if (result == 0)
+ _app->info_msg(string("Jack: Connected ")
+ + src_port->full_name() + " => " + dst_port->full_name());
+ else
+ _app->error_msg(string("Jack: Unable to connect ")
+ + src_port->full_name() + " => " + dst_port->full_name());
+
+ return (!result);
+}
+
+/** Disconnects two Jack audio ports.
+ * To be called from GTK thread only.
+ * \return Whether disconnection succeeded.
+ */
+bool
+JackDriver::disconnect(PatchagePort* const src_port,
+ PatchagePort* const dst_port)
+{
+ if (_client == NULL)
+ return false;
+
+ int result = jack_disconnect(_client, src_port->full_name().c_str(), dst_port->full_name().c_str());
+
+ if (result == 0)
+ _app->info_msg(string("Jack: Disconnected ")
+ + src_port->full_name() + " => " + dst_port->full_name());
+ else
+ _app->error_msg(string("Jack: Unable to disconnect ")
+ + src_port->full_name() + " => " + dst_port->full_name());
+
+ return (!result);
+}
+
+void
+JackDriver::jack_client_registration_cb(const char* name, int registered, void* jack_driver)
+{
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ assert(me->_client);
+
+ if (registered) {
+ me->_events.push(PatchageEvent(PatchageEvent::CLIENT_CREATION, name));
+ } else {
+ me->_events.push(PatchageEvent(PatchageEvent::CLIENT_DESTRUCTION, name));
+ }
+}
+
+void
+JackDriver::jack_port_registration_cb(jack_port_id_t port_id, int registered, void* jack_driver)
+{
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ assert(me->_client);
+
+ if (registered) {
+ me->_events.push(PatchageEvent(PatchageEvent::PORT_CREATION, port_id));
+ } else {
+ me->_events.push(PatchageEvent(PatchageEvent::PORT_DESTRUCTION, port_id));
+ }
+}
+
+void
+JackDriver::jack_port_connect_cb(jack_port_id_t src, jack_port_id_t dst, int connect, void* jack_driver)
+{
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ assert(me->_client);
+
+ if (connect) {
+ me->_events.push(PatchageEvent(PatchageEvent::CONNECTION, src, dst));
+ } else {
+ me->_events.push(PatchageEvent(PatchageEvent::DISCONNECTION, src, dst));
+ }
+}
+
+int
+JackDriver::jack_xrun_cb(void* jack_driver)
+{
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ assert(me->_client);
+
+ ++me->_xruns;
+ me->_xrun_delay = jack_get_xrun_delayed_usecs(me->_client);
+
+ jack_reset_max_delayed_usecs(me->_client);
+
+ return 0;
+}
+
+void
+JackDriver::jack_shutdown_cb(void* jack_driver)
+{
+ assert(jack_driver);
+ JackDriver* me = reinterpret_cast<JackDriver*>(jack_driver);
+ me->_app->info_msg("Jack: Shutdown.");
+ Glib::Mutex::Lock lock(me->_shutdown_mutex);
+ me->_client = NULL;
+ me->_is_activated = false;
+ me->signal_detached.emit();
+}
+
+jack_nframes_t
+JackDriver::buffer_size()
+{
+ if (_is_activated)
+ return _buffer_size;
+ else
+ return jack_get_buffer_size(_client);
+}
+
+void
+JackDriver::reset_xruns()
+{
+ _xruns = 0;
+ _xrun_delay = 0;
+}
+
+float
+JackDriver::get_max_dsp_load()
+{
+ float max_load = 0.0f;
+ if (_client) {
+ const float max_delay = jack_get_max_delayed_usecs(_client);
+ const float rate = sample_rate();
+ const float size = buffer_size();
+ const float period = size / rate * 1000000; // usec
+
+ if (max_delay > period) {
+ max_load = 1.0;
+ jack_reset_max_delayed_usecs(_client);
+ } else {
+ max_load = max_delay / period;
+ }
+ }
+ return max_load;
+}
+
+void
+JackDriver::reset_max_dsp_load()
+{
+ if (_client) {
+ jack_reset_max_delayed_usecs(_client);
+ }
+}
+
+bool
+JackDriver::set_buffer_size(jack_nframes_t size)
+{
+ if (buffer_size() == size) {
+ return true;
+ }
+
+ if (!_client) {
+ _buffer_size = size;
+ return true;
+ }
+
+ if (jack_set_buffer_size(_client, size)) {
+ _app->error_msg("[JACK] Unable to set buffer size");
+ return false;
+ } else {
+ _buffer_size = size;
+ return true;
+ }
+}
+
+void
+JackDriver::process_events(Patchage* app)
+{
+ while (!_events.empty()) {
+ PatchageEvent& ev = _events.front();
+ ev.execute(app);
+ _events.pop();
+ }
+}
diff --git a/src/JackDriver.hpp b/src/JackDriver.hpp
new file mode 100644
index 0000000..875bd61
--- /dev/null
+++ b/src/JackDriver.hpp
@@ -0,0 +1,109 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_JACKDRIVER_HPP
+#define PATCHAGE_JACKDRIVER_HPP
+
+#include <string>
+
+#include <jack/jack.h>
+
+#include <glibmm/thread.h>
+
+#include "Driver.hpp"
+#include "Queue.hpp"
+
+class Patchage;
+class PatchageEvent;
+class PatchageCanvas;
+class PatchagePort;
+class PatchageModule;
+
+/** Handles all externally driven functionality, registering ports etc.
+ *
+ * Jack callbacks and connect methods and things like that live here.
+ * Right now just for jack ports, but that will change...
+ */
+class JackDriver : public Driver
+{
+public:
+ explicit JackDriver(Patchage* app);
+ ~JackDriver();
+
+ void attach(bool launch_daemon);
+ void detach();
+
+ bool is_attached() const { return (_client != NULL); }
+ bool is_realtime() const { return _client && jack_is_realtime(_client); }
+
+ void refresh();
+ void destroy_all();
+
+ bool port_names(const PortID& id,
+ std::string& module_name,
+ std::string& port_name);
+
+ PatchagePort* create_port_view(Patchage* patchage,
+ const PortID& id);
+
+ bool connect(PatchagePort* src,
+ PatchagePort* dst);
+
+ bool disconnect(PatchagePort* src,
+ PatchagePort* dst);
+
+ uint32_t get_xruns() { return _xruns; }
+ void reset_xruns();
+ float get_max_dsp_load();
+ void reset_max_dsp_load();
+
+ jack_client_t* client() { return _client; }
+
+ jack_nframes_t sample_rate() { return jack_get_sample_rate(_client); }
+ jack_nframes_t buffer_size();
+ bool set_buffer_size(jack_nframes_t size);
+
+ void process_events(Patchage* app);
+
+private:
+ PatchagePort* create_port(
+ PatchageModule& parent,
+ jack_port_t* port,
+ PortID id);
+
+ void shutdown();
+
+ static void jack_client_registration_cb(const char* name, int registered, void* me);
+ static void jack_port_registration_cb(jack_port_id_t port_id, int registered, void* me);
+ static void jack_port_connect_cb(jack_port_id_t src, jack_port_id_t dst, int connect, void* me);
+ static int jack_xrun_cb(void* me);
+ static void jack_shutdown_cb(void* me);
+
+ Patchage* _app;
+ jack_client_t* _client;
+
+ Queue<PatchageEvent> _events;
+
+ Glib::Mutex _shutdown_mutex;
+
+ jack_position_t _last_pos;
+ jack_nframes_t _buffer_size;
+ uint32_t _xruns;
+ float _xrun_delay;
+ bool _is_activated :1;
+};
+
+#endif // PATCHAGE_JACKDRIVER_HPP
diff --git a/src/Legend.hpp b/src/Legend.hpp
new file mode 100644
index 0000000..b95d30c
--- /dev/null
+++ b/src/Legend.hpp
@@ -0,0 +1,71 @@
+/* This file is part of Patchage.
+ * Copyright 2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_LEGEND_HPP
+#define PATCHAGE_LEGEND_HPP
+
+#include <gtkmm/colorbutton.h>
+#include <gtkmm/box.h>
+
+#include "Configuration.hpp"
+
+class Legend : public Gtk::HBox {
+public:
+ Legend(const Configuration& configuration) {
+ add_button(JACK_AUDIO, "Audio", configuration.get_port_color(JACK_AUDIO));
+#ifdef HAVE_JACK_METADATA
+ add_button(JACK_CV, "CV", configuration.get_port_color(JACK_CV));
+ add_button(JACK_OSC, "OSC", configuration.get_port_color(JACK_OSC));
+#endif
+ add_button(JACK_MIDI, "MIDI", configuration.get_port_color(JACK_MIDI));
+ add_button(ALSA_MIDI, "ALSA MIDI", configuration.get_port_color(ALSA_MIDI));
+ show_all_children();
+ }
+
+ void add_button(int id, const std::string& label, uint32_t rgba) {
+ Gdk::Color col;
+ col.set_rgb(((rgba >> 24) & 0xFF) * 0x100,
+ ((rgba>> 16) & 0xFF) * 0x100,
+ ((rgba >> 8) & 0xFF) * 0x100);
+ Gtk::HBox* box = new Gtk::HBox();
+ Gtk::ColorButton* but = new Gtk::ColorButton(col);
+ but->set_use_alpha(false);
+ but->signal_color_set().connect(
+ sigc::bind(sigc::mem_fun(this, &Legend::on_color_set),
+ id, label, but));
+
+ box->pack_end(*Gtk::manage(but));
+ box->pack_end(*Gtk::manage(new Gtk::Label(label)), false, false, 2);
+
+ this->pack_start(*Gtk::manage(box), false, false, 6);
+ }
+
+ void on_color_set(const int id,
+ const std::string& label,
+ const Gtk::ColorButton* but) {
+ const Gdk::Color col = but->get_color();
+ const uint32_t rgba = (((col.get_red() / 0x100) << 24) |
+ ((col.get_green() / 0x100) << 16) |
+ ((col.get_blue() / 0x100) << 8) |
+ 0xFF);
+
+ signal_color_changed.emit(id, label, rgba);
+ }
+
+ sigc::signal<void, int, std::string, uint32_t> signal_color_changed;
+};
+
+#endif // PATCHAGE_LEGEND_HPP
diff --git a/src/Patchage.cpp b/src/Patchage.cpp
new file mode 100644
index 0000000..eae2ef9
--- /dev/null
+++ b/src/Patchage.cpp
@@ -0,0 +1,1078 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+
+#include <cmath>
+#include <fstream>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gtk/gtkwindow.h>
+
+#include <boost/format.hpp>
+
+#include <gtkmm/button.h>
+#include <gtkmm/filechooserdialog.h>
+#include <gtkmm/liststore.h>
+#include <gtkmm/menuitem.h>
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/treemodel.h>
+
+#include "ganv/Module.hpp"
+#include "ganv/Edge.hpp"
+
+#include "Configuration.hpp"
+#include "Legend.hpp"
+#include "Patchage.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageEvent.hpp"
+#include "UIFile.hpp"
+#include "patchage_config.h"
+
+#if defined(HAVE_JACK_DBUS)
+ #include "JackDbusDriver.hpp"
+#elif defined(PATCHAGE_LIBJACK)
+ #include "JackDriver.hpp"
+ #include <jack/statistics.h>
+#endif
+
+#ifdef PATCHAGE_JACK_SESSION
+ #include <jack/session.h>
+#endif
+
+#ifdef HAVE_ALSA
+ #include "AlsaDriver.hpp"
+#endif
+
+#ifdef PATCHAGE_GTK_OSX
+ #include <gtkosxapplication.h>
+
+static gboolean
+can_activate_cb(GtkWidget* widget, guint signal_id, gpointer data)
+{
+ return gtk_widget_is_sensitive(widget);
+}
+
+static void
+terminate_cb(GtkosxApplication* app, gpointer data)
+{
+ Patchage* patchage = (Patchage*)data;
+ patchage->save();
+ Gtk::Main::quit();
+}
+
+#endif
+
+static bool
+configure_cb(GtkWindow* parentWindow, GdkEvent* event, gpointer data)
+{
+ ((Patchage*)data)->store_window_location();
+ return FALSE;
+}
+
+static int
+port_order(const GanvPort* a, const GanvPort* b, void* data)
+{
+ const PatchagePort* pa = dynamic_cast<const PatchagePort*>(Glib::wrap(a));
+ const PatchagePort* pb = dynamic_cast<const PatchagePort*>(Glib::wrap(b));
+ if (pa && pb) {
+ if (pa->order() && pb->order()) {
+ return *pa->order() - *pb->order();
+ } else if (pa->order()) {
+ return -1;
+ } else if (pb->order()) {
+ return 1;
+ }
+ return pa->name().compare(pb->name());
+ }
+ return 0;
+}
+
+struct ProjectList_column_record : public Gtk::TreeModel::ColumnRecord {
+ Gtk::TreeModelColumn<Glib::ustring> label;
+};
+
+using std::cout;
+using std::endl;
+using std::string;
+
+#define INIT_WIDGET(x) x(_xml, ((const char*)#x) + 1)
+
+Patchage::Patchage(int argc, char** argv)
+ : _xml(UIFile::open("patchage"))
+#ifdef HAVE_ALSA
+ , _alsa_driver(NULL)
+#endif
+ , _jack_driver(NULL)
+ , _conf(NULL)
+ , INIT_WIDGET(_about_win)
+ , INIT_WIDGET(_main_scrolledwin)
+ , INIT_WIDGET(_main_win)
+ , INIT_WIDGET(_main_vbox)
+ , INIT_WIDGET(_menubar)
+ , INIT_WIDGET(_menu_alsa_connect)
+ , INIT_WIDGET(_menu_alsa_disconnect)
+ , INIT_WIDGET(_menu_file_quit)
+ , INIT_WIDGET(_menu_export_image)
+ , INIT_WIDGET(_menu_help_about)
+ , INIT_WIDGET(_menu_jack_connect)
+ , INIT_WIDGET(_menu_jack_disconnect)
+ , INIT_WIDGET(_menu_open_session)
+ , INIT_WIDGET(_menu_save_session)
+ , INIT_WIDGET(_menu_save_close_session)
+ , INIT_WIDGET(_menu_view_arrange)
+ , INIT_WIDGET(_menu_view_sprung_layout)
+ , INIT_WIDGET(_menu_view_messages)
+ , INIT_WIDGET(_menu_view_toolbar)
+ , INIT_WIDGET(_menu_view_refresh)
+ , INIT_WIDGET(_menu_view_human_names)
+ , INIT_WIDGET(_menu_view_sort_ports)
+ , INIT_WIDGET(_menu_zoom_in)
+ , INIT_WIDGET(_menu_zoom_out)
+ , INIT_WIDGET(_menu_zoom_normal)
+ , INIT_WIDGET(_menu_zoom_full)
+ , INIT_WIDGET(_menu_increase_font_size)
+ , INIT_WIDGET(_menu_decrease_font_size)
+ , INIT_WIDGET(_menu_normal_font_size)
+ , INIT_WIDGET(_toolbar)
+ , INIT_WIDGET(_clear_load_but)
+ , INIT_WIDGET(_xrun_progress)
+ , INIT_WIDGET(_buf_size_combo)
+ , INIT_WIDGET(_latency_label)
+ , INIT_WIDGET(_legend_alignment)
+ , INIT_WIDGET(_main_paned)
+ , INIT_WIDGET(_log_scrolledwindow)
+ , INIT_WIDGET(_status_text)
+ , _legend(NULL)
+ , _pane_initialized(false)
+ , _attach(true)
+ , _driver_detached(false)
+ , _refresh(false)
+ , _enable_refresh(true)
+ , _jack_driver_autoattach(true)
+#ifdef HAVE_ALSA
+ , _alsa_driver_autoattach(true)
+#endif
+{
+ _conf = new Configuration();
+ _canvas = boost::shared_ptr<PatchageCanvas>(new PatchageCanvas(this, 1600*2, 1200*2));
+
+ while (argc > 0) {
+ if (!strcmp(*argv, "-h") || !strcmp(*argv, "--help")) {
+ cout << "Usage: patchage [OPTION]..." << endl;
+ cout << "Visually connect JACK and ALSA Audio/MIDI ports." << endl << endl;
+ cout << "Options:" << endl;
+ cout << "\t-h --help Show this help" << endl;
+ cout << "\t-A --no-alsa Do not automatically attach to ALSA" << endl;
+ cout << "\t-J --no-jack Do not automatically attack to JACK" << endl;
+ exit(0);
+#ifdef HAVE_ALSA
+ } else if (!strcmp(*argv, "-A") || !strcmp(*argv, "--no-alsa")) {
+ _alsa_driver_autoattach = false;
+#endif
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ } else if (!strcmp(*argv, "-J") || !strcmp(*argv, "--no-jack")) {
+ _jack_driver_autoattach = false;
+#endif
+ }
+
+ argv++;
+ argc--;
+ }
+
+ Glib::set_application_name("Patchage");
+ _about_win->property_program_name() = "Patchage";
+ _about_win->property_logo_icon_name() = "patchage";
+ gtk_window_set_default_icon_name("patchage");
+
+ // Create list model for buffer size selector
+ Glib::RefPtr<Gtk::ListStore> buf_size_store = Gtk::ListStore::create(_buf_size_columns);
+ for (size_t i = 32; i <= 4096; i *= 2) {
+ Gtk::TreeModel::Row row = *(buf_size_store->append());
+ row[_buf_size_columns.label] = std::to_string(i);
+ }
+
+ _buf_size_combo->set_model(buf_size_store);
+ _buf_size_combo->pack_start(_buf_size_columns.label);
+
+ _main_scrolledwin->add(_canvas->widget());
+
+ _main_scrolledwin->property_hadjustment().get_value()->set_step_increment(10);
+ _main_scrolledwin->property_vadjustment().get_value()->set_step_increment(10);
+
+ _main_scrolledwin->signal_scroll_event().connect(
+ sigc::mem_fun(this, &Patchage::on_scroll));
+ _clear_load_but->signal_clicked().connect(
+ sigc::mem_fun(this, &Patchage::clear_load));
+ _buf_size_combo->signal_changed().connect(
+ sigc::mem_fun(this, &Patchage::buffer_size_changed));
+ _status_text->signal_size_allocate().connect(
+ sigc::mem_fun(this, &Patchage::on_messages_resized));
+
+#ifdef PATCHAGE_JACK_SESSION
+ _menu_open_session->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::show_open_session_dialog));
+ _menu_save_session->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::show_save_session_dialog));
+ _menu_save_close_session->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::show_save_close_session_dialog));
+#else
+ _menu_open_session->hide();
+ _menu_save_session->hide();
+ _menu_save_close_session->hide();
+#endif
+
+#ifdef HAVE_ALSA
+ _menu_alsa_connect->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::menu_alsa_connect));
+ _menu_alsa_disconnect->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::menu_alsa_disconnect));
+#else
+ _menu_alsa_connect->set_sensitive(false);
+ _menu_alsa_disconnect->set_sensitive(false);
+#endif
+
+ _menu_file_quit->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_quit));
+ _menu_export_image->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_export_image));
+ _menu_view_refresh->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::refresh));
+ _menu_view_human_names->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_view_human_names));
+ _menu_view_sort_ports->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_view_sort_ports));
+ _menu_view_arrange->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_arrange));
+ _menu_view_sprung_layout->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_sprung_layout_toggled));
+ _menu_view_messages->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_view_messages));
+ _menu_view_toolbar->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_view_toolbar));
+ _menu_help_about->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_help_about));
+ _menu_zoom_in->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_zoom_in));
+ _menu_zoom_out->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_zoom_out));
+ _menu_zoom_normal->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_zoom_normal));
+ _menu_zoom_full->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_zoom_full));
+ _menu_increase_font_size->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_increase_font_size));
+ _menu_decrease_font_size->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_decrease_font_size));
+ _menu_normal_font_size->signal_activate().connect(
+ sigc::mem_fun(this, &Patchage::on_normal_font_size));
+
+ if (_canvas->supports_sprung_layout()) {
+ _menu_view_sprung_layout->set_active(true);
+ } else {
+ _menu_view_sprung_layout->set_active(false);
+ _menu_view_sprung_layout->set_sensitive(false);
+ }
+
+ for (int s = Gtk::STATE_NORMAL; s <= Gtk::STATE_INSENSITIVE; ++s) {
+ _status_text->modify_base((Gtk::StateType)s, Gdk::Color("#000000"));
+ _status_text->modify_text((Gtk::StateType)s, Gdk::Color("#FFFFFF"));
+ }
+
+ _error_tag = Gtk::TextTag::create();
+ _error_tag->property_foreground() = "#CC0000";
+ _status_text->get_buffer()->get_tag_table()->add(_error_tag);
+
+ _warning_tag = Gtk::TextTag::create();
+ _warning_tag->property_foreground() = "#C4A000";
+ _status_text->get_buffer()->get_tag_table()->add(_warning_tag);
+
+ _canvas->widget().show();
+ _main_win->present();
+
+ _conf->set_font_size(_canvas->get_default_font_size());
+ _conf->load();
+ _canvas->set_zoom(_conf->get_zoom());
+ _canvas->set_font_size(_conf->get_font_size());
+ if (_conf->get_sort_ports()) {
+ _canvas->set_port_order(port_order, NULL);
+ }
+
+ _main_win->resize(
+ static_cast<int>(_conf->get_window_size().x),
+ static_cast<int>(_conf->get_window_size().y));
+
+ _main_win->move(
+ static_cast<int>(_conf->get_window_location().x),
+ static_cast<int>(_conf->get_window_location().y));
+
+ _legend = new Legend(*_conf);
+ _legend->signal_color_changed.connect(
+ sigc::mem_fun(this, &Patchage::on_legend_color_change));
+ _legend_alignment->add(*Gtk::manage(_legend));
+ _legend->show_all();
+
+ _about_win->set_transient_for(*_main_win);
+#ifdef __APPLE__
+ try {
+ _about_win->set_logo(
+ Gdk::Pixbuf::create_from_file(
+ bundle_location() + "/Resources/Patchage.icns"));
+ } catch (const Glib::Exception& e) {
+ error_msg((boost::format("failed to set logo (%s)") % e.what()).str());
+ }
+#endif
+
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ _jack_driver = new JackDriver(this);
+ _jack_driver->signal_detached.connect(sigc::mem_fun(this, &Patchage::driver_detached));
+
+ _menu_jack_connect->signal_activate().connect(sigc::bind(
+ sigc::mem_fun(_jack_driver, &JackDriver::attach), true));
+ _menu_jack_disconnect->signal_activate().connect(
+ sigc::mem_fun(_jack_driver, &JackDriver::detach));
+#endif
+
+#ifdef HAVE_ALSA
+ _alsa_driver = new AlsaDriver(this);
+#endif
+
+ connect_widgets();
+ update_state();
+ _menu_view_toolbar->set_active(_conf->get_show_toolbar());
+ _menu_view_sprung_layout->set_active(_conf->get_sprung_layout());
+ _menu_view_sort_ports->set_active(_conf->get_sort_ports());
+ _status_text->set_pixels_inside_wrap(2);
+ _status_text->set_left_margin(4);
+ _status_text->set_right_margin(4);
+ _status_text->set_pixels_below_lines(2);
+
+ g_signal_connect(_main_win->gobj(), "configure-event",
+ G_CALLBACK(configure_cb), this);
+
+ _canvas->widget().grab_focus();
+
+ // Idle callback, check if we need to refresh
+ Glib::signal_timeout().connect(
+ sigc::mem_fun(this, &Patchage::idle_callback), 100);
+
+#ifdef PATCHAGE_GTK_OSX
+ // Set up Mac menu bar
+ GtkosxApplication* osxapp = (GtkosxApplication*)g_object_new(
+ GTKOSX_TYPE_APPLICATION, NULL);
+ _menubar->hide();
+ _menu_file_quit->hide();
+ gtkosx_application_set_menu_bar(osxapp, GTK_MENU_SHELL(_menubar->gobj()));
+ gtkosx_application_insert_app_menu_item(
+ osxapp, GTK_WIDGET(_menu_help_about->gobj()), 0);
+ g_signal_connect(_menubar->gobj(), "can-activate-accel",
+ G_CALLBACK(can_activate_cb), NULL);
+ g_signal_connect(osxapp, "NSApplicationWillTerminate",
+ G_CALLBACK(terminate_cb), this);
+ gtkosx_application_ready(osxapp);
+#endif
+}
+
+Patchage::~Patchage()
+{
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ delete _jack_driver;
+#endif
+#ifdef HAVE_ALSA
+ delete _alsa_driver;
+#endif
+
+ delete _conf;
+
+ _about_win.destroy();
+ _xml.reset();
+}
+
+void
+Patchage::attach()
+{
+ _enable_refresh = false;
+
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ if (_jack_driver_autoattach)
+ _jack_driver->attach(true);
+#endif
+
+#ifdef HAVE_ALSA
+ if (_alsa_driver_autoattach)
+ _alsa_driver->attach();
+#endif
+
+ _enable_refresh = true;
+
+ refresh();
+ update_toolbar();
+}
+
+bool
+Patchage::idle_callback()
+{
+ // Initial run, attach
+ if (_attach) {
+ attach();
+ _menu_view_messages->set_active(_conf->get_show_messages());
+ _attach = false;
+ }
+
+ // Process any JACK events
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ if (_jack_driver) {
+ _jack_driver->process_events(this);
+ }
+#endif
+
+ // Process any ALSA events
+#ifdef HAVE_ALSA
+ if (_alsa_driver) {
+ _alsa_driver->process_events(this);
+ }
+#endif
+
+ // Do a full refresh
+ if (_refresh) {
+ refresh();
+ } else if (_driver_detached) {
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ if (_jack_driver && !_jack_driver->is_attached())
+ _jack_driver->destroy_all();
+#endif
+#ifdef HAVE_ALSA
+ if (_alsa_driver && !_alsa_driver->is_attached())
+ _alsa_driver->destroy_all();
+#endif
+ }
+
+ _refresh = false;
+ _driver_detached = false;
+
+ // Update load every 5 idle callbacks
+ static int count = 0;
+ if (++count == 5) {
+ update_load();
+ count = 0;
+ }
+
+ return true;
+}
+
+void
+Patchage::update_toolbar()
+{
+ static bool updating = false;
+ if (updating) {
+ return;
+ } else {
+ updating = true;
+ }
+
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ if (_jack_driver->is_attached()) {
+ const jack_nframes_t buffer_size = _jack_driver->buffer_size();
+ const jack_nframes_t sample_rate = _jack_driver->sample_rate();
+ if (sample_rate != 0) {
+ const int latency_ms = lrintf(buffer_size * 1000 / (float)sample_rate);
+ std::stringstream ss;
+ ss << " frames @ " << (sample_rate / 1000)
+ << "kHz (" << latency_ms << "ms)";
+ _latency_label->set_label(ss.str());
+ _latency_label->set_visible(true);
+ _buf_size_combo->set_active((int)log2f(_jack_driver->buffer_size()) - 5);
+ updating = false;
+ return;
+ }
+ }
+#endif
+ _latency_label->set_visible(false);
+ updating = false;
+}
+
+bool
+Patchage::update_load()
+{
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ if (_jack_driver->is_attached()) {
+ char buf[8];
+ snprintf(buf, sizeof(buf), "%u", _jack_driver->get_xruns());
+ _xrun_progress->set_text(std::string(buf) + " Dropouts");
+ _xrun_progress->set_fraction(_jack_driver->get_max_dsp_load());
+ }
+#endif
+
+ return true;
+}
+
+void
+Patchage::zoom(double z)
+{
+ _conf->set_zoom(z);
+ _canvas->set_zoom(z);
+}
+
+void
+Patchage::refresh()
+{
+ if (_canvas && _enable_refresh) {
+ _canvas->clear();
+
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ if (_jack_driver)
+ _jack_driver->refresh();
+#endif
+
+#ifdef HAVE_ALSA
+ if (_alsa_driver)
+ _alsa_driver->refresh();
+#endif
+ }
+}
+
+void
+Patchage::store_window_location()
+{
+ int loc_x, loc_y, size_x, size_y;
+ _main_win->get_position(loc_x, loc_y);
+ _main_win->get_size(size_x, size_y);
+ Coord window_location;
+ window_location.x = loc_x;
+ window_location.y = loc_y;
+ Coord window_size;
+ window_size.x = size_x;
+ window_size.y = size_y;
+ _conf->set_window_location(window_location);
+ _conf->set_window_size(window_size);
+}
+
+void
+Patchage::clear_load()
+{
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ _xrun_progress->set_fraction(0.0);
+ _jack_driver->reset_xruns();
+ _jack_driver->reset_max_dsp_load();
+#endif
+}
+
+void
+Patchage::error_msg(const std::string& msg)
+{
+ Glib::RefPtr<Gtk::TextBuffer> buffer = _status_text->get_buffer();
+ buffer->insert_with_tag(buffer->end(), std::string("\n") + msg, _error_tag);
+ _status_text->scroll_to_mark(buffer->get_insert(), 0);
+ _menu_view_messages->set_active(true);
+}
+
+void
+Patchage::info_msg(const std::string& msg)
+{
+ Glib::RefPtr<Gtk::TextBuffer> buffer = _status_text->get_buffer();
+ buffer->insert(buffer->end(), std::string("\n") + msg);
+ _status_text->scroll_to_mark(buffer->get_insert(), 0);
+}
+
+void
+Patchage::warning_msg(const std::string& msg)
+{
+ Glib::RefPtr<Gtk::TextBuffer> buffer = _status_text->get_buffer();
+ buffer->insert_with_tag(buffer->end(), std::string("\n") + msg, _warning_tag);
+ _status_text->scroll_to_mark(buffer->get_insert(), 0);
+}
+
+static void
+load_module_location(GanvNode* node, void* data)
+{
+ if (GANV_IS_MODULE(node)) {
+ Ganv::Module* gmod = Glib::wrap(GANV_MODULE(node));
+ PatchageModule* pmod = dynamic_cast<PatchageModule*>(gmod);
+ if (pmod) {
+ pmod->load_location();
+ }
+ }
+}
+
+void
+Patchage::update_state()
+{
+ _canvas->for_each_node(load_module_location, NULL);
+}
+
+/** Update the sensitivity status of menus to reflect the present.
+ *
+ * (eg. disable "Connect to Jack" when Patchage is already connected to Jack)
+ */
+void
+Patchage::connect_widgets()
+{
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ _jack_driver->signal_attached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_jack_connect, &Gtk::MenuItem::set_sensitive), false));
+ _jack_driver->signal_attached.connect(
+ sigc::mem_fun(this, &Patchage::refresh));
+ _jack_driver->signal_attached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_jack_disconnect, &Gtk::MenuItem::set_sensitive), true));
+
+ _jack_driver->signal_detached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_jack_connect, &Gtk::MenuItem::set_sensitive), true));
+ _jack_driver->signal_detached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_jack_disconnect, &Gtk::MenuItem::set_sensitive), false));
+#endif
+
+#ifdef HAVE_ALSA
+ _alsa_driver->signal_attached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_alsa_connect, &Gtk::MenuItem::set_sensitive), false));
+ _alsa_driver->signal_attached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_alsa_disconnect, &Gtk::MenuItem::set_sensitive), true));
+
+ _alsa_driver->signal_detached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_alsa_connect, &Gtk::MenuItem::set_sensitive), true));
+ _alsa_driver->signal_detached.connect(sigc::bind(
+ sigc::mem_fun(*_menu_alsa_disconnect, &Gtk::MenuItem::set_sensitive), false));
+#endif
+}
+
+#ifdef PATCHAGE_JACK_SESSION
+void
+Patchage::show_open_session_dialog()
+{
+ Gtk::FileChooserDialog dialog(*_main_win, "Open Session",
+ Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
+
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ Gtk::Button* open_but = dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+ open_but->property_has_default() = true;
+
+ if (dialog.run() != Gtk::RESPONSE_OK) {
+ return;
+ }
+
+ const std::string dir = dialog.get_filename();
+ if (g_chdir(dir.c_str())) {
+ error_msg("Failed to switch to session directory " + dir);
+ return;
+ }
+
+ if (system("./jack-session") < 0) {
+ error_msg("Error executing `./jack-session' in " + dir);
+ } else {
+ info_msg("Loaded session " + dir);
+ }
+}
+
+static void
+print_edge(GanvEdge* edge, void* data)
+{
+ std::ofstream* script = (std::ofstream*)data;
+ Ganv::Edge* edgemm = Glib::wrap(edge);
+
+ PatchagePort* src = dynamic_cast<PatchagePort*>((edgemm)->get_tail());
+ PatchagePort* dst = dynamic_cast<PatchagePort*>((edgemm)->get_head());
+
+ if (!src || !dst || src->type() == ALSA_MIDI || dst->type() == ALSA_MIDI) {
+ return;
+ }
+
+ (*script) << "jack_connect '" << src->full_name()
+ << "' '" << dst->full_name() << "' &" << endl;
+}
+
+void
+Patchage::save_session(bool close)
+{
+ Gtk::FileChooserDialog dialog(*_main_win, "Save Session",
+ Gtk::FILE_CHOOSER_ACTION_SAVE);
+
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ Gtk::Button* save_but = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+ save_but->property_has_default() = true;
+
+ if (dialog.run() != Gtk::RESPONSE_OK) {
+ return;
+ }
+
+ std::string path = dialog.get_filename();
+ if (g_mkdir_with_parents(path.c_str(), 0740)) {
+ error_msg("Failed to create session directory " + path);
+ return;
+ }
+
+ path += '/';
+ jack_session_command_t* cmd = jack_session_notify(
+ _jack_driver->client(),
+ NULL,
+ close ? JackSessionSaveAndQuit : JackSessionSave,
+ path.c_str());
+
+ const std::string script_path = path + "jack-session";
+ std::ofstream script(script_path.c_str());
+ script << "#!/bin/sh" << endl << endl;
+
+ const std::string var("${SESSION_DIR}");
+ for (int c = 0; cmd[c].uuid; ++c) {
+ std::string command = cmd[c].command;
+ const size_t index = command.find(var);
+ if (index != string::npos) {
+ command.replace(index, var.length(), cmd[c].client_name);
+ }
+
+ script << command << " &" << endl;
+ }
+
+ script << endl;
+ script << "sleep 3" << endl;
+ script << endl;
+
+ _canvas->for_each_edge(print_edge, &script);
+
+ script.close();
+ g_chmod(script_path.c_str(), 0740);
+}
+
+void
+Patchage::show_save_session_dialog()
+{
+ save_session(false);
+}
+
+void
+Patchage::show_save_close_session_dialog()
+{
+ save_session(true);
+}
+
+#endif
+
+#ifdef HAVE_ALSA
+void
+Patchage::menu_alsa_connect()
+{
+ _alsa_driver->attach(false);
+ _alsa_driver->refresh();
+}
+
+void
+Patchage::menu_alsa_disconnect()
+{
+ _alsa_driver->detach();
+ refresh();
+}
+#endif
+
+void
+Patchage::on_arrange()
+{
+ if (_canvas) {
+ _canvas->arrange();
+ }
+}
+
+void
+Patchage::on_sprung_layout_toggled()
+{
+ const bool sprung = _menu_view_sprung_layout->get_active();
+
+ _canvas->set_sprung_layout(sprung);
+ _conf->set_sprung_layout(sprung);
+}
+
+void
+Patchage::on_help_about()
+{
+ _about_win->run();
+ _about_win->hide();
+}
+
+static void
+update_labels(GanvNode* node, void* data)
+{
+ const bool human_names = *(const bool*)data;
+ if (GANV_IS_MODULE(node)) {
+ Ganv::Module* gmod = Glib::wrap(GANV_MODULE(node));
+ PatchageModule* pmod = dynamic_cast<PatchageModule*>(gmod);
+ if (pmod) {
+ for (Ganv::Port* gport : *gmod) {
+ PatchagePort* pport = dynamic_cast<PatchagePort*>(gport);
+ if (pport) {
+ pport->show_human_name(human_names);
+ }
+ }
+ }
+ }
+}
+
+void
+Patchage::on_view_human_names()
+{
+ bool human_names = show_human_names();
+ _canvas->for_each_node(update_labels, &human_names);
+}
+
+void
+Patchage::on_view_sort_ports()
+{
+ const bool sort_ports = this->sort_ports();
+ _canvas->set_port_order(sort_ports ? port_order : NULL, NULL);
+ _conf->set_sort_ports(sort_ports);
+ refresh();
+}
+
+void
+Patchage::on_zoom_in()
+{
+ const float zoom = _canvas->get_zoom() * 1.25;
+ _canvas->set_zoom(zoom);
+ _conf->set_zoom(zoom);
+}
+
+void
+Patchage::on_zoom_out()
+{
+ const float zoom = _canvas->get_zoom() * 0.75;
+ _canvas->set_zoom(zoom);
+ _conf->set_zoom(zoom);
+}
+
+void
+Patchage::on_zoom_normal()
+{
+ _canvas->set_zoom(1.0);
+ _conf->set_zoom(1.0);
+}
+
+void
+Patchage::on_zoom_full()
+{
+ _canvas->zoom_full();
+ _conf->set_zoom(_canvas->get_zoom());
+}
+
+void
+Patchage::on_increase_font_size()
+{
+ const float points = _canvas->get_font_size() + 1.0;
+ _canvas->set_font_size(points);
+ _conf->set_font_size(points);
+}
+
+void
+Patchage::on_decrease_font_size()
+{
+ const float points = _canvas->get_font_size() - 1.0;
+ _canvas->set_font_size(points);
+ _conf->set_font_size(points);
+}
+
+void
+Patchage::on_normal_font_size()
+{
+ _canvas->set_font_size(_canvas->get_default_font_size());
+ _conf->set_font_size(_canvas->get_default_font_size());
+}
+
+static inline guint
+highlight_color(guint c, guint delta)
+{
+ const guint max_char = 255;
+ const guint r = MIN((c >> 24) + delta, max_char);
+ const guint g = MIN(((c >> 16) & 0xFF) + delta, max_char);
+ const guint b = MIN(((c >> 8) & 0xFF) + delta, max_char);
+ const guint a = c & 0xFF;
+
+ return ((((guint)(r)) << 24) |
+ (((guint)(g)) << 16) |
+ (((guint)(b)) << 8) |
+ (((guint)(a))));
+}
+
+static void
+update_port_colors(GanvNode* node, void* data)
+{
+ Patchage* patchage = (Patchage*)data;
+ if (!GANV_IS_MODULE(node)) {
+ return;
+ }
+
+ Ganv::Module* gmod = Glib::wrap(GANV_MODULE(node));
+ PatchageModule* pmod = dynamic_cast<PatchageModule*>(gmod);
+ if (!pmod) {
+ return;
+ }
+
+ for (PatchageModule::iterator i = pmod->begin(); i != pmod->end(); ++i) {
+ PatchagePort* port = dynamic_cast<PatchagePort*>(*i);
+ if (port) {
+ const uint32_t rgba = patchage->conf()->get_port_color(port->type());
+ port->set_fill_color(rgba);
+ port->set_border_color(highlight_color(rgba, 0x20));
+ }
+ }
+}
+
+static void
+update_edge_color(GanvEdge* edge, void* data)
+{
+ Patchage* patchage = (Patchage*)data;
+ Ganv::Edge* edgemm = Glib::wrap(edge);
+
+ PatchagePort* tail = dynamic_cast<PatchagePort*>((edgemm)->get_tail());
+ if (tail) {
+ edgemm->set_color(patchage->conf()->get_port_color(tail->type()));
+ }
+}
+
+void
+Patchage::on_legend_color_change(int id, const std::string& label, uint32_t rgba)
+{
+ _conf->set_port_color((PortType)id, rgba);
+ _canvas->for_each_node(update_port_colors, this);
+ _canvas->for_each_edge(update_edge_color, this);
+}
+
+void
+Patchage::on_messages_resized(Gtk::Allocation& alloc)
+{
+ const int max_pos = _main_paned->get_allocation().get_height();
+ _conf->set_messages_height(max_pos - _main_paned->get_position());
+}
+
+void
+Patchage::save()
+{
+ _conf->set_zoom(_canvas->get_zoom()); // Can be changed by ganv
+ _conf->save();
+}
+
+void
+Patchage::on_quit()
+{
+#ifdef HAVE_ALSA
+ _alsa_driver->detach();
+#endif
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ _jack_driver->detach();
+#endif
+ _main_win->hide();
+}
+
+void
+Patchage::on_export_image()
+{
+ Gtk::FileChooserDialog dialog("Export Image", Gtk::FILE_CHOOSER_ACTION_SAVE);
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+ dialog.set_default_response(Gtk::RESPONSE_OK);
+ dialog.set_transient_for(*_main_win);
+
+ typedef std::map<std::string, std::string> Types;
+ Types types;
+ types["*.dot"] = "Graphviz DOT";
+ types["*.pdf"] = "Portable Document Format";
+ types["*.ps"] = "PostScript";
+ types["*.svg"] = "Scalable Vector Graphics";
+ for (Types::const_iterator t = types.begin(); t != types.end(); ++t) {
+ Gtk::FileFilter filt;
+ filt.add_pattern(t->first);
+ filt.set_name(t->second);
+ dialog.add_filter(filt);
+ }
+
+ Gtk::CheckButton* bg_but = new Gtk::CheckButton("Draw _Background", true);
+ Gtk::Alignment* extra = new Gtk::Alignment(1.0, 0.5, 0.0, 0.0);
+ bg_but->set_active(true);
+ extra->add(*Gtk::manage(bg_but));
+ extra->show_all();
+ dialog.set_extra_widget(*Gtk::manage(extra));
+
+ if (dialog.run() == Gtk::RESPONSE_OK) {
+ const std::string filename = dialog.get_filename();
+ if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) {
+ Gtk::MessageDialog confirm(
+ std::string("File exists! Overwrite ") + filename + "?",
+ true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true);
+ confirm.set_transient_for(dialog);
+ if (confirm.run() != Gtk::RESPONSE_YES) {
+ return;
+ }
+ }
+ _canvas->export_image(filename.c_str(), bg_but->get_active());
+ }
+}
+
+void
+Patchage::on_view_messages()
+{
+ if (_menu_view_messages->get_active()) {
+ Glib::RefPtr<Gtk::TextBuffer> buffer = _status_text->get_buffer();
+ if (!_pane_initialized) {
+ int y, line_height;
+ _status_text->get_line_yrange(buffer->begin(), y, line_height);
+ const int pad = _status_text->get_pixels_inside_wrap();
+ const int max_pos = _main_paned->get_allocation().get_height();
+ const int min_height = (line_height + 2 * pad);
+ const int conf_height = _conf->get_messages_height();
+ _main_paned->set_position(max_pos - std::max(conf_height, min_height));
+ _pane_initialized = true;
+ }
+
+ _log_scrolledwindow->show();
+ _status_text->scroll_to_mark(
+ _status_text->get_buffer()->get_insert(), 0);
+ _conf->set_show_messages(true);
+ } else {
+ _log_scrolledwindow->hide();
+ _conf->set_show_messages(false);
+ }
+}
+
+void
+Patchage::on_view_toolbar()
+{
+ if (_menu_view_toolbar->get_active()) {
+ _toolbar->show();
+ } else {
+ _toolbar->hide();
+ }
+ _conf->set_show_toolbar(_menu_view_toolbar->get_active());
+}
+
+bool
+Patchage::on_scroll(GdkEventScroll* ev)
+{
+ return false;
+}
+
+void
+Patchage::buffer_size_changed()
+{
+#if defined(HAVE_JACK) || defined(HAVE_JACK_DBUS)
+ const int selected = _buf_size_combo->get_active_row_number();
+
+ if (selected == -1) {
+ update_toolbar();
+ } else {
+ const jack_nframes_t buffer_size = 1 << (selected + 5);
+ _jack_driver->set_buffer_size(buffer_size);
+ update_toolbar();
+ }
+#endif
+}
+
diff --git a/src/Patchage.hpp b/src/Patchage.hpp
new file mode 100644
index 0000000..cf550f0
--- /dev/null
+++ b/src/Patchage.hpp
@@ -0,0 +1,212 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_PATCHAGE_HPP
+#define PATCHAGE_PATCHAGE_HPP
+
+#include <set>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <gtkmm/aboutdialog.h>
+#include <gtkmm/alignment.h>
+#include <gtkmm/builder.h>
+#include <gtkmm/button.h>
+#include <gtkmm/checkmenuitem.h>
+#include <gtkmm/combobox.h>
+#include <gtkmm/dialog.h>
+#include <gtkmm/imagemenuitem.h>
+#include <gtkmm/label.h>
+#include <gtkmm/main.h>
+#include <gtkmm/menubar.h>
+#include <gtkmm/menuitem.h>
+#include <gtkmm/paned.h>
+#include <gtkmm/progressbar.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/textview.h>
+#include <gtkmm/toolbar.h>
+#include <gtkmm/toolbutton.h>
+#include <gtkmm/viewport.h>
+#include <gtkmm/window.h>
+
+#include "patchage_config.h"
+#include "Widget.hpp"
+#include "Legend.hpp"
+
+class AlsaDriver;
+class JackDriver;
+class PatchageCanvas;
+class Configuration;
+
+namespace Ganv { class Module; }
+
+class Patchage {
+public:
+ Patchage(int argc, char** argv);
+ ~Patchage();
+
+ const boost::shared_ptr<PatchageCanvas>& canvas() const { return _canvas; }
+
+ Gtk::Window* window() { return _main_win.get(); }
+
+ Configuration* conf() const { return _conf; }
+ JackDriver* jack_driver() const { return _jack_driver; }
+#ifdef HAVE_ALSA
+ AlsaDriver* alsa_driver() const { return _alsa_driver; }
+#endif
+#ifdef PATCHAGE_JACK_SESSION
+ void show_open_session_dialog();
+ void show_save_session_dialog();
+ void show_save_close_session_dialog();
+#endif
+
+ Glib::RefPtr<Gtk::Builder> xml() { return _xml; }
+
+ void attach();
+ void save();
+ void quit() { _main_win->hide(); }
+
+ void refresh();
+ inline void queue_refresh() { _refresh = true; }
+ inline void driver_detached() { _driver_detached = true; }
+
+ void info_msg(const std::string& msg);
+ void error_msg(const std::string& msg);
+ void warning_msg(const std::string& msg);
+
+ void update_state();
+ void store_window_location();
+
+ bool show_human_names() const { return _menu_view_human_names->get_active(); }
+ bool sort_ports() const { return _menu_view_sort_ports->get_active(); }
+
+protected:
+ class BufferSizeColumns : public Gtk::TreeModel::ColumnRecord {
+ public:
+ BufferSizeColumns() { add(label); }
+
+ Gtk::TreeModelColumn<Glib::ustring> label;
+ };
+
+ void connect_widgets();
+
+ void on_arrange();
+ void on_sprung_layout_toggled();
+ void on_help_about();
+ void on_quit();
+ void on_export_image();
+ void on_view_messages();
+ void on_view_toolbar();
+ void on_store_positions();
+ void on_view_human_names();
+ void on_view_sort_ports();
+ void on_zoom_in();
+ void on_zoom_out();
+ void on_zoom_normal();
+ void on_zoom_full();
+ void on_increase_font_size();
+ void on_decrease_font_size();
+ void on_normal_font_size();
+ void on_legend_color_change(int id, const std::string& label, uint32_t rgba);
+ void on_messages_resized(Gtk::Allocation& alloc);
+
+ bool on_scroll(GdkEventScroll* ev);
+
+ void zoom(double z);
+ bool idle_callback();
+ void clear_load();
+ bool update_load();
+ void update_toolbar();
+
+ void buffer_size_changed();
+
+ Glib::RefPtr<Gtk::Builder> _xml;
+
+#ifdef HAVE_ALSA
+ AlsaDriver* _alsa_driver;
+ void menu_alsa_connect();
+ void menu_alsa_disconnect();
+#endif
+
+#ifdef PATCHAGE_JACK_SESSION
+ void save_session(bool close);
+#endif
+
+ boost::shared_ptr<PatchageCanvas> _canvas;
+
+ JackDriver* _jack_driver;
+ Configuration* _conf;
+
+ Gtk::Main* _gtk_main;
+
+ BufferSizeColumns _buf_size_columns;
+
+ Widget<Gtk::AboutDialog> _about_win;
+ Widget<Gtk::ScrolledWindow> _main_scrolledwin;
+ Widget<Gtk::Window> _main_win;
+ Widget<Gtk::VBox> _main_vbox;
+ Widget<Gtk::MenuBar> _menubar;
+ Widget<Gtk::MenuItem> _menu_alsa_connect;
+ Widget<Gtk::MenuItem> _menu_alsa_disconnect;
+ Widget<Gtk::MenuItem> _menu_file_quit;
+ Widget<Gtk::MenuItem> _menu_export_image;
+ Widget<Gtk::MenuItem> _menu_help_about;
+ Widget<Gtk::MenuItem> _menu_jack_connect;
+ Widget<Gtk::MenuItem> _menu_jack_disconnect;
+ Widget<Gtk::MenuItem> _menu_open_session;
+ Widget<Gtk::MenuItem> _menu_save_session;
+ Widget<Gtk::MenuItem> _menu_save_close_session;
+ Widget<Gtk::MenuItem> _menu_view_arrange;
+ Widget<Gtk::CheckMenuItem> _menu_view_sprung_layout;
+ Widget<Gtk::CheckMenuItem> _menu_view_messages;
+ Widget<Gtk::CheckMenuItem> _menu_view_toolbar;
+ Widget<Gtk::MenuItem> _menu_view_refresh;
+ Widget<Gtk::CheckMenuItem> _menu_view_human_names;
+ Widget<Gtk::CheckMenuItem> _menu_view_sort_ports;
+ Widget<Gtk::ImageMenuItem> _menu_zoom_in;
+ Widget<Gtk::ImageMenuItem> _menu_zoom_out;
+ Widget<Gtk::ImageMenuItem> _menu_zoom_normal;
+ Widget<Gtk::ImageMenuItem> _menu_zoom_full;
+ Widget<Gtk::MenuItem> _menu_increase_font_size;
+ Widget<Gtk::MenuItem> _menu_decrease_font_size;
+ Widget<Gtk::MenuItem> _menu_normal_font_size;
+ Widget<Gtk::Toolbar> _toolbar;
+ Widget<Gtk::ToolButton> _clear_load_but;
+ Widget<Gtk::ProgressBar> _xrun_progress;
+ Widget<Gtk::ComboBox> _buf_size_combo;
+ Widget<Gtk::Label> _latency_label;
+ Widget<Gtk::Alignment> _legend_alignment;
+ Widget<Gtk::Paned> _main_paned;
+ Widget<Gtk::ScrolledWindow> _log_scrolledwindow;
+ Widget<Gtk::TextView> _status_text;
+ Legend* _legend;
+
+ Glib::RefPtr<Gtk::TextTag> _error_tag;
+ Glib::RefPtr<Gtk::TextTag> _warning_tag;
+
+ bool _pane_initialized;
+ bool _attach;
+ bool _driver_detached;
+ bool _refresh;
+ bool _enable_refresh;
+ bool _jack_driver_autoattach;
+#ifdef HAVE_ALSA
+ bool _alsa_driver_autoattach;
+#endif
+};
+
+#endif // PATCHAGE_PATCHAGE_HPP
diff --git a/src/PatchageCanvas.cpp b/src/PatchageCanvas.cpp
new file mode 100644
index 0000000..4d63a4b
--- /dev/null
+++ b/src/PatchageCanvas.cpp
@@ -0,0 +1,338 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/format.hpp>
+
+#include "patchage_config.h"
+
+#if defined(HAVE_JACK_DBUS)
+ #include "JackDbusDriver.hpp"
+#elif defined(PATCHAGE_LIBJACK)
+ #include "JackDriver.hpp"
+#endif
+#ifdef HAVE_ALSA
+ #include "AlsaDriver.hpp"
+#endif
+
+#include "ganv/Edge.hpp"
+
+#include "Patchage.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageModule.hpp"
+#include "PatchagePort.hpp"
+
+using std::string;
+using boost::format;
+
+PatchageCanvas::PatchageCanvas(Patchage* app, int width, int height)
+ : Ganv::Canvas(width, height)
+ , _app(app)
+{
+ signal_event.connect(
+ sigc::mem_fun(this, &PatchageCanvas::on_event));
+ signal_connect.connect(
+ sigc::mem_fun(this, &PatchageCanvas::connect));
+ signal_disconnect.connect(
+ sigc::mem_fun(this, &PatchageCanvas::disconnect));
+}
+
+PatchageModule*
+PatchageCanvas::find_module(const string& name, ModuleType type)
+{
+ const ModuleIndex::const_iterator i = _module_index.find(name);
+ if (i == _module_index.end())
+ return NULL;
+
+ PatchageModule* io_module = NULL;
+ for (ModuleIndex::const_iterator j = i; j != _module_index.end() && j->first == name; ++j) {
+ if (j->second->type() == type) {
+ return j->second;
+ } else if (j->second->type() == InputOutput) {
+ io_module = j->second;
+ }
+ }
+
+ // Return InputOutput module for Input or Output (or NULL if not found at all)
+ return io_module;
+}
+
+void
+PatchageCanvas::remove_module(const string& name)
+{
+ ModuleIndex::iterator i = _module_index.find(name);
+ while (i != _module_index.end()) {
+ PatchageModule* mod = i->second;
+ _module_index.erase(i);
+ i = _module_index.find(name);
+ delete mod;
+ }
+}
+
+PatchagePort*
+PatchageCanvas::find_port(const PortID& id)
+{
+ PatchagePort* pp = NULL;
+
+ PortIndex::iterator i = _port_index.find(id);
+ if (i != _port_index.end()) {
+ assert(i->second->get_module());
+ return i->second;
+ }
+
+#ifdef PATCHAGE_LIBJACK
+ // Alsa ports are always indexed (or don't exist at all)
+ if (id.type == PortID::JACK_ID) {
+ jack_port_t* jack_port = jack_port_by_id(_app->jack_driver()->client(), id.id.jack_id);
+ if (!jack_port)
+ return NULL;
+
+ string module_name;
+ string port_name;
+ _app->jack_driver()->port_names(id, module_name, port_name);
+
+ PatchageModule* module = find_module(
+ module_name, (jack_port_flags(jack_port) & JackPortIsInput) ? Input : Output);
+
+ if (module)
+ pp = dynamic_cast<PatchagePort*>(module->get_port(port_name));
+
+ if (pp)
+ index_port(id, pp);
+ }
+#endif // PATCHAGE_LIBJACK
+
+ return pp;
+}
+
+void
+PatchageCanvas::remove_port(const PortID& id)
+{
+ PatchagePort* const port = find_port(id);
+ _port_index.erase(id);
+ delete port;
+}
+
+struct RemovePortsData {
+ typedef bool (*Predicate)(const PatchagePort*);
+
+ RemovePortsData(Predicate p) : pred(p) {}
+
+ Predicate pred;
+ std::set<PatchageModule*> empty;
+};
+
+static void
+delete_port_if_matches(GanvPort* port, void* cdata)
+{
+ RemovePortsData* data = (RemovePortsData*)cdata;
+ PatchagePort* pport = dynamic_cast<PatchagePort*>(Glib::wrap(port));
+ if (pport && data->pred(pport)) {
+ delete pport;
+ }
+}
+
+static void
+remove_ports_matching(GanvNode* node, void* cdata)
+{
+ if (!GANV_IS_MODULE(node)) {
+ return;
+ }
+
+ Ganv::Module* cmodule = Glib::wrap(GANV_MODULE(node));
+ PatchageModule* pmodule = dynamic_cast<PatchageModule*>(cmodule);
+ if (!pmodule) {
+ return;
+ }
+
+ RemovePortsData* data = (RemovePortsData*)cdata;
+
+ pmodule->for_each_port(delete_port_if_matches, data);
+
+ if (pmodule->num_ports() == 0) {
+ data->empty.insert(pmodule);
+ }
+}
+
+void
+PatchageCanvas::remove_ports(bool (*pred)(const PatchagePort*))
+{
+ RemovePortsData data(pred);
+
+ for_each_node(remove_ports_matching, &data);
+
+ for (PortIndex::iterator i = _port_index.begin();
+ i != _port_index.end();) {
+ PortIndex::iterator next = i;
+ ++next;
+ if (pred(i->second)) {
+ _port_index.erase(i);
+ }
+ i = next;
+ }
+
+ for (std::set<PatchageModule*>::iterator i = data.empty.begin();
+ i != data.empty.end(); ++i) {
+ delete *i;
+ }
+}
+
+PatchagePort*
+PatchageCanvas::find_port_by_name(const std::string& client_name,
+ const std::string& port_name)
+{
+ const ModuleIndex::const_iterator i = _module_index.find(client_name);
+ if (i == _module_index.end())
+ return NULL;
+
+ for (ModuleIndex::const_iterator j = i; j != _module_index.end() && j->first == client_name; ++j) {
+ PatchagePort* port = dynamic_cast<PatchagePort*>(j->second->get_port(port_name));
+ if (port)
+ return port;
+ }
+
+ return NULL;
+}
+
+void
+PatchageCanvas::connect(Ganv::Node* port1,
+ Ganv::Node* port2)
+{
+ PatchagePort* p1 = dynamic_cast<PatchagePort*>(port1);
+ PatchagePort* p2 = dynamic_cast<PatchagePort*>(port2);
+ if (!p1 || !p2)
+ return;
+
+ if ((p1->type() == JACK_AUDIO && p2->type() == JACK_AUDIO) ||
+ (p1->type() == JACK_MIDI && p2->type() == JACK_MIDI) ||
+ (p1->type() == JACK_AUDIO && p2->type() == JACK_CV) ||
+ (p1->type() == JACK_CV && p2->type() == JACK_CV) ||
+ (p1->type() == JACK_OSC && p2->type() == JACK_OSC)) {
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ _app->jack_driver()->connect(p1, p2);
+#endif
+#ifdef HAVE_ALSA
+ } else if (p1->type() == ALSA_MIDI && p2->type() == ALSA_MIDI) {
+ _app->alsa_driver()->connect(p1, p2);
+#endif
+ } else {
+ _app->warning_msg("Cannot make connection, incompatible port types.");
+ }
+}
+
+void
+PatchageCanvas::disconnect(Ganv::Node* port1,
+ Ganv::Node* port2)
+{
+ PatchagePort* input = dynamic_cast<PatchagePort*>(port1);
+ PatchagePort* output = dynamic_cast<PatchagePort*>(port2);
+ if (!input || !output)
+ return;
+
+ if (input->is_output() && output->is_input()) {
+ // Damn, guessed wrong
+ PatchagePort* swap = input;
+ input = output;
+ output = swap;
+ }
+
+ if (!input || !output || input->is_output() || output->is_input()) {
+ _app->error_msg("Attempt to disconnect mismatched/unknown ports.");
+ return;
+ }
+
+ if (input->type() == JACK_AUDIO ||
+ input->type() == JACK_MIDI ||
+ input->type() == JACK_CV ||
+ input->type() == JACK_OSC) {
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ _app->jack_driver()->disconnect(output, input);
+#endif
+#ifdef HAVE_ALSA
+ } else if (input->type() == ALSA_MIDI) {
+ _app->alsa_driver()->disconnect(output, input);
+#endif
+ } else {
+ _app->error_msg("Attempt to disconnect ports with strange types.");
+ }
+}
+
+void
+PatchageCanvas::add_module(const std::string& name, PatchageModule* module)
+{
+ _module_index.insert(std::make_pair(name, module));
+
+ // Join partners, if applicable
+ PatchageModule* in_module = NULL;
+ PatchageModule* out_module = NULL;
+ if (module->type() == Input) {
+ in_module = module;
+ out_module = find_module(name, Output);
+ } else if (module->type() == Output) {
+ in_module = find_module(name, Output);
+ out_module = module;
+ }
+
+ if (in_module && out_module)
+ out_module->set_partner(in_module);
+}
+
+static void
+disconnect_edge(GanvEdge* edge, void* data)
+{
+ PatchageCanvas* canvas = (PatchageCanvas*)data;
+ Ganv::Edge* edgemm = Glib::wrap(edge);
+ canvas->disconnect(edgemm->get_tail(), edgemm->get_head());
+}
+
+bool
+PatchageCanvas::on_event(GdkEvent* ev)
+{
+ if (ev->type == GDK_KEY_PRESS && ev->key.keyval == GDK_Delete) {
+ for_each_selected_edge(disconnect_edge, this);
+ clear_selection();
+ return true;
+ }
+
+ return false;
+}
+
+bool
+PatchageCanvas::make_connection(Ganv::Node* tail, Ganv::Node* head)
+{
+ new Ganv::Edge(*this, tail, head);
+ return true;
+}
+
+void
+PatchageCanvas::remove_module(PatchageModule* module)
+{
+ // Remove module from cache
+ for (ModuleIndex::iterator i = _module_index.find(module->get_label());
+ i != _module_index.end() && i->first == module->get_label(); ++i) {
+ if (i->second == module) {
+ _module_index.erase(i);
+ return;
+ }
+ }
+}
+
+void
+PatchageCanvas::clear()
+{
+ _port_index.clear();
+ _module_index.clear();
+ Ganv::Canvas::clear();
+}
diff --git a/src/PatchageCanvas.hpp b/src/PatchageCanvas.hpp
new file mode 100644
index 0000000..4b5fac1
--- /dev/null
+++ b/src/PatchageCanvas.hpp
@@ -0,0 +1,85 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_PATCHAGECANVAS_HPP
+#define PATCHAGE_PATCHAGECANVAS_HPP
+
+#include <map>
+#include <string>
+
+#include "patchage_config.h"
+
+#ifdef HAVE_ALSA
+ #include <alsa/asoundlib.h>
+#endif
+
+#include "ganv/Canvas.hpp"
+
+#include "PatchageEvent.hpp"
+#include "PatchageModule.hpp"
+#include "PortID.hpp"
+
+class Patchage;
+class PatchageModule;
+class PatchagePort;
+
+class PatchageCanvas : public Ganv::Canvas {
+public:
+ PatchageCanvas(Patchage* _app, int width, int height);
+
+ PatchageModule* find_module(const std::string& name, ModuleType type);
+ PatchagePort* find_port(const PortID& id);
+
+ void remove_module(const std::string& name);
+ void remove_module(PatchageModule* module);
+
+ PatchagePort* find_port_by_name(const std::string& client_name,
+ const std::string& port_name);
+
+ void connect(Ganv::Node* port1,
+ Ganv::Node* port2);
+
+ void disconnect(Ganv::Node* port1,
+ Ganv::Node* port2);
+
+ void index_port(const PortID& id, PatchagePort* port) {
+ _port_index.insert(std::make_pair(id, port));
+ }
+
+ void remove_ports(bool (*pred)(const PatchagePort*));
+
+ void add_module(const std::string& name, PatchageModule* module);
+
+ bool make_connection(Ganv::Node* tail, Ganv::Node* head);
+
+ void remove_port(const PortID& id);
+
+ void clear();
+
+private:
+ Patchage* _app;
+
+ bool on_event(GdkEvent* ev);
+ bool on_connection_event(Ganv::Edge* c, GdkEvent* ev);
+
+ typedef std::map<const PortID, PatchagePort*> PortIndex;
+ PortIndex _port_index;
+
+ typedef std::multimap<const std::string, PatchageModule*> ModuleIndex;
+ ModuleIndex _module_index;
+};
+
+#endif // PATCHAGE_PATCHAGECANVAS_HPP
diff --git a/src/PatchageEvent.cpp b/src/PatchageEvent.cpp
new file mode 100644
index 0000000..ea9a758
--- /dev/null
+++ b/src/PatchageEvent.cpp
@@ -0,0 +1,110 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/format.hpp>
+
+#include "patchage_config.h"
+#include "Patchage.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageModule.hpp"
+#include "PatchageEvent.hpp"
+#include "Driver.hpp"
+#if defined(HAVE_JACK_DBUS)
+# include "JackDbusDriver.hpp"
+#elif defined(PATCHAGE_LIBJACK)
+# include "JackDriver.hpp"
+#endif
+#ifdef HAVE_ALSA
+# include "AlsaDriver.hpp"
+#endif
+
+using std::endl;
+using boost::format;
+
+void
+PatchageEvent::execute(Patchage* patchage)
+{
+ if (_type == REFRESH) {
+ patchage->refresh();
+
+ } else if (_type == CLIENT_CREATION) {
+ // No empty modules (for now)
+ g_free(_str);
+ _str = NULL;
+
+ } else if (_type == CLIENT_DESTRUCTION) {
+ patchage->canvas()->remove_module(_str);
+ g_free(_str);
+ _str = NULL;
+
+ } else if (_type == PORT_CREATION) {
+
+ Driver* driver = NULL;
+ if (_port_1.type == PortID::JACK_ID) {
+#if defined(PATCHAGE_LIBJACK) || defined(HAVE_JACK_DBUS)
+ driver = patchage->jack_driver();
+#endif
+#ifdef HAVE_ALSA
+ } else if (_port_1.type == PortID::ALSA_ADDR) {
+ driver = patchage->alsa_driver();
+#endif
+ }
+
+ if (driver) {
+ PatchagePort* port = driver->create_port_view(patchage, _port_1);
+ if (!port) {
+ patchage->error_msg(
+ (format("Unable to create view for port `%1%'")
+ % _port_1).str());
+ }
+ } else {
+ patchage->error_msg(
+ (format("Unknown type for port `%1%'") % _port_1).str());
+ }
+
+ } else if (_type == PORT_DESTRUCTION) {
+
+ patchage->canvas()->remove_port(_port_1);
+
+ } else if (_type == CONNECTION) {
+
+ PatchagePort* port_1 = patchage->canvas()->find_port(_port_1);
+ PatchagePort* port_2 = patchage->canvas()->find_port(_port_2);
+
+ if (!port_1)
+ patchage->error_msg((format("Unable to find port `%1%' to connect")
+ % _port_1).str());
+ else if (!port_2)
+ patchage->error_msg((format("Unable to find port `%1%' to connect")
+ % _port_2).str());
+ else
+ patchage->canvas()->make_connection(port_1, port_2);
+
+ } else if (_type == DISCONNECTION) {
+
+ PatchagePort* port_1 = patchage->canvas()->find_port(_port_1);
+ PatchagePort* port_2 = patchage->canvas()->find_port(_port_2);
+
+ if (!port_1)
+ patchage->error_msg((format("Unable to find port `%1%' to disconnect")
+ % _port_1).str());
+ else if (!port_2)
+ patchage->error_msg((format("Unable to find port `%1%' to disconnect")
+ % _port_2).str());
+ else
+ patchage->canvas()->remove_edge_between(port_1, port_2);
+ }
+}
diff --git a/src/PatchageEvent.hpp b/src/PatchageEvent.hpp
new file mode 100644
index 0000000..899f77f
--- /dev/null
+++ b/src/PatchageEvent.hpp
@@ -0,0 +1,87 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_PATCHAGEEVENT_HPP
+#define PATCHAGE_PATCHAGEEVENT_HPP
+
+#include <cstring>
+
+#include "patchage_config.h"
+
+#ifdef PATCHAGE_LIBJACK
+ #include <jack/jack.h>
+#endif
+#ifdef HAVE_ALSA
+ #include <alsa/asoundlib.h>
+#endif
+
+#include "PatchagePort.hpp"
+#include "PortID.hpp"
+
+class Patchage;
+
+/** A Driver event to be processed by the GUI thread.
+ */
+class PatchageEvent {
+public:
+ enum Type {
+ NULL_EVENT = 0,
+ REFRESH,
+ CLIENT_CREATION,
+ CLIENT_DESTRUCTION,
+ PORT_CREATION,
+ PORT_DESTRUCTION,
+ CONNECTION,
+ DISCONNECTION
+ };
+
+ explicit PatchageEvent(Type type=NULL_EVENT)
+ : _str(NULL)
+ , _type(type)
+ {}
+
+ PatchageEvent(Type type, const char* str)
+ : _str(g_strdup(str))
+ , _type(type)
+ {}
+
+ template <typename P>
+ PatchageEvent(Type type, P port)
+ : _str(NULL)
+ , _port_1(port)
+ , _type(type)
+ {}
+
+ template <typename P>
+ PatchageEvent(Type type, P port_1, P port_2)
+ : _str(NULL)
+ , _port_1(port_1, false)
+ , _port_2(port_2, true)
+ , _type(type)
+ {}
+
+ void execute(Patchage* patchage);
+
+ inline Type type() const { return (Type)_type; }
+
+private:
+ char* _str;
+ PortID _port_1;
+ PortID _port_2;
+ uint8_t _type;
+};
+
+#endif // PATCHAGE_PATCHAGEEVENT_HPP
diff --git a/src/PatchageModule.cpp b/src/PatchageModule.cpp
new file mode 100644
index 0000000..8ba5296
--- /dev/null
+++ b/src/PatchageModule.cpp
@@ -0,0 +1,157 @@
+/* This file is part of Patchage.
+ * Copyright 2010-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Patchage.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageModule.hpp"
+#include "PatchagePort.hpp"
+
+PatchageModule::PatchageModule(
+ Patchage* app, const std::string& name, ModuleType type, double x, double y)
+ : Module(*app->canvas().get(), name, x, y)
+ , _app(app)
+ , _menu(NULL)
+ , _name(name)
+ , _type(type)
+{
+ signal_event().connect(
+ sigc::mem_fun(this, &PatchageModule::on_event));
+
+ signal_moved().connect(
+ sigc::mem_fun(this, &PatchageModule::store_location));
+
+ // Set as source by default, turned off if input ports added
+ set_is_source(true);
+}
+
+PatchageModule::~PatchageModule()
+{
+ _app->canvas()->remove_module(this);
+ delete _menu;
+ _menu = NULL;
+}
+
+void
+PatchageModule::update_menu()
+{
+ if (!_menu)
+ return;
+
+ if (_type == InputOutput) {
+ bool has_in = false;
+ bool has_out = false;
+ for (const_iterator p = begin(); p != end(); ++p) {
+ if ((*p)->is_input()) {
+ has_in = true;
+ } else {
+ has_out = true;
+ }
+ if (has_in && has_out) {
+ _menu->items()[0].show(); // Show "Split" menu item
+ return;
+ }
+ }
+ _menu->items()[0].hide(); // Hide "Split" menu item
+ }
+}
+
+bool
+PatchageModule::show_menu(GdkEventButton* ev)
+{
+ _menu = new Gtk::Menu();
+ Gtk::Menu::MenuList& items = _menu->items();
+ if (_type == InputOutput) {
+ items.push_back(
+ Gtk::Menu_Helpers::MenuElem(
+ "_Split", sigc::mem_fun(this, &PatchageModule::split)));
+ update_menu();
+ } else {
+ items.push_back(
+ Gtk::Menu_Helpers::MenuElem(
+ "_Join", sigc::mem_fun(this, &PatchageModule::join)));
+ }
+ items.push_back(
+ Gtk::Menu_Helpers::MenuElem(
+ "_Disconnect All",
+ sigc::mem_fun(this, &PatchageModule::menu_disconnect_all)));
+
+ _menu->popup(ev->button, ev->time);
+ return true;
+}
+
+bool
+PatchageModule::on_event(GdkEvent* ev)
+{
+ if (ev->type == GDK_BUTTON_PRESS && ev->button.button == 3) {
+ return show_menu(&ev->button);
+ }
+ return false;
+}
+
+void
+PatchageModule::load_location()
+{
+ Coord loc;
+
+ if (_app->conf()->get_module_location(_name, _type, loc))
+ move_to(loc.x, loc.y);
+ else
+ move_to(20 + rand() % 640,
+ 20 + rand() % 480);
+}
+
+void
+PatchageModule::store_location(double x, double y)
+{
+ Coord loc(get_x(), get_y());
+ _app->conf()->set_module_location(_name, _type, loc);
+}
+
+void
+PatchageModule::split()
+{
+ assert(_type == InputOutput);
+ _app->conf()->set_module_split(_name, true);
+ _app->refresh();
+}
+
+void
+PatchageModule::join()
+{
+ assert(_type != InputOutput);
+ _app->conf()->set_module_split(_name, false);
+ _app->refresh();
+}
+
+void
+PatchageModule::menu_disconnect_all()
+{
+ for (iterator p = begin(); p != end(); ++p)
+ (*p)->disconnect();
+}
+
+PatchagePort*
+PatchageModule::get_port(const std::string& name)
+{
+ for (iterator p = begin(); p != end(); ++p) {
+ PatchagePort* pport = dynamic_cast<PatchagePort*>(*p);
+ if (pport && pport->name() == name) {
+ return pport;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/PatchageModule.hpp b/src/PatchageModule.hpp
new file mode 100644
index 0000000..99527ac
--- /dev/null
+++ b/src/PatchageModule.hpp
@@ -0,0 +1,67 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_PATCHAGEMODULE_HPP
+#define PATCHAGE_PATCHAGEMODULE_HPP
+
+#include <string>
+
+#include <gtkmm/menu_elems.h>
+
+#include "ganv/Module.hpp"
+#include "ganv/Port.hpp"
+
+#include "Configuration.hpp"
+
+class Patchage;
+class PatchagePort;
+
+class PatchageModule : public Ganv::Module
+{
+public:
+ PatchageModule(Patchage* app,
+ const std::string& name,
+ ModuleType type,
+ double x = 0,
+ double y = 0);
+ ~PatchageModule();
+
+ void split();
+ void join();
+
+ bool show_menu(GdkEventButton* ev);
+ void update_menu();
+
+ PatchagePort* get_port(const std::string& name);
+
+ void load_location();
+ void menu_disconnect_all();
+ void show_dialog() {}
+ void store_location(double x, double y);
+
+ ModuleType type() const { return _type; }
+ const std::string& name() const { return _name; }
+
+protected:
+ bool on_event(GdkEvent* ev);
+
+ Patchage* _app;
+ Gtk::Menu* _menu;
+ std::string _name;
+ ModuleType _type;
+};
+
+#endif // PATCHAGE_PATCHAGEMODULE_HPP
diff --git a/src/PatchagePort.hpp b/src/PatchagePort.hpp
new file mode 100644
index 0000000..d5d6cb3
--- /dev/null
+++ b/src/PatchagePort.hpp
@@ -0,0 +1,104 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_PATCHAGEPORT_HPP
+#define PATCHAGE_PATCHAGEPORT_HPP
+
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <gtkmm/menu.h>
+#include <gtkmm/menushell.h>
+
+#include "ganv/Port.hpp"
+#include "ganv/Module.hpp"
+
+#include "Configuration.hpp"
+#include "PatchageCanvas.hpp"
+#include "PatchageModule.hpp"
+#include "PortID.hpp"
+#include "patchage_config.h"
+
+/** A Port on a PatchageModule
+ */
+class PatchagePort : public Ganv::Port
+{
+public:
+ PatchagePort(Ganv::Module& module,
+ PortType type,
+ const std::string& name,
+ const std::string& human_name,
+ bool is_input,
+ uint32_t color,
+ bool show_human_name,
+ boost::optional<int> order=boost::optional<int>())
+ : Port(module,
+ (show_human_name && !human_name.empty()) ? human_name : name,
+ is_input,
+ color)
+ , _type(type)
+ , _name(name)
+ , _human_name(human_name)
+ , _order(order)
+ {
+ signal_event().connect(
+ sigc::mem_fun(this, &PatchagePort::on_event));
+ }
+
+ virtual ~PatchagePort() {}
+
+ /** Returns the full name of this port, as "modulename:portname" */
+ std::string full_name() const {
+ PatchageModule* pmod = dynamic_cast<PatchageModule*>(get_module());
+ return std::string(pmod->name()) + ":" + _name;
+ }
+
+ void show_human_name(bool human) {
+ if (human && !_human_name.empty()) {
+ set_label(_human_name.c_str());
+ } else {
+ set_label(_name.c_str());
+ }
+ }
+
+ bool on_event(GdkEvent* ev) {
+ if (ev->type != GDK_BUTTON_PRESS || ev->button.button != 3) {
+ return false;
+ }
+
+ Gtk::Menu* menu = Gtk::manage(new Gtk::Menu());
+ menu->items().push_back(
+ Gtk::Menu_Helpers::MenuElem(
+ "Disconnect", sigc::mem_fun(this, &Port::disconnect)));
+
+ menu->popup(ev->button.button, ev->button.time);
+ return true;
+ }
+
+ PortType type() const { return _type; }
+ const std::string& name() const { return _name; }
+ const std::string& human_name() const { return _human_name; }
+ const boost::optional<int>& order() const { return _order; }
+
+private:
+ PortType _type;
+ std::string _name;
+ std::string _human_name;
+ boost::optional<int> _order;
+};
+
+#endif // PATCHAGE_PATCHAGEPORT_HPP
diff --git a/src/PortID.hpp b/src/PortID.hpp
new file mode 100644
index 0000000..3f916c0
--- /dev/null
+++ b/src/PortID.hpp
@@ -0,0 +1,120 @@
+/* This file is part of Patchage.
+ * Copyright 2008-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_PORTID_HPP
+#define PATCHAGE_PORTID_HPP
+
+#include <cstring>
+#include <iostream>
+
+#include "patchage_config.h"
+
+#ifdef PATCHAGE_LIBJACK
+ #include <jack/jack.h>
+#endif
+#ifdef HAVE_ALSA
+ #include <alsa/asoundlib.h>
+#endif
+
+#include "PatchagePort.hpp"
+
+struct PortID {
+ PortID() : type(NULL_PORT_ID) { memset(&id, 0, sizeof(id)); }
+ PortID(const PortID& copy) : type(copy.type) {
+ memcpy(&id, &copy.id, sizeof(id));
+ }
+
+ enum { NULL_PORT_ID, JACK_ID, ALSA_ADDR } type;
+
+#ifdef PATCHAGE_LIBJACK
+ PortID(jack_port_id_t jack_id, bool ign=false)
+ : type(JACK_ID) { id.jack_id = jack_id; }
+#endif
+
+#ifdef HAVE_ALSA
+ PortID(snd_seq_addr_t addr, bool in)
+ : type(ALSA_ADDR) { id.alsa_addr = addr; id.is_input = in; }
+#endif
+
+ union {
+#ifdef PATCHAGE_LIBJACK
+ jack_port_id_t jack_id;
+#endif
+#ifdef HAVE_ALSA
+ struct {
+ snd_seq_addr_t alsa_addr;
+ bool is_input : 1;
+ };
+#endif
+ } id;
+};
+
+static inline std::ostream&
+operator<<(std::ostream& os, const PortID& id)
+{
+ switch (id.type) {
+ case PortID::NULL_PORT_ID:
+ return os << "(null)";
+ case PortID::JACK_ID:
+#ifdef PATCHAGE_LIBJACK
+ return os << "jack:" << id.id.jack_id;
+#endif
+ break;
+ case PortID::ALSA_ADDR:
+#ifdef HAVE_ALSA
+ return os << "alsa:" << (int)id.id.alsa_addr.client << ":" << (int)id.id.alsa_addr.port
+ << ":" << (id.id.is_input ? "in" : "out");
+#endif
+ break;
+ }
+ assert(false);
+ return os;
+}
+
+static inline bool
+operator<(const PortID& a, const PortID& b)
+{
+ if (a.type != b.type)
+ return a.type < b.type;
+
+ switch (a.type) {
+ case PortID::NULL_PORT_ID:
+ return true;
+ case PortID::JACK_ID:
+#ifdef PATCHAGE_LIBJACK
+ return a.id.jack_id < b.id.jack_id;
+#endif
+ break;
+ case PortID::ALSA_ADDR:
+#ifdef HAVE_ALSA
+ if ((a.id.alsa_addr.client < b.id.alsa_addr.client)
+ || ((a.id.alsa_addr.client == b.id.alsa_addr.client)
+ && a.id.alsa_addr.port < b.id.alsa_addr.port)) {
+ return true;
+ } else if (a.id.alsa_addr.client == b.id.alsa_addr.client
+ && a.id.alsa_addr.port == b.id.alsa_addr.port) {
+ return (a.id.is_input < b.id.is_input);
+ } else {
+ return false;
+ }
+#endif
+ break;
+ }
+ assert(false);
+ return false;
+}
+
+#endif // PATCHAGE_PORTID_HPP
diff --git a/src/Queue.hpp b/src/Queue.hpp
new file mode 100644
index 0000000..ab47aed
--- /dev/null
+++ b/src/Queue.hpp
@@ -0,0 +1,131 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2017 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QUEUE_HPP_INCLUDED
+#define QUEUE_HPP_INCLUDED
+
+#include <atomic>
+#include <cassert>
+
+/** Realtime-safe single-reader single-writer queue */
+template <typename T>
+class Queue
+{
+public:
+ /** @param size Size in number of elements */
+ explicit Queue(size_t size);
+ ~Queue();
+
+ // Any thread:
+
+ inline size_t capacity() const { return _size - 1; }
+
+ // Write thread(s):
+
+ inline bool full() const;
+ inline bool push(const T& obj);
+
+ // Read thread:
+
+ inline bool empty() const;
+ inline T& front() const;
+ inline void pop();
+
+private:
+ std::atomic<size_t> _front; ///< Index to front of queue
+ std::atomic<size_t> _back; ///< Index to back of queue (one past end)
+ const size_t _size; ///< Size of `_objects` (at most _size-1)
+ T* const _objects; ///< Fixed array containing queued elements
+};
+
+template<typename T>
+Queue<T>::Queue(size_t size)
+ : _front(0)
+ , _back(0)
+ , _size(size + 1)
+ , _objects(new T[_size])
+{
+ assert(size > 1);
+}
+
+template <typename T>
+Queue<T>::~Queue()
+{
+ delete[] _objects;
+}
+
+/** Return whether or not the queue is empty.
+ */
+template <typename T>
+inline bool
+Queue<T>::empty() const
+{
+ return (_back.load() == _front.load());
+}
+
+/** Return whether or not the queue is full.
+ */
+template <typename T>
+inline bool
+Queue<T>::full() const
+{
+ return (((_front.load() - _back.load() + _size) % _size) == 1);
+}
+
+/** Return the element at the front of the queue without removing it
+ */
+template <typename T>
+inline T&
+Queue<T>::front() const
+{
+ return _objects[_front.load()];
+}
+
+/** Push an item onto the back of the Queue - realtime-safe, not thread-safe.
+ *
+ * @returns true if `elem` was successfully pushed onto the queue,
+ * false otherwise (queue is full).
+ */
+template <typename T>
+inline bool
+Queue<T>::push(const T& elem)
+{
+ if (full()) {
+ return false;
+ } else {
+ unsigned back = _back.load();
+ _objects[back] = elem;
+ _back = (back + 1) % _size;
+ return true;
+ }
+}
+
+/** Pop an item off the front of the queue - realtime-safe, not thread-safe.
+ *
+ * It is a fatal error to call pop() when the queue is empty.
+ *
+ * @returns the element popped.
+ */
+template <typename T>
+inline void
+Queue<T>::pop()
+{
+ assert(!empty());
+ assert(_size > 0);
+
+ _front = (_front.load() + 1) % (_size);
+}
+
+#endif // QUEUE_HPP_INCLUDED
diff --git a/src/UIFile.hpp b/src/UIFile.hpp
new file mode 100644
index 0000000..f1ab5f8
--- /dev/null
+++ b/src/UIFile.hpp
@@ -0,0 +1,66 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_GLADEFILE_HPP
+#define PATCHAGE_GLADEFILE_HPP
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <gtkmm/builder.h>
+
+#include "patchage_config.h"
+#ifdef PATCHAGE_BINLOC
+#include "binary_location.h"
+#endif
+
+class UIFile {
+public:
+ inline static bool is_readable(const std::string& filename) {
+ std::ifstream fs(filename.c_str());
+ const bool fail = fs.fail();
+ fs.close();
+ return !fail;
+ }
+
+ static Glib::RefPtr<Gtk::Builder> open(const std::string& base_name) {
+ std::string ui_filename;
+#ifdef PATCHAGE_BINLOC
+ const std::string bundle = bundle_location();
+ if (!bundle.empty()) {
+ ui_filename = bundle + "/" + base_name + ".ui";
+ if (is_readable(ui_filename)) {
+ std::cout << "Loading UI file " << ui_filename << std::endl;
+ return Gtk::Builder::create_from_file(ui_filename);
+ }
+ }
+#endif
+ ui_filename = std::string(PATCHAGE_DATA_DIR) + "/" + base_name + ".ui";
+ if (is_readable(ui_filename)) {
+ std::cout << "Loading UI file " << ui_filename << std::endl;
+ return Gtk::Builder::create_from_file(ui_filename);
+ }
+
+ std::stringstream ss;
+ ss << "Unable to find " << base_name << std::endl;
+ throw std::runtime_error(ss.str());
+ return Glib::RefPtr<Gtk::Builder>();
+ }
+};
+
+#endif // PATCHAGE_GLADEFILE_HPP
diff --git a/src/Widget.hpp b/src/Widget.hpp
new file mode 100644
index 0000000..038f880
--- /dev/null
+++ b/src/Widget.hpp
@@ -0,0 +1,46 @@
+/* This file is part of Patchage
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PATCHAGE_WIDGET_HPP
+#define PATCHAGE_WIDGET_HPP
+
+#include <string>
+
+#include <boost/utility.hpp>
+
+#include <gtkmm/builder.h>
+
+template <typename W>
+class Widget : public boost::noncopyable {
+public:
+ Widget(Glib::RefPtr<Gtk::Builder> xml, const std::string& name) {
+ xml->get_widget(name, _me);
+ }
+
+ void destroy() { delete _me; }
+
+ W* get() { return _me; }
+ const W* get() const { return _me; }
+ W* operator->() { return _me; }
+ const W* operator->() const { return _me; }
+ W& operator*() { return *_me; }
+ const W& operator*() const { return *_me; }
+
+private:
+ W* _me;
+};
+
+#endif // PATCHAGE_WIDGET_HPP
diff --git a/src/binary_location.h b/src/binary_location.h
new file mode 100644
index 0000000..303a3bd
--- /dev/null
+++ b/src/binary_location.h
@@ -0,0 +1,54 @@
+/* This file is part of Patchage.
+ * Copyright 2008-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+#endif
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include <string>
+
+/** Return the absolute path of the binary. */
+static std::string
+binary_location()
+{
+ Dl_info dli;
+ std::string loc;
+ const int ret = dladdr((void*)&binary_location, &dli);
+ if (ret) {
+ char* const bin_loc = (char*)calloc(PATH_MAX, 1);
+ if (realpath(dli.dli_fname, bin_loc)) {
+ loc = bin_loc;
+ }
+ free(bin_loc);
+ }
+ return loc;
+}
+
+/** Return the absolute path of the bundle (binary parent directory). */
+static std::string
+bundle_location()
+{
+ const std::string binary = binary_location();
+ if (binary.empty()) {
+ return "";
+ }
+ return binary.substr(0, binary.find_last_of('/'));
+}
diff --git a/src/jackey.h b/src/jackey.h
new file mode 100644
index 0000000..02a7735
--- /dev/null
+++ b/src/jackey.h
@@ -0,0 +1,72 @@
+/*
+ Copyright 2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ The supported event types of an event port.
+
+ This is a kludge around Jack only supporting MIDI, particularly for OSC.
+ This property is a comma-separated list of event types, currently "MIDI" or
+ "OSC". If this contains "OSC", the port may carry OSC bundles (first byte
+ '#') or OSC messages (first byte '/'). Note that the "status byte" of both
+ OSC events is not a valid MIDI status byte, so MIDI clients that check the
+ status byte will gracefully ignore OSC messages if the user makes an
+ inappropriate connection.
+*/
+#define JACKEY_EVENT_TYPES "http://jackaudio.org/metadata/event-types"
+
+/**
+ The type of an audio signal.
+
+ This property allows audio ports to be tagged with a "meaning". The value
+ is a simple string. Currently, the only type is "CV", for "control voltage"
+ ports. Hosts SHOULD be take care to not treat CV ports as audibile and send
+ their output directly to speakers. In particular, CV ports are not
+ necessarily periodic at all and may have very high DC.
+*/
+#define JACKEY_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type"
+
+/**
+ The name of the icon for the subject (typically client).
+
+ This is used for looking up icons on the system, possibly with many sizes or
+ themes. Icons should be searched for according to the freedesktop Icon
+ Theme Specification:
+
+ http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+*/
+#define JACKEY_ICON_NAME "http://jackaudio.org/metadata/icon-name"
+
+/**
+ Channel designation for a port.
+
+ This allows ports to be tagged with a meaningful designation like "left",
+ "right", "lfe", etc.
+
+ The value MUST be a URI. An extensive set of URIs for designating audio
+ channels can be found at http://lv2plug.in/ns/ext/port-groups
+*/
+#define JACKEY_DESIGNATION "http://lv2plug.in/ns/lv2core#designation"
+
+/**
+ Order for a port.
+
+ This is used to specify the best order to show ports in user interfaces.
+ The value MUST be an integer. There are no other requirements, so there may
+ be gaps in the orders for several ports. Applications should compare the
+ orders of ports to determine their relative order, but must not assign any
+ other relevance to order values.
+*/
+#define JACKEY_ORDER "http://jackaudio.org/metadata/order"
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..4822d3d
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,93 @@
+/* This file is part of Patchage.
+ * Copyright 2007-2014 David Robillard <http://drobilla.net>
+ *
+ * Patchage is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * Patchage 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 details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Patchage. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __APPLE__
+#include <stdlib.h>
+#include <unistd.h>
+#include <string>
+#include <gtk/gtkrc.h>
+#include "binary_location.h"
+#endif
+
+#include <iostream>
+
+#include <glibmm/exception.h>
+
+#include "Patchage.hpp"
+
+#ifdef __APPLE__
+void
+set_bundle_environment()
+{
+ const std::string bundle = bundle_location();
+ const std::string lib_path = bundle + "/lib";
+ if (!Glib::file_test(lib_path, Glib::FILE_TEST_EXISTS)) {
+ // If lib does not exist, we have not been bundleified, do nothing
+ return;
+ }
+
+ setenv("GTK_PATH", lib_path.c_str(), 1);
+ setenv("DYLD_LIBRARY_PATH", lib_path.c_str(), 1);
+
+ const std::string pangorc_path(bundle + "/Resources/pangorc");
+ if (Glib::file_test(pangorc_path, Glib::FILE_TEST_EXISTS)) {
+ setenv("PANGO_RC_FILE", pangorc_path.c_str(), 1);
+ }
+
+ const std::string fonts_conf_path(bundle + "/Resources/fonts.conf");
+ if (Glib::file_test(fonts_conf_path, Glib::FILE_TEST_EXISTS)) {
+ setenv("FONTCONFIG_FILE", fonts_conf_path.c_str(), 1);
+ }
+
+ const std::string loaders_cache_path(bundle + "/Resources/loaders.cache");
+ if (Glib::file_test(loaders_cache_path, Glib::FILE_TEST_EXISTS)) {
+ setenv("GDK_PIXBUF_MODULE_FILE", loaders_cache_path.c_str(), 1);
+ }
+
+ const std::string gtkrc_path(bundle + "/Resources/gtkrc");
+ if (Glib::file_test(gtkrc_path, Glib::FILE_TEST_EXISTS)) {
+ gtk_rc_parse(gtkrc_path.c_str());
+ }
+}
+#endif
+
+int
+main(int argc, char** argv)
+{
+#ifdef __APPLE__
+ set_bundle_environment();
+#endif
+
+ try {
+
+ Glib::thread_init();
+
+ Gtk::Main app(argc, argv);
+
+ Patchage patchage(argc, argv);
+ app.run(*patchage.window());
+ patchage.save();
+
+ } catch (std::exception& e) {
+ std::cerr << "patchage: error: " << e.what() << std::endl;
+ return 1;
+ } catch (Glib::Exception& e) {
+ std::cerr << "patchage: error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/patchage.gladep b/src/patchage.gladep
new file mode 100644
index 0000000..8d205c3
--- /dev/null
+++ b/src/patchage.gladep
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+ <name>Patchage</name>
+ <program_name>patchage</program_name>
+ <language>C++</language>
+ <gnome_support>FALSE</gnome_support>
+</glade-project>
diff --git a/src/patchage.svg b/src/patchage.svg
new file mode 120000
index 0000000..ce73588
--- /dev/null
+++ b/src/patchage.svg
@@ -0,0 +1 @@
+../icons/scalable/patchage.svg \ No newline at end of file
diff --git a/src/patchage.ui b/src/patchage.ui
new file mode 100644
index 0000000..355d4dd
--- /dev/null
+++ b/src/patchage.ui
@@ -0,0 +1,1260 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="main_win">
+ <property name="can_focus">False</property>
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">Patchage</property>
+ <child>
+ <object class="GtkVBox" id="main_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="file_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="file_menu_menu">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_open_session">
+ <property name="label">gtk-open</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="O" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_open_session_menuitem_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_save_session">
+ <property name="label">gtk-save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="s" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_save_close_session">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Save and _Close</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_export_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Export Image...</property>
+ <property name="use_underline">True</property>
+ <accelerator key="e" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menu_file_quit_sep">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_file_quit">
+ <property name="label">gtk-quit</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_quit1_activate" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_file_system">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_System</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="file_system_menuitem_menu">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_jack_connect">
+ <property name="label">Connect to _Jack</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">False</property>
+ <accelerator key="J" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_menu_jack_connect_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_jack_disconnect">
+ <property name="label">Disconnect from Jack</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="use_stock">False</property>
+ <accelerator key="J" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_disconnect_from_jack1_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separator4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_alsa_connect">
+ <property name="label">Connect to _Alsa</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">False</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_menu_alsa_connect_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_alsa_disconnect">
+ <property name="label">Disconnect from ALSA</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="use_stock">False</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_menu_alsa_disconnect_activate" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="view_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="view_menu_menu">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkCheckMenuItem" id="menu_view_messages">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Messages</property>
+ <property name="use_underline">True</property>
+ <accelerator key="M" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="menu_view_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Tool_bar</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <accelerator key="b" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem0">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="menu_view_human_names">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Human Names</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <accelerator key="H" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="menu_view_sort_ports">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Sort Ports by Name</property>
+ <property name="use_underline">True</property>
+ <property name="active">True</property>
+ <accelerator key="S" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_zoom_in">
+ <property name="label">gtk-zoom-in</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <accelerator key="equal" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_zoom_out">
+ <property name="label">gtk-zoom-out</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_zoom_normal">
+ <property name="label">gtk-zoom-100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="0" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_zoom_full">
+ <property name="label">gtk-zoom-fit</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="F" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_increase_font_size">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Increase Font Size</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Up" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_decrease_font_size">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Decrease Font Size</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Down" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menu_normal_font_size">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Normal Font Size</property>
+ <property name="use_underline">True</property>
+ <accelerator key="1" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_view_refresh">
+ <property name="label">gtk-refresh</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="R" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_refresh2_activate" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_view_arrange">
+ <property name="label">_Arrange</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">False</property>
+ <accelerator key="G" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <signal name="activate" handler="on_menu_view_arrange" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckMenuItem" id="menu_view_sprung_layout">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Sprung Layou_t</property>
+ <property name="use_underline">True</property>
+ <accelerator key="t" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="help_menu">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="help_menu_menu">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="menu_help_about">
+ <property name="label">gtk-about</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_about1_activate" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="toolbar_style">icons</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">1</property>
+ <property name="icon_size_set">True</property>
+ <child>
+ <object class="GtkToolButton" id="clear_load_but">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_markup">Clear the dropout indicator</property>
+ <property name="tooltip_text" translatable="yes">Clear dropout indicator.</property>
+ <property name="stock_id">gtk-clear</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem30">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkProgressBar" id="xrun_progress">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_markup">Drouput (XRun) Indicator
+
+The bar represents the percentage of available time used for audio processing (i.e. the DSP load). If the bar reaches 100%, a dropout will occur.</property>
+ <property name="tooltip_text" translatable="yes">Load and dropout gauge. The bar shows the percentage of available time used for audio processing. If it reaches 100%, a dropout will occur, and the bar is reset. Click to reset.</property>
+ <property name="show_text">True</property>
+ <property name="pulse_step">0.10000000149</property>
+ <property name="text" translatable="yes">0 Dropouts</property>
+ <property name="discrete_blocks">100</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem28">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="visible_vertical">False</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Jack buffer size and sample rate.</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes"> / </property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="buf_size_combo">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_markup">Jack buffer length in frames</property>
+ <property name="tooltip_text" translatable="yes">Jack buffer length in frames</property>
+ <property name="border_width">1</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="latency_label">
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">frames @ ? kHz (? ms)</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">1</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment" id="legend_alignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="xscale">0</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVPaned" id="main_paned">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">3200</property>
+ <child>
+ <object class="GtkScrolledWindow" id="main_scrolledwin">
+ <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="shadow_type">in</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="log_scrolledwindow">
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="status_text">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="cursor_visible">False</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAboutDialog" id="about_win">
+ <property name="can_focus">False</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="transient_for">main_win</property>
+ <property name="program_name">Patchage</property>
+ <property name="version">@PATCHAGE_VERSION@</property>
+ <property name="copyright" translatable="yes">© 2005-2017 David Robillard
+© 2008 Nedko Arnaudov</property>
+ <property name="comments" translatable="yes">A JACK and ALSA front-end.</property>
+ <property name="website">http://drobilla.net/software/patchage</property>
+ <property name="license" translatable="yes"> GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. &lt;http://fsf.org/&gt;
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ &lt;one line to give the program's name and a brief idea of what it does.&gt;
+ Copyright (C) &lt;year&gt; &lt;name of author&gt;
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ &lt;program&gt; Copyright (C) &lt;year&gt; &lt;name of author&gt;
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+&lt;http://www.gnu.org/licenses/&gt;.
+
+ The GNU 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 Lesser General
+Public License instead of this License. But first, please read
+&lt;http://www.gnu.org/philosophy/why-not-lgpl.html&gt;.</property>
+ <property name="authors">David Robillard &lt;d@drobilla.net&gt;
+Nedko Arnaudov &lt;nedko@arnaudov.name&gt;</property>
+ <property name="translator_credits" translatable="yes" comments="TRANSLATORS: Replace this string with your names, one name per line.">translator-credits</property>
+ <property name="artists">Icon:
+ Lapo Calamandrei</property>
+ <property name="logo_icon_name">patchage</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/waf b/waf
index e22930a..9c57f6f 100755
--- a/waf
+++ b/waf
@@ -1,16 +1,169 @@
#!/usr/bin/env python
+# encoding: latin-1
+# Thomas Nagy, 2005-2017
+#
+"""
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
-# Minimal waf script for projects that include waflib directly
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
-from waflib import Context, Scripting
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
-import inspect
-import os
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
-def main():
- script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main)))
- project_path = os.path.dirname(script_path)
- Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path)
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import os, sys, inspect
+
+VERSION="2.0.4"
+REVISION="5e4201f8b4f4a2a744d52154db826b02"
+GIT="x"
+INSTALL=''
+C1='#<'
+C2='#4'
+C3='#)'
+cwd = os.getcwd()
+join = os.path.join
+
+
+WAF='waf'
+def b(x):
+ return x
+if sys.hexversion>0x300000f:
+ WAF='waf3'
+ def b(x):
+ return x.encode()
+
+def err(m):
+ print(('\033[91mError: %s\033[0m' % m))
+ sys.exit(1)
+
+def unpack_wafdir(dir, src):
+ f = open(src,'rb')
+ c = 'corrupt archive (%d)'
+ while 1:
+ line = f.readline()
+ if not line: err('run waf-light from a folder containing waflib')
+ if line == b('#==>\n'):
+ txt = f.readline()
+ if not txt: err(c % 1)
+ if f.readline() != b('#<==\n'): err(c % 2)
+ break
+ if not txt: err(c % 3)
+ txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
+
+ import shutil, tarfile
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ try:
+ for x in ('Tools', 'extras'):
+ os.makedirs(join(dir, 'waflib', x))
+ except OSError:
+ err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
+
+ os.chdir(dir)
+ tmp = 't.bz2'
+ t = open(tmp,'wb')
+ try: t.write(txt)
+ finally: t.close()
+
+ try:
+ t = tarfile.open(tmp)
+ except:
+ try:
+ os.system('bunzip2 t.bz2')
+ t = tarfile.open('t')
+ tmp = 't'
+ except:
+ os.chdir(cwd)
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ err("Waf cannot be unpacked, check that bzip2 support is present")
+
+ try:
+ for x in t: t.extract(x)
+ finally:
+ t.close()
+
+ for x in ('Tools', 'extras'):
+ os.chmod(join('waflib',x), 493)
+
+ if sys.hexversion<0x300000f:
+ sys.path = [join(dir, 'waflib')] + sys.path
+ import fixpy2
+ fixpy2.fixdir(dir)
+
+ os.remove(tmp)
+ os.chdir(cwd)
+
+ try: dir = unicode(dir, 'mbcs')
+ except: pass
+ try:
+ from ctypes import windll
+ windll.kernel32.SetFileAttributesW(dir, 2)
+ except:
+ pass
+
+def test(dir):
+ try:
+ os.stat(join(dir, 'waflib'))
+ return os.path.abspath(dir)
+ except OSError:
+ pass
+
+def find_lib():
+ src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+ base, name = os.path.split(src)
+
+ #devs use $WAFDIR
+ w=test(os.environ.get('WAFDIR', ''))
+ if w: return w
+
+ #waf-light
+ if name.endswith('waf-light'):
+ w = test(base)
+ if w: return w
+ err('waf-light requires waflib -> export WAFDIR=/folder')
+
+ dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
+ for i in (INSTALL,'/usr','/usr/local','/opt'):
+ w = test(i + '/lib/' + dirname)
+ if w: return w
+
+ #waf-local
+ dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
+ w = test(dir)
+ if w: return w
+
+ #unpack
+ unpack_wafdir(dir, src)
+ return dir
+
+wafdir = find_lib()
+sys.path.insert(0, wafdir)
if __name__ == '__main__':
- main()
+
+ from waflib import Scripting
+ Scripting.waf_entry_point(cwd, VERSION, wafdir)
+
+#==>
+#BZh91AY&SY��GH�����������������E��� 0�#)��bIwz�8#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)_m�m���V3[Y��T��T�[+��$��s�a���Z��7�č�}5���ݩ�٥��{n&[>o{�o�#4�͖�m��@��&��v��-n�j��r/wtv��}��_2H�#4��<2u�;�s����z��]�d�s���=�xn`n��m����h�WmC%�g�#)#)#)�#)#)�h�:W�p>����ƃ�;��#)�����6�G�I�(����ܯ��(���#<hH4#4@6�#)w����*�*J�za�(�ET*�%Jv�N��{h���zu��+���ό�t�|�����UkP�����w7W}�څd�#4��e�j�}z˱뤽ӽ7�y_Y$�g���V�����2:eV�}���T�3�iwk��=o7l}=t��h�*HTJzz:�{�L����+{޻���4���iN�ztt��[m *��v{�qN������ty@#)":�T������^��U٭o������}����:}�n�Qkh,��v/�@ݮ�p�f6�'y����9�z�����6������z��Ǡ�ז�ö:�DI�*[��wo<sv�X��.�����������7�s7���F��=�y������ݳo�=�7���{�ܭ��G�����5��٢6ힺ�˳s�[�O ��˒no��n���W5�>>y��8��{�z�{��6z\X�Y��y��>�C֜oq��rhf�T���f#)�)绽=;�}�=�y��^:!B����jR#<F�4U�:�w@�E6�+fݳ� t��v�lOs���^�^�;� ]ww+���j���ݝ�;+&z�o#)#)��ծ@#)�̽]����|�]�h�׬wk�U�;g����7���7:G�y��]#)}S���KJ�嵦&����y����7��^���U�&��o1��h�6�m�t}���ϼ�����m�o�T&��{��I�q�Ox��۸x��ۯz�޽��a�w���ϭ���@KM#)>N����{w����ݱ�7n��T�i}x|1��-{��޽ST��]#)#<�zSkvh3��ͽu�=;�u��\a���S�M;ϼ�z�� ��]b����Aw>���}Y�ۢ�=��������ׯ:Ӷo,7z��� +;�=yx������ʹ4�N��R��X/t�=��[�3�|�{�p�h�@�gKx�{������fu�.2�͂�}ܯ^�;�� �uE���u��N�1��D��ŪDzWn$�����>��>��-������]`�۴|Gf ɣ��36���έ{��ݎ{���t6�'����o���=�ۆ�.�OSx����|7#44@#)�#) ��#)��@ѡ#4#P���������S@�@ Кjf�hMQ�=T�H�ё��#)@#)#)#)#)$!#@L����~&�h�ԞS@=OB��#)�#)#)#)#)$�II"i��S� ����O����#)z�����#)#)#)#)$�&�#)#)��#)F���OS jbdi��#4#4#4F�#)$�D�LA22& =S�z��<��~��@��#)#)#)#)���?��V�����nV�ܗ�V�P�"1���]� �`�5�ٗ�U��[Z��$}ij B?3���u��f��n�R��y�DR�����;�b����B�]��Dg����DªݠJ�6(9�Zt�w�K�\<�����:�N�m�x���8����� �6�o\qMf�)�#C��Sgȅ�)�U"$F1PcRD �R�-E� ҂Dj Q��S����T̬ S@* ��$QB�$PlDP�\A�#)�QRj�ݩ�lV-�wmI[j��kmj�33!d�3P)�&�KI!4�����j�c �(Ҋm�� �$�&���h�5њK&�F# %��EF#"m)F�XԱ�LX��*P6L��E&�hڈ�,�Ҙ��TD,������5֍Eh�*#4&���ئ2P&4�2�1��cR�l%�ڶU���� ؓFfI��"�,�i��M�MIF�J���R�2�ZL������5f�IK#<YX�2B$ʋ4��&�D�6)5"Q�Tb#<��(؄!A"2���2#$��0d"�,� DD� ��hҲ��ёA"���Y(�6!��X�K) 5����Rd`�4(�bARh�%�d#4d*1bH�L�Zb)���!���i�l&hX��6&��`D��b�%��� $M�(�#<��e d��b� �JJƦj6+ i5$I1���HȲ[E�e�2MfR#c&�ȊQ�"`��i���)`��*Cd�M!����ʐ�$R�! ��"�MH4Z�$��e6Q��S3H�#A��ژ,Pi,�B1d��$���0��Q�0����&*#4�$H$0��L��F���2�b�6I�35f4U)TJ"fCR4�4͊�6�h�#Eb�I6H�Q�%M�i�,lE4�I�LR��ͦD�D`�)�#<ji4�!��4���M�DlԩL�S#$d�)Y4X�X�eI�EcI�M%MFɨ�I4��1�2٤��#4*`�0�L����d6dI)�S#J �&Bj͕�XK�1&�d�,[F��5��[#<D�%ؤH0��RX�i�S4Ҙ6J�iL��A�$�)1�B�Y6De0H�ʍ2�Ҡ�2&c"H�6ڕ[FP�BZe��cf�RJf"+A�K`R�b�Le e�6Y6 �S-��F&��e( �BR�����!55@�# c6�1Q��d�4��Y$R�hĨh։��*66RMF�De�Q�Le"�`%I�m" 134��5ciP�6���d��2�$̌�6�CJ�ɓ-&�lYV6�#EM�#k"e�TƳQL)�c)36�m6cF֋Dkd�5�J�e6�kE6�R�cf5�H�Hڋ��&�R&#<TF�U����d��X��Y(��h��5bh��*�Q*Ѱ�F�X�i4�4BkP�J)� TLD-2LJ�X�5�LKcT�#4�AV�+m�iU ��MY4I�1!��Qd�ږ�lb��m�iS��Tȋm*�I35J��4� ���3$�e� �RK&�YR�h�Q�2�EE� 3 RbŁ��A��"Aa1�l���f%E ��VI���&�6D6X�Se h�Y�D,JfIaIE��`bjZ5�!��m&J#���f�$�F�&CJ2����@���cA��Z,Q4�E1fY�l&1(,��0��L�Bj-"�L�Q�)$� ���Qe+#L6�����#RE���2V4�#4C1�I�ٔٔ��6���S��$j!�I+EQ�Z3DV*SI��,k2���$d# LR#416$�6�(QR�b���[%2�h*�Sd�T�Bb�M$��CJ2Vd��["6"��"TA���1DC&��#b�5�M��Dؘ��! �h�l%EF��j�e��a�P�R4�dlb�Al �+,�Y �X�fJl�*H�m �QX�&��R��A�A�6MQh4�J!���(jcL0�J�TF��K!E$ɶdZ$ Z��J�Zh�-�&�%�"�e+(V,�� #)��%M�DL�U���b�j*KE�)E�Z���[I�J�*�6ѴMbS!�MIa@Y,T�l�$�ĕIHk��F-f@�CQe*eLE6ԕ�� �)���F#<&�i6�e$�RcI�#F,$�Ԣ4m,�Z6�&֊�e(�J�B�m�RQb�M �S#0&4ԉ(53d�����j����kBV��cRTQ$[b��imE�h֣b�I�3e�[+mE�Q�M���E1�(22�S%cE�$Ѣ�M&kcS6$�A����dѴ���QX�3H�2Q��ckm���D͢TĔc �,E�J�#%X�ش�[F5���42��Y-���Bڍ5bh�(���+$��a�Ԛ����eDP�j#I��"2(��T��`,d���o���@��?�x�k3HH���1��p���tKG,#4����P)�I����yo9���>���n�Q�+k���z#<�0(!�����Qh0\:8����I �m���8|{'�l�����絣��IB��@%m#<�H��+�O�jq����d ��}�,u�$ɬ���u��4�Zv����"28�ca�N ׋��#�kf��RRW�6S;qC-�P #��2&�8cDOL#n�#��5uB*35S4��(�\�ww,��߱�g5r#&��P�(M љN���X��0��$��b\��Y#)�cFq#)��=ݨe}�͑��sI`�"�AL^.LUA��.9_�6X� n�� ��`���@S#<-��~��$?'V�h�'���`v�^��D˳b(��ФY;����I�7>#4���;�"J<v�����Յw�#<�A�#4#4e�����^���uQYʄ��m�������#4k�����d��.���W6�d4QW6��豱B�yF�Ii��Ȥ��}HZ~���}�Em���H��`+�5g=� a��a#4�O,��Ô����a�tFa�#))�4�Ӑ��4��9�� Fԣ+��esF��k�W{���r�J)u�$�s!_��������j��T�R�",0%+��v�˦N#-X,��OUM?6�#4+j~/LHe����Hl��L��#����T�C �1|�b�_�- ��E�����9�G��o��б�T�[ D�j��r�D��Խ�����blù^�N��W�cv\�|�~�_B�,rp�{q�rugbh��&�/�z��3,�k�����^Mm^��[)�TR��_��Ny�C�;7kt���P����HNB�F��e�����RO��ܲa2�х����m��M��Z+<�T�)AG9�x�_�:+���j�*�6~Yjd�o$�~ �G�.͡uQF��]��S,b� C��PV1��h�ʊ���7����Y`�Pm��q��<KB��E|���J��ek�]Y�)���d*(���&�y�QV���]�1��u&�ҹd�[��#<��(5A)�AQUyk�7!�dLcI�)>�7+�ΠJ��RtN�PL �r��RE�Pɢ幨�����>o4���T>�rŢ��[����z�H���Ti�%��J֪���Y�d��~�y�A���t�H�Q�=��-6��`|���R4��Ib?����԰�#]+k6gޒ|R���t�n���m�_ι%�*�n�� L+� �PH�e!BRk�yc�\��as�~.Y#�~�05T�����4��E2�R]U�"��լ��j瓢o�YNNYlӾ��Bj/������y��-%���^S�hT�qF�#4������{� �„S���F��ح-��:���[0"<�% kTt�@'�Su�o҂5��|��\i*�'��I���#V.(�,��_�g�<��Bׂh�:p���Tn�I�~p��컈 �����I['��5�U%&aFeS?���0��0#�N�j# <0��D?�3���}��g��S� ~$����#rB���E8�*m]*�w;=��g#<����YJ����f���Հ��2���WSn=|f\�1�Śh�w����dA���jC?L[��&C�|�޾��TbtNl�L���\S���g�S�C�W�Br�|P�Ã��ͣ�e#<w�����N��m�0��x�̵���B�g��UP�:��:���J}9�&�E��8��#=�#)���a������8`�h�� !d��{����Ev�I�(^��Q��\T�%��hԘ�t��҇�\_�Mz}�M�uXa�Ht���"$�I?5G���& ܟj�bt�dz�s4���z`s�UYL�YI�j��P�(Ib��1G�|��Y<@�&�\2wCK���f��#<B�!�;?�;|���ǒ'OO7WC��Q8��8��w H�g��SV�p���M�5��ݏ͕��!���r(���1� ���:+ho*�L��a7~���Hا�.�A��ds�(>>3h�q�I���!u�Z��٭<�#<�5DVh�0Vʔ���\�#4�����j�7��Pm�I��G�G}�;wB1�nn�:��P� �q69�7O�#4sOl#<ߧ��x섒H�eT�Ƕ���ڔ�ңo�#4_ӓF�0��i�A�| 9��#4�!&u;.x�cVya����i�a7'�BX���O�Q��T���@[�\|�]20ӛ��#48@��V��[�(��cm�c�#;j�1�-���b���o��1ý��_�7��b��!��Al���-���J�u��׹� R�(sd��)$�yoy�4�.�7+��v����:��d ~�A�5ڕ���ƴ��7"������(O#<����U����:��/���ѧ�UA�����'�v���yAb5+좙�g���no����Wsv�Gr��Z����{b��f�d�?��ھ�黄�4jMPk��PC��55�)�����Ż?���m�-kY����}�kw�i���?��N�ieT�0�B�]~IvS<f}���4j=,j�.��V\�R���$\�%��F#4�c;�9����'�#4[��8G��DL4ww�9���9����n������U��8�f��EE�f�#4�6&�u�����N�c"�S�h�~=�S�߿|m�?A����`ʼn���i���齇����"��mw`aI-�=o�Q�/r+��r�aYAx�k>Y��qb�1��6����=G�DM�~���?�R|�Ë�����a�DC,��ZF��"&g����_g��T����Ow��0����2�XS�j��Mwq��3TYL(Xm�(-��ۺa����=���ݑ�L�xdl[�w�G��fHg�J���~5Py#Ϧ�k��#�g5��]�\M��/��h��P�+ҡ�@��͜�S�#���h��1|��z'+?�Z<xT4`v-<R唠�M&��1&#4��緖���(WnN�I0��L��_�|��l���x���G�V���hH�(��Vͣ,W�&�d0P�bhP��(��<�U����n�L�r�";{=L��C!$ȴ�ÓX�Z�;����Ӻ�w���$vp���c#��|�T\N>T��:L��WnʚbMr�a�Y��t��j��$(�o��=�ա���@w��nȰ[��{Vt9���L!��Eb�7�V���� \�5/�#<j�0J��W�$����(,EYoUe���s�[�D8Y�B,U�ґ��CK������L�v���&�7:}��*a�Y��TL �!���6�C~4a��� ��.�ҏ�p�Jm ��i����3$���q&��L�)�/֪�q�ʳ2�Tl8�����sև����7廘�ޖ ⍣��{D��p��}}�c�Xy�z���. o�n���q � KCW ���͘^E,Q�m5 Eu6�뒌 }9�v��5����tX���bA;����y���(1DE珆}ٔ���k���8�g�G���~�������{g��٪2qū-���1�|;o�.���1�;�&�e��[�F8Eխ#<UQ��'�:���[��>[;��!���#4j��>ޗ�C(E��P'�=�f�\iȉUD|X�� \w�%!��]s��oT*���W���)QC\U{�}z��R�Ʃ��X>��{ ������ ݟjs��s��^�����m�u�V��F{(������Q��;;���ӺY�T�C T��Xn�@�v�-N�s΄�o�~wJ,�/~�4�86ΏmQ3ʲ�䰏v��$@�&s�ť胚�#O% e5�=�Q�*�o�r��'��=QruW�.5r�ڀ�j{LY��Ō���6�_�\���7S��x�`�.��M�wq� �sfF#<�VR��7݉�iGk5��g�tͽ�UQ8���HjJ�9s�A�@�Շ�i�#4��x4����[�S�Z-�jz��Ƽ��lc�j�<SFg2�*���v�C,�R���gU�I���Êm����5��-�tQ�*Q��(�� �F>"?ݘc�<88������|�T��=�ڃ��Z���1�~��(�:U/�Se��8��*��ph��'�K�u��c:�67�O3ϳt�`��RCR�TX*�Qn�b���b��y�1��O���Z��7�#�<^�}��ҿW�� .�ipH���$#�Gt�-icX�ڸs,�TX*#<�Rl�O��y@Q}��@?_{��J����Q�уɥ�r���Л����?|��t[�#4�)���zEL��(�X���1R��݋qV�G�5=�����Y��?�]���vf�6u�����'0�d+���D� P�������FʲV��� w���n+�8˃ڎ6�vO"ό�#)�]L�z��.���QI�����\u�\����r->��/�W�p��lQ3P�c�s��Ŭ+Il��:c�(�~���P�����*�ݎV�Q�I��?�xH���Q�k#)�ED����H��NjDvq�z����+b>�8`i��jm�w[�{���q����)YSW���7��ȥ�UܜQ�H�"��O��3���4���~l" ���w7 l�@��+��xv�$�T�K�d����m���?�����,�}Dw�N!�+#4g�A5('V?[��~[~g���<��b��P@r��ó��w�)���0Sʣa�l[|�-��3��N��:?y��ֈa|�}1c���i2���P�*񷺢:��T�6M�|�hA�졜��f8u���[�4팍95j��kŋQ�F�H4�^�����blP�s�y��aj���04Z>�� UW���B(q_S"�D�QiF!�2!�8��,��+������}p�28�|ף����`��}�:�ոQ��Ј�g�L�S�mY�.��q��H��f�4Ш�7J./b���*t��á��m�J1j��d^E��.Z��=X<W�,<�Y-�U#<�b-U��e����;��$�\��hp�ݸ���|`u����4�t�����)��9zb�$p)w������3M���"iIU���!h2o�P��jh��@�^TF�v4��^��;���O���-]�ߒH�Y��ߞ!�ہi�0�;���)�v����������2�JG� ��*��m���7��,z��m#4S�]Rg�ݸB���Ԕ��5y{�j�`�x�>��iS���+ �1�;��uC��'b�����C����復����33���\����^�q�=�Fd����:#���k.�F{� �π���sQ��({�)q�j6��.3�� X0`��Y�L�%���=Mz[�"�[��ݜ#�C���R�L�$9�>��$! 8��DT��H`Oq�zV�1QfL�j��r���/VzԺ��O����F{.�V��(�.)2(u;�!�92� Ux��4�:�b��'�� ��W�G��y��ƶ����R �A�"/R�ESQ�ۇ��񁨴��㽵��A���nP�J�����*v�;i��������=sLr�!��J_�i��S������3���F�Ǐ��(S�����K��������9���(l�K�B2j�mdHrtq���NNlIW�O���ʺ���[\\ �pA�o�8&�Mo�Gg�c�#47�Ug��o7�Q�XʎZ_r D�8uNf̖D������J�﴿� �zvN��z�gG(��l�s�2�_n*���`�z�}�#<�v-�J#�Y?C�]��' ȋ��@\�j,,Qw 2[GH�����/�k_,���h�:���y�� ݊CK6���y��>��{�#4\�#�#8Ў�x����8���߮]�c���nzĺeR�F��6�a.2.s���Z@��z�M�#<����m����3`��ˣ�Ú�%n�$��,A��SMd�`�M)C�#4�!��Yo"��R��#)���=pt������x;ں���B�^܂ �V���\:H��e |�8���lr��[\��#<6�����8�����4 7|v��re�D́9Q�I�j�z�����S��9S����#/G<�����$�"ԣ��_9�f��wE�_��߳�&���t�d���:�� <nK��U�p�#��}��1�;��Έ �o�PM��&������H�����|�$��o{.0y���WY��u�\��C���4�a�:Hqs�:�xK���uYD�r�a�s�5z�uT;�ۘ� E��l���U��Y�G�S��]{���v�M�R4lQ�3�����5ϡ֠��i�`�D0����=?���㧇�m��4>zq^3�mT��8R����b�]�_{������م(�Yi���G���h=��Dd��s��L�DY�~P�&#)�}K��z�o �\�#4 ���A���"/]��"#��O6��~#4�t��wZ:�ME��\��k�y�����/MI���M�kL�( ���͈�SI~y���&�� b����1��M���%O�) a�)٘�ym�������W3��o\zDi ���V�D�,������tƣB#)��!#) ���gK���l0y���q�f�� _/���2�e�O�K���~E��W�W���.�E���=#)���5�<��Ag�rK������l|1����o�x��?[�\n�����ئv��bJ���k]��Ԓ>�'_�}�@�C�3�Gl�=cOO��+wx�`�?(� 9>n���t���#��>�� $$#4�|Y���E����=���uR�����E#)�#)t��g��=Z���7)�42!����x�< XɺQT��<N\�~�nf�8�*�l�#�9���X:�p�閂�|!y������J2��^8�SeM���n������0g7ͮ��i��������f��*��X�� ��kӗR���?�c���p�T�bWvƃki�E��'F���QU�l��M�y��4���� #<P�_���V�x�#4��U޻�q#)#<�b�W�n$�qٮn6�3�Zq��9���."� L��rJ�*�ICx���$y9��x��,�e>��$�?7]E�,�#6�q.��� ���93O{l��'����t��Di���j�c�U���2ڠ�������/��+��q�!���C�KN��A>�9�$�mz�S�4����j#40�_�Ƽ�Я�-Kr�|z54�I�3‹���_ş��9�A�w^�1(6��QͰ2 IS�;�)<�P�8�ܛI�a<Uyu��p���o[����ԝ3�w���|��R2�h�%��"��F�y�ã����w6wk7�e��W���I>�5��.����E���h�V?�`f�⡈y��&���m+�P#<����B�=�~��W/<쇦2f.V7N��#4na���>���6ҷ9,#)�o�Zf(�߯���_�:��'s�v���3�a�"�քqTET͗�\Y,�'y�f)V����RzN�a�4Tpmu���1�!�g���\"����d���L�ѹ|8��7����29>�#4#4�&�$QF���Z�!��X���}��;?� �ۘ~�#4��p��7�ڐ�K��*#)-X*&���u��G��dK�:���e IL�P�D&Q�Ti��5 �U�RY�d@3�1-�xv�Ӌ+��n���Y��dsӾ�1����d�-�#)�S��I��}�<��/�#4Y���DL�ew j%J ����� ڣ]���� �N��)��MP� �5���?k��o���[_c`��9�b| ���#4B���Lɏg���ڤ�U��s�rQ�B���|��Fm=�Ab�G#4Ҫ���#4,U����6e��!HbC(�S��08��愻��B�e��w9c�k"+|�m|��[����{�$�E�� ƃ�>����Vy[�kUtG�]W�� ���C���ù�qyJ�eE`��U��/7�*��y��1�5^;q�1��q�����ِ�5p�f`��HH� �Φ@���|��]���h>�[�e��7eu�#)���PW�ww����#)l��S+m����@�&����0�e�ٯ������􏇤^Ϯ�j��t����������Jh>��:�EС`�����HӁ��UW�ީF!kj�|;k�;iU"���0)1- #)2#<6c=$�7j������]�_���Đ�/�LJn#4���h���t�GJv�����oW�!r����Z�saz�?gߕ�M����0����e5E�Y��,�s�MnB:,W,(��*֑�9�z���Ul��1��X蛎��t�r3�����9�Ö�y�O^���v^5<"�7;���B���J�І�99ptb�d*GN�.�)c9�p�C�9�뙺�Xv}Cvf�}[�N���j]�P3o��Ʊ�鱯H\��W��F8h�R8�c�a���cc���K��ͣ�I�L%� ;>������0�ۥ�0ez�:�Tjy�Yd���,�|�����z��ۊ%/)�~��Z��k��YE���-Tv�q<S8-y����)�v�ׁ�6� ��s�v��Ŵ8o���=�#<�s��#<r��R���{�k�b���� =��ݲ]P��%�]���\Zy��h��E�k��P�ɵE�̘(�ڃ�><�g��/^H<� w)���B�D6 ��C,�m#4S˜�z ,�r������0iP���1�� @6��;�ș �{���E�_������G��X�qtѩ;����H�tiX�'����?����#<���PY?#<���*��.S4#4���c�4��.��L;;�}���I8�'#4����C�R�]� ��9&��$��E��8U�&B��)���wQNFL�T`����<n��gMa�Kn9��S� ��N�����+ b!�m��M����sJ7�I��s�m�8����Z-l�.7|����� ��ƱA' ?E۠`,\:�iɳ����V �����c��"$���:��Q9#4��c�Z/�iݘ�H�K�VpN���-#4ژ���J�)��+�+F��A���a�i3� j!������"Q�iF!�U�Cb'�B�f34V�,X�dFL�B�,y#*+�Q���3�>��;�͏�螚���o�^\9�*�l���0X�#��j����~E|�uW �cL�Q`�2ѷ�K_f�hٴi��%� f1[<�#<ɛ�c����-ynW���sR�Y &a!��������>�8��F�~�-�yzL�Q�� ��}��Y�����}�~{:��>+}F�!�.�[ܓ�u{)���m���‚賖�#DǠv��g6D��2���O�i�+;������X���Jq>�׈�BQPo/#4���>`+�`��G�d�8�R�����?UErdy^Bo{GZ�)��6��'T���?I�Q��,˂����y3{��w�G��w��7��_N��痧�r��8r&A��l�f4:Y@�B`jG�Z��GK�j)���l�1𻒵�g�S�7}�f��P[a ���>�Fx^<�^O��9�ߔ��;‡��:~�� �c���#<e�0�)[�k�c7C&�$�id��L��AǕ����S>.11��?[x�s��8�N�������>��>)&�<f��}��<�Z�oS��*p�[�t}�+L���$֩(���(00�Z#<��8��|T�n�8�s��#<��߹0YE���+Wz+V\���ܒZ�ؖ �#)�~� �ϓ�Z������� ���Zq�$. 8'uJ����y�%�m�)�ʿH��>�4���.�Ó`�rhm1�M�=Y�L�"�#46r�L��E�Xa)&�A��Pȳ"$�֋i'�G\�4�)c�QG�m mA&� =7�B�&�i�ÖZ�!��֨o.�%v����J�У �Q̚"ؙ#<Ȗ�C��:�1�a)�L�a�bfU*E $e���I/<V7�M3�x%�&����TS_�zI];Q<ys���8\~|���:�cI������>����JX�=(,�5�z>u8�X_i���T:肝���W���ڟ=ӑ�� ���H�CY�\%�=A{����Q�Z>��AT.�T@�n8�K-�ӹ��hؖ eb�/��,���{g��}e��7nشC&ϣ��]z(��9h#4�\� 'n�,�o��#��/t���iv6�s*3*6�^%e���#l+2Zd}^�e��w|zGj���p�(uMM�C��l��S�0�eF׶ɼMR���4<5��#)� 0Z8�pY�r*�e�rG�DÊ�&{*9F� :$���}��¤�c� 4�$���;>)͙]���,.%�?ߜ)ǐ~���>��k#e���� sh0�*1�Δ�Ȗ5�����s��Z��ssFl�uժ������<E�S�"�4��1�x��>��o~_�`c��Ɓ���㉑F243�tim˓*�Db���b��zջk.i��2o��h�E=N��h��:��=����|�O�+�[>��>T�b�{qW���@���۴D4��R�B��vï�����c��U����~� �N����#X�M�H��,ȀR7ou�����q�@�k,�d؍�@z�p�t(k��aU���ld��7vƭt؍\�$֊ܶ�5f���}Qb!c��]��V#0[�;z#4D��J-k?z�5ہvڰ��#)B9C��I֝3�3�o����G�\�p�~�hRF��Оl�}��J ���̿�W2Aq��(r���>��#4����`u����m7F�s�/k#)��u z�@�C��:ok�:fu�5g����?�� ?;�k��M$$�(@F%������*�=���~����~��n�-�@�S��#4!ֈ�_Ծ�9\���Iv��ŧ��E>�9�g�Ge���R���Q{������`,| �3�X���ı9�d���p��E�#4�-��Ւ�=����7c�x���4�.�.�: X�As���l��>� _6�9T Q��̬�S����\��KM���;��-��鳏�؜��n7S�^���x��}xm��I���<��>l\=>�C����/鯺�gɘ�-��C!�շx��yـ�>��l�x������W�oB� z��tY�����L���~'$�.�gvVru}Z�E3�s@@�� ���$�e�Ɗܶ�Q�j���}�=���0�T1d�{�g������f&k�<�8�W����=}�}��*���&dt�ɨڡ �@�K�f=�����/����� A�z|l���;�� ���}�75�� ���ȁUP2��%4u��߄,�N�#4#)��|�G�2y���"s�60�o�������-�"¡E��v����mId�3��������M���_Gmڍ��{κ�j�%?G�,(k�@��[��[W����~`D&��)4�.=��[0�8���D�c��b�Ms���~o�5y \���n��i����m�^��?�_������c���a �tX�E���G�V$1�g����k��jcI�*��Q�����O�) �"�?΁) J���� {ғ��r�E�Ѣ$#�#<�� ���@����*�o�#V���� �*�����������Uy�$7خC��f�ii��U�l�p��A�H,0ʨT�^em/�x�4��{z�m�X8/ W2ho��������(Y�Щ�E`�����ʓro�ɠ"��E�r�TrC���6��kWoҨ:�|��%��оc2�磓�йL�u�Qk/��8���6�k8\�a���ԇ�2����k��h�1�`���jZᡂ�~�t冭���o$�#����B�#<0����N5S"��#<�l&I��,U)j�Y������3��wK�T�li;�i.�l:<Q�ˆ��Q��E ����C�?;Q �(�d�t��a��^��?u��8L��}��m�͏�y�k;~�~�;D����z���'��h~~�B� ��޽�d���[�����z��Ի=?�L?[��,���w�"V�5�l9��Z�G�?���>ܻ�`[oF��-�tHk���p�#)��$U����m���-�9��9��yR�:o�^�� ���6��,k��_�S�������u�g{�Ç�<#�6�U�����o�s�eܮJ�a�ù�W���7�Rz�V�%���@#<Q?#`�u��Kޡ��pj�u��nv5��8a�A�s�J�2q���G��r�$JK ���X�K��}T���5E�[��ጾ9y4l�P(�rt_<�U5]�nWyJ6�� *�����`�a㌺��u�KH���6,��>��=a~�@��S���� w􄐤�̞���Mhr,�0������z���EF�e0@#<�������"<5f�@�1�RY}���ӳҤ������,U����&�v?�����G�,�_39 ���|��O�"%I�a����k���c��?s#kQêM�ղήZ/!@W��'J��!�~� `G�x����(�6P�#4#'$y�t�a���Q�!`TP��egx��W�`����Q���a�~B�F��,��$�DF� �"���#<��]�i����H�LFPCx`�v�����*��S�Q�҄���*���gG)�-5aC%Db#4E��5�xP���H��J��Ϲ�F*:��3�� �Ď+��0�j� ��bW֬I�����ą�j�qbOO'���U�I<YHR�T'�v�����ky��?E�?�ݴ��k������/��ƻ�O�5�o��ԇM���X’�2S Z�BY�R0�I#<����M��n���-��?�}�Lj�l�7`��ث�b/v��zw�]�?O��移��\~f��*���� ���Z��>���l���~�=����׍>��%-Z�:.Z��w��l�������p݅���=�>���4'#4�9�.��za��<��[*��_��tv�K���_O��:��z���Ӣ���pbtp��`�<���7���y��Ì�.a������X��-����ӳ�~���� �Z��'�c��:�VΟV�#44�{��ߣp��z����$����9|��G!#)�4��|�w���s����ǣL��b-�k�Gܩ˟�����JZ��h;^�܌=X\ۯ���åx���ɷ�7[���Ǚ���)��#<1�-��?D��4pt{�M�ykOF�S�}�v�0�Vս�\�my�n:�}Ϙֱ�C�|��]�[��G:^ĵ���N�L8b�v�v�9�"��������7Yf�[f؛l�����Ʊ�ɝ�����,>��9;�}�k{�MS<q�-��!0X��H]����W4���F�lӏ6�;���ˎ�|�y�����ۉ�X�w��|>�����w��<8�{����(a�bǖɺ�=#<�8��r��m����u�Hw�����+�X{��XU�_���w�c�9���5��>��G��`��GR���p�Bk�== ��L�/?�{������8��C�o�4[!�g��_���}z�#��g%����O϶����k��h�ϧ������J�0��ypd�'x`W4�*��ɱ�]��>�����*+H#4oF6���M��-��N2�5%����#���O�>�0��3�GW�QB>��3N�+�}#4�7th��]��'q8י?7���%MW~}�˵��1���Z?��:��%���$������!�2��_���ݜ(=6����N�X�.b����>�;��2����f�W}�6����8�3��?S�FƎ����������]�7�v�����^z�<aM.z90�`G��v��u�^i۳���]w��OG?>ǧ�~�����?yo�u�j��n<�M½����l�֒�W����`�������g嗡�iv����_K���P{�#<��  b�Z#<#&*��|~�at����Y���E���U��l���j#<��Kj�����%��� 1��QpH#<./E�w�Q�n��?�oH�U�����hO��u[����~����y����N��<M?p���x��3�Oc�����=��E,�N͚�fߛ^�sRf�N_�~��|x��P����#����|~Jxr�~<�<3�?��#<>1����< 啾HyG�#<=Oi����f��Ə���}�o��� 9VZ�~�Dp?���a��o��.��of)�n�����׻U��w�]n���3V���_�`�,�$�<��9n�c#4��ⷎ�����~:��~v�s ��A��Ĕ ����us���/�>Ե<���ss�ʭ��0��5<�x��������!P�-��O^Mل܎�%BExo�N<�s<i�Y��{��^##4�[כ��G���J�1b�{r�0|�ws.�w �Vd�}��v��4Q�<��,4�#��#<y�F:p��`7_NN���%�$60I���=��0tZ��c��Nf 0Ϣ�T(��][E�d��2A7:�%l� #)��j�C���`�d�>�"r:J핌렦�i^�y4β�W�t9~�ݞt�M����r�C�DG[0�]��A��(�g�Ɖt*����Y�x;.ٙ��X�%�o�(�6!�7��:���W�o�'��{x~����Xqi4����#)[��O^7A�h2�c�u�����+�?*��}�u�T�t>|�#5hT2�TFmA:�`�p�i3b�;��Z�f�clT�ԡ�������=�}k�i���@�|� ����GD�^DvC��Sd�[Ʊ.���\C�h���#��m�+���?���=�*ۦ9� ��R� .l�kЃ1�P�x��V�h��h��U�O��x��<}'fK���c����@��II�E#<��o�x�;N�4�����LB��o5����#4KO����.1��@j�>ODŽW����o��b��5����XtO��\�#�6-�^#4���X1�1#)N�7$z���#<:��=GI.SQ�EG�[֓��N�BC�Gv�@V�vi��g�����w�{���8�C�����e���3�Q�o$)Rܧ�Y3f~E����z�"BO~?��~��/'�78�O�n#��5��la�<���ڻ\h�Ï���Σ��4ݦ�ޫ�1���LRI͵�Ê]�u�����/ulu��Y�rs~�z��'w�Y�W��M�#)���#)Ôԃ���&= I(�@Ѹ��G��L�Js���w�����E쓖U������������f#4A��:��G~ߗǷ���i��~��X��d?�Z�̸���+nF�e��i��FA�aI��V6j�A��H��#<+EJ$Ӷ #D49���cI��UV�nmM���n��$1��}=3��֤Ol�&D�#u� c�GMQ5�5i�Sj�CCt!Ij�4�[a�F6ێF#@���d����,�C�d���������]]�K�.F�B�QhJ���Ȳm:��O��[�kG���p�9#�J�;��z�2]�n�,l�|�Z�9-�e��k��D�B������6��^%�_N]���ߦ>��L��<<�/F{�Hmi[S H��)3�NY&b�P�`�,KJ�DL!j�&��dl��IT��"�9(%]&n�Nϻbh�n�4|�\�Yڢ�?"iôS�c�.�� ��z���"+��ᮝ��u�( �w+V�\۬���:<�����*}s���w����%�*���ҿ���#ާ?dz���7�<�Na<������;>�?vR?��A�x���X�r]���=K(J�_�x��U�_ �e����lt����ѓ�9|b���.��#)ᠧ��f~TАIAۃ����h��|��_���կ��Q-B<�x��.4��}��^�j��g���<�>!Ӓ��g�y� ��w�7���\�3ܮ��0����ٌz?��#<���������?-6��w��o6��n(����~���d�a|_h[���vzU�E#�A�+��.8��8a�w;cF�י���AL1b�I������x�)N�/�� 15�#4���!X6b����o{'��e*M�D�i�K1��;0 u�*�Ԛ�ʪ(׺��yk4F�d�ȣ���XF�GU����v��`�hyʫW9�c|��n/f0�P�; T+) 1��#<2A�n���8�٘R1���H#��hW2p����ipb �AZ0<��iv�.��pc�$�1��L*&)ũ�v�-!�i���!�I��-�kD��8o�) �#4��i��� ٥�cΣh���7�Yhl��m&P����u�s*/;�(��T�tY�@���GUh"�<�#��h��2���]d�c�=�B2t��@g�>Ua� ��AT�P��»YC�el���/_l��ެwC�|G'@PV��?@ѝ�Y�����W�q�� ո\�7�(�@�%�Z/�;��t��E'�]���?��X���K��I����"Z0d�O)���#4H��|p�/Wgg�=�:��#4�2߁ݭ��)�o��L�юe���5?~��s���;��c������K����!!J�yW�{)ņ[�ػm4��y�~�����#o'���*��o��������+��6�T�káت/�^�x!w��H.FW�� �#)�q郆d���� >�G���Þ�cO�>�� z�2��aE�����2�P{�$�ʪ�r���\]>�H�����1�#4N���k�zf_���?O��C��c���~��H?��m��J�;���l愶��wn��������ƒ�O�vt����\�+�~��^~>��m���hd! �������P�iTR#4D�mYwt͒X��xr<_h�2�S[��I�H^H�w�{vv#4"����uˮ���O/I��eZ{��o�_���5@?)# �TXBR�V�,m�F�HG�"�5��+`��t�m�^�FA����#"�g��/��G����_��o�c>��w��V'���"��,)�����"��e�`Ƕ�u� ��12-7Ț� �t`�)�X�#4MT(���<2�a��1㚹�>4U�2j �6����� �2�F��&a��F@*��\�^%�����#<�m�o��8���o�#<ш;���A�`��7cUPߐنh�j�F�~ S,�T���#4�UlZ!B8vx ̎T#��>5Q\ɷ����]WYEQ��p�(DάL�Rb���V�V�i�ш0�(�����b���4�PݑE�cm'[C��`�2�%�e̻a�(����4.�gXoں�m��F9�#l��l�v�FۖĪ�m�=� *��L<�+gxM �f`x��0���d B����g�*R��*#4�7m����E�u�O��\x�T~���j�G��C���)Y�6Ayj���k���N&a���=@�W3��G<�t�3�?7��7I=\�e��`'�~Q,�z�=w��=����ޯU��z����%�T,��1b�Ҫ��U0iƑ�.�ǻ~�mH�E��NQ�":~SU<�#�� Hȑ#eL!��߲ƫzA:��}�F3�r���e��CX8Xw�i4�s�K&KAC��;B+�oF�4��Z%m�h�DF<�0uQ�ѱ�ڰ�UZ*�B�-�=�w�۶�m�o�8P����9�{]q ��տ�G�y��߳����<{:�<}9d�^�~z��i��=��vс y��Q�C�X?�EG��tq&�E�J�dn-Љ��҃�1��2�X&�REAW�05 LO�F:�S 0dh}�u�}��);º�ˈQ���p�;s�⿲�^=Ϟ�{^��Ȯ��#T���8��C�g\���qE����J��DaDqaBM�Q�0F� 3~0�,B7�2�(�%�P�i�� �d1sd���MpKGj�$;#4ʺs|6�l]�7w.�I�Q�C�Vh���Zi#)Ԝna�6b�s�*:�HT+��`ff���� G���FjO�+k�}̂�8��#<�ݱX� (e�,�Y#FS!(H�c��w��-ڕ�d���f"d2�0���]��b(ǭ�o-�Q�^s��3��3��Ln�> e�(aF��OFh&Rc��@&�6轟�ýqm�Y�+2ʬL���b�sc;��h|��ΨY�Ic�,}�.ߵu��ui�F����sY�z����Ib�tFD7Z�6�Y�#ԋ�w���j����f�{p�'��R����fFܛ�+[]R�c�mV���-�LH�J������Z����C��k�[w�{�k�O\0eX&�(�ظyL��NM&m�S%&ټ� �AAO�`Pw��j���W�� �'�l��BmXZ��M�ˌ����tx@���Q�lf��mBfm`wI�v8-�-�С�9��mp�����l0��:����*�8q�J�}o�m�$o���/oJp$��O�'�3z�#4�p�3,\7���&,�ŷ�4�il�ڏ)� ���#42�m��jHp� Lͤ�x[w�/��v��ю��o�Z��4 &����6[���ӛ����j�$��5$�-�s��ӳ9��<r��<(�8\���͟���o�;�@G#-� Tm)E�[� ���ڹQ��V��kq��".��2X���h9)f�æӽ��6ڎî��+&��X��i�6#<9��wM[�t��B�'�A�Xlm�ow;����Z��:1M�W\g3ɝ�*g�qio�\ ��[�ƭθ���ͧ� Nt/96�7��3�B(K�f�Wg��ʤ�ɹ˛�d�O$�Ԛa��6f ����lLxt�[����m�����,G�d]<]�����<{g���7b�(Ӯ����xU!I=�<6=�;e�9`sg���NL����a�%��w��*j��CI_�7q98آ`�h��ݓ�6Sx�'7#<�}�9w�z D�9n1�ND\�;�O��\����D���0�Sq�C�4d�#4��eM�:�񥬬��cy��"h���[D��8�!i̪>;�˾i�@�ґ^���(�� ���}B`���0&���E���[t����v��#4�1Bi��F���p�߾��N�c�k#<��z�>��#451c���f���1��e�tuZ[N� �`5Bi�g%l��2�%$��Mz3��!���M*KN�:�����-J9!��3�LDE`�Vl�%t_u�vؠ���o�tҵ�E� �� ���-Ҥ$ފch4X�NY�WE�Dۥy ��6�{��]��89�܍r���%�n�<�Ѯu����tI�x]E2�Ql�i�`�7�1��X!����n�4ؔ���*�q�Ki�8��F#<��L�v�_Txu�Dn�Fߚ��%�o��8��g���]�&-x4IyX�V���n��o��ë���#<�P<`ϳ#)/j#<:���ٿ��F�ċ_�f�j(7�iV����(t�A#�vE�>8�֗�����x���ιc����W9@�3im/��p��`�uu���r����O8w�/��~@�S�(�O�hl��r�Wao�J��<9e =���p� r IΚ^)>�#��D���rz~�ee�`DFz��/� ~�����W#<^��7�/��| �f|��{�/σ-��.tw�o���;l��i<�3�"~�Q wuI$�/j���C{fb�w(����2����,�3�Q�K������aJ�����/��9� {���B��b�� ,��#<y)����պ�%�b��@�?#<(�<��=#)��KX�8p_=�½��9�ǝptj��g�:O�/�1I�񹞼�|:���#4�q5Q �4�?p�raK؉ zi�+xr��)? ����̛ν#<+%�wc�^�c�����l��o�G���Zq+;NL��Jn��M�`d*Jԋ�c*����%���T9˻�7#4' -�hߏ�., E(����&L�S�1��e�x灲)^��~-'4�$(AF���KtnF0Ɍ0�$W�c�����?��s� ��$\�J-��5MSD{���ۙ��}i���id'x=�ό��u�'�K��b�#)} �8��KQdBLI��N��E���yj�ݯ=d�!R�"��l�t �lp)��Z��a\���8,f����5�������TGu�s�U�t���L�2;����+oTu��i��xT����2�6^�4M�4C�z��A1ر��oM������ۭ��ɻ��v�_�G���# �D�s� �d��[yd -;.N����쏏G�;ɯ@zd����DlQݜX[vLGWaГ0᜚7ץ7���'n���Z�5Y=%����l��ԍ.�X��]g���'�v������Pә��ff��m}D�l������7�#4��{�謯Jui�<4����=�y���ћ7�_��Z�I2�N€��o旷>Lf�Ώ~B�Cn�}Ji��+���t��;kr�;���d7����f�#4���b��."��'��T�z�75��v�b8s�M?-�����l()4� �5�AՅ�9����HZa�hJ��J�x�G9�e��e���豱nY,Pʹ�#4U���"�[#4?V�}ay�3 �1�� �\���CV���`�HA�E�އ0�:XO��B�m�K=�����-���t嵐B�`��j =�/�|j�����������Bq*�D'$�(Sӭ����/��R�1�2I��v��Ouա��˻L��Q���E W�C{�oy�Sʼvf#4 =��-�/��u)@9�楏+Z��n�����`q�P J�@�+��a�p�n ��s=�^�x�.[7e E�E����~b5�g�5b���}�Iӣ��#4�PtUO6����x,�7�q_L�v�#)e*�d��g�C��\>�O�_k������w�6����g^U�>}��$q[4up��M����[���& j鑦��t��w����.�3�0�Ր�Ҭ���p��^��8�F�Q��0�f�ޭ�F�G�><GU���N^�T)(H$3�_Ҧ��5c����_�pS�s� B|��(x�[K�}g]i�`Xa#<S�Nԧ�>�P;��^�%�Nىy����P^g�5[�$߭��c���3�&|�Cg��#)~�g3S��gU�š#�����Z���J#<�e���B�~�m8�w����o|yO]f���:��"�޺�]sit{p�#��k$�w���@#)f2 �f�inMdf�[ �#ָE�5�A� �ih"�Bٛ^����ٍ*��Л <f�ʽV%'��C��?�CNs��5���m����˽C��;w�dp�Mh��'�i��FǞ�n'�}����OD;���~0o���G�Z�Nwϊ�C�^y>��?[��������8�ab�F��x�rY�1�b63&�wߙ-�>/h��%���>g^�ț���۵����[�F�wg��o6���72a��'��)K�Y>{�;t['6Ĥ�&�Jc~Gva��ˉ�YA-�� =�!�B#)oa��@�J.P����+vo~8<3b�:K}��B�w(�仍eU�"���fc���V��x'>m����p$oG��]� Z�{f�(����@�R5��v66�g_�qj�#�|K�6#4=�G��8+�Ha��&����7�`#�E���r���p?��l�Q�f���VU#�N'�f>� �|����C�Fu,>�M� ��m�N�Nԭ��c�K��i)�)��"�<Lb.*(h�vs�i�oO�$��?��9��rV��+irѿ�v��﻾��F�� ��KW~dKe���m;c}�D��0��ȗ#o,�Pa���&|3M�U��m��q�d}�&����t�a�̍�x6�����#4#<[��]�n�\#a�tr�(�y�-�� ҏ�&�:c�b�\a���/S���y�n���W20�m#<��؂"�vv٦r � 2ٜQ��#4���Vk�,|�x3S��C�C�C2������� ��{�y=����\���^�q�C0#=�<Z�J�7yӶˋK�֓�g�aζ������AI��>hA�B^���UЃǗ�E��t�#)�9 �����^�V�gO�t��V�-��c��iH�`�7M奓a�hK+�p��}���赮��y#< %�Rs`Ϸ�zo�.k �`V��߰<Z$���G���x�#<�-f�0�y�CQY�VN��r��U�D��:Qc�=[ �����G�5.��e�E��bF��v�t��mZD%,�ep�V?�by����N*���en�7���-����}�> 7��q��5W}:?E4b�^I�%v�,k��ܳ��|u�*6�[�u��C����y�B����U�$�P��~�~������|�[�.g�&<Z����c�1�*E���V�~��\�B��{�#<��������qک�v��_o`7����ͫ�b G�&�:R4�bˠ� G�'���:.YG�To�dzW_3ǭq�s��]zH����SIG�ڪ)"�<I^���}5�����ۙ�f~��d����/�U>V ��(^7��/��>F�t�}T�D��k'T_�+���.�n���"�$�WG`x|�-��͗�m���|vZ �嶃��?Ƿ0'�������˄��3o�x@wS��ɲ8�a�W�#45�#4���d&$dd���^Xۊ�G�k};�)���%y��{�WE(���5c�^�7��m#4-"y�B�..yz�~~�M�J{&��e]�ߺ.�bs�Rb)��3/#)�LXYc<��y�� R�� �V ����s(lw[hwlt�1�4�"��'����l7L��<�t��D[ٍq8\e���lX{]��뉜۹��\9�#4��B��SG��<=pv>L�:o�"��+�p�gE�ٌ\�d����c�� cVTd��#)��,��D3���x�A���_�h鱒�dxte�#)�C���ڍ��{����!�k�I�rZ]�xU��|����Xn�����:�;��Ih� w�' 挥������H�p�L���sU�~v@a#4�6�y�C�a�6x@OAH󮅶�Vfk�܋ڭ!cN��:iW���8-��.�c��$v:=��'��TM&^(��L�(ZP������CtB��{,���i2)�y!X��V�Y|m/s͙\o�7I�bt^���7KE,-~~�L�P!eME�9���t)�'Q�4�DA��W��tp�m���pX2-y E�� �K�j<M�8Q۰��Q�"�L�{;�K�`�t\��� _U#<"d̡������'����F|��x�`NG�@�Nl&�k����K1�%�̹�;y�>;蚜��K"(��G�s��da�ͤ���1�?_�5�g;@��LS���nr{�~J ��J�x��Bؚ#)���[�pb*�c�A��~���8��.m<8 �!`�0�iX�l�J���B�Z��A���J7!H�0Q‚�u���c���Y)Z��;�W\FP� �<������W~ٺ.[�4�I9���h-���9�<*�[mԱ��óN����Y5��;�]n]�+?U���;IS|�R�kZ����%�S����;����42fbY�G�7.�7�@*2�u�ĺ�#4 gL&G07|W���F:E��ƜX�)�7��2s �E�~U����W�~=�\ٞG�a�LZ���|r�7q���K������ ����g)Pw���rv6Tn��'�*��9����g\����9螢ҝ]is������3�:Z�ʢU��#�.ҷ��#4�N�=JГ'j�� (�Q�gW#��\��yԚ:�q�����^�L�.w�?EF',J�v]�>��X9~`<=B���ϗi�xN�����pZu�:�?E��9�/@�~_e�,D$-*����o�X�`sk�fk�4G�g1#<��5��-c��T����弜����&�� ��/o��������^]�&�^�8¿��&�7,��$��c(��\V�[N�Z`����fH"ą�nd�C�����_(�����ڒl$��Q���F��U7)�^]�>k�m�P/Xs�s?��'�!����俶� ;�Ū>�S��{{��G�娧��0},���R�U��� >�dGG���z����劗׎%8S�Lm���h���8෻Y�n�������Ń�4Wlj�-,%��f<:L� �����5|�"_�Ë.���=���ˢ��J��M�m��t�d���'5V7v7]T�i:���.gA���⫵��=�{O%� ���:D&�/�ݵ�m�����G���UDŽj��ť�&����e��)��(�/7������?M����Y0n�LA�1��wc�K0�V*��/��*���ΌT���5�˕�(H\�7�Q�1Q�s�y�r���� ��)`%�3L�o�;37T#4�l��8�P�z?��f�3�u-IDl�7Ѡr�E��K�_�S>�j����uy]��Dž����S9ҮK��C'���jN#8�9���Hb284����?�D6��az�i��௞�hDXL�'V��Xێ#<�=��Žk�z��@�`��G:^#����k������n�W����eCčg�R�Q�yv2u�x�ډm-P^S��L�;��᠀�F�@�d �u�����CqT���\��r��AJ��EZj�9 E�Z�H� k̶͸�`�k��������q}����Ѣcp��K��𐾪�ྀX�ISD]���� O�����]�@����.U�Q��ǀ��a��Z��?�ߟ_���;uʢs��� .�����_����U�/�}N�u��ɹe��^�!zU����c�s��8o8�h�� z��8ן���' &�������|��Sep���Šr3�y�ni8�bĦ����D�; �i��1�b0��}֋D$�P)�4���)��#<_���tAd^�`�gl�������I֗�z�rQ����� s���1�Ai��!�:;_|4<c�K�T,;�7��x�~n�'eЋ��v�%%|#qt!k��SMU"ϑXVLr؍�{��7��2�����#)w=�O�a�ٺ�S��}} �GyF���z�G�У�$r��,��͛�J|9�~�DJ�t].x+��X��`ԡf6[���|2��D�`�֪A��3��ϑ���\g*y���}f�s��i�>�������Þ��-��q#<O�)�t�c *����h���k�fSDԫ�l�=���#��I2��,Ǡz�fa��ye��#4'��D��V0rX|t77t�^�%� �P.eA��>U�-!#)\Zzѽ�׃Yf�^��׵��+��?)�T��C�;�J��P��[�'_���V:9u��GC�h{Z]�V]��ӠT{��BB`SB��p�]�.{���VA�`�ӂm��>�6�q��WAנ;t0����;�/7���Sˮ�i�� ��8��ʪf����ċ��0��M��#(z��_4iC�V��]�>�y�!#J�c��*Y^����a��.}#Q6����kO�cg�LKJg7��߾�#<�?c��m]H�!&�5{�5�1��/�y���rn�����Gk�;#u�x �ׄ3U��)�(�H!j�I����4���U�6���4� ��P3c���@���6���}�_m���*�<}3�V?�� [1"�N/����+&�!�iƜp�Z�ε���8:��X���V�8Ͼ�>O� ��L��CدGm��C��C�o�UW�%0�`q/�ϖN� ��1z�Ю�&�#4 �ԖiK�)��AV���g���W>�=W�c�����6(�ë��ӹ�j'����ys���\|���:�N��������5��F��uu������-�����9w�Z��yh�+�Ā�I��D8���%WO�H�:�E�J�d� �|0a�|�v�#�%o�l��K�9�<S5Nor���E�L4��P�*�)P�o9-��L"%$�#<�s��+���>��>o`w�μ�G�A������������rd��JJ���k����'M�iqφ�q>�⡍�ۄb�ި�#j�d:V�+db5R�@�UA.���H���I��~JO��Fus�9�J�I���B�^.a@:����x��o�R��̇��bo���7�s�}Y�⣳�mlE��M� N�& *�#4|uCT�t�!,~Y�a�=�ir÷8x 7��oH�!�1LJ`cZ�*��O9F㷖�"M.�����L[Z����5� ��q�x�\��t:D#~��GK�\ R��^JlJN�#<&G���H;��C6N�����^~����nX~�Š{�/�B�^՝9�L~���i��j�e:\�νX�m��5�챋�� �ܕq3,�3��������c�@1��#)�~9� �C�뿎���>Z�� QP���u����Mg��1'����> I��t#)E�+�-Fz�n��ӹ��_M�(�g}�.��v�m���M=��`��فp̍d){"���i_��|��p���C����v�d��0�OS��)w�T�*vPM&����ݿ �6�O�5��7�~1����gMp#<��)�{�|ׄ�u�"՞?�������Ynz!ϋ1w(m*~-b�䀌eW�S��#<� wJ�>�p"j�����\�Ka4�z!�,�V��ӑwr�m1�y�4��f?(�l���(�2����=�#÷?c�i�+kiʸ�Y[��EwJK��៥ِ !�� �'E�.�͡,��1JͩW�';j��뺄 &C��D�R};�;Vᙰ��(Y�=,8��l������ <ȹ|�{��;��'��O�KJ���(#)`S֩�z�����O���� hF�9��g0�� U��8;q�+ª&n#<R^�3�<5�_z{�S�}��J������HX-Qm�7��%R|���Y4 ZQU$�5��#4{n��ĤZ�H�� Pp3J�8N�v��bsr%�������ՠp����9D�������>�3��dϾ��M�ę��oI$�"�d�#$C$=�2��w���x��)%(�**�����p)#42T�M*� �`�?T�Y�1�v6����0��؁R�Rs2�'z��%?FY���E,@���>���GH�/�׼?B��si 1b #<�J���ȟ*��Y��P�B�`1���SC�'�������60EPnr�ֈ�K��#)1�벨��m�ppx�� nv����S[����p���/��M�9}A���D#��/�X���`�t;`v|��;8hiM�)PH�}�07�#9yF��[���?7�����z�nBc�ci���06$���kHsz-s��2�ϼK}�s��eR�l�B�sK�{=�b��2&-=e��!��L0�B'��jhG� ma�:H9l��Y�=�5GR'����cd�)�N�B7%�Z�^�U�-�4i�_ҿv�o�J�� �V��İ����������ݖq�ʿp���}�~��Μ�Y���֖��<)F��7�@~��:����µ٤#)�0�G��- y!#<���O�3R��i��#4ҋ�����ҧW��`~o؇ބ�J`s�!�PTP7|��mq#<��΀�j���xAU�1�DQFK"�H���*c;�� !Z;�9D)���#4�h5'�f=�%g��`ty�IhCw\�Z�It� �x�#45ܹ)�G�U�%��\��c#)���i�[���O����M��x��nso�!0�f�1��/X~N<��є$>=�h$cw��7�P��� �����G���0��;!6^9ӯ�:*��I�s����m��?���Z�=ROg�0��Gr8�?�n�)Vkw�&&�E�� C��.iT1�B)�[%�<����v#4Щ}��{~]���+����3v��T��ˇ)U��[�]CO]g�u9�5�ThT��|"ߜ�E���f��O �u2�/�H8)}�<}g�;iɗ���wؘ��Ws#<�=/�vB�[DF�{���J9u�N�������4�����sD���ȭ�&�#<��~����#4F��S���LD��[k/wBñ��2�GW�Ϸ��X��\�*�Ul�� ��e#4f؃�{gKcm����^�wul���Zquu�H�@hgo�@���C@�'P��� ��m�Iא��^c����*��B���:V����tuCZ�ٲ�͚3��@녎!Q��M�X�TVPxS���q� � �>N׸�p#)_�\z/w��<g���u�K�����.#<oz���  ]�/N:|�#<T����$�$�nk�JH��%Ac×Knۭ9FK��-~v6'�Y�?��v�؄Zg<>�.谧����8�`ش��MzԸ� �|����6�6��g��3�d�/��$I���TNj����|YHa�J�9۫���u��N�R����X[�t�;R1 ����e#<�hB=]�~�V;3q�!��bX8�|q�����?�y3zl������N�cx��N�ǣ��rq�&2��!�C?�3�M�/�����#�Zn������^�<]��-@&�9�PxK�Ž�'~�������ϼ1}��t��ͪ� �m�g]%�$%r��\4I� ��o����Fx��4 ���P�ߴ;�SL9���z.�/ӥ� ���̝�`ۢ�ԧ���y7I�U(¹.P�1�� hJ��9H�坟�&x��:fo7"��h��FRH���Zb����$�q��n�����ݹ-�'�1�-v7C��}�1�\$HCL� �ۇhl��*��!�=�QA�����l���P�E� m(���CX�g���nh��#4�0|��X0�at�ͳ�a���l�悖���#)���3-�`KHt��B�-G��s"���v�� �Q$�<J�Վ� x#_]�^�0¡D��;�lD�8[…����;�`����p;a�J���7�>����뚓7\<fe�ݓi�8�o�:�)#<�ЩF�*�ѸF�Y羻H,�x��4�#�?f��{(!�z��n�N�qC��0! 4��(X �kcu��[� ����@J$�J�4��N{��N���_~+HGo��u��\�7�h�#4�R!#4��#)�2׺/㡚��paM3�"��[l��Hc5�9ۖd���}�?��:!}��I-u�<G�^���p2#H�#<�K���±����/��O;S&��)-!/�+�� �<���#�����;�0��0�~����>i�X���׎��HN���w7E�)� ��Mfze�j~ijUod@�R�@-��#!C;{y`�Ȏ.)jv]A�mua,��ڹ�E�UL�,֢(�rO'��O������s=����_P����lb)��޾�&G�+�+�#4�w�״�ҲH�4�)H'!��2����� 3�=�=5�#\T<��`R�/#�U�^�M�B���Me�$+�<��B�*z.���=�=Ps����M�����T)�%�v�f��>� 1$��3s/�T[�0B���Fd�Ðm�� %r�Kh�7l�#<k,ؤ��Ln^OI�{�p��V{�FpR� �Fj�X/#<�f%b�(�a1I����!�kib�Q�#<]�޹_���;X�}g��$'�B�pe �]{�qUTA�y���4��A��"���G\I# r.�ډ���'Pt5*p��t���R^��H>�u�qgǏ��T�\��#y�ؘ�O�}�UWM���٩�2t�-���#4�2��SrS�� �:� ��}��K�@��~F��X�0ڥI�s+�҃����Ѳ�Bn���������2N�@���uH�b�Y ��4��:PJ�1)W�̞�Gn)�� �}P �i̓�0�k\���W�2#)����LyON�&6���`=��ֽz�� �����Y��<N֧;؆��1��ܶge���è�ّC�6{#<J�M�X��>5n�N��Mx���0՝y�6�&����_aYG�lp8,�=�~:H�O,Gfθ�#+�P��@d�Q5+$#4��|3R6_��ձ)�yO�!\���������ci!Tͽ�^�XyW��ք��!UAU#^�Q>��V�ն��B:�J�D����\��Ƨ#)���##<b�U��ۺ��D.�,��2JU`����}ʩ�{R�#4�ќ�������C�y#<��D�A�=r����V9Be�AUO�j ��}$�G���{�z���� !�OZ��>��QUHI��tBЃ�I3�P�4#)q�~ ����'���3�8�qd�ZS}���aB ��!@{��m��N�.�;7r���K\�@��ݏ�o.!p��j�GV�N�=S$��R����� d�#<0Ay��(�Cǭt|��a��������\u9�>u�"'^|?&�{�a�T:ǚrp�:x������d�t�!��T��x�3ʽ�rB� ���hû7C���⨦ٞ�����J�J���.R�Ǭ��S��|#�g�~�������CA9`X�;k��=}�l���퍱�立���^6΅g���a���L��W~�;Pfg� �Vנ��r� B���۪�?Dh����Q����� 15EźU,6�TM���pb+��p����#4Xh��0U�C�.V���|��ĦW,��y�9|�}Y����O�Q(��zY#)8���P7�~��?���l��.��A`B�dx#)|���;B˖Ez�M��pU�(3@��B�=,AMP�#�}9%#<�)\@�@� )M�9�������6环D�B�#4YBd�!$��K�GR��(XX��R�� �6�3[�/��0#)�Ѳh�g�]��c&�����,8$K(�`��مPd�&��DAPC8����9F�=$�|FS�����=E,Ë"#4$94���)��^�P9�F_�ү�����$�V��1A��B�d�٧�r�I,8��Ab"� qf'� '��h����[��~�Uf*��e!߃0ݦ�kZ��,1����-�u�ז��I������{A���9�s�����W �#)�``ۚ�{Yҹ��Q ���u�r�� ����Dj �B�۵�uw.��[�Ҍ^��p�#4������MY!nST��t�p�'��\⧷�o+��ʿ�' �x��C�,{� �d��g�Z��h��6�i�뽊)���q8q<��Q�ԕ�.���82w�Fj�VXz���ҩ�i�`E�7D���u~���`�l<Z vTT���jŎ�\�v�������#)�k��P[=�J= Zh}�5?�xC�~�{2^��U�����d�&���Mf���b�?LC׏.#4���`z��I,ЪTdX ����)�T�ZfJb��{�Θ8*���(-1*��ۯ�Y�L�j ��q��si6��c��6��$]�D5f���6v�ẆD��\uEPbCT��2���+ĺv�= @ZD�'��m�yk�H����N�, �"��f�#l˥t��A�.����jD�&�l.��I$3�3U�+?f�LΉ����;S�Ε�����L<��~ ��L]8���PvV�h�u*Q��9�c���to���熨|�g.����2�5�y`��[��8@˃��qH���$<���e �GUKPkDy�d.r&?~��K��ׇ~��J�'�Zv�m J�sO�񢫙Z������C#)��H�AS6���,�6�1Z���We�ּ�UʲmW0ibF ��!>�d�Dƛ@6��r��|���m͵���)4`����YAs\H�3\1.j�]��#)yq����a�t�v#)�[Y�,dse�9䇾��EVT��gœ�q�QÛ��x�l�-C_!>;�[�� Ҿw(C�����Y0��V|�hg��y\N�Ni`����"!"�� �lK"�>�|��8;: S��`ɭ�+��1d�=��.y�dR�H2�8�˳pz��\��A���#)��H������&��`�'�_��I� �Gfl|�{9�J��r�h�Fh�T@#<��T�M���[�}�Z5E���^E$�@�!T�K�mA5~�}�N9�� �;z���Z�X�kóadڧ�D g]�L�<QM(�8��p�e������ ���*��FP�4&�b��`CD�1�����Eg�ռ�}i��"�`@� "��`K#4LY�D��:��(Yt��6�`@ ��V�9m�C���}��7\��xF1$��k��C}��z$W#��.��T��+�4���������‘`��J�}t4m�)��R��R����##4��gpD��Y���0Ұ,�шl`�ޏ��*5 OIՆ��S#)�c\�f�?��=���<���yg��"*j���H|)dX�$(*��(�1�I�.k�pϞ�y��.�WwJl� �1��ڍK�dts$��v���l��(ޟ*�3 ̺8�l)��62fQ1����rs�֗�����)��ʧ*J�{�q `Ύ�o�����iD�tҷ��2);�� &�%|�l1����6<��;��_$M��x�m��ǛL��5�woլ7#4�gn����5Z�{��$�N�?#<{�Bl������a�aǜ���ۦ�Τ߬�,7��Ζt�H�Lf�����<��$`����1�Qu���.��}G�����cO��ݿ:v�,�'����C[�Q��WT�J�d�-�Ξ0�>��pzt��6�/��(j�H���O`pö�Y�o��i�]�M�xC�, t���|�����^�Y:�$nG$q�6= ^�Wݕ�*54���Ⓖ�O�"��R��b-��c�B^�Q��<k�p�Y��E&��㉞~e����2�O���Ͳ�uNȰ<ޚ by#ዣ8��]�N��mݦ�L�U�����.�e46ZR,�d�8[��l��&���Xoz�p�e�p������D"C�s���J�w��Qz��!jR�����e9��=��]��N�Eo'�v�.��OQ�4Q�J#< Q`��Q�KC�%*�wΙ+�����v�s�,�࿕_K��+�9*�ǁ|�f<�t���\�k��b͋���o}卙�e_��v>ײ:�1O3�ŜΆ�8!��PdXfo�#)=O��ĕN�,��o�����\�a��������~#4�B9����wQ��J�z�0Ύ�sd���� �˧�o'��W��*�xW�\8�z$<��ٍO#)(#4���h4�g�S��#�R�V�;-����d=�rPQAe�:��n� ����7�i̜����w����Y��;s٬6J�"wQ�(,U��5!�|5��1߉ �����9��{�^t;/�K�i����6YE�ш�ng֢y:��O��D���|��Gw�&˒%��8~��,��D ���`�x�Ru�g����UV4�3H�g�ڱ���$�ˉ��2t���癰��Coo!�3��5��f-�PbPErl!���Z�4���ns�vA=^c M���k�u��H�36���@y?C�q����5i#)�w��,-�����w�`��3���{`�к;�M� A�C2.�\�t0�$��#<�6�EI��kM���X�-v�Iub�B�FgC[�4���18ϑ]EHu���\�"�=������ٌ�O��?u��f���4�k�)�~��m@���������b��OI� �䇅�'J��5e���}%���X��K��)��>� ��+�ˣ��������>�%J�Bn�<D���5��c&q:�����8l6��x�B����9sO��Ǵ�dQ�CӣZ���L<'ɞ�5u`��%1h�ƒ�ӏw-��p�;�&��!��8\)�)��?#4�'û�l�{|���;~�{�� ���V����<Q#4�֨V�ya{*�rYx��r1��y�I����T�('_��5���{�&ؘ: ��[�ؠ���<���.p�����Q n{�����■෉����ł��{�P�z��Ws� �#4}CG�R��lsq%��"�%�sO�n0�V2�t7�K�k��#)���G�2Z�1��<��֓@D��FJ5('?o���wB��\��/��ap|�8�#)��D8&�#<AQ$�C$��2� �y~���k�+01MD[�|�b��88�uK�;����8vT0V�򫔔��i�Tz�DI�x��E� (��`Bz���نޞm0¦�x�gd@�� �����?��ߚ����?���_6 ���c �����X��f��L��*+?�/�'MS#4#<���#4Z��R{Ga٠jFA� ���*as��S8_�����L�-��ٶ'���J�Z�������b����ۂ��LJ��#<,��MUCT�]�}�8?��w'�5(��a�Z���q�ޒ4��L4����o޾~=q}���U~i���Nl) ?�m�����o���)�dsp:��l�qO�3������P#<�RJ�>�9��g�Z'��f �I|�=ta�*�L���:،\�󽷈���iH�TLN�� �A j�'E#)Jg�>���Oj�%P`T;�\z/�N�zjy�G��b��5���3!~U�ޜ��Pӆ텾� ��^E��,rP��\�KE�7}' ��S�u6��V ����a��כ%O���<ۓJg���)�L�]�z&�3�e�v/�!p�#<�b��<&,�}�_eԺ#��J[�~��0N��@�R�qe@PDc#)�JD����Zߦr��]Eb\����ξ.�n�.f��REx�6ȝ���q�W�c��O���^ ?�oA�>l��X�D�'�F��:6EUv����%%����2��Y0)�����[���@�w����K�h��"h��5�|���j ��;T��/3�WP��g���7o��t2�zs��$TZ�"p���ʍ w�⊹r$]�_�N��d��>���{�LV�u_s�=��)_���-����� oc��!;<��))#<�[��-���"a�O��1B��H!�"��#)WՇ������1��z���{�S��j��u��1��;7�11<�>U>�.�G�ߺ���px�F��k:�'�#)���dDw;C���p�+��$t��Ǖ���C�sE{91�����O ���O ahrz�T�� P[(���PO�x���_���u}�+���6�1���n�_����9+�/�u�Qa:���g��C;�[�� ��x�o@�z<�S4P��:>]�Ө�ّ#)A���#4�L[H���+j9J�@5=ڕPu� `�e!���ۻd��2�<��:��M㣨pt�!�C�V��ltI��`#4�������h���#)���?S<�/jf��NPl���xs?x#4�yzt�RnO�ԁݐk����q�J��TTK��N��~k��AT8�I�dT)�!<XD/k3�����HV���U�,���O�����Pu:�����Bx�}�$�CT� aB��vXXy���+�� 3ۏ����v_2�$���A̙�;�&��tz�SMuy�I>���~?�ř�L�C�:�{�t�۵[vVVS��N��h9���V7��kA���=c���r{)4�-�5��r����㿗 �9�ݝ��öt� ݔ�8dV7Rɸ�.��!@�@,��[�յC�ZO���~vџ�sf����L��-�.��q���IF>#<� B����#�Elf�&���מ����w�#)�#<�`G�~���?~�Z^�6�" ���,��)�#<���n�<�ݥΎ�e���R6`)e؎O�:2&:�P���~Ů���r΀�%����)Z�]��+}���Gy�%��5ڏ����j��� ���p��L]���au%<�Wz�����/gH[�������u�?���=FeE+1�y��A�� U��g���v��������ݕ��??t�{�����r>��qvs����{l�gA�7���<[{��1�'+qfx�;�j���K�#�2�e�(���.q'EPE�A_�paNw���uC�r�ˢ�G�:�ל]R~��Eo{{Vr�S�x�ʾ���� z3:6��YI*H�+į~֧��5�I��N#<��P�����6��W��8�Ǫxz�<=ˤ�A�)7���@N�,�Z�S��,�<X�E[��Ƀp��鷐:�1�Y�ʪ���'���,��vR�{�V0�rMs"T��WB�#)�(ū�+�U1($߂�Z<���u�c�2�BaB� ��9�Յ��<�{b~NO^Y`�ܩ��W��)���劘���p�a+�}��:�?�k� ��@��q�Fn�n��?�U���j)fe� �kHcq��D�,#<�����g-?���a���)i��~X���$r�g��Qőۗ"�C��qztvD:�����]�i XFmd��\1��׉�#4�5��6�L$�A�!�뛥���*XZ��1ܱeG�3p���9��ߍ�CW���.(1�S�������M��w}��1���� s��x�������#<�qE���b��5�zλ\ꇨ1����#4X ���W��o,�hſ��g������a��"X��rq��p`������m��F�0bII-.�7�z�B��~x�m��:�v������n�v�Sa΄�-#)�(ȅ���Ӿ��K����Zܺ��TE��3H�r�G�G#�h�9~RRH�d#<Z�A#4�D{!#<��9�w��U�'r��}�#4�ư�)�O�t�w�y�1����v���-����h���4�)���'�T3Ape-�D��ak*�RR�ZD��g��;C�[K�vu����s�3��!���,�,t�� ӻe����߫�D\͈zw��;{S�}�z��RSuH��~�q�|�o�g��:Ӭ;��r�������ؤc/�w�G���K�;�tS��J�#4>&��;:*����GݧO�����Wt��6Do �iǤ޺�t��,:���S��s���y�^=�ڻT�� C�����FO��j��9����H��#<=��\�,c��,z�����{��rj\n�.XB��:�t��Y(��ol�,.������ҭ���55�S&�#<4��d�����R�+�#4�_j���1 #<�E�����K#e,,��s��y7k)�V/�{S8 7��.G ������cĬ��C�m��1���X�4��,��'l7.I�s�G���r�����t5�F<J�����Eq�p�V x�8:1M��g��P�ȯ���1�}�V)�E���M, �#4��#<���|߅h�ܤW�9;K���mv��.�i��>�oT���6|�O�@��*�"��u�n�8�~�/�����:�Rh��L#4�/_Y#<��(bs���3��͘<���ۦ�΍s�k���Ί����fQ|V���#<���(�{j� �M�Eչ�j�' � �w� O�=���x��>��'�v&�����.ިi������"R��}�O�"�����7�Jq�^��6��ّ�(�����0����U�}��##<�Ԅ#����sF���pu���1���š�H�a����TdM��D4w�ښ: ���)�a�+����V&�9�S�x�-l��~���#4;;�!��:i�e.�ty��W�c�m���ci.G���ɺS#<9�h�#<�Q.P���J��(��J��H0�CLG�Q��vĻ57!���g��]��py��M��x���ϕ�Z�w���Be.(xr]�z}8��vɮ<"���$A�����zVm6�X'�c�t7��r��L��ޠ��?�H_^�D�I�� �.���q�4�9MR�8�cK�̉~���稍Hm�غ~ݡe�p��y�����Ը��[�����<2��Wr�,.t�d�GM���������6ʽUuA���a-��:�U�7�����Ze�%1O�'�}�(~�Hvo��go�g�CW�����Ɖf����a�l<������x����8��$ q#<D��`ԃAV� w��C9�v��T��{̋ ��4��G �H���+�_P�P�d m)�ĐDlm#4�L��FS#).cMRK+hF$�m(L���e�2�UCȀ�Q"�j[OXd}x��Pz�������e`�8�6� �&����&&����3�n�؇�;�8p˞X�w�=��}�?.�{Q<�z?�;��/�i|���t:��F<�|����Y��Q�ge\�R_�� ��Om�墄�E�M����b�Ĉ�#)^�g�?_�0LM;�#���#4DŪ0$<#�.�,��~%"�yI���D,}�<�Uq��6Ԣk��?p�S�b��@����_����l��S���"mN>����x���{}�ƎI��y��J%�?��%���l�o��Y@�Y��r������.n5�����5�g��>��4�?�tu>�#)q�<�a��S�t�.�1��=6|�������`�7�Ho�FrǨo�W>߳�|y�Н�$=!�����qӤyb����Ϝ��%?z��74���#<� 5�EF(;=�9�GXh�Az�Q2��9#H:˭@�%r�+��zِ�$��!V��A{~?���n?���i��Ј��b�rx#4��P�0G����h �(_�ͣ�9����bu$���r� ;���LV���>b�X#<S�� �₳���o٪� Џ�=/�]����:[�\$5hynX�r��m#)�j͵7&�����@1&; �p@$A�c�e/��z �C� }��-l�dX�Ľnf�:`#4�8�sz�W�t��0��p�{��7F��Оnu�N��)� ��)��Zl�7���P�O9#<H�;E`� <��[��A�F�ֆ���������A��4G#)��k.[��������#)VE lp��=i�zX�z��$�ϲU!� ңT#<����,�0�Wؤ�����y]��&*��M'D� �� &=|�Y��8Ԓ�����m&��4g��#4���#ߨP�����ϣ����[�A�d��w4 ����w��NXi� �#���Z09���#<ds߉���G����|�QE�.�t�%]�}�&V�͞h ��m3�x8�ˆߔ�R�þ<��A.[ �b���8ruw��L�X�qŧ�n[��m�W\�<��نz�p�㲸J���ݎX���._2�5����S�������~p�H�]��� ���F�Í��[�� �`���Nh?n6�h0%����GK򍹕�%gv"�L�@P�-��9.*#4���p[��>�z|��f�o��.�o�#� ��\����u��&=�m�Go �-!ؠ{$Pm���"�P]��C��[6�H������A�#<M�f�3A��m4Y��6��e��O+$t2�E�*��`�gt��wĄ��>.5vv=�R��{m3ԁ��| �������\���#<�r�WT���6���1j�ư��_��݉�4hx���Ŵo����[;�EG�[{���w����O�E����l�sy\�D�gA@LMQ^�Y� �G�=0��q���#<#<���0���kb����q����o�]Dz�=տ�^��?�Q�����{�>����u�����@zc( B�mp�'{>�yL�(�A�����-�� �o�;6��ƕ5�ou�եc*�(����ԛG�9�i��ز�Ӧp���j��KQ�����+ނ#��}f�;c�2�%l������W��y7�)�E�� ��f�\�,��29M�R��R�8抪����ZZ-ϻ�_Km߄ꮾ��^ͻ>�Hh�n<�i�-Do���.qF}j��T�b#<q�j˕:�>,��Ͽ]_�.�u��/�D���G@�3�.� �Ó>���555��mgi{9ne�8��L#4��.jhمdQlŒ�]���^�;Wjψ��'^[)մf���E��Z�:q��o��=�� L�A�g��yr��� W��C࡞t�~�&瀶+<��z�9Fw-��UTUk��ZK � ���h�is�י*�]z|��!��Wg�aQ���2���r�<3{J��i�}k�:n]e��t��Γ���$\�$v�=� i= ʡ�pQ$�:0�h��2�)�@�7\��M8��n�G���TkK���+�^��mڽ�B�o��tyY�v�=�����iw��d HP�����Փmu?�1sK���љ�X��;�#�3lcX8 1���o[�ڬ������iT^>�Ń�zX�,�l!�ר�>Qg���?y�o�uZ �3���E��Krקό/����Qww���q3pp�=b���Sfy}u��#4�p���7zQ��g+����?᫔!�G;L �!�Nc�"<_=�H�{��'��!1p��"�v�3C�e˻9Bn�4�G21�v��*�p����1tΘ�!�j�#4�񺚆����L�׈n��\��M�j#<f@�U�-S�ٹ�O�-��p�#)�b���W@�!hi�����^��A��;����?N��n�1P-E�\��40�9��L�(g�x�4���rpE�N�.��f��f����?Nv���n� N<��S2y�J��ޞ_>��u��K`1�A��0D����R��t{<<j<DV�``#<Q�*A��s�C��z����л�pwT¢�U��u���bB8���D�u�7oǔS �s�Q9UF��73�*�5�.r�6��J�)|/#�Cc�܎�mV��^E�*� P�9��|�����t��%_���'A(#)綧�SSτ�k��x��7Hf�!�#<Q��'��k�<��bh#<��04��@������y�{#)�{�����:XsOF����/$���HG���v�x�"��n��u߹Ä(*f.���"H9 r~�����b�gA��y�g�����Up/�i�j�/�����=�? ���PIGm���)|'*w�v��3wX{�0�:� �� ��Q�{�#<� �lP%�Z#����R�66^R3�-.�j�e�l�~� ���W�{Ib0�'Y��J�_L�R��@^ٜ�c�A���� ȶlHb�/���Qݪ�{�7ͱW9�;SV��@�#���sˈ��5�C��A�Cn�����%4���v�r�y�_ ��.�7�5��NJ�>L�\�;3�i�3x@m�#4���R�o�M���Y��],Z`�C���=�J �����~��f;�>v�&��9�:���ӿ܍��lZ��~��ڥ�I�=��.�;n]\TX��>�A�r�+#��##)ȯ/*����5�(���dK}VZXʵ��P���D잂9#<ss�r �+�%<���<7�z�Z*oa�QB�¡�+i��:��T9^�@��Ҍ� \D�E1�H�_��5uF"u�z� nQ���Lr:; ��_���zE�ε���IN����2˪#4��c0S�k N7ZӺW!�z�g��G���i��-U�8��A�~An��Oo`��\�K��p! Q��Z�4��T���%��K� ������-ضs������hCE�j"�z��U7ߠW�h:�o#4x�s ��[�Փ��T` �(�|Sڈh�Nf�Cc��1��ց�=ݎ�d#<�N�j~�B��!�ˡ�=��k#4�-� 1F�"::�b6ůu����(���^�(�m�D|O�(����8��E���Z uh5����׽��������VD��g�����=�7�$I�#)�e�����a��FN��^#4>Δ"o�?�K��d�#4��A�{���9�X��p>���_�[�#4��@�ѿ,m�\�m X@)�Ck@-��#<S�l�Q��9�1�֕�T�.�Ӭ�L��[�}#4?}3'�#pc~�L}�����`!t��AG�yvS��_!�Ϟ��H��޸b� M� f�i��C��Ҕdp0.���r�u�8%�'�9�l�7"�KYr��ɫbgz-�ۯ*,� �k�T_�\�~�a�׭��� .G@h�D�YAt��J�P,Z[������ʋ�����x'8B�[W�p�aރG$8�� q�:�/�w<��"&�z��p�Q1 ��!@n�~h�����e��`PfGH7t�S�/�c�4Y���_�ڬmP�Ti)�#+��Z-t$��#<�5�Ӡ�x�ߧg6�|�ݤ>S�U#ÿ��DX���?^�o����{��"�7��o���wS�!���J��?����_*�$���{� �RD**�|�>ߧ��MZ���(�t��u�{I���M#<�B*�`V^�������KE����q#)��>Jڞ.s�ߴrs0���>�T^��#4՝�N'�e:���/W}��yp���Y�α�u�Xb2�(� _�<�����!hoR��|�[�ؿ�:"�p�ˍu�1Q&�f����r�Z����/�NY[�������tf�S��B �`��B���CԾ��w?��o����MD��C�B�� Y�#)._��2�e_��R�RKUP��Œ1)K�v�Y��h���4l�G`�E�p#4*7�KB 3 G[B���?����=ooe��B���Ét����h�?�V�l?8d�&`w���t֋ ���7��� vI�����Ysf����UC�T!.e�נȝWh�C�h��D+�/��#<��f�#<݄�#<"���� �q �@oX���zǴ�� ������L=��{�ṙ�}�i���k�%��j;���������<mAw�N�5� -�Ņ�\���Jhd��th?��` >�B����v?>��)�#)�zא��6 �5�D��~���yw��SzSx��@�^#?»*�T��a��2@�����jm��悌@&T+�{M�2���R������d"9����#)���W���w�`���>r��ۈ9{5-d#4��`�@J(�ͧ�p#4��{��GU��Y�>!�i}!��iϘH�ʲY*�H,]�!p�����[S/�-A���a��]���_�%��� ��\i�~(p��<�� �yM���ݑ�"X%.3�x`�]#w>bЯ�n%@��`��M�4P�$C�=ɃN��.�'Lo�T�O�ܡ �iN�2QǴ���DDC�55[l���'����k��g�{#<�a� qӹ�P&Š2�;�SQ�u�F�����GU���y�_7��h���d%����P��.�:��^t#4�{�l�ZR��@.�=[�d�A�����㨳jj� �^��CDj���-��Z|�?JO����z�@�"�S#<�zD ]�}�`^���?��G�Vf�xjߔ3�1J�<7�_��śm�m���o�#)���y�M�-��?m������X` !�R�� �!��"����v�]d� �*=J2:,sJ�೿|a�ς��s]�c�LD#)�J}���؝G}%+ _�ƀ�ʯm��=�SP���ѳ]:��{O�xA�,�#4��� &�}]P�!����]q�!��A��c����t���M���Z�s;˿n��l�x���DDb;M;��m(��6�J6�Ķ`R���KH2Op�{|M��G!�~��UR��<�q����qPIT�#4DK���Cc{�u�:��&`t)���g�js���s9wg~����Vj#7f�T�{�S�gkS����"طF8�+`n*�h��g5J����]�³_�2�3��}�t��T`S�8t-o�P�b|���pj3ݸ�9n#4o@�pu�a��^!�#(p7�Ǩ�#4Px����fijl\�,ܢ��F�� h{0�'��Ȁ7���Ȃ`./(�@�?���h�}�#��H��1m�ap� "2�ʔ'�OA�<�?s9��|��N#�4����}R��g�<�'���;�� �� lZ( @(`��~7���1-*<#<`P�W�mdX,�6;4�RĂ�f�I�!��g|�-0/����i�X1#)�|�sbQI��?�/Sj�*R��0D�(��8���| M�a����㋎#<�|h�@1#4moո^��qH�����/ �3"��1@��{�$[=_o������}�ZH4P�"����nM?@=CS������ ��n�a�P ���ӾXb�� �x�i1P #4FD ���OY�q�N3��YP~�J��4/.��>�z�jSܖ�>#G���� #�%܀z��r�H符�J`ǘ##Z��f�gwBN����e��͋с��aP�D)k�����#4]F��ڋ�.Y��s�\�@#���B��\&�\�:_ �� :*#�@�Y$��а`6�j!������������ ~H2��32����9���%��e��¯��8�O�:�� ��v�%J lc`�?^#Գw�?��F�KK�F�2��WYY�Y��2'�6C� k9 Z]`�:�2�JD1Y7k�$�I3Uk���ֵ%����f�j�^��Y��u�.���aG�o{�kzDhhk�%�7���X��Ũd�KT�Ec?�2W>�g�!Q����@*<�/���R��#�/��:�� MX��~C�����L��b�>n��ɨ���:BA*�;"{6��C��ō�WOd?�Z( ����O�W؇*xp �w���C�}�-���ž���UW$�#):��,�ϱ�>�5Ĺ�g�߳���ͮ`O�[�)_KB�A��d㻂���RWz�5��_�$�m> ��I$��>PlY;>�q�g&���izZ�����U�#AIDF�#<1#4#��(��p4�Zm�CL����Ck/�����u���UJ~�1���/��X�p1�ة�Ɔ���)��fś�Hu>ts.���%�������D�c#i��_��ӌ�iJ� ��b ��Z����M.���������A��+�Mb�H�"�R�_�B�;b��_R��9>��-���Va�#<tYJG��x�H]~����6���\��BW#<P� -�8��9:�v����Z��HBC^܍� ��je��K�� �B�f�eB�@00��|��t���X?NEg�+�ni]���j������%� ������'k��=b�Bآ�d��H�� �ZH~�)h�'x7�1v��,��1<w�����i(^A�59�[m���@�P.��D0�H ���^�0�k���n�P�#)!_����roq�;�J����p�}6�#)HAHjO{�T� ﯮ�I'ׅ�ש%�/`(��U�{���m6�y�[��I����ܘ=0}?{�=��-�{��t�ԇ&ϰl e�O�a�9�qdP�0�#)�I��˱���7��-��XƜ@���/��=A�ak9�ؑ u���;6x��7�sO��v�R�$n��6>]��0�%�+R���˥wx��0pv�b#4�A�j�%h�8��|�Ħ�T��g#)���Zq�7��q��E�͌&�i�2}�Z7���0�p(��J5��ܜ�3k�w#)3 ��S��-��O~�Ò����V���hQ�q�Y?y���_P�E�_��������s�oư�0��,J�0~|n��>@�}�_G�^_U�#)f���>�� F�,�U_�����5��]*�� k#)D�>�?���"�}�b'\q��j��B���w�:|��A�=�gã��᭭��;�q��q&`�.���=�#)1������,���O��z#<#4�� �(�Ad �.ϰ��L{�A�U��O�#l�N��,��9ה#<npbMA�ay��ǁh8������,s��x3%��x��g�Z�(�V-U���񪏪L�ȣ�� *�t2�:�8����8D�X��S/�R�r��֡Q��_w���d@��*��A$U�,��7ϛ�!��ak�[��A������z�蚟V�Rl��Ȋd#)�?q�e� � �+ |: %�Ԩ����Q�nmb%#<m��?.Ӽ�O�F��أeR�k�~�w4D����zE;��&a���� ���0Њ���i��i���ʖ��̐$P��`�*���{{�'�>��'�~����a�vY3*��W���*�4����,(ڣ�eRR������r +��`� )$PXu�����ڕ(�)TMVn}]}���w��<���>5�ΰq�{ 9D[2i}'�OZ�o��$��5��`Q�&^�j�ue�Oqٯ_��=�P�X� ���>Cs�:�q 2� HaP���S�8h��*�p:��S���$������^�l)�bv��gҿm߻^_,ey��'�F>:��&�K0F�� >�#<�z�#<��o��L��2U��`)��D{���^��5@�ǿ�>G`�i(�3�� �L{�������#<�4�ܹ� ���[�(�G�1+�5X�X%Z�2�iq�y��ڨ����Y$A�'�r#)�������~j�KO A��7�/`=�Ƅ���EX�U Du.�D�����$Ҡ&�Yڇ�@���]��#)Q��Q�h�Ԣh����N�yM)Js�*@J#))c#<I�}�l؝��"S�q�L������Qt���B���X�!��Ƕ~3������*���j�]R$R+�F&��D�<-_:6�`0��~r�n�*!F��L�#4+��m��r��\�#jݐ�P�A��-��^˪�Nމi�$#<n��:��z��~r�XpNC� ����o�=ϳqtZ#<����f��q ׫>D3Fc���Fܒ8�`X� ��o�h��?X�a7,AoT��b������@��N��cP�A��9r�H��O/'��:뿋�wi�ф�%�6}F��ܩ�/� ~�B���;��458:dWf��S�E$�ZEf���ê��#)#4g���'0/�aFú![Ghrl�� ���u��m���mfXK�C��2��jbK�#4�[8T#4C�i�k����l�܂�w�^۫��e��|CÃ���E(ʢ�D���@�>���_���^s�w�O����>��"�#<�i��ѱ�������2���#<����[&\2����/Y�� &Pޅ���U�I�!0}1�7B^t=b���0�����2����Po�9��k����)�=�H�]S���s���E�=�ub��Q�1qB�v?н��!�D׭H�*BC�j�Db�@t����:��_h��%�Ҋ��X���܆HZL:�r�� )ܒ�{S�e�TFt����#4�{�QW�&�f4&�~��ut$yK�x��+�x��Z#'P�H��b���v!� �$��}wF�#4�8< �� gѢ~?��d/�+��՚� ��~�V��2#���5a�~R��T�'�!O��{>�88�ה�",��"��)Uh�=���p��K�OV�d_�΂�&�Tソ���V+#<�"�����d��8�2���pㄵm�U!� B#�H��H�`���%/���:a��+��������B�ϒ!H�,E �d�(p��`� ��9���E�@�nr裩5"���}�c��~�]9n�5�G�7BnW�>��������p#<�#<$ �5n����ϳP�)�:Pny��p�$����l��x��4u;��b��Ͷe��:m�`ɗ��O�YuEY0daT ��[�D#<�C����8�#/o<|�/�qg{�҃N�#<���6vf^Y��ʿC�N,&�A;Ø�C2������H|���� �t#)B�S�l�((�����^��T4�,-�rB������؜&��1�ܕ|���Ւ��wlqȇǧj7p|O�k���y����0d�>���/@��*�U'2n��+t�)i�楇Jf��(A�|��o*6cv�am�hz� ��#4�<{�����p��ہ�n0���$���:WZw�q%Ȣ�Z�KQ��㬄qs�2:��+�Pw�^������( �1 ϯWK^މ=T��Dx �n#)�#) `��O������&��T=�CJ��;�w�3�q�bB9�1�A��F�,1@)X'#}�+����1 ��ĖB� �š��!�{&")3CXr��}���x`G�YtaH���F�Ҩ4�)<P��B�Y׎u۹S-�]���>61(�Nϰj%s��[ �QK�� ��è�e�(������ð휫�@#<�P}����鳯�E%�$w�A�4���GS��������gD�{�ý�s8Cʃ-���S���W���QPuw�Ja&?��˓(oۛ+�����?C�j��Q��_���wgY�~����9f+|���}6|�� �v�#4m�:1v�Cn�/NLF&0�Ie����v�����5Y�B�Z�l�b�_/`>�����ڥUs�����Y�#<סJ��}���Ɂ��h:]K�a�#):d\~���dU�/s0��#4H,y�dr�BA#���� ���x���>)��� @��&Z*��#)�K���嫫6�h�����7:!����a.���˒G���5�Lލ"���6r#)9�_��bOΏ��U#<��w�[��G��Al�ִ��Y��~�0�2lj������U ��e4#<�}ޯ����w�&'�U�zT�Y#<����F���u�ݻC�y�uV~���zf� ���#4jqb?�#<��#��&���2싞x��rM��4ߚ�� t�vFm����U��8��W�� �#4譍Z�*怘FB*�}ߟ\�>����6_ ��ĉA�D ��T?#<���z}�쇮Wm�>�����~KB'�03=h1���>�p����X����8#(#)�8ɒ����Gy�8'#)rn!��7��}�#<���X������� ͣM�j�hC�HCiK��o��#����V���ED�B2��;]wweggUs�~�LU�r���i�Z���1J���E�bZ� ѣ�z�m��%#��تm��j��KpfH�4��@@M �2O�˨��ύ��;U���P��v�S���h�U����xdMiK�M��=��S�:dոS_J�MD�s��pOk�3��N��.XI�J#)�9��🨍sƏ�j�n�Q�}����%z�Ǻ�����1:����ɐ���#��T.x������q��j�ϡ��HV@!!�EC*U�{�'���Zw�#��,�Ɩ�������`�1&��8�N���TK��h��jN5�>�]�j���{��1W�Phf/?�F�#4�#)р\�#;�`���#< ��Axz��_�������%��^8ntA)#)#�ܞ_+����E$4b�m�K�#)��l�(�Yl�LI��t<�)�-0e�\�_m�#)ږ�*����ė?�_SԜ���J>�͝A�~J���K��Up��NT-܀��{т�7�3�/���折�#4؂���&��#<�3{�t̶OS��w����4����x�&���`dȑF$�a�� ����~�#<�Y`8�I�f,��L ����he*�� �-SE�dk��a�v#4�����'�pT���$�R�e�� D��qp ��sn���.6�=k��o�fF�Mɼ7$�d�Ĺ:1W�t�ЌM����Rօ�ڛ��Gb?�p�`������|#4!ﺉf4�Aϕ Ѣ a�DO�6�7���=vf�O D� #4��oԟ�P#z��?V�a���dT�����Xv��V&@����A“��W�j\��}-�� ��1���!hƢ#)`��~<=$?��*�B��b��-CJ�s8�,�~!q<�sOS�s��A*/p� P�V`�9Eu<#4XNڽ+ 4Y���i0�n�.nˤ��Q����]��j2/z����C^�{��K� �P�{��A_c�Ֆ2��i!ah�,<� ���`<J#�2;����xV��>~�#<oL`�p8@�j-!���qI2͒BZZ�F#<~�0`#),���� #)���ء�8(<�#40��*2����}l���q�?MI�{��+�ˉ_�� 1�a�ށs\:�j����F p�eyx�V �'A���.�e1T7Ѐ|wp��-���ьE�>���7�ۿ_#9���6H��8*P(3���=�!z�%��#4��_�s\��)(>RM�8�m ���铆�C:�b�:���Y�5���J&��ꘊf8g:��A%�ZԢ���#4|������/�y��x�3���,��QfI��|�sTp����Š�X����WA�Eع��a苍H�+"�Z����3�������k�;�} �U ֐�@��w��!y���� |�w��rJ�����7�;�u��#)���� �����1)�5���q~�Z�$!#)�z� 9@g�����{� ����E�s�p�G2!�?��������yE��tx~J���E8d��||f�h��D��L#<s�e�#)ŝ���#<�����D�k�B�/hl3萏ǀ��C�?����(5��B��+�����Wߔ5��w���LM�@?j��Oಇ���&���3�lk��8w�v����ǹ���:�$�AG��ç�~f�xە����tgC�8�?~,�B�Ke�O��|�B=��r���=�h�s>��9A��x|�j�����e��,�3��X�#4���=Ή����Ah��z� ޢn��r��ھ�QlcáK�r㷇Z/v�鷩ڣ�e�/T�r5] ͂/��%���sun�ӾP�Wߔơ����Tj��"��v��V�~�����e�p��P21!7YF@�51r�#<���(���|��pln���o=��n��UƇ3���?���S�_,���$ߙXM�n��0���:��?cx�ﲞ�B�<�%K����v�"wZ�;.�\cv.z@�[66.�X!���lJ�'i|^���%/��#4-��UK���;U �e��G�\�6|��=9��8���|���\���ܗm�'��/��t���S,�f^Z�(ێ�m��3U=hs[�����( `�U!� ���#<NU��NX��g�1,Θw<;o �%�q�v9���a�V3���ʁt-�0�c���3:�&!t�k�N#��P��#)�+�|����awꗬ��t��6翸�G��w�@a���p�;{+ש]f!�D���Un�+�?*�[�ׄ�lW�Ypǂ5'~'R�kQ��\iE��]b\D>l$�K���b=΄�������Ш�i�0������%����)�/�I�p� �_�]���w��HfM�$adv"D���#)ݭDH��s#�u�׮>s���U!�v0�?RO�ݨ�����Q�ӛ���5>}|���%�mm�j��}v��������w�ᩀ������]����7�Ĺ�]xڶ��G5���� DЛ�wB�+���#)���)������Fc�d�d��v���8��#)g�â2��+6���q�H�����.Bq��#<��,z7~���7�ӆ'g�Xl���s�=)���P�1c����^p�A�%���#<m�fm���G#Nib��qp���z_��E<cP��L��&\���&�����a��||�|6��4#�q��4�C@qc&̔�z�x8a�����d�xC��2��y�<��K`Ԛ)�����4����|���{Q,�#4ou���J)}W3�&�,g.�H�v�D�ș��G��Nެ����-hwª��|�#4�M��ez@��z+�*�È�����x��BE��XF��d�������GD�"oֱb��4��t��>t��mOԒTR���h�sۆ#c#<8�(��ȈHȧу�~�ǽ�{��$����P���u�����h�Ž��FZ��'2�6^����݇碑A�������{;�����Ȥ%0���������d#4#I�X���������}�������� �3��$�/�c�oEUU���C����f0�Y�&��l���b@�9���2#4}�^����M���w�`o��d�o�(�&��i�$�H����O���#4�M掣p�܍��f��=� ٶ;��`���5��0��Inl-��3{d� ��@Y �o�F;#< �k�s;�DzP}X� B��:CV��{� 8�$:�;���pq#)��zb���|Q�̒��!�S"SDk3�,̆�V���c���zDJ*���J�R���vI�u;2��3���P-c��͆�09�a�*�|�G�o�%}����S��lJ@0�^6�&��s�9t��Cˡ�2uuދ|%�k�<<�h'�py�.����k��X��o�LߓHo<����R�QQ���m7{vz9��`Á��A� k A��4#��#<Q�3K@E���y#)�;F��o�7ahg�1�(̰�*��^g��xv#<���E�(����X���@�>��]��髅�)��p�#4��GBM{��Y������#� �@:}��O���e��;�ws���t�����.D��>]�7�����Y��;7�T8���A$8ϥ�o�z#<4.�$�1����Z� a�n��B��Lm�V��"A�F0a�+@�\i�`�E C?���帒�+�&ܙ^��?����SZ[3F�cX���k�/k�T$��F�ҕD�0~_�5��)_T�g���wmٓ� w������@ !�i�0d����`B-��B���u5�� �Q���V+�Ǔ��vv�<C��� ��,��5�C[����3����V���-�{��,��MBwؔuq��󗛃T �� ��B�Ա�42��Cs|���a�Ԅ1l���!|+8���M���sk��RAj�1S��9��4�#41���4E�DT߰�����`��s��}�۠��Y���xx�f�MK� �����݂��p�!F�Ox����Ü���^��8m���@���8< mћ���웟���^�V�:�����j�d̆f���!����p41x������D����$]Y���z��1�V0)��\b-�����<K;�X��#</4�׵��d��!��<��w'�^}҅ y2�!�4z��o&���qJ:9ézT�70�#���m�3��Hݎ����D\�Tl�pƁ����8hB��)�[������;J7��L�e�ڸ����7w��*�wF���4{qQB�QJ{1m��z� D�`[3q�6b#4���maa3I���|?�T�[;�^˚�ٹ�8� fw�d�3�X($�dzI#4:pۗw?_�o2B�B�)G�c;�hh�U���#<.GG�:��z�#��ӹ[�9�ا{���(5���Pc3��j6�Q��i�x6���u��� P�mŠ�Y�a�y[P� =ݻ�0��l� C$���=#)��A#4!rq�g#)e�D���G�4$y.�1����np�׈_-�Yb�p�+J����Z���D�:b�9 �9CU�U���a��� f&��[� `"�a�#4��#)w�*������8�r�R��!5;uX(���2^�ka�4��I0x�s�tם��+�*��l�9/oX�/v!��_og]���?�9�x����j�;G�ۑq-�Zd\6�9[Yg5�n�QaAU��w�ބf6��T�5e#)��W�ֶԍ*�@���a��M��2&�Ŏ*tl�v ��h�a�K�E7,I���x����P�r��(EM���,��Y�hfӗc��jd�2�a-(ر���5\���ϒ&��Ƨ�7�/�*�vB�E@��B��#4���x�+ c�#<�T���7iw�D�3�1��{���M w�d�<�L�) ,�*���Z*�P�#�x�|�AI�ټ�S/q9J.��h�1���ʉ���5�X~7ջ��75s�+)��DDi9��U#B�u(�zM��a-:���Lp�횙�Q�#<s.<�ɗ332�ٍeY�fI*�l�D?+૎��Ad(Uu����J�F()@aL�Qd�W®�be��}ޡ�M�#4����6O(�<��rL�i�;ۏw�m��ҌhlԄ]��myT���z�ge�8�T�:�\'��F3#4@�`�J��e uյ$‘3!#4ZD����'[��dt�M٘a�ے��:r���"�g�/m��M]�%������,��cF�.륽�oG]��d� ��W~�ט����m�"���/jy���� �B͚��[��p�lkf�w��wY��a���3ڨU$�u2������#4dӒC��G)��Akn��7[�Ґ!D:M��QV���'-@�T��Lۂ���:d�*�p�A�#)wnah���79��UCȃNdj��d����� �E�*x]eD�*�$�F�R�<��v|�^&��}|��~,�'��3�ꍜ��Va߁�`s��M�,�d!��V(ն�z�5�w��:����_�����c ]���:��h�/p6�! .$Gn�9�<��Ȳŝ��s�6ܹi���/'�S-��D�� \q^��Bhi2s��]"y��#4�Ⱥ�#)1���(c�L&��e�I#)�>���O s�n"��c����0� >Z��K�p� e����$�uU&P�F{%��ʓ!���nW����H�B�2u�:u:�Z�W%���Դ@�o���>q�H��q�����!��;�CV`�L�t����P�_�4�`�EjEQ(׭�8�F%K8�L��eeX'��6�3ޢ��r��S X�p�^��#�y����� �7�'Z\9�lJ vL6Ak�o KT!X0���E(ꌃ���41"z�f� fa&$18N���{s5+SV�٘`Z���"Kʈ���3I�!ֵܴ{��(�����G��Y��sl# my��\�����΋�y��k�1��8���!%{�hB^�s�+�A�d��d�� R�ch�l�2�M���2(�Z�w�t<�!��sb�s� ��kMg��'K�)����Z�~���� �:=g��2��� K���5�Q� ��B͎�xS�ԧ�C"�Eƈ�~�rU(LE?D�T#4����}WF��є��ǯ��di�׃u+��ߢ�G��B�#<��{{�����*#}��},@dmj�q���.�c'��{zj�֞oHcHhc8,L��<`�Kv/��M�ʲI,[��J�zKkc#)�g��82a�,^���jV��-��HHb{u�;�GɘM܌�L$����u`�LM��l��6'�?I�&0�#4M����nzs�Ś��(Mk-��̌#<�U�#)�r:s�t��� �ꦫ��IL�8`�KZ�_�?����q�M)6Ν�e�i�7�ԑ��3�������!|�B�#<�����Ih;c��?Q�7����i�`�#<%؅� �`eJ�# �S�H߯by�D�%��p!�UJ�=����eA�>�q������஽��/0s��d{���=�,�����֢�^�_�y@�$"F�>B�*�y��o�j��S�.�J-��JD���"����ј��U!��@��]������"m~����!�1�T�&&��uk�-�Y� DN#N�x��j ��PE-A$Q#)�ľX0�����B�g®J�lV|����H�Az%�fL���9�Bw0 �(�-@+�/���ʟ����|�n�(�$�) Z��#)x)�Psy��9Y|h�`M<p�i��Z;HM��Q7�u��(TJ�nd TG �+���R���\,9bѩ1#4d�M��%�"�LOI #)�!�?tH�Rê���=r�4��(\4Gb�E#<C���=���0 H u#4��$���<� 7����s�g�;��V�u��Ͽո��]��+Ε��r*��Qc��‰E4pŨDCX@8���"�#4;;��򕋍8�l~�r5�F�͛�/�q�yUC�|n�����lFO�4�U�� �h�/AH(z�ΰ�T~�}h�Ɵ�Jfay�$&���zk��� d�I_����:� cen]b6�ǎ�����y�� pO�� H��������"�A$#)�B REH��8*d@�∲(aJ��$�1 *؎%ġA�@��T�c����~)'�ܬn�Y��ea;ebYUB�p�5B�I� KVd)؝j��*i4��`$�C�$��ad޳J��y+ ��W�/+�n]I$[�ݼ���|��ɼ�bh��� �!�e����J�5#4��nD�ܩ++�G�t�]~ONۤ�/h)!DI�Q�Xu �2��#�����6A��o�����m[���E� �┶#)ȱm�E�Q c���9�م��s��;~�7v[$�N�<YCIuE�;���6o�/�ôy���رcB1�,�G��������Y5�,�&�'ê_��W>]'Q=X�Ñ�m�C�$��X��a����-|�j���{#4��LQSTJ��DVD�^(�mz�#"�nMM�I��k�K�?�'A��a�Kw�'�j�?�Z�]S~-�k4��� �\#%������0�4�@ԘH��B��%&̆Mu�> i22@#)�BP†���m�nfB�R�q�L/��@��� { k�t��#4�M���o?�*H�#<��V[�>��n���[�\f��$&|7z<��u'��<b{��n��6�H��U`�� �!%�x\�'^�I g�kk�4sT�aD!X=�������L�`�םD�a�^�`����T�&���D�HV���t�<5^K��,�d���#4Fh�;/ݻ�m�bYAl�wC��B����>S� ��w�QB5MSEC���{���h��Q֠kOM&�^�S�⚆�6��*+nM�V4��XT#)>�NaqܑA�H0�/��<�d��)9��ʩ%T�d!��t� vE4-���LB��W$�L��&����U�[�f3$}�h����� �#��~{�0����;c%UJ#<YP�ә{�]�i;v5��=��[R_}T˱iJ��H\=�!$$f���UIZ��A�V4�ej)6�e��X�U%��+L�f4��c#)C#)Z��� ���p�9��T�9C~�S�#4�A��+QEzZހ=�A��B��*R�;�_A��Z���h�\�h��\�P�xhlu�Q�D�(�h�L.��x�=«,�\�`�A����Z�ڳݢ!�����"�D�0�A�X�h�x�6#<mҾW�����U�Piƴ�A�%�N�`�����ǜ�ܷ]��#)A$Q�#)$#){)T�$؊��䡖��c�V������H&�d�u� 2b%#2�bbS_�������0g���@g�Mr�b��T��^���B� ��Xᆉ|Dd$F$�dX1���b4�EcZ��-�[�eB�)�-��QRm"�-4� �vg�<����f�ڗ�<�J@� $�VDQ�Q;�P�!Ⱦ�>���}����0�5XC(Z77KHfd�p����A=G"��x��w]��T5=�4#)����>2��w~O���?������5�а(yD���U�1 #),��Y����1U�R`n�m�-�w���e���U*�5�T#4A�}2Fp~g��<���|=��]0$59��x ��L�q�� ���~�tȨ#<�[?��:� ��L�h����#<殟���zc�D��tG������Lj�T"����Q#<�9n��F�ӆ�r�;��n����)CU8{����;W�hlTf�AG��7 ��#<f@C����6#<� �,p!E10M�wG�u����Gh]{r$���;�� �!�g$���g�����ٝ0Ը� ��5���똝����H� �puG�:�8e�aG�퇯y�U�%N�y�4Ҹh�|���U 4I�n~�� �(�a�7�h!�>���2V:'\e&1�κh�=�9j�@ϑ�p�٩A�l�� y������Du��A2lj�P��t�"ܸ�nRh�,B��P�3tB�e�p�M� q)qc�a��x�[81�s�y[�nF��gW �xm�LYT8�����p�q����k�x޴�'Q�<����O$ �����2���ci8���%首�px�, 1!Bz�MV�C�>`�Q⠚ͧ���(;|�`�?R��|5�!n����C�D�,�a��yCJ�Ք�TTQ��,jY�t<���#<2/�U#<�R-0h�H/?O�h��d�����uu'���Pœr���+ao2���R�f?��4�ܒ�����Fr#�0Hh�إn;�L4Z��Xb�Y���K�#4{9Kl��Ul�cՐ�&�^`܃QB�~���XA3�DH @�X8}��&�L5t������ 0�λDb 6ż4��ޡ���CǑ`E�x�g�@��r���˗qэ�hT��.R��_���\��#<I��E�LMB�i~�_��4���0Y6FM#<qf��ʁg�]��΢��k�o�c^�8��A�ф�gvwԤ�^/��,��m�;Y����Ye��p�@V&L�f��U�U)��.i�����:G!;�<�U6�3C��r��a�t���۞=�ʉl7<�2A�E�r ��FNL^�[:�k;Ph�Ðg#4��+�������ű��|�ũڌL�(+�vn������3��Ҽ�=�Z��#ǁH�<�v���Nfm0�8����l�#)��,��&7#)��y��.�� �;7�ɍ�#4�����t��s.ܔ��Ӕ��E���K%���;�!Ǭu�D*{�/#)���F}"(Re��/��,�(��YB������ȁ�O;��g�X��"x�y}zt��c@��V� R����U��w� ���?8*r�9_Ɲp"�H��J��M��E��B1D�~s=rک�c@_�"��~!� �����1W3�j�GMg��Y@4�����N�{7lߟ X��ܘ�(�a�M�K�#)��d���Kb���_#<��@6;�[t�T.h�9K��`&7s��[�9�z�$��8Q����J�i�!#)����n��pnkE"�+��#�h��U7T'������c�z"���ܙȂ!� �q�3 #)�<����GW��C�$P�ݘ*���%�9M��gg�u3�����?>�Zk�ف��.��(�o?u'��}:�E�[��C�z�����rf���I"����=�(�)a��4o55 h��>���5ԑ'"wۥ��GN&=�d+�6;m�N�S5@&�Y��?�կ`�FAm����P �!��=��ձ@�4N!�'m+PN`r��)�W�߇����SJҵ]U�idhf�Z�sdHwWR�n�\�5YKk����sOy�HDA���X*2*��"hd��ix<xX�4-%�P��+�A��Bх�#)��A�!!6���9A`+�Pc�! �H��n���a֎!�x�9�!����z|�K������Ù��}��ț��S�?mה���\��M��m�m�W�W]����Iv#< R�$���P:B<k�nȼ'�2*��>׭2v��0(�B HMN��Lq��Ad��;�P���5��@l�\=���c#4fЬ#<�B�����h�Y+���J�{0�{EZhSf�<��7k4����{�)��Q��3�#�ὀw�&��3�\v[�B � ����z>�9!�x��ѭs]gu�][����Ka#<)v���T�`��H%�#�I�[A!�fd�p=����/~�9i�b#4��_/�)���[�6��C\C�&Qa' ߓz�/%B��ɯ��j��\����#4�[���#4Y�i��#b"�g��y������r)Lw<gb�c2΄u~.6f����KL��#�m�m�Z�v>@�!�Y#)y�WM��6y��9��yN�rvN�[O��#4ܭ��"��v�6"Z0<A �cM�k�#<x4I�Z����BE�fku3^@��d��Q "�g� UF����#4{�x@���T�G@�l֙� ��v�p%I�"�#<)O�u:�FCD�ň�D�v�b6�S�=�4���K�UY#^l�T�~��zHyѶ�9��mM�vi[��l�o�N"��r�s�67M� 0Ze���l/8:������� ���݅2#4T)֖R������m�Ǚ˹s�p:R�?O=�k}�F�����7��jD��|��"��2�Z��̓��#.o�K�I$��F����"G���#4&��<�Ф���!�ז�P�0�-�t>���FPA�s�1 �P��<#4ÂY���Ѐ��%iЗ9xT�,��8������0��Y�jĂWX��C`�a�Y.�#<�D��lI�J��i�q��!���Q�#4L�7cL#4#<.��R-h�n%�!8�7.�nUB_bA�h�y�؎�h0J���a��,k�*�i`Pm bi�p�@�+�@e!�Ѡeԥ��[�<�g9����<B�~��a��K�R�ER�35�F�,�&nG�@3rW�!��O��P��ׄ;$ˉvP�%����g�� � ��6i�d^�������E� D5b��m �n߰��Y/�M>,���m�r~�ltg�Nvp���l��S�q��i�0fiZ�]�=�Hz%�g3��uim���͎>�}b�]f�c���K�#�s&k����0Er�lJ��c�z�����C��-&� J�@�"���� �Ȳ# �z!�*DkY���knw[Z�)tǞ�f2V.G���*�K�n�MR���Y�K�R�U���h���#<f�c�>�MÿÜ~mJ��0�l�#)`�#<C��7Z!��#<�q&�G?{"Q)m2"�JcC0�o�SS�����m���t�{���j�3 3'?K <JQU�)?�#�^��e��`��,�4�#$H�y��@���jԕ�Z�������[#4e�lށ3T����N� =�b�#:$#lh�!��`���Ǹ����j(wH��EB57�ϯH�!�!I�x�+ӌ��Q! L��a~J0FN���6�����ZR[,� :l�����)#4��I���n�X�p6�k(�ӌ�5�>x,�$d!f͢�ث�%P�#<�w���#�4l�@��_7�=�ޕ�a��l�Ļ���y�^]kG���bX����Ɇ�8����&�(,d�KL Y���آ�4�>�1hi�N�X�z5 [E"�#�Վ��奱��*�ᅁ�8�P��m��3XR� �^Šf��T=�͖�FN�'P����Ԅ�佯�J�����6B���ƅ�����3�~E=�FY�#4Y�SGK*�ZqE�I�v�#4�6��z��S�T#<54O��k]����z�O��G��ن=|���t(���*�T�#4>�P�Qq�����9��q����׭�*TIq���ˤv���^�1���aw�vG;� S����f"������6#4S#:c�r7s;�Y d8� �#4�P�F�DUTB"�&.H@Còp��"��e�]���W��#4�o�pѳ��%��#lGa�m���0�o$:b��S`D�)�)5�sA��Q)��4I I&�!�M�gLLb<P��I ��m.[�Rs;h� 孙+I�B!*��a� �2!L���UKCt4x04BhȂ�,47j݇GH�t������ �|"����T��T��Mb#<K.E勉Q�1H��b�z�*$tgM#<^��F��F�#4��'6�#[�p6����C'������IK��p�.Q��PHl��nl)#4K�#4?Z��v�46�\6jfN�Ri�7�\�Pr;�p 9�G0 ��!�(�D$Q��@�&��b/#4���:�q�x� e5�k"#<r���H1����g7��ʐ��7�{�,pF6��BN�a=���"k�8ɕn�Dt1�s����ß��3Ma�+L�) ��6�#<ʨ�0�V�UY����b}�!�5�Kn.X-�N�>�˜��1���^��k���~O�԰���lKa��3L�ɰ}�3��A��9��b��������)���A6�du ����nϡ�V;9� 6 @)€�Ǩ��{�G�'�s�� .'�q*"�X#<6`��-��*.V�|�#)i2~�,V `;�ɹ�k70p)Q�i5�ɍd�9�#TІ�"������L�G��E��f��g�4�l���Dc�*�F� ����͏�#4���&�FNQCv�N#�}`@��1��#���z�#4��� ;�7ay�頚��\�?j�œ�#4N��Fs���#�k�ei߆o$���#4���͓v���{��V4` 0�}��K�y�#����S3/N�!��/�.he81���f\VV-���Ǜ���� �PD E��:�!�3�OP��P�k_�s�n�,#4���m�5\�U��j��j��m��Z����B��vO�f��<��'���"!��XQ%��FR��pv_l#)��]���H�|�� �������YB'�X�TLB ���L�j��2VR�b�j6�-'����miJ�6h�B�i�_�na�6�ŒQ)J١L���d �4hEQ�&�M/��@Fő�ALi%%�&A�4j����L"�1�d֔l�*BS��4�lJ��h)��E�6D�#����嫍<Hr�����R…@bl�p �|_���+�v�y7}����}?�����J}c8C8��G0��}f��uv@�yn�%���r'�4%�/b@�2ض�0ؗ`FՑ˨�;7#w�E#<�c�����r� ����t8,�#){�pԦ��}T�f��#4X�KߎY}F�Xia�#<·���7���R`��N&�7�$�E }����@m&� e+x5[#��xap`�.�߱�� fĊ��X��6�i�ɶ-�#1ETDH��:�u����:��8Qo#<��@W�SO���3�-�,����0�נ�_�՝�%��S8�#45wl����7�^�`*�z|Ā��ʑ�ǔ�P#,&Q]��7R|�/֐�9^�a�"o�=� /���}�C9�l���`���S<Ӥp�r�%�D]��� �7J!����ik#T�!B��#<H�8i��c����#<z9M+�n�����6t��x4��}UW�D�q�m�>#4�˼8�-v����Z֓��>�������#&g�L�93T8Lyf[M�S���������Ơ���M#<�j�Z�\�A��K���BYi{Y6���ۖ8���E/>%��l����$,7���m�����ic�4X�=7��o����>�81j C:���G�S����^^���\��F��P<��2��ni�q=敹��4#4�maw5�5��a? �,;b��O���r�R,��I���>27%����L#<�eS�.g'���m;DD�����s.ꂃ3�f�68�r�"��J����4$����ݶ�̋�준�����@��2�T�>�����I�ۿ��ǐ#]$P!�o��e[��'�������c��,�xK��U���lN�)&�� ��mT�"2��� �q�0Q+9\��`��<�T t�-G'��+�<4��K�j`h#<4gJA9jT�24W���CH8c=�q�Ι���9R\;;<�ꋨao]#S�%�xTe�X]5�={ ��C?J����UMU�2��Տ ,#4 ��ISGvQ�%���2"j3�I����t��~�RlM̗]�9|4��;?x�@�&P$D�V�J՞t�=��_���{�2�_�y��>uX������h�²���h��F(�H��˙�f�fMTA�0y\(�ָӣ{�����3����1��t�c�i�5#4$&�;�E�Ӓ�\�hD�/i��� o�c'�R<8s1ڲio5�D�dZ.�7�3#9���&��ՓP��m�k�o'�\�-)o��a.syѡo�L3��p)�\,@�uCTI�3>���D䂉���*qi=��\̡2Ҩ�˵R�1)�!�"��(ˈ�D�B��;SZ�8� ��8�?GI�^��M<ah�w����ٮN��B�>L��q �ē�GXƤy��Hգ�d�\��N ˑ�l#GWv��0���Qb��1���6�}]��}s6'HE;mc�<�q�����Yׇk�i��IF$wz`�c ��0#<?ft��*�V I|��Cɮ�M#�767��L����!�C��i�Q��}��as/��� ��!9�v�n���m��x�ҦV��c-nƓ3J|��lQb��^0�L�Z97�2׊���9����!�v;�#<L��� �;P�e�e)ԣ38j�� �Q�&�M��#<����h߲1z�PN�"�;tFQy�cƍ2齘z"V��5��L�X���'Z����=%$mN"U�Ȼ�x��.�G�\k�c�}s¼�x�t�bo�m��'��pQ ��$�>4%&8s5�����A��Iߦת���N͵�x Lи��ꥯ���[��s�n�8�r��%C�rN�a�nng;#4����ڣ �<�����Q%aIP��i�o����;1�:�c�!X<�ܮ y�YM�d�!�c�uC�R �<-��nvY6#4��z gv��f���"hq�-k��$��DF�iu� �AA�?qbEa�P}"Hu�&qT���H�I�#ut��NL#4+".��#<�WVK�ls��w/hmLI�B&Y���F��P�~0L�׎��'�ö�<'1t�k{�iW֨���2�#4kv����� �3��%jMt�C�Rn&#<��Z$"��&爉[�g��H�I�MQZ4�a�D-��Y�_h�n����X-l�t✖0�%5�ݜ�2f���6��_����t�A���:x�P9�o7j$�h��4�kk*��k�]��X��mx�A9��!��޷y��;A�/Rc/S<�re��چ�0�٥D�-¶���#4=a���͐�WLB)U Q�*#4�J:��"�\�{��l��U�#4Mu��!�އ�I�����q��0�l�m�퍶/<��p<��]�A�@�W�w�mfu&�7�|p���^7l&�.��9.2���tjFۭp� �#5&�_�� rl�y_��\��>��z�kpk���O�W5m�i���Fos;:�`6qap�2v�F�}����5�3ə%3�<�S�D�@���tT��t³�p4C����e�o$����u��c|�&쎆o[�f�ٙY����yD�;D�z�;�5#<j�R��x��4�9̿Rh�:TL�]US�M*��1�_�[���� �O�1 epS���x�K���*������[A���1TL��9�jɫ��Y�#��ܺ��q��c��&��F�.:jn�5䏇���&�6��2�ffjti$�knm�fM�53�N�Fcelvj�1<����D��Mv�r��2r�W^�e5�p�؎o�e�[N�9Y٪��R��l6[Y;B7�����i7��'C�6i淜"k*Q'��N+X���V(�q\p����$�1�8��pr�9����#4���� �8Q���!9�R��-K���LTf��#�"�li�6���NT�2C"�:`6+<��ɤ�q$�$}��J�Z�5Z):U �g�zh�*Б�pV1��M�-UM0��� $Ƶeѧ�N�ki��v�~!F����l�r���#)B�����#)�KT*H�#4�GTlM� ��s���� ����iI+F5ez�~Wȁ�������mΏV7-aa�629'���cl����Rbi@(R `n�e׍�E45�=�uL3�B��e"8���p��(���ؠ����6��Zc�D-EV�!.��y�w���b�[$��#<AHj“t�iR�fY2�4cJ�֢9dώ��mh9`q�#�L���&�e��2c��Fn�#)��h� �-�RvQRh4�ڡ-��©H�&{+FgJ,G5)#4�0��I C�n��f�̔�ab����g$��mZpҦ��4�&��ٷ�S &��Ԧ�4@�F7 \>ɴ���C�1���ҍl�X�r�8Dt�Jmڔ�Х�f�H#<F�\0��@�ƻ�Θ�f#4�"e'PH�� �:zr��/\t�c1�-��e#)�KӇ�m�,�������גcht'9C���� m�F��m�(ZF��گ8٘֔n�9���ss�rl�!f��h�FquJ��Q��ru���'2b�K�O�)�j�ⶓg#�k&/���&�bDŽ7#)�5T^SBgj`E�cc����^S��ʍ��2����[݊��b&�"�$Pkl� �5@�'6���3d�C��a�[�t���2�)�ɐ!�`��`�C��tF8%���a(�2�4&�L��V���,�`��b�#[x��b��h Mx<C�j>�3��O Tl��2e��m�$`1���x�~�s �\�:��5��6�������"�j=���.}�(�'��h���A�61�`�n= K��L�F�&,� �9M{Ҭ����8ƙ�#<���$�S=��FMQn��a8 @��R��!�9�,#4X��T��3p��ɀ��#4�nHcɌ�C�#�<�eU"7�r@"����i���Fw�՜PQI�0\̴����9�˔�2\f�����dA#)I �,S�l3�#E6�L�˦��7�6�.�c@��[�ي�f��t06.&h9��D�Զ���D���� k�R9kc�%��,�䒈K��m#4��������eKBGF�*J�a�� G��d?��ЎgR��I�2Ym36�͙!#)Vғ�E!\5��^@�A4(G�䔊0Q�C�/ռ=���BG@��V�$�$���ʪ��$CȪ71�c�`��*��#<n#)�9�H��#�o-Z�z��{�����$ƍf�#4#)`Rp@raG��S�5�SH&HX�v��'Ǝb��n��r�;H>��|�H|�3��N�l}I�'y��e�Ŵ�n#4fR��c&��[H,�JV�.b�mMi�ᤸn-��) O���V>N�[�BR>/s p�I��f�2Ma�3.�䴄q�r�6���12 �pv �u,:��x�r�8�t�;��_B@��p�}�O��΁K�†t%��B$�PGh;��p�~'��j��-U�n��7��r�-Ș��hhHLh�1��)h#<�����Ȳ��� ��u@�o�|�B��&T�=\_�!3�6�4jBJ����5 ȬNe�B%��*ɺ��f�#)3$;ţ�a�ߊ������l��R$�@~ #�X�I"Q4�$M25.���l��^Խ���5l�T�����xu�"Y(Y����Lv(.�p&�8�#)HC.G<yt�Ay@��2�Z�10��{Om~�v�g���j? dĻ���d A���M�f��{��IN[W"� �z锎����W�ڥRb `0(�` e�#<c�M���'�ne��=�PǸ-���)Ni1��#<i�@ŽG� W#4��F�QeF�'1R.E� 1�Rw�\1�ì)*#4+�~&!���_��(��) ��*佂q>��F�uC� ��Ml[�m�cQ�Ch�3-��[H2%�fTB�2��D���Q����#�y�Z�6_��i1 Wytt;`)���!�Y�Ti�I>W��B�!�d��뮾5U�t�k�� &iq�58A5��C'�t1;��퇬�e��OZe*UϚ.��>�L���\!�G��z4��y���4}k�n��M�f��(9�b��P��]���2�y'�A:����( #4���#<��-��<���#4d1ZWp��Q�0��T*�cKi"ȣ�Bn_�ͮ�ߑ��|j�"��U��Ɍ#4�JY�CM"�q���fh�[��b��ρ�H��� ⻕�C��D��ږ��H�Ze�`5}��{��#<����Էnjƍ�>a��N�3]�-QV#<��7��n;�!���Bac}mV6E i�jHT��R�#ֈV��nC��%�S� ����5uI�z×d��"^lE���#4o5AtǑ�\b;�"�#)XD&�'MN+�煱��~`�@<���c��Sr��H+�V�R#<��@�E��T�@�K�R��$ ��͚�~]�#)2UA�@C3��1����/4�N3/Ki���f���#4�6���ۘ�F�4�۸��A��W##<�7������d\}�Q�=vɟ�Ji�a <H�}�-�`6̤!j���<��I���B)�RoT(0�w�Pن��dS� KJ��E�f���a�22���U����`��'��N�6q��u��U� Q*�(k��Pr�!.�pZ� ��s��[b!�� �ط� ����' ���dg)&9�(�wg9��&3-� �9�aa"���Mu��� ��#��P6��}��R)�mtXQ��:s��,�W ��M�|��Ѻ�� g>q���{!S��ggjޘЄ ��;#4���z!��ҨaRi��k���A��� ��K-#�n�"dL� f� K�����L LEߤ� #<�$PR� փ��L�`c�#4��e���#42����?�#)�"!r;@H��{B�k��}m}����̙�+�ӊE'���#4G��t�`��0�e�>��_T��a �:��lk#4�����(&�$?�c5�jbi%��G���}t %�N���vi�=k��[AmF��Zɴj�-� T��!$BD����S$>�}W��R�V��?3.�e��H}��8����һ�[S�ט���=,������#)�&�V��;+j�:��I+�Cd����Y�%)��Q��s0�>��qK-�)��{ͶɌ�����7cRײ�b��R��I�a�� �!ml��4�p�Rm�%���I�q��#2��'wl{�#4VIQ����ޯ�7��iȉ3��͙ ��T���dA_MoD��x��m���Ŷo,��e(g��2���G��MW>f�xe��xӐ�9�K#<�p�ܺ6דr1�/ �B�Rc7V�P�Zۤ�v*��v՚�N��w�H�|��c��h�3��3+5��I&�G.�D��'z���i�MEZ���[���|��%�VI6X�H;�|��}���ɱe����0_��ɜ�#4HPe�S3Hk$F,7��c�V�Ҕa*S#) Z�L�G�lYF����q�iVR� e%0�E�7��C�L,���0i#u�5��ym�z�j̊��r�(�!$#)/ (�P[d��TOʩ{Zd/�԰s]uvIUk�m�M�ֺmMwh�-ңb�����=yx�d����A�vA��Um�L��;[�Ʀ��n��{[��f77d�w|�]��fӴ�뽚���\�+u+r{ױ���$RD�6e?ߞ*D��M�m�_[q�[ko�є�3l�#<R����-eI�����tՊm2kK ٶ����v��>h�A�Ʀ%�Kj��BTj�iI�\����+7�#�G�F�J4�q/a�*��l���@�h3y��)�Q[|�t�Um���Z��N�#4�_<GrD='y�z��y�F3�,(v��b]L�3N�gג'������$�t���J�X'ցl� i33����ÿ=30�g�M(.��H�6��z�ZJ.#<4|���}�rC�冧dn���5�R��_�ʁ�Ž�R��h�H�Pl�B����"��#)��Ă4�nLjN�����ce�״��F9��CV�V-�d�@����#4��n�$M�#4�#4��9��l�H=+b֦S1�މ����H(D�6ۿOW�x�Mܵ��{ �V��2�qՑ|��V,��t���~F2s>�|po}xu(<d\��7.8��V���)���cy��V$�[Lgh[)C�h"d�̲!+��ϹB\��#D#4�rNW$3�ȗ+��XĆ�ڿ���D �psT'��;����׏��a?�%ZuЩ6u�Ƿ�@�p�X#)�(�@��|ޯ��u����ko �ob�J���Y�Q0�;|��l���M�N�?6��|vMh֐�uEY$�릚3i>��DHJM�涙a5$�I�Y�mk��e����a���O9M3H�n�i�M��,��D�k]aH�e3#4��#LK6�-jj��|qF�5R�.�P�����Y��Ɗ������15_'R�&�"-,ceW�6۶�dM�_;�I�(��"kj�D�M�k[�m-L��WI���>w�h�M��)�KXTZ�ͤ�m�V���n�͛{��w&R֙#4#4_�y�m5I�#<횼��J%V5^&�1�����A\��/ �r��V���7^{(?��^?�\b#�"��W���^��\4�3��UU¯�?��.I0�}�LZ����g���~,La}���#4Z#<��JI�]���#4.�b�#��h�΂��&�m��-�%>�隖�J��#)A#)� ��f z 0$(��i[4վ�Z�Z����КTD`�#��(4"MF�vʮ��آ�X�2�P7�0������:�S:v(鴥�H*k+Q@  m�͘�b�1����*�(Ԥ�)�m6͙cm&چ��J���[�0�@�j+*�hĤ�iI#<,6�e)dML�1���X�*##<Q)���RbM&D�� %F��h��ScT��)�ʔ�Dٱ�DcL�M%I)�J�1d�R��Y�Ҥ(II�&Ra4�4�R�T���"b�R�ͩ2H��ԳY2hҙ-)��5�&�k�k��T�J�Zk%��i-�A�U�,@�#)QaD�T��G�%�*`�F-�~V�5J[���4�$X#<j��TDĒ��O8d�/�E�r�S!�@-gfJH��.��:�ˎ�q�3����o�g()����q��z���G�΃�I�#)#4-۞��C�V]:X<��z��$j�ʛj�Q$!��1�x*�v���w�a�yC|uǪY��A�db�g�wc][:���k(&c�c=WJ���JQ�ICÐEJWi�v_[5z7��?��Zc�Q�v]�Ѓ���2$ fl��ha!���/@)��k #M1��)P���� (�W^�ذ��I_�P[�RܨK�ި��\�G9�k�q��H#<����g�qD��`��2�бڅ�U��P���|�F�d�D�&̊�|Y�a!"���X��2�H����Ҩl6���{A�S ���PLE��ˀ�f�lzsm�J�C�Y;`��,Ł@ �x������q#<zyT|��A�H�*^�9#<�L�#<��C�r0xU+C���6�+![8���r�Y$gf�[� L�GT5^UU5���z7`Uy���Z8�)��a�q{�RA���CV�u���m��Aѝg�q�#4�ڊ�FE�C�w����A #)�H!�y~g�����"Ƥ�@��#)#)-��-�1Y'�TT���i\��Bd:j�w�!#)�(HRE�']����F=�<1�#/���� w�Y(�!��~N�,����.�F�� �(���D��p��b�����Z��>���o.����37A�%?Z�a��@O>a��7x��b�o������lyWV��S=��j^�T�ci�o���'I�M�e�����G��n�j�7���9�s���#��Q��C#)�dA��P%��!c?�N���Z]ԤU ��$F C�ED�4֞0��-�[`V#C�fI3U�LQ0#4I����yh���h��!��c�����M�UV6p�Pbm9]�B3��D6&iVW�J<����{a��$ #4���ۤ��KV��E�teS8��!�F:�q-�cC��'���=����7h��#)Y�@���5����n|u ��!�>n�T�h��j�fʴkf��'�raud�&��<�?��Ɍ��Ty��#)� �  G��#4����lk���<�<I�����f� ,��-AX��N�wf�Em�)���$=�����0��( c�Tn6.n�۵�������>�EdcP�j��������ui�L\^)����s���%mƥ�����%�qɹ��a!3�������n�h@|��RgHRfe��.���ڒ͑P��2�B0V�~47��\��C��^�}=p�d<A����"{og�+�V������޻N '�4W`��>��ž��R8`<���#4a㰝���Ri�ƻXj#<����Σ���I�>�����:�e3�0���%l;Q؀��i��0c! �pUe�()�II���j"�����@�Hy�1��6}��׹�fl ���#4��?uJƃ4bC!���i�=G"׍�a�Pi�R�F�3�� ֩Y(�g΋�#<I�e�����Q\�z�� �s�x��w_�*���zq ;2�UKє ���ĩ�IS#4v������ P�i��E�Q��X�҉��I�!��fb����_q����܇����3��-ѡˍ�OW%�'q�X�O�ZP�0Pd��O�T���e.\���;�翈�m��p���40�:��f�15�SsD����!�GN#)���ˆ����6#4;}�[qF�Qt���Xl�b��9-�I�Tl �� � �]�c��p6�l6�Vl�[)�q�Ó,���M��2|���5$��U���E*�$����#Ӽ��f�gr�q�yԓ>�Ҍ� �P� (�N�X��*�Y 6HQD*����S�SΕ4>㹉q"{�X�Tp#)q�Q͍8Cyp��a�z�]�Y��Դ7�ә��,E$`HA����U��+�Yb��##b̔$��Ƃ �&��ZYN>Zwk����g8�Ws1��s��0e��7��׾���`�U`�}�v_���yQo�[�~>xL0ci���B+�*&8�]�{݅+�s���&뻗G�N��,��փ����w�(�aM�a 6���#4��5��"8��mz��2���I��������K`^Ƿ���w�!*�K���]��KQ�y9��B*C�E].��;{m�z��>�հ(1�ؔl B��8���K��}��Vّ��90� �6�E���f\������%�׬;;��р�G �X�8�������z�:�X�=Aؕ���l�{Nx�C�h=�"ova�4�����` ,�=ݴ���Ҧ�љE����-��3�&��T�T�VFS��ofNo�#Ab�����Q������$����$�[i/6����.���~��"e2C ��{婫��⩍�RJ#)ȡ��#)�U��iE�]��\�;�2\�ru���+�55oi�^�-�T�b��#)���Z˸$C��� w�Sonx#)k\5���g�x �q�whirO��eK[ZNd8��B���,F�L%�V.O����1�z%��l�0��c�F���df�! A֎wL� ��*��+}�4Ʊ2��ݪ��ͳM&�Һ蹿"�/)�,�F�ֿ��sdQ@�9� ��S)�$���#/���f�����^�w�6,p- Q�ᫀA3ROՊ�hY��X��2뵙�L�-�,%�RTB���1�0m�D��ޅ� v�u� ,go2�HD�/�����`�,C�I������h~>�vFWm#4� ���`��<#4�f�]�{d����L�t@�(o8CWh��I[�s[~k�:�A����Ǹ,̪����],#)�� �l2��j!�c�����L��=�#<�D*]�h�dj4�F��;[,PMa�ۺW�v�b0�Lݳ ci�J�Q��.a����IiU������������2" �bA&!�*?� ���W'����amע� �hh0� #4���M���I���(Q<�������E��d���0Ps�|��`�ш#)�t�eA.z�X;-����,�۾Ys ��~���T#��#4a̐���L�$�/��W�Mj��#<�=��135�n��Mn5��<��霶{�K��߅������Y��}�)�3ݒ���n�va&�����C��ey� U��ȵ?�����()�ێ��,W���\�+V�ޠ�"m�B.��Q�`���E�G8_�\�#)�D̄#� ����\!MQM�>'��!���"��p��"y���:�k�X�_�*�\��U��U�J�ͪ�kK��-�0/� "��d%#)*Rgd=w�ToA��/�A^����]+�K#<p2���>�^g ���sNg�ǃ֧��6`��'�<� M��OZQ�8;���g�O�w'hÌU) R0�R��$:߉d@ߕN�D}̖�-�"�#4/2�ܐX����^���*�LV~�~��n�N'�!Apҳ�\o5`�JOC�uh+g��߬:'���H�=�B�Ӯ��9�:${�N8��������d�d�V,D �D��Ϩ��s@�D���"��y���Te���I���f5CG1�U �QT*�� �f-�4T2�2,� ^I�j��b�:`���@F0�XB�, �v�O�*c�nʅ ��1�gV%� k2��(�W[��$�*Ń����Hh��%��B6�ɐ#<�Y�Q�1 Rw`a�v�0 �Y(��V"z蚽�o/v�'��$�yw��Wsu�n���N�m���YLVJ֤��b�D\��Vpҏ20��� �� \�����sV-�Q����弳im������W,{`��`MYi4r�M�+S-`�R�R��.��czxa�EXVÖ��boUYV��A�h���קwN��k�6Ѵ���Qhƹn[����.4=u�_z��o>}ݷ�i7�ݧR �cV���b�0ƑZN��E��#4fe��7Ép��<e 0�!\hP���UL%�I�@� 8P����`�ɪn��ZM�!��@L�r`�Y^�e���>��� Ԑ9��P1q�f� >t��'���B�i�~(pu���C��Dg��# ��+1�꫽�i�=v�D��0K���IL G:C�m����6�p��y�û75N;P��K�aT���?I�A�a�S���A�q���km-+Rm5���M_&�Ӹ�n^�ym٩i�������c�9�.[��0ج�uҭ��cZ�U�6m�*���m6�ڈ)�Z�#�n ,��jT�T�!?d��h0b8����#)�BɄHA���o>�"�j�X��������x���q����078��s5� �>��P���@���ʩal@��Ab H�'J,P��S �H���J��jxsnyD�?�.�>�����(��z��H����箷m�mrܿ#z��d��6�nt�T�m\ۚ�+~v��/����5���5Q�X��U�Qk�k�_:ݚ��~#4V�k��4b�JI*ѫRM6���u��#@hi!�$QA�Ddy�A����ƍ!����kopГ0Yܩr��h$R0�ˍY#*Q� /�d#)�A�L�f#4�@�P��ahH_DYD*�����Q8�А����*}�=�c�L-�����8̮���� #4�Wd%�K�I#4�qؾ� ��ةq�l���y�o���T#<�"i���z(�#)�Bl*�EH,�l�f� 9���$˸�K�P���L!��g,.�.Xr,��*�O�/dT��g_��QG]���|���M�W1��KXn��Z��;:�2exKNuv˰�F�ȴ�1�F��$�5���F���l�~�}J�6�13Ea� �0/���7����d�]�������5J*�DB�9��):�������/�G���hh�v$�9������5Nən]��� e�� Ȇr��u��yL��Ʈ� ��p������d�9I11����Ja� G{���chi���d��;�T�4�08ZH�F�Gw�dr4Yb$!��#��`��DO$����C�UU�.� � � ��4�Ox��m�P��:x�GR�� *=�8�"���Ň 1�0-�Tݖ��Zj���o�?.8��I������ �u%\�9�/�l�hд��I�}^�q��r1��#<�:�b5)�&����M���#<2����M�l��,� ��b( „#4����Mt�(ʩN�^������[>�W��8?V����Ga ���#<}2��#Ti�X&@~�t�Fu�Q���幢�M�@�6�<c�L�:���ec*�ꆦN��>��*��&�׍o�1P�|T������p�Hq�F�dU�R#4��,͚f*���[l�4ؓX(�b�����BAc3i#4����ܪ2V-��M"�B�j�kR��F,UM�ʲѵ��m�*�����ER��&�Vh�j��[Q�ii��[�_��C�A���?2��u6$vU�Ԑ����!ᒩ����D"#4AYm��m�klmu5�r۳=�^£���k�m�޴�!8 �z����p@��5#)�ȵ��c'R�k_.��~�A?����q_V�M��T\��Hƚa���!H�}R� `�̓�2B֠0�B{���Gl����Dok�C�M<S��>)@J 5#)Ogʀ;�#4G�9+�?���߾/�&鴉WN%�S�-'�����yv�m�t����#)�2 ^ԶH`A��Bb�mx��Q�;������i���5� �4b@�>�d�u M�������ZN(6ҙ��K��66�c��;�FF��8���AL�V a`f]B�"���޳�y�#<�$Fk��_����WǜT�\&��8C�J�A�q���Q]Q1ܟ��sDC�v�;��o�#)1l���t%�zɤva�o�A3��H�8��rE-d`Ƒ�@�~�t���ѫ �Z��E`���$bD�!�� �Vjqj�(��r0cj�<??���^C�������1a��:��*����"鏓��]��"���Rb!��[��q�N��bK�mI���1��:*�#<"* �� p�;q�Wo���F��$!=�G��V����:>y��ʳ�i��9z�X��42j��hL^��Ra�v����v�_/�||M1�m��>��ًR%PeE�A"Ecb�a��^j{��0$���Z;� {ҬD�������5�>-���U���,ͿW]E����B4�#<�� �$m��>__�P� �x�01Z����!wDY$RF��ƑYZ@44 ��� �-@һ�1�&�i񸷓���0��&?P*Йt � x�UE#)[�U�Y�H�9R^��C����P��p��m��ٕ4H�0��A\�(@��#�:�E#�^���ݜ�:<s/T��4Jq�:����x�ڜ��mQ�80�â� �L�z� ^1���fǖ��g0�%������� D#)H�,w����h�������M�jj^�vj�A��`��H�R��ef�����. ׭Q�:�0?�J]}�;{|��TH tB�1IE�E���f��a���L;� ��R��6j#40 FL³��F[�NA�*=��6;6�;MH�<� ��q�|�p��EJ��ۮ�ɲU�^���^w��4��S='�'ć1���S�7T����Wl��$�"2)�`�.�B�<�+�)�����~����D�e`�=��w(�(|��DC����ȇY��]�j m3�� y '/KG ���VA�_�0���(m�6�8Ѥ�k��xʁ��ysL�e�v6��ȱ�T'𯃁LA����*��@��l���o����p�#)�a�ޖN3z���O�A"G�n��h����3ј�� �6�>a�lO�[f)�V�xѸ��[��EUk��t� a"�J�T�E#<B��Ɗ����1�#��y�Q��eaF|�55����'բ$�3�D�9�ö��y�B<��Ԫ(�Y��#4S�6��**�^OF"�6[�H�J<;���h�.��c�թk�}�Z�14�ú���7�e]�AI�ᰡ5r�I칣5\l^��Q[!�욗�/�J����E�� ֐�����i�ه����#:J�#4p}(��F%U<���Z��6����u4�^me���d�)#)� �Fa[���Pu�AOY#)/wq��!��l���6s.�'w�� �x��� ���#<#�B�3�JӫW�,�N�G3��lT18�4����8���0� �8��և�XH&�'M{�>��`(��l8L]�@\8��hl~1M]}~}j�iݞDհð>��k B/��zZM�#<6� xq�l֞@P#yJ"w�Y��@��^:�e�dKi�1W��t�{�y{���%eR�KU��e�w:�i��z�u��) P#4����i��QAl�V_�q�<z´1{1-�#<������I� ��/ª���`-FA&%�A�� ���I)�e"�� 3T1>�2O�8���ܗ{o;Md�\-2��0⸁�f�41�L�����m�����z�]��e��#<`L�d��l3Te#4Y)�|��ے���#�uu|믒Y�X%�10l��Sj�{�,#<Βg#Z"9 VS͝>i���#�x���6��<f�YB@�6j^� 8a�_ߛN�"DDj#4!o,6��ȱn�61� ��>f!�dl�0D"Y�F����p6U"vr�Ba5�J#��F@\2#)��SṴ̈̀�*��������R���(�zDr��l���/@�D�?ѽ��f���4��q�]��:��BR�P $�ȁ恷�m{+4�Tk%lL�lZMmF�K[��~�6��~��թ��6�y\ە�S"@��>�G�"0�pڟ�x�[GH<�,�7u6�������#)C��k5d*>�8��_.�GLp�#)1(<`W�� �|���i �{�li�����󩶵��ו���Ӷګ �LS᡿ʺ4�[4<f8e�����{���f�L$�{�� oٓ�&��ǀ�x��Λ�� d=�S���#<DMD��N�^Ra{l.5�.�լd&�Bdɷ�gӮ��1`X�;�&��Ls�I��@Xp�9�7m�as�%�ca���k����� [E2ܖ�<o̥�r�]0�z|O"�z���k��{m��ڎ<� j� �"��q���K&fG����$��4w_*N�����γD��س�aw�=��kJ(���{��/c�)�X �<TE��Q��bͧ�����=�yA�T|A�ͱ b4�s44��+ҀM���cQ�3#4;k��&�����_T��D�,���MF���x2��4tϱ"��Y�M]�x�=��Z1LGO�����W%���f�8�=̟h�=��(5�44A������� ͯ��g���^��/p�{�z4����Ƙ}�a�;Ԃ�AT{��j+���V�6��� �ՊapDXI���I-�)h rC8n����(��[���1�Kx;$��'�A����h��� ,c�6�"�Q� 12���� ��l '`8).���B#)-1�(�)�H�S �0�3hw#4*Bga��r.x�}|NX�=)�|8:�y�;�G�^wU3 X��+\�ӂ/�1�pL|�����Ӧ@Ӹٹ�Ã�������[CN�7u&m�E�� ��#�#<L�0dN�ѳ׃t0��ZǢ�:i�7qr<�U��Ԗ���7��G�>he�,�$z��ڇq�ӑ�#)�/����%�����H�fk�MϮpi�QMCTj� �̡Z�M�F�A�"�]�ͳMiM�;v��6U�f�]VƊ�e���v���o�l�diR�R�Պֻ~x7;��=�O/"������JV����9Z��nš�����#<m���D���|FRn<s��i>��A���vS�R��;��1�rr��lj�1��I�7�9s�� ��Z5��U��#�+H֢F�p�d�F�Y�{r#4��}�f �r!2�Vy���7A��| ��(e1���m�ّF&������h4#4�7�&� YX�W�A�r����l�|#)��U�p>{U��z}�XȻ!��b�B�դ�@@ G�X�ܻ#4Eص[ٻNw��ۚ��H��@<�N�)?:κ��\������@�V�fpv�x���@fE ğl7|߇��/��M�m��)js�5�4���'zk���7�ְ�@�d>�J�Ҕ��2�WV�#4$�!���� �AR��V #4¬}��1�?U��P�B�����IE`�I�>�o"���#4�.�.��8��������m)��^f����`�@nx����Yt'�;�Ӧ��'ݓ69l��@~=��m�`�����e?���U��I$��6B�W��4f[o�FS�eyo�X2&.��5�#[�Ev� ��A��S��#)�FB�R�:x�l�ؽ���?#�;þ �^�-��:c(����] ����R`Q�͕Yt�^�[�~�� ۿy5 �s��4��fM�Q� �L��<���Y�u��~�L38#<�jw�]CF 8�z�Hj�H=~�h��!)�B��>cE_�=o@ [�~� �,#���A����jzDET�{�[]�0Hm A�+�`Z4}������L[��a�[0�38W^t��W�e�u ��P,��フ��5�\3�a#4��D��H!#<E4�����ަ(_.�.�b��� �=��kc[��T^m��Ej"���Ũڊ�Q�k2��Klm�Q[�V����P�"h؀g���|���� �#44���F"D���0U] �� R�d�����y��B}��z�r!�j%��C���ǫ��`��)5�<ڀ ��q0�`��L�_nhV:�<��� ���x]5t<�#!�x��m� CEli� �2�=��i��qƧ�}��!���e}g�#<�hy��^lMy��6)�#)� ��������:��wN�#<�ß�b4�w���_�Rj��R�:ckƦ����p9w碗Ȑ�:������T@;�� y��͠Ƣ7@� ���'�t���1�\��$�l�@>g'��)�����O�Ã!Y%W�-U-٢z,{�j����r/H�eBQ���� Z<`�T-- ��.�(�p`ip�H�1]ND�� �'�"���h� ��$��W�C�0�*HP:����CxbFH!�*�EV�T���}���|������5�a�k�WS��w]���#<c&���H�=i�^�����⭖��=43��*��,'+�j��V�s�0�HI�#<�T�r����١��P���we�ޚO#41,Z d/��Xk��‚�� x;Uc���?�OB����@�^���#)�q~F���!D����:�:˩��u9�z2/�-]��e�#<��֍,�!�+WD�d)���U#<#@�ڛ������L#4f\h1�F�! ���#<�la��u����r41�!HD0IVQ�ECE�\]�kM#)�cW?!�l�B�o �ZhA&2L`�(�DJ0'��W}t���ԥ�.q�Z��ǨP��kE��T���.�5!V����Ew�q���p�Ꙣ~�j�b�L�IuwfIh�[��_�WL�6M�&�:I����n�R��k�{���*�_<�*�+�k�A뮥�սMj"�&�4�fdƄ+1�fW�w�]�iii�l��MCB�F�5I!�+,��U3"�SR����j�s��nL��n޺�sGe�����z&�#<����o��Ϧl353P�6RE�t�M��40%#lFh 66�j��\DJ��<&�Z��""�T0`7�⚱���cy�S��koRl֢�60q�\��9�i�Ï���0�`�&[ jm2�ē�DŽ�m�ϕG6�D8���)~\4��1�e6���Fچ4�rg���4�֣Z j���Q�F���uڻ�zj�����z��D6�o�7IE��qY��0�@��Z��`�.�>�4� `��Tұ4�1��h��kdNԭ�c��#<�Ѳ�|�^��r�_ ޞ�2�Q��"J���"s[i�m&�n�&41��[Zc �l���#O#ňjٔ���ˆ����4�����R�,!14�bnd����66�!�������i�l(A�����Zu��M�74���yS5�����knos�QS嵤��a��1�'�3��O��d��T��[F8fc/hF�#4$n��#<ᴃY�[f7��bU6�zB�ABi���Z�������o�o�5C?�}����K.�:#)�G����a� ��{�6��8�$���a~�<��{t��c��ގq&�EFcm��#<�B����ǠQu� C�Am)������%Sj�|e��=ӎ.BbS|^7�Ęv��E�f�Uk1�U�'J����HF [��#����k�Xa�#<aCd7Z���yX@��T�0@�#4����H�؛I�#)��n2�2�ݶ�� mM��~zo�p�hShA�(���g1�� `QQ��}��s�sh���U뫅�-�İ��(!x���lF��00�ňc�a#<6��](���S�a�-�����=Og�\p <,�6�e�RtN�<��D���Q ���m�7�����L��`��R9��>r�Ό�^���b�k]ZQ��'�K ��st����Wi�� ��^�MH����z� � G�^���X�o���%+ݑ���.C��2��9�9����F->J�f�ٚ� �YVd���Xh�¼tv�̾g�e�R�e���_�\!�J-����I!��Q��S��W��#4o=M�s)�E�;�4�D�ё�0"�'������mU�Pń0�zH�6"'�����t��p�'M��1Y �S���VB���#<�����^!�C[r��F�z���(��^�t%�he�� �� �>�ig#�V��1�Φs��br��=٨B���~n@y2c�����*c6�i���%�C�����~ې熙H��d�߿���kt��J;C3J4i%bZN ��tL!��2"�"����;�>:����H"#),�j���J�nj� ��FF�З��D�5�� ��dFYG|LQ.UU����]���������J��We�ª�^t�y��[��7M]��mݺd7.���S�i,EH��T�nqݷwm�Ԛ�����^my�[I���n���5�-Ql���!�p������i~H-���h�H�/��}Md[BQ#4 �V��]j��e6��2]�)�!�eF�y�H$!J7B� I:��M�Q#<4 � ED���k�k���_��j��By��X�DɒP#<!B1(L"�EEjRIF����t�#4٢� ��+ƨ��e5�ضĚ�hэ3ZM�)��+M"�ҳ@l�j����to�Ů�'������n %��,�I�B6J֢��A�ڣhMHh����ߧ������k�y<� z�z�X�����gi`�"�n��g m��Z�"^&P3�΍�4T�"�16�M6���4m�9���+�4kWJ�ح�#4V,����Aj4�2��&��n���W]�b5��d4��[��[���6֚�W�ݚ�Ѩ�m���ٺ��no����v�N$bLr#)�xI����C��!0db�$#�M����]�qC�a$� ��2#4���u��<����q��WI�5"$��50,�c�Xa�p��v~�#)��;�{BU9rE��i ���„�u=��R�0u�C*1B}3M�����L��]��m��{TɆ27?;,q��et̓MB`�#<a!r�ڕ�8$� �2�\�};aƩmw�8��XdS%����>�]r1���J��&5lc&+'M�9��Ɏ0J#<wu�Ryh�d�J�.�W���h��Œ�a�1%�O28��%�$��0��Bvs��-6݈!Bs��@�� �kLL���QF�S.L4�w{���X*�);�V1"�̝X#<N���ێ�����;G�a;w����C����<����gŠ)����0lT��T�d |���Uy��:BS��x�>q�D�DY5�Ԗ߆���Ҳb�o�y��04�@FBKP$�4 $H(� ���h�Z ��l0V������+��!��(�Ϯ�$#)C$��s���F~����c��2"�,M�m5�Y��u�V�\̪� }�X`@ zw���2 [X�[T�.!�Sb�ڊE���?�l����\�[��Dw�Y!U�&1�jֽ���Y*��WlS�w�u�$�˚��m��<d�E#)M4���]�-k7p�xi:��dB��l mkV�)V��aV���#<!DQ#<#<��,@�|�!��4��+�����"� ! r��IѤ�H���.$'��X�A#y7�$<����� ��JB�������}�;[A�������Κ�x��ŗr����Fd� 4c�BZ��3�n�nh���Tx�DD e��UQD��� ��$6����<���F0���+Pby��o�^�5̣t?;���Q�F�\CcJ��2b�9̟���z*��a�Ut����V�O�^�5#�����<�]k>N�����$�gnM����]��r��El�G)I�{��!���S�8�u��p#4�<�R��"C]!#<�%��:��Q=F�d�o� A9{�5s<��8u�`ݖ�<�#<�6�LCZ��x`�Pmg��Qa�;{D2e!��D�#<�F���0]M������&#4���Y�Y̯7=}�����] Bb?��z�@�p�[}�=S��{5� �+‹�&�<2�E��(����ߜ3�M��L��\֮ԣ��e�*��4��%6Q��]嵐#<�PC�d�����\M���xJ��wU�ml&VvV�#������W��ğ,� �.���#4]R��WJ:���6%(���b� "S3P�)Z=*楥Rm��/7������nS�t�޻q�]��tK��ۤWvۯ-�M��E-��n�]�fX���Y��#��TԬ����M��m��6��6�۴�%o.�7Njs3��"��۠殲�l�k9Z����j�m��֗��oX�U��#<XJz �#4���R*f��ҸD��a�X��#)�pw��`�j�� �#<'k,���.���Q�ϯ5���ت�����YC��Ab��L{�!=A�i�e�(|:�`�V�>��I�P�A����w�ͤ:�����$L�V�33M��뚫�Z��D��"����]�iO� �6|~}������B $b�ĉ�z�}#<Y�A���]�+�]���j�;�9K6�2�oK^[�o����i��B�g�!��ÉV�=?����|[nmҞ�o�bA;�%$͈���6��V��P�H(�� Tq#I���!F���zcV�]��f����}K����۳"E��AƂ���?�j�&!J|R��-�Pc?ߧ�׭"#)����Jı���?��&��X��"�g@ؙ�ވ�W�V��U5l١��ȩ#)@ ��y��&琢��#��!+ )FH��A�����L�(#~k�����S\�t�-37��1H���B88�8"�#<�TZ�f��wQ�]H�C�$(3��;.P�\�@�~'�\j�U*�hk,l�p���z=7;���S��_���8� �Sȵ^�����NX@@еn�6���dɞE���iU���Mu��f�+�w')#4ݑE�IHܓ�q8zq�ɮ��Y�7��g$����*酻��=$��v��r�V�"_F��8��L+�p� � �,aY$�Q�N< En�n�_n��vc�I[G�Q���H��m.!)�B"B06�#)lO[�Tn�� Q@1�*�3��ݲ�y��U���-o*�?����ܩ-�B!$-�X��.#)�~�g��� ��rq+U|\I���P��ʫ0�`�h(#<b@&�(2jp� ��Y%z�!Z�5��F�}uAk#))� )�X39�L��b lAi@BDVADn��h� ��f����`��&����@��#)�Rnݸ5�� h�3&ˀ��M�#<��Ý�R�{��v� _���v����� �X�#)�4�5�H$b%� �ܮ�=�U0���S�a�#<��$c l ��:������#)�@��P��*�B+���V�*�ҿTi�W_-;b�D[0l�A~#4�N��$b+C�B�s�xOTM��y����$�O�O��"mx�}?zo�XLѵ�#4YJ�ŴBX;������a����]?񟻹�n�~f/���CC����H75̖�ɮ����9���=�{�P�� $jmW�G�Q�+m�!+������8fٵ�"� 켼��5,�8#4T*�«m��v�(�O��s^o#<Y�=a�H���D<�7��3�<�9`T���h�)PC^�?<���#4�I�吂��d4P$TS5 Ez�i���=������#<��@J@�(|�Ń�`؛��s��_�0�tazòn������X�7�!���}�ݿn��Ғh��6#<��'��cg�9(��>`��  p ��ϫ��G��zx�e���n���?�iD���Ҽ�~�Ʒ�K.BmA��模5!!9B4����a��O�+��W�}�4��'����P�-�ؔj_5;DL�D�#)�N �8G-V$� �&q�9q�бws]<#�2����|LX��.�́5>�.OC2�\$Dt���#<0#)�AQ��64V�|�S�v���z�o,��q�\\����UT�k��#)�)p|�� \Y%�`a�h�ʒB,��n^^&��)�I���(�+��H�����w�|������N[���X>�-#<���#�U7c��w)�[��g��J�OJ�[��G���d��nR��%�-2=�R�dп���1�.�X��SK��ߒ���w�Ȥr�Ѳ<`�6����#)�d #<�#30]̕�!�(�i4b�����Nie���K�ߧt������2��x�k�<uo+��[E(��=g����O4������``�t��5�<� wيǩ��ŵ�� f��l"� ��[��5��œ�h���z�8��Wr�M�+0����ǻ�5A5c6#<q��#<4��5�t�{�#4����McAT�'� �G�$ً�Cg��~��#<���샬���;3�����׍�ֈ��p�#<��C1�Рp��a�H�+ ��%2 %A2��xY�< [�����飾R��V�����#<&T Pn�F�s�T�e�p���8<畸��pȾ��v�t��mL�#)�U%���F��ih�Ĩ5��j8��;g��fX�����!3���n�I�;�x+Q�%D��x5$3q���Q��u716 ��1[�M���H��a?�(��,�c���Cu��� ~�o�[���jUUf�omi3��g܄��l\0�J�Y$��m�\���w�/�ܷ��/%q;��{Z��eb�|�&�m�%�����n�|���㉥��ީ��Q�l^�����X/~�n��8mL�(�b*�١AؾE�w��y3�Bhd�#<���Gq�g<��g��X� VŦ�1|�(%R&�1�Ç<�s���T��LU`�@����K ��Q����s��N��b��}�/;�ĉ�$ta+�^��pUBfM�t�ؽ�1�#444�ϻF������V�oHX��z��,L(�$�q�ߨ�ㅓ��l�!��#4���vڣD�]��q�D��=J/�<4�W�������0�&���@Cpo~@݀D�HȊ%�## ���B!�:a��P��X8>��Y�$A�$H�(wn/Wة㿖H�V��W��#26�M�q �F�1����D��z|��1������@W�j90�[�x�ķnE��(�zNޱp�Z;��Ψ��C��l���꽂ӹ���!�6�#)#�I_'�� S�յm�h;4��s�h�"��d���=��U�;؈\NtHH����ݼ�W\��L:q��(���Q�>�*dh����mf�>'o c�0>] &8�R��Z���yc�(�DaĠ(��#)��`��˙��M�i�,����q$p8������F����w$ω�]�oh�(4����bqC嵣��-q�<����<�o����">�ltƉa;D��F�"C��l�{�Cȟo><��X�B��J�3�H��P�<�wB��}!�����0=�a��GC<NW� *�<���3?#43'1A��IK�� �8�#<�O�3lhj%�(�Y��8e���=�n�.��]���+f^�_d|K0�1��6��:�΋Q����uV���@�%sK�t�u�Mßc���1��c&�U^͆(<�ÞC�4��Ne��V�s� �*�R94P���+�W׶������X���V�v�66 ����yy_��az*����b�(�.P R�e�%1ܝt�7���ƚ��:p@/�N�#)�u�uE5Y��� ��`'ո�|{�v�7��#<  $���|lƧ�n�G ��B�����{���6Ꚕ�������<yCi Ha�'kQFIdM l�|2�����x��Ҹõ8t՗��b>��a�+���r ߍb,�#<jzs�U�?���F5����X�B *$RAB3�v� ��Z�H�0܇5�b�ە��B�J)�sS9lTX��o��/���o�x�B�2Lђ"�����LB�BbcFQ��)�2lM#Ad��l@���$�����y��D�y�댱T>"n��C^�8�t�#X0p�f�C��.:3Sa�!Lj#8X�.o�#`!` {:ŕ�R���O�g��!@�4*T�|—^Y�c�L�T��R5*��l��[Ϝ��ov^�=�i�N����hbcm�3��ޗ ��`-��MUNB7TRzk6��^�\qKP�����X,L� ��o��[hM[��<�~����j�*�G#�K��1��\kF\�͔#Q��<j�A�y�2e�w��kƎ��̅AďS��#3�Q@�k-�������nsnuo+��ao�Z5k���7��c�*M�;����#+����77T�U�;�{�H�I#<Q�6�3|�c7�P`�L)��@2 � 9�) �1���D@�b�&"�Tm�#�(��J�K}*�II�HF�6�n\�\��ܾ����p�����1y5�i��>O#)SDHE�l�*GSCDȊj����.�"~mD��e���s4԰��NW��8Y��Ul3I�XM`t��ī A�(ҚL3�y1�D��8�q#4��R���4�*��QH��hf(<�V��kf��#Q��)"E�I6 �F��A4����#4.���D�yw���H��U��m�DH#<S�a�`�f�&j���>E�0\��n��cfӌ�q�B1�[ji�4�i���@Y�lc`ۆ�hV���2��a�:U���ܪ�:R\���+���Ͱ���&���v�[z��yw�$�#<�"e(l��a)F�G pn(�5�����4#4���n��o�n�**��Iv���3�iR #<�������k7��Lc#�ӷ�_Tr�#4��ѻˏ�rWGF�'b̥+H�;#U�� HVI�DJL���n��������H-�>�Ԛ�)�Z�Wl��I�� � 81[|�k��D#4���m��Y��#mU��pf��B ѱ��fXV�ę�J#4��B�|G1- `�i�>�#4*�4�����*\`V!��daYF�N�cZN0�C"N�p."�Ȯ�2��h2Vj�RdgH�֌��t�RP M>���&�jb%��FR�S+j��,b�i��Q�U�RS,JW:�&c&�C#4��L�� �0�)�$c ]B0��MX�A�r �F4opA�EQQϾi�8-��P�6��6��N�f��[1#<X-��A�c��7�Q����>ƌ�Q&�O���Q7�Z��8��h�Ō���_Z͚5����rb&���A�ǹ�VG���5�C�d|I�#[hZ�%|�c#4��F&�w�E���{�a R��:�$ g�֊ ���B#4�TEX�R�G�:�\�%�E��H�Ƶɬb���lB�2"i�?F�8��y�x�F#4UD@!�DL�R�" ��� Q%�:�Ue��j��kd�2k13X(#EI�BF����Yh�H�(. MjP���Ok�����K��Ѯ��wu�?��z�Q�k�ut���q>\��5H�-njN�D@��#)�jl�f�&M[�k���.����J~�?ӊ��� ��Z:�S�~��a։QE�� ��(��as�Q�ao���}�P8GHOEY���V�I'"{��#)�E��H�`$���hDK�ם윤a���L��_#)a4'7]>�8��J��@�"������Æ�cr�'�5g�v��#4��UW��>I��b���O�����r�툋k�����!��$#< 3�CEPl2�؝<���7 �q"���Sy$��U��ؐ575ݚ9`�4r�t#�8K��4.|���������6Pc��%�p��9���]y[�[��"Έ�0|^{��nNc���<�X'�5#)���pn��������|��Y�m����xf��DJ�>�W��` �B��r���L��. �C#4QBC ���V[��p����:�����6������� 1��A.��~qO��S���{#4�O�>��,���M��<C� $�G�zw��#<�9��'0��X x�O6�'d�|NZ��#@��ߜ#4�bv�)�^���?;�'�HX��v]ޥ�g��~CM";ne�\:I��T �[���S,ښ'���]�S p�&�imH��4��"a=s��,I�iK�*�mk�V;,o(@��~����*�2��1B�X< ��X2h��=�+P��LR�d �Z��j���y~�.�a̋\��U�E���ޜgCF�<X�)#4\�R@�mE��mg��#4Α9`8���jI�#)��9 ���P��da���Z l�m6�(�ל���-�*݉ncZ��U�G+��6��!�TZ�mr�k���j�N��hړZ���5 L$j�!�Kp�D8�swID��D��E�+�a�GaF%g�=�b̄sf#"�c��>lc9���������:�q0�qA"�#)W� #)d��(G��h��EȀT�������U{��?o��'yUO��=�#<�W/ĠQ519�Y}���6}��$�sAw�4�.�#c�뗈��M D���$n" `�*���_:E-.�#���`�$T!0RKm-v�&�t���"t֭�K�F#)���v�G�Q#4�f��~�mif�h�k$j@@!D#���'�B�Pt#�Z�z�O�!�҃���0�C C@��B?D"��﬏-��>>��#4�g#)@ܙ��ɷ����ͤ�)��Ƶ���#4�X��28 �ubWҤ��נd�P����,��#<g�2 B��IT�m$��C�ZY �F�b�#<�4Z����)&ѡM��ڋkڍ�M�i�J4jeM��ci6�S[��|�_:A�9%a�H�4�rcA��Hs�0� m6��##q�0�/��ѽ1D��T�F��(��`�4@Q� c,d&Tݯ,Y��.����o2_;�M�$ƃ F l A�)�Q@�#DE��JA*�B"�Z�0_P�Tj����`�&}�!jZD�M�Swt��\;S��v��[��vk^j��R�4�ޛ���އhn* K���e�d�Y��?Ea�O��_����Z\��E*�W������C��������5OС��f(����g��?;C@K.0��%�� wh�Pō��`�0���X� CT���~5�u��4��H���٢�i�XB��+�tQ�@=?����d=P=#)�w��(�0�cU���n��-�.ʛJf@����m\�m�Z-�Ѷ�-l�#<�Guu/��`lA�'��1���!�I"�/Ou��[��R������ڲb ��u�#����� ��BF���"���i�N͛�5���} ���u �h9� �ȥ/���5�5‘��"�i*l����]�u}�%��xM��AB�~�;���Z�<���%�*��&�����ׯB~:��o���B�D��Z��)�(Iٴ/��߇�-���'�qa��� }�anT���y�D $�lA�$�DP�^:�P������g�ڑ;ws;����w��������r�P�@:�|t��kF@ ѷ��,f������:�V`���1/�] uƶݙ� N˹�4g��Y��[���� ��c�?\���� �����P4�8����D�@�m�����m��©�J�r�3����"���"x�� 6U�JaA;J�j���#4k��P�����\]t����,�-��Z#)�C;u��4�����U�2NC��׽-�k�IB�T$��褦C��� ��Ȧ��9@J%��?��q�'!H�7ׇ%u<��g��~��u���P-�R�|"�Y�]��-�o�N %U�5`B�yۢ�j�X�t=7�J›��]J D�Vk �JA�1q�^8� 5�*P�l�׮��)ihJz% ��V�:d9�&K\߄ Sf�~u�=�3�K6S<��E�#<C�Z/�ɨI��b3���0�.�����Eh|�t�"P��j���$�:�U��5&5�6����he$�����<+;˛kiȁM�sR����C�m�f�� 虘����8k��[������ t��ty[��6���qj#Y�EU*�M9Y�gr��o:,�7Œ#4��c: | ;�/�96�jb_Ce�����6l��-:���3ǭ�<i6!�Z9���b�RѤn�97�`gg�\z׆�69�{2�K텯 �Z���T��i�3��#4��� y�yNQO$�����G�q�Ľ%{f�q��%�<`hP��֓�Q{��3�-�;����seS�{e9j��j��h�P��^�"n����2�ږ�ӗ�9������z�����Yv��pb�s%.����j˴.��#")c�fGw�?x���Z��E�J�g �ט������n����x���K�81�l��uڎ��ۓ5Խ|p�I5=����t��#{5�1�����tܧY\�����&;�0��1�!�;\��r��A��n���/v]����b�#4��т�A#<��B�~�4rhj�z�f'�@?8 �PA"/�5�9��r�B �X�6ƥ-��JF]5&#z^.W���函`6Θ,h�/Y�ږhL�1G}���K�Mzw�$�&��f|��a��C��Ŷv�~��Bs� 3%�R\ ��2O��������뢣p|�{�)�'Ǔ�s�/Y�����@^��!��(��!�c�v)�#40�kwF�1����X��r �y`�(��^XF\�Ě*����b�jЃ��:(�74�V��u�^8# Rg��p3�)�q�.�l��#<Hmm��̅^f����vC��W�שA� [�#4dB����[��+�k�z�}m���rQ���<Ir�`ܝrFbHu ;q`5?[<�q{��yiO�b-�a@�U#<3��[��n`��)�C3���<u%��*%0cq�#)B#)),"���6���Z�A~{k�vx�;Y��!if��aZ6#����%J+�i~�E33y�q��v�) a�yLp5k�~�N6Q�f�t�X*���&[q2l�eyw�G��rw��x�R�q@yK#)�mShԁo�&�� �!D�Tݹ��&Q�`�D��O|[_ ���A0p��S �J���ΉM�bzwR@�$AE9,8:�.���)}�PI�����ku�W 5�y 1[mů0zмmr��‡�*(ESiDUMM9qφn']�@��1Y�l\o�j���i���3��\�d��ⷡ� ��d�>�e5#�ǁ@��$vn�K'���5q��˹�&�CR�/L#4�1-�"x,�?�Ñ#<,���:��}/��6#��v؝��3�Z}�t�h��xwu��xt���a?�.pn1�YVAH�_JR�+��|z���I�c�I����7ZUu��ds|>��sN���+b�FB��}]�E=]���-5��A�w�Uٹgi;zu=38�#4�+�6����i��jz�p���0v�ht.뮲������D�]z��Sq�s�����s�SV�q��ٻ0�����/����~��ڦ�w6漥�� ��5�];ϙ~/�4wBߢ�ު|�4A���Mv����8p�|��_8���_�������o�}�"�y�?X�UY�2%M�>_Y�ɡ�)�!t�Հi�(~�:�t �������Y��S�y~|�����,:��ʰ6w�U 1mrD@f��5���`^��#4�Ҍ����xt4|���|#)�x׀�Oa�gJ��[ �Wv-�3�7ող��-�!�U��� hG�aB��C=��#)�\�b��YwqX�7�{&� �sV��,rblm�:��$ǔ,,��g �=h�z {��ʤj��P�ۊ�B�v��5��nT�Z�%9���� qCLAR6���� �fr�Q �X)�#1d��=��+��I��a�����NA#)`�-s�#)ose �=�A~�]�����0نn��5�t�N��Cw��0�dR��D �a�ɏ��d؏�y���K{ ~� � �D���O�x#4�#4�� ��m��R�����N�$� "^����{ `qC.��mN�U�I*k��әX�����Z<�r���ZsT�Ht�s�pq�|�ڣ�*�J�l���� J1~3u�ku����ɡ�t�C�rH&5����ؔC�bj�$"�"�CIS�W{|��[�|ksFѩ)6�sk��;�I�i���u��Sv%J�\�@@#<"�aKAEF�RDV��QG���,P�L���@�X,m5��ఠ�QFB�����<�g����~wn�a#4R�L#)nm�����&0-�m%�N�AQ��6_�8��o+yW#�ƴڵ�උU�[W6������m�֟�܍k?�#4&�co����Ϻ����-N��_��c����#<�D2��}p�� �m�\� �#4h���?�:�1h�]�2����O�7�lT�[q���3�+��#<R��א�K�!#<HR U�e!�)TE|���opǭj�Ƴ�, ,�Ť��Ԑq͐‰��m6�5$R�.�+M1�@%r�Q�J ��2m�v�H�B���-��p��a#<�Ͷ����0�)d� 2m �pb#<e��. S5��2&�i���{S�#<C�3�ͅKܺ��MD����U�=6��S �!���I��Z����η�V�m��6X*#<;F�ȋ��SJ��M��4FL16���m��3�k���뜝۵�²��x��� �6 �c�Z�Q�%q�1��x��b�� kU�����dP�`M,�7�2���&GJ�f����˩D� ����!��:���siH��N��ӆ�;j8����#)ʒj��6Q��)28 F0���"��A�'#4[��kH��9:C%i��!Ѹe)�#<��4n#<hp�M615�Z6�qm��c��z�a�[ �t��C :�6EJ��g�CW Wki5a�j�������c1��s�?�yo�n!1�a:���aB#i�:���2��7$~�^U*|�t�S#<���cgi�L�v�O#7���}s�j�2I ���q� ����԰�!��Z1�KZ&�c\�O&g6�3s0t��PQ(XS ��Z��E�0����u�A�"Q[Y��J<AZ��� a�*Q��b,`cJ�ȩ�Q��t�E�xi�`n �|�W�Zhx���D�鷋 fR�q �@i�X3;�ޘ2�O��!�,�c��xw }�f·�j���#42j�#)���83V#<�ebkw�Z!� ��[�vJ�#kS�(i���Ę��H7γ�Y�^D(@�`�dYr �&��Ͳٷ�`y�Pi�>I��j�20D��J;K.� &��ɞ��^���uJ�1�&���_�e&�7���:�JFU#�&߁�(�k�#)��}wLH~f44[���ýTwlf�`���7��`o��W��۱G���������ooT�Q�M�����gC��1���$��+�)�>@*���V��@��Y^��H%�������驇 5��'��Kd����U��݂��!R�ɷCl���. ��z_�ǫCUDT��-���0ϯy��]��1�5 �$�i�9;�f��ϯƾ��dX�_��mx�c�z@�v�pi8�mFrF��T�3HRC����t����*:����K! �{B.O#)T0�/$j:��ͽ�yY+���*����o6ɍ�.���b�ݪ���@〘´!*@��#<���#):%#4d�"<Lґ���w][y��4F5RM������1%D��J���Mcl��X�����F͕�#-#<Y�M)6JQL�*#CZ#<�LKFh�4R�E*�a��#4��k��DҁFAT�(�DD�C�Q�+�0A�Ñ�چ"�"q���;Y�i���}��f�p �8��?(q$��7��p ��_}�� ����>���jGي�*�� >���8e��,F�`���f��#)�9F׵� �Ҵ�@-Bj{(D��'ש�ޛn���w�w� $ �U��f��1#4�һN�����܂#�aK1<�e�B蜍-n�����}ں�k���1TʩJ��Ц�Vl[zm�,d�+2�1z��k ��p���d�#<&�b�DmIkE��k���w[�u#<MF�m��g麮c+�ku�|����O�,�dd�d2CՆL��PaT���g���[4�E��L�*ˮ7����5�g��X�Dy#<��#4���ˣU�4�ˋ��C��82V�ԥV��<DP6`�`������Vjl"��#)�e�� h�8�a�i0�ɖŋC4Kę����k�;F2�њ���K��$��j鎦�����r�7�t\co(����Ei��Ek�A�ȬH� ��uQ�2�d�<���ꗙD����%⍡�T�m�8D6^f$B���m~#4n�1+��c}#�Fu�De���f�g���j�Uʨ��SyYRw|w�0�E��u�#�Ob�b�� �Q2JAB]U 4B�U&B�&-�@����Ҙ� aP�-�<�>vII$��� ������I���<�#)�)\2n�i��@#)�Oke=��f���+#)�>�����g�&���4LG�O�1L~���Ȧb��3� k��%��M������r~'0�G`:��Z��j����F��O��R�\2�yPB�D����D����v������]��U��FX�ڃ�b�4z]�1Y��yc*�7߭��*��!�X�t:��*o}Aa4�mE�60H�#��N6�$OP�y|�� /]#4.��t[���WF������n�1��q���7Bhc�dʆ�����$���(.�G�-˚�[��i�1�hE�zO]�TM��*ꇉ�e��K�}Y��hp��N���k��羪$��� �X�Ɗ��� ��BH-)Q �b�����On��3>�_�M˰#4����9;�Jbb��(Ɠ�uR�nYG�����ا��5憦��KK&^eA�k��[#4"[@�����@�_�ibN¬y�4p���- _��1#4݃��5��w?��4a��2O��A�T ����L|�н�Qgy3/�����`TCB��<��P��.�����hk ��le$��3����f��u�!U�E���H�cku`�OX�v�F�ޓ�� ��Խ7�`\�x2r��#)Alj)�����o��q{� ����¼Z\��u4���L�]E�+ù�*�񇃍c���!`�֥�G9)�d���#4���Ĉ�"����#<�ЉJwcL]��4��g���uU��͕U��!l4��Ah#)��!H�C]�����#)�M} l�+rK�(�QQh�dI��)��w&�F����`�� ��2���<I�E��i.�wp+X=0銰l|(C<�/x���6o���s��Nf��f���7Ԁ��r#4���cdJq�@j2m%��Ξ�uz�'|#=s�LaZ�R}Iǧ�0���t��� �eY��H��K�Pfw��\�bA)���o�J��E�W�������6g#)�@݆1�FZy�{����D*�T��za�k��0� �&6+��L��~N(]v�V�#4 Bls/, =C�����W���3���@�;S&��{z���ﺆm�L0�c�� ��ô�l)[���[!20ɨ �� 3�ED��x]�v������^�{�%��LO\q2���9��F�a�&\kn�Lp�f�����1�)�@.�"T�[��CX��<#4��p"� � �#4�#<������k5b���� P1BV X�Ir �#<�J8A��d;�є#<��2�����3R�c�F][���W$���;b"�� �2���vu*9i#��Q��G�R�F�f#���SR۴X�E���"�u���eA��-s�凙D�� � �m�m2�t 5���8-�2a;H�P�R�-�I�ҥ�V��z����ӱQ�LĚ�#)A�m���L\�#Ё�:y1���_;��}NQ [`��/�Ȝ�䘆n#4��#4����Ҟ�q������6�&��-�m���!��C���vy��l��d�.��e�ψ[m$�KYq(tS��I.���$[!uv�y��� pBs����H�`�燍��14ª#w��y��I-��>�pz8���Wf���d�p�����_��d�-�l�pte ��Qఱz�Z���P󉮽�5�x'�X��s&�i�t�J�k��#<��eP#(k��+r��5�k����L5��7�H\�*8�8r"�ᗥ�[�-l�y��L�MFU��E>�KKK#<KGE�ܸ��뜢��"2pk�*�"�}*e� č�f��7�m�t���aq���..g|���Xw0d��:�m͓t���@ᾄA�%q�HF����"8��Wn�4i����X��#4��pI�H&Y@�8��M �#)�ilb,(B�#)F�����2�8`��b�V�\��I ���+ࣣYVHk��l�ofMha��/��P�)�c$:r��v�sg�r0�Z���]l�(vi���Slk�L�k�m������C�;�SJ:R�7NI����>xM�Y��S7Q#4�?�j�O��G=�}Շ�Id0$ �bS?��l��:ǒ��]<�)��!�r���ǥ�՝#�qr�ȹ�a�oS���F�$�M��p�L'��}4������xh�L&GD$�KXr����M�v+�J�.�jQ�\��,P��.&��ƯϭѠl:j'F�ZE6�}Qc�lt� ��K�c�./��#���4&�>9�T�L���E�zM��g#4���d:���vh���͆�g�h�8�� )2)�CB�B�h;x�=F�gM�3�9i��dT�CYC������ԁ�2�!����Q���ՄR�;�m�8q���&GND���^na����@���״㶯�&��Զ���O]nN2At��Sw�{۶���,1�s�P� �Z��@�Z4������7����0o9W��g���Q1�JV���t��0l(]�ri�%ɫ�jh�i�S�֚H�����Z|;`A���Hy!��lE�A�f��\�(c.�@d�>ZL_ 0���5�H��!F�01-�@C#<i�����Y��\�nR؊4!�F#V �3�P�p0\`�LA���!�C>񒍤�p5PykaHŲ�R�J��j�j�#4 F#4VJ�pр� ̱U�Lt�XP���35���6�Z��ۋ��5-���/r#<ikn�gt��#4`�QC�#4��G`|�ظ#h�&T�l�$M7� �0gA�Pߏ%X� j�Б�r�g&l9A� 0H��#<�6v4�#<0�e�#�CFD#+#)�rMڂ #4�kbȘ��Mxœ��o��`r8�#4x6��;w���tbDU�l#�ͪ�#<��[bf4��k�m�6��i�T0�� ����{ �G>�#<��2�*"( kի��m$8I�M����7 �/w%� �Ш��! ��#)�Z#) ̈v��ͮ�("��O����w6|����������b#4�_R�k����v���R��gu� жi)e�dED2�#4�� ���fy�E�����4��<�����,#< �`^:1έ5赬oK�bMp��缌� \�C)���k���Ut�J�*��Z��� �2R�1B���ȋě�C�4�� z�J#<)g) ��pp�^��c|���z;��w)S�UJ>�Q&v�@�q#H��O��$��gfJyU��"i�'�N�l~ G5i�ϧ{��W7=À��� h�_�. ���/�6��t�7���nh���=,�f����"Ʌ���-άa�A}jz����)�F;���>2�q�U�-m6D���϶��]#4�9D�|5b��"�����}��'vؿR���Rvu�Ȝa��y(��8�f�dk�W#)��JуIF�bB����$J�d� �S5��7&���f�W{��p����ҹq&8� �rK0�$(,�M4��[�5�{��%.Ͷ�o��#4���#)��}qgM�ި^��8�\������ifn�5a�� L��ԫ28B�0d���#c|�vv�����|a���M��#�@zAS�s�S�d|��QP����c'յ�����@�4:H���� ��#4�]�&��L+����ÔGR�u�3[8 A��=<�k�ǁ @�;���x2ӽ��)�c��#4�{�d#=�c9��n�ﺥǷғљ��E��7������k6M#<�#<@��6��KvXaTN+�MN8��B04����<8мR�;�u#NO#4"��3OL�o�5?T̛u���a�x���#)�B60�h@EA�e|-�l룭E5ua��w�� ��.Hl9L�_�󖨲�Af1���1��^H�?��_����b��D��R?3Y� �ש�3�FM.������D�BQ#_�Ƶ��m�L��(�6Ͷ��2md֙_�5���)#HB� 4 �9D����{U��"� ��#��D�Uꈎ��Ɏ��BҤ,8��#)#)=��fk�i\�#4�Xwm��ńR �@X�!������|��b��-Jﻆ@�X0dTdL>0��Z�W�}%;�Q�k�������H��#4��C�;�q֕��ůuBN�s \� 815 p�5�~� 4a�e����)#4�j\�j�D#)Q���� �Q"d�:�bt-�cU�e�\�A3Li����{v�Q�{qحRm���v�v��޽6�<�]o�(o*6� U�Uॅ�1,�r3��� =���=<)a� 0��^�u3'# Ao� !�ʌ;Qz�d��,R�*�f�@�&����F��;]�TnBX� _2I%�u�t��%�2���ϊ���0��Z�YBeJ�� �&Q�׻�&#<#���#4�"n�i�6X�.#)[�P4�&t����M�I,x{>��l|t�7�b�LtK�3�E]s�(�$�#<�X��F�*W����#)�ġ��R@\�c��������s����)�L�k���h�?�އ�d$Qy�B��H,0�"&�$�˳9s��)O_1�=80$��7�\�9���y�<����,�5V�o��|#45�mͤԔ�YTɍ�)C6��[0�B4l�ƫIW�4[W�tƍfͮ� K���7�6�2zc���x����Z6�!���7y�$3@;�g�j6�ϫ1�v�5�糎�Ud`A$AC>�sg̎)��)o �{6�RN\�C���'���A0�*�0#<Tv1~i�Hs>�|zvL�;|=��?�'{�T�jdp��F�K���)�c_g����5��D�:)�q�$l�ӹ0��� 57��n(6��Ւ˹,: 6Pj�e(li�t�g|� 6����X �s$����s�y��.�vN#)�t��I�%-[⍬qnD`�5�b,��KVZ��+��+m���S�'�mC������,̘(�h�`��43Й&�@�m��~���DT�w�d`"���L��0��(��(֑�֭h`}/F��Xa��_�X �d΅a.�n�B͏��#)���E��u���,�{�泻�0�W���E�G�1�"(�$`�&Z�� !���:����N��щ�exQ�mC�"H����P�Q�����wK��ԟ9H#)W�����%�hԍ#<��&F�'� FqY��D��W���� D�1�e �:�$��c����6/,Öl��P��I!�0�d;�����s���ѻl�vs#�L�k�q� ����Ro���x{v��bː�w��ɥ�'�xs�u�؁w��������Z�1�#)�@0#<m%�5�?�����r^�0\���- ��ҧ�#6�k#)#4C�(��G�6��#)��ۢ�,ld $Hd�3=�Q&�h�nu�rF&E��~n��V��7�2�{o|�-� )(�R�0�F(�zn�,��6- ZM�C&\�$�l�������B���"�Ă!�����Oi��(����W��c������J����#<z#<���H�H0���2)  Ȃ#4�{ͣ��6����SbB�R���i#<Y����v���"�~�W濻#4"(щ���f��#< �|}<1�1C`�'Ѐ #)Z"i�BR�#4��$L�@�Ȇ*y�-�2�<*H�Ĝ`�}Ҿi��lƛȭ��E��#�[M(�h1>7�[i6ԋ5�-[��hK�B.�#<���~������������.��I<@<� �����}'�鮦VEMW!aWٮ�b|� X7�b!�"F=��#)ށי\����%E�=H>|���SU�o�{�,AR���"��O�#<"( �+(OD��#4�ńA��?b!ǿ�g�!l���#4oZ��H���� �=�P@P�^h���E��,�*@��#\��֒�-L�l�Zl��PdI �7�_��l�ߜ����ЬƄʍ>4���Å�2�ۡ�V��t!�#<��"hO��^�?q������Lf�`= gvq-)A�/7��v���#��X�3(�:�e�x��-�2@�\������^�cc�Isb>�`��#4��a���0})�� G����<�F�@�MR�4�!)lb��QT�T)h������V��4�ĩ�Fxw�=}�v�4#4QdD�#<�#< "]��ȑ+O�0ra#/*�)bb)�fk�Q�YA(#4�#)y� ��-˳]�v�u"��#)B�����V��V�5E��6X��m�M�����3�:zu7��ݽ���%$�k���4�b"�k54��YcDcx�1��b�X�Q�.<��?((4��������dU�V������Y�fX�Ҏ #�D�&yn�6`�h���da��U�t�/X�u��η�Y���J���#<��vmwvW���k�mKo�\���MٙU�7mE\�C-�,�z�ƚd�����l�*J�����5���U�nJE��" �0(���xE�z�*�4�MI�*��v�����V��6�L̀ȂP� ���4�M �#< ���=����gd�2@�Dh�@U�F�� ��ό�PocpX��оd-�Q����1�-�#)�D-��{��7 p�#k��m�AV/�L�mu�â@���}Z���t!�=W3.F=��?���.#)w�\�~��T��x���=�4.(X�Æ0$6���Xn*Sn�ccѸ�K� �o�/#����F$ǩCUR�W�t��i�Mf�i��1���#3\�; >ݏ8�H��+�R1�x\�Ԙ��S�\�~�5�E���$fL�dZ�.� F��1=>[�]Yw~%��e$���#)D�_���:}�V]�J��[�7����6�#<��h�D�}�@q3�M^-�Os x ��l�!�c2�U>3�n��q��8�d��#<���؆.��҆O���_���`oҊ�������������(�Y�`j���r͔�'S-���&[Hp ����4L�bu��3u0�@�٣0(�~���0���M{ :U�^��ʓh��qJSJ1(@��!ں���7�#4u��!V�Lh��ت�6�p� �w8ځ����dW�2�KD��j2�f�@�]����h˭���\������rȣ��d�U�xMv�e0���hr~���DVg��N����%�A���X�v�#4/�k������f�DdZ=J�+x���Uv���Тcpkc�����Gٓ�)���N��� :D#DF�g���2��D�"�zY��-3\NbBC��Ö��p�������cc�4m��m����c ;�q�H����#)�t8/B �E0=����y{s1 nr1���V��#4��ޫ~N���ڰ ����l�<b����>ܠ��?#4_�x@�A6M���,�A�#)k��|��,�ž�/�c*(���|������w�}�Y�]��4#) ��M_�ʭ�����������������/�o�������m�]������/����ǻ�}^ߟ�|�������G���C�ߓ����������?������~.S�_F����t�����#�����>�}g�?�@#�Q�!�������������pLS"����U!�*�%GT����%jĸ��i�O�����CC�t�!��G���z�wQ$�&f~mo�T�_�UH�坲�k� ���!�=HV�Bc�����{+��"u���nؘ�{#< ����7�m���/t_��&�A�x<���������݄�����9a�F3�#4��ƳߙN�:��C�SO�p �H�#`����R�Ў�ۦq�77]Ҝc�'��|�F]���GM�)AE��L��@��B">#4}e��P'�C��o���E�k���'O���1�t����"�f���,{R�UTޔ�=.&QFiu��|oj� 5�����p����ph�ޱa�v.��u�����4��#4�5*��-ӆ���,�-��]Q,��i���QGj�6ui�sYe��4��TLԱ�[�ƃx��M2�E�����ĘY.�0eջ&BI��ʦ�� k����xK�w�/>�U�q�1�<d1����k%��P�!���c(�L�M��#)�wv(WJb����$H��Z��l^F��1�`����H6�f~9k�eY��cȠ�]��t��:Fƍp� �p��#�qK��Xͼ޳�j�N�UA�<�w��1'��w���n�f�b��Y0N�X�G�$�DL=>XK�Q�p,�#<�^>]�����|6�5�`���@����~>�w�%w\�_>{��3DEATLP�J����}�2���9�FA4��#4nJg�Hҭ�#<���H!.wwz<ǯ�L�]�li$ 0�#45 �:<����Zбk��p�3��@ۙ�bfp������Y '���nLw�焙���X0D��e��Y��w�������iH���������cmK&m�[%c$i!A�*caJ��eB8�����l��J���$���]�i�T�oH("���]�@a.Ԡ�@O`�X|��>�FT7����o�O�?8�oշ}�uc���83��m_/*���*�"(���LY������d���Z.���L!ٶF�K��}�����$2������hxq֐�۔ ��T��#4���P=�Z����N�x�Gm: ����0��A��ڟ!�#4 �9���r_�/m:��9b����<M�f������Z5? �6�#<l��t��3;��Ǧ~�j��ס��*�l �Rj�~V챭�#<ߥ�jJ��j�j-F7���F���|�6ԛ�Z�٭r�}+�uUu��AL���T�gJ�!���Fe�5EaM��ZS�pk�Ҁ��') 8����@�w��b��]��v��&0M����\I�P���r�Y|�,~T�p#< �N����tE�#4$� 3rn�����;�>�GMOEw(j}��_(]O��(�:�)���U�I#) #)H@�,5���M�v��;msf�d��3\-�j�-_�b�5&���ֿ�EFBPqG��v}#0�Y�O�����>={���E��>ߓ���e���=�� a���&��OTO��A���~O��D?.� �����Jkik��*H����>R ��ĩ��{��U#�x��$OX!� �gg�������#)���ˠ�Y������'���m~��<����i�Н�\�<�wtz��m�}n )�=%����O����k�#4���:��1��1����1H@������3�!�L����a�����=g52�s�{K�X�=~_�i���T��_��%�i¡��po�T����һx�UL����q�s��8���iӶ��eec�9��xF�7��Q��8���-�0�aw<?�^���ƙ&HG;���.��@�fĸ@��~>���W�r7�i��>X�|}��V�T�G\[�&g��Ŵ�� N@`��^�����=���Ľ�K�D��ܑN$/��#)
+#<==
diff --git a/.gitignore b/waflib/.gitignore
index 8d35cb3..8d35cb3 100644
--- a/.gitignore
+++ b/waflib/.gitignore
diff --git a/Build.py b/waflib/Build.py
index 1afcba6..1afcba6 100644
--- a/Build.py
+++ b/waflib/Build.py
diff --git a/waflib/COPYING b/waflib/COPYING
new file mode 100644
index 0000000..a4147d2
--- /dev/null
+++ b/waflib/COPYING
@@ -0,0 +1,25 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/ConfigSet.py b/waflib/ConfigSet.py
index b300bb5..b300bb5 100644
--- a/ConfigSet.py
+++ b/waflib/ConfigSet.py
diff --git a/Configure.py b/waflib/Configure.py
index d0a4793..d0a4793 100644
--- a/Configure.py
+++ b/waflib/Configure.py
diff --git a/Context.py b/waflib/Context.py
index bb47c92..bb47c92 100644
--- a/Context.py
+++ b/waflib/Context.py
diff --git a/Errors.py b/waflib/Errors.py
index bf75c1b..bf75c1b 100644
--- a/Errors.py
+++ b/waflib/Errors.py
diff --git a/Logs.py b/waflib/Logs.py
index 2a47516..2a47516 100644
--- a/Logs.py
+++ b/waflib/Logs.py
diff --git a/Node.py b/waflib/Node.py
index 4ac1ea8..4ac1ea8 100644
--- a/Node.py
+++ b/waflib/Node.py
diff --git a/Options.py b/waflib/Options.py
index ad802d4..ad802d4 100644
--- a/Options.py
+++ b/waflib/Options.py
diff --git a/README.md b/waflib/README.md
index c5361b9..c5361b9 100644
--- a/README.md
+++ b/waflib/README.md
diff --git a/Runner.py b/waflib/Runner.py
index 261084d..261084d 100644
--- a/Runner.py
+++ b/waflib/Runner.py
diff --git a/Scripting.py b/waflib/Scripting.py
index 749d4f2..749d4f2 100644
--- a/Scripting.py
+++ b/waflib/Scripting.py
diff --git a/Task.py b/waflib/Task.py
index 0fc449d..0fc449d 100644
--- a/Task.py
+++ b/waflib/Task.py
diff --git a/TaskGen.py b/waflib/TaskGen.py
index a74e643..a74e643 100644
--- a/TaskGen.py
+++ b/waflib/TaskGen.py
diff --git a/Tools/__init__.py b/waflib/Tools/__init__.py
index 079df35..079df35 100644
--- a/Tools/__init__.py
+++ b/waflib/Tools/__init__.py
diff --git a/Tools/ar.py b/waflib/Tools/ar.py
index b39b645..b39b645 100644
--- a/Tools/ar.py
+++ b/waflib/Tools/ar.py
diff --git a/Tools/asm.py b/waflib/Tools/asm.py
index b6f26fb..b6f26fb 100644
--- a/Tools/asm.py
+++ b/waflib/Tools/asm.py
diff --git a/Tools/bison.py b/waflib/Tools/bison.py
index eef56dc..eef56dc 100644
--- a/Tools/bison.py
+++ b/waflib/Tools/bison.py
diff --git a/Tools/c.py b/waflib/Tools/c.py
index effd6b6..effd6b6 100644
--- a/Tools/c.py
+++ b/waflib/Tools/c.py
diff --git a/Tools/c_aliases.py b/waflib/Tools/c_aliases.py
index c9d5369..c9d5369 100644
--- a/Tools/c_aliases.py
+++ b/waflib/Tools/c_aliases.py
diff --git a/Tools/c_config.py b/waflib/Tools/c_config.py
index d2b3c0d..d2b3c0d 100644
--- a/Tools/c_config.py
+++ b/waflib/Tools/c_config.py
diff --git a/Tools/c_osx.py b/waflib/Tools/c_osx.py
index f70b128..f70b128 100644
--- a/Tools/c_osx.py
+++ b/waflib/Tools/c_osx.py
diff --git a/Tools/c_preproc.py b/waflib/Tools/c_preproc.py
index 7e04b4a..7e04b4a 100644
--- a/Tools/c_preproc.py
+++ b/waflib/Tools/c_preproc.py
diff --git a/Tools/c_tests.py b/waflib/Tools/c_tests.py
index f858df5..f858df5 100644
--- a/Tools/c_tests.py
+++ b/waflib/Tools/c_tests.py
diff --git a/Tools/ccroot.py b/waflib/Tools/ccroot.py
index cfef8bf..cfef8bf 100644
--- a/Tools/ccroot.py
+++ b/waflib/Tools/ccroot.py
diff --git a/Tools/clang.py b/waflib/Tools/clang.py
index 3828e39..3828e39 100644
--- a/Tools/clang.py
+++ b/waflib/Tools/clang.py
diff --git a/Tools/clangxx.py b/waflib/Tools/clangxx.py
index 152013c..152013c 100644
--- a/Tools/clangxx.py
+++ b/waflib/Tools/clangxx.py
diff --git a/Tools/compiler_c.py b/waflib/Tools/compiler_c.py
index 2dba3f8..2dba3f8 100644
--- a/Tools/compiler_c.py
+++ b/waflib/Tools/compiler_c.py
diff --git a/Tools/compiler_cxx.py b/waflib/Tools/compiler_cxx.py
index 1af65a2..1af65a2 100644
--- a/Tools/compiler_cxx.py
+++ b/waflib/Tools/compiler_cxx.py
diff --git a/Tools/compiler_d.py b/waflib/Tools/compiler_d.py
index 43bb1f6..43bb1f6 100644
--- a/Tools/compiler_d.py
+++ b/waflib/Tools/compiler_d.py
diff --git a/Tools/compiler_fc.py b/waflib/Tools/compiler_fc.py
index 96b58e7..96b58e7 100644
--- a/Tools/compiler_fc.py
+++ b/waflib/Tools/compiler_fc.py
diff --git a/Tools/cs.py b/waflib/Tools/cs.py
index aecca6d..aecca6d 100644
--- a/Tools/cs.py
+++ b/waflib/Tools/cs.py
diff --git a/Tools/cxx.py b/waflib/Tools/cxx.py
index 194fad7..194fad7 100644
--- a/Tools/cxx.py
+++ b/waflib/Tools/cxx.py
diff --git a/Tools/d.py b/waflib/Tools/d.py
index e4cf73b..e4cf73b 100644
--- a/Tools/d.py
+++ b/waflib/Tools/d.py
diff --git a/Tools/d_config.py b/waflib/Tools/d_config.py
index 6637556..6637556 100644
--- a/Tools/d_config.py
+++ b/waflib/Tools/d_config.py
diff --git a/Tools/d_scan.py b/waflib/Tools/d_scan.py
index 14c6c31..14c6c31 100644
--- a/Tools/d_scan.py
+++ b/waflib/Tools/d_scan.py
diff --git a/Tools/dbus.py b/waflib/Tools/dbus.py
index d520f1c..d520f1c 100644
--- a/Tools/dbus.py
+++ b/waflib/Tools/dbus.py
diff --git a/Tools/dmd.py b/waflib/Tools/dmd.py
index 8917ca1..8917ca1 100644
--- a/Tools/dmd.py
+++ b/waflib/Tools/dmd.py
diff --git a/Tools/errcheck.py b/waflib/Tools/errcheck.py
index de8d75a..de8d75a 100644
--- a/Tools/errcheck.py
+++ b/waflib/Tools/errcheck.py
diff --git a/Tools/fc.py b/waflib/Tools/fc.py
index d9e8d8c..d9e8d8c 100644
--- a/Tools/fc.py
+++ b/waflib/Tools/fc.py
diff --git a/Tools/fc_config.py b/waflib/Tools/fc_config.py
index 222f3a5..222f3a5 100644
--- a/Tools/fc_config.py
+++ b/waflib/Tools/fc_config.py
diff --git a/Tools/fc_scan.py b/waflib/Tools/fc_scan.py
index 12cb0fc..12cb0fc 100644
--- a/Tools/fc_scan.py
+++ b/waflib/Tools/fc_scan.py
diff --git a/Tools/flex.py b/waflib/Tools/flex.py
index 2256657..2256657 100644
--- a/Tools/flex.py
+++ b/waflib/Tools/flex.py
diff --git a/Tools/g95.py b/waflib/Tools/g95.py
index f69ba4f..f69ba4f 100644
--- a/Tools/g95.py
+++ b/waflib/Tools/g95.py
diff --git a/Tools/gas.py b/waflib/Tools/gas.py
index 77afed7..77afed7 100644
--- a/Tools/gas.py
+++ b/waflib/Tools/gas.py
diff --git a/Tools/gcc.py b/waflib/Tools/gcc.py
index acdd473..acdd473 100644
--- a/Tools/gcc.py
+++ b/waflib/Tools/gcc.py
diff --git a/Tools/gdc.py b/waflib/Tools/gdc.py
index d89a66d..d89a66d 100644
--- a/Tools/gdc.py
+++ b/waflib/Tools/gdc.py
diff --git a/Tools/gfortran.py b/waflib/Tools/gfortran.py
index 1050667..1050667 100644
--- a/Tools/gfortran.py
+++ b/waflib/Tools/gfortran.py
diff --git a/Tools/glib2.py b/waflib/Tools/glib2.py
index 949fe37..949fe37 100644
--- a/Tools/glib2.py
+++ b/waflib/Tools/glib2.py
diff --git a/Tools/gnu_dirs.py b/waflib/Tools/gnu_dirs.py
index 2847071..2847071 100644
--- a/Tools/gnu_dirs.py
+++ b/waflib/Tools/gnu_dirs.py
diff --git a/Tools/gxx.py b/waflib/Tools/gxx.py
index 22c5d26..22c5d26 100644
--- a/Tools/gxx.py
+++ b/waflib/Tools/gxx.py
diff --git a/Tools/icc.py b/waflib/Tools/icc.py
index b6492c8..b6492c8 100644
--- a/Tools/icc.py
+++ b/waflib/Tools/icc.py
diff --git a/Tools/icpc.py b/waflib/Tools/icpc.py
index 8a6cc6c..8a6cc6c 100644
--- a/Tools/icpc.py
+++ b/waflib/Tools/icpc.py
diff --git a/Tools/ifort.py b/waflib/Tools/ifort.py
index 74934f3..74934f3 100644
--- a/Tools/ifort.py
+++ b/waflib/Tools/ifort.py
diff --git a/Tools/intltool.py b/waflib/Tools/intltool.py
index af95ba8..af95ba8 100644
--- a/Tools/intltool.py
+++ b/waflib/Tools/intltool.py
diff --git a/Tools/irixcc.py b/waflib/Tools/irixcc.py
index c3ae1ac..c3ae1ac 100644
--- a/Tools/irixcc.py
+++ b/waflib/Tools/irixcc.py
diff --git a/Tools/javaw.py b/waflib/Tools/javaw.py
index f6fd20c..f6fd20c 100644
--- a/Tools/javaw.py
+++ b/waflib/Tools/javaw.py
diff --git a/Tools/ldc2.py b/waflib/Tools/ldc2.py
index a51c344..a51c344 100644
--- a/Tools/ldc2.py
+++ b/waflib/Tools/ldc2.py
diff --git a/Tools/lua.py b/waflib/Tools/lua.py
index 15a333a..15a333a 100644
--- a/Tools/lua.py
+++ b/waflib/Tools/lua.py
diff --git a/Tools/md5_tstamp.py b/waflib/Tools/md5_tstamp.py
index 6428e46..6428e46 100644
--- a/Tools/md5_tstamp.py
+++ b/waflib/Tools/md5_tstamp.py
diff --git a/Tools/msvc.py b/waflib/Tools/msvc.py
index 17b347d..17b347d 100644
--- a/Tools/msvc.py
+++ b/waflib/Tools/msvc.py
diff --git a/Tools/nasm.py b/waflib/Tools/nasm.py
index 411d582..411d582 100644
--- a/Tools/nasm.py
+++ b/waflib/Tools/nasm.py
diff --git a/Tools/nobuild.py b/waflib/Tools/nobuild.py
index 2e4b055..2e4b055 100644
--- a/Tools/nobuild.py
+++ b/waflib/Tools/nobuild.py
diff --git a/Tools/perl.py b/waflib/Tools/perl.py
index 32b03fb..32b03fb 100644
--- a/Tools/perl.py
+++ b/waflib/Tools/perl.py
diff --git a/Tools/python.py b/waflib/Tools/python.py
index 25841d0..25841d0 100644
--- a/Tools/python.py
+++ b/waflib/Tools/python.py
diff --git a/Tools/qt5.py b/waflib/Tools/qt5.py
index 4f9c690..4f9c690 100644
--- a/Tools/qt5.py
+++ b/waflib/Tools/qt5.py
diff --git a/Tools/ruby.py b/waflib/Tools/ruby.py
index 8d92a79..8d92a79 100644
--- a/Tools/ruby.py
+++ b/waflib/Tools/ruby.py
diff --git a/Tools/suncc.py b/waflib/Tools/suncc.py
index 33d34fc..33d34fc 100644
--- a/Tools/suncc.py
+++ b/waflib/Tools/suncc.py
diff --git a/Tools/suncxx.py b/waflib/Tools/suncxx.py
index 3b384f6..3b384f6 100644
--- a/Tools/suncxx.py
+++ b/waflib/Tools/suncxx.py
diff --git a/Tools/tex.py b/waflib/Tools/tex.py
index eaf9fdb..eaf9fdb 100644
--- a/Tools/tex.py
+++ b/waflib/Tools/tex.py
diff --git a/Tools/vala.py b/waflib/Tools/vala.py
index 822ec50..822ec50 100644
--- a/Tools/vala.py
+++ b/waflib/Tools/vala.py
diff --git a/Tools/waf_unit_test.py b/waflib/Tools/waf_unit_test.py
index a71ed1c..a71ed1c 100644
--- a/Tools/waf_unit_test.py
+++ b/waflib/Tools/waf_unit_test.py
diff --git a/Tools/winres.py b/waflib/Tools/winres.py
index 586c596..586c596 100644
--- a/Tools/winres.py
+++ b/waflib/Tools/winres.py
diff --git a/Tools/xlc.py b/waflib/Tools/xlc.py
index 134dd41..134dd41 100644
--- a/Tools/xlc.py
+++ b/waflib/Tools/xlc.py
diff --git a/Tools/xlcxx.py b/waflib/Tools/xlcxx.py
index 76aa59b..76aa59b 100644
--- a/Tools/xlcxx.py
+++ b/waflib/Tools/xlcxx.py
diff --git a/Utils.py b/waflib/Utils.py
index b4665c4..b4665c4 100644
--- a/Utils.py
+++ b/waflib/Utils.py
diff --git a/__init__.py b/waflib/__init__.py
index 079df35..079df35 100644
--- a/__init__.py
+++ b/waflib/__init__.py
diff --git a/ansiterm.py b/waflib/ansiterm.py
index 0d20c63..0d20c63 100644
--- a/ansiterm.py
+++ b/waflib/ansiterm.py
diff --git a/extras/__init__.py b/waflib/extras/__init__.py
index c8a3c34..c8a3c34 100644
--- a/extras/__init__.py
+++ b/waflib/extras/__init__.py
diff --git a/extras/autowaf.py b/waflib/extras/autowaf.py
index a4a06ba..a4a06ba 100644
--- a/extras/autowaf.py
+++ b/waflib/extras/autowaf.py
diff --git a/extras/batched_cc.py b/waflib/extras/batched_cc.py
index aad2872..aad2872 100644
--- a/extras/batched_cc.py
+++ b/waflib/extras/batched_cc.py
diff --git a/extras/biber.py b/waflib/extras/biber.py
index fd9db4e..fd9db4e 100644
--- a/extras/biber.py
+++ b/waflib/extras/biber.py
diff --git a/extras/bjam.py b/waflib/extras/bjam.py
index 8e04d3a..8e04d3a 100644
--- a/extras/bjam.py
+++ b/waflib/extras/bjam.py
diff --git a/extras/blender.py b/waflib/extras/blender.py
index e5efc28..e5efc28 100644
--- a/extras/blender.py
+++ b/waflib/extras/blender.py
diff --git a/extras/boo.py b/waflib/extras/boo.py
index 06623d4..06623d4 100644
--- a/extras/boo.py
+++ b/waflib/extras/boo.py
diff --git a/extras/boost.py b/waflib/extras/boost.py
index c2aaaa9..c2aaaa9 100644
--- a/extras/boost.py
+++ b/waflib/extras/boost.py
diff --git a/extras/build_file_tracker.py b/waflib/extras/build_file_tracker.py
index c4f26fd..c4f26fd 100644
--- a/extras/build_file_tracker.py
+++ b/waflib/extras/build_file_tracker.py
diff --git a/extras/build_logs.py b/waflib/extras/build_logs.py
index cdf8ed0..cdf8ed0 100644
--- a/extras/build_logs.py
+++ b/waflib/extras/build_logs.py
diff --git a/extras/buildcopy.py b/waflib/extras/buildcopy.py
index a6d9ac8..a6d9ac8 100644
--- a/extras/buildcopy.py
+++ b/waflib/extras/buildcopy.py
diff --git a/extras/c_bgxlc.py b/waflib/extras/c_bgxlc.py
index 6e3eaf7..6e3eaf7 100644
--- a/extras/c_bgxlc.py
+++ b/waflib/extras/c_bgxlc.py
diff --git a/extras/c_dumbpreproc.py b/waflib/extras/c_dumbpreproc.py
index ce9e1a4..ce9e1a4 100644
--- a/extras/c_dumbpreproc.py
+++ b/waflib/extras/c_dumbpreproc.py
diff --git a/extras/c_emscripten.py b/waflib/extras/c_emscripten.py
index e1ac494..e1ac494 100644
--- a/extras/c_emscripten.py
+++ b/waflib/extras/c_emscripten.py
diff --git a/extras/c_nec.py b/waflib/extras/c_nec.py
index 96bfae4..96bfae4 100644
--- a/extras/c_nec.py
+++ b/waflib/extras/c_nec.py
diff --git a/extras/cabal.py b/waflib/extras/cabal.py
index e10a0d1..e10a0d1 100644
--- a/extras/cabal.py
+++ b/waflib/extras/cabal.py
diff --git a/extras/cfg_altoptions.py b/waflib/extras/cfg_altoptions.py
index 47b1189..47b1189 100644
--- a/extras/cfg_altoptions.py
+++ b/waflib/extras/cfg_altoptions.py
diff --git a/extras/clang_compilation_database.py b/waflib/extras/clang_compilation_database.py
index 4d9b5e2..4d9b5e2 100644
--- a/extras/clang_compilation_database.py
+++ b/waflib/extras/clang_compilation_database.py
diff --git a/extras/codelite.py b/waflib/extras/codelite.py
index 523302c..523302c 100644
--- a/extras/codelite.py
+++ b/waflib/extras/codelite.py
diff --git a/extras/color_gcc.py b/waflib/extras/color_gcc.py
index b68c5eb..b68c5eb 100644
--- a/extras/color_gcc.py
+++ b/waflib/extras/color_gcc.py
diff --git a/extras/color_rvct.py b/waflib/extras/color_rvct.py
index f89ccbd..f89ccbd 100644
--- a/extras/color_rvct.py
+++ b/waflib/extras/color_rvct.py
diff --git a/extras/compat15.py b/waflib/extras/compat15.py
index 0e74df8..0e74df8 100644
--- a/extras/compat15.py
+++ b/waflib/extras/compat15.py
diff --git a/extras/cppcheck.py b/waflib/extras/cppcheck.py
index 13ff424..13ff424 100644
--- a/extras/cppcheck.py
+++ b/waflib/extras/cppcheck.py
diff --git a/extras/cpplint.py b/waflib/extras/cpplint.py
index e3302e5..e3302e5 100644
--- a/extras/cpplint.py
+++ b/waflib/extras/cpplint.py
diff --git a/extras/cross_gnu.py b/waflib/extras/cross_gnu.py
index 309f53b..309f53b 100644
--- a/extras/cross_gnu.py
+++ b/waflib/extras/cross_gnu.py
diff --git a/extras/cython.py b/waflib/extras/cython.py
index 481d6f4..481d6f4 100644
--- a/extras/cython.py
+++ b/waflib/extras/cython.py
diff --git a/extras/dcc.py b/waflib/extras/dcc.py
index c1a57c0..c1a57c0 100644
--- a/extras/dcc.py
+++ b/waflib/extras/dcc.py
diff --git a/extras/distnet.py b/waflib/extras/distnet.py
index 09a31a6..09a31a6 100644
--- a/extras/distnet.py
+++ b/waflib/extras/distnet.py
diff --git a/extras/doxygen.py b/waflib/extras/doxygen.py
index 28f56e9..28f56e9 100644
--- a/extras/doxygen.py
+++ b/waflib/extras/doxygen.py
diff --git a/extras/dpapi.py b/waflib/extras/dpapi.py
index b94d482..b94d482 100644
--- a/extras/dpapi.py
+++ b/waflib/extras/dpapi.py
diff --git a/extras/eclipse.py b/waflib/extras/eclipse.py
index bb78741..bb78741 100644
--- a/extras/eclipse.py
+++ b/waflib/extras/eclipse.py
diff --git a/extras/erlang.py b/waflib/extras/erlang.py
index 49f6d5b..49f6d5b 100644
--- a/extras/erlang.py
+++ b/waflib/extras/erlang.py
diff --git a/extras/fast_partial.py b/waflib/extras/fast_partial.py
index b3af513..b3af513 100644
--- a/extras/fast_partial.py
+++ b/waflib/extras/fast_partial.py
diff --git a/extras/fc_bgxlf.py b/waflib/extras/fc_bgxlf.py
index cca1810..cca1810 100644
--- a/extras/fc_bgxlf.py
+++ b/waflib/extras/fc_bgxlf.py
diff --git a/extras/fc_cray.py b/waflib/extras/fc_cray.py
index da733fa..da733fa 100644
--- a/extras/fc_cray.py
+++ b/waflib/extras/fc_cray.py
diff --git a/extras/fc_nag.py b/waflib/extras/fc_nag.py
index edcb218..edcb218 100644
--- a/extras/fc_nag.py
+++ b/waflib/extras/fc_nag.py
diff --git a/extras/fc_nec.py b/waflib/extras/fc_nec.py
index 67c8680..67c8680 100644
--- a/extras/fc_nec.py
+++ b/waflib/extras/fc_nec.py
diff --git a/extras/fc_open64.py b/waflib/extras/fc_open64.py
index 413719f..413719f 100644
--- a/extras/fc_open64.py
+++ b/waflib/extras/fc_open64.py
diff --git a/extras/fc_pgfortran.py b/waflib/extras/fc_pgfortran.py
index afb2817..afb2817 100644
--- a/extras/fc_pgfortran.py
+++ b/waflib/extras/fc_pgfortran.py
diff --git a/extras/fc_solstudio.py b/waflib/extras/fc_solstudio.py
index 53766df..53766df 100644
--- a/extras/fc_solstudio.py
+++ b/waflib/extras/fc_solstudio.py
diff --git a/extras/fc_xlf.py b/waflib/extras/fc_xlf.py
index 5a3da03..5a3da03 100644
--- a/extras/fc_xlf.py
+++ b/waflib/extras/fc_xlf.py
diff --git a/extras/file_to_object.py b/waflib/extras/file_to_object.py
index 1393b51..1393b51 100644
--- a/extras/file_to_object.py
+++ b/waflib/extras/file_to_object.py
diff --git a/extras/fluid.py b/waflib/extras/fluid.py
index 4814a35..4814a35 100644
--- a/extras/fluid.py
+++ b/waflib/extras/fluid.py
diff --git a/extras/freeimage.py b/waflib/extras/freeimage.py
index f27e525..f27e525 100644
--- a/extras/freeimage.py
+++ b/waflib/extras/freeimage.py
diff --git a/extras/fsb.py b/waflib/extras/fsb.py
index 1b8f398..1b8f398 100644
--- a/extras/fsb.py
+++ b/waflib/extras/fsb.py
diff --git a/extras/fsc.py b/waflib/extras/fsc.py
index c67e70b..c67e70b 100644
--- a/extras/fsc.py
+++ b/waflib/extras/fsc.py
diff --git a/extras/gccdeps.py b/waflib/extras/gccdeps.py
index d9758ab..d9758ab 100644
--- a/extras/gccdeps.py
+++ b/waflib/extras/gccdeps.py
diff --git a/extras/gdbus.py b/waflib/extras/gdbus.py
index 0e0476e..0e0476e 100644
--- a/extras/gdbus.py
+++ b/waflib/extras/gdbus.py
diff --git a/extras/gob2.py b/waflib/extras/gob2.py
index b4fa3b9..b4fa3b9 100644
--- a/extras/gob2.py
+++ b/waflib/extras/gob2.py
diff --git a/extras/halide.py b/waflib/extras/halide.py
index 6078e38..6078e38 100644
--- a/extras/halide.py
+++ b/waflib/extras/halide.py
diff --git a/extras/javatest.py b/waflib/extras/javatest.py
index 979b8d8..979b8d8 100755
--- a/extras/javatest.py
+++ b/waflib/extras/javatest.py
diff --git a/extras/kde4.py b/waflib/extras/kde4.py
index e49a9ec..e49a9ec 100644
--- a/extras/kde4.py
+++ b/waflib/extras/kde4.py
diff --git a/extras/local_rpath.py b/waflib/extras/local_rpath.py
index b2507e1..b2507e1 100644
--- a/extras/local_rpath.py
+++ b/waflib/extras/local_rpath.py
diff --git a/extras/lv2.py b/waflib/extras/lv2.py
index 815987f..815987f 100644
--- a/extras/lv2.py
+++ b/waflib/extras/lv2.py
diff --git a/extras/make.py b/waflib/extras/make.py
index 933d9ca..933d9ca 100644
--- a/extras/make.py
+++ b/waflib/extras/make.py
diff --git a/extras/midl.py b/waflib/extras/midl.py
index 43e6cf9..43e6cf9 100644
--- a/extras/midl.py
+++ b/waflib/extras/midl.py
diff --git a/extras/msvcdeps.py b/waflib/extras/msvcdeps.py
index fc1ecd4..fc1ecd4 100644
--- a/extras/msvcdeps.py
+++ b/waflib/extras/msvcdeps.py
diff --git a/extras/msvs.py b/waflib/extras/msvs.py
index 8aa2db0..8aa2db0 100644
--- a/extras/msvs.py
+++ b/waflib/extras/msvs.py
diff --git a/extras/netcache_client.py b/waflib/extras/netcache_client.py
index dc49048..dc49048 100644
--- a/extras/netcache_client.py
+++ b/waflib/extras/netcache_client.py
diff --git a/extras/objcopy.py b/waflib/extras/objcopy.py
index 82d8359..82d8359 100644
--- a/extras/objcopy.py
+++ b/waflib/extras/objcopy.py
diff --git a/extras/ocaml.py b/waflib/extras/ocaml.py
index afe73c0..afe73c0 100644
--- a/extras/ocaml.py
+++ b/waflib/extras/ocaml.py
diff --git a/extras/package.py b/waflib/extras/package.py
index c06498e..c06498e 100644
--- a/extras/package.py
+++ b/waflib/extras/package.py
diff --git a/extras/parallel_debug.py b/waflib/extras/parallel_debug.py
index 35883a3..35883a3 100644
--- a/extras/parallel_debug.py
+++ b/waflib/extras/parallel_debug.py
diff --git a/extras/pch.py b/waflib/extras/pch.py
index 103e752..103e752 100644
--- a/extras/pch.py
+++ b/waflib/extras/pch.py
diff --git a/extras/pep8.py b/waflib/extras/pep8.py
index 676beed..676beed 100644
--- a/extras/pep8.py
+++ b/waflib/extras/pep8.py
diff --git a/extras/pgicc.py b/waflib/extras/pgicc.py
index 9790b9c..9790b9c 100644
--- a/extras/pgicc.py
+++ b/waflib/extras/pgicc.py
diff --git a/extras/pgicxx.py b/waflib/extras/pgicxx.py
index eae121c..eae121c 100644
--- a/extras/pgicxx.py
+++ b/waflib/extras/pgicxx.py
diff --git a/extras/proc.py b/waflib/extras/proc.py
index 764abec..764abec 100644
--- a/extras/proc.py
+++ b/waflib/extras/proc.py
diff --git a/extras/protoc.py b/waflib/extras/protoc.py
index f3cb4d8..f3cb4d8 100644
--- a/extras/protoc.py
+++ b/waflib/extras/protoc.py
diff --git a/extras/pyqt5.py b/waflib/extras/pyqt5.py
index c21dfa7..c21dfa7 100644
--- a/extras/pyqt5.py
+++ b/waflib/extras/pyqt5.py
diff --git a/extras/pytest.py b/waflib/extras/pytest.py
index 7dd5a1a..7dd5a1a 100644
--- a/extras/pytest.py
+++ b/waflib/extras/pytest.py
diff --git a/extras/qnxnto.py b/waflib/extras/qnxnto.py
index 1158124..1158124 100644
--- a/extras/qnxnto.py
+++ b/waflib/extras/qnxnto.py
diff --git a/extras/qt4.py b/waflib/extras/qt4.py
index 90cae7e..90cae7e 100644
--- a/extras/qt4.py
+++ b/waflib/extras/qt4.py
diff --git a/extras/relocation.py b/waflib/extras/relocation.py
index 7e821f4..7e821f4 100644
--- a/extras/relocation.py
+++ b/waflib/extras/relocation.py
diff --git a/extras/remote.py b/waflib/extras/remote.py
index 3b038f7..3b038f7 100644
--- a/extras/remote.py
+++ b/waflib/extras/remote.py
diff --git a/extras/resx.py b/waflib/extras/resx.py
index caf4d31..caf4d31 100644
--- a/extras/resx.py
+++ b/waflib/extras/resx.py
diff --git a/extras/review.py b/waflib/extras/review.py
index 561e062..561e062 100644
--- a/extras/review.py
+++ b/waflib/extras/review.py
diff --git a/extras/rst.py b/waflib/extras/rst.py
index f3c3a5e..f3c3a5e 100644
--- a/extras/rst.py
+++ b/waflib/extras/rst.py
diff --git a/extras/run_do_script.py b/waflib/extras/run_do_script.py
index f3c5812..f3c5812 100644
--- a/extras/run_do_script.py
+++ b/waflib/extras/run_do_script.py
diff --git a/extras/run_m_script.py b/waflib/extras/run_m_script.py
index b5f27eb..b5f27eb 100644
--- a/extras/run_m_script.py
+++ b/waflib/extras/run_m_script.py
diff --git a/extras/run_py_script.py b/waflib/extras/run_py_script.py
index 3670381..3670381 100644
--- a/extras/run_py_script.py
+++ b/waflib/extras/run_py_script.py
diff --git a/extras/run_r_script.py b/waflib/extras/run_r_script.py
index b0d8f2b..b0d8f2b 100644
--- a/extras/run_r_script.py
+++ b/waflib/extras/run_r_script.py
diff --git a/extras/sas.py b/waflib/extras/sas.py
index 754c614..754c614 100644
--- a/extras/sas.py
+++ b/waflib/extras/sas.py
diff --git a/extras/satellite_assembly.py b/waflib/extras/satellite_assembly.py
index 005eb07..005eb07 100644
--- a/extras/satellite_assembly.py
+++ b/waflib/extras/satellite_assembly.py
diff --git a/extras/scala.py b/waflib/extras/scala.py
index a9880f0..a9880f0 100644
--- a/extras/scala.py
+++ b/waflib/extras/scala.py
diff --git a/extras/slow_qt4.py b/waflib/extras/slow_qt4.py
index ec7880b..ec7880b 100644
--- a/extras/slow_qt4.py
+++ b/waflib/extras/slow_qt4.py
diff --git a/extras/softlink_libs.py b/waflib/extras/softlink_libs.py
index 50c777f..50c777f 100644
--- a/extras/softlink_libs.py
+++ b/waflib/extras/softlink_libs.py
diff --git a/extras/stale.py b/waflib/extras/stale.py
index cac3f46..cac3f46 100644
--- a/extras/stale.py
+++ b/waflib/extras/stale.py
diff --git a/extras/stracedeps.py b/waflib/extras/stracedeps.py
index 37d82cb..37d82cb 100644
--- a/extras/stracedeps.py
+++ b/waflib/extras/stracedeps.py
diff --git a/extras/swig.py b/waflib/extras/swig.py
index fd3d6d2..fd3d6d2 100644
--- a/extras/swig.py
+++ b/waflib/extras/swig.py
diff --git a/extras/syms.py b/waflib/extras/syms.py
index dfa0059..dfa0059 100644
--- a/extras/syms.py
+++ b/waflib/extras/syms.py
diff --git a/extras/ticgt.py b/waflib/extras/ticgt.py
index f43a7ea..f43a7ea 100644
--- a/extras/ticgt.py
+++ b/waflib/extras/ticgt.py
diff --git a/extras/unity.py b/waflib/extras/unity.py
index 78128ed..78128ed 100644
--- a/extras/unity.py
+++ b/waflib/extras/unity.py
diff --git a/extras/use_config.py b/waflib/extras/use_config.py
index ef5129f..ef5129f 100644
--- a/extras/use_config.py
+++ b/waflib/extras/use_config.py
diff --git a/extras/valadoc.py b/waflib/extras/valadoc.py
index c50f69e..c50f69e 100644
--- a/extras/valadoc.py
+++ b/waflib/extras/valadoc.py
diff --git a/extras/waf_xattr.py b/waflib/extras/waf_xattr.py
index 351dd63..351dd63 100644
--- a/extras/waf_xattr.py
+++ b/waflib/extras/waf_xattr.py
diff --git a/extras/why.py b/waflib/extras/why.py
index 1bb941f..1bb941f 100644
--- a/extras/why.py
+++ b/waflib/extras/why.py
diff --git a/extras/win32_opts.py b/waflib/extras/win32_opts.py
index 9f7443c..9f7443c 100644
--- a/extras/win32_opts.py
+++ b/waflib/extras/win32_opts.py
diff --git a/extras/wix.py b/waflib/extras/wix.py
index d87bfbb..d87bfbb 100644
--- a/extras/wix.py
+++ b/waflib/extras/wix.py
diff --git a/extras/xcode6.py b/waflib/extras/xcode6.py
index 91bbff1..91bbff1 100644
--- a/extras/xcode6.py
+++ b/waflib/extras/xcode6.py
diff --git a/fixpy2.py b/waflib/fixpy2.py
index 24176e0..24176e0 100644
--- a/fixpy2.py
+++ b/waflib/fixpy2.py
diff --git a/processor.py b/waflib/processor.py
index 2eecf3b..2eecf3b 100755
--- a/processor.py
+++ b/waflib/processor.py
diff --git a/waflib/waf b/waflib/waf
new file mode 100755
index 0000000..e22930a
--- /dev/null
+++ b/waflib/waf
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+# Minimal waf script for projects that include waflib directly
+
+from waflib import Context, Scripting
+
+import inspect
+import os
+
+def main():
+ script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main)))
+ project_path = os.path.dirname(script_path)
+ Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path)
+
+if __name__ == '__main__':
+ main()
diff --git a/wscript b/wscript
new file mode 100644
index 0000000..037231c
--- /dev/null
+++ b/wscript
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+# Licensed under the GNU GPL v3 or later, see COPYING file for details.
+# Copyright 2008-2013 David Robillard
+# Copyright 2008 Nedko Arnaudov
+
+import os
+
+from waflib.extras import autowaf as autowaf
+import waflib.Options as Options, waflib.Utils as Utils
+
+# Version of this package (even if built as a child)
+PATCHAGE_VERSION = '1.0.1'
+
+# Variables for 'waf dist'
+APPNAME = 'patchage'
+VERSION = PATCHAGE_VERSION
+APP_HUMAN_NAME = 'Patchage'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(ctx):
+ ctx.load('compiler_cxx')
+ autowaf.set_options(ctx)
+ opt = ctx.get_option_group('Configuration options')
+ opt.add_option('--patchage-install-name', type='string', default=APPNAME,
+ dest='patchage_install_name',
+ help='patchage install name. [Default: '' + APPNAME + '']')
+ opt.add_option('--patchage-human-name', type='string', default=APP_HUMAN_NAME,
+ dest='patchage_human_name',
+ help='patchage human name [Default: '' + APP_HUMAN_NAME + '']')
+ opt.add_option('--jack-dbus', action='store_true', dest='jack_dbus',
+ help='use Jack via D-Bus [Default: False (use libjack)]')
+ opt.add_option('--jack-session-manage', action='store_true', dest='jack_session_manage',
+ help='include experimental JACK session management (save/restore) support')
+ opt.add_option('--no-alsa', action='store_true', dest='no_alsa',
+ help='do not build Alsa Sequencer support')
+ opt.add_option('--no-binloc', action='store_true', dest='no_binloc',
+ help='do not try to read files from executable location')
+ opt.add_option('--light-theme', action='store_true', dest='light_theme',
+ help='use light coloured theme')
+
+def configure(conf):
+ autowaf.display_header('Patchage Configuration')
+ conf.load('compiler_cxx', cache=True)
+ conf.load('autowaf', cache=True)
+ autowaf.set_cxx_lang(conf, 'c++11')
+
+ autowaf.check_pkg(conf, 'dbus-1', uselib_store='DBUS',
+ mandatory=False)
+ autowaf.check_pkg(conf, 'dbus-glib-1', uselib_store='DBUS_GLIB',
+ mandatory=False)
+ autowaf.check_pkg(conf, 'gthread-2.0', uselib_store='GTHREAD',
+ atleast_version='2.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'glibmm-2.4', uselib_store='GLIBMM',
+ atleast_version='2.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'gtkmm-2.4', uselib_store='GTKMM',
+ atleast_version='2.12.0', mandatory=True)
+ autowaf.check_pkg(conf, 'ganv-1', uselib_store='GANV',
+ atleast_version='1.5.2', mandatory=True)
+
+ if conf.env.DEST_OS == 'darwin':
+ autowaf.check_pkg(conf, 'gtk-mac-integration', uselib_store='GTK_OSX',
+ atleast_version='1.0.0', mandatory=False)
+ if conf.env.HAVE_GTK_OSX:
+ autowaf.define(conf, 'PATCHAGE_GTK_OSX', 1)
+
+ # Check for dladdr
+ autowaf.check_function(conf, 'cxx', 'dladdr',
+ header_name = 'dlfcn.h',
+ defines = ['_GNU_SOURCE'],
+ lib = ['dl'],
+ define_name = 'HAVE_DLADDR',
+ mandatory = False)
+
+ # Use Jack D-Bus if requested (only one jack driver is allowed)
+ if Options.options.jack_dbus and conf.env.HAVE_DBUS and conf.env.HAVE_DBUS_GLIB:
+ autowaf.define(conf, 'HAVE_JACK_DBUS', 1)
+ else:
+ autowaf.check_pkg(conf, 'jack', uselib_store='JACK',
+ atleast_version='0.120.0', mandatory=False)
+ if conf.env.HAVE_JACK:
+ autowaf.define(conf, 'PATCHAGE_LIBJACK', 1)
+ if Options.options.jack_session_manage:
+ autowaf.define(conf, 'PATCHAGE_JACK_SESSION', 1)
+ autowaf.check_function(conf, 'cxx', 'jack_get_property',
+ header_name = 'jack/metadata.h',
+ define_name = 'HAVE_JACK_METADATA',
+ uselib = 'JACK',
+ mandatory = False)
+
+ # Use Alsa if present unless --no-alsa
+ if not Options.options.no_alsa:
+ autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA', mandatory=False)
+
+ # Find files at binary location if we have dladdr unless --no-binloc
+ if not Options.options.no_binloc and conf.is_defined('HAVE_DLADDR'):
+ autowaf.define(conf, 'PATCHAGE_BINLOC', 1)
+
+ if Options.options.light_theme:
+ autowaf.define(conf, 'PATCHAGE_USE_LIGHT_THEME', 1)
+
+ # Boost headers
+ conf.check_cxx(header_name='boost/format.hpp')
+ conf.check_cxx(header_name='boost/shared_ptr.hpp')
+ conf.check_cxx(header_name='boost/utility.hpp')
+ conf.check_cxx(header_name='boost/weak_ptr.hpp')
+
+ conf.env.PATCHAGE_VERSION = PATCHAGE_VERSION
+
+ conf.env.APP_INSTALL_NAME = Options.options.patchage_install_name
+ conf.env.APP_HUMAN_NAME = Options.options.patchage_human_name
+ autowaf.define(conf, 'PATCHAGE_DATA_DIR', os.path.join(
+ conf.env.DATADIR, conf.env.APP_INSTALL_NAME))
+
+ conf.write_config_header('patchage_config.h', remove=False)
+
+ autowaf.display_summary(conf)
+ autowaf.display_msg(conf, "Install name", "'" + conf.env.APP_INSTALL_NAME + "'", 'CYAN')
+ autowaf.display_msg(conf, "App human name", "'" + conf.env.APP_HUMAN_NAME + "'", 'CYAN')
+ autowaf.display_msg(conf, "Jack (D-Bus)", bool(conf.env.HAVE_JACK_DBUS))
+ autowaf.display_msg(conf, "Jack (libjack)", conf.is_defined('PATCHAGE_LIBJACK'))
+ autowaf.display_msg(conf, "Jack Session Management", conf.is_defined('PATCHAGE_JACK_SESSION'))
+ autowaf.display_msg(conf, "Jack Metadata", conf.is_defined('HAVE_JACK_METADATA'))
+ autowaf.display_msg(conf, "Alsa Sequencer", bool(conf.env.HAVE_ALSA))
+ if conf.env.DEST_OS == 'darwin':
+ autowaf.display_msg(conf, "Mac Integration", bool(conf.env.HAVE_GTK_OSX))
+
+ print('')
+
+def build(bld):
+ out_base = ''
+ if bld.env.DEST_OS == 'darwin':
+ out_base = 'Patchage.app/Contents/'
+
+ # Program
+ prog = bld(features = 'cxx cxxprogram',
+ includes = ['.', 'src'],
+ target = out_base + bld.env.APP_INSTALL_NAME,
+ install_path = '${BINDIR}')
+ autowaf.use_lib(bld, prog, 'DBUS GANV DBUS_GLIB GTKMM GNOMECANVAS GTHREAD GTK_OSX')
+ prog.source = '''
+ src/Configuration.cpp
+ src/Patchage.cpp
+ src/PatchageCanvas.cpp
+ src/PatchageEvent.cpp
+ src/PatchageModule.cpp
+ src/main.cpp
+ '''
+ if bld.env.HAVE_JACK_DBUS:
+ prog.source += ' src/JackDbusDriver.cpp '
+ if bld.is_defined('PATCHAGE_LIBJACK'):
+ prog.source += ' src/JackDriver.cpp '
+ prog.uselib += ' JACK NEWJACK '
+ if bld.env.HAVE_ALSA:
+ prog.source += ' src/AlsaDriver.cpp '
+ prog.uselib += ' ALSA '
+ if bld.is_defined('PATCHAGE_BINLOC') and bld.is_defined('HAVE_DLADDR'):
+ prog.lib = ['dl']
+
+ # XML UI definition
+ bld(features = 'subst',
+ source = 'src/patchage.ui',
+ target = out_base + 'patchage.ui',
+ install_path = '${DATADIR}/' + bld.env.APP_INSTALL_NAME,
+ chmod = Utils.O644,
+ PATCHAGE_VERSION = PATCHAGE_VERSION)
+
+ # 'Desktop' file (menu entry, icon, etc)
+ bld(features = 'subst',
+ source = 'patchage.desktop.in',
+ target = 'patchage.desktop',
+ install_path = '${DATADIR}/applications',
+ chmod = Utils.O644,
+ BINDIR = os.path.normpath(bld.env.BINDIR),
+ APP_INSTALL_NAME = bld.env.APP_INSTALL_NAME,
+ APP_HUMAN_NAME = bld.env.APP_HUMAN_NAME)
+
+ if bld.env.DEST_OS == 'darwin':
+ # Property list
+ bld(features = 'subst',
+ source = 'osx/Info.plist.in',
+ target = out_base + 'Info.plist',
+ install_path = '',
+ chmod = Utils.O644)
+
+ # Icons
+ bld(rule = 'cp ${SRC} ${TGT}',
+ source = 'osx/Patchage.icns',
+ target = out_base + 'Resources/Patchage.icns')
+
+ # Gtk/Pango/etc configuration files
+ for i in ['pangorc', 'pango.modules', 'loaders.cache', 'gtkrc']:
+ bld(rule = 'cp ${SRC} ${TGT}',
+ source = 'osx/' + i,
+ target = out_base + 'Resources/' + i)
+
+ # Icons
+ # After installation, icon cache should be updated using:
+ # gtk-update-icon-cache -f -t $(datadir)/icons/hicolor
+ icon_sizes = [16, 22, 24, 32, 48, 128, 256]
+ for s in icon_sizes:
+ d = '%dx%d' % (s, s)
+ bld.install_as(
+ os.path.join(bld.env.DATADIR, 'icons', 'hicolor', d, 'apps',
+ bld.env.APP_INSTALL_NAME + '.png'),
+ os.path.join('icons', d, 'patchage.png'))
+
+ bld.install_as(
+ os.path.join(bld.env.DATADIR, 'icons', 'hicolor', 'scalable', 'apps',
+ bld.env.APP_INSTALL_NAME + '.svg'),
+ os.path.join('icons', 'scalable', 'patchage.svg'))
+
+ bld.install_files('${MANDIR}/man1', bld.path.ant_glob('doc/*.1'))
+
+def posts(ctx):
+ path = str(ctx.path.abspath())
+ autowaf.news_to_posts(
+ os.path.join(path, 'NEWS'),
+ {'title' : 'Patchage',
+ 'description' : autowaf.get_blurb(os.path.join(path, 'README')),
+ 'dist_pattern' : 'http://download.drobilla.net/patchage-%s.tar.bz2'},
+ { 'Author' : 'drobilla',
+ 'Tags' : 'Hacking, LAD, Patchage' },
+ os.path.join(out, 'posts'))