gnome-bluetooth r305 - in trunk: . applet browse common debian docs icons pixmaps po properties sendto src wizard



Author: hadess
Date: Wed Feb 25 14:35:27 2009
New Revision: 305
URL: http://svn.gnome.org/viewvc/gnome-bluetooth?rev=305&view=rev

Log:
bluez-gnome -> gnome-bluetooth

Remove the obsolete gnome-bluetooth files, and move our adopted
bluez-gnome in.

Added:
   trunk/COPYING.LIB
      - copied, changed from r304, /trunk/COPYING.gnomebt
   trunk/ChangeLog.README
   trunk/ChangeLog.pre-2.27
      - copied, changed from r304, /trunk/ChangeLog
   trunk/applet/
   trunk/applet/Makefile.am
   trunk/applet/agent.c
   trunk/applet/agent.h
   trunk/applet/bluetooth-applet.1
   trunk/applet/bluetooth-applet.desktop.in
   trunk/applet/main.c
   trunk/applet/notify.c
   trunk/applet/notify.h
   trunk/applet/obex.c
   trunk/applet/obex.h
   trunk/applet/test-agentdialog.c
   trunk/browse/
   trunk/browse/Makefile.am
   trunk/browse/bluetooth-browse.1
   trunk/browse/main.c
   trunk/common/
   trunk/common/Makefile.am
   trunk/common/bluetooth-agent.c
   trunk/common/bluetooth-agent.h
   trunk/common/bluetooth-agent.xml
   trunk/common/bluetooth-client.c
   trunk/common/bluetooth-client.h
   trunk/common/bluetooth-client.xml
   trunk/common/bluetooth-device-selection.c
   trunk/common/bluetooth-device-selection.h
   trunk/common/bluetooth-instance.c
   trunk/common/bluetooth-instance.h
   trunk/common/bluetooth-instance.xml
   trunk/common/helper.c
   trunk/common/helper.h
   trunk/common/marshal.list
   trunk/common/obex-agent.c
   trunk/common/obex-agent.h
   trunk/common/obex-agent.xml
   trunk/common/test-agent.c
   trunk/common/test-client.c
   trunk/common/test-deviceselection.c
   trunk/configure.ac
   trunk/icons/
   trunk/icons/Makefile.am
   trunk/icons/hicolor_apps_16x16_bluetooth.png
   trunk/icons/hicolor_apps_16x16_bluetooth.svg
   trunk/icons/hicolor_apps_22x22_bluetooth.png
   trunk/icons/hicolor_apps_22x22_bluetooth.svg
   trunk/icons/hicolor_apps_24x24_bluetooth.png
   trunk/icons/hicolor_apps_32x32_bluetooth.png
   trunk/icons/hicolor_apps_32x32_bluetooth.svg
   trunk/icons/hicolor_apps_48x48_bluetooth.png
   trunk/icons/hicolor_apps_scalable_bluetooth.svg
   trunk/po/bs.po
   trunk/po/en_AU.po
   trunk/po/eo.po
   trunk/po/fy.po
   trunk/po/hr.po
   trunk/po/id.po
   trunk/po/ka.po
   trunk/po/ko.po
   trunk/po/ku.po
   trunk/po/mn.po
   trunk/po/ms.po
   trunk/po/mus.po
   trunk/po/sr.po
   trunk/po/tr.po
   trunk/properties/
   trunk/properties/Makefile.am
   trunk/properties/adapter.c
   trunk/properties/adapter.h
   trunk/properties/bluetooth-manager.schemas.in
   trunk/properties/bluetooth-properties.1
   trunk/properties/bluetooth-properties.desktop.in
   trunk/properties/general.c
   trunk/properties/general.h
   trunk/properties/killswitch.c
   trunk/properties/killswitch.h
   trunk/properties/main.c
   trunk/properties/service.c
   trunk/properties/service.h
   trunk/sendto/
   trunk/sendto/Makefile.am
   trunk/sendto/bluetooth-sendto.1
   trunk/sendto/main.c
   trunk/wizard/
   trunk/wizard/Makefile.am
   trunk/wizard/bluetooth-wizard.1
   trunk/wizard/main.c
Removed:
   trunk/COPYING.gnomebt
   trunk/ChangeLog
   trunk/HACKING
   trunk/configure.in
   trunk/debian/changelog
   trunk/debian/control
   trunk/debian/copyright
   trunk/debian/rocks
   trunk/debian/rules
   trunk/docs/sketch.txt
   trunk/gnome-bluetooth.pc.in
   trunk/gob2.m4
   trunk/pixmaps/.cvsignore
   trunk/pixmaps/Makefile.am
   trunk/pixmaps/blueradio-48.png
   trunk/pixmaps/blueradio.png
   trunk/pixmaps/blueradio.svg
   trunk/pixmaps/blueradio.xcf
   trunk/pixmaps/btdevice-audiovideo.png
   trunk/pixmaps/btdevice-computer.png
   trunk/pixmaps/btdevice-imaging.png
   trunk/pixmaps/btdevice-lan.png
   trunk/pixmaps/btdevice-misc.png
   trunk/pixmaps/btdevice-peripheral.png
   trunk/pixmaps/btdevice-phone.png
   trunk/pixmaps/frame1.png
   trunk/pixmaps/frame2.png
   trunk/pixmaps/frame3.png
   trunk/pixmaps/frame4.png
   trunk/pixmaps/frame5.png
   trunk/pixmaps/frame6.png
   trunk/po/.cvsignore
   trunk/po/ChangeLog
   trunk/po/be latin po
   trunk/po/dz.po
   trunk/po/en_CA.po
   trunk/po/et.po
   trunk/po/gl.po
   trunk/po/gu.po
   trunk/po/mk.po
   trunk/po/ne.po
   trunk/po/pa.po
   trunk/po/rw.po
   trunk/po/sq.po
   trunk/po/vi.po
   trunk/po/zh_HK.po
   trunk/python-headers.m4
   trunk/src/.cvsignore
   trunk/src/Makefile.am
   trunk/src/chooser.gob
   trunk/src/controller.gob
   trunk/src/gconftest.c
   trunk/src/gnomebt-chooser-pymodule.c
   trunk/src/gnomebt-chooser-test.c
   trunk/src/gnomebt-chooser.override
   trunk/src/gnomebt-controller-pymodule.c
   trunk/src/gnomebt-controller-test.c
   trunk/src/gnomebt-controller.override
   trunk/src/spinner-test.c
   trunk/src/spinner.gob
Modified:
   trunk/AUTHORS
   trunk/COPYING
   trunk/INSTALL
   trunk/Makefile.am
   trunk/NEWS
   trunk/README
   trunk/autogen.sh
   trunk/po/LINGUAS
   trunk/po/POTFILES.in
   trunk/po/POTFILES.skip
   trunk/po/ar.po
   trunk/po/be.po
   trunk/po/bg.po
   trunk/po/ca.po
   trunk/po/cs.po
   trunk/po/da.po
   trunk/po/de.po
   trunk/po/el.po
   trunk/po/en_GB.po
   trunk/po/es.po
   trunk/po/eu.po
   trunk/po/fi.po
   trunk/po/fr.po
   trunk/po/he.po
   trunk/po/hu.po
   trunk/po/it.po
   trunk/po/ja.po
   trunk/po/lv.po
   trunk/po/nb.po
   trunk/po/nl.po
   trunk/po/oc.po
   trunk/po/pl.po
   trunk/po/pt.po
   trunk/po/pt_BR.po
   trunk/po/ro.po
   trunk/po/ru.po
   trunk/po/sk.po
   trunk/po/sl.po
   trunk/po/sv.po
   trunk/po/uk.po
   trunk/po/zh_CN.po
   trunk/po/zh_TW.po

Modified: trunk/AUTHORS
==============================================================================
--- trunk/AUTHORS	(original)
+++ trunk/AUTHORS	Wed Feb 25 14:35:27 2009
@@ -1,17 +1,6 @@
-Edd Dumbill <edd usefulinc com>
+Marcel Holtmann <marcel holtmann org>
 Bastien Nocera <hadess hadess net>
-
-The OBEX server uses code from the openobex project, whose contributors
-include:
-	Dag Brattli <dagb cs uit no>
-	Pontus Fuchs <pontus fuchs tactel se>
-	Fons Botman <budely tref nl>
-	Jean Tourrilhes <jt hpl hp com>
-	Marcel Holtmann <marcel holtmann org>
-
-It also uses code from GNOME libegg, whose authors can be found in
-libegg/AUTHORS, and include:
-	Jonathan Blandford <jrb redhat com>
-	Kristian Rietveld <kris gtk org>
-	Anders Carlsson <andersca gnu org>
-	James Henstridge <james daa com au>
+Chris Rivera <crivera novell com>
+Jaap A. Haitsma <jaap haitsma org>
+Antti KaijanmÃki <antti kaijanmaki net>
+Michael Terry <michael terry canonical com>

Modified: trunk/COPYING
==============================================================================
--- trunk/COPYING	(original)
+++ trunk/COPYING	Wed Feb 25 14:35:27 2009
@@ -2,7 +2,7 @@
 		       Version 2, June 1991
 
  Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -305,7 +305,7 @@
 
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 
 Also add information on how to contact you by electronic and paper mail.
@@ -313,7 +313,7 @@
 If the program is interactive, make it output a short notice like this
 when it starts in an interactive mode:
 
-    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision version 69, Copyright (C) year name of author
     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     This is free software, and you are welcome to redistribute it
     under certain conditions; type `show c' for details.

Copied: trunk/COPYING.LIB (from r304, /trunk/COPYING.gnomebt)
==============================================================================
--- /trunk/COPYING.gnomebt	(original)
+++ trunk/COPYING.LIB	Wed Feb 25 14:35:27 2009
@@ -1,9 +1,8 @@
-
-                  GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 2.1, February 1999
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
 
  Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -11,7 +10,7 @@
  as the successor of the GNU Library Public License, version 2, hence
  the version number 2.1.]
 
-                            Preamble
+			    Preamble
 
   The licenses for most software are designed to take away your
 freedom to share and change it.  By contrast, the GNU General Public
@@ -23,8 +22,7 @@
 Free Software Foundation and other authors who decide to use it.  You
 can use it too, but we suggest you first think carefully about whether
 this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations
-below.
+strategy to use in any particular case, based on the explanations below.
 
   When we speak of free software, we are referring to freedom of use,
 not price.  Our General Public Licenses are designed to make sure that
@@ -89,9 +87,9 @@
 special circumstances.
 
   For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it
-becomes a de-facto standard.  To achieve this, non-free programs must
-be allowed to use the library.  A more frequent case is that a free
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
 library does the same job as widely used non-free libraries.  In this
 case, there is little to gain by limiting the free library to free
 software only, so we use the Lesser General Public License.
@@ -114,7 +112,7 @@
 former contains code derived from the library, whereas the latter must
 be combined with the library in order to run.
 
-                  GNU LESSER GENERAL PUBLIC LICENSE
+		  GNU LESSER GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
   0. This License Agreement applies to any software library or other
@@ -138,8 +136,8 @@
   "Source code" for a work means the preferred form of the work for
 making modifications to it.  For a library, complete source code means
 all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control
-compilation and installation of the library.
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
 
   Activities other than copying, distribution and modification are not
 covered by this License; they are outside its scope.  The act of
@@ -148,7 +146,7 @@
 on the Library (independent of the use of the Library in a tool for
 writing it).  Whether that is true depends on what the Library does
 and what the program that uses the Library does.
-
+  
   1. You may copy and distribute verbatim copies of the Library's
 complete source code as you receive it, in any medium, provided that
 you conspicuously and appropriately publish on each copy an
@@ -305,10 +303,10 @@
     the user installs one, as long as the modified version is
     interface-compatible with the version that the work was made with.
 
-    c) Accompany the work with a written offer, valid for at least
-    three years, to give the same user the materials specified in
-    Subsection 6a, above, for a charge no more than the cost of
-    performing this distribution.
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
 
     d) If distribution of the work is made by offering access to copy
     from a designated place, offer equivalent access to copy the above
@@ -386,10 +384,9 @@
 the only way you could satisfy both it and this License would be to
 refrain entirely from distribution of the Library.
 
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply, and the section as a whole is intended to apply in other
-circumstances.
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
 
 It is not the purpose of this section to induce you to infringe any
 patents or other property right claims or to contest validity of any
@@ -407,11 +404,11 @@
 
   12. If the distribution and/or use of the Library is restricted in
 certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License
-may add an explicit geographical distribution limitation excluding those
-countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
 
   13. The Free Software Foundation may publish revised and/or new
 versions of the Lesser General Public License from time to time.
@@ -435,7 +432,7 @@
 of all derivatives of our free software and of promoting the sharing
 and reuse of software generally.
 
-                            NO WARRANTY
+			    NO WARRANTY
 
   15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
 WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
@@ -458,22 +455,20 @@
 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 DAMAGES.
 
-                     END OF TERMS AND CONDITIONS
+		     END OF TERMS AND CONDITIONS
 
            How to Apply These Terms to Your New Libraries
 
   If you develop a new library, and you want it to be of the greatest
 possible use to the public, we recommend making it free software that
 everyone can redistribute and change.  You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms
-of the ordinary General Public License).
-
-  To apply these terms, attach the following notices to the library.
-It is safest to attach them to the start of each source file to most
-effectively convey the exclusion of warranty; and each file should
-have at least the "copyright" line and a pointer to where the full
-notice is found.
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
 
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
 
     <one line to give the library's name and a brief idea of what it does.>
     Copyright (C) <year>  <name of author>
@@ -490,17 +485,16 @@
 
     You should have received a copy of the GNU Lesser General Public
     License along with this library; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 Also add information on how to contact you by electronic and paper mail.
 
-You should also get your employer (if you work as a programmer) or
-your school, if any, to sign a "copyright disclaimer" for the library,
-if necessary.  Here is a sample; alter the names:
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
 
   Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James
-  Random Hacker.
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
 
   <signature of Ty Coon>, 1 April 1990
   Ty Coon, President of Vice

Added: trunk/ChangeLog.README
==============================================================================
--- (empty file)
+++ trunk/ChangeLog.README	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,16 @@
+GNOME Bluetooth doesn't use ChangeLog anymore. Instead, we use SVN checkin comments
+to autogenerate a ChangeLog file at "make dist" time. Use "make ChangeLog" in the
+toplevel directory to manually create a ChangeLog. Or use
+http://svn.gnome.org/viewvc/gnome-bluetooth/?view=queryform
+to look up the version history.
+
+When committing a patch to svn, use a checkin comment that describes the changes
+made. If the checkin is related to a bug, reference the bug number. Example:
+
+        When removing a toolbar, make its items available again in the toolbar
+        editor. (Closes: #131182)
+
+Checkin comments MUST use the UTF-8 encoding.
+
+Do NOT commit to this module without permission from a maintainer.
+See the MAINTAINERS file for who they are.

Copied: trunk/ChangeLog.pre-2.27 (from r304, /trunk/ChangeLog)
==============================================================================

Modified: trunk/INSTALL
==============================================================================
--- trunk/INSTALL	(original)
+++ trunk/INSTALL	Wed Feb 25 14:35:27 2009
@@ -1,13 +1,16 @@
-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
-Foundation, Inc.
+Installation Instructions
+*************************
 
-   This file is free documentation; the Free Software Foundation gives
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
 unlimited permission to copy, distribute and modify it.
 
 Basic Installation
 ==================
 
-   These are generic installation instructions.
+These are generic installation instructions.
 
    The `configure' shell script attempts to guess correct values for
 various system-dependent variables used during compilation.  It uses
@@ -67,9 +70,9 @@
 Compilers and Options
 =====================
 
-   Some systems require unusual options for compilation or linking that
-the `configure' script does not know about.  Run `./configure --help'
-for details on some of the pertinent environment variables.
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
 
    You can give `configure' initial values for configuration parameters
 by setting variables in the command line or in the environment.  Here
@@ -82,7 +85,7 @@
 Compiling For Multiple Architectures
 ====================================
 
-   You can compile the package for more than one kind of computer at the
+You can compile the package for more than one kind of computer at the
 same time, by placing the object files for each architecture in their
 own directory.  To do this, you must use a version of `make' that
 supports the `VPATH' variable, such as GNU `make'.  `cd' to the
@@ -99,19 +102,19 @@
 Installation Names
 ==================
 
-   By default, `make install' will install the package's files in
+By default, `make install' will install the package's files in
 `/usr/local/bin', `/usr/local/man', etc.  You can specify an
 installation prefix other than `/usr/local' by giving `configure' the
-option `--prefix=PATH'.
+option `--prefix=PREFIX'.
 
    You can specify separate installation prefixes for
 architecture-specific files and architecture-independent files.  If you
-give `configure' the option `--exec-prefix=PATH', the package will use
-PATH as the prefix for installing programs and libraries.
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
 Documentation and other data files will still use the regular prefix.
 
    In addition, if you use an unusual directory layout you can give
-options like `--bindir=PATH' to specify different values for particular
+options like `--bindir=DIR' to specify different values for particular
 kinds of files.  Run `configure --help' for a list of the directories
 you can set and what kinds of files go in them.
 
@@ -122,7 +125,7 @@
 Optional Features
 =================
 
-   Some packages pay attention to `--enable-FEATURE' options to
+Some packages pay attention to `--enable-FEATURE' options to
 `configure', where FEATURE indicates an optional part of the package.
 They may also pay attention to `--with-PACKAGE' options, where PACKAGE
 is something like `gnu-as' or `x' (for the X Window System).  The
@@ -137,11 +140,11 @@
 Specifying the System Type
 ==========================
 
-   There may be some features `configure' cannot figure out
-automatically, but needs to determine by the type of machine the package
-will run on.  Usually, assuming the package is built to be run on the
-_same_ architectures, `configure' can figure that out, but if it prints
-a message saying it cannot guess the machine type, give it the
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
 `--build=TYPE' option.  TYPE can either be a short name for the system
 type, such as `sun4', or a canonical name which has the form:
 
@@ -167,9 +170,9 @@
 Sharing Defaults
 ================
 
-   If you want to set default values for `configure' scripts to share,
-you can create a site shell script called `config.site' that gives
-default values for variables like `CC', `cache_file', and `prefix'.
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
 `configure' looks for `PREFIX/share/config.site' if it exists, then
 `PREFIX/etc/config.site' if it exists.  Or, you can set the
 `CONFIG_SITE' environment variable to the location of the site script.
@@ -178,7 +181,7 @@
 Defining Variables
 ==================
 
-   Variables not defined in a site shell script can be set in the
+Variables not defined in a site shell script can be set in the
 environment passed to `configure'.  However, some packages may run
 configure again during the build, and the customized values of these
 variables may be lost.  In order to avoid this problem, you should set
@@ -186,14 +189,18 @@
 
      ./configure CC=/usr/local2/bin/gcc
 
-will cause the specified gcc to be used as the C compiler (unless it is
-overridden in the site shell script).
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).  Here is a another example:
+
+     /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
 
 `configure' Invocation
 ======================
 
-   `configure' recognizes the following options to control how it
-operates.
+`configure' recognizes the following options to control how it operates.
 
 `--help'
 `-h'

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -1,13 +1,37 @@
-## Process this file with automake to produce Makefile.in
 
-SUBDIRS = po src pixmaps
+SUBDIRS = po icons common applet properties wizard \
+				sendto browse analyzer explorer proximity
 
-EXTRA_DIST = \
-	intltool-extract.in intltool-merge.in intltool-update.in \
-	COPYING COPYING.gnomebt TODO MAINTAINERS $(PACKAGE).pc.in
+EXTRA_DIST = intltool-extract.in intltool-update.in intltool-merge.in
 
-DISTCLEANFILES = intltool-extract intltool-merge intltool-update
+DISTCHECK_CONFIGURE_FLAGS = --disable-schemas-install \
+	--disable-desktop-update --disable-mime-update --disable-icon-update
 
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = $(PACKAGE).pc
+DISTCLEANFILES = intltool-extract intltool-update intltool-merge
 
+MAINTAINERCLEANFILES = Makefile.in \
+	aclocal.m4 configure config.h.in config.sub config.guess \
+	ltmain.sh depcomp missing install-sh mkinstalldirs \
+	intltool-extract.in intltool-update.in intltool-merge.in
+
+install-data-hook:
+if ICON_UPDATE
+	$(UPDATE_ICON_CACHE) -f -t $(DESTDIR)$(datadir)/icons/hicolor
+endif
+if MIME_UPDATE
+	$(UPDATE_MIME_DATABASE) $(DESTDIR)$(datadir)/mime
+endif
+if DESKTOP_UPDATE
+	$(UPDATE_DESKTOP_DATABASE) $(DESTDIR)$(datadir)/applications
+endif
+
+uninstall-hook:
+if ICON_UPDATE
+	$(UPDATE_ICON_CACHE) $(DESTDIR)$(datadir)/icons/hicolor
+endif
+if MIME_UPDATE
+	$(UPDATE_MIME_DATABASE) $(DESTDIR)$(datadir)/mime
+endif
+if DESKTOP_UPDATE
+	$(UPDATE_DESKTOP_DATABASE) $(DESTDIR)$(datadir)/applications
+endif

Modified: trunk/NEWS
==============================================================================
--- trunk/NEWS	(original)
+++ trunk/NEWS	Wed Feb 25 14:35:27 2009
@@ -1,169 +1,198 @@
-gnome-bluetooth 0.12.0
-======================
+ver 1.8:
+	Add additional support for using obex-client.
+	Make mice and keyboards automatically trusted.
+		
+ver 1.7:
+	Add support for HAL enabled kill switches.
+
+ver 1.6:
+	Add new visibility settings.
+	Add device setup button to properties application.
+	Add activation menu with option to setup devices.
+	Use the Icon property for device type icons.
+	Use the Alias property for device names.
+
+ver 1.5:
+	Use GIO to normalize pathnames.
+	Fix getting name of remote devices.
+	Write "time left" in human readable terms.
+
+ver 1.4:
+	Use longer D-Bus timeout for device creation.
+	Add Simple Pairing support to the wizard.
+	Add bluetooth-browse application.
+	Stop discovery when closing browse dialog.
+	Connect keyboards and mice after setup.
+	Introduce headphone device type.
+
+ver 1.3:
+	Fix pincode handling for headsets and mice.
+	Fix complete status when going back to search page.
+	Use StartDiscovery and StopDiscovery method calls.
+	Sort search lists by RSSI value.
+
+ver 1.2:
+	Add support for numeric passkey requests.
+	Add support for confirmation requests.
+	Remove --singleton from desktop files.
+
+ver 1.1:
+	Fix device status display in selection lists.
+	Enable bluetooth-sendto application.
+	Enable bluetooth-wizard application.
+
+ver 1.0:
+	Make use of the BlueZ 4.x D-Bus API.
+
+ver 0.28:
+	Import translations from Ubuntu Rosetta.
+	Add selected device name property.
+	Hide filters box when not needed.
+
+ver 0.27:
+	Import translations from Ubuntu Rosetta.
+	Improve the handling of error messages.
+	Make the display of service names translatable.
+	Remove status column for services.
+	Disable the passkey confirmation part.
+	Enable PIE by default if supported.
+	Don't optimize when debug is enabled.
+
+ver 0.26:
+	Import translations from Ubuntu Rosetta
+	Update user interface of sendto application.
+	Move analyzer application to the system category.
+
+ver 0.25:
+	Remove signal handlers when no longer needed.
+	Show OBEX errors and close server on failure.
+
+ver 0.24:
+	Update various translations.
+	Add Frisian translation.
+	Add tablets and joypads/joysticks device types.
+	Fix setting filter properties in the device selection widget.
+	Fix missing assignment of filter labels.
+	Show all the possibly known devices.
+
+ver 0.23:
+	Include more translations from Ubuntu Rosetta.
+
+ver 0.22:
+	Update for obex-data-server version 0.3 API.
+
+ver 0.21:
+	Update Polish translation.
+	Allow translation of the sendto application.
+	Fix annotation for missing translatable strings.
+	Fix translation issue for desktop files.
+
+ver 0.20:
+	Disable signals when finalizing the object.
+	Make about dialog a single instance.
+
+ver 0.19:
+	Add new Tango friendly Bluetooth icon.
+	Add support for file receiving.
+	Add support for file sharing.
+	Don't show class of device selector when HAL is used.
+	Enable the clickable links and call applications.
+	Make search button and discovery work again.
+	Fix detection of database update tools.
+
+ver 0.18:
+	Update Japanese translation.
+	Separate status icon, notification and agent handling.
+
+ver 0.17:
+	Update Polish translation.
+	Handle uint64 file size variables.
+	Use object path for session path type.
+	Use well known name to autostart the OBEX service.
+
+ver 0.16:
+	Update Polish translation.
+	Add Japanese translation.
+	Add more supported HAL classes.
+	Add support for handling default adapter changes.
+	Make device selection widget look better.
+	Make notification library a requirement.
+	Enable sendto application.
+
+ver 0.15:
+	Use buttons in notification popups.
+	Use CreateSecureDevice for input device setup.
+	Add remove button for network connections.
+	Enable connect/disconnect button for network devices.
+	Enable singleton support.
+	Update device selection widget.
+	Make tooltips HIG compliant.
+
+ver 0.14:
+	Add support for Bluetooth mime types.
+	Add support for service specific actions.
+	Ask user to confirm when deleting a bonding.
+
+ver 0.13:
+	Include translations from Ubuntu Rosetta.
+
+ver 0.12:
+	Add support for disconnecting devices.
+	Add support for changing the trust status.
+	Add trust checkbox to authorization dialog.
+
+ver 0.11:
+	Add support for BTSnoop files with baseband data.
+	Add support for showing list of bonded devices.
+	Split properties code into multiple files.
+
+ver 0.10:
+	Add Bluetooth protocol analyzer application.
+	Add Bluetooth device selection widget.
+	Add browse device menu entry.
+	Remove GTK compat code for status icon.
+
+ver 0.9:
+	Simplify the GConf callbacks.
+	Make some strings more translator friendly.
+	Don't translate command-line warnings.
+	Rename Brazilian translation file.
+	Update Polish translation.
+	Add Romanian translation.
+
+ver 0.8:
+	Add support for limited discovery mode.
+
+ver 0.7:
+	Fix handling of multiple adapters.
+	Use HAL for initial class of device setup.
+	Add basic support for authorization agent.
+	Add missing manual pages.
+	Add GConf schema support.
+	Add translatable desktop and schema files.
+	Add Finish translation.
+	Add Brazilian translation.
+	Add Polish translation.
+	Add Hungarian translation.
+
+ver 0.6:
+	Fix policy for showing status icon.
+	Add support for confirmation callback.
+	Add application for Bluetooth settings.
+
+ver 0.5:
+	Add support for periodic discovery.
+
+ver 0.4:
+	Fix handling of daemon restarts.
+	Always show the status icon.
+
+ver 0.3:
+	Add notification support.
+	Add about dialog.
 
- * Remove gnome-obex-server, as it's replaced by gnome-user-share
-
-gnome-bluetooth 0.11.0
-======================
-
- * Remove gnome-obex-send as it's replaced by bluez-gnome's bluetooth-sendto
-
-gnome-bluetooth 0.10.0
-======================
-
- * Unescape URIs when uploading files, so we don't end up with
- escaping in filenames (eg. file%20with%20space instead of "file with space")
-
-gnome-bluetooth 0.9.1
-=====================
-
- * Fix a typo that meant the obex server was crashing when receiving
- a file
-
-gnome-bluetooth 0.9.0
-=====================
-
- * Send a disconnect when we finish sending a file, fixes reception
- of files on some devices
-
- * Use new GTK+ features for the tray icon
-
- * Build fixes
-
-gnome-bluetooth 0.8.0
-=====================
-
- * Fix icons not showing up properly
-
- * Allow passing URIs as well as local filenames to gnome-obex-send
-
- * Save to ~/Desktop/Downloads by default, then fallback to ~/Desktop and ~/,
-   as Epiphany does, or to the user specified directory
-
- * Remove direct openobex and bluez-devel dependencies, as well as gnome-vfs
-
-gnome-bluetooth 0.7.0
-=====================
-
- * Fix a bad crasher that was plaguing 0.6.0 users
-
- * Have the menu icons magically appear again
-
- * Fix some memory leaks
-
- * Better message when an error occurs sending a file to a Palm
-
- * Fix build warnings
-
- * Fix Python detection for bi-arch systems
-
- * Update for libbtctl 0.6.0
-
-gnome-bluetooth 0.6.0
-=====================
-
- * gnome-obex-server: Now preserves timestamps of reserved files (Tumoas
-   Salo).
-
- * Save received files to the path specified in the
-   /apps/gnome-obex-server/savedir GConf key
-
- * Remove the nautilus extension, sending files over Bluetooth devices is
-   now handled though nautilus-sendto (Bastien Nocera)
-
- * Make gnome-bluetooth-manager run with newer PyGTK (Olav Vitters,
-   Harald Hoyer)
-
- * Make dialogues comply better with the HIG (Bastien Nocera)
-
- * Add a test program for the spinner widget (Bastien Nocera)
-
- * Build fixes (Bastien Nocera)
-
- * Updated translations:
-	cs: Miloslav Trmac 
-	de: Hendrik Brandt 
-	en_CA: Adam Weinberger 
-	en_GB: David Lodge 
-	es: Francisco Javier F. Serrador 
-	fr: RaphaÃl Tournoy 
-	hu: Gabor Kelemen 
-	ja: Takeshi AIHANA 
-	nb: Kjartan Maraas 
-	nl: Reinout van Schouwen 
-	no: Kjartan Maraas 
-	pa: Amanpreet Singh Alam 
-	pt_BR: Raphael Higino 
-	pt: Duarte Loreto 
-	ru: Leonid Kanter 
-	sq: Laurent Dhima 
-	sv: Christian Rose 
-	uk: Maxim Dziumanenko 
-	zh_CN: Funda Wang 
-
-gnome-bluetooth 0.5.1
-=====================
-
- * gnome-obex-server: Ask user what to do with received files, displaying
-   nice little box with file details.  Also displays proper name of a
-   connecting device.
-
- * libgnomebt: Now includes pkg-config file so libgnomebt can be used by other
-   packages.
-
-gnome-bluetooth 0.5.0
-=====================
-
-This release provides similar functionality to the 0.4 release, but
-with many internal rewrites and cleanups.  New features will come in
-the next release, so don't worry!  I'm aware that HIG love is needed
-in many places, but as the dialog requirements aren't stable yet, too
-much refinement is premature.
-
- * gnome-obex-send and gnome-obex-server are now based on the new
-   OBEX code in libbtctl.  This should lead to an increase in reliability.
-   gnome-obex-server now lets you decide whether a device is allowed to
-   connect or not.  The download directory can be configured from the
-   gconf key /apps/gnome-bluetooth/obex-savedir.
-
- * gnome-bluetooth-admin has been retired in favour of a new program
-   called gnome-bluetooth-manager.  This is implemented in Python, but
-   is only at an early stage right now.
-
- * gnome-vfs support has been removed.  Use gnome-bluetooth-manager to
-   view all the devices.
-
- * OBEX sending support is now available on the Nautilus context menus
-   for files.
-
- * Radical changes in underlying code!
-
-   The Bonobo component has been completely removed, in favour of a
-   normal GObject, GnomebtController, to be found in libgnomebt.  This
-   embodies all of the functionality from the Bonobo object, but without
-   getting CORBA/Bonobo involved.  It ships with Python bindings, too.
-
-   To find out how to use it, check out gnomebt-controller-test.c in src/
-   for C, and the applications in the python/ directory for Python.
-
-   Porting your code to this new object should make your programs smaller
-   and more reliable.
-
- * New widgets include GnomebtChooser, GnomebtPermissionDialog, 
-   GnomebtSpinner.
-
--- Edd Dumbill,  Thu Jun 10 15:26:09 BST 2004
-
-
-
-gnome-bluetooth 0.4.1
-=====================
-
-* Added gnome-obex-send program, which sends files via OBEX push to a
-  destination server.
-
-* Added gnome-vfs module, go to bluetooth:/// in nautilus to see which
-  remote devices have been discovered.
-
--- Edd Dumbill 2003-06-07
+ver 0.2:
+	Add status icon support.
 
+ver 0.1:
+	Initial public release.

Modified: trunk/README
==============================================================================
--- trunk/README	(original)
+++ trunk/README	Wed Feb 25 14:35:27 2009
@@ -1,93 +1,24 @@
-GNOME Bluetooth Subsystem
-=========================
+GNOME Bluetooth
+===============
 
-Maintained by Edd Dumbill <edd usefulinc com>
+gnome-bluetooth is a fork of bluez-gnome focused on integration with
+the GNOME desktop environment.
 
-The GNOME Bluetooth Subsystem is being developed with the ultimate
-intention of being submitted to the GNOME desktop project.
+gnome-bluetooth is the historical name of the first GNOME focused
+Bluetooth effort for Linux. As such, when the need came to name
+this project, we selected something that was still in the people's
+minds.
 
-The software is in its early stages right now.  See the NEWS file for
-the latest changes in this release.
+You can find historical versions of gnome-bluetooth in the GNOME SVN
+repository at:
+  http://svn.gnome.org/viewvc/gnome-bluetooth/trunk/
 
-This package contains a controller class, GnomebtController, to control
-Bluetooth devices, and a simple GUI to explore which devices are
-available (gnome-bluetooth-manager).  An OBEX server is available,
-gnome-obex-server.  This will receive files sent via Bluetooth to your
-PC, and save them in your home directory.  The program gnome-obex-send
-enables you to send files.  It is used by the Nautilus component --
-select the files you want to send and choose "Send via Bluetooth..."
-from the context menu.
+gnome-bluetooth does not include the bluez-gnome analyzer, or the
+proximity tool.
 
-There is a mailing list discussing this software at 
-http://lists.usefulinc.com/mailman/listinfo/gnome-bluetooth
+Copyright
+---------
 
-The tools in obex/ and python/ are licensed under the GNU General
-Public License, Version 2.   See the file COPYING, which accompanies
-this distribution, and the section "License for gnome-bluetooth
-applications" below.
-
-The libgnomebt library in src/ is licensed under the GNU Lesser General
-Public License.  See the file COPYING.gnomebt, which accompanies this
-distribution, and the section "License for libgnomebt" belows.
-
-
-Building
-========
-
-You need:
-
-* Bluetooth libs, utils installed installed (bluez.sf.net)
-  Version 2.7 or better recommended.
-
-* libbtctl 0.4, available from http://usefulinc.com/software/gnome-bluetooth,
-  or GNOME CVS.
-
-* Bluetooth kernel modules installed (either kernel 2.4.22 or get patches
-  from bluez.sf.net).
-
-* These GNOME 2 packages:
-  	gobject-2.0 libgnomeui-2.0 >= 1.110.0 libbonobo-2.0
-	bonobo-activation-2.0 gconf-2.0 gob2 pygtk-2.2 python2.2/2.3
-
-
-License for libgnomebt
-======================
-
-    libgnomebt -- GNOME Bluetooth libraries
-    Copyright (C) 2003-2004 Edd Dumbill
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library 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
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
-
-
-License for gnome-bluetooth applications
-========================================
-
-    gnome-bluetooth obex and administration tools
-    Copyright (C) 2003-2004 Edd Dumbill
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+The original bluez-gnome is:
+Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
 

Added: trunk/applet/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/applet/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,34 @@
+
+bin_PROGRAMS = bluetooth-applet
+
+bluetooth_applet_SOURCES = main.c notify.h notify.c agent.h agent.c
+
+bluetooth_applet_LDADD = $(top_builddir)/common/libcommon.a \
+			@NOTIFY_LIBS@ @GCONF_LIBS@ @GTK_LIBS@ @DBUS_LIBS@
+
+noinst_PROGRAMS = test-agentdialog
+
+test_agentdialog_SOURCES = test-agentdialog.c notify.h notify.c
+
+test_agentdialog_LDADD = $(top_builddir)/common/libcommon.a \
+				@NOTIFY_LIBS@ @GTK_LIBS@ @DBUS_LIBS@
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GTK_CFLAGS@ @GCONF_CFLAGS@ @NOTIFY_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common
+
+man_MANS = bluetooth-applet.1
+
+autostartdir = $(sysconfdir)/xdg/autostart
+
+autostart_in_files = bluetooth-applet.desktop.in
+
+autostart_DATA = $(autostart_in_files:.desktop.in=.desktop)
+
+ INTLTOOL_DESKTOP_RULE@
+
+CLEANFILES = $(autostart_DATA)
+
+EXTRA_DIST = $(man_MANS) $(autostart_in_files)
+
+MAINTAINERCLEANFILES = Makefile.in

Added: trunk/applet/agent.c
==============================================================================
--- (empty file)
+++ trunk/applet/agent.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,888 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <bluetooth-client.h>
+#include <bluetooth-agent.h>
+
+#include "notify.h"
+#include "agent.h"
+
+static BluetoothClient *client;
+static GtkTreeModel *adapter_model;
+
+typedef enum {
+	AGENT_ERROR_REJECT
+} AgentError;
+
+#define AGENT_ERROR (agent_error_quark())
+
+#define AGENT_ERROR_TYPE (agent_error_get_type()) 
+
+static GQuark agent_error_quark(void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string("agent");
+
+	return quark;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+static GType agent_error_get_type(void)
+{
+	static GType etype = 0;
+	if (etype == 0) {
+		static const GEnumValue values[] = {
+			ENUM_ENTRY(AGENT_ERROR_REJECT, "Rejected"),
+			{ 0, 0, 0 }
+		};
+
+		etype = g_enum_register_static("agent", values);
+	}
+
+	return etype;
+}
+
+static GList *input_list = NULL;
+
+struct input_data {
+	char *path;
+	char *uuid;
+	gboolean numeric;
+	DBusGProxy *device;
+	DBusGMethodInvocation *context;
+	GtkWidget *dialog;
+	GtkWidget *button;
+	GtkWidget *entry;
+};
+
+static gint input_compare(gconstpointer a, gconstpointer b)
+{
+	struct input_data *a_data = (struct input_data *) a;
+	struct input_data *b_data = (struct input_data *) b;
+
+	return g_ascii_strcasecmp(a_data->path, b_data->path);
+}
+
+static void input_free(struct input_data *input)
+{
+	gtk_widget_destroy(input->dialog);
+
+	input_list = g_list_remove(input_list, input);
+
+	if (input->device != NULL)
+		g_object_unref(input->device);
+
+	g_free(input->uuid);
+	g_free(input->path);
+	g_free(input);
+
+	if (g_list_length(input_list) == 0)
+		disable_blinking();
+}
+
+static void passkey_callback(GtkWidget *dialog,
+				gint response, gpointer user_data)
+{
+	struct input_data *input = user_data;
+
+	if (response == GTK_RESPONSE_ACCEPT) {
+		const char *text;
+		text = gtk_entry_get_text(GTK_ENTRY(input->entry));
+
+		if (input->numeric == TRUE) {
+			guint passkey = atoi(text);
+			dbus_g_method_return(input->context, passkey);
+		} else
+			dbus_g_method_return(input->context, text);
+	} else {
+		GError *error;
+		error = g_error_new(AGENT_ERROR, AGENT_ERROR_REJECT,
+						"Pairing request rejected");
+		dbus_g_method_return_error(input->context, error);
+	}
+
+	input_free(input);
+}
+
+static void confirm_callback(GtkWidget *dialog,
+				gint response, gpointer user_data)
+{
+	struct input_data *input = user_data;
+
+	if (response != GTK_RESPONSE_YES) {
+		GError *error;
+		error = g_error_new(AGENT_ERROR, AGENT_ERROR_REJECT,
+					"Confirmation request rejected");
+		dbus_g_method_return_error(input->context, error);
+	} else
+		dbus_g_method_return(input->context);
+
+	input_free(input);
+}
+
+static void set_trusted(struct input_data *input)
+{
+	GValue value = { 0 };
+	gboolean active;
+
+	if (input->device == NULL)
+		return;
+
+	active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(input->button));
+	if (active == FALSE)
+		return;
+
+	g_value_init(&value, G_TYPE_BOOLEAN);
+	g_value_set_boolean(&value, TRUE);
+
+	dbus_g_proxy_call(input->device, "SetProperty", NULL,
+			G_TYPE_STRING, "Trusted",
+			G_TYPE_VALUE, &value, G_TYPE_INVALID, G_TYPE_INVALID);
+
+	g_value_unset(&value);
+}
+
+static void auth_callback(GtkWidget *dialog,
+				gint response, gpointer user_data)
+{
+	struct input_data *input = user_data;
+
+	if (response == GTK_RESPONSE_YES) {
+		set_trusted(input);
+		dbus_g_method_return(input->context);
+	} else {
+		GError *error;
+		error = g_error_new(AGENT_ERROR, AGENT_ERROR_REJECT,
+					"Authorization request rejected");
+		dbus_g_method_return_error(input->context, error);
+	}
+
+	input_free(input);
+}
+
+static void insert_callback(GtkEditable *editable, const gchar *text,
+			gint length, gint *position, gpointer user_data)
+{
+	struct input_data *input = user_data;
+	gint i;
+
+	if (input->numeric == FALSE)
+		return;
+
+	for (i = 0; i < length; i++) {
+		if (g_ascii_isdigit(text[i]) == FALSE) {
+			g_signal_stop_emission_by_name(editable,
+							"insert-text");
+		}
+	}
+}
+
+static void changed_callback(GtkWidget *editable, gpointer user_data)
+{
+	struct input_data *input = user_data;
+	const gchar *text;
+
+	text = gtk_entry_get_text(GTK_ENTRY(input->entry));
+
+	gtk_widget_set_sensitive(input->button, *text != '\0' ? TRUE : FALSE);
+}
+
+static void toggled_callback(GtkWidget *button, gpointer user_data)
+{
+	struct input_data *input = user_data;
+	gboolean mode;
+
+	mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+
+	gtk_entry_set_visibility(GTK_ENTRY(input->entry), mode);
+}
+
+static void passkey_dialog(DBusGProxy *adapter, DBusGProxy *device,
+		const char *address, const char *name, gboolean numeric,
+						DBusGMethodInvocation *context)
+{
+	GtkWidget *dialog;
+	GtkWidget *button;
+	GtkWidget *image;
+	GtkWidget *label;
+	GtkWidget *entry;
+	GtkWidget *table;
+	GtkWidget *vbox;
+	struct input_data *input;
+	gchar *markup;
+
+	input = g_try_malloc0(sizeof(*input));
+	if (!input)
+		return;
+
+	input->path = g_strdup(dbus_g_proxy_get_path(adapter));
+	input->numeric = numeric;
+	input->context = context;
+	input->device = g_object_ref(device);
+
+	dialog = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(dialog), _("Authentication request"));
+	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+	gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
+	gtk_window_set_urgency_hint(GTK_WINDOW(dialog), TRUE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	input->dialog = dialog;
+
+	button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+				GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
+	button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+					GTK_STOCK_OK, GTK_RESPONSE_ACCEPT);
+	gtk_widget_grab_default(button);
+	gtk_widget_set_sensitive(button, FALSE);
+	input->button = button;
+
+	table = gtk_table_new(5, 2, FALSE);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 20);
+	gtk_container_set_border_width(GTK_CONTAINER(table), 12);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
+	image = gtk_image_new_from_icon_name(GTK_STOCK_DIALOG_AUTHENTICATION,
+							GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment(GTK_MISC(image), 0.0, 0.0);
+	gtk_table_attach(GTK_TABLE(table), image, 0, 1, 0, 5,
+						GTK_SHRINK, GTK_FILL, 0, 0);
+	vbox = gtk_vbox_new(FALSE, 6);
+
+	label = gtk_label_new(_("Pairing request for device:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+	gtk_table_attach(GTK_TABLE(table), vbox, 1, 2, 0, 1,
+				GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+	label = gtk_label_new(NULL);
+	markup = g_strdup_printf("<b>%s</b>", name);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_widget_set_size_request(GTK_WIDGET(label), 280, -1);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+	vbox = gtk_vbox_new(FALSE, 6);
+
+	label = gtk_label_new(_("Enter passkey for authentication:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+	gtk_table_attach(GTK_TABLE(table), vbox, 1, 2, 2, 3,
+				GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+	entry = gtk_entry_new();
+	if (numeric == TRUE) {
+		gtk_entry_set_max_length(GTK_ENTRY(entry), 6);
+		gtk_entry_set_width_chars(GTK_ENTRY(entry), 6);
+		g_signal_connect(G_OBJECT(entry), "insert-text",
+					G_CALLBACK(insert_callback), input);
+	} else {
+		gtk_entry_set_max_length(GTK_ENTRY(entry), 16);
+		gtk_entry_set_width_chars(GTK_ENTRY(entry), 16);
+		gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+	}
+	gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+	input->entry = entry;
+	g_signal_connect(G_OBJECT(entry), "changed",
+				G_CALLBACK(changed_callback), input);
+	gtk_container_add(GTK_CONTAINER(vbox), entry);
+
+	if (numeric == FALSE) {
+		button = gtk_check_button_new_with_label(_("Show input"));
+		g_signal_connect(G_OBJECT(button), "toggled",
+					G_CALLBACK(toggled_callback), input);
+		gtk_container_add(GTK_CONTAINER(vbox), button);
+	}
+
+	input_list = g_list_append(input_list, input);
+
+	g_signal_connect(G_OBJECT(dialog), "response",
+				G_CALLBACK(passkey_callback), input);
+
+	enable_blinking();
+}
+
+static void display_dialog(DBusGProxy *adapter, DBusGProxy *device,
+		const char *address, const char *name, const char *value,
+				guint entered, DBusGMethodInvocation *context)
+{
+}
+
+static void confirm_dialog(DBusGProxy *adapter, DBusGProxy *device,
+		const char *address, const char *name, const char *value,
+						DBusGMethodInvocation *context)
+{
+	GtkWidget *dialog;
+	GtkWidget *button;
+	GtkWidget *image;
+	GtkWidget *label;
+	GtkWidget *table;
+	GtkWidget *vbox;
+	gchar *markup;
+	struct input_data *input;
+
+	input = g_try_malloc0(sizeof(*input));
+	if (!input)
+		return;
+
+	input->path = g_strdup(dbus_g_proxy_get_path(adapter));
+	input->device = g_object_ref(device);
+	input->context = context;
+
+	dialog = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(dialog), _("Confirmation request"));
+	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+	gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
+	gtk_window_set_urgency_hint(GTK_WINDOW(dialog), TRUE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	input->dialog = dialog;
+
+	button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+					GTK_STOCK_NO, GTK_RESPONSE_NO);
+	button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+					GTK_STOCK_YES, GTK_RESPONSE_YES);
+
+	table = gtk_table_new(5, 2, FALSE);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 20);
+	gtk_container_set_border_width(GTK_CONTAINER(table), 12);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
+
+	image = gtk_image_new_from_icon_name(GTK_STOCK_DIALOG_AUTHENTICATION,
+							GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment(GTK_MISC(image), 0.0, 0.0);
+	gtk_table_attach(GTK_TABLE(table), image, 0, 1, 0, 5,
+						GTK_SHRINK, GTK_FILL, 0, 0);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	label = gtk_label_new(_("Pairing request for device:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+	gtk_table_attach(GTK_TABLE(table), vbox, 1, 2, 0, 1,
+				GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+	label = gtk_label_new(NULL);
+	markup = g_strdup_printf("<b>%s</b>", name);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_widget_set_size_request(GTK_WIDGET(label), 280, -1);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	label = gtk_label_new(_("Confirm value for authentication:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+	gtk_table_attach(GTK_TABLE(table), vbox, 1, 2, 2, 3,
+				GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+	label = gtk_label_new(NULL);
+	markup = g_strdup_printf("<b>%s</b>\n", value);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+
+	input_list = g_list_append(input_list, input);
+
+	g_signal_connect(G_OBJECT(dialog), "response",
+				G_CALLBACK(confirm_callback), input);
+
+	enable_blinking();
+}
+
+static void auth_dialog(DBusGProxy *adapter, DBusGProxy *device,
+		const char *address, const char *name, const char *uuid,
+						DBusGMethodInvocation *context)
+{
+	GtkWidget *dialog;
+	GtkWidget *button;
+	GtkWidget *image;
+	GtkWidget *label;
+	GtkWidget *table;
+	GtkWidget *vbox;
+	gchar *markup, *text;
+	struct input_data *input;
+
+	input = g_try_malloc0(sizeof(*input));
+	if (!input)
+		return;
+
+	input->path = g_strdup(dbus_g_proxy_get_path(adapter));
+	input->uuid = g_strdup(uuid);
+	input->device = g_object_ref(device);
+	input->context = context;
+
+	dialog = gtk_dialog_new();
+	gtk_window_set_title(GTK_WINDOW(dialog), _("Authorization request"));
+	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
+	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+	gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
+	gtk_window_set_urgency_hint(GTK_WINDOW(dialog), TRUE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	input->dialog = dialog;
+
+	button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+					GTK_STOCK_NO, GTK_RESPONSE_NO);
+	button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+					GTK_STOCK_YES, GTK_RESPONSE_YES);
+
+	table = gtk_table_new(5, 2, FALSE);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 20);
+	gtk_container_set_border_width(GTK_CONTAINER(table), 12);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
+
+	image = gtk_image_new_from_icon_name(GTK_STOCK_DIALOG_AUTHENTICATION,
+							GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment(GTK_MISC(image), 0.0, 0.0);
+	gtk_table_attach(GTK_TABLE(table), image, 0, 1, 0, 5,
+						GTK_SHRINK, GTK_FILL, 0, 0);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	label = gtk_label_new(_("Authorization request for device:"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1,
+				GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+	label = gtk_label_new(NULL);
+	markup = g_strdup_printf("<b>%s</b>", name);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_widget_set_size_request(GTK_WIDGET(label), 280, -1);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+	gtk_table_attach(GTK_TABLE(table), vbox, 1, 2, 2, 3,
+				GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+
+	label = gtk_label_new(NULL);
+	/* translators: Whether to grant access to a particular service
+	 * to the device mentioned */
+	markup = g_strdup_printf("<i>%s</i>", uuid);
+	text = g_strdup_printf(_("Grant access to %s?"), markup);
+	g_free(markup);
+	markup = g_strdup_printf("%s\n", text);
+	gtk_label_set_markup(GTK_LABEL(label), markup);
+	g_free(markup);
+	g_free(text);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+	gtk_container_add(GTK_CONTAINER(vbox), label);
+
+	button = gtk_check_button_new_with_label(_("Always grant access"));
+	input->button = button;
+	gtk_container_add(GTK_CONTAINER(vbox), button);
+
+	input_list = g_list_append(input_list, input);
+
+	g_signal_connect(G_OBJECT(dialog), "response",
+				G_CALLBACK(auth_callback), input);
+
+	enable_blinking();
+}
+
+static void show_dialog(gpointer data, gpointer user_data)
+{
+	struct input_data *input = data;
+
+	gtk_widget_show_all(input->dialog);
+
+	gtk_window_present(GTK_WINDOW(input->dialog));
+}
+
+static void notification_closed(GObject *object, gpointer user_data)
+{
+	g_list_foreach(input_list, show_dialog, NULL);
+
+	disable_blinking();
+}
+
+#ifndef DBUS_TYPE_G_DICTIONARY
+#define DBUS_TYPE_G_DICTIONARY \
+	(dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
+#endif
+
+static gboolean device_get_properties(DBusGProxy *proxy,
+					GHashTable **hash, GError **error)
+{
+	return dbus_g_proxy_call(proxy, "GetProperties", error,
+		G_TYPE_INVALID, DBUS_TYPE_G_DICTIONARY, hash, G_TYPE_INVALID);
+}
+
+static gboolean pincode_request(DBusGMethodInvocation *context,
+					DBusGProxy *device, gpointer user_data)
+{
+	DBusGProxy *adapter = user_data;
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *alias;
+	gchar *name, *line;
+
+	device_get_properties(device, &hash, NULL);
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		alias = value ? g_value_get_string(value) : NULL;
+	} else {
+		address = NULL;
+		alias = NULL;
+	}
+
+	if (alias) {
+		if (g_strrstr(alias, address))
+			name = g_strdup(alias);
+		else
+			name = g_strdup_printf("%s (%s)", alias, address);
+	} else
+		name = g_strdup(address);
+
+	passkey_dialog(adapter, device, address, name, FALSE, context);
+
+	/* translators: this is a popup telling you a particular device
+	 * has asked for pairing */
+	line = g_strdup_printf(_("Pairing request for %s"), name);
+
+	g_free(name);
+
+	show_notification(_("Bluetooth device"),
+					line, _("Enter PIN code"), 0,
+					G_CALLBACK(notification_closed));
+
+	g_free(line);
+
+	return TRUE;
+}
+
+static gboolean passkey_request(DBusGMethodInvocation *context,
+					DBusGProxy *device, gpointer user_data)
+{
+	DBusGProxy *adapter = user_data;
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *alias;
+	gchar *name, *line;
+
+	device_get_properties(device, &hash, NULL);
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		alias = value ? g_value_get_string(value) : NULL;
+	} else {
+		address = NULL;
+		alias = NULL;
+	}
+
+	if (alias) {
+		if (g_strrstr(alias, address))
+			name = g_strdup(alias);
+		else
+			name = g_strdup_printf("%s (%s)", alias, address);
+	} else
+		name = g_strdup(address);
+
+	passkey_dialog(adapter, device, address, name, TRUE, context);
+
+	/* translators: this is a popup telling you a particular device
+	 * has asked for pairing */
+	line = g_strdup_printf(_("Pairing request for %s"), name);
+
+	g_free(name);
+
+	show_notification(_("Bluetooth device"),
+					line, _("Enter passkey"), 0,
+					G_CALLBACK(notification_closed));
+
+	g_free(line);
+
+	return TRUE;
+}
+
+static gboolean display_request(DBusGMethodInvocation *context,
+				DBusGProxy *device, guint passkey,
+					guint entered, gpointer user_data)
+{
+	DBusGProxy *adapter = user_data;
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *alias;
+	gchar *name, *line, *text;
+
+	device_get_properties(device, &hash, NULL);
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		alias = value ? g_value_get_string(value) : NULL;
+	} else {
+		address = NULL;
+		alias = NULL;
+	}
+
+	if (alias) {
+		if (g_strrstr(alias, address))
+			name = g_strdup(alias);
+		else
+			name = g_strdup_printf("%s (%s)", alias, address);
+	} else
+		name = g_strdup(address);
+
+	text = g_strdup_printf("%d", passkey);
+	display_dialog(adapter, device, address, name, text, entered, context);
+	g_free(text);
+
+	/* translators: this is a popup telling you a particular device
+	 * has asked for pairing */
+	line = g_strdup_printf(_("Pairing request for %s"), name);
+
+	g_free(name);
+
+	show_notification(_("Bluetooth device"),
+					line, _("Enter passkey"), 0,
+					G_CALLBACK(notification_closed));
+
+	g_free(line);
+
+	return TRUE;
+}
+
+static gboolean confirm_request(DBusGMethodInvocation *context,
+			DBusGProxy *device, guint passkey, gpointer user_data)
+{
+	DBusGProxy *adapter = user_data;
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *alias;
+	gchar *name, *line, *text;
+
+	device_get_properties(device, &hash, NULL);
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		alias = value ? g_value_get_string(value) : NULL;
+	} else {
+		address = NULL;
+		alias = NULL;
+	}
+
+	if (alias) {
+		if (g_strrstr(alias, address))
+			name = g_strdup(alias);
+		else
+			name = g_strdup_printf("%s (%s)", alias, address);
+	} else
+		name = g_strdup(address);
+
+	text = g_strdup_printf("%d", passkey);
+	confirm_dialog(adapter, device, address, name, text, context);
+	g_free(text);
+
+	/* translators: this is a popup telling you a particular device
+	 * has asked for pairing */
+	line = g_strdup_printf(_("Confirmation request for %s"), name);
+
+	g_free(name);
+
+	show_notification(_("Bluetooth device"),
+					line, _("Confirm passkey"), 0,
+					G_CALLBACK(notification_closed));
+
+	g_free(line);
+
+	return TRUE;
+}
+
+static gboolean authorize_request(DBusGMethodInvocation *context,
+		DBusGProxy *device, const char *uuid, gpointer user_data)
+{
+	DBusGProxy *adapter = user_data;
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *alias;
+	gchar *name, *line;
+
+	device_get_properties(device, &hash, NULL);
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		alias = value ? g_value_get_string(value) : NULL;
+	} else {
+		address = NULL;
+		alias = NULL;
+	}
+
+	if (alias) {
+		if (g_strrstr(alias, address))
+			name = g_strdup(alias);
+		else
+			name = g_strdup_printf("%s (%s)", alias, address);
+	} else
+		name = g_strdup(address);
+
+	auth_dialog(adapter, device, address, name, uuid, context);
+
+	line = g_strdup_printf(_("Authorization request for %s"), name);
+
+	g_free(name);
+
+	show_notification(_("Bluetooth device"),
+					line, _("Check authorization"), 0,
+					G_CALLBACK(notification_closed));
+
+	g_free(line);
+
+	return TRUE;
+}
+
+static gboolean cancel_request(DBusGMethodInvocation *context,
+							gpointer user_data)
+{
+	DBusGProxy *adapter = user_data;
+	GList *list;
+	GError *result;
+	struct input_data *input;
+
+	input = g_try_malloc0(sizeof(*input));
+	if (!input)
+		return FALSE;
+
+	input->path = g_strdup(dbus_g_proxy_get_path(adapter));
+
+	list = g_list_find_custom(input_list, input, input_compare);
+
+	g_free(input->path);
+	g_free(input);
+
+	if (!list || !list->data)
+		return FALSE;
+
+	input = list->data;
+
+	close_notification();
+
+	result = g_error_new(AGENT_ERROR, AGENT_ERROR_REJECT,
+						"Agent callback canceled");
+
+	dbus_g_method_return_error(input->context, result);
+
+	input_free(input);
+
+	return TRUE;
+}
+
+static gboolean adapter_insert(GtkTreeModel *model, GtkTreePath *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	BluetoothAgent *agent;
+	DBusGProxy *adapter;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PROXY, &adapter, -1);
+
+	if (adapter == NULL)
+		return FALSE;
+
+	agent = bluetooth_agent_new();
+
+	bluetooth_agent_set_pincode_func(agent, pincode_request, adapter);
+	bluetooth_agent_set_passkey_func(agent, passkey_request, adapter);
+	bluetooth_agent_set_display_func(agent, display_request, adapter);
+	bluetooth_agent_set_confirm_func(agent, confirm_request, adapter);
+	bluetooth_agent_set_authorize_func(agent, authorize_request, adapter);
+	bluetooth_agent_set_cancel_func(agent, cancel_request, adapter);
+
+	bluetooth_agent_register(agent, adapter);
+
+	return FALSE;
+}
+
+static void adapter_added(GtkTreeModel *model, GtkTreePath *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	adapter_insert(model, path, iter, user_data);
+}
+
+int setup_agents(void)
+{
+	dbus_g_error_domain_register(AGENT_ERROR, "org.bluez.Error",
+							AGENT_ERROR_TYPE);
+
+	client = bluetooth_client_new();
+
+	adapter_model = bluetooth_client_get_adapter_model(client);
+
+	g_signal_connect(G_OBJECT(adapter_model), "row-inserted",
+					G_CALLBACK(adapter_added), NULL);
+
+	gtk_tree_model_foreach(adapter_model, adapter_insert, NULL);
+
+	return 0;
+}
+
+void cleanup_agents(void)
+{
+	g_object_unref(adapter_model);
+
+	g_object_unref(client);
+}
+
+void show_agents(void)
+{
+	close_notification();
+
+	g_list_foreach(input_list, show_dialog, NULL);
+
+	disable_blinking();
+}

Added: trunk/applet/agent.h
==============================================================================
--- (empty file)
+++ trunk/applet/agent.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int setup_agents(void);
+void cleanup_agents(void);
+
+void show_agents(void);

Added: trunk/applet/bluetooth-applet.1
==============================================================================
--- (empty file)
+++ trunk/applet/bluetooth-applet.1	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,30 @@
+.TH BLUETOOTH-APPLET 1 "Oct 4, 2006" "bluez-gnome" "Linux User's Manual"
+.SH NAME
+bluetooth-applet - GNOME applet for prompting the user for a Bluetooth passkey (PIN)
+.SH SYNOPSIS
+.B bluetooth-applet
+.SH DESCRIPTION
+.I bluetooth-applet
+will stay in your GNOME panel as a Bluetooth icon and will pop up a dialog
+whenever a passkey (aka PIN) is required from the Linux Bluetooth stack.
+.I bluetooth-applet
+is part of bluez-gnome, see also http://www.bluez.org
+.SH OPTIONS
+.I bluetooth-applet
+takes no options
+.SH AUTHOR
+Marcel Holtmann <marcel holtmann org>
+.SH LICENSE
+bluetooth-applet is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Added: trunk/applet/bluetooth-applet.desktop.in
==============================================================================
--- (empty file)
+++ trunk/applet/bluetooth-applet.desktop.in	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Bluetooth Manager
+_Comment=Bluetooth Manager applet
+Icon=bluetooth
+Exec=bluetooth-applet
+Terminal=false
+Type=Application
+Categories=
+OnlyShowIn=GNOME;

Added: trunk/applet/main.c
==============================================================================
--- (empty file)
+++ trunk/applet/main.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,447 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <gconf/gconf-client.h>
+
+#include <bluetooth-instance.h>
+#include <bluetooth-client.h>
+
+#include "notify.h"
+#include "agent.h"
+
+static BluetoothClient *client;
+static GtkTreeModel *adapter_model;
+static gboolean adapter_present = FALSE;
+
+enum {
+	ICON_POLICY_NEVER,
+	ICON_POLICY_ALWAYS,
+	ICON_POLICY_PRESENT,
+};
+
+static int icon_policy = ICON_POLICY_PRESENT;
+
+#define PREF_DIR		"/apps/bluetooth-manager"
+#define PREF_ICON_POLICY	PREF_DIR "/icon_policy"
+#if 0
+#define PREF_RECEIVE_ENABLED	PREF_DIR "/receive_enabled"
+#define PREF_SHARING_ENABLED	PREF_DIR "/sharing_enabled"
+#endif
+
+static GConfClient* gconf;
+
+static GtkWidget *menuitem_sendto = NULL;
+static GtkWidget *menuitem_browse = NULL;
+
+static void open_uri(GtkWindow *parent, const char *uri)
+{
+	GtkWidget *dialog;
+	GdkScreen *screen;
+	GError *error = NULL;
+	gchar *cmdline;
+
+	screen = gtk_window_get_screen(parent);
+
+	cmdline = g_strconcat("xdg-open ", uri, NULL);
+
+	if (gdk_spawn_command_line_on_screen(screen,
+						cmdline, &error) == FALSE) {
+		dialog = gtk_message_dialog_new(parent,
+			GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
+					GTK_BUTTONS_CLOSE, error->message);
+		gtk_dialog_run(GTK_DIALOG(dialog));
+		gtk_widget_destroy(dialog);
+		g_error_free(error);
+	}
+
+	g_free(cmdline);
+}
+
+static void about_url_hook(GtkAboutDialog *dialog,
+					const gchar *url, gpointer data)
+{
+	open_uri(GTK_WINDOW(dialog), url);
+}
+
+static void about_email_hook(GtkAboutDialog *dialog,
+					const gchar *email, gpointer data)
+{
+	gchar *uri;
+
+	uri = g_strconcat("mailto:";, email, NULL);
+	open_uri(GTK_WINDOW(dialog), uri);
+	g_free(uri);
+}
+
+static void about_callback(GtkWidget *item, gpointer user_data)
+{
+	const gchar *authors[] = {
+		"Marcel Holtmann <marcel holtmann org>",
+		"Bastien Nocera <hadess hadess net>",
+		NULL
+	};
+	const gchar *artists[] = {
+		"Andreas Nilsson <nisses mail home se>",
+		NULL,
+	};
+
+	gtk_about_dialog_set_url_hook(about_url_hook, NULL, NULL);
+	gtk_about_dialog_set_email_hook(about_email_hook, NULL, NULL);
+
+	gtk_show_about_dialog(NULL, "version", VERSION,
+		"copyright", "Copyright \xc2\xa9 2005-2008 Marcel Holtmann",
+		"comments", _("A Bluetooth manager for the GNOME desktop"),
+		"authors", authors,
+		"artists", artists,
+		"translator-credits", _("translator-credits"),
+		"website", "http://www.bluez.org/";,
+		"website-label", "www.bluez.org",
+		"logo-icon-name", "bluetooth", NULL);
+}
+
+static void settings_callback(GObject *widget, gpointer user_data)
+{
+	const char *command = "bluetooth-properties";
+
+	if (!g_spawn_command_line_async(command, NULL))
+		g_printerr("Couldn't execute command: %s\n", command);
+}
+
+static void browse_callback(GObject *widget, gpointer user_data)
+{
+	const char *command = "bluetooth-browse";
+
+	if (!g_spawn_command_line_async(command, NULL))
+		g_printerr("Couldn't execute command: %s\n", command);
+}
+
+static void sendto_callback(GObject *widget, gpointer user_data)
+{
+	const char *command = "bluetooth-sendto";
+
+	if (!g_spawn_command_line_async(command, NULL))
+		g_printerr("Couldn't execute command: %s\n", command);
+}
+
+static void wizard_callback(GObject *widget, gpointer user_data)
+{
+	const char *command = "bluetooth-wizard";
+
+	if (!g_spawn_command_line_async(command, NULL))
+		g_printerr("Couldn't execute command: %s\n", command);
+}
+
+static void activate_callback(GObject *widget, gpointer user_data)
+{
+	guint32 activate_time = gtk_get_current_event_time();
+	GtkWidget *menu;
+	GtkWidget *item;
+
+	if (query_blinking() == TRUE) {
+		show_agents();
+		return;
+	}
+
+	menu = gtk_menu_new();
+
+	item = gtk_menu_item_new_with_label(_("Setup new device..."));
+	g_signal_connect(item, "activate",
+				G_CALLBACK(wizard_callback), NULL);
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
+			gtk_status_icon_position_menu,
+			GTK_STATUS_ICON(widget), 1, activate_time);
+}
+
+static gboolean program_available(const char *program)
+{
+	gchar *path;
+
+	path = g_find_program_in_path(program);
+	if (path == NULL)
+		return FALSE;
+
+	g_free(path);
+
+	return TRUE;
+}
+
+static void popup_callback(GObject *widget, guint button,
+				guint activate_time, gpointer user_data)
+{
+	GtkWidget *menu = user_data;
+
+	gtk_widget_set_sensitive(menuitem_sendto,
+				program_available("obex-data-server") &&
+						adapter_present == TRUE);
+
+	gtk_widget_set_sensitive(menuitem_browse,
+					program_available("nautilus") &&
+						adapter_present == TRUE);
+
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
+			gtk_status_icon_position_menu,
+			GTK_STATUS_ICON(widget), button, activate_time);
+}
+
+static GtkWidget *create_popupmenu(void)
+{
+	GtkWidget *menu;
+	GtkWidget *item;
+
+	menu = gtk_menu_new();
+
+	item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
+	g_signal_connect(item, "activate",
+				G_CALLBACK(settings_callback), NULL);
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	item = gtk_menu_item_new_with_label(_("Setup new device..."));
+	g_signal_connect(item, "activate",
+				G_CALLBACK(wizard_callback), NULL);
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	item = gtk_separator_menu_item_new();
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	item = gtk_image_menu_item_new_with_label(_("Send files to device..."));
+	g_signal_connect(item, "activate",
+				G_CALLBACK(sendto_callback), NULL);
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	menuitem_sendto = item;
+
+	item = gtk_image_menu_item_new_with_label(_("Browse files on device..."));
+	g_signal_connect(item, "activate",
+				G_CALLBACK(browse_callback), NULL);
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	menuitem_browse = item;
+
+	item = gtk_separator_menu_item_new();
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
+	g_signal_connect(item, "activate",
+				G_CALLBACK(about_callback), NULL);
+	gtk_widget_show(item);
+	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+	return menu;
+}
+
+static void adapter_added(GtkTreeModel *model, GtkTreePath *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	adapter_present = TRUE;
+
+	if (icon_policy != ICON_POLICY_NEVER)
+		show_icon();
+}
+
+static void adapter_removed(GtkTreeModel *model, GtkTreePath *path,
+							gpointer user_data)
+{
+	if (gtk_tree_model_iter_n_children(model, NULL) < 1) {
+		adapter_present = FALSE;
+
+		if (icon_policy != ICON_POLICY_ALWAYS)
+			hide_icon();
+	}
+}
+
+static void update_icon_visibility()
+{
+	if (icon_policy == ICON_POLICY_NEVER)
+		hide_icon();
+	else if (icon_policy == ICON_POLICY_ALWAYS)
+		show_icon();
+	else if (icon_policy == ICON_POLICY_PRESENT) {
+		if (adapter_present == TRUE)
+			show_icon();
+		else
+			hide_icon();
+	}
+}
+
+static GConfEnumStringPair icon_policy_enum_map [] = {
+	{ ICON_POLICY_NEVER,	"never"		},
+	{ ICON_POLICY_ALWAYS,	"always"	},
+	{ ICON_POLICY_PRESENT,	"present"	},
+	{ ICON_POLICY_PRESENT,	NULL		},
+};
+
+static void gconf_callback(GConfClient *client, guint cnxn_id,
+					GConfEntry *entry, gpointer user_data)
+{
+	GConfValue *value;
+
+	value = gconf_entry_get_value(entry);
+	if (value == NULL)
+		return;
+
+	if (g_str_equal(entry->key, PREF_ICON_POLICY) == TRUE) {
+		const char *str;
+
+		str = gconf_value_get_string(value);
+		if (!str)
+			return;
+
+		gconf_string_to_enum(icon_policy_enum_map, str, &icon_policy);
+
+		update_icon_visibility();
+		return;
+	}
+
+#if 0
+	if (g_str_equal(entry->key, PREF_RECEIVE_ENABLED) == TRUE) {
+		set_receive_enabled(gconf_value_get_bool(value));
+		return;
+	}
+
+	if (g_str_equal(entry->key, PREF_SHARING_ENABLED) == TRUE) {
+		set_sharing_enabled(gconf_value_get_bool(value));
+		return;
+	}
+#endif
+}
+
+static GOptionEntry options[] = {
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	BluetoothInstance *instance;
+	GtkStatusIcon *statusicon;
+	GtkWidget *menu;
+	GError *error = NULL;
+	char *str;
+
+	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+	textdomain(GETTEXT_PACKAGE);
+
+	if (gtk_init_with_args(&argc, &argv, NULL,
+				options, GETTEXT_PACKAGE, &error) == FALSE) {
+		if (error) {
+			g_print("%s\n", error->message);
+			g_error_free(error);
+		} else
+			g_print("An unknown error occurred\n");
+
+		gtk_exit(1);
+	}
+
+	instance = bluetooth_instance_new("applet");
+	if (instance == NULL)
+		gtk_exit(0);
+
+	g_set_application_name(_("Bluetooth Applet"));
+
+	gtk_window_set_default_icon_name("bluetooth");
+
+	client = bluetooth_client_new();
+
+	adapter_model = bluetooth_client_get_adapter_model(client);
+
+	g_signal_connect(G_OBJECT(adapter_model), "row-inserted",
+					G_CALLBACK(adapter_added), NULL);
+
+	g_signal_connect(G_OBJECT(adapter_model), "row-deleted",
+					G_CALLBACK(adapter_removed), NULL);
+
+	if (gtk_tree_model_iter_n_children(adapter_model, NULL) > 0)
+		adapter_present = TRUE;
+
+	gconf = gconf_client_get_default();
+
+	str = gconf_client_get_string(gconf, PREF_ICON_POLICY, NULL);
+	if (str) {
+		gconf_string_to_enum(icon_policy_enum_map, str, &icon_policy);
+		g_free(str);
+	}
+
+#if 0
+	set_receive_enabled(gconf_client_get_bool(gconf,
+						PREF_RECEIVE_ENABLED, NULL));
+
+	set_sharing_enabled(gconf_client_get_bool(gconf,
+						PREF_SHARING_ENABLED, NULL));
+#endif
+
+	gconf_client_add_dir(gconf, PREF_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+	gconf_client_notify_add(gconf, PREF_DIR,
+					gconf_callback, NULL, NULL, NULL);
+
+	statusicon = init_notification();
+
+	update_icon_visibility();
+
+	g_signal_connect(statusicon, "activate",
+				G_CALLBACK(activate_callback), NULL);
+
+	menu = create_popupmenu();
+
+	g_signal_connect(statusicon, "popup-menu",
+				G_CALLBACK(popup_callback), menu);
+
+	setup_agents();
+
+	gtk_main();
+
+	gtk_widget_destroy(menu);
+
+	g_object_unref(gconf);
+
+	cleanup_agents();
+
+	cleanup_notification();
+
+	g_object_unref(adapter_model);
+
+	g_object_unref(client);
+
+	g_object_unref(instance);
+
+	return 0;
+}

Added: trunk/applet/notify.c
==============================================================================
--- (empty file)
+++ trunk/applet/notify.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,137 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libnotify/notify.h>
+
+static GtkStatusIcon *statusicon = NULL;
+static NotifyNotification *notify = NULL;
+
+static void notify_action(NotifyNotification *notify,
+					gchar *action, gpointer user_data)
+{
+}
+
+void show_notification(const gchar *summary, const gchar *message,
+			const gchar *action, gint timeout, GCallback handler)
+{
+	NotifyActionCallback callback;
+	GdkScreen *screen;
+	GdkRectangle area;
+
+	if (notify_is_initted() == FALSE)
+		return;
+
+	if (notify) {
+		g_signal_handlers_destroy(notify);
+		notify_notification_close(notify, NULL);
+	}
+
+	notify = notify_notification_new(summary, message, "bluetooth", NULL);
+
+	notify_notification_set_timeout(notify, timeout);
+
+	if (gtk_status_icon_get_visible(statusicon) == TRUE) {
+		gtk_status_icon_get_geometry(statusicon, &screen, &area, NULL);
+
+		notify_notification_set_hint_int32(notify,
+					"x", area.x + area.width / 2);
+		notify_notification_set_hint_int32(notify,
+					"y", area.y + area.height / 2);
+	}
+
+	notify_notification_set_urgency(notify, NOTIFY_URGENCY_NORMAL);
+
+	callback = handler ? NOTIFY_ACTION_CALLBACK(handler) : notify_action;
+
+	notify_notification_add_action(notify, "default", "action",
+						callback, NULL, NULL);
+	if (action != NULL)
+		notify_notification_add_action(notify, "button", action,
+							callback, NULL, NULL);
+
+	notify_notification_show(notify, NULL);
+}
+
+void close_notification(void)
+{
+	if (notify) {
+		g_signal_handlers_destroy(notify);
+		notify_notification_close(notify, NULL);
+		notify = NULL;
+	}
+}
+
+GtkStatusIcon *init_notification(void)
+{
+	notify_init("bluetooth-manager");
+
+	statusicon = gtk_status_icon_new();
+	g_object_unref(statusicon);
+
+	statusicon = gtk_status_icon_new_from_icon_name("bluetooth");
+
+	//gtk_status_icon_set_tooltip(statusicon, _("Bluetooth Manager"));
+
+	return statusicon;
+}
+
+void cleanup_notification(void)
+{
+	close_notification();
+
+	g_object_unref(statusicon);
+
+	notify_uninit();
+}
+
+void show_icon(void)
+{
+	gtk_status_icon_set_visible(statusicon, TRUE);
+}
+
+void hide_icon(void)
+{
+	gtk_status_icon_set_visible(statusicon, FALSE);
+}
+
+void enable_blinking(void)
+{
+	gtk_status_icon_set_blinking(statusicon, TRUE);
+}
+
+void disable_blinking(void)
+{
+	gtk_status_icon_set_blinking(statusicon, FALSE);
+}
+
+gboolean query_blinking(void)
+{
+	return gtk_status_icon_get_blinking(statusicon);
+}

Added: trunk/applet/notify.h
==============================================================================
--- (empty file)
+++ trunk/applet/notify.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,37 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+GtkStatusIcon *init_notification(void);
+void cleanup_notification(void);
+
+void show_notification(const gchar *summary, const gchar *message,
+			const gchar *action, gint timeout, GCallback handler);
+void close_notification(void);
+
+void show_icon(void);
+void hide_icon(void);
+
+void enable_blinking(void);
+void disable_blinking(void);
+gboolean query_blinking(void);

Added: trunk/applet/obex.c
==============================================================================
--- (empty file)
+++ trunk/applet/obex.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,341 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus-glib.h>
+
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+
+#include "notify.h"
+#include "obex.h"
+
+static gboolean receive_enabled = FALSE;
+static gboolean sharing_enabled = FALSE;
+
+static gboolean opp_startup = FALSE;
+static gboolean ftp_startup = FALSE;
+
+static DBusGProxy *opp_server = NULL;
+static DBusGProxy *ftp_server = NULL;
+
+static void close_opp_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	if (dbus_g_proxy_end_call(proxy, call, NULL, G_TYPE_INVALID) == FALSE)
+		return;
+
+	receive_enabled = FALSE;
+
+	g_object_unref(opp_server);
+	opp_server = NULL;
+
+	close_notification();
+}
+
+static void close_opp_server(void)
+{
+	if (opp_server == NULL)
+		return;
+
+	dbus_g_proxy_begin_call(opp_server, "Close",
+				close_opp_notify, NULL, NULL, G_TYPE_INVALID);
+}
+
+static void close_ftp_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	if (dbus_g_proxy_end_call(proxy, call, NULL, G_TYPE_INVALID) == FALSE)
+		return;
+
+	sharing_enabled = FALSE;
+
+	g_object_unref(ftp_server);
+	ftp_server = NULL;
+
+	close_notification();
+}
+
+static void close_ftp_server(void)
+{
+	if (ftp_server == NULL)
+		return;
+
+	dbus_g_proxy_begin_call(ftp_server, "Close",
+				close_ftp_notify, NULL, NULL, G_TYPE_INVALID);
+}
+
+static void start_opp_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	GError *error = NULL;
+	opp_startup = FALSE;
+
+	if (dbus_g_proxy_end_call(proxy, call,
+					&error, G_TYPE_INVALID) == FALSE) {
+		if (error != NULL) {
+			g_printerr("Bluetooth OBEX start failed: %s\n",
+							error->message);
+			g_error_free(error);
+		}
+
+		close_opp_server();
+		return;
+	}
+
+	receive_enabled = TRUE;
+
+	show_notification(_("File transfer"),
+				_("Receiving of incoming files is enabled"),
+							NULL, 4000, NULL);
+}
+
+static void create_opp_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	GError *error = NULL;
+	const gchar *dir, *path = NULL;
+
+	if (dbus_g_proxy_end_call(proxy, call, &error,
+					DBUS_TYPE_G_OBJECT_PATH, &path,
+						G_TYPE_INVALID) == FALSE) {
+		if (error != NULL) {
+			g_printerr("Bluetooth OBEX server failed: %s\n",
+							error->message);
+			g_error_free(error);
+		}
+
+		opp_startup = FALSE;
+		return;
+	}
+
+	dir = g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD);
+	if (dir == NULL) {
+		g_printerr("G_USER_DIRECTORY_DOWNLOAD is not set\n");
+		return;
+	}
+
+	opp_server = dbus_g_proxy_new_from_proxy(proxy,
+					"org.openobex.Server", path);
+
+	dbus_g_proxy_begin_call(opp_server, "Start",
+				start_opp_notify, NULL, NULL,
+				G_TYPE_STRING, dir,
+				G_TYPE_BOOLEAN, TRUE,
+				G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID);
+}
+
+static void create_opp_server(DBusGProxy *manager)
+{
+	opp_startup = TRUE;
+
+	dbus_g_proxy_begin_call(manager, "CreateBluetoothServer",
+				create_opp_notify, NULL, NULL,
+				G_TYPE_STRING, "00:00:00:00:00:00",
+				G_TYPE_STRING, "opp",
+				G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID);
+}
+
+static void start_ftp_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	GError * error = NULL;
+	ftp_startup = FALSE;
+
+	if (dbus_g_proxy_end_call(proxy, call,
+					&error, G_TYPE_INVALID) == FALSE) {
+		if (error != NULL) {
+			g_printerr("Bluetooth FTP start failed: %s\n",
+							error->message);
+			g_error_free(error);
+		}
+
+		close_ftp_server();
+		return;
+	}
+
+	sharing_enabled = TRUE;
+
+	show_notification(_("File transfer"),
+				_("Public file sharing has been activated"),
+							NULL, 4000, NULL);
+}
+
+static void create_ftp_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	GError *error = NULL;
+	const gchar *dir, *path = NULL;
+
+	if (dbus_g_proxy_end_call(proxy, call, &error,
+					DBUS_TYPE_G_OBJECT_PATH, &path,
+						G_TYPE_INVALID) == FALSE) {
+		if (error != NULL) {
+			g_printerr("Bluetooth FTP server failed: %s\n",
+							error->message);
+			g_error_free(error);
+		}
+
+		ftp_startup = FALSE;
+		return;
+	}
+
+	dir = g_get_user_special_dir(G_USER_DIRECTORY_PUBLIC_SHARE);
+	if (dir == NULL) {
+		g_printerr("G_USER_DIRECTORY_PUBLIC_SHARE is not set\n");
+		return;
+	}
+
+	ftp_server = dbus_g_proxy_new_from_proxy(proxy,
+					"org.openobex.Server", path);
+
+	dbus_g_proxy_begin_call(ftp_server, "Start",
+				start_ftp_notify, NULL, NULL,
+				G_TYPE_STRING, dir,
+				G_TYPE_BOOLEAN, FALSE,
+				G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID);
+}
+
+static void create_ftp_server(DBusGProxy *manager)
+{
+	ftp_startup = TRUE;
+
+	dbus_g_proxy_begin_call(manager, "CreateBluetoothServer",
+				create_ftp_notify, NULL, NULL,
+				G_TYPE_STRING, "00:00:00:00:00:00",
+				G_TYPE_STRING, "ftp",
+				G_TYPE_BOOLEAN, TRUE, G_TYPE_INVALID);
+}
+
+static DBusGConnection *connection = NULL;
+static DBusGProxy *manager = NULL;
+
+static void name_owner_changed(DBusGProxy *object, const char *name,
+			const char *prev, const char *new, gpointer user_data)
+{
+	if (g_str_equal(name, "org.openobex") == TRUE && *new == '\0') {
+		if (opp_server != NULL) {
+			g_object_unref(opp_server);
+			opp_server = NULL;
+		}
+
+		if (ftp_server != NULL) {
+			g_object_unref(ftp_server);
+			ftp_server = NULL;
+		}
+	}
+
+	if (g_str_equal(name, "org.openobex") == TRUE && *prev == '\0') {
+		if (opp_startup == FALSE && receive_enabled == TRUE)
+			create_opp_server(manager);
+
+		if (ftp_startup == FALSE && sharing_enabled == TRUE)
+			create_ftp_server(manager);
+	}
+}
+
+int setup_obex(void)
+{
+	DBusGProxy *proxy;
+	GError *error = NULL;
+
+	connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+	if (error != NULL) {
+		g_printerr("Connecting to session bus failed: %s\n",
+							error->message);
+		g_error_free(error);
+		return -1;
+	}
+
+	proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS,
+					DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+
+	dbus_g_proxy_add_signal(proxy, "NameOwnerChanged",
+		G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(proxy, "NameOwnerChanged",
+				G_CALLBACK(name_owner_changed), NULL, NULL);
+
+	manager = dbus_g_proxy_new_for_name(connection, "org.openobex",
+				"/org/openobex", "org.openobex.Manager");
+
+	if (opp_startup == FALSE && receive_enabled == TRUE)
+		create_opp_server(manager);
+
+	if (ftp_startup == FALSE && sharing_enabled == TRUE)
+		create_ftp_server(manager);
+
+	return 0;
+}
+
+void cleanup_obex(void)
+{
+	if (receive_enabled == TRUE)
+		close_opp_server();
+
+	if (sharing_enabled == TRUE)
+		close_ftp_server();
+
+	if (manager != NULL)
+		g_object_unref(manager);
+
+	if (connection != NULL)
+		dbus_g_connection_unref(connection);
+}
+
+void set_receive_enabled(gboolean value)
+{
+	if (receive_enabled == value)
+		return;
+
+	if (manager == NULL) {
+		receive_enabled = value;
+		return;
+	}
+
+	if (value == TRUE)
+		create_opp_server(manager);
+	else
+		close_opp_server();
+}
+
+void set_sharing_enabled(gboolean value)
+{
+	if (sharing_enabled == value)
+		return;
+
+	if (manager == NULL) {
+		sharing_enabled = value;
+		return;
+	}
+
+	if (value == TRUE)
+		create_ftp_server(manager);
+	else
+		close_ftp_server();
+}

Added: trunk/applet/obex.h
==============================================================================
--- (empty file)
+++ trunk/applet/obex.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int setup_obex(void);
+void cleanup_obex(void);
+
+void set_receive_enabled(gboolean value);
+void set_sharing_enabled(gboolean value);

Added: trunk/applet/test-agentdialog.c
==============================================================================
--- (empty file)
+++ trunk/applet/test-agentdialog.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,84 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "agent.c"
+
+static void activate_callback(GObject *widget, gpointer user_data)
+{
+	show_agents();
+}
+
+int main(int argc, char *argv[])
+{
+	GtkStatusIcon *statusicon;
+	DBusGConnection *conn;
+	DBusGProxy *adapter, *device;
+	GError *error = NULL;
+
+	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+	textdomain(GETTEXT_PACKAGE);
+
+	gtk_init(&argc, &argv);
+
+	gtk_window_set_default_icon_name("bluetooth");
+
+	conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+	if (error != NULL) {
+		g_printerr("Connecting to system bus failed: %s\n",
+							error->message);
+		g_error_free(error);
+		gtk_exit(1);
+	}
+
+	statusicon = init_notification();
+
+	g_signal_connect(statusicon, "activate",
+				G_CALLBACK(activate_callback), NULL);
+
+	setup_agents();
+
+	adapter = dbus_g_proxy_new_for_name(conn, "org.bluez",
+						"/hci0", "org.bluez.Adapter");
+
+	device = dbus_g_proxy_new_from_proxy(adapter,
+			"/hci0/dev_11_22_33_44_55_66", "org.bluez.Device");
+
+	//display_dialog(adapter, device, "00:11:22:33:44:55", "Test", "123456", 0, NULL);
+	passkey_dialog(adapter, device, "00:11:22:33:44:55", "Test", FALSE, NULL);
+	//confirm_dialog(adapter, device, "00:11:22:33:44:55", "Test", "123456", NULL);
+	//auth_dialog(adpater, device, "00:11:22:33:44:55", "Test", "UUID", NULL);
+
+	gtk_main();
+
+	g_object_unref(device);
+	g_object_unref(adapter);
+
+	cleanup_notification();
+
+	cleanup_agents();
+
+	dbus_g_connection_unref(conn);
+
+	return 0;
+}

Modified: trunk/autogen.sh
==============================================================================
--- trunk/autogen.sh	(original)
+++ trunk/autogen.sh	Wed Feb 25 14:35:27 2009
@@ -4,11 +4,9 @@
 srcdir=`dirname $0`
 test -z "$srcdir" && srcdir=.
 
-PKG_NAME="Gnome Bluetooth Controller"
+PKG_NAME="Gnome Bluetooth"
 
-(cd $srcdir && cat python-headers.m4 gob2.m4 >acinclude.m4)
-
-(test -f $srcdir/configure.in) || {
+(test -f $srcdir/configure.ac) || {
     echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
     echo " top-level directory"
     exit 1

Added: trunk/browse/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/browse/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,17 @@
+
+bin_PROGRAMS = bluetooth-browse
+
+bluetooth_browse_SOURCES = main.c
+
+bluetooth_browse_LDADD = $(top_builddir)/common/libcommon.a \
+						@GTK_LIBS@ @DBUS_LIBS@
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GTK_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common
+
+man_MANS = bluetooth-browse.1
+
+EXTRA_DIST = $(man_MANS)
+
+MAINTAINERCLEANFILES = Makefile.in

Added: trunk/browse/bluetooth-browse.1
==============================================================================
--- (empty file)
+++ trunk/browse/bluetooth-browse.1	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,29 @@
+.TH BLUETOOTH-BROWSE 1 "Oct 4, 2006" "bluez-gnome" "Linux User's Manual"
+.SH NAME
+bluetooth-browse - GTK application for browsing directories over Bluetooth
+.SH SYNOPSIS
+.B bluetooth-browse
+.SH DESCRIPTION
+.I bluetooth-browse
+will display a dialog for browsing directories over Bluetooth.
+.I bluetooth-browse
+is part of bluez-gnome, see also http://www.bluez.org
+.SH OPTIONS
+.I bluetooth-browse
+takes no options
+.SH AUTHOR
+Marcel Holtmann <marcel holtmann org>
+.SH LICENSE
+bluetooth-browse is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Added: trunk/browse/main.c
==============================================================================
--- (empty file)
+++ trunk/browse/main.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,81 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <bluetooth-client.h>
+#include <helper.h>
+
+static gchar *option_device = NULL;
+
+static GOptionEntry options[] = {
+	{ "device", 0, 0, G_OPTION_ARG_STRING, &option_device,
+				N_("Remote device to use"), "ADDRESS" },
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	GError *error = NULL;
+	gchar *command;
+
+	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+	textdomain(GETTEXT_PACKAGE);
+
+	if (gtk_init_with_args(&argc, &argv, NULL,
+				options, GETTEXT_PACKAGE, &error) == FALSE) {
+		if (error != NULL) {
+			g_printerr("%s\n", error->message);
+			g_error_free(error);
+		} else
+			g_printerr("An unknown error occurred\n");
+
+		gtk_exit(1);
+	}
+
+	gtk_window_set_default_icon_name("bluetooth");
+
+	if (option_device == NULL) {
+		option_device = show_browse_dialog();
+		if (option_device == NULL)
+			gtk_exit(1);
+	}
+
+	command = g_strdup_printf("%s --no-default-window \"obex://[%s]\"",
+						"nautilus", option_device);
+
+	if (!g_spawn_command_line_async(command, NULL))
+		g_printerr("Couldn't execute command: %s\n", command);
+
+	g_free(command);
+
+	g_free(option_device);
+
+	return 0;
+}

Added: trunk/common/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/common/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,52 @@
+
+noinst_LIBRARIES = libcommon.a
+
+libcommon_a_SOURCES = helper.h helper.c \
+		bluetooth-instance.h bluetooth-instance.c \
+		bluetooth-client.h bluetooth-client.c \
+		bluetooth-agent.h bluetooth-agent.c \
+		obex-agent.h obex-agent.c
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GTK_CFLAGS@
+
+BUILT_SOURCES = marshal.h marshal.c \
+		bluetooth-instance-glue.h \
+		bluetooth-client-glue.h \
+		bluetooth-agent-glue.h \
+		obex-agent-glue.h
+
+nodist_libcommon_a_SOURCES = $(BUILT_SOURCES)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+noinst_PROGRAMS = test-client test-agent
+
+test_client_LDADD = libcommon.a @GTK_LIBS@ @DBUS_LIBS@
+
+test_agent_LDADD = libcommon.a @DBUS_LIBS@
+
+EXTRA_DIST = marshal.list \
+		bluetooth-instance.xml \
+		bluetooth-client.xml \
+		bluetooth-agent.xml \
+		obex-agent.xml
+
+MAINTAINERCLEANFILES = Makefile.in
+
+marshal.h: marshal.list
+	$(GLIB_GENMARSHAL) --prefix=marshal $< --header > $@
+
+marshal.c: marshal.list
+	$(GLIB_GENMARSHAL) --prefix=marshal $< --body > $@
+
+bluetooth-instance-glue.h: bluetooth-instance.xml
+	$(DBUS_BINDING_TOOL) --prefix=bluetooth_instance --mode=glib-server --output=$@ $<
+
+bluetooth-client-glue.h: bluetooth-client.xml
+	$(DBUS_BINDING_TOOL) --prefix=bluetooth_client --mode=glib-client --output=$@ $<
+
+bluetooth-agent-glue.h: bluetooth-agent.xml
+	$(DBUS_BINDING_TOOL) --prefix=bluetooth_agent --mode=glib-server --output=$@ $<
+
+obex-agent-glue.h: obex-agent.xml
+	$(DBUS_BINDING_TOOL) --prefix=obex_agent --mode=glib-server --output=$@ $<

Added: trunk/common/bluetooth-agent.c
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-agent.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,530 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "bluetooth-agent.h"
+
+#ifdef DEBUG
+#define DBG(fmt, arg...) printf("%s:%s() " fmt "\n", __FILE__, __FUNCTION__ , ## arg)
+#else
+#define DBG(fmt...)
+#endif
+
+#define BLUEZ_SERVICE	"org.bluez"
+
+#define BLUEZ_MANAGER_PATH	"/"
+#define BLUEZ_MANAGER_INTERFACE	"org.bluez.Manager"
+#define BLUEZ_DEVICE_INTERFACE	"org.bluez.Device"
+
+static DBusGConnection *connection = NULL;
+
+#define BLUETOOTH_AGENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+				BLUETOOTH_TYPE_AGENT, BluetoothAgentPrivate))
+
+typedef struct _BluetoothAgentPrivate BluetoothAgentPrivate;
+
+struct _BluetoothAgentPrivate {
+	gchar *busname;
+	gchar *path;
+	DBusGProxy *adapter;
+
+	BluetoothAgentPasskeyFunc pincode_func;
+	gpointer pincode_data;
+
+	BluetoothAgentDisplayFunc display_func;
+	gpointer display_data;
+
+	BluetoothAgentPasskeyFunc passkey_func;
+	gpointer passkey_data;
+
+	BluetoothAgentConfirmFunc confirm_func;
+	gpointer confirm_data;
+
+	BluetoothAgentAuthorizeFunc authorize_func;
+	gpointer authorize_data;
+
+	BluetoothAgentCancelFunc cancel_func;
+	gpointer cancel_data;
+};
+
+G_DEFINE_TYPE(BluetoothAgent, bluetooth_agent, G_TYPE_OBJECT)
+
+static gboolean bluetooth_agent_request_pin_code(BluetoothAgent *agent,
+			const char *path, DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	DBusGProxy *device;
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->pincode_func) {
+		if (priv->adapter != NULL)
+			device = dbus_g_proxy_new_from_proxy(priv->adapter,
+						BLUEZ_DEVICE_INTERFACE, path);
+		else
+			device = NULL;
+
+		result = priv->pincode_func(context, device,
+							priv->pincode_data);
+
+		if (device != NULL)
+			g_object_unref(device);
+	}
+
+	return result;
+}
+
+static gboolean bluetooth_agent_request_passkey(BluetoothAgent *agent,
+			const char *path, DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	DBusGProxy *device;
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->passkey_func) {
+		if (priv->adapter != NULL)
+			device = dbus_g_proxy_new_from_proxy(priv->adapter,
+						BLUEZ_DEVICE_INTERFACE, path);
+		else
+			device = NULL;
+
+		result = priv->passkey_func(context, device,
+							priv->passkey_data);
+
+		if (device != NULL)
+			g_object_unref(device);
+	}
+
+	return result;
+}
+
+static gboolean bluetooth_agent_display_passkey(BluetoothAgent *agent,
+			const char *path, guint passkey, guint8 entered,
+						DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	DBusGProxy *device;
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->display_func) {
+		if (priv->adapter != NULL)
+			device = dbus_g_proxy_new_from_proxy(priv->adapter,
+						BLUEZ_DEVICE_INTERFACE, path);
+		else
+			device = NULL;
+
+		result = priv->display_func(context, device, passkey, entered,
+							priv->display_data);
+
+		if (device != NULL)
+			g_object_unref(device);
+	}
+
+	return result;
+}
+
+static gboolean bluetooth_agent_request_confirmation(BluetoothAgent *agent,
+					const char *path, guint passkey,
+						DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	DBusGProxy *device;
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->confirm_func) {
+		if (priv->adapter != NULL)
+			device = dbus_g_proxy_new_from_proxy(priv->adapter,
+						BLUEZ_DEVICE_INTERFACE, path);
+		else
+			device = NULL;
+
+		result = priv->confirm_func(context, device, passkey,
+							priv->confirm_data);
+
+		if (device != NULL)
+			g_object_unref(device);
+	}
+
+	return result;
+}
+
+static gboolean bluetooth_agent_authorize(BluetoothAgent *agent,
+					const char *path, const char *uuid,
+						DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	DBusGProxy *device;
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->authorize_func) {
+		if (priv->adapter != NULL)
+			device = dbus_g_proxy_new_from_proxy(priv->adapter,
+						BLUEZ_DEVICE_INTERFACE, path);
+		else
+			device = NULL;
+
+		result = priv->authorize_func(context, device, uuid,
+							priv->authorize_data);
+
+		if (device != NULL)
+			g_object_unref(device);
+	}
+
+	return result;
+}
+
+static gboolean bluetooth_agent_confirm_mode(BluetoothAgent *agent,
+			const char *mode, DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	dbus_g_method_return(context);
+
+	return TRUE;
+}
+
+static gboolean bluetooth_agent_cancel(BluetoothAgent *agent,
+						DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->cancel_func)
+		result = priv->cancel_func(context, priv->cancel_data);
+
+	return result;
+}
+
+static gboolean bluetooth_agent_release(BluetoothAgent *agent,
+						DBusGMethodInvocation *context)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	g_object_unref(agent);
+
+	dbus_g_method_return(context);
+
+	return TRUE;
+}
+
+#include "bluetooth-agent-glue.h"
+
+static void bluetooth_agent_init(BluetoothAgent *agent)
+{
+	DBG("agent %p", agent);
+}
+
+static void bluetooth_agent_finalize(GObject *agent)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	if (priv->adapter != NULL) {
+		dbus_g_proxy_call(priv->adapter, "UnregisterAgent", NULL,
+					DBUS_TYPE_G_OBJECT_PATH, priv->path,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+		g_object_unref(priv->adapter);
+	}
+
+	g_free(priv->path);
+	g_free(priv->busname);
+
+	G_OBJECT_CLASS(bluetooth_agent_parent_class)->finalize(agent);
+}
+
+static void bluetooth_agent_class_init(BluetoothAgentClass *klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+	GError *error = NULL;
+
+	DBG("class %p", klass);
+
+	g_type_class_add_private(klass, sizeof(BluetoothAgentPrivate));
+
+	object_class->finalize = bluetooth_agent_finalize;
+
+	connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+
+	if (error != NULL) {
+		g_printerr("Connecting to system bus failed: %s\n",
+							error->message);
+		g_error_free(error);
+	}
+
+	dbus_g_object_type_install_info(BLUETOOTH_TYPE_AGENT,
+				&dbus_glib_bluetooth_agent_object_info);
+
+	//dbus_g_error_domain_register(AGENT_ERROR, "org.bluez.Error",
+	//						AGENT_ERROR_TYPE);
+}
+
+BluetoothAgent *bluetooth_agent_new(void)
+{
+	BluetoothAgent *agent;
+
+	agent = BLUETOOTH_AGENT(g_object_new(BLUETOOTH_TYPE_AGENT, NULL));
+
+	DBG("agent %p", agent);
+
+	return agent;
+}
+
+gboolean bluetooth_agent_setup(BluetoothAgent *agent, const char *path)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	DBusGProxy *proxy;
+	GObject *object;
+
+	DBG("agent %p", agent);
+
+	if (priv->path != NULL)
+		return FALSE;
+
+	priv->path = g_strdup(path);
+
+	proxy = dbus_g_proxy_new_for_name_owner(connection, BLUEZ_SERVICE,
+			BLUEZ_MANAGER_PATH, BLUEZ_MANAGER_INTERFACE, NULL);
+
+	g_free(priv->busname);
+
+	if (proxy != NULL) {
+		priv->busname = g_strdup(dbus_g_proxy_get_bus_name(proxy));
+		g_object_unref(proxy);
+	} else
+		priv->busname = NULL;
+
+	object = dbus_g_connection_lookup_g_object(connection, priv->path);
+	if (object != NULL)
+		g_object_unref(object);
+
+	dbus_g_connection_register_g_object(connection,
+						priv->path, G_OBJECT(agent));
+
+	return TRUE;
+}
+
+gboolean bluetooth_agent_register(BluetoothAgent *agent, DBusGProxy *adapter)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	DBusGProxy *proxy;
+	GObject *object;
+	GError *error = NULL;
+	gchar *path;
+
+	DBG("agent %p", agent);
+
+	if (priv->adapter != NULL)
+		return FALSE;
+
+	priv->adapter = g_object_ref(adapter);
+
+	path = g_path_get_basename(dbus_g_proxy_get_path(adapter));
+
+	priv->path = g_strdup_printf("/org/bluez/agent/%s", path);
+
+	g_free(path);
+
+	proxy = dbus_g_proxy_new_for_name_owner(connection,
+			dbus_g_proxy_get_bus_name(priv->adapter),
+			dbus_g_proxy_get_path(priv->adapter),
+			dbus_g_proxy_get_interface(priv->adapter), NULL);
+
+	g_free(priv->busname);
+
+	if (proxy != NULL) {
+		priv->busname = g_strdup(dbus_g_proxy_get_bus_name(proxy));
+		g_object_unref(proxy);
+	} else
+		priv->busname = g_strdup(dbus_g_proxy_get_bus_name(adapter));
+
+	object = dbus_g_connection_lookup_g_object(connection, priv->path);
+	if (object != NULL)
+		g_object_unref(object);
+
+	dbus_g_connection_register_g_object(connection,
+						priv->path, G_OBJECT(agent));
+
+	dbus_g_proxy_call(priv->adapter, "RegisterAgent", &error,
+					DBUS_TYPE_G_OBJECT_PATH, priv->path,
+					G_TYPE_STRING, "DisplayYesNo",
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+	if (error != NULL) {
+		g_printerr("Agent registration failed: %s\n",
+							error->message);
+		g_error_free(error);
+	}
+
+	return TRUE;
+}
+
+gboolean bluetooth_agent_unregister(BluetoothAgent *agent)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+	GError *error = NULL;
+
+	DBG("agent %p", agent);
+
+	if (priv->adapter == NULL)
+		return FALSE;
+
+	dbus_g_proxy_call(priv->adapter, "UnregisterAgent", &error,
+					DBUS_TYPE_G_OBJECT_PATH, priv->path,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+	if (error != NULL) {
+		g_printerr("Agent unregistration failed: %s\n",
+							error->message);
+		g_error_free(error);
+	}
+
+	g_object_unref(priv->adapter);
+	priv->adapter = NULL;
+
+	g_free(priv->path);
+	priv->path = NULL;
+
+	return TRUE;
+}
+
+void bluetooth_agent_set_pincode_func(BluetoothAgent *agent,
+				BluetoothAgentPasskeyFunc func, gpointer data)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->pincode_func = func;
+	priv->pincode_data = data;
+}
+
+void bluetooth_agent_set_passkey_func(BluetoothAgent *agent,
+				BluetoothAgentPasskeyFunc func, gpointer data)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->passkey_func = func;
+	priv->passkey_data = data;
+}
+
+void bluetooth_agent_set_display_func(BluetoothAgent *agent,
+				BluetoothAgentDisplayFunc func, gpointer data)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->display_func = func;
+	priv->display_data = data;
+}
+
+void bluetooth_agent_set_confirm_func(BluetoothAgent *agent,
+				BluetoothAgentConfirmFunc func, gpointer data)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->confirm_func = func;
+	priv->confirm_data = data;
+}
+
+void bluetooth_agent_set_authorize_func(BluetoothAgent *agent,
+				BluetoothAgentAuthorizeFunc func, gpointer data)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->authorize_func = func;
+	priv->authorize_data = data;
+}
+
+void bluetooth_agent_set_cancel_func(BluetoothAgent *agent,
+				BluetoothAgentCancelFunc func, gpointer data)
+{
+	BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->cancel_func = func;
+	priv->cancel_data = data;
+}

Added: trunk/common/bluetooth-agent.h
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-agent.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,93 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BLUETOOTH_AGENT_H
+#define __BLUETOOTH_AGENT_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define BLUETOOTH_TYPE_AGENT (bluetooth_agent_get_type())
+#define BLUETOOTH_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+					BLUETOOTH_TYPE_AGENT, BluetoothAgent))
+#define BLUETOOTH_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+					BLUETOOTH_TYPE_AGENT, BluetoothAgentClass))
+#define BLUETOOTH_IS_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+							BLUETOOTH_TYPE_AGENT))
+#define BLUETOOTH_IS_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+							BLUETOOTH_TYPE_AGENT))
+#define BLUETOOTH_GET_AGENT_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+					BLUETOOTH_TYPE_AGENT, BluetoothAgentClass))
+
+typedef struct _BluetoothAgent BluetoothAgent;
+typedef struct _BluetoothAgentClass BluetoothAgentClass;
+
+struct _BluetoothAgent {
+	GObject parent;
+};
+
+struct _BluetoothAgentClass {
+	GObjectClass parent_class;
+};
+
+GType bluetooth_agent_get_type(void);
+
+BluetoothAgent *bluetooth_agent_new(void);
+
+gboolean bluetooth_agent_setup(BluetoothAgent *agent, const char *path);
+
+gboolean bluetooth_agent_register(BluetoothAgent *agent, DBusGProxy *adapter);
+gboolean bluetooth_agent_unregister(BluetoothAgent *agent);
+
+typedef gboolean (*BluetoothAgentPasskeyFunc) (DBusGMethodInvocation *context,
+					DBusGProxy *device, gpointer data);
+typedef gboolean (*BluetoothAgentDisplayFunc) (DBusGMethodInvocation *context,
+					DBusGProxy *device, guint passkey,
+						guint entered, gpointer data);
+typedef gboolean (*BluetoothAgentConfirmFunc) (DBusGMethodInvocation *context,
+					DBusGProxy *device, guint passkey,
+								gpointer data);
+typedef gboolean (*BluetoothAgentAuthorizeFunc) (DBusGMethodInvocation *context,
+					DBusGProxy *device, const char *uuid,
+								gpointer data);
+typedef gboolean (*BluetoothAgentCancelFunc) (DBusGMethodInvocation *context,
+								gpointer data);
+
+void bluetooth_agent_set_pincode_func(BluetoothAgent *agent,
+				BluetoothAgentPasskeyFunc func, gpointer data);
+void bluetooth_agent_set_passkey_func(BluetoothAgent *agent,
+				BluetoothAgentPasskeyFunc func, gpointer data);
+void bluetooth_agent_set_display_func(BluetoothAgent *agent,
+				BluetoothAgentDisplayFunc func, gpointer data);
+void bluetooth_agent_set_confirm_func(BluetoothAgent *agent,
+				BluetoothAgentConfirmFunc func, gpointer data);
+void bluetooth_agent_set_authorize_func(BluetoothAgent *agent,
+				BluetoothAgentAuthorizeFunc func, gpointer data);
+void bluetooth_agent_set_cancel_func(BluetoothAgent *agent,
+				BluetoothAgentCancelFunc func, gpointer data);
+
+G_END_DECLS
+
+#endif /* __BLUETOOTH_AGENT_H */

Added: trunk/common/bluetooth-agent.xml
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-agent.xml	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/">
+  <interface name="org.bluez.Agent">
+    <method name="RequestPinCode">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="device"/>
+      <arg type="s" name="pincode" direction="out"/>
+    </method>
+
+    <method name="RequestPasskey">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="device"/>
+      <arg type="u" name="passkey" direction="out"/>
+    </method>
+
+    <method name="DisplayPasskey">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="device"/>
+      <arg type="u" name="passkey"/>
+      <arg type="y" name="entered"/>
+    </method>
+
+    <method name="RequestConfirmation">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="device"/>
+      <arg type="u" name="passkey"/>
+    </method>
+
+    <method name="Authorize">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="device"/>
+      <arg type="s" name="uuid"/>
+    </method>
+
+    <method name="ConfirmMode">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="s" name="mode"/>
+    </method>
+
+    <method name="Cancel">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    </method>
+
+    <method name="Release">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    </method>
+  </interface>
+</node>

Added: trunk/common/bluetooth-client.c
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-client.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,1105 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+
+#include "bluetooth-client.h"
+#include "bluetooth-client-glue.h"
+
+#include "marshal.h"
+
+#ifdef DEBUG
+#define DBG(fmt, arg...) printf("%s:%s() " fmt "\n", __FILE__, __FUNCTION__ , ## arg)
+#else
+#define DBG(fmt...)
+#endif
+
+#ifndef DBUS_TYPE_G_OBJECT_PATH_ARRAY
+#define DBUS_TYPE_G_OBJECT_PATH_ARRAY \
+	(dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
+#endif
+
+#ifndef DBUS_TYPE_G_DICTIONARY
+#define DBUS_TYPE_G_DICTIONARY \
+	(dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
+#endif
+
+#define BLUEZ_SERVICE	"org.bluez"
+
+#define BLUEZ_MANAGER_PATH	"/"
+#define BLUEZ_MANAGER_INTERFACE	"org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE	"org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE	"org.bluez.Device"
+#define BLUEZ_INPUT_INTERFACE	"org.bluez.Input"
+
+static DBusGConnection *connection = NULL;
+static BluetoothClient *bluetooth_client = NULL;
+
+#define BLUETOOTH_CLIENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+				BLUETOOTH_TYPE_CLIENT, BluetoothClientPrivate))
+
+typedef struct _BluetoothClientPrivate BluetoothClientPrivate;
+
+struct _BluetoothClientPrivate {
+	DBusGProxy *dbus;
+	DBusGProxy *manager;
+	GtkTreeStore *store;
+};
+
+G_DEFINE_TYPE(BluetoothClient, bluetooth_client, G_TYPE_OBJECT)
+
+const gchar *bluetooth_type_to_string(guint type)
+{
+	switch (type) {
+	case BLUETOOTH_TYPE_ANY:
+		return N_("All types");
+	case BLUETOOTH_TYPE_PHONE:
+		return N_("Phone");
+	case BLUETOOTH_TYPE_MODEM:
+		return N_("Modem");
+	case BLUETOOTH_TYPE_COMPUTER:
+		return N_("Computer");
+	case BLUETOOTH_TYPE_NETWORK:
+		return N_("Network");
+	case BLUETOOTH_TYPE_HEADSET:
+		return N_("Headset");
+	case BLUETOOTH_TYPE_HEADPHONE:
+		return N_("Headphone");
+	case BLUETOOTH_TYPE_KEYBOARD:
+		return N_("Keyboard");
+	case BLUETOOTH_TYPE_MOUSE:
+		return N_("Mouse");
+	case BLUETOOTH_TYPE_CAMERA:
+		return N_("Camera");
+	case BLUETOOTH_TYPE_PRINTER:
+		return N_("Printer");
+	case BLUETOOTH_TYPE_JOYPAD:
+		return N_("Joypad");
+	case BLUETOOTH_TYPE_TABLET:
+		return N_("Tablet");
+	default:
+		return N_("Unknown");
+	}
+}
+
+static guint class_to_type(guint32 class)
+{
+	switch ((class & 0x1f00) >> 8) {
+	case 0x01:
+		return BLUETOOTH_TYPE_COMPUTER;
+	case 0x02:
+		switch ((class & 0xfc) >> 2) {
+		case 0x01:
+		case 0x02:
+		case 0x03:
+		case 0x05:
+			return BLUETOOTH_TYPE_PHONE;
+		case 0x04:
+			return BLUETOOTH_TYPE_MODEM;
+		}
+		break;
+	case 0x03:
+		return BLUETOOTH_TYPE_NETWORK;
+	case 0x04:
+		switch ((class & 0xfc) >> 2) {
+		case 0x01:
+		case 0x02:
+			return BLUETOOTH_TYPE_HEADSET;
+		case 0x06:
+			return BLUETOOTH_TYPE_HEADPHONE;
+		}
+		break;
+	case 0x05:
+		switch ((class & 0xc0) >> 6) {
+		case 0x00:
+			switch ((class & 0x1e) >> 2) {
+			case 0x01:
+			case 0x02:
+				return BLUETOOTH_TYPE_JOYPAD;
+			}
+			break;
+		case 0x01:
+			return BLUETOOTH_TYPE_KEYBOARD;
+		case 0x02:
+			switch ((class & 0x1e) >> 2) {
+			case 0x05:
+				return BLUETOOTH_TYPE_TABLET;
+			default:
+				return BLUETOOTH_TYPE_MOUSE;
+			}
+		}
+		break;
+	case 0x06:
+		if (class & 0x80)
+			return BLUETOOTH_TYPE_PRINTER;
+		if (class & 0x20)
+			return BLUETOOTH_TYPE_CAMERA;
+		break;
+	}
+
+	return BLUETOOTH_TYPE_ANY;
+}
+
+typedef gboolean (*IterSearchFunc) (GtkTreeStore *store,
+				GtkTreeIter *iter, gpointer user_data);
+
+static gboolean iter_search(GtkTreeStore *store,
+				GtkTreeIter *iter, GtkTreeIter *parent,
+				IterSearchFunc func, gpointer user_data)
+{
+	gboolean cont, found = FALSE;
+
+	if (parent == NULL)
+		cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store),
+									iter);
+	else
+		cont = gtk_tree_model_iter_children(GTK_TREE_MODEL(store),
+								iter, parent);
+
+	while (cont == TRUE) {
+		GtkTreeIter child;
+
+		found = func(store, iter, user_data);
+		if (found == TRUE)
+			break;
+
+		found = iter_search(store, &child, iter, func, user_data);
+		if (found == TRUE) {
+			*iter = child;
+			break;
+		}
+
+		cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
+	}
+
+	return found;
+}
+
+static gboolean compare_proxy(GtkTreeStore *store,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	DBusGProxy *proxy = user_data;
+	DBusGProxy *object;
+	gboolean found = FALSE;
+
+	gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
+					BLUETOOTH_COLUMN_PROXY, &object, -1);
+
+	if (object != NULL) {
+		found = g_str_equal(dbus_g_proxy_get_path(proxy),
+						dbus_g_proxy_get_path(object));
+		g_object_unref(object);
+	}
+
+	return found;
+}
+
+static gboolean get_iter_from_proxy(GtkTreeStore *store,
+					GtkTreeIter *iter, DBusGProxy *proxy)
+{
+	return iter_search(store, iter, NULL, compare_proxy, proxy);
+}
+
+static gboolean compare_path(GtkTreeStore *store,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	const gchar *path = user_data;
+	DBusGProxy *object;
+	gboolean found = FALSE;
+
+	gtk_tree_model_get(GTK_TREE_MODEL(store), iter,
+					BLUETOOTH_COLUMN_PROXY, &object, -1);
+
+	if (object != NULL) {
+		found = g_str_equal(path, dbus_g_proxy_get_path(object));
+		g_object_unref(object);
+	}
+
+	return found;
+}
+
+static gboolean get_iter_from_path(GtkTreeStore *store,
+					GtkTreeIter *iter, const char *path)
+{
+	return iter_search(store, iter, NULL, compare_path, (gpointer) path);
+}
+
+static void device_changed(DBusGProxy *device, const char *property,
+					GValue *value, gpointer user_data)
+{
+	BluetoothClient *client = BLUETOOTH_CLIENT(user_data);
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+
+	DBG("client %p property %s", client, property);
+
+	if (get_iter_from_proxy(priv->store, &iter, device) == FALSE)
+		return;
+
+	if (g_str_equal(property, "Name") == TRUE) {
+		const gchar *name = g_value_get_string(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+					BLUETOOTH_COLUMN_NAME, name, -1);
+	} else if (g_str_equal(property, "Alias") == TRUE) {
+		const gchar *alias = g_value_get_string(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+					BLUETOOTH_COLUMN_ALIAS, alias, -1);
+	} else if (g_str_equal(property, "Icon") == TRUE) {
+		const gchar *icon = g_value_get_string(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+					BLUETOOTH_COLUMN_ICON, icon, -1);
+	} else if (g_str_equal(property, "Paired") == TRUE) {
+		gboolean paired = g_value_get_boolean(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+				BLUETOOTH_COLUMN_PAIRED, paired, -1);
+	} else if (g_str_equal(property, "Trusted") == TRUE) {
+		gboolean trusted = g_value_get_boolean(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+				BLUETOOTH_COLUMN_TRUSTED, trusted, -1);
+	} else if (g_str_equal(property, "Connected") == TRUE) {
+		gboolean connected = g_value_get_boolean(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+				BLUETOOTH_COLUMN_CONNECTED, connected, -1);
+	}
+}
+
+static void add_device(DBusGProxy *adapter, GtkTreeIter *parent,
+					BluetoothClient *client,
+					const char *path, GHashTable *hash)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	DBusGProxy *device;
+	GValue *value;
+	const gchar *address, *alias, *name, *icon;
+	gboolean paired, trusted, connected;
+	guint type;
+	gint rssi;
+	GtkTreeIter iter;
+	gboolean cont;
+
+	if (hash == NULL) {
+		device = dbus_g_proxy_new_from_proxy(adapter,
+						BLUEZ_DEVICE_INTERFACE, path);
+
+		if (device != NULL)
+			device_get_properties(device, &hash, NULL);
+	} else
+		device = NULL;
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Alias");
+		alias = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		name = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Class");
+		type = class_to_type(g_value_get_uint(value));
+
+		value = g_hash_table_lookup(hash, "Icon");
+		icon = value ? g_value_get_string(value) : "bluetooth";
+
+		value = g_hash_table_lookup(hash, "RSSI");
+		rssi = value ? g_value_get_int(value) : 0;
+
+		value = g_hash_table_lookup(hash, "Paired");
+		paired = value ? g_value_get_boolean(value) : FALSE;
+
+		value = g_hash_table_lookup(hash, "Trusted");
+		trusted = value ? g_value_get_boolean(value) : FALSE;
+
+		value = g_hash_table_lookup(hash, "Connected");
+		connected = value ? g_value_get_boolean(value) : FALSE;
+	} else {
+		address = NULL;
+		alias = NULL;
+		name = NULL;
+		type = BLUETOOTH_TYPE_ANY;
+		icon = NULL;
+		rssi = 0;
+		paired = FALSE;
+		trusted = FALSE;
+		connected = FALSE;
+	}
+
+	cont = gtk_tree_model_iter_children(GTK_TREE_MODEL(priv->store),
+								&iter, parent);
+
+	while (cont == TRUE) {
+		gchar *value;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(priv->store), &iter,
+					BLUETOOTH_COLUMN_ADDRESS, &value, -1);
+
+		if (g_ascii_strcasecmp(address, value) == 0) {
+			gtk_tree_store_set(priv->store, &iter,
+					BLUETOOTH_COLUMN_ADDRESS, address,
+					BLUETOOTH_COLUMN_ALIAS, alias,
+					BLUETOOTH_COLUMN_NAME, name,
+					BLUETOOTH_COLUMN_TYPE, type,
+					BLUETOOTH_COLUMN_ICON, icon,
+					BLUETOOTH_COLUMN_RSSI, rssi, -1);
+
+			if (device != NULL) {
+				gtk_tree_store_set(priv->store, &iter,
+					BLUETOOTH_COLUMN_PROXY, device,
+					BLUETOOTH_COLUMN_CONNECTED, connected,
+					BLUETOOTH_COLUMN_TRUSTED, trusted,
+					BLUETOOTH_COLUMN_PAIRED, paired, -1);
+			}
+
+			goto done;
+		}
+
+		cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->store), &iter);
+	}
+
+	gtk_tree_store_insert_with_values(priv->store, &iter, parent, -1,
+				BLUETOOTH_COLUMN_PROXY, device,
+				BLUETOOTH_COLUMN_ADDRESS, address,
+				BLUETOOTH_COLUMN_ALIAS, alias,
+				BLUETOOTH_COLUMN_NAME, name,
+				BLUETOOTH_COLUMN_TYPE, type,
+				BLUETOOTH_COLUMN_ICON, icon,
+				BLUETOOTH_COLUMN_RSSI, rssi,
+				BLUETOOTH_COLUMN_PAIRED, paired,
+				BLUETOOTH_COLUMN_TRUSTED, trusted,
+				BLUETOOTH_COLUMN_CONNECTED, connected, -1);
+
+done:
+	if (device != NULL) {
+		dbus_g_proxy_add_signal(device, "PropertyChanged",
+				G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+		dbus_g_proxy_connect_signal(device, "PropertyChanged",
+				G_CALLBACK(device_changed), client, NULL);
+
+		g_object_unref(device);
+	}
+}
+
+static void device_found(DBusGProxy *adapter, const char *address,
+					GHashTable *hash, gpointer user_data)
+{
+	BluetoothClient *client = user_data;
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+
+	DBG("client %p address %s", client, address);
+
+	if (get_iter_from_proxy(priv->store, &iter, adapter) == TRUE)
+		add_device(adapter, &iter, client, NULL, hash);
+}
+
+static void device_created(DBusGProxy *adapter,
+				const char *path, gpointer user_data)
+{
+	BluetoothClient *client = user_data;
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+
+	DBG("client %p path %s", client, path);
+
+	if (get_iter_from_proxy(priv->store, &iter, adapter) == TRUE)
+		add_device(adapter, &iter, client, path, NULL);
+}
+
+static void device_removed(DBusGProxy *adapter,
+				const char *path, gpointer user_data)
+{
+	BluetoothClient *client = user_data;
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+
+	DBG("client %p path %s", client, path);
+
+	if (get_iter_from_path(priv->store, &iter, path) == TRUE)
+		gtk_tree_store_remove(priv->store, &iter);
+}
+
+static void adapter_changed(DBusGProxy *adapter, const char *property,
+					GValue *value, gpointer user_data)
+{
+	BluetoothClient *client = BLUETOOTH_CLIENT(user_data);
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+
+	DBG("client %p property %s", client, property);
+
+	if (get_iter_from_proxy(priv->store, &iter, adapter) == FALSE)
+		return;
+
+	if (g_str_equal(property, "Name") == TRUE) {
+		const gchar *name = g_value_get_string(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+					BLUETOOTH_COLUMN_NAME, name, -1);
+	} else if (g_str_equal(property, "Discovering") == TRUE) {
+		gboolean discovering = g_value_get_boolean(value);
+
+		gtk_tree_store_set(priv->store, &iter,
+				BLUETOOTH_COLUMN_DISCOVERING, discovering, -1);
+	}
+}
+
+static void adapter_added(DBusGProxy *manager,
+				const char *path, gpointer user_data)
+{
+	BluetoothClient *client = user_data;
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+	DBusGProxy *adapter;
+	GPtrArray *array = NULL;
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *name;
+	gboolean discovering;
+
+	DBG("client %p path %s", client, path);
+
+	adapter = dbus_g_proxy_new_from_proxy(manager,
+					BLUEZ_ADAPTER_INTERFACE, path);
+
+	adapter_get_properties(adapter, &hash, NULL);
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		name = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Discovering");
+		discovering = value ? g_value_get_boolean(value) : FALSE;
+	} else {
+		address = NULL;
+		name = NULL;
+		discovering = FALSE;
+	}
+
+	gtk_tree_store_insert_with_values(priv->store, &iter, NULL, -1,
+				BLUETOOTH_COLUMN_PROXY, adapter,
+				BLUETOOTH_COLUMN_ADDRESS, address,
+				BLUETOOTH_COLUMN_NAME, name,
+				BLUETOOTH_COLUMN_DISCOVERING, discovering, -1);
+
+	dbus_g_proxy_add_signal(adapter, "PropertyChanged",
+				G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(adapter, "PropertyChanged",
+				G_CALLBACK(adapter_changed), client, NULL);
+
+	dbus_g_proxy_add_signal(adapter, "DeviceCreated",
+				DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(adapter, "DeviceCreated",
+				G_CALLBACK(device_created), client, NULL);
+
+	dbus_g_proxy_add_signal(adapter, "DeviceRemoved",
+				DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(adapter, "DeviceRemoved",
+				G_CALLBACK(device_removed), client, NULL);
+
+	dbus_g_proxy_add_signal(adapter, "DeviceFound",
+			G_TYPE_STRING, DBUS_TYPE_G_DICTIONARY, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(adapter, "DeviceFound",
+				G_CALLBACK(device_found), client, NULL);
+
+	adapter_list_devices(adapter, &array, NULL);
+	if (array != NULL) {
+		int i;
+
+		for (i = 0; i < array->len; i++) {
+			gchar *path = g_ptr_array_index(array, i);
+			device_created(adapter, path, client);
+			g_free(path);
+		}
+	}
+
+	g_object_unref(adapter);
+}
+
+static void adapter_removed(DBusGProxy *manager,
+				const char *path, gpointer user_data)
+{
+	BluetoothClient *client = user_data;
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+	gboolean cont;
+
+	DBG("client %p path %s", client, path);
+
+	cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->store),
+									&iter);
+
+	while (cont == TRUE) {
+		DBusGProxy *adapter;
+		const char *adapter_path;
+		gboolean found;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(priv->store), &iter,
+					BLUETOOTH_COLUMN_PROXY, &adapter, -1);
+
+		adapter_path = dbus_g_proxy_get_path(adapter);
+
+		found = g_str_equal(path, adapter_path);
+		if (found == TRUE) {
+			g_signal_handlers_disconnect_by_func(adapter,
+						adapter_changed, client);
+			g_signal_handlers_disconnect_by_func(adapter,
+						device_created, client);
+			g_signal_handlers_disconnect_by_func(adapter,
+						device_removed, client);
+			g_signal_handlers_disconnect_by_func(adapter,
+						device_found, client);
+		}
+
+		g_object_unref(adapter);
+
+		if (found == TRUE) {
+			cont = gtk_tree_store_remove(priv->store, &iter);
+			continue;
+		}
+
+		cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->store),
+									&iter);
+	}
+}
+
+static void default_adapter_changed(DBusGProxy *manager,
+				const char *path, gpointer user_data)
+{
+	BluetoothClient *client = user_data;
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+	gboolean cont;
+
+	DBG("client %p path %s", client, path);
+
+	cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->store),
+									&iter);
+
+	while (cont == TRUE) {
+		DBusGProxy *adapter;
+		const char *adapter_path;
+		gboolean found;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(priv->store), &iter,
+					BLUETOOTH_COLUMN_PROXY, &adapter, -1);
+
+		adapter_path = dbus_g_proxy_get_path(adapter);
+
+		found = g_str_equal(path, adapter_path);
+
+		g_object_unref(adapter);
+
+		gtk_tree_store_set(priv->store, &iter,
+					BLUETOOTH_COLUMN_DEFAULT, found, -1);
+
+		cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->store),
+									&iter);
+	}
+}
+
+static void name_owner_changed(DBusGProxy *dbus, const char *name,
+			const char *prev, const char *new, gpointer user_data)
+{
+	BluetoothClient *client = user_data;
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+	gboolean cont;
+
+	if (g_str_equal(name, BLUEZ_SERVICE) == FALSE || *new != '\0')
+		return;
+
+	DBG("client %p name %s", client, name);
+
+	cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->store),
+									&iter);
+
+	while (cont == TRUE)
+		cont = gtk_tree_store_remove(priv->store, &iter);
+}
+
+static void bluetooth_client_init(BluetoothClient *client)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GPtrArray *array = NULL;
+	gchar *default_path = NULL;
+
+	DBG("client %p", client);
+
+	priv->store = gtk_tree_store_new(_BLUETOOTH_NUM_COLUMNS, G_TYPE_OBJECT,
+			G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+			G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INT,
+			G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+					G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+
+	priv->dbus = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS,
+				DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+
+	dbus_g_proxy_add_signal(priv->dbus, "NameOwnerChanged",
+					G_TYPE_STRING, G_TYPE_STRING,
+					G_TYPE_STRING, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(priv->dbus, "NameOwnerChanged",
+				G_CALLBACK(name_owner_changed), client, NULL);
+
+	priv->manager = dbus_g_proxy_new_for_name(connection, BLUEZ_SERVICE,
+				BLUEZ_MANAGER_PATH, BLUEZ_MANAGER_INTERFACE);
+
+	dbus_g_proxy_add_signal(priv->manager, "AdapterAdded",
+				DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(priv->manager, "AdapterAdded",
+				G_CALLBACK(adapter_added), client, NULL);
+
+	dbus_g_proxy_add_signal(priv->manager, "AdapterRemoved",
+				DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(priv->manager, "AdapterRemoved",
+				G_CALLBACK(adapter_removed), client, NULL);
+
+	dbus_g_proxy_add_signal(priv->manager, "DefaultAdapterChanged",
+				DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal(priv->manager, "DefaultAdapterChanged",
+			G_CALLBACK(default_adapter_changed), client, NULL);
+
+	manager_list_adapters(priv->manager, &array, NULL);
+	if (array != NULL) {
+		int i;
+
+		for (i = 0; i < array->len; i++) {
+			gchar *path = g_ptr_array_index(array, i);
+			adapter_added(priv->manager, path, client);
+			g_free(path);
+		}
+	}
+
+	manager_default_adapter(priv->manager, &default_path, NULL);
+	if (default_path != NULL) {
+		default_adapter_changed(priv->manager, default_path, client);
+		g_free(default_path);
+	}
+}
+
+static void bluetooth_client_finalize(GObject *client)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+
+	DBG("client %p", client);
+
+	g_signal_handlers_disconnect_by_func(priv->dbus,
+					name_owner_changed, client);
+	g_object_unref(priv->dbus);
+
+	g_signal_handlers_disconnect_by_func(priv->manager,
+						adapter_added, client);
+	g_signal_handlers_disconnect_by_func(priv->manager,
+						adapter_removed, client);
+	g_signal_handlers_disconnect_by_func(priv->manager,
+					default_adapter_changed, client);
+	g_object_unref(priv->manager);
+
+	g_object_unref(priv->store);
+
+	G_OBJECT_CLASS(bluetooth_client_parent_class)->finalize(client);
+}
+
+static void bluetooth_client_class_init(BluetoothClientClass *klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+	GError *error = NULL;
+
+	DBG("class %p", klass);
+
+	g_type_class_add_private(klass, sizeof(BluetoothClientPrivate));
+
+	object_class->finalize = bluetooth_client_finalize;
+
+	dbus_g_object_register_marshaller(marshal_VOID__STRING_BOXED,
+						G_TYPE_NONE, G_TYPE_STRING,
+						G_TYPE_VALUE, G_TYPE_INVALID);
+
+	connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+
+	if (error != NULL) {
+		g_printerr("Connecting to system bus failed: %s\n",
+							error->message);
+		g_error_free(error);
+	}
+}
+
+BluetoothClient *bluetooth_client_new(void)
+{
+	if (bluetooth_client != NULL)
+		return g_object_ref(bluetooth_client);
+
+	bluetooth_client = BLUETOOTH_CLIENT(g_object_new(BLUETOOTH_TYPE_CLIENT,
+									NULL));
+
+	DBG("client %p", bluetooth_client);
+
+	return bluetooth_client;
+}
+
+GtkTreeModel *bluetooth_client_get_model(BluetoothClient *client)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeModel *model;
+
+	DBG("client %p", client);
+
+	model = g_object_ref(priv->store);
+
+	return model;
+}
+
+GtkTreeModel *bluetooth_client_get_filter_model(BluetoothClient *client,
+				GtkTreeModelFilterVisibleFunc func,
+				gpointer data, GtkDestroyNotify destroy)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeModel *model;
+
+	DBG("client %p", client);
+
+	model = gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->store), NULL);
+
+	gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(model),
+							func, data, destroy);
+
+	return model;
+}
+
+static gboolean adapter_filter(GtkTreeModel *model,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	DBusGProxy *proxy;
+	gboolean active;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PROXY, &proxy, -1);
+
+	if (proxy == NULL)
+		return FALSE;
+
+	active = g_str_equal(BLUEZ_ADAPTER_INTERFACE,
+					dbus_g_proxy_get_interface(proxy));
+
+	g_object_unref(proxy);
+
+	return active;
+}
+
+GtkTreeModel *bluetooth_client_get_adapter_model(BluetoothClient *client)
+{
+	DBG("client %p", client);
+
+	return bluetooth_client_get_filter_model(client, adapter_filter,
+								NULL, NULL);
+}
+
+GtkTreeModel *bluetooth_client_get_device_model(BluetoothClient *client,
+							DBusGProxy *adapter)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeModel *model;
+	GtkTreePath *path;
+	GtkTreeIter iter;
+	gboolean cont, found = FALSE;
+
+	DBG("client %p", client);
+
+	cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->store),
+									&iter);
+
+	while (cont == TRUE) {
+		DBusGProxy *proxy;
+		gboolean is_default;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(priv->store), &iter,
+				BLUETOOTH_COLUMN_PROXY, &proxy,
+				BLUETOOTH_COLUMN_DEFAULT, &is_default, -1);
+
+		if (adapter == NULL && is_default == TRUE)
+			found = TRUE;
+
+		if (proxy == adapter)
+			found = TRUE;
+
+		g_object_unref(proxy);
+
+		if (found == TRUE)
+			break;
+
+		cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->store),
+									&iter);
+	}
+
+	if (found == TRUE) {
+		path = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->store),
+									&iter);
+		model = gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->store),
+									path);
+		gtk_tree_path_free(path);
+	} else
+		model = NULL;
+
+	return model;
+}
+
+GtkTreeModel *bluetooth_client_get_device_filter_model(BluetoothClient *client,
+		DBusGProxy *adapter, GtkTreeModelFilterVisibleFunc func,
+				gpointer data, GtkDestroyNotify destroy)
+{
+	GtkTreeModel *model;
+
+	DBG("client %p", client);
+
+	model = bluetooth_client_get_device_model(client, adapter);
+
+	gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(model),
+							func, data, destroy);
+
+	return model;
+}
+
+DBusGProxy *bluetooth_client_get_default_adapter(BluetoothClient *client)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	GtkTreeIter iter;
+	gboolean cont;
+
+	DBG("client %p", client);
+
+	cont = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->store),
+									&iter);
+
+	while (cont == TRUE) {
+		DBusGProxy *adapter;
+		gboolean is_default;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(priv->store), &iter,
+				BLUETOOTH_COLUMN_PROXY, &adapter,
+				BLUETOOTH_COLUMN_DEFAULT, &is_default, -1);
+
+		if (is_default == TRUE)
+			return adapter;
+
+		g_object_unref(adapter);
+
+		cont = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->store),
+									&iter);
+	}
+
+	return NULL;
+}
+
+gboolean bluetooth_client_start_discovery(BluetoothClient *client)
+{
+	DBusGProxy *adapter;
+
+	DBG("client %p", client);
+
+	adapter = bluetooth_client_get_default_adapter(client);
+	if (adapter == NULL)
+		return FALSE;
+
+	adapter_start_discovery(adapter, NULL);
+
+	g_object_unref(adapter);
+
+	return TRUE;
+}
+
+gboolean bluetooth_client_stop_discovery(BluetoothClient *client)
+{
+	DBusGProxy *adapter;
+
+	DBG("client %p", client);
+
+	adapter = bluetooth_client_get_default_adapter(client);
+	if (adapter == NULL)
+		return FALSE;
+
+	adapter_stop_discovery(adapter, NULL);
+
+	g_object_unref(adapter);
+
+	return TRUE;
+}
+
+typedef struct {
+	BluetoothClientCreateDeviceFunc func;
+	gpointer data;
+} CreateDeviceData;
+
+static void create_device_callback(DBusGProxy *proxy,
+					DBusGProxyCall *call, void *user_data)
+{
+	CreateDeviceData *devdata = user_data;
+	GError *error = NULL;
+	char *path = NULL;
+
+	dbus_g_proxy_end_call(proxy, call, &error,
+			DBUS_TYPE_G_OBJECT_PATH, &path, G_TYPE_INVALID);
+
+	if (error != NULL) {
+		path = NULL;
+		g_error_free(error);
+	}
+
+	if (devdata->func)
+		devdata->func(path, devdata->data);
+
+	g_object_unref(proxy);
+}
+
+gboolean bluetooth_client_create_device(BluetoothClient *client,
+			const char *address, const char *agent,
+			BluetoothClientCreateDeviceFunc func, gpointer data)
+{
+	CreateDeviceData *devdata;
+	DBusGProxy *adapter;
+	DBusGProxyCall *call;
+
+	DBG("client %p", client);
+
+	adapter = bluetooth_client_get_default_adapter(client);
+	if (adapter == NULL)
+		return FALSE;
+
+	devdata = g_try_new0(CreateDeviceData, 1);
+	if (devdata == NULL)
+		return FALSE;
+
+	devdata->func = func;
+	devdata->data = data;
+
+	if (agent != NULL)
+		call = dbus_g_proxy_begin_call_with_timeout(adapter,
+				"CreatePairedDevice", create_device_callback,
+				devdata, g_free, 90 * 1000,
+				G_TYPE_STRING, address,
+				DBUS_TYPE_G_OBJECT_PATH, agent,
+				G_TYPE_STRING, "DisplayOnly", G_TYPE_INVALID);
+	else
+		call = dbus_g_proxy_begin_call_with_timeout(adapter,
+				"CreateDevice", create_device_callback,
+				devdata, g_free, 60 * 1000,
+				G_TYPE_STRING, address, G_TYPE_INVALID);
+
+	return TRUE;
+}
+
+gboolean bluetooth_client_set_trusted(BluetoothClient *client,
+					const char *device, gboolean trusted)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	DBusGProxy *proxy;
+	GValue value = { 0 };
+
+	DBG("client %p", client);
+
+	proxy = dbus_g_proxy_new_from_proxy(priv->manager,
+					BLUEZ_DEVICE_INTERFACE, device);
+	if (proxy == NULL)
+		return FALSE;
+
+	g_value_init(&value, G_TYPE_BOOLEAN);
+	g_value_set_boolean(&value, trusted);
+
+	dbus_g_proxy_call(proxy, "SetProperty", NULL,
+					G_TYPE_STRING, "Trusted",
+					G_TYPE_VALUE, &value,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+	g_value_unset(&value);
+
+	g_object_unref(proxy);
+
+	return TRUE;
+}
+
+typedef struct {
+	BluetoothClientConnectFunc func;
+	gpointer data;
+} ConnectData;
+
+static void connect_input_callback(DBusGProxy *proxy,
+					DBusGProxyCall *call, void *user_data)
+{
+	ConnectData *conndata = user_data;
+	GError *error = NULL;
+
+	dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INVALID);
+
+	if (error != NULL)
+		g_error_free(error);
+
+	if (conndata->func)
+		conndata->func(conndata->data);
+
+	g_object_unref(proxy);
+}
+
+gboolean bluetooth_client_connect_input(BluetoothClient *client,
+				const char *device,
+				BluetoothClientConnectFunc func, gpointer data)
+{
+	BluetoothClientPrivate *priv = BLUETOOTH_CLIENT_GET_PRIVATE(client);
+	ConnectData *conndata;
+	DBusGProxy *proxy;
+	DBusGProxyCall *call;
+
+	DBG("client %p", client);
+
+	proxy = dbus_g_proxy_new_from_proxy(priv->manager,
+						BLUEZ_INPUT_INTERFACE, device);
+	if (proxy == NULL)
+		return FALSE;
+
+	conndata = g_try_new0(ConnectData, 1);
+	if (conndata == NULL) {
+		g_object_unref(proxy);
+		return FALSE;
+	}
+
+	conndata->func = func;
+	conndata->data = data;
+
+	call = dbus_g_proxy_begin_call(proxy, "Connect",
+				connect_input_callback, conndata, g_free,
+							G_TYPE_INVALID);
+
+	return TRUE;
+}

Added: trunk/common/bluetooth-client.h
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-client.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,131 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BLUETOOTH_CLIENT_H
+#define __BLUETOOTH_CLIENT_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define BLUETOOTH_TYPE_CLIENT (bluetooth_client_get_type())
+#define BLUETOOTH_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+					BLUETOOTH_TYPE_CLIENT, BluetoothClient))
+#define BLUETOOTH_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+					BLUETOOTH_TYPE_CLIENT, BluetoothClientClass))
+#define BLUETOOTH_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+							BLUETOOTH_TYPE_CLIENT))
+#define BLUETOOTH_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+							BLUETOOTH_TYPE_CLIENT))
+#define BLUETOOTH_GET_CLIENT_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+					BLUETOOTH_TYPE_CLIENT, BluetoothClientClass))
+
+typedef struct _BluetoothClient BluetoothClient;
+typedef struct _BluetoothClientClass BluetoothClientClass;
+
+struct _BluetoothClient {
+	GObject parent;
+};
+
+struct _BluetoothClientClass {
+	GObjectClass parent_class;
+};
+
+GType bluetooth_client_get_type(void);
+
+BluetoothClient *bluetooth_client_new(void);
+
+GtkTreeModel *bluetooth_client_get_model(BluetoothClient *client);
+GtkTreeModel *bluetooth_client_get_filter_model(BluetoothClient *client,
+				GtkTreeModelFilterVisibleFunc func,
+				gpointer data, GtkDestroyNotify destroy);
+GtkTreeModel *bluetooth_client_get_adapter_model(BluetoothClient *client);
+GtkTreeModel *bluetooth_client_get_device_model(BluetoothClient *client,
+							DBusGProxy *adapter);
+GtkTreeModel *bluetooth_client_get_device_filter_model(BluetoothClient *client,
+		DBusGProxy *adapter, GtkTreeModelFilterVisibleFunc func,
+				gpointer data, GtkDestroyNotify destroy);
+
+DBusGProxy *bluetooth_client_get_default_adapter(BluetoothClient *client);
+
+gboolean bluetooth_client_start_discovery(BluetoothClient *client);
+gboolean bluetooth_client_stop_discovery(BluetoothClient *client);
+
+typedef void (*BluetoothClientCreateDeviceFunc) (const char *path, gpointer data);
+
+gboolean bluetooth_client_create_device(BluetoothClient *client,
+			const char *address, const char *agent,
+			BluetoothClientCreateDeviceFunc func, gpointer data);
+
+gboolean bluetooth_client_set_trusted(BluetoothClient *client,
+					const char *device, gboolean trusted);
+
+typedef void (*BluetoothClientConnectFunc) (gpointer data);
+
+gboolean bluetooth_client_connect_input(BluetoothClient *client,
+				const char *device,
+				BluetoothClientConnectFunc, gpointer data);
+
+enum {
+	BLUETOOTH_COLUMN_PROXY,
+	BLUETOOTH_COLUMN_ADDRESS,
+	BLUETOOTH_COLUMN_ALIAS,
+	BLUETOOTH_COLUMN_NAME,
+	BLUETOOTH_COLUMN_TYPE,
+	BLUETOOTH_COLUMN_ICON,
+	BLUETOOTH_COLUMN_RSSI,
+	BLUETOOTH_COLUMN_DEFAULT,
+	BLUETOOTH_COLUMN_PAIRED,
+	BLUETOOTH_COLUMN_TRUSTED,
+	BLUETOOTH_COLUMN_CONNECTED,
+	BLUETOOTH_COLUMN_DISCOVERING,
+	_BLUETOOTH_NUM_COLUMNS
+};
+
+enum {
+	BLUETOOTH_TYPE_ANY		= 1 << 0,
+	BLUETOOTH_TYPE_PHONE		= 1 << 1,
+	BLUETOOTH_TYPE_MODEM		= 1 << 2,
+	BLUETOOTH_TYPE_COMPUTER		= 1 << 3,
+	BLUETOOTH_TYPE_NETWORK		= 1 << 4,
+	BLUETOOTH_TYPE_HEADSET		= 1 << 5,
+	BLUETOOTH_TYPE_HEADPHONE	= 1 << 6,
+	BLUETOOTH_TYPE_KEYBOARD		= 1 << 7,
+	BLUETOOTH_TYPE_MOUSE		= 1 << 8,
+	BLUETOOTH_TYPE_CAMERA		= 1 << 9,
+	BLUETOOTH_TYPE_PRINTER		= 1 << 10,
+	BLUETOOTH_TYPE_JOYPAD		= 1 << 11,
+	BLUETOOTH_TYPE_TABLET		= 1 << 12,
+};
+
+#define _BLUETOOTH_TYPE_NUM_TYPES 12
+
+#define BLUETOOTH_TYPE_INPUT (BLUETOOTH_TYPE_KEYBOARD | BLUETOOTH_TYPE_MOUSE)
+
+const gchar *bluetooth_type_to_string(guint type);
+
+G_END_DECLS
+
+#endif /* __BLUETOOTH_CLIENT_H */

Added: trunk/common/bluetooth-client.xml
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-client.xml	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/">
+  <interface name="manager">
+    <method name="DefaultAdapter">
+      <arg type="o" direction="out"/>
+    </method>
+
+    <method name="FindAdapter">
+      <arg type="s" name="pattern"/>
+      <arg type="o" direction="out"/>
+    </method>
+
+    <method name="ListAdapters">
+      <arg type="ao" direction="out"/>
+    </method>
+  </interface>
+
+  <interface name="adapter">
+    <method name="GetProperties">
+      <arg type="a{sv}" direction="out"/>
+    </method>
+
+    <method name="ListDevices">
+      <arg type="ao" direction="out"/>
+    </method>
+
+    <method name="StartDiscovery">
+    </method>
+
+    <method name="StopDiscovery">
+    </method>
+
+    <method name="CreateDevice">
+      <arg type="s" name="address"/>
+      <arg type="o" direction="out"/>
+    </method>
+
+    <method name="CreatePairedDevice">
+      <arg type="s" name="address"/>
+      <arg type="o" name="agent"/>
+      <arg type="s" name="capability"/>
+      <arg type="o" direction="out"/>
+    </method>
+
+    <method name="RemoveDevice">
+      <arg type="o" name="device"/>
+    </method>
+  </interface>
+
+  <interface name="device">
+    <method name="GetProperties">
+      <arg type="a{sv}" direction="out"/>
+    </method>
+  </interface>
+</node>

Added: trunk/common/bluetooth-device-selection.c
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-device-selection.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,816 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "client.h"
+
+#include "bluetooth-device-selection.h"
+
+enum {
+	SELECTED_DEVICE_CHANGED,
+	LAST_SIGNAL
+};
+
+static int selection_table_signals[LAST_SIGNAL] = { 0 };
+
+#define BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+										 BLUETOOTH_TYPE_DEVICE_SELECTION, BluetoothDeviceSelectionPrivate))
+
+typedef struct _BluetoothDeviceSelectionPrivate BluetoothDeviceSelectionPrivate;
+
+struct _BluetoothDeviceSelectionPrivate {
+	BluetoothClient *client;
+	GtkTreeSelection *selection;
+	GtkTreeModel *model, *filter;
+	GtkWidget *label;
+
+	gulong discov_started_id;
+	gulong discov_completed_id;
+	gulong default_adapter_changed_id;
+
+	/* Widgets/UI bits that can be shown or hidden */
+	GtkCellRenderer *bonded_cell;
+	GtkWidget *treeview;
+	GtkWidget *search_button;
+	GtkWidget *device_type_label, *device_type;
+	GtkWidget *device_category_label, *device_category;
+	GtkWidget *filters_vbox;
+
+	/* Current filter */
+	int device_type_filter;
+	int device_category_filter;
+
+	guint show_bonded : 1;
+	guint show_search : 1;
+	guint show_device_type : 1;
+	guint show_device_category : 1;
+};
+
+G_DEFINE_TYPE(BluetoothDeviceSelection, bluetooth_device_selection, GTK_TYPE_VBOX)
+
+static const char *
+bluetooth_device_category_to_string (int type)
+{
+	switch (type) {
+	case BLUETOOTH_CATEGORY_ALL:
+		return N_("All categories");
+	case BLUETOOTH_CATEGORY_BONDED:
+		return N_("Bonded");
+	case BLUETOOTH_CATEGORY_TRUSTED:
+		return N_("Trusted");
+	default:
+		return N_("Unknown");
+	}
+}
+
+static int
+int_log2(int v)
+{
+	int rv = 0;
+	while (v >>= 1)
+		rv++;
+	return rv;
+}
+
+static void
+name_to_text (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+	      GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	gchar *address;
+	gchar *name;
+
+	gtk_tree_model_get (model, iter, COLUMN_ADDRESS, &address,
+			    COLUMN_NAME, &name, -1);
+
+	/* If we don't have a name, replace the name with the
+	 * Bluetooth address, with the ":" replaced by "-" */
+	if (name == NULL) {
+		name = g_strdup (address);
+		g_strdelimit (name, ":", '-');
+	}
+
+	g_object_set (cell, "text", name ? name : address, NULL);
+
+	g_free (name);
+	g_free (address);
+}
+
+static void
+type_to_icon (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+	      GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	guint type;
+
+	gtk_tree_model_get (model, iter, COLUMN_TYPE, &type, -1);
+
+	switch (type) {
+	case BLUETOOTH_TYPE_PHONE:
+		g_object_set (cell, "icon-name", "stock_cell-phone", NULL);
+		break;
+	case BLUETOOTH_TYPE_MODEM:
+		g_object_set (cell, "icon-name", "modem", NULL);
+		break;
+	case BLUETOOTH_TYPE_COMPUTER:
+		g_object_set (cell, "icon-name", "computer", NULL);
+		break;
+	case BLUETOOTH_TYPE_NETWORK:
+		g_object_set (cell, "icon-name", "network-wireless", NULL);
+		break;
+	case BLUETOOTH_TYPE_HEADSET:
+		g_object_set (cell, "icon-name", "stock_headphones", NULL);
+		break;
+	case BLUETOOTH_TYPE_HEADPHONE:
+		g_object_set (cell, "icon-name", "stock_headphones", NULL);
+		break;
+	case BLUETOOTH_TYPE_KEYBOARD:
+		g_object_set (cell, "icon-name", "input-keyboard", NULL);
+		break;
+	case BLUETOOTH_TYPE_MOUSE:
+		g_object_set (cell, "icon-name", "input-mouse", NULL);
+		break;
+	case BLUETOOTH_TYPE_CAMERA:
+		g_object_set (cell, "icon-name", "camera-photo", NULL);
+		break;
+	case BLUETOOTH_TYPE_PRINTER:
+		g_object_set (cell, "icon-name", "printer", NULL);
+		break;
+	default:
+		g_object_set (cell, "icon-name", "bluetooth", NULL);
+		break;
+	}
+}
+
+static void
+bonded_to_icon (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+	      GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	gboolean bonded;
+
+	gtk_tree_model_get (model, iter, COLUMN_BONDED, &bonded, -1);
+
+	if (bonded == FALSE)
+		g_object_set (cell, "stock-id", NULL, NULL);
+	else
+		g_object_set (cell, "stock-id", GTK_STOCK_DIALOG_AUTHENTICATION, NULL);
+}
+
+static void
+type_to_text (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+	      GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	guint type;
+
+	gtk_tree_model_get (model, iter, COLUMN_TYPE, &type, -1);
+	if (type == 0)
+		g_object_set (cell, "text", _("Unknown"), NULL);
+	else
+		g_object_set (cell, "text", bluetooth_type_to_string (type), NULL);
+}
+
+void
+bluetooth_device_selection_start_discovery (BluetoothDeviceSelection *self)
+{
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+
+	gtk_widget_set_sensitive (GTK_WIDGET(priv->search_button), FALSE);
+	bluetooth_client_discover_devices (priv->client, NULL);
+}
+
+gchar *
+bluetooth_device_selection_get_selected_device (BluetoothDeviceSelection *self)
+{
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+	GtkTreeIter iter;
+	gchar *address;
+	gboolean selected;
+
+	selected = gtk_tree_selection_get_selected (priv->selection, NULL, &iter);
+	if (selected == FALSE)
+		return NULL;
+
+	gtk_tree_model_get (priv->filter, &iter, COLUMN_ADDRESS, &address, -1);
+	return address;
+}
+
+gchar *
+bluetooth_device_selection_get_selected_device_name (BluetoothDeviceSelection *self)
+{
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+	GtkTreeIter iter;
+	gchar *name;
+	gboolean selected;
+
+	selected = gtk_tree_selection_get_selected (priv->selection, NULL, &iter);
+	if (selected == FALSE)
+		return NULL;
+
+	gtk_tree_model_get (priv->filter, &iter, COLUMN_NAME, &name, -1);
+	return name;
+}
+
+static void
+search_button_clicked (GtkButton *button, gpointer user_data)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION(user_data);
+
+	bluetooth_device_selection_start_discovery (self);
+}
+
+static void
+discovery_started (BluetoothClient *client, const char *adapter_path, gboolean is_default, gpointer user_data)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION(user_data);
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+
+	if (is_default)
+		gtk_widget_set_sensitive (GTK_WIDGET(priv->search_button), FALSE);
+}
+
+static void
+discovery_completed (BluetoothClient *client, const char *adapter_path, gboolean is_default, gpointer user_data)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION(user_data);
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+
+	if (is_default)
+		gtk_widget_set_sensitive (GTK_WIDGET(priv->search_button), TRUE);
+}
+
+static void
+select_browse_device_callback (GtkTreeSelection *selection, gpointer user_data)
+{
+	BluetoothDeviceSelection *self = user_data;
+	gchar *address;
+
+	g_object_notify (G_OBJECT(self), "device-selected");
+	address = bluetooth_device_selection_get_selected_device (self);
+	g_signal_emit (G_OBJECT (self),
+		       selection_table_signals[SELECTED_DEVICE_CHANGED],
+		       0, address);
+	g_free (address);
+}
+
+static gboolean
+filter_type_func (GtkTreeModel *model, GtkTreeIter *iter, BluetoothDeviceSelectionPrivate *priv)
+{
+	int type;
+
+	if (priv->device_type_filter == BLUETOOTH_TYPE_ANY)
+		return TRUE;
+
+	gtk_tree_model_get (model, iter, COLUMN_TYPE, &type, -1);
+	return (type & priv->device_type_filter);
+}
+
+static gboolean
+filter_category_func (GtkTreeModel *model, GtkTreeIter *iter, BluetoothDeviceSelectionPrivate *priv)
+{
+	if (priv->device_category_filter == BLUETOOTH_CATEGORY_ALL)
+		return TRUE;
+
+	if (priv->device_category_filter == BLUETOOTH_CATEGORY_BONDED) {
+		gboolean bonded;
+
+		gtk_tree_model_get (model, iter, COLUMN_BONDED, &bonded, -1);
+		return bonded;
+	}
+	if (priv->device_category_filter == BLUETOOTH_CATEGORY_TRUSTED) {
+		gboolean trusted;
+
+		gtk_tree_model_get (model, iter, COLUMN_TRUSTED, &trusted, -1);
+		return trusted;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+filter_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION (data);
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+
+	return filter_type_func (model, iter, priv) && filter_category_func (model, iter, priv);
+}
+
+static void
+filter_type_changed_cb (GtkComboBox *widget, gpointer data)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION (data);
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+
+	priv->device_type_filter = 1 << gtk_combo_box_get_active (GTK_COMBO_BOX(priv->device_type));
+	if (priv->filter)
+		gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+	g_object_notify (G_OBJECT(self), "device-type-filter");
+}
+static void
+filter_category_changed_cb (GtkComboBox *widget, gpointer data)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION (data);
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+
+	priv->device_category_filter = gtk_combo_box_get_active (GTK_COMBO_BOX(priv->device_category));
+	if (priv->filter)
+		gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+	g_object_notify (G_OBJECT(self), "device-category-filter");
+}
+
+static void default_adapter_changed (GObject    *gobject,
+				     GParamSpec *arg1,
+				     gpointer    data)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION (data);
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+	char *adapter;
+
+	g_object_get (gobject, "default-adapter", &adapter, NULL);
+
+	if (adapter == NULL) {
+		gtk_widget_set_sensitive (GTK_WIDGET (priv->treeview), FALSE);
+		gtk_tree_view_set_model (GTK_TREE_VIEW(priv->treeview), NULL);
+	}
+
+	if (priv->model) {
+		g_object_unref (priv->model);
+		priv->model = NULL;
+	}
+
+	if (adapter == NULL)
+		return;
+
+	priv->model = bluetooth_client_get_model_with_filter (priv->client, NULL, NULL, NULL);
+	if (priv->model) {
+		priv->filter = gtk_tree_model_filter_new (priv->model, NULL);
+		gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter),
+							filter_func, self, NULL);
+		gtk_tree_view_set_model (GTK_TREE_VIEW(priv->treeview), priv->filter);
+		g_object_unref (priv->filter);
+		gtk_widget_set_sensitive (GTK_WIDGET (priv->treeview), TRUE);
+	}
+}
+
+static GtkWidget *
+create_treeview (BluetoothDeviceSelection *self)
+{
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+	GtkWidget *scrolled, *tree;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+
+	/* Create the scrolled window */
+	scrolled = gtk_scrolled_window_new (NULL, NULL);
+
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled),
+					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scrolled),
+					     GTK_SHADOW_OUT);
+
+	/* Create the tree view */
+	tree = gtk_tree_view_new ();
+
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(tree), TRUE);
+
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(tree), TRUE);
+
+	g_object_set (tree, "show-expanders", FALSE, NULL);
+
+	column = gtk_tree_view_column_new ();
+
+	gtk_tree_view_column_set_title (column, _("Device"));
+
+	/* The type icon */
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_set_spacing (column, 4);
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+
+	gtk_tree_view_column_set_cell_data_func (column, renderer,
+						 type_to_icon, NULL, NULL);
+
+	/* The device name */
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+
+	gtk_tree_view_column_set_cell_data_func (column, renderer,
+						 name_to_text, NULL, NULL);
+
+	/* The bonded icon */
+	priv->bonded_cell = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_end (column, priv->bonded_cell, FALSE);
+
+	gtk_tree_view_column_set_cell_data_func (column, priv->bonded_cell,
+						 bonded_to_icon, NULL, NULL);
+
+	gtk_tree_view_append_column (GTK_TREE_VIEW(tree), column);
+
+	gtk_tree_view_column_set_min_width (GTK_TREE_VIEW_COLUMN(column), 280);
+
+	gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW(tree), -1,
+						    _("Type"), gtk_cell_renderer_text_new(),
+						    type_to_text, NULL, NULL);
+
+	priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree));
+
+	gtk_tree_selection_set_mode (priv->selection, GTK_SELECTION_SINGLE);
+
+	g_signal_connect (G_OBJECT(priv->selection), "changed",
+			  G_CALLBACK(select_browse_device_callback), self);
+
+	/* Set the model, and filter */
+	priv->model = bluetooth_client_get_model_with_filter (priv->client, NULL, NULL, NULL);
+	if (priv->model) {
+		priv->filter = gtk_tree_model_filter_new (priv->model, NULL);
+		gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter),
+							filter_func, self, NULL);
+		gtk_tree_view_set_model (GTK_TREE_VIEW(tree), priv->filter);
+		g_object_unref (priv->filter);
+	} else {
+		gtk_widget_set_sensitive (GTK_WIDGET (tree), FALSE);
+	}
+
+	gtk_container_add (GTK_CONTAINER(scrolled), tree);
+	priv->treeview = tree;
+
+	return scrolled;
+}
+
+static void
+bluetooth_device_selection_init(BluetoothDeviceSelection *self)
+{
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(self);
+	GtkTooltips *tooltips;
+	char *str;
+	int i;
+
+	GtkWidget *vbox;
+	GtkWidget *label;
+	GtkWidget *alignment;
+	GtkWidget *hbox;
+	GtkWidget *scrolled_window;
+	GtkWidget *table;
+
+	priv->show_bonded = FALSE;
+	priv->show_search = FALSE;
+
+	priv->client = bluetooth_client_new ();
+
+	priv->discov_started_id = g_signal_connect (G_OBJECT(priv->client),
+			"discovery-started", G_CALLBACK(discovery_started), self);
+	priv->discov_completed_id = g_signal_connect (G_OBJECT(priv->client),
+			"discovery-completed", G_CALLBACK(discovery_completed), self);
+
+	tooltips = gtk_tooltips_new ();
+
+	/* Setup the widget itself */
+	gtk_box_set_spacing (GTK_BOX(self), 18);
+	gtk_container_set_border_width (GTK_CONTAINER(self), 0);
+
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_widget_show (vbox);
+	gtk_box_pack_start (GTK_BOX (self), vbox, TRUE, TRUE, 0);
+
+	/* The top level label */
+	str = g_strdup_printf ("<b>%s</b>", _("Recognized Bluetooth Devices"));
+	priv->label = gtk_label_new (str);
+	g_free (str);
+	gtk_widget_show (priv->label);
+	gtk_box_pack_start (GTK_BOX (vbox), priv->label, FALSE, TRUE, 0);
+	gtk_label_set_use_markup (GTK_LABEL (priv->label), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (priv->label), 0, 0.5);
+
+	alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+	gtk_widget_show (alignment);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, TRUE, TRUE, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
+
+	/* The treeview label */
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_widget_show (vbox);
+	gtk_container_add (GTK_CONTAINER (alignment), vbox);
+
+	hbox = gtk_hbox_new (FALSE, 24);
+	gtk_widget_show (hbox);
+	gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
+
+	label = gtk_label_new_with_mnemonic (_("_Select a device from the following list:"));
+	gtk_widget_show (label);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 1);
+
+	/* The search button */
+	priv->search_button = gtk_button_new_with_mnemonic (_("S_earch"));
+	gtk_widget_set_no_show_all (priv->search_button, TRUE);
+	gtk_box_pack_end (GTK_BOX (hbox), priv->search_button, FALSE, TRUE, 0);
+	g_signal_connect (G_OBJECT(priv->search_button), "clicked",
+			  G_CALLBACK(search_button_clicked), self);
+	gtk_tooltips_set_tip (tooltips, priv->search_button, _("Rescan Bluetooth devices"), NULL);
+	if (priv->show_search)
+		gtk_widget_show (priv->search_button);
+
+	/* The treeview */
+	scrolled_window = create_treeview (self);
+	gtk_widget_show_all (scrolled_window);
+	gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
+
+	vbox = gtk_vbox_new (FALSE, 6);
+	gtk_widget_show (vbox);
+	gtk_box_pack_start (GTK_BOX (self), vbox, FALSE, TRUE, 0);
+	priv->filters_vbox = vbox;
+
+	/* The filters */
+	str = g_strdup_printf ("<b>%s</b>", _("Show Only Bluetooth Devices With..."));
+	label = gtk_label_new (str);
+	g_free (str);
+	gtk_widget_show (label);
+	gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+	gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+
+	alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+	gtk_widget_show (alignment);
+	gtk_box_pack_start (GTK_BOX (vbox), alignment, TRUE, TRUE, 0);
+	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
+
+	table = gtk_table_new (2, 2, FALSE);
+	gtk_widget_show (table);
+	gtk_container_add (GTK_CONTAINER (alignment), table);
+	gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+	gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+
+	/* The device category filter */
+	label = gtk_label_new_with_mnemonic (_("Device _category:"));
+	gtk_widget_set_no_show_all (label, TRUE);
+	gtk_widget_show (label);
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+			  (GtkAttachOptions) (GTK_FILL),
+			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	priv->device_category_label = label;
+
+	priv->device_category = gtk_combo_box_new_text ();
+	gtk_widget_set_no_show_all (priv->device_category, TRUE);
+	gtk_widget_show (priv->device_category);
+	gtk_table_attach (GTK_TABLE (table), priv->device_category, 1, 2, 1, 2,
+			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
+	gtk_tooltips_set_tip (tooltips, priv->device_category, _("Select the device category to filter above list"), NULL);
+	for (i = 0; i < BLUETOOTH_CATEGORY_NUM_CATEGORIES; i++) {
+		gtk_combo_box_append_text (GTK_COMBO_BOX(priv->device_category),
+					   _(bluetooth_device_category_to_string (i)));
+	}
+	g_signal_connect (G_OBJECT (priv->device_category), "changed",
+			  G_CALLBACK(filter_category_changed_cb), self);
+	gtk_combo_box_set_active (GTK_COMBO_BOX(priv->device_category), priv->device_category_filter);
+	if (priv->show_device_category) {
+		gtk_widget_show (priv->device_category_label);
+		gtk_widget_show (priv->device_category);
+	}
+
+	/* The device type filter */
+	label = gtk_label_new_with_mnemonic (_("Device _type:"));
+	gtk_widget_set_no_show_all (label, TRUE);
+	gtk_widget_show (label);
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+			  (GtkAttachOptions) (GTK_FILL),
+			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
+	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+	priv->device_type_label = label;
+
+	priv->device_type = gtk_combo_box_new_text ();
+	gtk_widget_set_no_show_all (priv->device_type, TRUE);
+	gtk_widget_show (priv->device_type);
+	gtk_table_attach (GTK_TABLE (table), priv->device_type, 1, 2, 0, 1,
+			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+			  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0);
+	gtk_tooltips_set_tip (tooltips, priv->device_type, _("Select the device type to filter above list"), NULL);
+	/* The types match the types used in client.h */
+	for (i = 0; i < BLUETOOTH_TYPE_NUM_TYPES; i++) {
+		gtk_combo_box_append_text (GTK_COMBO_BOX(priv->device_type),
+					   _(bluetooth_type_to_string (1 << i)));
+	}
+	g_signal_connect (G_OBJECT (priv->device_type), "changed",
+			  G_CALLBACK(filter_type_changed_cb), self);
+	gtk_combo_box_set_active (GTK_COMBO_BOX(priv->device_type), int_log2(priv->device_type_filter));
+	if (priv->show_device_type) {
+		gtk_widget_show (priv->device_type_label);
+		gtk_widget_show (priv->device_type);
+	}
+
+	/* if filters are not visible hide the vbox */
+	if (!priv->show_device_type && !priv->show_device_category)
+		gtk_widget_hide (priv->filters_vbox);
+
+	priv->default_adapter_changed_id = g_signal_connect (priv->client, "notify::default-adapter",
+							     G_CALLBACK (default_adapter_changed), self);
+}
+
+static void
+bluetooth_device_selection_finalize (GObject *object)
+{
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(object);
+
+	g_signal_handler_disconnect (G_OBJECT(priv->client), priv->discov_started_id);
+	g_signal_handler_disconnect (G_OBJECT(priv->client), priv->discov_completed_id);
+	g_signal_handler_disconnect (G_OBJECT(priv->client), priv->default_adapter_changed_id);
+
+	bluetooth_client_cancel_discovery (priv->client, NULL);
+}
+
+enum {
+	PROP_0,
+	PROP_TITLE,
+	PROP_DEVICE_SELECTED,
+	PROP_DEVICE_SELECTED_NAME,
+	PROP_SHOW_BONDING,
+	PROP_SHOW_SEARCH,
+	PROP_SHOW_DEVICE_TYPE,
+	PROP_SHOW_DEVICE_CATEGORY,
+	PROP_DEVICE_TYPE_FILTER,
+	PROP_DEVICE_CATEGORY_FILTER
+};
+
+static void
+bluetooth_device_selection_set_property (GObject *object, guint prop_id,
+					 const GValue *value, GParamSpec *pspec)
+{
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(object);
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		{
+			char *str;
+
+			if (!g_value_get_string(value))
+				break;
+
+			str = g_strdup_printf ("<b>%s</b>", g_value_get_string(value));
+			gtk_label_set_markup (GTK_LABEL(priv->label), str);
+			g_free (str);
+		}
+		break;
+	case PROP_SHOW_BONDING:
+		priv->show_bonded = g_value_get_boolean (value);
+		if (priv->bonded_cell != NULL)
+			g_object_set (G_OBJECT (priv->bonded_cell), "visible", priv->show_bonded, NULL);
+		break;
+	case PROP_SHOW_SEARCH:
+		priv->show_search = g_value_get_boolean (value);
+		g_object_set (G_OBJECT (priv->search_button), "visible", priv->show_search, NULL);
+		break;
+	case PROP_SHOW_DEVICE_TYPE:
+		priv->show_device_type = g_value_get_boolean (value);
+		g_object_set (G_OBJECT (priv->device_type_label), "visible", priv->show_device_type, NULL);
+		g_object_set (G_OBJECT (priv->device_type), "visible", priv->show_device_type, NULL);
+		if (priv->show_device_type || priv->show_device_category)
+			g_object_set (G_OBJECT (priv->filters_vbox), "visible", TRUE, NULL);
+		else
+			g_object_set (G_OBJECT (priv->filters_vbox), "visible", FALSE, NULL);
+		break;
+	case PROP_SHOW_DEVICE_CATEGORY:
+		priv->show_device_category = g_value_get_boolean (value);
+		g_object_set (G_OBJECT (priv->device_category_label), "visible", priv->show_device_category, NULL);
+		g_object_set (G_OBJECT (priv->device_category), "visible", priv->show_device_category, NULL);
+		if (priv->show_device_type || priv->show_device_category)
+			g_object_set (G_OBJECT (priv->filters_vbox), "visible", TRUE, NULL);
+		else
+			g_object_set (G_OBJECT (priv->filters_vbox), "visible", FALSE, NULL);
+		break;
+	case PROP_DEVICE_TYPE_FILTER:
+		priv->device_type_filter = g_value_get_int (value);
+		gtk_combo_box_set_active (GTK_COMBO_BOX(priv->device_type), int_log2(priv->device_type_filter));
+		break;
+	case PROP_DEVICE_CATEGORY_FILTER:
+		priv->device_category_filter = g_value_get_int (value);
+		gtk_combo_box_set_active (GTK_COMBO_BOX(priv->device_category), priv->device_category_filter);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+bluetooth_device_selection_get_property (GObject *object, guint prop_id,
+					 GValue *value, GParamSpec *pspec)
+{
+	BluetoothDeviceSelection *self = BLUETOOTH_DEVICE_SELECTION(object);
+	BluetoothDeviceSelectionPrivate *priv = BLUETOOTH_DEVICE_SELECTION_GET_PRIVATE(object);
+
+	switch (prop_id) {
+	case PROP_DEVICE_SELECTED:
+		g_value_take_string (value, bluetooth_device_selection_get_selected_device (self));
+		break;
+	case PROP_DEVICE_SELECTED_NAME:
+		g_value_take_string (value, bluetooth_device_selection_get_selected_device_name (self));
+		break;
+	case PROP_SHOW_BONDING:
+		g_value_set_boolean (value, priv->show_bonded);
+		break;
+	case PROP_SHOW_SEARCH:
+		g_value_set_boolean (value, priv->show_search);
+		break;
+	case PROP_SHOW_DEVICE_TYPE:
+		g_value_set_boolean (value, priv->show_device_type);
+		break;
+	case PROP_SHOW_DEVICE_CATEGORY:
+		g_value_set_boolean (value, priv->show_device_category);
+		break;
+	case PROP_DEVICE_TYPE_FILTER:
+		g_value_set_int (value, priv->device_type_filter);
+		break;
+	case PROP_DEVICE_CATEGORY_FILTER:
+		g_value_set_int (value, priv->device_category_filter);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+bluetooth_device_selection_class_init (BluetoothDeviceSelectionClass *klass)
+{
+	g_type_class_add_private(klass, sizeof(BluetoothDeviceSelectionPrivate));
+
+	G_OBJECT_CLASS(klass)->finalize = bluetooth_device_selection_finalize;
+
+	G_OBJECT_CLASS(klass)->set_property = bluetooth_device_selection_set_property;
+	G_OBJECT_CLASS(klass)->get_property = bluetooth_device_selection_get_property;
+
+	selection_table_signals[SELECTED_DEVICE_CHANGED] =
+		g_signal_new ("selected-device-changed",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (BluetoothDeviceSelectionClass, selected_device_changed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__STRING,
+			      G_TYPE_NONE, 1, G_TYPE_STRING);
+
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_TITLE, g_param_spec_string ("title",
+									  NULL, NULL, NULL, G_PARAM_WRITABLE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_DEVICE_SELECTED, g_param_spec_string ("device-selected",
+										    NULL, NULL, NULL, G_PARAM_READABLE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_DEVICE_SELECTED_NAME, g_param_spec_string ("device-selected-name",
+										    NULL, NULL, NULL, G_PARAM_READABLE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_SHOW_BONDING, g_param_spec_boolean ("show-bonding",
+										  NULL, NULL, FALSE, G_PARAM_READWRITE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_SHOW_SEARCH, g_param_spec_boolean ("show-search",
+										 NULL, NULL, FALSE, G_PARAM_READWRITE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_SHOW_DEVICE_TYPE, g_param_spec_boolean ("show-device-type",
+										      NULL, NULL, TRUE, G_PARAM_READWRITE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_SHOW_DEVICE_CATEGORY, g_param_spec_boolean ("show-device-category",
+											  NULL, NULL, TRUE, G_PARAM_READWRITE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_DEVICE_TYPE_FILTER, g_param_spec_int ("device-type-filter", NULL, NULL,
+										    1, 1 << (BLUETOOTH_TYPE_NUM_TYPES - 1), 1, G_PARAM_READWRITE));
+	g_object_class_install_property (G_OBJECT_CLASS(klass),
+					 PROP_DEVICE_CATEGORY_FILTER, g_param_spec_int ("device-category-filter", NULL, NULL,
+					 						0, BLUETOOTH_CATEGORY_NUM_CATEGORIES, 0, G_PARAM_READWRITE));
+}
+
+GtkWidget *
+bluetooth_device_selection_new (const gchar *title)
+{
+	return g_object_new(BLUETOOTH_TYPE_DEVICE_SELECTION,
+			    "title", title,
+			    NULL);
+}
+

Added: trunk/common/bluetooth-device-selection.h
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-device-selection.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,72 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BLUETOOTH_DEVICE_SELECTION_H
+#define __BLUETOOTH_DEVICE_SELECTION_H
+
+#include <gtk/gtkvbox.h>
+
+G_BEGIN_DECLS
+
+#define BLUETOOTH_TYPE_DEVICE_SELECTION (bluetooth_device_selection_get_type())
+#define BLUETOOTH_DEVICE_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+				BLUETOOTH_TYPE_DEVICE_SELECTION, BluetoothDeviceSelection))
+#define BLUETOOTH_DEVICE_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+				BLUETOOTH_TYPE_DEVICE_SELECTION, BluetoothDeviceSelectionClass))
+#define BLUETOOTH_IS_DEVICE_SELECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+						BLUETOOTH_TYPE_DEVICE_SELECTION))
+#define BLUETOOTH_IS_DEVICE_SELECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+						BLUETOOTH_TYPE_DEVICE_SELECTION))
+#define BLUETOOTH_GET_DEVICE_SELECTION_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+				BLUETOOTH_TYPE_DEVICE_SELECTION, BluetoothDeviceSelectionClass))
+
+enum {
+	BLUETOOTH_CATEGORY_ALL,
+	BLUETOOTH_CATEGORY_BONDED,
+	BLUETOOTH_CATEGORY_TRUSTED,
+	BLUETOOTH_CATEGORY_NUM_CATEGORIES
+};
+
+typedef struct _BluetoothDeviceSelection BluetoothDeviceSelection;
+typedef struct _BluetoothDeviceSelectionClass BluetoothDeviceSelectionClass;
+
+struct _BluetoothDeviceSelection {
+	GtkVBox parent;
+};
+
+struct _BluetoothDeviceSelectionClass {
+	GtkVBoxClass parent_class;
+
+	void (*selected_device_changed) (BluetoothDeviceSelection *sel, gchar *device);
+};
+
+GType bluetooth_device_selection_get_type (void);
+
+GtkWidget *bluetooth_device_selection_new (const gchar *title);
+gchar *bluetooth_device_selection_get_selected_device (BluetoothDeviceSelection *sel);
+void bluetooth_device_selection_start_discovery (BluetoothDeviceSelection *sel);
+
+G_END_DECLS
+
+#endif /* __BLUETOOTH_DEVICE_SELECTION_H */

Added: trunk/common/bluetooth-instance.c
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-instance.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,144 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <dbus/dbus-glib.h>
+
+#include "bluetooth-instance.h"
+#include "bluetooth-instance-glue.h"
+
+G_DEFINE_TYPE(BluetoothInstance, bluetooth_instance, G_TYPE_OBJECT)
+
+#define BLUETOOTH_INSTANCE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+				BLUETOOTH_TYPE_INSTANCE, BluetoothInstancePrivate))
+
+typedef struct _BluetoothInstancePrivate BluetoothInstancePrivate;
+
+struct _BluetoothInstancePrivate {
+	GtkWindow *window;
+	DBusGConnection *connection;
+};
+
+void bluetooth_instance_set_window(BluetoothInstance *self, GtkWindow *window)
+{
+	BluetoothInstancePrivate *priv = BLUETOOTH_INSTANCE_GET_PRIVATE(self);
+
+	priv->window = window;
+}
+
+gboolean bluetooth_instance_present(BluetoothInstance *self, GError **error)
+{
+	BluetoothInstancePrivate *priv = BLUETOOTH_INSTANCE_GET_PRIVATE(self);
+
+	if (priv->window)
+		gtk_window_present(priv->window);
+
+	return TRUE;
+}
+
+static void bluetooth_instance_init(BluetoothInstance *self)
+{
+}
+
+static void bluetooth_instance_finalize(GObject *self)
+{
+	BluetoothInstancePrivate *priv = BLUETOOTH_INSTANCE_GET_PRIVATE(self);
+
+	if (priv->connection)
+		dbus_g_connection_unref(priv->connection);
+}
+
+static void bluetooth_instance_class_init(BluetoothInstanceClass *klass)
+{
+	g_type_class_add_private(klass, sizeof(BluetoothInstancePrivate));
+
+	G_OBJECT_CLASS(klass)->finalize = bluetooth_instance_finalize;
+
+	dbus_g_object_type_install_info(BLUETOOTH_TYPE_INSTANCE,
+				&dbus_glib_bluetooth_instance_object_info);
+}
+
+BluetoothInstance *bluetooth_instance_new(const gchar *name)
+{
+	BluetoothInstance *self;
+	BluetoothInstancePrivate *priv;
+	DBusGConnection *conn;
+	DBusGProxy *proxy;
+	GError *error = NULL;
+	gchar *path, *service;
+	guint result;
+
+	service = g_strdup_printf("org.bluez.%s", name);
+	if (service == NULL)
+		return NULL;
+
+	conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+	if (error != NULL) {
+		g_printerr("Can't get session bus: %s\n", error->message);
+		g_error_free(error);
+		g_free(service);
+		return NULL;
+	}
+
+	proxy = dbus_g_proxy_new_for_name(conn, DBUS_SERVICE_DBUS,
+					DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+
+	if (dbus_g_proxy_call(proxy, "RequestName", NULL,
+			G_TYPE_STRING, service, G_TYPE_UINT, 0, G_TYPE_INVALID,
+			G_TYPE_UINT, &result, G_TYPE_INVALID) == FALSE) {
+		g_printerr("Can't get unique name on session bus\n");
+		g_object_unref(proxy);
+		dbus_g_connection_unref(conn);
+		g_free(service);
+		return NULL;
+	}
+
+	g_object_unref(proxy);
+
+	path = g_strdup_printf("/org/bluez/%s", name);
+
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		proxy = dbus_g_proxy_new_for_name(conn, service, path,
+							"org.bluez.Instance");
+
+		dbus_g_proxy_call_no_reply(proxy, "Present",
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+		g_object_unref(G_OBJECT(proxy));
+		dbus_g_connection_unref(conn);
+		g_free(service);
+		g_free(path);
+		return NULL;
+	}
+
+	self = g_object_new(BLUETOOTH_TYPE_INSTANCE, NULL);
+
+	priv = BLUETOOTH_INSTANCE_GET_PRIVATE(self);
+	priv->connection = conn;
+
+	dbus_g_connection_register_g_object(conn, path, G_OBJECT(self));
+
+	g_free(service);
+	g_free(path);
+
+	return self;
+}

Added: trunk/common/bluetooth-instance.h
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-instance.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,61 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BLUETOOTH_INSTANCE_H
+#define __BLUETOOTH_INSTANCE_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define BLUETOOTH_TYPE_INSTANCE (bluetooth_instance_get_type())
+#define BLUETOOTH_INSTANCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+				BLUETOOTH_TYPE_INSTANCE, BluetoothInstance))
+#define BLUETOOTH_INSTANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+				BLUETOOTH_TYPE_INSTANCE, BluetoothInstanceClass))
+#define BLUETOOTH_IS_INSTANCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+						BLUETOOTH_TYPE_INSTANCE))
+#define BLUETOOTH_IS_INSTANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+						BLUETOOTH_TYPE_INSTANCE))
+#define BLUETOOTH_GET_INSTANCE_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+				BLUETOOTH_TYPE_INSTANCE, BluetoothInstanceClass))
+
+typedef struct _BluetoothInstance BluetoothInstance;
+typedef struct _BluetoothInstanceClass BluetoothInstanceClass;
+
+struct _BluetoothInstance {
+	GObject parent;
+};
+
+struct _BluetoothInstanceClass {
+	GObjectClass parent_class;
+};
+
+GType bluetooth_instance_get_type(void);
+BluetoothInstance *bluetooth_instance_new(const gchar *name);
+void bluetooth_instance_set_window(BluetoothInstance *self, GtkWindow *window);
+gboolean bluetooth_instance_present(BluetoothInstance *self, GError **error);
+
+G_END_DECLS
+
+#endif /* __BLUETOOTH_INSTANCE_H */

Added: trunk/common/bluetooth-instance.xml
==============================================================================
--- (empty file)
+++ trunk/common/bluetooth-instance.xml	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/">
+  <interface name="org.bluez.Instance">
+    <method name="Present">
+    </method>
+  </interface>
+</node>

Added: trunk/common/helper.c
==============================================================================
--- (empty file)
+++ trunk/common/helper.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,254 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <bluetooth-client.h>
+
+static void connected_to_icon(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+			GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	gboolean connected;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_CONNECTED, &connected, -1);
+
+	if (connected == TRUE)
+		g_object_set(cell, "icon-name", GTK_STOCK_CONNECT, NULL);
+
+	g_object_set(cell, "visible", connected, NULL);
+}
+
+static void trusted_to_icon(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+			GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	gboolean trusted;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_TRUSTED, &trusted, -1);
+
+	if (trusted == TRUE)
+		g_object_set(cell, "icon-name", GTK_STOCK_ABOUT, NULL);
+
+	g_object_set(cell, "visible", trusted, NULL);
+}
+
+static void paired_to_icon(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+			GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	gboolean paired;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PAIRED, &paired, -1);
+
+	if (paired == TRUE)
+		g_object_set(cell, "icon-name",
+				GTK_STOCK_DIALOG_AUTHENTICATION, NULL);
+
+	g_object_set(cell, "visible", paired, NULL);
+}
+
+GtkWidget *create_tree(GtkTreeModel *model, gboolean icons)
+{
+	GtkWidget *tree;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+
+	tree = gtk_tree_view_new();
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
+	gtk_widget_set_size_request(tree, -1, 100);
+
+	gtk_tree_view_set_model(GTK_TREE_VIEW(tree), model);
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_title(column, "Device");
+	gtk_tree_view_column_set_expand(GTK_TREE_VIEW_COLUMN(column), TRUE);
+	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
+
+	if (icons == TRUE) {
+		renderer = gtk_cell_renderer_pixbuf_new();
+		gtk_tree_view_column_set_spacing(column, 4);
+		gtk_tree_view_column_pack_start(column, renderer, FALSE);
+		gtk_tree_view_column_add_attribute(column, renderer,
+					"icon-name", BLUETOOTH_COLUMN_ICON);
+	}
+
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(column, renderer, TRUE);
+	gtk_tree_view_column_add_attribute(column, renderer,
+					"text", BLUETOOTH_COLUMN_ALIAS);
+
+	gtk_tree_view_insert_column_with_data_func(GTK_TREE_VIEW(tree), -1,
+				"Connected", gtk_cell_renderer_pixbuf_new(),
+					connected_to_icon, NULL, NULL);
+
+	gtk_tree_view_insert_column_with_data_func(GTK_TREE_VIEW(tree), -1,
+				"Trusted", gtk_cell_renderer_pixbuf_new(),
+					trusted_to_icon, NULL, NULL);
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_visible(column, FALSE);
+	gtk_tree_view_insert_column_with_data_func(GTK_TREE_VIEW(tree), -1,
+				"Paired", gtk_cell_renderer_pixbuf_new(),
+					paired_to_icon, NULL, NULL);
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_column_set_visible(column, FALSE);
+	gtk_tree_view_insert_column(GTK_TREE_VIEW(tree), column, -1);
+	gtk_tree_view_set_expander_column(GTK_TREE_VIEW(tree), column);
+
+	return tree;
+}
+
+static void select_callback(GtkTreeSelection *selection, gpointer user_data)
+{
+	GtkWidget *dialog = user_data;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gboolean selected;
+
+	selected = gtk_tree_selection_get_selected(selection, &model, &iter);
+
+	if (selected == TRUE) {
+		gchar *address;
+
+		address = g_object_get_data(G_OBJECT(dialog), "address");
+		g_free(address);
+
+		gtk_tree_model_get(model, &iter,
+				BLUETOOTH_COLUMN_ADDRESS, &address, -1);
+
+		g_object_set_data(G_OBJECT(dialog), "address", address);
+	}
+
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+						GTK_RESPONSE_ACCEPT, selected);
+}
+
+gchar *show_browse_dialog(void)
+{
+	BluetoothClient *client;
+	GtkWidget *dialog;
+	GtkWidget *scrolled;
+	GtkWidget *tree;
+	GtkTreeModel *model;
+	GtkTreeModel *sorted;
+	GtkTreeSelection *selection;
+	gchar *address = NULL;
+
+	dialog = gtk_dialog_new_with_buttons(_("Select Device"),
+				NULL, GTK_DIALOG_NO_SEPARATOR,
+				GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+				GTK_STOCK_CONNECT, GTK_RESPONSE_ACCEPT, NULL);
+
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+						GTK_RESPONSE_ACCEPT, FALSE);
+
+	gtk_window_set_default_size(GTK_WINDOW(dialog), 450, 400);
+
+	gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
+	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 2);
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_container_set_border_width(GTK_CONTAINER(scrolled), 5);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
+							GTK_SHADOW_OUT);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), scrolled);
+
+	client = bluetooth_client_new();
+
+	model = bluetooth_client_get_device_model(client, NULL);
+	if (model != NULL) {
+		sorted = gtk_tree_model_sort_new_with_model(model);
+		gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sorted),
+				BLUETOOTH_COLUMN_RSSI, GTK_SORT_DESCENDING);
+	} else
+		sorted = NULL;
+
+	tree = create_tree(sorted, TRUE);
+
+	if (sorted != NULL)
+		g_object_unref(sorted);
+	if (model != NULL)
+		g_object_unref(model);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+	g_signal_connect(G_OBJECT(selection), "changed",
+				G_CALLBACK(select_callback), dialog);
+
+	gtk_container_add(GTK_CONTAINER(scrolled), tree);
+
+	gtk_widget_show_all(scrolled);
+
+	bluetooth_client_start_discovery(client);
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+		address = g_object_get_data(G_OBJECT(dialog), "address");
+
+	bluetooth_client_stop_discovery(client);
+
+	g_object_unref(client);
+
+	gtk_widget_destroy(dialog);
+
+	return address;
+}
+
+gchar **show_select_dialog(void)
+{
+	GtkWidget *dialog;
+	gchar **files = NULL;
+
+	dialog = gtk_file_chooser_dialog_new(_("Choose files to send"), NULL,
+				GTK_FILE_CHOOSER_ACTION_OPEN,
+				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+				GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
+
+	gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
+
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+		GSList *list, *filenames;
+		int i;
+
+		filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+
+		files = g_new(gchar *, g_slist_length(filenames) + 1);
+
+		for (list = filenames, i = 0; list; list = list->next, i++)
+			files[i] = list->data;
+		files[i] = NULL;
+
+		g_slist_free(filenames);
+	}
+
+	gtk_widget_destroy(dialog);
+
+	return files;
+}

Added: trunk/common/helper.h
==============================================================================
--- (empty file)
+++ trunk/common/helper.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,27 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+GtkWidget *create_tree(GtkTreeModel *model, gboolean icons);
+
+gchar *show_browse_dialog(void);
+gchar **show_select_dialog(void);

Added: trunk/common/marshal.list
==============================================================================
--- (empty file)
+++ trunk/common/marshal.list	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,5 @@
+VOID:STRING,BOXED
+
+VOID:STRING,STRING
+VOID:STRING,STRING,UINT64
+VOID:UINT64

Added: trunk/common/obex-agent.c
==============================================================================
--- (empty file)
+++ trunk/common/obex-agent.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,310 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "obex-agent.h"
+
+#ifdef DEBUG
+#define DBG(fmt, arg...) printf("%s:%s() " fmt "\n", __FILE__, __FUNCTION__ , ## arg)
+#else
+#define DBG(fmt...)
+#endif
+
+#define OBEX_SERVICE	"org.openobex.client"
+
+#define OBEX_CLIENT_PATH	"/"
+#define OBEX_CLIENT_INTERFACE	"org.openobex.Client"
+#define OBEX_TRANSFER_INTERFACE	"org.openobex.Transfer"
+
+static DBusGConnection *connection = NULL;
+
+#define OBEX_AGENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+					OBEX_TYPE_AGENT, ObexAgentPrivate))
+
+typedef struct _ObexAgentPrivate ObexAgentPrivate;
+
+struct _ObexAgentPrivate {
+	gchar *busname;
+	gchar *path;
+
+	ObexAgentReleaseFunc release_func;
+	gpointer release_data;
+
+	ObexAgentRequestFunc request_func;
+	gpointer request_data;
+
+	ObexAgentProgressFunc progress_func;
+	gpointer progress_data;
+
+	ObexAgentCompleteFunc complete_func;
+	gpointer complete_data;
+};
+
+G_DEFINE_TYPE(ObexAgent, obex_agent, G_TYPE_OBJECT)
+
+static gboolean obex_agent_request(ObexAgent *agent, const char *path,
+						DBusGMethodInvocation *context)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->request_func) {
+		DBusGProxy *proxy;
+
+		proxy = dbus_g_proxy_new_for_name(connection, OBEX_SERVICE,
+						path, OBEX_TRANSFER_INTERFACE);
+
+		result = priv->request_func(context, proxy,
+						priv->request_data);
+
+		g_object_unref(proxy);
+	} else
+		dbus_g_method_return(context, "");
+
+	return TRUE;
+}
+
+static gboolean obex_agent_progress(ObexAgent *agent, const char *path,
+			guint64 transferred, DBusGMethodInvocation *context)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->progress_func) {
+		DBusGProxy *proxy;
+
+		proxy = dbus_g_proxy_new_for_name(connection, OBEX_SERVICE,
+						path, OBEX_TRANSFER_INTERFACE);
+
+		result = priv->progress_func(context, proxy, transferred,
+							priv->progress_data);
+
+		g_object_unref(proxy);
+	} else
+		dbus_g_method_return(context);
+
+	return result;
+}
+
+static gboolean obex_agent_complete(ObexAgent *agent, const char *path,
+						DBusGMethodInvocation *context)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->complete_func) {
+		DBusGProxy *proxy;
+
+		proxy = dbus_g_proxy_new_for_name(connection, OBEX_SERVICE,
+						path, OBEX_TRANSFER_INTERFACE);
+
+		result = priv->complete_func(context, proxy,
+						priv->complete_data);
+
+		g_object_unref(proxy);
+	} else
+		dbus_g_method_return(context);
+
+	return result;
+}
+
+static gboolean obex_agent_release(ObexAgent *agent,
+						DBusGMethodInvocation *context)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+	const char *sender = dbus_g_method_get_sender(context);
+	gboolean result = FALSE;
+
+	DBG("agent %p sender %s", agent, sender);
+
+	if (g_str_equal(sender, priv->busname) == FALSE)
+		return FALSE;
+
+	if (priv->release_func)
+		result = priv->release_func(context, priv->release_data);
+	else
+		dbus_g_method_return(context);
+
+	g_object_unref(agent);
+
+	return result;
+}
+
+#include "obex-agent-glue.h"
+
+static void obex_agent_init(ObexAgent *agent)
+{
+	DBG("agent %p", agent);
+}
+
+static void obex_agent_finalize(GObject *agent)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	g_free(priv->path);
+	g_free(priv->busname);
+
+	G_OBJECT_CLASS(obex_agent_parent_class)->finalize(agent);
+}
+
+static void obex_agent_class_init(ObexAgentClass *klass)
+{
+	GObjectClass *object_class = (GObjectClass *) klass;
+	GError *error = NULL;
+
+	DBG("class %p", klass);
+
+	g_type_class_add_private(klass, sizeof(ObexAgentPrivate));
+
+	object_class->finalize = obex_agent_finalize;
+
+	connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+
+	if (error != NULL) {
+		g_printerr("Connecting to session bus failed: %s\n",
+							error->message);
+		g_error_free(error);
+	}
+
+	dbus_g_object_type_install_info(OBEX_TYPE_AGENT,
+					&dbus_glib_obex_agent_object_info);
+
+	//dbus_g_error_domain_register(AGENT_ERROR, "org.openobex.Error",
+	//						AGENT_ERROR_TYPE);
+}
+
+ObexAgent *obex_agent_new(void)
+{
+	ObexAgent *agent;
+
+	agent = OBEX_AGENT(g_object_new(OBEX_TYPE_AGENT, NULL));
+
+	DBG("agent %p", agent);
+
+	return agent;
+}
+
+gboolean obex_agent_setup(ObexAgent *agent, const char *path)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+	DBusGProxy *proxy;
+	GObject *object;
+
+	DBG("agent %p path %s", agent, path);
+
+	if (priv->path != NULL)
+		return FALSE;
+
+	priv->path = g_strdup(path);
+
+	proxy = dbus_g_proxy_new_for_name_owner(connection, OBEX_SERVICE,
+			OBEX_CLIENT_PATH, OBEX_CLIENT_INTERFACE, NULL);
+
+	g_free(priv->busname);
+
+	if (proxy != NULL) {
+		priv->busname = g_strdup(dbus_g_proxy_get_bus_name(proxy));
+		g_object_unref(proxy);
+	} else
+		priv->busname = NULL;
+
+	object = dbus_g_connection_lookup_g_object(connection, priv->path);
+	if (object != NULL)
+		g_object_unref(object);
+
+	dbus_g_connection_register_g_object(connection,
+						priv->path, G_OBJECT(agent));
+
+	return TRUE;
+}
+
+void obex_agent_set_release_func(ObexAgent *agent,
+				ObexAgentReleaseFunc func, gpointer data)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->release_func = func;
+	priv->release_data = data;
+}
+
+void obex_agent_set_request_func(ObexAgent *agent,
+				ObexAgentRequestFunc func, gpointer data)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->request_func = func;
+	priv->request_data = data;
+}
+
+void obex_agent_set_progress_func(ObexAgent *agent,
+				ObexAgentProgressFunc func, gpointer data)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->progress_func = func;
+	priv->progress_data = data;
+}
+
+void obex_agent_set_complete_func(ObexAgent *agent,
+				ObexAgentCompleteFunc func, gpointer data)
+{
+	ObexAgentPrivate *priv = OBEX_AGENT_GET_PRIVATE(agent);
+
+	DBG("agent %p", agent);
+
+	priv->complete_func = func;
+	priv->complete_data = data;
+}

Added: trunk/common/obex-agent.h
==============================================================================
--- (empty file)
+++ trunk/common/obex-agent.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,82 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __OBEX_AGENT_H
+#define __OBEX_AGENT_H
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define OBEX_TYPE_AGENT (obex_agent_get_type())
+#define OBEX_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+						OBEX_TYPE_AGENT, ObexAgent))
+#define OBEX_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+					OBEX_TYPE_AGENT, ObexAgentClass))
+#define OBEX_IS_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+							OBEX_TYPE_AGENT))
+#define OBEX_IS_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+							OBEX_TYPE_AGENT))
+#define OBEX_GET_AGENT_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+					OBEX_TYPE_AGENT, ObexAgentClass))
+
+typedef struct _ObexAgent ObexAgent;
+typedef struct _ObexAgentClass ObexAgentClass;
+
+struct _ObexAgent {
+	GObject parent;
+};
+
+struct _ObexAgentClass {
+	GObjectClass parent_class;
+};
+
+GType obex_agent_get_type(void);
+
+ObexAgent *obex_agent_new(void);
+
+gboolean obex_agent_setup(ObexAgent *agent, const char *path);
+
+typedef gboolean (*ObexAgentReleaseFunc) (DBusGMethodInvocation *context,
+								gpointer data);
+typedef gboolean (*ObexAgentRequestFunc) (DBusGMethodInvocation *context,
+					DBusGProxy *transfer, gpointer data);
+typedef gboolean (*ObexAgentProgressFunc) (DBusGMethodInvocation *context,
+					DBusGProxy *transfer,
+					guint64 transferred, gpointer data);
+typedef gboolean (*ObexAgentCompleteFunc) (DBusGMethodInvocation *context,
+					DBusGProxy *transfer, gpointer data);
+
+void obex_agent_set_release_func(ObexAgent *agent,
+				ObexAgentReleaseFunc func, gpointer data);
+void obex_agent_set_request_func(ObexAgent *agent,
+				ObexAgentRequestFunc func, gpointer data);
+void obex_agent_set_progress_func(ObexAgent *agent,
+				ObexAgentProgressFunc func, gpointer data);
+void obex_agent_set_complete_func(ObexAgent *agent,
+				ObexAgentCompleteFunc func, gpointer data);
+
+G_END_DECLS
+
+#endif /* __OBEX_AGENT_H */

Added: trunk/common/obex-agent.xml
==============================================================================
--- (empty file)
+++ trunk/common/obex-agent.xml	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/">
+  <interface name="org.openobex.Agent">
+    <method name="Request">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="transfer"/>
+      <arg type="s" name="name" direction="out"/>
+    </method>
+
+    <method name="Progress">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="transfer"/>
+      <arg type="t" name="transferred"/>
+    </method>
+
+    <method name="Complete">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg type="o" name="transfer"/>
+    </method>
+
+    <method name="Release">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    </method>
+  </interface>
+</node>

Added: trunk/common/test-agent.c
==============================================================================
--- (empty file)
+++ trunk/common/test-agent.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,119 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include "bluetooth-agent.h"
+#include "bluetooth-client-glue.h"
+
+static gboolean agent_pincode(DBusGMethodInvocation *context,
+					DBusGProxy *device, gpointer user_data)
+{
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *name;
+
+	device_get_properties(device, &hash, NULL);
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		name = value ? g_value_get_string(value) : NULL;
+	} else {
+		address = NULL;
+		name = NULL;
+	}
+
+	printf("address %s name %s\n", address, name);
+
+	dbus_g_method_return(context, "1234");
+
+	return TRUE;
+}
+
+static GMainLoop *mainloop = NULL;
+
+static void sig_term(int sig)
+{
+	g_main_loop_quit(mainloop);
+}
+
+int main(int argc, char *argv[])
+{
+	struct sigaction sa;
+	BluetoothAgent *agent;
+	DBusGConnection *conn;
+	DBusGProxy *proxy;
+	GError *error = NULL;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_flags = SA_NOCLDSTOP;
+	sa.sa_handler = sig_term;
+	sigaction(SIGTERM, &sa, NULL);
+	sigaction(SIGINT,  &sa, NULL);
+
+	g_type_init();
+
+	mainloop = g_main_loop_new(NULL, FALSE);
+
+	conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+	if (error != NULL) {
+		g_printerr("Connecting to system bus failed: %s\n",
+							error->message);
+		g_main_loop_unref(mainloop);
+		g_error_free(error);
+		exit(1);
+	}
+
+	proxy = dbus_g_proxy_new_for_name(conn, "org.bluez", "/hci0",
+							"org.bluez.Adapter");
+
+	agent = bluetooth_agent_new();
+
+	bluetooth_agent_set_pincode_func(agent, agent_pincode, NULL);
+
+	bluetooth_agent_register(agent, proxy);
+
+	g_main_loop_run(mainloop);
+
+	//bluetooth_agent_unregister(agent);
+
+	g_object_unref(agent);
+
+	g_object_unref(proxy);
+
+	dbus_g_connection_unref(conn);
+
+	g_main_loop_unref(mainloop);
+
+	return 0;
+}

Added: trunk/common/test-client.c
==============================================================================
--- (empty file)
+++ trunk/common/test-client.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,225 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+
+#include "bluetooth-client.h"
+
+static BluetoothClient *client;
+static GtkTreeSelection *selection;
+
+static void delete_callback(GtkWidget *window, GdkEvent *event,
+						gpointer user_data)
+{
+	gtk_widget_destroy(window);
+
+	gtk_main_quit();
+}
+
+static void scan_callback(GtkWidget *button, gpointer user_data)
+{
+	bluetooth_client_start_discovery(client);
+}
+
+static void select_callback(GtkTreeSelection *selection, gpointer user_data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gboolean selected;
+
+	selected = gtk_tree_selection_get_selected(selection, &model, &iter);
+}
+
+static void row_inserted(GtkTreeModel *model, GtkTreePath *path,
+				GtkTreeIter *iter, gpointer user_data)
+{
+	GtkTreeView *tree = user_data;
+
+	gtk_tree_view_expand_all(tree);
+}
+
+static void proxy_to_text(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+		GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
+{
+	DBusGProxy *proxy;
+	gchar *path;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PROXY, &proxy, -1);
+
+	if (proxy == NULL) {
+		g_object_set(cell, "text", "", NULL);
+		return;
+	}
+
+	path = g_path_get_basename(dbus_g_proxy_get_path(proxy));
+
+	g_object_set(cell, "text", path, NULL);
+
+	g_free(path);
+
+	g_object_unref(proxy);
+}
+
+static void type_to_text(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+		GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
+{
+	guint type;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_TYPE, &type, -1);
+
+	g_object_set(cell, "text", bluetooth_type_to_string(type), NULL);
+}
+
+static void create_window(void)
+{
+	GtkWidget *window;
+	GtkWidget *vbox;
+	GtkWidget *toolbar;
+	GtkToolItem *item;
+	GtkWidget *scrolled;
+	GtkWidget *tree;
+	GtkTreeModel *model;
+	GtkTreeModel *sorted;
+	GtkWidget *statusbar;
+
+	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(window), "Test client");
+	gtk_window_set_icon_name(GTK_WINDOW(window), "bluetooth");
+	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+	gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
+	g_signal_connect(G_OBJECT(window), "delete-event",
+					G_CALLBACK(delete_callback), NULL);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(window), vbox);
+
+	toolbar = gtk_toolbar_new();
+	gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, TRUE, 0);
+
+	item = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH);
+	gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
+	g_signal_connect(item, "clicked", G_CALLBACK(scan_callback), NULL);
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_container_add(GTK_CONTAINER(vbox), scrolled);
+
+	tree = gtk_tree_view_new();
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), TRUE);
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
+	gtk_widget_grab_focus(GTK_WIDGET(tree));
+	gtk_container_add(GTK_CONTAINER(scrolled), tree);
+
+	gtk_tree_view_insert_column_with_data_func(GTK_TREE_VIEW(tree), -1,
+					"Proxy", gtk_cell_renderer_text_new(),
+						proxy_to_text, NULL, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Address", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_ADDRESS, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Alias", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_ALIAS, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Name", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_NAME, NULL);
+
+	gtk_tree_view_insert_column_with_data_func(GTK_TREE_VIEW(tree), -1,
+					"Type", gtk_cell_renderer_text_new(),
+						type_to_text, NULL, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Icon", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_ICON, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"RSSI", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_RSSI, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Default", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_DEFAULT, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Paired", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_PAIRED, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Trusted", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_TRUSTED, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Connected", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_CONNECTED, NULL);
+
+	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree), -1,
+					"Discovering", gtk_cell_renderer_text_new(),
+					"text", BLUETOOTH_COLUMN_DISCOVERING, NULL);
+
+	model = bluetooth_client_get_model(client);
+	sorted = gtk_tree_model_sort_new_with_model(model);
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sorted),
+				BLUETOOTH_COLUMN_RSSI, GTK_SORT_DESCENDING);
+	gtk_tree_view_set_model(GTK_TREE_VIEW(tree), sorted);
+	g_signal_connect(G_OBJECT(model), "row-inserted",
+					G_CALLBACK(row_inserted), tree);
+	g_object_unref(sorted);
+	g_object_unref(model);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+	g_signal_connect(G_OBJECT(selection), "changed",
+				G_CALLBACK(select_callback), NULL);
+
+	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree));
+
+	statusbar = gtk_statusbar_new();
+	gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
+
+	gtk_widget_show_all(window);
+}
+
+int main(int argc, char *argv[])
+{
+	gtk_init(&argc, &argv);
+
+	client = bluetooth_client_new();
+
+	create_window();
+
+	gtk_main();
+
+	g_object_unref(client);
+
+	return 0;
+}

Added: trunk/common/test-deviceselection.c
==============================================================================
--- (empty file)
+++ trunk/common/test-deviceselection.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,110 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include "bluetooth-device-selection.h"
+#include "client.h"
+
+static void device_selected_cb(GObject *object,
+			       GParamSpec *spec, gpointer user_data)
+{
+	g_message ("Property \"device-selected\" changed");
+}
+
+static void device_type_filter_selected_cb(GObject *object,
+					   GParamSpec *spec, gpointer user_data)
+{
+	g_message ("Property \"device-type-filter\" changed");
+}
+
+static void device_category_filter_selected_cb(GObject *object,
+				GParamSpec *spec, gpointer user_data)
+{
+	g_message ("Property \"device-category-filter\" changed");
+}
+
+static void select_device_changed(BluetoothDeviceSelection *sel,
+				  gchar *address, gpointer user_data)
+{
+	GtkDialog *dialog = user_data;
+
+	gtk_dialog_set_response_sensitive(dialog,
+				GTK_RESPONSE_ACCEPT, address != NULL);
+}
+
+int main(int argc, char **argv)
+{
+	GtkWidget *dialog, *selector;
+	int response;
+
+	gtk_init(&argc, &argv);
+
+	dialog = gtk_dialog_new_with_buttons("Browse Devices", NULL,
+				GTK_DIALOG_NO_SEPARATOR,
+				GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+				GTK_STOCK_CONNECT, GTK_RESPONSE_ACCEPT, NULL);
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+					GTK_RESPONSE_ACCEPT, FALSE);
+	gtk_window_set_default_size(GTK_WINDOW(dialog), 480, 400);
+
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
+
+	selector = bluetooth_device_selection_new("Select a Device to Setup");
+	gtk_container_set_border_width(GTK_CONTAINER(selector), 5);
+	gtk_widget_show(selector);
+	g_object_set(selector,
+		     "show-search", TRUE,
+		     "device-type-filter", BLUETOOTH_TYPE_PHONE,
+		     NULL);
+	g_signal_connect(selector, "selected-device-changed",
+			 G_CALLBACK(select_device_changed), dialog);
+	g_signal_connect(selector, "notify::device-selected",
+			 G_CALLBACK(device_selected_cb), dialog);
+	g_signal_connect(selector, "notify::device-type-filter",
+			 G_CALLBACK(device_type_filter_selected_cb), dialog);
+	g_signal_connect(selector, "notify::device-category-filter",
+			 G_CALLBACK(device_category_filter_selected_cb), dialog);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), selector);
+	gtk_widget_show(dialog);
+
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+
+	if (response == GTK_RESPONSE_ACCEPT) {
+		char *address;
+
+		g_object_get(selector, "device-selected", &address, NULL);
+		g_message("Selected device is: %s", address);
+		g_free(address);
+	}
+
+	gtk_widget_destroy(dialog);
+
+	return 0;
+}

Added: trunk/configure.ac
==============================================================================
--- (empty file)
+++ trunk/configure.ac	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,96 @@
+AC_PREREQ(2.50)
+AC_INIT()
+
+AM_INIT_AUTOMAKE(bluez-gnome, 1.8)
+AM_CONFIG_HEADER(config.h)
+
+AM_MAINTAINER_MODE
+
+AC_INIT_BLUEZ
+
+AC_LANG_C
+
+AC_PROG_CC
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+GETTEXT_PACKAGE=bluetooth-manager
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
+					[The name of the gettext domain])
+
+AM_GLIB_GNU_GETTEXT
+IT_PROG_INTLTOOL([0.35.0])
+
+AC_PATH_PROG(GCONFTOOL, [gconftool-2])
+AM_GCONF_SOURCE_2
+
+AC_ARG_ENABLE(desktop-update, AC_HELP_STRING([--disable-desktop-update],
+					[Disable desktop database update]))
+if (test "$enable_desktop_update" != no); then
+	AC_PATH_PROG(UPDATE_DESKTOP_DATABASE, [update-desktop-database])
+fi
+AM_CONDITIONAL([DESKTOP_UPDATE], [test -n "$UPDATE_DESKTOP_DATABASE"])
+
+AC_ARG_ENABLE(mime-update, AC_HELP_STRING([--disable-mime-update],
+					[Disable mime database update]))
+if (test "$enable_mime_update" != no); then
+	AC_PATH_PROG(UPDATE_MIME_DATABASE, [update-mime-database])
+fi
+AM_CONDITIONAL([MIME_UPDATE], [test -n "$UPDATE_MIME_DATABASE"])
+
+AC_ARG_ENABLE(icon-update, AC_HELP_STRING([--disable-icon-update],
+					[Disable icon cache update]))
+if (test "$enable_icon_update" != no); then
+	AC_PATH_PROG(UPDATE_ICON_CACHE, [gtk-update-icon-cache])
+fi
+AM_CONDITIONAL([ICON_UPDATE], [test -n "$UPDATE_ICON_CACHE"])
+
+PKG_CHECK_MODULES(DBUS, dbus-glib-1 >= 0.73, dummy=yes,
+				AC_MSG_ERROR(dbus-glib >= 0.73 is required))
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+DBUS_BINDING_TOOL="dbus-binding-tool"
+AC_SUBST(DBUS_BINDING_TOOL)
+
+PKG_CHECK_MODULES(HAL, hal >= 0.5.8, [
+		AC_DEFINE(HAVE_HAL, 1, [Define to 1 if you have HAL support.])
+], dummy=no)
+AC_SUBST(HAL_CFLAGS)
+AC_SUBST(HAL_LIBS)
+
+PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.16, dummy=yes,
+				AC_MSG_ERROR(gio >= 2.16 is required))
+AC_SUBST(GIO_CFLAGS)
+AC_SUBST(GIO_LIBS)
+
+PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.10, dummy=yes,
+				AC_MSG_ERROR(gtk+ >= 2.10 is required))
+AC_SUBST(GTK_CFLAGS)
+AC_SUBST(GTK_LIBS)
+
+GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
+AC_SUBST(GLIB_GENMARSHAL)
+
+PKG_CHECK_MODULES(GCONF, gconf-2.0 >= 2.16, dummy=yes,
+				AC_MSG_ERROR(gconf >= 2.16 is required))
+AC_SUBST(GCONF_CFLAGS)
+AC_SUBST(GCONF_LIBS)
+
+PKG_CHECK_MODULES(NOTIFY, libnotify >= 0.4.3, dummy=yes,
+				AC_MSG_ERROR(libnotify is required))
+AC_SUBST(NOTIFY_CFLAGS)
+AC_SUBST(NOTIFY_LIBS)
+
+dnl PKG_CHECK_MODULES(OPENOBEX, libopenobex-glib >= 1.4, dummy=yes, dummy=no)
+dnl AC_SUBST(OPENOBEX_CFLAGS)
+dnl AC_SUBST(OPENOBEX_LIBS)
+
+AC_ARG_BLUEZ
+
+AC_OUTPUT(Makefile icons/Makefile common/Makefile
+			applet/Makefile properties/Makefile wizard/Makefile
+			sendto/Makefile browse/Makefile analyzer/Makefile
+			explorer/Makefile proximity/Makefile po/Makefile.in)

Added: trunk/icons/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/icons/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,34 @@
+
+icons = hicolor_apps_16x16_bluetooth.png \
+	hicolor_apps_22x22_bluetooth.png \
+	hicolor_apps_24x24_bluetooth.png \
+	hicolor_apps_32x32_bluetooth.png \
+	hicolor_apps_48x48_bluetooth.png \
+	hicolor_apps_scalable_bluetooth.svg
+
+noinst_DATA = hicolor_apps_16x16_bluetooth.svg \
+		hicolor_apps_22x22_bluetooth.svg \
+		hicolor_apps_32x32_bluetooth.svg
+
+EXTRA_DIST = $(icons) $(noinst_DATA)
+
+MAINTAINERCLEANFILES = Makefile.in
+
+install-data-local:
+	for i in $(icons); do \
+		THEME=`echo $$i | cut -d_ -f1`; \
+		CONTEXT=`echo $$i | cut -d_ -f2`; \
+		SIZE=`echo $$i | cut -d_ -f3`; \
+		FILE=`echo $$i | cut -d_ -f4`; \
+		mkdir -p $(DESTDIR)$(datadir)/icons/$$THEME/$$SIZE/$$CONTEXT; \
+		$(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(datadir)/icons/$$THEME/$$SIZE/$$CONTEXT/$$FILE; \
+	done
+
+uninstall-local:
+	for i in $(icons); do \
+		THEME=`echo $$i | cut -d_ -f1`; \
+		CONTEXT=`echo $$i | cut -d_ -f2`; \
+		SIZE=`echo $$i | cut -d_ -f3`; \
+		FILE=`echo $$i | cut -d_ -f4`; \
+		rm -f $(DESTDIR)$(datadir)/icons/$$THEME/$$SIZE/$$CONTEXT/$$FILE; \
+	done

Added: trunk/icons/hicolor_apps_16x16_bluetooth.png
==============================================================================
Binary files (empty file) and trunk/icons/hicolor_apps_16x16_bluetooth.png	Wed Feb 25 14:35:27 2009 differ

Added: trunk/icons/hicolor_apps_16x16_bluetooth.svg
==============================================================================
--- (empty file)
+++ trunk/icons/hicolor_apps_16x16_bluetooth.svg	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,289 @@
+<?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="svg2160"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docname="bluetooth-applet-16.svg"
+   sodipodi:docbase="/home/lapo/Scrivania/bluetooth-icons"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/andreas/Desktop/bluetooth/bluetooth-applet-16.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs2162">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3236">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3238" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop3240" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient8666">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1"
+         offset="0"
+         id="stop8668" />
+      <stop
+         style="stop-color:#3465a4;stop-opacity:1"
+         offset="1"
+         id="stop8670" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3236"
+       id="linearGradient2254"
+       gradientUnits="userSpaceOnUse"
+       x1="5.8374686"
+       y1="3.6999054"
+       x2="10.423636"
+       y2="26.346195"
+       gradientTransform="translate(-70,0)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient8666"
+       id="linearGradient2257"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.9311828,-70,6.66667e-2)"
+       x1="4.2876263"
+       y1="0.62152362"
+       x2="10.618814"
+       y2="8" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="-31.207866"
+     inkscape:cy="23.321019"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     width="16px"
+     height="16px"
+     inkscape:window-width="872"
+     inkscape:window-height="946"
+     inkscape:window-x="418"
+     inkscape:window-y="49"
+     gridspacingx="0.5px"
+     gridspacingy="0.5px"
+     gridempspacing="2"
+     inkscape:grid-points="true"
+     inkscape:showpageshadow="false"
+     showborder="true" />
+  <metadata
+     id="metadata2165">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/"; />
+      </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
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       id="g2254"
+       transform="translate(70,0)">
+      <rect
+         ry="4.6423116"
+         rx="4.6423116"
+         y="0.53225815"
+         x="-67.5"
+         height="13.967742"
+         width="11"
+         id="rect2168"
+         style="color:#000000;fill:url(#linearGradient2257);fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:1.10000002;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" />
+      <rect
+         ry="3.6786165"
+         rx="3.6786165"
+         y="1.5"
+         x="-66.5"
+         height="12"
+         width="8.999999"
+         id="rect3161"
+         style="opacity:0.45945943;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2254);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" />
+      <g
+         id="g2259">
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M -64.500003,9.500005 L -59.500003,5.5000049 L -62.500003,2.5000049 L -62.500003,12.500005 L -59.500003,9.500005 L -64.500003,5.5000049"
+           id="path2874" />
+        <g
+           id="g2794"
+           transform="translate(-56.000003,5e-6)">
+          <rect
+             ry="5.171875"
+             rx="0"
+             y="2"
+             x="-7"
+             height="11"
+             width="1"
+             id="rect2762"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="11"
+             x="-6"
+             height="1"
+             width="1"
+             id="rect2764"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="10"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2766"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="9"
+             x="-4"
+             height="1"
+             width="1"
+             id="rect2768"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="8"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2770"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="7"
+             x="-6"
+             height="1"
+             width="1"
+             id="rect2772"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="6"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2774"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="5"
+             x="-4"
+             height="1"
+             width="1"
+             id="rect2776"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="4"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2778"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="3"
+             x="-6"
+             height="1"
+             width="1"
+             id="rect2780"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="6"
+             x="-8"
+             height="1"
+             width="1"
+             id="rect2782"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="5"
+             x="-9"
+             height="1"
+             width="1"
+             id="rect2784"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="8"
+             x="-8"
+             height="1"
+             width="1"
+             id="rect2788"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="9"
+             x="-9"
+             height="1"
+             width="1"
+             id="rect2790"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>

Added: trunk/icons/hicolor_apps_22x22_bluetooth.png
==============================================================================
Binary files (empty file) and trunk/icons/hicolor_apps_22x22_bluetooth.png	Wed Feb 25 14:35:27 2009 differ

Added: trunk/icons/hicolor_apps_22x22_bluetooth.svg
==============================================================================
--- (empty file)
+++ trunk/icons/hicolor_apps_22x22_bluetooth.svg	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,355 @@
+<?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="svg2160"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docname="bluetooth-applet-22.svg"
+   sodipodi:docbase="/home/lapo/Scrivania/bluetooth-icons"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/andreas/Desktop/bluetooth/bluetooth-applet-16.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs2162">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3250">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1;"
+         offset="0"
+         id="stop3252" />
+      <stop
+         style="stop-color:#729fcf;stop-opacity:0;"
+         offset="1"
+         id="stop3254" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4140"
+       inkscape:collect="always">
+      <stop
+         id="stop4142"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1" />
+      <stop
+         id="stop4144"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient7697"
+       inkscape:collect="always">
+      <stop
+         id="stop7699"
+         offset="0"
+         style="stop-color:#729fcf;stop-opacity:1" />
+      <stop
+         id="stop7701"
+         offset="1"
+         style="stop-color:#3465a4;stop-opacity:1" />
+    </linearGradient>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8963782,0,6.4445216e-6,0.1737589,2.7974976,36.922651)"
+       r="17.625"
+       fy="45.047184"
+       fx="27"
+       cy="45.047184"
+       cx="27"
+       id="radialGradient8698"
+       xlink:href="#linearGradient8692"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient8692"
+       inkscape:collect="always">
+      <stop
+         id="stop8694"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop8696"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3250"
+       id="linearGradient3256"
+       x1="10.5"
+       y1="18.999071"
+       x2="10.5"
+       y2="14.782861"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.9444444,-40,1.1111127)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4140"
+       id="linearGradient3260"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-39.999998,3.1544124e-7)"
+       x1="8.375"
+       y1="8.2352943"
+       x2="16.038452"
+       y2="19.513702" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient7697"
+       id="radialGradient3277"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.6429197,-0.1187455,9.5333642e-2,1.1838133,-45.417002,-1.3243634)"
+       cx="7.5811033"
+       cy="6.2417436"
+       fx="7.5811033"
+       fy="6.2417436"
+       r="6.9808745" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="-31.207866"
+     inkscape:cy="23.321019"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     width="22px"
+     height="22px"
+     inkscape:window-width="872"
+     inkscape:window-height="946"
+     inkscape:window-x="418"
+     inkscape:window-y="49"
+     gridspacingx="0.5px"
+     gridspacingy="0.5px"
+     gridempspacing="2"
+     inkscape:grid-points="true"
+     inkscape:showpageshadow="false"
+     showborder="false" />
+  <metadata
+     id="metadata2165">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/"; />
+      </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
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       id="g2275"
+       transform="translate(40,0)">
+      <path
+         transform="matrix(0.5390071,0,0,0.8163267,-43.553192,-16.9796)"
+         d="M 44.625 44.6875 A 17.625 3.0625 0 1 1  9.375,44.6875 A 17.625 3.0625 0 1 1  44.625 44.6875 z"
+         sodipodi:ry="3.0625"
+         sodipodi:rx="17.625"
+         sodipodi:cy="44.6875"
+         sodipodi:cx="27"
+         id="path8690"
+         style="opacity:0.49034753;fill:url(#radialGradient8698);fill-opacity:1;stroke:none;stroke-width:0.80671531;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <rect
+         ry="5.7845125"
+         rx="6.1058745"
+         y="2.5"
+         x="-35.5"
+         height="18"
+         width="12.961749"
+         id="rect3275"
+         style="color:#000000;fill:url(#radialGradient3277);fill-opacity:1;fill-rule:evenodd;stroke:#204a87;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" />
+      <rect
+         style="opacity:1;color:#000000;fill:url(#linearGradient3256);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"
+         id="rect3248"
+         width="11.961753"
+         height="17"
+         x="-35"
+         y="3"
+         rx="5.597013"
+         ry="5.2860675" />
+      <rect
+         ry="4.9411759"
+         rx="5.25"
+         y="3.5"
+         x="-34.5"
+         height="15.999999"
+         width="11"
+         id="rect2250"
+         style="opacity:0.46718146;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3260);stroke-width:0.99999994;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" />
+      <g
+         transform="translate(33,4)"
+         id="g2277">
+        <path
+           style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+           d="M -64.500003,9.500005 L -59.500003,5.5000049 L -62.500003,2.5000049 L -62.500003,12.500005 L -59.500003,9.500005 L -64.500003,5.5000049"
+           id="path2279" />
+        <g
+           id="g2281"
+           transform="translate(-56.000003,5e-6)">
+          <rect
+             ry="5.171875"
+             rx="0"
+             y="2"
+             x="-7"
+             height="11"
+             width="1"
+             id="rect2283"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="11"
+             x="-6"
+             height="1"
+             width="1"
+             id="rect2285"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="10"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2287"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="9"
+             x="-4"
+             height="1"
+             width="1"
+             id="rect2289"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="8"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2291"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="7"
+             x="-6"
+             height="1"
+             width="1"
+             id="rect2293"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="6"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2295"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="5"
+             x="-4"
+             height="1"
+             width="1"
+             id="rect2297"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="4"
+             x="-5"
+             height="1"
+             width="1"
+             id="rect2299"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="3"
+             x="-6"
+             height="1"
+             width="1"
+             id="rect2301"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="6"
+             x="-8"
+             height="1"
+             width="1"
+             id="rect2303"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="5"
+             x="-9"
+             height="1"
+             width="1"
+             id="rect2305"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="8"
+             x="-8"
+             height="1"
+             width="1"
+             id="rect2307"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+          <rect
+             ry="0.47017044"
+             rx="0"
+             y="9"
+             x="-9"
+             height="1"
+             width="1"
+             id="rect2309"
+             style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>

Added: trunk/icons/hicolor_apps_24x24_bluetooth.png
==============================================================================
Binary files (empty file) and trunk/icons/hicolor_apps_24x24_bluetooth.png	Wed Feb 25 14:35:27 2009 differ

Added: trunk/icons/hicolor_apps_32x32_bluetooth.png
==============================================================================
Binary files (empty file) and trunk/icons/hicolor_apps_32x32_bluetooth.png	Wed Feb 25 14:35:27 2009 differ

Added: trunk/icons/hicolor_apps_32x32_bluetooth.svg
==============================================================================
--- (empty file)
+++ trunk/icons/hicolor_apps_32x32_bluetooth.svg	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,234 @@
+<?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="svg2160"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docname="bluetooth-applet-32.svg"
+   sodipodi:docbase="/home/lapo/Scrivania/bluetooth-icons"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/andreas/Desktop/bluetooth/bluetooth-applet-16.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs2162">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3393">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1;"
+         offset="0"
+         id="stop3395" />
+      <stop
+         style="stop-color:#729fcf;stop-opacity:0;"
+         offset="1"
+         id="stop3397" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3383">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1"
+         offset="0"
+         id="stop3385" />
+      <stop
+         style="stop-color:#3465a4;stop-opacity:1"
+         offset="1"
+         id="stop3387" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4140"
+       inkscape:collect="always">
+      <stop
+         id="stop4142"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1" />
+      <stop
+         id="stop4144"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient8692"
+       inkscape:collect="always">
+      <stop
+         id="stop8694"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop8696"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4140"
+       id="linearGradient3319"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.8181819,0,0,1.74897,-4.5000002,-4.6049144)"
+       x1="8.2285843"
+       y1="7.8516045"
+       x2="19.823189"
+       y2="24.029535" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3393"
+       id="linearGradient3322"
+       gradientUnits="userSpaceOnUse"
+       x1="23.25"
+       y1="48.216961"
+       x2="23.25"
+       y2="32.278145"
+       gradientTransform="matrix(0.6562501,0,0,0.6590909,-0.2500023,0.3409108)" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3383"
+       id="radialGradient3325"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.9810874,-0.2760916,0.1127398,0.8086634,-19.026436,-1.3297631)"
+       cx="10.930984"
+       cy="8.4716578"
+       fx="10.930984"
+       fy="8.4716578"
+       r="17.000002" />
+    <filter
+       inkscape:collect="always"
+       id="filter3337">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.30690609"
+         id="feGaussianBlur3339" />
+    </filter>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient8692"
+       id="radialGradient3343"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8963782,0,6.4445216e-6,0.1737589,2.7974976,36.922651)"
+       cx="27"
+       cy="45.047184"
+       fx="27"
+       fy="45.047184"
+       r="17.625" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="-31.207866"
+     inkscape:cy="23.321019"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     width="32px"
+     height="32px"
+     inkscape:window-width="872"
+     inkscape:window-height="946"
+     inkscape:window-x="418"
+     inkscape:window-y="49"
+     gridspacingx="0.5px"
+     gridspacingy="0.5px"
+     gridempspacing="2"
+     inkscape:grid-points="true"
+     inkscape:showpageshadow="false"
+     showborder="true" />
+  <metadata
+     id="metadata2165">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/"; />
+      </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
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       id="g2298">
+      <path
+         sodipodi:type="arc"
+         style="opacity:0.46330273;fill:url(#radialGradient3343);fill-opacity:1;stroke:none;stroke-width:0.80671531;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         id="path3341"
+         sodipodi:cx="27"
+         sodipodi:cy="44.6875"
+         sodipodi:rx="17.625"
+         sodipodi:ry="3.0625"
+         d="M 44.625 44.6875 A 17.625 3.0625 0 1 1  9.375,44.6875 A 17.625 3.0625 0 1 1  44.625 44.6875 z"
+         transform="matrix(0.7298953,0,0,0.9387754,-4.2071741,-12.576525)" />
+      <rect
+         ry="10.987156"
+         rx="10.98918"
+         y="0.51377058"
+         x="4.5108204"
+         height="29.97246"
+         width="21.978359"
+         id="rect3298"
+         style="color:#000000;fill:url(#radialGradient3325);fill-opacity:1;fill-rule:evenodd;stroke:#204a87;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" />
+      <rect
+         ry="10.630676"
+         rx="10.5"
+         y="1.0000006"
+         x="5"
+         height="29"
+         width="21"
+         id="rect3300"
+         style="color:#000000;fill:url(#linearGradient3322);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03091633;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" />
+      <rect
+         ry="10.084088"
+         rx="10"
+         y="1.5164809"
+         x="5.4999995"
+         height="27.983519"
+         width="20"
+         id="rect3302"
+         style="opacity:0.39768342;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3319);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" />
+      <path
+         sodipodi:nodetypes="cccccc"
+         id="path3327"
+         d="M 9.0929649,21.216579 L 21,10.695345 L 15.0538,4.835568 L 15.036005,24.164434 L 20.995823,18.349673 L 9.0005646,9.571871"
+         style="fill:none;fill-rule:evenodd;stroke:#3465a4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3337)" />
+      <path
+         sodipodi:nodetypes="cccccc"
+         id="path3306"
+         d="M 9.0929649,22.216579 L 21,11.695345 L 15.0538,5.835568 L 15.036005,25.164434 L 20.995823,19.349673 L 9.0005646,10.571871"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>

Added: trunk/icons/hicolor_apps_48x48_bluetooth.png
==============================================================================
Binary files (empty file) and trunk/icons/hicolor_apps_48x48_bluetooth.png	Wed Feb 25 14:35:27 2009 differ

Added: trunk/icons/hicolor_apps_scalable_bluetooth.svg
==============================================================================
--- (empty file)
+++ trunk/icons/hicolor_apps_scalable_bluetooth.svg	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,234 @@
+<?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="svg2160"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docname="bluetooth-applet-48.svg"
+   sodipodi:docbase="/home/lapo/Scrivania/bluetooth-icons"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="/home/andreas/Desktop/bluetooth/bluetooth-applet-16.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs2162">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3393">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1;"
+         offset="0"
+         id="stop3395" />
+      <stop
+         style="stop-color:#729fcf;stop-opacity:0;"
+         offset="1"
+         id="stop3397" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3383">
+      <stop
+         style="stop-color:#729fcf;stop-opacity:1"
+         offset="0"
+         id="stop3385" />
+      <stop
+         style="stop-color:#3465a4;stop-opacity:1"
+         offset="1"
+         id="stop3387" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4140"
+       inkscape:collect="always">
+      <stop
+         id="stop4142"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1" />
+      <stop
+         id="stop4144"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient8692"
+       inkscape:collect="always">
+      <stop
+         id="stop8694"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop8696"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <filter
+       inkscape:collect="always"
+       id="filter3274">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.511583"
+         id="feGaussianBlur3276" />
+    </filter>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient8692"
+       id="radialGradient3286"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8963782,0,6.4445216e-6,0.1737589,2.7974976,36.922651)"
+       cx="27"
+       cy="45.047184"
+       fx="27"
+       fy="45.047184"
+       r="17.625" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3383"
+       id="radialGradient3288"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.9717705,-0.4142331,0.1691176,1.2132755,-27.792082,-2.2504815)"
+       cx="10.930984"
+       cy="8.4716578"
+       fx="10.930984"
+       fy="8.4716578"
+       r="17.000002" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3393"
+       id="linearGradient3290"
+       gradientUnits="userSpaceOnUse"
+       x1="23.25"
+       y1="48.216961"
+       x2="23.25"
+       y2="32.278145" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4140"
+       id="linearGradient3292"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.8151854,0,0,2.6854398,-6.9670375,-7.8825588)"
+       x1="8.2285843"
+       y1="7.8516045"
+       x2="19.823189"
+       y2="24.029535" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="-31.207866"
+     inkscape:cy="23.321019"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     width="48px"
+     height="48px"
+     inkscape:window-width="872"
+     inkscape:window-height="946"
+     inkscape:window-x="418"
+     inkscape:window-y="49"
+     gridspacingx="0.5px"
+     gridspacingy="0.5px"
+     gridempspacing="2"
+     inkscape:grid-points="true"
+     inkscape:showpageshadow="false"
+     showborder="false" />
+  <metadata
+     id="metadata2165">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/"; />
+      </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
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       id="g3278"
+       transform="translate(0,1)">
+      <path
+         transform="matrix(1.0212766,0,0,1.2040816,-3.5744683,-10.244898)"
+         d="M 44.625 44.6875 A 17.625 3.0625 0 1 1  9.375,44.6875 A 17.625 3.0625 0 1 1  44.625 44.6875 z"
+         sodipodi:ry="3.0625"
+         sodipodi:rx="17.625"
+         sodipodi:cy="44.6875"
+         sodipodi:cx="27"
+         id="path3361"
+         style="opacity:0.46330273;fill:url(#radialGradient3286);fill-opacity:1;stroke:none;stroke-width:0.80671531;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+         sodipodi:type="arc" />
+      <rect
+         ry="16.484543"
+         rx="16.484543"
+         y="0.51545823"
+         x="7.5154586"
+         height="44.969082"
+         width="32.969086"
+         id="rect3363"
+         style="color:#000000;fill:url(#radialGradient3288);fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:1.03091633;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" />
+      <rect
+         ry="16.129303"
+         rx="15.999998"
+         y="0.99999833"
+         x="8.0000029"
+         height="44"
+         width="31.999996"
+         id="rect3391"
+         style="color:#000000;fill:url(#linearGradient3290);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.03091633;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" />
+      <rect
+         ry="15.48352"
+         rx="15.48352"
+         y="1.516481"
+         x="8.5164804"
+         height="42.967037"
+         width="30.967039"
+         id="rect3365"
+         style="opacity:0.39768342;color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3292);stroke-width:1.03296196;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" />
+      <path
+         sodipodi:nodetypes="cccccc"
+         id="path3401"
+         d="M 14.638608,32.077196 L 32.499999,16.291697 L 23.58028,7.4999996 L 23.553586,36.5 L 32.493732,27.775843 L 14.500001,14.606097"
+         style="fill:none;fill-rule:evenodd;stroke:#3465a4;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3274)" />
+      <path
+         sodipodi:nodetypes="cccccc"
+         id="path3367"
+         d="M 14.638608,33.077196 L 32.499999,17.291697 L 23.58028,8.4999996 L 23.553586,37.5 L 32.493732,28.775843 L 14.500001,15.606097"
+         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>

Modified: trunk/po/LINGUAS
==============================================================================
--- trunk/po/LINGUAS	(original)
+++ trunk/po/LINGUAS	Wed Feb 25 14:35:27 2009
@@ -1,45 +1,47 @@
+# please keep this list sorted alphabetically
 ar
 be
-be latin
 bg
+bs
 ca
 cs
 da
 de
-dz
 el
-en_CA
+en_AU
 en_GB
+eo
 es
-et
 eu
 fi
 fr
-gl
-gu
+fy
 he
+hr
 hu
+id
 it
 ja
+ka
+ko
+ku
 lv
-mk
+mn
+ms
+mus
 nb
-ne
 nl
 oc
-pa
 pl
 pt
 pt_BR
 ro
 ru
-rw
 sk
 sl
-sq
+sr
 sv
+tr
 uk
-vi
 zh_CN
-zh_HK
 zh_TW

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Wed Feb 25 14:35:27 2009
@@ -1,8 +1,20 @@
-# List of source files containing translatable strings.
-# Please keep this file sorted alphabetically.
-[encoding: UTF-8]
-src/chooser.gob
-src/gconftest.c
-src/gnomebt-chooser-test.c
-src/gnomebt-controller-pymodule.c
-src/gnomebt-controller-test.c
+common/bluetooth-client.c
+common/helper.c
+applet/main.c
+applet/notify.c
+applet/agent.c
+applet/bluetooth-applet.desktop.in
+properties/main.c
+properties/general.c
+properties/adapter.c
+properties/killswitch.c
+properties/bluetooth-properties.desktop.in
+properties/bluetooth-manager.schemas.in
+wizard/main.c
+sendto/main.c
+browse/main.c
+analyzer/main.c
+analyzer/dialog.c
+analyzer/tracer.c
+analyzer/bluetooth-analyzer.desktop.in
+analyzer/bluetooth-manager.xml.in

Modified: trunk/po/POTFILES.skip
==============================================================================
--- trunk/po/POTFILES.skip	(original)
+++ trunk/po/POTFILES.skip	Wed Feb 25 14:35:27 2009
@@ -1,3 +1,2 @@
-src/gnomebt-chooser.c
-src/gnomebt-fileactiondialog.c
-src/gnomebt-permissiondialog.c
+wizard/main.c
+proximity/main.c

Added: trunk/properties/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/properties/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,46 @@
+
+bin_PROGRAMS = bluetooth-properties
+
+bluetooth_properties_SOURCES = main.c general.h general.c \
+				killswitch.h killswitch.c adapter.h adapter.c
+
+bluetooth_properties_LDADD = $(top_builddir)/common/libcommon.a \
+				@GCONF_LIBS@ @GTK_LIBS@ @HAL_LIBS@ @DBUS_LIBS@
+
+AM_CFLAGS = @DBUS_CFLAGS@ @HAL_CFLAGS@ @GTK_CFLAGS@ @GCONF_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common
+
+man_MANS = bluetooth-properties.1
+
+desktopdir = $(datadir)/applications
+
+desktop_in_files = bluetooth-properties.desktop.in
+
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+
+ INTLTOOL_DESKTOP_RULE@
+
+schemadir = $(GCONF_SCHEMA_FILE_DIR)
+
+schema_in_files = bluetooth-manager.schemas.in
+
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+ INTLTOOL_SCHEMAS_RULE@
+
+CLEANFILES = $(desktop_DATA) $(schema_DATA)
+
+EXTRA_DIST = $(man_MANS) $(desktop_in_files) $(schema_in_files)
+
+MAINTAINERCLEANFILES = Makefile.in
+
+if GCONF_SCHEMAS_INSTALL
+install-data-local:
+	-GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) \
+	$(GCONFTOOL) --makefile-install-rule $(schema_DATA)
+
+uninstall-local:
+	-GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) \
+	$(GCONFTOOL) --makefile-uninstall-rule $(schema_DATA)
+endif

Added: trunk/properties/adapter.c
==============================================================================
--- (empty file)
+++ trunk/properties/adapter.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,930 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <bluetooth-client.h>
+#include <helper.h>
+
+#include "general.h"
+
+static BluetoothClient *client;
+static GtkTreeModel *adapter_model;
+
+struct adapter_data {
+	DBusGProxy *proxy;
+	GtkWidget *notebook;
+	GtkWidget *child;
+	GtkWidget *button_dummy;
+	GtkWidget *button_hidden;
+	GtkWidget *button_always;
+	GtkWidget *button_timeout;
+	GtkWidget *timeout_scale;
+	GtkWidget *entry;
+	GtkWidget *button_delete;
+	GtkWidget *button_trusted;
+	GtkWidget *button_disconnect;
+	GtkTreeSelection *selection;
+	GtkTreeRowReference *reference;
+	guint signal_hidden;
+	guint signal_always;
+	guint signal_timeout;
+	guint signal_scale;
+	gboolean powered;
+	gboolean discoverable;
+	guint timeout_value;
+	guint timeout_remain;
+	int name_changed;
+};
+
+static void block_signals(struct adapter_data *adapter)
+{
+	g_signal_handler_block(adapter->button_hidden,
+						adapter->signal_hidden);
+	g_signal_handler_block(adapter->button_always,
+						adapter->signal_always);
+	g_signal_handler_block(adapter->button_timeout,
+						adapter->signal_timeout);
+	g_signal_handler_block(adapter->timeout_scale,
+						adapter->signal_scale);
+}
+
+static void unblock_signals(struct adapter_data *adapter)
+{
+	g_signal_handler_unblock(adapter->button_hidden,
+						adapter->signal_hidden);
+	g_signal_handler_unblock(adapter->button_always,
+						adapter->signal_always);
+	g_signal_handler_unblock(adapter->button_timeout,
+						adapter->signal_timeout);
+	g_signal_handler_unblock(adapter->timeout_scale,
+						adapter->signal_scale);
+}
+
+static void update_tab_label(GtkNotebook *notebook,
+					GtkWidget *child, const char *name)
+{
+	GtkWidget *label;
+
+	gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(notebook), child,
+				name && name[0] != '\0' ? name : _("Adapter"));
+
+	label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), child);
+	gtk_label_set_max_width_chars(GTK_LABEL(label), 20);
+	gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
+}
+
+static void mode_callback(GtkWidget *button, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	GValue discoverable = { 0 };
+	GValue timeout = { 0 };
+
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == FALSE)
+		return;
+
+	g_value_init(&discoverable, G_TYPE_BOOLEAN);
+	g_value_init(&timeout, G_TYPE_UINT);
+
+	if (button == adapter->button_hidden) {
+		g_value_set_boolean(&discoverable, FALSE);
+		g_value_set_uint(&timeout, 0);
+	} else if (button == adapter->button_always) {
+		g_value_set_boolean(&discoverable, TRUE);
+		g_value_set_uint(&timeout, 0);
+	} else if (button == adapter->button_timeout) {
+		g_value_set_boolean(&discoverable, TRUE);
+		if (adapter->timeout_value > 0)
+			g_value_set_uint(&timeout, adapter->timeout_value);
+		else
+			g_value_set_uint(&timeout, 3 * 60);
+	} else
+		return;
+
+	dbus_g_proxy_call(adapter->proxy, "SetProperty", NULL,
+					G_TYPE_STRING, "Discoverable",
+					G_TYPE_VALUE, &discoverable,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+	dbus_g_proxy_call(adapter->proxy, "SetProperty", NULL,
+					G_TYPE_STRING, "DiscoverableTimeout",
+					G_TYPE_VALUE, &timeout,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+	g_value_unset(&discoverable);
+	g_value_unset(&timeout);
+}
+
+static void scale_callback(GtkWidget *scale, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	GValue timeout = { 0 };
+	gdouble value;
+
+	value = gtk_range_get_value(GTK_RANGE(scale));
+	adapter->timeout_value = value * 60;
+
+	g_value_init(&timeout, G_TYPE_UINT);
+	g_value_set_uint(&timeout, adapter->timeout_value);
+
+	dbus_g_proxy_call(adapter->proxy, "SetProperty", NULL,
+					G_TYPE_STRING, "DiscoverableTimeout",
+					G_TYPE_VALUE, &timeout,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+	g_value_unset(&timeout);
+}
+
+static gchar *format_callback(GtkWidget *scale,
+				gdouble value, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+
+	if (value == 0) {
+		if (adapter->discoverable == TRUE)
+			return g_strdup(_("always"));
+		else
+			return g_strdup(_("hidden"));
+	}
+
+	return g_strdup_printf(ngettext("%'g minute",
+					"%'g minutes", value), value);
+}
+
+static void name_callback(GtkWidget *editable, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+
+	adapter->name_changed = 1;
+}
+
+static gboolean focus_callback(GtkWidget *editable,
+				GdkEventFocus *event, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	GValue value = { 0 };
+	const gchar *text;
+
+	if (!adapter->name_changed)
+		return FALSE;
+
+	g_value_init(&value, G_TYPE_STRING);
+
+	text = gtk_entry_get_text(GTK_ENTRY(editable));
+
+	g_value_set_string(&value, text);
+
+	dbus_g_proxy_call(adapter->proxy, "SetProperty", NULL,
+			G_TYPE_STRING, "Name",
+			G_TYPE_VALUE, &value, G_TYPE_INVALID, G_TYPE_INVALID);
+
+	g_value_unset(&value);
+
+	adapter->name_changed = 0;
+
+	return FALSE;
+}
+
+static void update_buttons(struct adapter_data *adapter, gboolean bonded,
+					gboolean trusted, gboolean connected)
+{
+	gtk_widget_set_sensitive(adapter->button_delete, bonded);
+	gtk_widget_set_sensitive(adapter->button_disconnect, connected);
+}
+
+static void select_callback(GtkTreeSelection *selection, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	DBusGProxy *proxy;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gboolean selected;
+	gboolean paired = FALSE, trusted = FALSE, connected = FALSE;
+
+	selected = gtk_tree_selection_get_selected(selection, &model, &iter);
+
+	if (selected == TRUE) {
+		gtk_tree_model_get(model, &iter,
+				BLUETOOTH_COLUMN_PROXY, &proxy,
+				BLUETOOTH_COLUMN_PAIRED, &paired,
+				BLUETOOTH_COLUMN_TRUSTED, &trusted,
+				BLUETOOTH_COLUMN_CONNECTED, &connected, -1);
+
+		if (proxy != NULL) {
+			paired = TRUE;
+			g_object_unref(proxy);
+		}
+	}
+
+	update_buttons(adapter, paired, trusted, connected);
+
+	if (selected == TRUE) {
+		gtk_widget_show(adapter->button_disconnect);
+		gtk_widget_show(adapter->button_trusted);
+		gtk_widget_show(adapter->button_delete);
+	} else {
+		gtk_widget_hide(adapter->button_disconnect);
+		gtk_widget_hide(adapter->button_trusted);
+		gtk_widget_hide(adapter->button_delete);
+	}
+}
+
+static void row_callback(GtkTreeModel *model, GtkTreePath  *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	gboolean bonded = FALSE, trusted = FALSE, connected = FALSE;
+
+	if (gtk_tree_selection_iter_is_selected(adapter->selection,
+							iter) == FALSE)
+		return;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PAIRED, &bonded,
+					BLUETOOTH_COLUMN_TRUSTED, &trusted,
+					BLUETOOTH_COLUMN_CONNECTED, &connected, -1);
+
+	update_buttons(adapter, bonded, trusted, connected);
+}
+
+static void wizard_callback(GtkWidget *button, gpointer user_data)
+{
+	const char *command = "bluetooth-wizard";
+
+	if (!g_spawn_command_line_async(command, NULL))
+		g_printerr("Couldn't execute command: %s\n", command);
+}
+
+static gboolean show_confirm_dialog(void)
+{
+	GtkWidget *dialog;
+	gint response;
+	gchar *text;
+
+	text = g_strdup_printf("<big><b>%s</b></big>\n\n%s",
+				_("Remove from list of known devices?"),
+				_("If you delete the device, you have to "
+					"set it up again before next use."));
+	dialog = gtk_message_dialog_new_with_markup(NULL, GTK_DIALOG_MODAL,
+				GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, text);
+	g_free(text);
+
+	gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL,
+							GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_DELETE,
+							GTK_RESPONSE_ACCEPT);
+
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+
+	gtk_widget_destroy(dialog);
+
+	if (response == GTK_RESPONSE_ACCEPT)
+		return TRUE;
+
+	return FALSE;
+}
+
+static void delete_callback(GtkWidget *button, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	DBusGProxy *device;
+	const char *path;
+
+	if (gtk_tree_selection_get_selected(adapter->selection,
+						&model, &iter) == FALSE)
+		return;
+
+	gtk_tree_model_get(model, &iter, BLUETOOTH_COLUMN_PROXY, &device, -1);
+
+	if (device == NULL)
+		return;
+
+	path = dbus_g_proxy_get_path(device);
+
+	if (show_confirm_dialog() == TRUE) {
+		dbus_g_proxy_call(adapter->proxy, "RemoveDevice", NULL,
+					DBUS_TYPE_G_OBJECT_PATH, path,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+	}
+
+	g_object_unref(device);
+}
+
+static void trusted_callback(GtkWidget *button, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	GValue value = { 0 };
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	DBusGProxy *device;
+	gboolean trusted = FALSE;
+
+	if (gtk_tree_selection_get_selected(adapter->selection,
+						&model, &iter) == FALSE)
+		return;
+
+	gtk_tree_model_get(model, &iter, BLUETOOTH_COLUMN_PROXY, &device,
+					BLUETOOTH_COLUMN_TRUSTED, &trusted, -1);
+
+	if (device == NULL)
+		return;
+
+	g_value_init(&value, G_TYPE_BOOLEAN);
+
+	g_value_set_boolean(&value, !trusted);
+
+	dbus_g_proxy_call(device, "SetProperty", NULL,
+			G_TYPE_STRING, "Trusted",
+			G_TYPE_VALUE, &value, G_TYPE_INVALID, G_TYPE_INVALID);
+
+	g_value_unset(&value);
+
+	g_object_unref(device);
+}
+
+static void disconnect_callback(GtkWidget *button, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	DBusGProxy *device;
+
+	if (gtk_tree_selection_get_selected(adapter->selection,
+						&model, &iter) == FALSE)
+		return;
+
+	gtk_tree_model_get(model, &iter, BLUETOOTH_COLUMN_PROXY, &device, -1);
+
+	if (device == NULL)
+		return;
+
+	dbus_g_proxy_call(device, "Disconnect", NULL,
+					G_TYPE_INVALID, G_TYPE_INVALID);
+
+	g_object_unref(device);
+
+	gtk_widget_set_sensitive(button, FALSE);
+}
+
+static gboolean device_filter(GtkTreeModel *model,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	DBusGProxy *proxy;
+	gboolean active;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PROXY, &proxy,
+					BLUETOOTH_COLUMN_PAIRED, &active, -1);
+
+	if (proxy != NULL) {
+		active = TRUE;
+		g_object_unref(proxy);
+	}
+
+	return active;
+}
+
+static gboolean counter_callback(gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+	gdouble value;
+
+	if (adapter->timeout_remain == 0)
+		return FALSE;
+
+	else if (adapter->timeout_remain < 60)
+		value = 1;
+	else if (adapter->timeout_remain > 60 * 30)
+		value = 30;
+	else
+		value = adapter->timeout_remain / 60;
+
+	adapter->timeout_remain--;
+
+	gtk_range_set_fill_level(GTK_RANGE(adapter->timeout_scale), value);
+
+	return TRUE;
+}
+
+static void create_adapter(struct adapter_data *adapter)
+{
+	GHashTable *hash = NULL;
+	GValue *value;
+	const gchar *address, *name;
+	gboolean powered, discoverable;
+	guint timeout;
+
+	GtkWidget *mainbox;
+	GtkWidget *vbox;
+	GtkWidget *label;
+	GtkWidget *image;
+	GtkWidget *button;
+	GtkWidget *scale;
+	GtkWidget *entry;
+	GtkWidget *buttonbox;
+	GtkWidget *scrolled;
+	GtkWidget *tree;
+	GtkTreeModel *model;
+	GtkTreeSelection *selection;
+	GSList *group = NULL;
+	gdouble scale_value;
+
+	dbus_g_proxy_call(adapter->proxy, "GetProperties", NULL, G_TYPE_INVALID,
+				dbus_g_type_get_map("GHashTable",
+						G_TYPE_STRING, G_TYPE_VALUE),
+				&hash, G_TYPE_INVALID);
+
+	if (hash != NULL) {
+		value = g_hash_table_lookup(hash, "Address");
+		address = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Name");
+		name = value ? g_value_get_string(value) : NULL;
+
+		value = g_hash_table_lookup(hash, "Powered");
+		powered = value ? g_value_get_boolean(value) : FALSE;
+
+		value = g_hash_table_lookup(hash, "Discoverable");
+		discoverable = value ? g_value_get_boolean(value) : FALSE;
+
+		value = g_hash_table_lookup(hash, "DiscoverableTimeout");
+		timeout = value ? g_value_get_uint(value) : 0;
+	} else {
+		address = NULL;
+		name = NULL;
+		powered = FALSE;
+		discoverable = FALSE;
+		timeout = 0;
+	}
+
+	adapter->powered = powered;
+	adapter->discoverable = discoverable;
+	adapter->timeout_value = timeout;
+
+	mainbox = gtk_vbox_new(FALSE, 18);
+	gtk_container_set_border_width(GTK_CONTAINER(mainbox), 12);
+
+	gtk_notebook_prepend_page(GTK_NOTEBOOK(adapter->notebook),
+							mainbox, NULL);
+
+	update_tab_label(GTK_NOTEBOOK(adapter->notebook), mainbox, name);
+
+	adapter->child = mainbox;
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(mainbox), vbox, FALSE, FALSE, 0);
+
+	label = create_label(_("Visibility setting"));
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	button = gtk_radio_button_new_with_label(group, NULL);
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	gtk_widget_set_no_show_all(button, TRUE);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+
+	adapter->button_dummy = button;
+
+	button = gtk_radio_button_new_with_label(group,
+					_("Hidden"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	if (powered == TRUE && discoverable == FALSE)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+
+	adapter->button_hidden = button;
+	adapter->signal_hidden = g_signal_connect(G_OBJECT(button), "toggled",
+					G_CALLBACK(mode_callback), adapter);
+
+	button = gtk_radio_button_new_with_label(group,
+					_("Always visible"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	if (discoverable == TRUE && timeout == 0)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+
+	adapter->button_always = button;
+	adapter->signal_always = g_signal_connect(G_OBJECT(button), "toggled",
+					G_CALLBACK(mode_callback), adapter);
+
+	button = gtk_radio_button_new_with_label(group,
+					_("Temporary visible"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	if (discoverable == TRUE && timeout > 0)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+
+	adapter->button_timeout = button;
+	adapter->signal_timeout = g_signal_connect(G_OBJECT(button), "toggled",
+					G_CALLBACK(mode_callback), adapter);
+
+	scale = gtk_hscale_new_with_range(0, 30, 1);
+	gtk_scale_set_digits(GTK_SCALE(scale), 0);
+	gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_BOTTOM);
+	gtk_range_set_restrict_to_fill_level(GTK_RANGE(scale), FALSE);
+	gtk_range_set_show_fill_level(GTK_RANGE(scale), TRUE);
+
+	if (discoverable == TRUE) {
+		if (timeout == 0)
+			scale_value = 0;
+		else if (timeout < 60)
+			scale_value = 1;
+		else if (timeout > 60 * 30)
+			scale_value = 30;
+		else
+			scale_value = timeout / 60;
+	} else
+		scale_value = 0.0;
+
+	gtk_range_set_value(GTK_RANGE(scale), scale_value);
+	gtk_range_set_fill_level(GTK_RANGE(scale), scale_value);
+	gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_DISCONTINUOUS);
+	gtk_box_pack_start(GTK_BOX(vbox), scale, FALSE, FALSE, 0);
+
+	if (discoverable == TRUE && timeout > 0) {
+		adapter->timeout_remain = timeout;
+		g_timeout_add_seconds(1, counter_callback, adapter);
+	}
+
+	adapter->timeout_scale = scale;
+	adapter->signal_scale = g_signal_connect(G_OBJECT(scale), "value-changed",
+					G_CALLBACK(scale_callback), adapter);
+
+	g_signal_connect(G_OBJECT(scale), "format-value",
+					G_CALLBACK(format_callback), adapter);
+
+	if (discoverable == FALSE || timeout == 0)
+		gtk_widget_set_sensitive(GTK_WIDGET(scale), FALSE);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(mainbox), vbox, TRUE, TRUE, 0);
+
+	label = create_label(_("Friendly name"));
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	entry = gtk_entry_new();
+	gtk_entry_set_max_length(GTK_ENTRY(entry), 248);
+	gtk_widget_set_size_request(entry, 240, -1);
+	gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
+
+	if (name != NULL)
+		gtk_entry_set_text(GTK_ENTRY(entry), name);
+
+	adapter->entry = entry;
+
+	g_signal_connect(G_OBJECT(entry), "changed",
+					G_CALLBACK(name_callback), adapter);
+	g_signal_connect(G_OBJECT(entry), "focus-out-event",
+					G_CALLBACK(focus_callback), adapter);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(mainbox), vbox, TRUE, TRUE, 0);
+
+	label = create_label(_("Known devices"));
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
+							GTK_SHADOW_OUT);
+	gtk_container_add(GTK_CONTAINER(vbox), scrolled);
+
+	model = bluetooth_client_get_device_filter_model(client,
+				adapter->proxy, device_filter, NULL, NULL);
+	g_signal_connect(G_OBJECT(model), "row-changed",
+				G_CALLBACK(row_callback), adapter);
+	tree = create_tree(model, FALSE);
+	g_object_unref(model);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+	g_signal_connect(G_OBJECT(selection), "changed",
+				G_CALLBACK(select_callback), adapter);
+
+	adapter->selection = selection;
+
+	gtk_container_add(GTK_CONTAINER(scrolled), tree);
+
+	buttonbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(buttonbox),
+						GTK_BUTTONBOX_START);
+	gtk_box_set_spacing(GTK_BOX(buttonbox), 6);
+	gtk_box_set_homogeneous(GTK_BOX(buttonbox), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), buttonbox, FALSE, FALSE, 0);
+
+	button = gtk_button_new();
+	image = gtk_image_new_from_stock(GTK_STOCK_ADD,
+						GTK_ICON_SIZE_BUTTON);
+	gtk_container_add(GTK_CONTAINER(button), image);
+	gtk_container_set_border_width(GTK_CONTAINER(button), 0);
+	gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+				G_CALLBACK(wizard_callback), adapter);
+
+	button = gtk_button_new();
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	image = gtk_image_new_from_stock(GTK_STOCK_DISCONNECT,
+						GTK_ICON_SIZE_BUTTON);
+	gtk_container_add(GTK_CONTAINER(button), image);
+	gtk_container_set_border_width(GTK_CONTAINER(button), 0);
+	gtk_widget_set_no_show_all(button, TRUE);
+	gtk_widget_show(image);
+	gtk_box_pack_end(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
+	gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(buttonbox),
+								button, TRUE);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+				G_CALLBACK(disconnect_callback), adapter);
+
+	adapter->button_disconnect = button;
+
+	button = gtk_button_new();
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	image = gtk_image_new_from_stock(GTK_STOCK_ABOUT,
+						GTK_ICON_SIZE_BUTTON);
+	gtk_container_add(GTK_CONTAINER(button), image);
+	gtk_container_set_border_width(GTK_CONTAINER(button), 0);
+	gtk_widget_set_no_show_all(button, TRUE);
+	gtk_widget_show(image);
+	gtk_box_pack_end(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
+	gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(buttonbox),
+								button, TRUE);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+				G_CALLBACK(trusted_callback), adapter);
+
+	adapter->button_trusted = button;
+
+	button = gtk_button_new();
+	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+	image = gtk_image_new_from_stock(GTK_STOCK_DELETE,
+						GTK_ICON_SIZE_BUTTON);
+	gtk_container_add(GTK_CONTAINER(button), image);
+	gtk_container_set_border_width(GTK_CONTAINER(button), 0);
+	gtk_widget_set_no_show_all(button, TRUE);
+	gtk_widget_show(image);
+	gtk_box_pack_end(GTK_BOX(buttonbox), button, FALSE, FALSE, 0);
+	gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(buttonbox),
+								button, TRUE);
+
+	g_signal_connect(G_OBJECT(button), "clicked",
+				G_CALLBACK(delete_callback), adapter);
+
+	adapter->button_delete = button;
+
+	g_object_set_data(G_OBJECT(mainbox), "adapter", adapter);
+
+	gtk_widget_show_all(mainbox);
+}
+
+static void update_visibility(struct adapter_data *adapter)
+{
+	GtkWidget *scale = adapter->timeout_scale;
+	GtkWidget *button;
+	gboolean sensitive;
+	gdouble value;
+
+	if (adapter->discoverable == TRUE) {
+		if (adapter->timeout_value == 0)
+			value = 0;
+		else if (adapter->timeout_value < 60)
+			value = 1;
+		else if (adapter->timeout_value > 60 * 30)
+			value = 30;
+		else
+			value = adapter->timeout_value / 60;
+
+		if (adapter->timeout_value > 0) {
+			button = adapter->button_timeout;
+			sensitive = TRUE;
+		} else {
+			button = adapter->button_always;
+			sensitive = FALSE;
+		}
+
+		if (adapter->timeout_remain == 0)
+			g_timeout_add_seconds(1, counter_callback, adapter);
+
+		adapter->timeout_remain = adapter->timeout_value;
+	} else {
+		value = 0.0;
+
+		if (adapter->powered == FALSE)
+			button = adapter->button_dummy;
+		else
+			button = adapter->button_hidden;
+
+		sensitive = FALSE;
+		adapter->timeout_remain = 0;
+	}
+
+	block_signals(adapter);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+	gtk_range_set_value(GTK_RANGE(scale), value);
+	gtk_range_set_fill_level(GTK_RANGE(scale), value);
+	if (sensitive == FALSE) {
+		gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
+		gtk_scale_set_draw_value(GTK_SCALE(scale), TRUE);
+	}
+	gtk_widget_set_sensitive(GTK_WIDGET(scale), sensitive);
+	unblock_signals(adapter);
+}
+
+static void property_changed(DBusGProxy *proxy, const char *property,
+					GValue *value, gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+
+	if (g_str_equal(property, "Name") == TRUE) {
+		const gchar *name = g_value_get_string(value);
+
+		update_tab_label(GTK_NOTEBOOK(adapter->notebook),
+							adapter->child, name);
+
+		gtk_entry_set_text(GTK_ENTRY(adapter->entry),
+							name ? name : "");
+
+		adapter->name_changed = 0;
+	} else if (g_str_equal(property, "Powered") == TRUE) {
+		gboolean powered = g_value_get_boolean(value);
+
+		adapter->powered = powered;
+
+		if (powered == FALSE)
+			adapter->discoverable = FALSE;
+
+		update_visibility(adapter);
+	} else if (g_str_equal(property, "Discoverable") == TRUE) {
+		gboolean discoverable = g_value_get_boolean(value);
+
+		adapter->powered = TRUE;
+		adapter->discoverable = discoverable;
+
+		update_visibility(adapter);
+	} else if (g_str_equal(property, "DiscoverableTimeout") == TRUE) {
+		guint timeout = g_value_get_uint(value);
+
+		adapter->timeout_value = timeout;
+
+		if (adapter->timeout_remain == 0)
+			g_timeout_add_seconds(1, counter_callback, adapter);
+
+		adapter->timeout_remain = timeout;
+
+		update_visibility(adapter);
+	}
+}
+
+static struct adapter_data *adapter_alloc(GtkTreeModel *model,
+		GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
+{
+	DBusGProxy *proxy;
+	struct adapter_data *adapter;
+
+	adapter = g_try_malloc0(sizeof(*adapter));
+	if (adapter == NULL)
+		return NULL;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PROXY, &proxy, -1);
+
+	if (proxy == NULL) {
+		g_free(adapter);
+		return NULL;
+	}
+
+	adapter->notebook = user_data;
+
+	adapter->reference = gtk_tree_row_reference_new(model, path);
+	adapter->proxy = proxy;
+
+	return adapter;
+}
+
+static gboolean adapter_create(gpointer user_data)
+{
+	struct adapter_data *adapter = user_data;
+
+	dbus_g_proxy_connect_signal(adapter->proxy, "PropertyChanged",
+				G_CALLBACK(property_changed), adapter, NULL);
+
+	create_adapter(adapter);
+
+	return FALSE;
+}
+
+static gboolean adapter_insert(GtkTreeModel *model, GtkTreePath *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	struct adapter_data *adapter;
+
+	adapter = adapter_alloc(model, path, iter, user_data);
+	if (adapter == NULL)
+		return FALSE;
+
+	adapter_create(adapter);
+
+	return FALSE;
+}
+
+static void adapter_added(GtkTreeModel *model, GtkTreePath *path,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	struct adapter_data *adapter;
+
+	adapter = adapter_alloc(model, path, iter, user_data);
+	if (adapter == NULL)
+		return;
+
+	g_timeout_add(250, adapter_create, adapter);
+}
+
+static void adapter_removed(GtkTreeModel *model, GtkTreePath *path,
+							gpointer user_data)
+{
+	GtkNotebook *notebook = user_data;
+	int i, count = gtk_notebook_get_n_pages(notebook);
+
+	for (i = 0; i < count; i++) {
+		GtkWidget *widget;
+		struct adapter_data *adapter;
+
+		widget = gtk_notebook_get_nth_page(notebook, i);
+		if (widget == NULL)
+			continue;
+
+		adapter = g_object_get_data(G_OBJECT(widget), "adapter");
+		if (adapter == NULL)
+			continue;
+
+		if (gtk_tree_row_reference_valid(adapter->reference) == TRUE)
+			continue;
+
+		gtk_tree_row_reference_free(adapter->reference);
+		adapter->reference = NULL;
+
+		gtk_notebook_remove_page(notebook, i);
+
+		g_signal_handlers_disconnect_by_func(adapter->proxy,
+						property_changed, adapter);
+
+		g_object_unref(adapter->proxy);
+		g_free(adapter);
+	}
+}
+
+void setup_adapter(GtkNotebook *notebook)
+{
+	client = bluetooth_client_new();
+
+	adapter_model = bluetooth_client_get_adapter_model(client);
+
+	g_signal_connect_after(G_OBJECT(adapter_model), "row-inserted",
+					G_CALLBACK(adapter_added), notebook);
+
+	g_signal_connect_after(G_OBJECT(adapter_model), "row-deleted",
+					G_CALLBACK(adapter_removed), notebook);
+
+	gtk_tree_model_foreach(adapter_model, adapter_insert, notebook);
+}
+
+void cleanup_adapter(void)
+{
+	g_object_unref(adapter_model);
+
+	g_object_unref(client);
+}

Added: trunk/properties/adapter.h
==============================================================================
--- (empty file)
+++ trunk/properties/adapter.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void setup_adapter(GtkNotebook *notebook);
+void cleanup_adapter(void);

Added: trunk/properties/bluetooth-manager.schemas.in
==============================================================================
--- (empty file)
+++ trunk/properties/bluetooth-manager.schemas.in	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,41 @@
+<gconfschemafile>
+  <schemalist>
+
+    <schema>
+      <key>/schemas/apps/bluetooth-manager/icon_policy</key>
+      <applyto>/apps/bluetooth-manager/icon_policy</applyto>
+      <owner>bluetooth-manager</owner>
+      <type>string</type>
+      <default>present</default>
+      <locale name="C">
+        <short>When to show the notification icon</short>
+        <long>Display options for the notification icon. Valid options are "never", "present" and "always".</long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/bluetooth-manager/receive_enabled</key>
+      <applyto>/apps/bluetooth-manager/receive_enabled</applyto>
+      <owner>bluetooth-manager</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>If receiving of remote files via Bluetooth is enabled</short>
+        <long>If the service for receiving remote files via Bluetooth is enabled or not.</long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/bluetooth-manager/sharing_enabled</key>
+      <applyto>/apps/bluetooth-manager/sharing_enabled</applyto>
+      <owner>bluetooth-manager</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>If Bluetooth file sharing is enabled</short>
+        <long>If the service for sharing files via Bluetooth is enabled or not.</long>
+      </locale>
+    </schema>
+
+  </schemalist>
+</gconfschemafile>

Added: trunk/properties/bluetooth-properties.1
==============================================================================
--- (empty file)
+++ trunk/properties/bluetooth-properties.1	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,29 @@
+.TH BLUETOOTH-PROPERTIES 1 "Oct 4, 2006" "bluez-gnome" "Linux User's Manual"
+.SH NAME
+bluetooth-properties - GTK dialog for managing properties of the Linux Bluetooth stack
+.SH SYNOPSIS
+.B bluetooth-properties
+.SH DESCRIPTION
+.I bluetooth-properties
+will display a dialog for changing Bluetooth preferences.
+.I bluetooth-properties
+is part of bluez-gnome, see also http://www.bluez.org
+.SH OPTIONS
+.I bluetooth-properties
+takes no options
+.SH AUTHOR
+Marcel Holtmann <marcel holtmann org>
+.SH LICENSE
+bluetooth-properties is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Added: trunk/properties/bluetooth-properties.desktop.in
==============================================================================
--- (empty file)
+++ trunk/properties/bluetooth-properties.desktop.in	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Bluetooth
+_Comment=Configure Bluetooth settings
+Icon=bluetooth
+Exec=bluetooth-properties
+Terminal=false
+Type=Application
+Categories=GTK;GNOME;Settings;HardwareSettings;
+OnlyShowIn=GNOME;

Added: trunk/properties/general.c
==============================================================================
--- (empty file)
+++ trunk/properties/general.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,288 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <gconf/gconf-client.h>
+
+#include <bluetooth-client.h>
+
+#include "killswitch.h"
+#include "general.h"
+
+typedef enum {
+	ICON_POLICY_NEVER,
+	ICON_POLICY_ALWAYS,
+	ICON_POLICY_PRESENT,
+} EnumIconPolicy;
+
+static int icon_policy = ICON_POLICY_PRESENT;
+
+#define PREF_DIR		"/apps/bluetooth-manager"
+#define PREF_ICON_POLICY	PREF_DIR "/icon_policy"
+#if 0
+#define PREF_RECEIVE_ENABLED	PREF_DIR "/receive_enabled"
+#define PREF_SHARING_ENABLED	PREF_DIR "/sharing_enabled"
+#endif
+
+static GConfEnumStringPair icon_policy_enum_map [] = {
+	{ ICON_POLICY_NEVER,	"never"		},
+	{ ICON_POLICY_ALWAYS,	"always"	},
+	{ ICON_POLICY_PRESENT,	"present"	},
+	{ ICON_POLICY_PRESENT,	NULL		},
+};
+
+static GConfClient* gconf;
+
+#if 0
+static GtkWidget *button_receive;
+static GtkWidget *button_sharing;
+#endif
+static GtkWidget *button_never;
+static GtkWidget *button_always;
+static GtkWidget *button_present;
+
+static void update_icon_policy(GtkWidget *button)
+{
+	GtkWidget *widget;
+	const char *str;
+
+	if (button == NULL) {
+		switch (icon_policy) {
+		case ICON_POLICY_NEVER:
+			widget = button_never;
+			break;
+		case ICON_POLICY_ALWAYS:
+			widget = button_always;
+			break;
+		case ICON_POLICY_PRESENT:
+		default:
+			widget = button_present;
+			break;
+		}
+
+		if (!widget)
+			return;
+
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
+
+		return;
+	}
+
+	if (button == button_always)
+		icon_policy = ICON_POLICY_ALWAYS;
+	else if (button == button_never)
+		icon_policy = ICON_POLICY_NEVER;
+	else if (button == button_present)
+		icon_policy = ICON_POLICY_PRESENT;
+
+	str = gconf_enum_to_string(icon_policy_enum_map, icon_policy);
+
+	gconf_client_set_string(gconf, PREF_ICON_POLICY, str, NULL);
+}
+
+static void policy_callback(GtkWidget *button, gpointer user_data)
+{
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == TRUE)
+		update_icon_policy(button);
+}
+
+#if 0
+static void receive_callback(GtkWidget *button, gpointer user_data)
+{
+	gboolean value;
+
+	value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+
+	gconf_client_set_bool(gconf, PREF_RECEIVE_ENABLED, value, NULL);
+}
+
+static void sharing_callback(GtkWidget *button, gpointer user_data)
+{
+	gboolean value;
+
+	value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+
+	gconf_client_set_bool(gconf, PREF_SHARING_ENABLED, value, NULL);
+}
+#endif
+
+GtkWidget *create_label(const gchar *str)
+{
+	GtkWidget *label;
+	gchar *tmp;
+
+	label = gtk_label_new(NULL);
+	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
+
+	tmp = g_strdup_printf("<b>%s</b>", str);
+	gtk_label_set_markup(GTK_LABEL(label), tmp);
+	g_free(tmp);
+
+	return label;
+}
+
+GtkWidget *create_general(void)
+{
+	GtkWidget *mainbox;
+	GtkWidget *vbox;
+	GtkWidget *label;
+	GSList *group = NULL;
+#if 0
+	gboolean value;
+#endif
+
+	mainbox = gtk_vbox_new(FALSE, 24);
+	gtk_container_set_border_width(GTK_CONTAINER(mainbox), 12);
+
+#if 0
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(mainbox), vbox, FALSE, FALSE, 0);
+
+	label = create_label(_("File transfer"));
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	button_receive = gtk_check_button_new_with_label(
+				_("Receive files from remote devices"));
+	value = gconf_client_get_bool(gconf, PREF_RECEIVE_ENABLED, NULL);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_receive), value);
+	gtk_box_pack_start(GTK_BOX(vbox), button_receive, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button_receive), "toggled",
+					G_CALLBACK(receive_callback), NULL);
+
+	button_sharing = gtk_check_button_new_with_label(
+				_("Share files from public folder"));
+	value = gconf_client_get_bool(gconf, PREF_SHARING_ENABLED, NULL);
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_sharing), value);
+	gtk_box_pack_start(GTK_BOX(vbox), button_sharing, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button_sharing), "toggled",
+					G_CALLBACK(sharing_callback), NULL);
+#endif
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(mainbox), vbox, FALSE, FALSE, 0);
+
+	label = create_label(_("Notification area"));
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	button_never = gtk_radio_button_new_with_label(group,
+						_("Never display icon"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button_never));
+	gtk_box_pack_start(GTK_BOX(vbox), button_never, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button_never), "toggled",
+					G_CALLBACK(policy_callback), NULL);
+
+	button_present = gtk_radio_button_new_with_label(group,
+				_("Only display when adapter present"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button_present));
+	gtk_box_pack_start(GTK_BOX(vbox), button_present, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button_present), "toggled",
+					G_CALLBACK(policy_callback), NULL);
+
+	button_always = gtk_radio_button_new_with_label(group,
+						_("Always display icon"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button_always));
+	gtk_box_pack_start(GTK_BOX(vbox), button_always, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button_always), "toggled",
+					G_CALLBACK(policy_callback), NULL);
+
+	update_icon_policy(NULL);
+
+	vbox = create_killswitch();
+	gtk_box_pack_start(GTK_BOX(mainbox), vbox, FALSE, FALSE, 0);
+
+	return mainbox;
+}
+
+static void gconf_callback(GConfClient *client, guint cnxn_id,
+					GConfEntry *entry, gpointer user_data)
+{
+	GConfValue *value;
+
+	value = gconf_entry_get_value(entry);
+	if (value == NULL)
+		return;
+
+	if (g_str_equal(entry->key, PREF_ICON_POLICY) == TRUE) {
+		const char *str;
+
+		str = gconf_value_get_string(value);
+		if (str)
+			gconf_string_to_enum(icon_policy_enum_map,
+							str, &icon_policy);
+
+		update_icon_policy(NULL);
+	}
+
+#if 0
+	if (g_str_equal(entry->key, PREF_RECEIVE_ENABLED) == TRUE) {
+		if (!button_receive)
+			return;
+
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_receive),
+						gconf_value_get_bool(value));
+	}
+
+	if (g_str_equal(entry->key, PREF_SHARING_ENABLED) == TRUE) {
+		if (!button_sharing)
+			return;
+
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_sharing),
+						gconf_value_get_bool(value));
+	}
+#endif
+}
+
+void setup_general(void)
+{
+	char *str;
+
+	gconf = gconf_client_get_default();
+
+	str = gconf_client_get_string(gconf, PREF_ICON_POLICY, NULL);
+	if (str) {
+		gconf_string_to_enum(icon_policy_enum_map, str, &icon_policy);
+		g_free(str);
+	}
+
+	gconf_client_add_dir(gconf, PREF_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+	gconf_client_notify_add(gconf, PREF_DIR,
+					gconf_callback, NULL, NULL, NULL);
+
+	setup_killswitch();
+}
+
+void cleanup_general(void)
+{
+	cleanup_killswitch();
+
+	g_object_unref(gconf);
+}

Added: trunk/properties/general.h
==============================================================================
--- (empty file)
+++ trunk/properties/general.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+GtkWidget *create_label(const gchar *str);
+GtkWidget *create_general(void);
+void setup_general(void);
+void cleanup_general(void);

Added: trunk/properties/killswitch.c
==============================================================================
--- (empty file)
+++ trunk/properties/killswitch.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,248 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib-lowlevel.h>
+
+#ifdef HAVE_HAL
+#include <hal/libhal.h>
+#endif
+
+#include "general.h"
+#include "killswitch.h"
+
+#ifdef HAVE_HAL
+static LibHalContext *halctx = NULL;
+
+static void setpower_reply(DBusPendingCall *call, void *user_data)
+{
+	GtkWidget *button = user_data;
+	DBusMessage *reply;
+	gint32 power;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INT32, &power,
+						DBUS_TYPE_INVALID) == FALSE) {
+		dbus_message_unref(reply);
+		return;
+	}
+
+	dbus_message_unref(reply);
+
+	gtk_widget_set_sensitive(button, TRUE);
+}
+
+static void toggle_callback(GtkWidget *button, gpointer user_data)
+{
+	DBusConnection *conn;
+	DBusPendingCall *call;
+	DBusMessage *message;
+	const char *udi;
+	gboolean value;
+
+	value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+
+	gtk_widget_set_sensitive(button, FALSE);
+
+	udi = g_object_get_data(G_OBJECT(button), "UDI");
+
+	message = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+				"org.freedesktop.Hal.Device.KillSwitch",
+								"SetPower");
+	if (message == NULL)
+		return;
+
+	dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &value,
+							DBUS_TYPE_INVALID);
+
+	conn = libhal_ctx_get_dbus_connection(halctx);
+
+	if (dbus_connection_send_with_reply(conn, message,
+						&call, -1) == FALSE) {
+		dbus_message_unref(message);
+		return;
+	}
+
+	dbus_pending_call_set_notify(call, setpower_reply, button, NULL);
+
+	dbus_message_unref(message);
+}
+
+static void getpower_reply(DBusPendingCall *call, void *user_data)
+{
+	GtkWidget *button = user_data;
+	DBusMessage *reply;
+	gint32 power;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INT32, &power,
+						DBUS_TYPE_INVALID) == FALSE) {
+		dbus_message_unref(reply);
+		return;
+	}
+
+	if (power > 0)
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+
+	dbus_message_unref(reply);
+
+	gtk_widget_set_sensitive(button, TRUE);
+}
+
+static gboolean add_killswitch(GtkWidget *vbox, const char *udi)
+{
+	DBusConnection *conn;
+	DBusPendingCall *call;
+	DBusMessage *message;
+	GtkWidget *button;
+	const char *type, *name;
+
+	type = libhal_device_get_property_string(halctx, udi,
+						"killswitch.type", NULL);
+	if (type == NULL || g_str_equal(type, "bluetooth") == FALSE)
+		return FALSE;
+
+	name = libhal_device_get_property_string(halctx, udi,
+							"info.product", NULL);
+	if (name == NULL)
+		name = "Bluetooth Switch";
+
+	button = gtk_check_button_new_with_label(name);
+	g_object_set_data(G_OBJECT(button), "UDI", g_strdup(udi));
+	gtk_widget_set_sensitive(button, FALSE);
+	gtk_box_pack_end(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(button), "toggled",
+					G_CALLBACK(toggle_callback), NULL);
+
+	message = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+				"org.freedesktop.Hal.Device.KillSwitch",
+								"GetPower");
+	if (message == NULL)
+		return TRUE;
+
+	conn = libhal_ctx_get_dbus_connection(halctx);
+
+	if (dbus_connection_send_with_reply(conn, message,
+						&call, -1) == FALSE) {
+		dbus_message_unref(message);
+		return TRUE;
+	}
+
+	dbus_pending_call_set_notify(call, getpower_reply, button, NULL);
+
+	dbus_message_unref(message);
+
+	return TRUE;
+}
+
+static gboolean detect_killswitch(GtkWidget *vbox)
+{
+	gboolean result = FALSE;
+	char **list;
+	int num;
+
+	list = libhal_find_device_by_capability(halctx, "killswitch",
+								&num, NULL);
+	if (list) {
+		char **tmp = list;
+
+		while (*tmp) {
+			if (add_killswitch(vbox, *tmp) == TRUE)
+				result = TRUE;
+			tmp++;
+		}
+
+		libhal_free_string_array(list);
+	}
+
+	return result;
+}
+#else
+#define detect_killswitch(widget) FALSE
+#endif
+
+GtkWidget *create_killswitch(void)
+{
+	GtkWidget *vbox;
+	GtkWidget *label;
+
+	vbox = gtk_vbox_new(FALSE, 6);
+
+	if (detect_killswitch(vbox) == TRUE) {
+		label = create_label(_("Power switches"));
+		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	}
+
+	return vbox;
+}
+
+static DBusConnection *connection;
+
+void setup_killswitch(void)
+{
+	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+	if (connection == NULL)
+		return;
+
+#ifdef HAVE_HAL
+	halctx = libhal_ctx_new();
+	if (halctx == NULL)
+		return;
+
+	if (libhal_ctx_set_dbus_connection(halctx, connection) == FALSE) {
+		libhal_ctx_free(halctx);
+		halctx = NULL;
+		return;
+	}
+
+	if (libhal_ctx_init(halctx, NULL) == FALSE) {
+		g_printerr("Couldn't init HAL context\n");
+		libhal_ctx_free(halctx);
+		halctx = NULL;
+		return;
+	}
+#endif
+}
+
+void cleanup_killswitch(void)
+{
+#ifdef HAVE_HAL
+	if (halctx != NULL) {
+		libhal_ctx_shutdown(halctx, NULL);
+		libhal_ctx_free(halctx);
+		halctx = NULL;
+	}
+#endif
+
+	if (connection != NULL)
+		dbus_connection_unref(connection);
+}

Added: trunk/properties/killswitch.h
==============================================================================
--- (empty file)
+++ trunk/properties/killswitch.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,27 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+GtkWidget *create_killswitch(void);
+void setup_killswitch(void);
+void cleanup_killswitch(void);

Added: trunk/properties/main.c
==============================================================================
--- (empty file)
+++ trunk/properties/main.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,156 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <bluetooth-instance.h>
+
+#include "general.h"
+#include "adapter.h"
+
+static gboolean delete_callback(GtkWidget *window, GdkEvent *event,
+							gpointer user_data)
+{
+	gtk_widget_destroy(GTK_WIDGET(window));
+
+	gtk_main_quit();
+
+	return FALSE;
+}
+
+static void close_callback(GtkWidget *button, gpointer user_data)
+{
+	GtkWidget *window = user_data;
+
+	gtk_widget_destroy(GTK_WIDGET(window));
+
+	gtk_main_quit();
+}
+
+static GtkWidget *create_window(GtkWidget *notebook)
+{
+	GtkWidget *window;
+	GtkWidget *widget;
+	GtkWidget *vbox;
+	GtkWidget *buttonbox;
+	GtkWidget *button;
+
+	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(window), _("Bluetooth Preferences"));
+	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+	gtk_window_set_default_size(GTK_WINDOW(window), 420, 420);
+	g_signal_connect(G_OBJECT(window), "delete-event",
+					G_CALLBACK(delete_callback), NULL);
+
+	vbox = gtk_vbox_new(FALSE, 12);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+	gtk_container_add(GTK_CONTAINER(window), vbox);
+
+	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+
+	buttonbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(buttonbox), GTK_BUTTONBOX_END);
+	gtk_box_pack_start(GTK_BOX(vbox), buttonbox, FALSE, FALSE, 0);
+
+	button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+	gtk_container_add(GTK_CONTAINER(buttonbox), button);
+	g_signal_connect(G_OBJECT(button), "clicked",
+					G_CALLBACK(close_callback), window);
+
+#if 0
+	button = gtk_button_new_from_stock(GTK_STOCK_HELP);
+	gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(buttonbox),
+								button, TRUE);
+	gtk_container_add(GTK_CONTAINER(buttonbox), button);
+#endif
+
+	widget = create_general();
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, NULL);
+	gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(notebook),
+						widget, _("General"));
+
+	gtk_widget_show_all(window);
+
+	return window;
+}
+
+static GOptionEntry options[] = {
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	BluetoothInstance *instance;
+	GtkWidget *window;
+	GtkWidget *notebook;
+	GError *error = NULL;
+
+	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+	textdomain(GETTEXT_PACKAGE);
+
+	if (gtk_init_with_args(&argc, &argv, NULL,
+				options, GETTEXT_PACKAGE, &error) == FALSE) {
+		if (error) {
+			g_print("%s\n", error->message);
+			g_error_free(error);
+		} else
+			g_print("An unknown error occurred\n");
+
+		gtk_exit(1);
+	}
+
+	instance = bluetooth_instance_new("properties");
+	if (instance == NULL)
+		gtk_exit(0);
+
+	g_set_application_name(_("Bluetooth Properties"));
+
+	gtk_window_set_default_icon_name("bluetooth");
+
+	setup_general();
+
+	notebook = gtk_notebook_new();
+
+	setup_adapter(GTK_NOTEBOOK(notebook));
+
+	window = create_window(notebook);
+
+	bluetooth_instance_set_window(instance, GTK_WINDOW(window));
+
+	gtk_main();
+
+	cleanup_adapter();
+
+	cleanup_general();
+
+	g_object_unref(instance);
+
+	return 0;
+}

Added: trunk/properties/service.c
==============================================================================
--- (empty file)
+++ trunk/properties/service.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,400 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus-glib.h>
+
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+
+#include "general.h"
+#include "service.h"
+#include "network.h"
+#include "input.h"
+#include "audio.h"
+
+static DBusGConnection *connection = NULL;
+static DBusGProxy *manager = NULL;
+
+static GtkWidget *notebook;
+static GtkWidget *page_network;
+static GtkWidget *page_input;
+static GtkWidget *page_audio;
+
+static GtkListStore *service_store;
+
+enum {
+	COLUMN_PATH,
+	COLUMN_IDENT,
+};
+
+static void show_service(const gchar *identifier)
+{
+	GtkWidget *widget = NULL;
+	gint page = -1;
+
+	if (g_ascii_strcasecmp(identifier, "network") == 0)
+		widget = page_network;
+
+	if (g_ascii_strcasecmp(identifier, "input") == 0)
+		widget = page_input;
+
+	if (g_ascii_strcasecmp(identifier, "audio") == 0)
+		widget = page_audio;
+
+	page = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), widget);
+	if (page < 0) {
+		gtk_widget_hide(notebook);
+		return;
+	}
+
+	gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page);
+	gtk_widget_show(notebook);
+}
+
+static void select_callback(GtkTreeSelection *selection, gpointer user_data)
+{
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gboolean selected;
+	gchar *identifier;
+
+	selected = gtk_tree_selection_get_selected(selection, &model, &iter);
+
+	if (selected == TRUE) {
+		gtk_tree_model_get(model, &iter, COLUMN_IDENT, &identifier, -1);
+		show_service(identifier);
+		g_free(identifier);
+	} else
+		gtk_widget_hide(notebook);
+}
+
+static void ident_to_text(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+			GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+	gchar *identifier, *text;
+
+	gtk_tree_model_get(model, iter, COLUMN_IDENT, &identifier, -1);
+	if (identifier == NULL)
+		text = "unknown";
+
+	if (g_ascii_strcasecmp(identifier, "network") == 0)
+		text = _("Network service");
+
+	if (g_ascii_strcasecmp(identifier, "input") == 0)
+		text = _("Input service");
+
+	if (g_ascii_strcasecmp(identifier, "audio") == 0)
+		text = _("Audio service");
+
+	if (g_ascii_strcasecmp(identifier, "serial") == 0)
+		text = _("Serial service");
+
+	g_object_set(cell, "text", text, NULL);
+
+	g_free(identifier);
+}
+
+GtkWidget *create_service(void)
+{
+	GtkWidget *mainbox;
+	GtkWidget *vbox;
+	GtkWidget *label;
+	GtkWidget *scrolled;
+	GtkWidget *tree;
+	GtkTreeViewColumn *column;
+	GtkTreeSelection *selection;
+
+	mainbox = gtk_vbox_new(FALSE, 18);
+	gtk_container_set_border_width(GTK_CONTAINER(mainbox), 12);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(mainbox), vbox, FALSE, FALSE, 0);
+
+	label = create_label(_("Available services"));
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
+							GTK_SHADOW_OUT);
+	gtk_container_add(GTK_CONTAINER(vbox), scrolled);
+
+	tree = gtk_tree_view_new();
+	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
+	gtk_widget_set_size_request(tree, -1, 130);
+	gtk_tree_view_set_model(GTK_TREE_VIEW(tree),
+					GTK_TREE_MODEL(service_store));
+
+	gtk_tree_view_insert_column_with_data_func(GTK_TREE_VIEW(tree), 0,
+					"Name", gtk_cell_renderer_text_new(),
+						ident_to_text, NULL, NULL);
+	column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree), 0);
+	gtk_tree_view_column_set_expand(GTK_TREE_VIEW_COLUMN(column), TRUE);
+
+	gtk_container_add(GTK_CONTAINER(scrolled), tree);
+
+	notebook = gtk_notebook_new();
+	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
+	gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
+	gtk_widget_set_no_show_all(notebook, TRUE);
+	gtk_container_add(GTK_CONTAINER(mainbox), notebook);
+
+	page_network = create_network();
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_network, NULL);
+
+	page_input = create_input();
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_input, NULL);
+
+	page_audio = create_audio();
+	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_audio, NULL);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+	g_signal_connect(G_OBJECT(selection), "changed",
+				G_CALLBACK(select_callback), NULL);
+
+	return mainbox;
+}
+
+static GList *service_list = NULL;
+
+struct service_data {
+	DBusGProxy *proxy;
+	gchar *path;
+	gchar *identifier;
+	gboolean present;
+	GtkTreeIter iter;
+};
+
+static void service_free(gpointer data, gpointer user_data)
+{
+	struct service_data *service = data;
+
+	service_list = g_list_remove(service_list, service);
+
+	g_free(service->identifier);
+	g_free(service->path);
+	g_free(service);
+}
+
+static void service_disable(gpointer data, gpointer user_data)
+{
+	struct service_data *service = data;
+
+	if (service->present) {
+		service->present = 0;
+		g_object_unref(service->proxy);
+		service->proxy = NULL;
+		gtk_list_store_remove(service_store, &service->iter);
+	}
+}
+
+static gint service_compare(gconstpointer a, gconstpointer b)
+{
+	const struct service_data *service = a;
+	const char *path = b;
+
+	return g_ascii_strcasecmp(service->path, path);
+}
+
+static void service_started(DBusGProxy *object, gpointer user_data)
+{
+	struct service_data *service = user_data;
+	const char *busname;
+
+	dbus_g_proxy_call(manager, "ActivateService", NULL,
+			G_TYPE_STRING, service->identifier, G_TYPE_INVALID,
+				G_TYPE_STRING, &busname, G_TYPE_INVALID);
+
+	if (g_ascii_strcasecmp(service->identifier, "network") == 0)
+		enable_network(connection, busname);
+
+	if (g_ascii_strcasecmp(service->identifier, "input") == 0)
+		enable_input(connection, busname);
+
+	if (g_ascii_strcasecmp(service->identifier, "audio") == 0)
+		enable_audio(connection, busname);
+}
+
+static void service_stopped(DBusGProxy *object, gpointer user_data)
+{
+	struct service_data *service = user_data;
+
+	if (g_ascii_strcasecmp(service->identifier, "network") == 0)
+		disable_network();
+
+	if (g_ascii_strcasecmp(service->identifier, "input") == 0)
+		disable_input();
+
+	if (g_ascii_strcasecmp(service->identifier, "audio") == 0)
+		disable_audio();
+}
+
+static void add_service(const char *path)
+{
+	DBusGProxy *object;
+	GList *list;
+	struct service_data *service;
+	const char *identifier = NULL;
+	gboolean running = FALSE;
+
+	list = g_list_find_custom(service_list, path, service_compare);
+	if (list && list->data) {
+		service = list->data;
+		goto done;
+	}
+
+	service = g_try_malloc0(sizeof(*service));
+	if (!service)
+		return;
+
+	service->path = g_strdup(path);
+
+	service_list = g_list_append(service_list, service);
+
+done:
+	service->present = 1;
+
+	object = dbus_g_proxy_new_for_name(connection, "org.bluez",
+						path, "org.bluez.Service");
+
+	service->proxy = object;
+
+	dbus_g_proxy_add_signal(object, "Started", G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(object, "Started",
+				G_CALLBACK(service_started), service, NULL);
+
+	dbus_g_proxy_add_signal(object, "Stopped", G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(object, "Stopped",
+				G_CALLBACK(service_stopped), service, NULL);
+
+	dbus_g_proxy_call(object, "GetIdentifier", NULL, G_TYPE_INVALID,
+				G_TYPE_STRING, &identifier, G_TYPE_INVALID);
+
+	if (g_ascii_strcasecmp(identifier, "headset") == 0 ||
+				g_ascii_strcasecmp(identifier, "sink") == 0) {
+		g_object_unref(object);
+		service_free(service, NULL);
+		return;
+	}
+
+	g_free(service->identifier);
+	service->identifier = g_strdup(identifier);
+	if (service->identifier == NULL)
+		service->identifier = g_strdup("");
+
+	gtk_list_store_insert_with_values(service_store, &service->iter, -1,
+			COLUMN_PATH, path, COLUMN_IDENT, identifier, -1);
+
+	if (running == TRUE)
+		service_started(object, service);
+}
+
+static void service_added(DBusGProxy *object,
+				const char *path, gpointer user_data)
+{
+	add_service(path);
+}
+
+static void service_removed(DBusGProxy *object,
+				const char *path, gpointer user_data)
+{
+	GList *list;
+
+	list = g_list_find_custom(service_list, path, service_compare);
+	if (list && list->data) {
+		struct service_data *service = list->data;
+
+		service->present = 0;
+
+		g_free(service->identifier);
+		service->identifier = NULL;
+
+		gtk_list_store_remove(service_store, &service->iter);
+	}
+}
+
+void setup_service(DBusGConnection *conn)
+{
+	GError *error = NULL;
+	const gchar **array = NULL;
+
+	service_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+	if (!service_store)
+		return;
+
+	manager = dbus_g_proxy_new_for_name(conn, "org.bluez",
+					"/org/bluez", "org.bluez.Manager");
+	if (manager == NULL)
+		return;
+
+	connection = dbus_g_connection_ref(conn);
+
+	dbus_g_proxy_add_signal(manager, "ServiceAdded",
+					G_TYPE_STRING, G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(manager, "ServiceAdded",
+				G_CALLBACK(service_added), conn, NULL);
+
+	dbus_g_proxy_add_signal(manager, "ServiceRemoved",
+					G_TYPE_STRING, G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(manager, "ServiceRemoved",
+				G_CALLBACK(service_removed), conn, NULL);
+
+	dbus_g_proxy_call(manager, "ListServices", &error,
+			G_TYPE_INVALID, G_TYPE_STRV, &array, G_TYPE_INVALID);
+
+	if (error == NULL) {
+		while (*array) {
+			add_service(*array);
+			array++;
+		}
+	} else
+		g_error_free(error);
+}
+
+void cleanup_service(void)
+{
+	g_list_foreach(service_list, service_free, NULL);
+
+	if (manager != NULL)
+		g_object_unref(manager);
+
+	dbus_g_connection_unref(connection);
+}
+
+void disable_service(void)
+{
+	g_list_foreach(service_list, service_disable, NULL);
+}

Added: trunk/properties/service.h
==============================================================================
--- (empty file)
+++ trunk/properties/service.h	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *  Copyright (C) 2006-2007  Bastien Nocera <hadess hadess net>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+GtkWidget *create_service(void);
+void setup_service(DBusGConnection *conn);
+void cleanup_service(void);
+void disable_service(void);

Added: trunk/sendto/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/sendto/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,17 @@
+
+bin_PROGRAMS = bluetooth-sendto
+
+bluetooth_sendto_SOURCES = main.c
+
+bluetooth_sendto_LDADD = $(top_builddir)/common/libcommon.a \
+					@GTK_LIBS@ @GIO_LIBS@ @DBUS_LIBS@
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GIO_CFLAGS@ @GTK_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common -I$(top_builddir)/common
+
+man_MANS = bluetooth-sendto.1
+
+EXTRA_DIST = $(man_MANS)
+
+MAINTAINERCLEANFILES = Makefile.in

Added: trunk/sendto/bluetooth-sendto.1
==============================================================================
--- (empty file)
+++ trunk/sendto/bluetooth-sendto.1	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,29 @@
+.TH BLUETOOTH-SENDTO 1 "Oct 4, 2006" "bluez-gnome" "Linux User's Manual"
+.SH NAME
+bluetooth-sendto - GTK application for transfering files over Bluetooth
+.SH SYNOPSIS
+.B bluetooth-sendto
+.SH DESCRIPTION
+.I bluetooth-sendto
+will display a dialog for transfering files over Bluetooth.
+.I bluetooth-sendto
+is part of bluez-gnome, see also http://www.bluez.org
+.SH OPTIONS
+.I bluetooth-sendto
+takes no options
+.SH AUTHOR
+Marcel Holtmann <marcel holtmann org>
+.SH LICENSE
+bluetooth-sendto is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Added: trunk/sendto/main.c
==============================================================================
--- (empty file)
+++ trunk/sendto/main.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,841 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <obex-agent.h>
+
+#include "helper.h"
+#include "marshal.h"
+
+#define AGENT_PATH "/org/bluez/agent/sendto"
+
+static DBusGConnection *conn = NULL;
+
+static GtkWidget *dialog;
+static GtkWidget *label_from;
+static GtkWidget *label_status;
+static GtkWidget *progress;
+
+static gchar *option_device = NULL;
+static gchar **option_files = NULL;
+
+static gchar *device_name = NULL;
+
+static guint64 current_size = 0;
+static guint64 total_size = 0;
+static guint64 total_sent = 0;
+
+static int file_count = 0;
+static int file_index = 0;
+
+static gint64 first_update = 0;
+static gint64 last_update = 0;
+
+static gchar *filename_to_path(const gchar *filename)
+{
+	GFile *file;
+	gchar *path;
+
+	file = g_file_new_for_commandline_arg(filename);
+	path = g_file_get_path(file);
+	g_object_unref(file);
+
+	return path;
+}
+
+static gint64 get_system_time(void)
+{
+	struct timeval tmp;
+
+	gettimeofday(&tmp, NULL);
+
+	return (gint64) tmp.tv_usec +
+			(gint64) tmp.tv_sec * G_GINT64_CONSTANT(1000000);
+}
+
+static gchar *format_time(gint seconds)
+{
+	gint hours, minutes;
+
+	if (seconds < 0)
+		seconds = 0;
+
+	if (seconds < 60)
+		return g_strdup_printf(ngettext("%'d second",
+					"%'d seconds", seconds), seconds);
+
+	if (seconds < 60 * 60) {
+		minutes = (seconds + 30) / 60;
+		return g_strdup_printf(ngettext("%'d minute",
+					"%'d minutes", minutes), minutes);
+	}
+
+	hours = seconds / (60 * 60);
+
+	if (seconds < 60 * 60 * 4) {
+		gchar *res, *h, *m;
+
+		minutes = (seconds - hours * 60 * 60 + 30) / 60;
+
+		h = g_strdup_printf(ngettext("%'d hour",
+					"%'d hours", hours), hours);
+		m = g_strdup_printf(ngettext("%'d minute",
+					"%'d minutes", minutes), minutes);
+		res = g_strconcat(h, ", ", m, NULL);
+		g_free(h);
+		g_free(m);
+		return res;
+	}
+
+	return g_strdup_printf(ngettext("approximately %'d hour",
+				"approximately %'d hours", hours), hours);
+}
+
+static void response_callback(GtkWidget *dialog,
+					gint response, gpointer user_data)
+{
+	gtk_widget_destroy(dialog);
+
+	gtk_main_quit();
+}
+
+static gboolean is_palm_device(const gchar *bdaddr)
+{
+	return (g_str_has_prefix(bdaddr, "00:04:6B") ||
+			g_str_has_prefix(bdaddr, "00:07:E0") ||
+				g_str_has_prefix(bdaddr, "00:0E:20"));
+}
+
+static void create_window(void)
+{
+	GtkWidget *vbox;
+	GtkWidget *table;
+	GtkWidget *label;
+	gchar *text;
+
+	dialog = gtk_dialog_new_with_buttons(_("File Transfer"), NULL,
+				GTK_DIALOG_NO_SEPARATOR,
+				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+	gtk_window_set_type_hint(GTK_WINDOW(dialog),
+						GDK_WINDOW_TYPE_HINT_NORMAL);
+	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+	gtk_window_set_default_size(GTK_WINDOW(dialog), 400, -1);
+	gtk_container_set_border_width(GTK_CONTAINER(dialog), 6);
+
+	vbox = gtk_vbox_new(FALSE, 0);
+	gtk_box_set_spacing(GTK_BOX(vbox), 6);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox);
+
+	label = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	text = g_markup_printf_escaped("<span size=\"larger\"><b>%s</b></span>",
+					_("Sending files via Bluetooth"));
+	gtk_label_set_markup(GTK_LABEL(label), text);
+	g_free(text);
+	gtk_box_pack_start_defaults(GTK_BOX(vbox), label);
+
+	table = gtk_table_new(2, 2, FALSE);
+	gtk_table_set_col_spacings(GTK_TABLE(table), 4);
+	gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+	gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 9);
+
+	label = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+	text = g_markup_printf_escaped("<b>%s</b>", _("From:"));
+	gtk_label_set_markup(GTK_LABEL(label), text);
+	g_free(text);
+	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+							GTK_FILL, 0, 0, 0);
+
+	label_from = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label_from), 0, 0.5);
+	gtk_label_set_ellipsize(GTK_LABEL(label_from), PANGO_ELLIPSIZE_MIDDLE);
+	text = g_get_current_dir();
+	gtk_label_set_markup(GTK_LABEL(label_from), text);
+	g_free(text);
+	gtk_table_attach_defaults(GTK_TABLE(table), label_from, 1, 2, 0, 1);
+
+	label = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+	text = g_markup_printf_escaped("<b>%s</b>", _("To:"));
+	gtk_label_set_markup(GTK_LABEL(label), text);
+	g_free(text);
+	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+							GTK_FILL, 0, 0, 0);
+
+	label = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+	gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
+	gtk_label_set_text(GTK_LABEL(label), device_name);
+	gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
+
+	progress = gtk_progress_bar_new();
+	gtk_progress_bar_set_ellipsize(GTK_PROGRESS_BAR(progress),
+							PANGO_ELLIPSIZE_END);
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),
+							_("Connecting..."));
+	gtk_box_pack_start_defaults(GTK_BOX(vbox), progress);
+
+	label_status = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label_status), 0, 0.5);
+	gtk_label_set_ellipsize(GTK_LABEL(label_status), PANGO_ELLIPSIZE_END);
+	gtk_box_pack_start(GTK_BOX(vbox), label_status, TRUE, TRUE, 2);
+
+	g_signal_connect(G_OBJECT(dialog), "response",
+				G_CALLBACK(response_callback), NULL);
+
+	gtk_widget_show_all(dialog);
+}
+
+static void finish_sending(DBusGProxy *proxy)
+{
+	gtk_label_set_markup(GTK_LABEL(label_status), NULL);
+
+	dbus_g_proxy_call(proxy, "Disconnect", NULL, G_TYPE_INVALID,
+							G_TYPE_INVALID);
+
+	gtk_widget_destroy(dialog);
+
+	gtk_main_quit();
+}
+
+static void send_file(DBusGProxy *proxy)
+{
+	gchar *filename = option_files[file_index];
+	GError *error = NULL;
+	gchar *basename, *text, *markup;
+
+	if (file_index > file_count - 1) {
+		finish_sending(proxy);
+		return;
+	}
+
+	if (filename[0] == '\0') {
+		file_index++;
+		send_file(proxy);
+		return;
+	}
+
+	text = g_path_get_dirname(filename);
+	gtk_label_set_markup(GTK_LABEL(label_from), text);
+	g_free(text);
+
+	basename = g_path_get_basename(filename);
+	text = g_strdup_printf(_("Sending %s"), basename);
+	g_free(basename);
+	markup = g_markup_printf_escaped("<i>%s</i>", text);
+	gtk_label_set_markup(GTK_LABEL(label_status), markup);
+	g_free(markup);
+	g_free(text);
+
+	text = g_strdup_printf(_("Sending file %d of %d"),
+						file_index + 1, file_count);
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), text);
+	g_free(text);
+
+	dbus_g_proxy_call(proxy, "SendFile", &error,
+				G_TYPE_STRING, filename, G_TYPE_INVALID,
+							G_TYPE_INVALID);
+
+	if (error != NULL) {
+		g_printerr("Sending of file %s failed: %s\n", filename,
+							error->message);
+		g_error_free(error);
+		gtk_main_quit();
+	}
+}
+
+static void transfer_started(DBusGProxy *proxy, gchar *a, gchar *b,
+					guint64 size, gpointer user_data)
+{
+	current_size = size;
+
+	last_update = get_system_time();
+}
+
+static void transfer_progress(DBusGProxy *proxy,
+					guint64 bytes, gpointer user_data)
+{
+	gint64 current_time;
+	gint elapsed_time;
+	gint remaining_time;
+	gint transfer_rate;
+	guint64 current_sent;
+	gdouble fraction;
+	gchar *time, *rate, *file, *text;
+
+	current_sent = total_sent + bytes;
+	fraction = (gdouble) current_sent / (gdouble) total_size;
+	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction);
+
+	current_time = get_system_time();
+	elapsed_time = (current_time - first_update) / 1000000;
+
+	if (current_time < last_update + 1000000)
+		return;
+
+	last_update = current_time;
+
+	if (elapsed_time == 0)
+		return;
+
+	transfer_rate = current_sent / elapsed_time;
+
+	if (transfer_rate == 0)
+		return;
+
+	remaining_time = (total_size - current_sent) / transfer_rate;
+
+	time = format_time(remaining_time);
+
+	if (transfer_rate >= 3000)
+		rate = g_strdup_printf(_("%d KB/s"), transfer_rate / 1000);
+	else
+		rate = g_strdup_printf(_("%d B/s"), transfer_rate);
+
+	file = g_strdup_printf(_("Sending file %d of %d"),
+						file_index + 1, file_count);
+	text = g_strdup_printf("%s (%s, %s)", file, rate, time);
+	g_free(file);
+	g_free(rate);
+	g_free(time);
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), text);
+	g_free(text);
+}
+
+static void transfer_completed(DBusGProxy *proxy, gpointer user_data)
+{
+	total_sent += current_size;
+
+	file_index++;
+
+	send_file(proxy);
+}
+
+static void transfer_cancelled(DBusGProxy *proxy, gpointer user_data)
+{
+	transfer_completed(proxy, user_data);
+}
+
+static void error_occurred(DBusGProxy *proxy, const gchar *name,
+				const gchar *message, gpointer user_data)
+{
+	gchar *text;
+
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),
+						_("Error Occurred"));
+
+	text = g_strdup_printf("<span foreground=\"red\">%s</span>", message);
+	gtk_label_set_markup(GTK_LABEL(label_status), text);
+	g_free(text);
+
+	gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+						GTK_RESPONSE_CLOSE, TRUE);
+}
+
+static void session_connected(DBusGProxy *proxy, gpointer user_data)
+{
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), NULL);
+
+	first_update = get_system_time();
+
+	send_file(proxy);
+}
+
+#define OPENOBEX_CONNECTION_FAILED "org.openobex.Error.ConnectionAttemptFailed"
+
+static gchar *get_error_message(GError *error)
+{
+	char *message;
+	const gchar *name;
+
+	if (error == NULL)
+		return g_strdup(_("An unknown error occured"));
+
+	if (error->code != DBUS_GERROR_REMOTE_EXCEPTION) {
+		message = g_strdup(error->message);
+		goto done;
+	}
+
+	name = dbus_g_error_get_name(error);
+	if (g_str_equal(name, OPENOBEX_CONNECTION_FAILED) == TRUE &&
+					is_palm_device(option_device)) {
+		message = g_strdup(_("Make sure that remote device "
+					"is switched on and that it "
+					"accepts Bluetooth connections"));
+		goto done;
+	}
+
+	message = g_strdup(error->message);
+
+done:
+	g_error_free(error);
+
+	return message;
+}
+
+static void create_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	GError *error = NULL;
+	const gchar *path = NULL;
+
+	if (dbus_g_proxy_end_call(proxy, call, &error,
+					DBUS_TYPE_G_OBJECT_PATH, &path,
+						G_TYPE_INVALID) == FALSE) {
+		gchar *message;
+
+		message = get_error_message(error);
+		error_occurred(proxy, NULL, message, NULL);
+		g_free(message);
+
+		return;
+	}
+
+	proxy = dbus_g_proxy_new_for_name(conn, "org.openobex",
+						path, "org.openobex.Session");
+
+	dbus_g_proxy_add_signal(proxy, "Connected", G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(proxy, "Connected",
+				G_CALLBACK(session_connected), NULL, NULL);
+
+	dbus_g_proxy_add_signal(proxy, "ErrorOccurred",
+				G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(proxy, "ErrorOccurred",
+				G_CALLBACK(error_occurred), NULL, NULL);
+
+	dbus_g_proxy_add_signal(proxy, "Cancelled", G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(proxy, "Cancelled",
+				G_CALLBACK(transfer_cancelled), NULL, NULL);
+
+	dbus_g_proxy_add_signal(proxy, "TransferStarted", G_TYPE_STRING,
+				G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(proxy, "TransferStarted",
+				G_CALLBACK(transfer_started), NULL, NULL);
+
+	dbus_g_proxy_add_signal(proxy, "TransferProgress",
+						G_TYPE_UINT64, G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(proxy, "TransferProgress",
+				G_CALLBACK(transfer_progress), NULL, NULL);
+
+	dbus_g_proxy_add_signal(proxy, "TransferCompleted", G_TYPE_INVALID);
+
+	dbus_g_proxy_connect_signal(proxy, "TransferCompleted",
+				G_CALLBACK(transfer_completed), NULL, NULL);
+
+	dbus_g_proxy_call(proxy, "Connect", NULL, G_TYPE_INVALID,
+							G_TYPE_INVALID);
+}
+
+static gchar *get_name(DBusGProxy *device)
+{
+	GHashTable *hash;
+
+	if (dbus_g_proxy_call(device, "GetProperties", NULL,
+			G_TYPE_INVALID, dbus_g_type_get_map("GHashTable",
+						G_TYPE_STRING, G_TYPE_VALUE),
+					&hash, G_TYPE_INVALID) != FALSE) {
+		GValue *value;
+		gchar *name;
+
+		value = g_hash_table_lookup(hash, "Name");
+		name = value ? g_value_dup_string(value) : NULL;
+		g_hash_table_destroy(hash);
+
+		return name;
+	}
+
+	return NULL;
+}
+
+static gchar *get_device_name(const gchar *address)
+{
+	DBusGConnection *connection;
+	DBusGProxy *manager;
+	GPtrArray *adapters;
+	gchar *name;
+	guint i;
+
+	name = NULL;
+
+	connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL);
+	if (connection == NULL)
+		return name;
+
+	manager = dbus_g_proxy_new_for_name(connection, "org.bluez",
+						"/", "org.bluez.Manager");
+	if (manager == NULL) {
+		dbus_g_connection_unref(connection);
+		return name;
+	}
+
+	if (dbus_g_proxy_call(manager, "ListAdapters", NULL,
+			G_TYPE_INVALID, dbus_g_type_get_collection("GPtrArray",
+						DBUS_TYPE_G_OBJECT_PATH),
+					&adapters, G_TYPE_INVALID) == FALSE) {
+		g_object_unref(manager);
+		dbus_g_connection_unref(connection);
+		return name;
+	}
+
+	for (i = 0; i < adapters->len && name == NULL; i++) {
+		DBusGProxy *adapter;
+		char *device_path;
+
+		adapter = dbus_g_proxy_new_for_name(connection, "org.bluez",
+						g_ptr_array_index(adapters, i),
+							"org.bluez.Adapter");
+
+		if (dbus_g_proxy_call(adapter, "FindDevice", NULL,
+					G_TYPE_STRING, address, G_TYPE_INVALID,
+					DBUS_TYPE_G_OBJECT_PATH, &device_path,
+						G_TYPE_INVALID) == TRUE) {
+			DBusGProxy *device;
+			device = dbus_g_proxy_new_for_name(connection,
+						"org.bluez", device_path,
+							"org.bluez.Device");
+			name = get_name(device);
+			g_object_unref(device);
+			break;
+		}
+
+		g_object_unref(adapter);
+	}
+
+	g_ptr_array_free(adapters, TRUE);
+	g_object_unref(manager);
+
+	dbus_g_connection_unref(connection);
+
+	return name;
+}
+
+static gboolean request_callback(DBusGMethodInvocation *context,
+				DBusGProxy *transfer, gpointer user_data)
+{
+	gchar *filename = option_files[file_index];
+	gchar *basename, *text, *markup;
+	GHashTable *hash;
+
+	if (dbus_g_proxy_call(transfer,
+				"GetProperties", NULL, G_TYPE_INVALID,
+				dbus_g_type_get_map("GHashTable",
+						G_TYPE_STRING, G_TYPE_VALUE),
+					&hash, G_TYPE_INVALID) == TRUE) {
+		GValue *value;
+
+		value = g_hash_table_lookup(hash, "Size");
+		if (value) {
+			current_size = g_value_get_uint64(value);
+
+			last_update = get_system_time();
+		}
+
+		g_hash_table_destroy(hash);
+	}
+
+	text = g_path_get_dirname(filename);
+	gtk_label_set_markup(GTK_LABEL(label_from), text);
+	g_free(text);
+
+	basename = g_path_get_basename(filename);
+	text = g_strdup_printf(_("Sending %s"), basename);
+	g_free(basename);
+	markup = g_markup_printf_escaped("<i>%s</i>", text);
+	gtk_label_set_markup(GTK_LABEL(label_status), markup);
+	g_free(markup);
+	g_free(text);
+
+	text = g_strdup_printf(_("Sending file %d of %d"),
+						file_index + 1, file_count);
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), text);
+	g_free(text);
+
+	dbus_g_method_return(context, "");
+
+	return TRUE;
+}
+
+static gboolean progress_callback(DBusGMethodInvocation *context,
+				DBusGProxy *transfer, guint64 transferred,
+							gpointer user_data)
+{
+	transfer_progress(NULL, transferred, NULL);
+
+	dbus_g_method_return(context);
+
+	return TRUE;
+}
+
+static gboolean complete_callback(DBusGMethodInvocation *context,
+				DBusGProxy *transfer, gpointer user_data)
+{
+	total_sent += current_size;
+
+	file_index++;
+
+	dbus_g_method_return(context);
+
+	return TRUE;
+}
+
+static gboolean release_callback(DBusGMethodInvocation *context,
+							gpointer user_data)
+{
+	dbus_g_method_return(context);
+
+	gtk_label_set_markup(GTK_LABEL(label_status), NULL);
+
+	gtk_widget_destroy(dialog);
+
+	gtk_main_quit();
+
+	return TRUE;
+}
+
+static void send_notify(DBusGProxy *proxy,
+				DBusGProxyCall *call, void *user_data)
+{
+	GError *error = NULL;
+
+	if (dbus_g_proxy_end_call(proxy, call, &error,
+						G_TYPE_INVALID) == FALSE) {
+		gchar *text, *message;
+
+		message = get_error_message(error);
+
+		text = g_strdup_printf("<span foreground=\"red\">%s</span>",
+								message);
+		gtk_label_set_markup(GTK_LABEL(label_status), text);
+		g_free(text);
+
+		g_free(message);
+
+		gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
+						GTK_RESPONSE_CLOSE, TRUE);
+		return;
+	}
+
+	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), NULL);
+
+	first_update = get_system_time();
+}
+
+static void value_free(GValue *value)
+{
+	g_value_unset(value);
+	g_free(value);
+}
+
+static void name_owner_changed(DBusGProxy *proxy, const char *name,
+			const char *prev, const char *new, gpointer user_data)
+{
+	if (g_str_equal(name, "org.openobex") == TRUE && *new == '\0')
+		gtk_main_quit();
+}
+
+static GOptionEntry options[] = {
+	{ "device", 0, 0, G_OPTION_ARG_STRING, &option_device,
+				N_("Remote device to use"), "ADDRESS" },
+	{ "dest", 0, G_OPTION_FLAG_HIDDEN,
+			G_OPTION_ARG_STRING, &option_device, NULL, NULL },
+	{ G_OPTION_REMAINING, 0, 0,
+			G_OPTION_ARG_FILENAME_ARRAY, &option_files },
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	DBusGProxy *proxy;
+	GError *error = NULL;
+	int i;
+
+	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+	textdomain(GETTEXT_PACKAGE);
+
+	error = NULL;
+
+	if (gtk_init_with_args(&argc, &argv, "[FILE...]",
+				options, GETTEXT_PACKAGE, &error) == FALSE) {
+		if (error != NULL) {
+			g_printerr("%s\n", error->message);
+			g_error_free(error);
+		} else
+			g_printerr("An unknown error occurred\n");
+
+		gtk_exit(1);
+	}
+
+	gtk_window_set_default_icon_name("bluetooth");
+
+	if (option_files == NULL) {
+		option_files = show_select_dialog();
+		if (option_files == NULL)
+			gtk_exit(1);
+	}
+
+	if (option_device == NULL) {
+		option_device = show_browse_dialog();
+		if (option_device == NULL) {
+			g_strfreev(option_files);
+			gtk_exit(1);
+		}
+	}
+
+	file_count = g_strv_length(option_files);
+
+	for (i = 0; i < file_count; i++) {
+		gchar *filename;
+		struct stat st;
+
+		filename = filename_to_path(option_files[i]);
+
+		if (filename != NULL) {
+			g_free(option_files[i]);
+			option_files[i] = filename;
+		}
+
+		if (g_file_test(option_files[i],
+					G_FILE_TEST_IS_REGULAR) == FALSE) {
+			option_files[i][0] = '\0';
+			continue;
+		}
+
+		if (g_stat(option_files[i], &st) < 0)
+			option_files[i][0] = '\0';
+		else
+			total_size += st.st_size;
+	}
+
+	conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+	if (conn == NULL) {
+		if (error != NULL) {
+			g_printerr("Connecting to session bus failed: %s\n",
+							error->message);
+			g_error_free(error);
+		} else
+			g_print("An unknown error occured\n");
+
+		gtk_exit(1);
+	}
+
+	dbus_g_object_register_marshaller(marshal_VOID__STRING_STRING,
+					G_TYPE_NONE, G_TYPE_STRING,
+					G_TYPE_STRING, G_TYPE_INVALID);
+
+	dbus_g_object_register_marshaller(marshal_VOID__STRING_STRING_UINT64,
+				G_TYPE_NONE, G_TYPE_STRING,
+				G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INVALID);
+
+	dbus_g_object_register_marshaller(marshal_VOID__UINT64,
+				G_TYPE_NONE, G_TYPE_UINT64, G_TYPE_INVALID);
+
+	device_name = get_device_name(option_device);
+	if (device_name == NULL)
+		device_name = g_strdup(option_device);
+
+	create_window();
+
+	proxy = dbus_g_proxy_new_for_name_owner(conn, "org.openobex.client",
+					"/", "org.openobex.Client", NULL);
+
+	if (proxy == NULL) {
+		proxy = dbus_g_proxy_new_for_name(conn, "org.openobex",
+				"/org/openobex", "org.openobex.Manager");
+
+		dbus_g_proxy_add_signal(proxy, "NameOwnerChanged",
+					G_TYPE_STRING, G_TYPE_STRING,
+					G_TYPE_STRING, G_TYPE_INVALID);
+
+		dbus_g_proxy_connect_signal(proxy, "NameOwnerChanged",
+				G_CALLBACK(name_owner_changed), NULL, NULL);
+
+		dbus_g_proxy_begin_call(proxy, "CreateBluetoothSession",
+					create_notify, NULL, NULL,
+					G_TYPE_STRING, option_device,
+					G_TYPE_STRING, "00:00:00:00:00:00",
+					G_TYPE_STRING, "opp", G_TYPE_INVALID);
+	} else {
+		GHashTable *hash = NULL;
+		GValue *value;
+		ObexAgent *agent;
+
+		agent = obex_agent_new();
+
+		obex_agent_set_release_func(agent, release_callback, NULL);
+		obex_agent_set_request_func(agent, request_callback, NULL);
+		obex_agent_set_progress_func(agent, progress_callback, NULL);
+		obex_agent_set_complete_func(agent, complete_callback, NULL);
+
+		obex_agent_setup(agent, AGENT_PATH);
+
+		hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+					g_free, (GDestroyNotify) value_free);
+
+		value = g_new0(GValue, 1);
+		g_value_init(value, G_TYPE_STRING);
+		g_value_set_string(value, option_device);
+		g_hash_table_insert(hash, g_strdup("Destination"), value);
+
+		dbus_g_proxy_begin_call(proxy, "SendFiles",
+				send_notify, NULL, NULL,
+				dbus_g_type_get_map("GHashTable",
+					G_TYPE_STRING, G_TYPE_VALUE), hash,
+				G_TYPE_STRV, option_files,
+				DBUS_TYPE_G_OBJECT_PATH, AGENT_PATH,
+							G_TYPE_INVALID);
+	}
+
+	gtk_main();
+
+	g_object_unref(proxy);
+
+	dbus_g_connection_unref(conn);
+
+	g_free(device_name);
+
+	g_strfreev(option_files);
+	g_free(option_device);
+
+	return 0;
+}

Added: trunk/wizard/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/wizard/Makefile.am	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,17 @@
+
+bin_PROGRAMS = bluetooth-wizard
+
+bluetooth_wizard_SOURCES = main.c
+
+bluetooth_wizard_LDADD = $(top_builddir)/common/libcommon.a \
+						@GTK_LIBS@ @DBUS_LIBS@
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GTK_CFLAGS@
+
+INCLUDES = -I$(top_srcdir)/common
+
+man_MANS = bluetooth-wizard.1
+
+EXTRA_DIST = $(man_MANS)
+
+MAINTAINERCLEANFILES = Makefile.in

Added: trunk/wizard/bluetooth-wizard.1
==============================================================================
--- (empty file)
+++ trunk/wizard/bluetooth-wizard.1	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,29 @@
+.TH BLUETOOTH-WIZARD 1 "Oct 4, 2006" "bluez-gnome" "Linux User's Manual"
+.SH NAME
+bluetooth-wizard - GTK wizard for setting up devices with the Linux Bluetooth stack
+.SH SYNOPSIS
+.B bluetooth-wizard
+.SH DESCRIPTION
+.I bluetooth-wizard
+will display a wizard for setting up Bluetooth devices.
+.I bluetooth-wizard
+is part of bluez-gnome, see also http://www.bluez.org
+.SH OPTIONS
+.I bluetooth-wizard
+takes no options
+.SH AUTHOR
+Marcel Holtmann <marcel holtmann org>
+.SH LICENSE
+bluetooth-wizard is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Added: trunk/wizard/main.c
==============================================================================
--- (empty file)
+++ trunk/wizard/main.c	Wed Feb 25 14:35:27 2009
@@ -0,0 +1,573 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2008  Marcel Holtmann <marcel holtmann org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <dbus/dbus-glib.h>
+
+#include <bluetooth-instance.h>
+#include <bluetooth-client.h>
+#include <bluetooth-agent.h>
+
+#include "helper.h"
+
+#define AGENT_PATH "/org/bluez/agent/wizard"
+
+static BluetoothClient *client;
+static BluetoothAgent *agent;
+
+static gchar *target_address = NULL;
+static gchar *target_name = NULL;
+static gchar *target_pincode = NULL;
+static guint target_type = BLUETOOTH_TYPE_ANY;
+static gboolean target_ssp = FALSE;
+
+static GtkWidget *page_search = NULL;
+static GtkWidget *page_setup = NULL;
+static GtkWidget *page_summary = NULL;
+
+static GtkWidget *label_setup = NULL;
+static GtkWidget *label_passkey = NULL;
+
+static GtkTreeSelection *search_selection = NULL;
+
+static gboolean pincode_callback(DBusGMethodInvocation *context,
+					DBusGProxy *device, gpointer user_data)
+{
+	const char *pincode = target_pincode;
+	gchar *text;
+
+	/* Apple Wireless and Mighty Mouse */
+	if (target_type == BLUETOOTH_TYPE_MOUSE && 
+				(g_str_has_prefix(target_address,
+							"00:0A:95:") == TRUE ||
+				g_str_has_prefix(target_address,
+							"00:14:51:") == TRUE))
+		pincode = "0000";
+
+	/* Most headsets are using 0000 as pincode */
+	if (target_type == BLUETOOTH_TYPE_HEADSET ||
+				target_type == BLUETOOTH_TYPE_HEADPHONE)
+		pincode = "0000";
+
+	/* Most GPS devices are using 0000 as pincode */
+	if (g_str_has_prefix(target_address, "00:0D:B5") == TRUE &&
+				(g_str_equal(target_name,
+					"TomTom Wireless GPS MkII") == TRUE ||
+				g_str_equal(target_name,
+						"GPS-GW-005") == TRUE))
+		pincode = "0000";
+
+	text = g_strdup_printf(_("Please enter the following PIN code: %s"),
+								pincode);
+	gtk_label_set_markup(GTK_LABEL(label_passkey), text);
+	g_free(text);
+
+	dbus_g_method_return(context, pincode);
+
+	return TRUE;
+}
+
+static gboolean display_callback(DBusGMethodInvocation *context,
+				DBusGProxy *device, guint passkey,
+					guint entered, gpointer user_data)
+{
+	gchar *text, *done, *code;
+
+	code = g_strdup_printf("%d", passkey);
+	done = g_strnfill(entered, '*');
+
+	text = g_strdup_printf(_("Please enter the following passkey: %s%s"),
+							done, code + entered);
+	gtk_label_set_markup(GTK_LABEL(label_passkey), text);
+	g_free(text);
+
+	g_free(done);
+	g_free(code);
+
+	target_ssp = TRUE;
+
+	dbus_g_method_return(context);
+
+	return TRUE;
+}
+
+static gboolean cancel_callback(DBusGMethodInvocation *context,
+							gpointer user_data)
+{
+	gchar *text;
+
+	if (target_ssp == FALSE)
+		text = g_strdup_printf(_("Pairing with %s canceled"),
+								target_name);
+	else
+		text = g_strdup_printf(_("Pairing with %s finished"),
+								target_name);
+
+	gtk_label_set_markup(GTK_LABEL(label_setup), text);
+
+	g_free(text);
+
+	gtk_label_set_markup(GTK_LABEL(label_passkey), NULL);
+
+	dbus_g_method_return(context);
+
+	return TRUE;
+}
+
+static void connect_callback(gpointer user_data)
+{
+}
+
+static void create_callback(const char *path, gpointer user_data)
+{
+	GtkAssistant *assistant = user_data;
+	gboolean complete = FALSE;
+	gchar *text;
+
+	if (path != NULL) {
+		gint page;
+
+		text = g_strdup_printf(_("Successfully paired with %s"),
+								target_name);
+
+		page = gtk_assistant_get_current_page(assistant);
+		if (page > 0)
+			gtk_assistant_set_current_page(assistant, page + 1);
+
+		if (target_type == BLUETOOTH_TYPE_KEYBOARD ||
+					target_type == BLUETOOTH_TYPE_MOUSE) {
+			bluetooth_client_set_trusted(client, path, TRUE);
+			bluetooth_client_connect_input(client, path,
+						connect_callback, assistant);
+		}
+
+		complete = TRUE;
+	} else
+		text = g_strdup_printf(_("Pairing with %s failed"),
+								target_name);
+
+	gtk_label_set_markup(GTK_LABEL(label_setup), text);
+	gtk_label_set_markup(GTK_LABEL(label_passkey), NULL);
+
+	g_free(text);
+
+	gtk_assistant_set_page_complete(assistant, page_setup, complete);
+}
+
+static void close_callback(GtkWidget *assistant, gpointer data)
+{
+	gtk_widget_destroy(assistant);
+
+	gtk_main_quit();
+}
+
+static void prepare_callback(GtkWidget *assistant,
+					GtkWidget *page, gpointer data)
+{
+	gboolean complete = TRUE;
+	const char *path = AGENT_PATH;
+
+	if (page == page_search) {
+		complete = gtk_tree_selection_get_selected(search_selection,
+								NULL, NULL);
+		bluetooth_client_start_discovery(client);
+	} else
+		bluetooth_client_stop_discovery(client);
+
+	if (page == page_setup) {
+		GtkTreeModel *model;
+		GtkTreeIter iter;
+		gchar *text, *markup, *address, *name;
+		guint type;
+
+		/* Get the info about the device now,
+		 * we can't get here without a valid selection */
+		if (gtk_tree_selection_get_selected(search_selection,
+						&model, &iter) == FALSE)
+			g_assert_not_reached();
+
+		gtk_tree_model_get(model, &iter,
+					BLUETOOTH_COLUMN_ADDRESS, &address,
+					BLUETOOTH_COLUMN_ALIAS, &name,
+					BLUETOOTH_COLUMN_TYPE, &type, -1);
+
+		g_free(target_address);
+		target_address = address;
+
+		g_free(target_name);
+		target_name = name;
+
+		target_type = type;
+
+		text = g_strdup_printf(_("Connecting to %s now ..."),
+								target_name);
+		markup = g_strdup_printf("%s\n\n", text);
+		g_free(text);
+
+		gtk_label_set_markup(GTK_LABEL(label_setup), markup);
+
+		g_free(markup);
+
+		complete = FALSE;
+
+		g_object_ref(agent);
+
+		if (target_type == BLUETOOTH_TYPE_MOUSE)
+			path = NULL;
+
+		/* Sony PlayStation 3 Remote Control */
+		if (g_str_equal(target_name, "BD Remote Control") == TRUE &&
+					(g_str_has_prefix(target_address,
+							"00:19:C1:") == TRUE ||
+					g_str_has_prefix(target_address,
+							"00:1E:3D:") == TRUE))
+			path = NULL;
+
+		bluetooth_client_create_device(client, target_address,
+					path, create_callback, assistant);
+	}
+
+	gtk_assistant_set_page_complete(GTK_ASSISTANT(assistant),
+							page, complete);
+}
+
+static GtkWidget *create_vbox(GtkWidget *assistant, GtkAssistantPageType type,
+				const gchar *title, const gchar *section)
+{
+	GtkWidget *vbox;
+	GtkWidget *label;
+	GdkPixbuf *pixbuf;
+	gchar *str;
+
+	vbox = gtk_vbox_new(FALSE, 6);
+
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), 24);
+	gtk_assistant_append_page(GTK_ASSISTANT(assistant), vbox);
+	gtk_assistant_set_page_type(GTK_ASSISTANT(assistant), vbox, type);
+	str = g_strdup_printf(" %s", title);
+	gtk_assistant_set_page_title(GTK_ASSISTANT(assistant), vbox, str);
+	g_free(str);
+
+	//pixbuf = gtk_widget_render_icon(GTK_WIDGET(assistant),
+	//		GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG, NULL);
+
+	pixbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
+						"bluetooth", 32, 0, NULL);
+	gtk_assistant_set_page_header_image(GTK_ASSISTANT(assistant),
+								vbox, pixbuf);
+	g_object_unref(pixbuf);
+
+	if (section) {
+		label = gtk_label_new(NULL);
+		str = g_strdup_printf("<b>%s</b>\n", section);
+		gtk_label_set_markup(GTK_LABEL(label), str);
+		g_free(str);
+		gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	}
+
+	return vbox;
+}
+
+static void create_intro(GtkWidget *assistant)
+{
+	GtkWidget *vbox;
+	GtkWidget *label;
+	GtkWidget *combo;
+	GtkTreeModel *model;
+	GtkCellRenderer *renderer;
+
+	vbox = create_vbox(assistant, GTK_ASSISTANT_PAGE_INTRO,
+			_("Introduction"),
+			_("Welcome to the Bluetooth device setup wizard"));
+
+	label = gtk_label_new(NULL);
+	gtk_label_set_markup(GTK_LABEL(label), _("The device wizard will "
+				"walk you through the process of configuring "
+				"Bluetooth enabled devices for use with this "
+				"computer.\n\n"));
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_widget_set_size_request(GTK_WIDGET(label), 380, -1);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
+
+	combo = gtk_combo_box_new();
+
+	model = bluetooth_client_get_adapter_model(client);
+	gtk_combo_box_set_model(GTK_COMBO_BOX(combo), model);
+	g_object_unref(model);
+
+	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
+	gtk_cell_layout_clear(GTK_CELL_LAYOUT(combo));
+
+	renderer = gtk_cell_renderer_text_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer,
+					"text", BLUETOOTH_COLUMN_NAME, NULL);
+
+	if (gtk_tree_model_iter_n_children(model, NULL) > 1)
+		gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
+}
+
+#if 0
+static void create_type(GtkWidget *assistant)
+{
+	GtkWidget *vbox;
+	GtkWidget *button;
+	GSList *group = NULL;
+
+	vbox = create_vbox(assistant, GTK_ASSISTANT_PAGE_CONTENT,
+			_("Device type"),
+			_("Select the type of device you want to setup"));
+
+	button = gtk_radio_button_new_with_label(group, _("Mouse"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	button = gtk_radio_button_new_with_label(group, _("Keyboard"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	button = gtk_radio_button_new_with_label(group, _("Mobile phone"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	button = gtk_radio_button_new_with_label(group, _("Printer"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	button = gtk_radio_button_new_with_label(group, _("Headset"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+
+	button = gtk_radio_button_new_with_label(group, _("Any device"));
+	group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+}
+#endif
+
+static void select_callback(GtkTreeSelection *selection, gpointer user_data)
+{
+	GtkAssistant *assistant = user_data;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
+	gboolean selected;
+
+	selected = gtk_tree_selection_get_selected(selection, &model, &iter);
+
+	if (selected == TRUE) {
+		gboolean paired = FALSE;
+
+		gtk_tree_model_get(model, &iter,
+				BLUETOOTH_COLUMN_PAIRED, &paired, -1);
+
+		if (paired == TRUE)
+			selected = FALSE;
+	}
+
+	gtk_assistant_set_page_complete(assistant, page_search, selected);
+}
+
+static gboolean device_filter(GtkTreeModel *model,
+					GtkTreeIter *iter, gpointer user_data)
+{
+	gboolean paired;
+
+	gtk_tree_model_get(model, iter, BLUETOOTH_COLUMN_PAIRED, &paired, -1);
+
+	return (paired == TRUE) ? FALSE : TRUE;
+}
+
+static void create_search(GtkWidget *assistant)
+{
+	GtkWidget *vbox;
+	GtkWidget *scrolled;
+	GtkWidget *tree;
+	GtkTreeModel *model;
+	GtkTreeModel *sorted;
+	GtkTreeSelection *selection;
+
+	vbox = create_vbox(assistant, GTK_ASSISTANT_PAGE_CONTENT,
+				_("Device search"),
+				_("Select the device you want to setup"));
+
+	scrolled = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
+							GTK_SHADOW_OUT);
+	gtk_container_add(GTK_CONTAINER(vbox), scrolled);
+
+	model = bluetooth_client_get_device_filter_model(client,
+					NULL, device_filter, NULL, NULL);
+	sorted = gtk_tree_model_sort_new_with_model(model);
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sorted),
+				BLUETOOTH_COLUMN_RSSI, GTK_SORT_DESCENDING);
+	tree = create_tree(sorted, TRUE);
+	g_object_unref(sorted);
+	g_object_unref(model);
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+	gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+	g_signal_connect(G_OBJECT(selection), "changed",
+				G_CALLBACK(select_callback), assistant);
+	search_selection = selection;
+
+	gtk_container_add(GTK_CONTAINER(scrolled), tree);
+
+	page_search = vbox;
+}
+
+static void create_setup(GtkWidget *assistant)
+{
+	GtkWidget *vbox;
+	GtkWidget *label;
+
+	vbox = create_vbox(assistant, GTK_ASSISTANT_PAGE_CONTENT,
+				_("Device setup"),
+				_("Setting up new device"));
+
+	label = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_widget_set_size_request(GTK_WIDGET(label), 380, -1);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
+	label_setup = label;
+
+	label = gtk_label_new(NULL);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
+	label_passkey = label;
+
+	page_setup = vbox;
+}
+
+static void create_summary(GtkWidget *assistant)
+{
+	GtkWidget *vbox;
+
+	vbox = create_vbox(assistant, GTK_ASSISTANT_PAGE_SUMMARY,
+				_("Summary"),
+				_("Succesfully configured new device"));
+
+	page_summary = vbox;
+}
+
+static GtkWidget *create_wizard(void)
+{
+	GtkWidget *assistant;
+
+	assistant = gtk_assistant_new();
+	gtk_window_set_title(GTK_WINDOW(assistant), _("Bluetooth Device Wizard"));
+	gtk_window_set_position(GTK_WINDOW(assistant), GTK_WIN_POS_CENTER);
+	gtk_window_set_default_size(GTK_WINDOW(assistant), 440, 440);
+
+	create_intro(assistant);
+	//create_type(assistant);
+	create_search(assistant);
+	create_setup(assistant);
+	create_summary(assistant);
+
+	g_signal_connect(G_OBJECT(assistant), "close",
+					G_CALLBACK(close_callback), NULL);
+	g_signal_connect(G_OBJECT(assistant), "cancel",
+					G_CALLBACK(close_callback), NULL);
+	g_signal_connect(G_OBJECT(assistant), "prepare",
+					G_CALLBACK(prepare_callback), NULL);
+
+	gtk_widget_show_all(assistant);
+
+	gtk_assistant_update_buttons_state(GTK_ASSISTANT(assistant));
+
+	return assistant;
+}
+
+static GOptionEntry options[] = {
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	BluetoothInstance *instance;
+	GtkWidget *window;
+	GError *error = NULL;
+
+	bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+	textdomain(GETTEXT_PACKAGE);
+
+	if (gtk_init_with_args(&argc, &argv, NULL,
+				options, GETTEXT_PACKAGE, &error) == FALSE) {
+		if (error) {
+			g_print("%s\n", error->message);
+			g_error_free(error);
+		} else
+			g_print("An unknown error occurred\n");
+
+		gtk_exit(1);
+	}
+
+	instance = bluetooth_instance_new("wizard");
+	if (instance == NULL)
+		gtk_exit(0);
+
+	gtk_window_set_default_icon_name("bluetooth");
+
+	target_pincode = g_strdup_printf("%d", g_random_int_range(1000, 9999));
+
+	client = bluetooth_client_new();
+
+	agent = bluetooth_agent_new();
+
+	bluetooth_agent_set_pincode_func(agent, pincode_callback, NULL);
+	bluetooth_agent_set_display_func(agent, display_callback, NULL);
+	bluetooth_agent_set_cancel_func(agent, cancel_callback, NULL);
+
+	bluetooth_agent_setup(agent, AGENT_PATH);
+
+	window = create_wizard();
+
+	bluetooth_instance_set_window(instance, GTK_WINDOW(window));
+
+	gtk_main();
+
+	g_object_unref(agent);
+
+	g_object_unref(client);
+
+	g_object_unref(instance);
+
+	return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]