[darcs-to-svn @ initial import]
authorXavier Claessens <xclaesse@src.gnome.org>
Wed, 25 Apr 2007 21:59:20 +0000 (21:59 +0000)
committerXavier Claessens <xclaesse@src.gnome.org>
Wed, 25 Apr 2007 21:59:20 +0000 (21:59 +0000)
svn path=/trunk/; revision=2

115 files changed:
AUTHORS [new file with mode: 0644]
CONTRIBUTORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
MAINTAINERS [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
accounts/Makefile.am [new file with mode: 0644]
accounts/empathy-accounts-main.c [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
autogen.sh [new file with mode: 0644]
chat/Makefile.am [new file with mode: 0644]
chat/empathy-chat-main.c [new file with mode: 0644]
chat/empathy-chat.chandler [new file with mode: 0644]
chat/org.gnome.Empathy.Chat.service.in [new file with mode: 0644]
configure.ac [new file with mode: 0644]
contact-list/Makefile.am [new file with mode: 0644]
contact-list/empathy-contact-list-main.c [new file with mode: 0644]
launcher/Makefile.am [new file with mode: 0644]
launcher/empathy-launcher.c [new file with mode: 0644]
libempathy-gtk/Makefile.am [new file with mode: 0644]
libempathy-gtk/empathy-accounts.glade [new file with mode: 0644]
libempathy-gtk/empathy-chat.glade [new file with mode: 0644]
libempathy-gtk/empathy.schemas.in [new file with mode: 0644]
libempathy-gtk/gossip-account-widget-generic.c [new file with mode: 0644]
libempathy-gtk/gossip-account-widget-generic.h [new file with mode: 0644]
libempathy-gtk/gossip-accounts-dialog.c [new file with mode: 0644]
libempathy-gtk/gossip-accounts-dialog.h [new file with mode: 0644]
libempathy-gtk/gossip-cell-renderer-expander.c [new file with mode: 0644]
libempathy-gtk/gossip-cell-renderer-expander.h [new file with mode: 0644]
libempathy-gtk/gossip-cell-renderer-text.c [new file with mode: 0644]
libempathy-gtk/gossip-cell-renderer-text.h [new file with mode: 0644]
libempathy-gtk/gossip-chat-manager.c [new file with mode: 0644]
libempathy-gtk/gossip-chat-manager.h [new file with mode: 0644]
libempathy-gtk/gossip-chat-manager.loT [new file with mode: 0644]
libempathy-gtk/gossip-chat-view.c [new file with mode: 0644]
libempathy-gtk/gossip-chat-view.h [new file with mode: 0644]
libempathy-gtk/gossip-chat-window.c [new file with mode: 0644]
libempathy-gtk/gossip-chat-window.h [new file with mode: 0644]
libempathy-gtk/gossip-chat.c [new file with mode: 0644]
libempathy-gtk/gossip-chat.h [new file with mode: 0644]
libempathy-gtk/gossip-contact-groups.c [new file with mode: 0644]
libempathy-gtk/gossip-contact-groups.dtd [new file with mode: 0644]
libempathy-gtk/gossip-contact-groups.h [new file with mode: 0644]
libempathy-gtk/gossip-contact-list.c [new file with mode: 0644]
libempathy-gtk/gossip-contact-list.h [new file with mode: 0644]
libempathy-gtk/gossip-preferences.c [new file with mode: 0644]
libempathy-gtk/gossip-preferences.h [new file with mode: 0644]
libempathy-gtk/gossip-private-chat.c [new file with mode: 0644]
libempathy-gtk/gossip-private-chat.h [new file with mode: 0644]
libempathy-gtk/gossip-profile-chooser.c [new file with mode: 0644]
libempathy-gtk/gossip-profile-chooser.h [new file with mode: 0644]
libempathy-gtk/gossip-spell.c [new file with mode: 0644]
libempathy-gtk/gossip-spell.h [new file with mode: 0644]
libempathy-gtk/gossip-stock.c [new file with mode: 0644]
libempathy-gtk/gossip-stock.h [new file with mode: 0644]
libempathy-gtk/gossip-theme-manager.c [new file with mode: 0644]
libempathy-gtk/gossip-theme-manager.h [new file with mode: 0644]
libempathy-gtk/gossip-ui-utils.c [new file with mode: 0644]
libempathy-gtk/gossip-ui-utils.h [new file with mode: 0644]
libempathy/Makefile.am [new file with mode: 0644]
libempathy/empathy-chandler.c [new file with mode: 0644]
libempathy/empathy-chandler.h [new file with mode: 0644]
libempathy/empathy-chandler.xml [new file with mode: 0644]
libempathy/empathy-contact-list.c [new file with mode: 0644]
libempathy/empathy-contact-list.h [new file with mode: 0644]
libempathy/empathy-contact-manager.c [new file with mode: 0644]
libempathy/empathy-contact-manager.h [new file with mode: 0644]
libempathy/empathy-marshal-main.c [new file with mode: 0644]
libempathy/empathy-marshal.list [new file with mode: 0644]
libempathy/empathy-session.c [new file with mode: 0644]
libempathy/empathy-session.h [new file with mode: 0644]
libempathy/empathy-tp-chat.c [new file with mode: 0644]
libempathy/empathy-tp-chat.h [new file with mode: 0644]
libempathy/gossip-avatar.c [new file with mode: 0644]
libempathy/gossip-avatar.h [new file with mode: 0644]
libempathy/gossip-conf.c [new file with mode: 0644]
libempathy/gossip-conf.h [new file with mode: 0644]
libempathy/gossip-contact.c [new file with mode: 0644]
libempathy/gossip-contact.h [new file with mode: 0644]
libempathy/gossip-debug.c [new file with mode: 0644]
libempathy/gossip-debug.h [new file with mode: 0644]
libempathy/gossip-message.c [new file with mode: 0644]
libempathy/gossip-message.h [new file with mode: 0644]
libempathy/gossip-paths.c [new file with mode: 0644]
libempathy/gossip-paths.h [new file with mode: 0644]
libempathy/gossip-presence.c [new file with mode: 0644]
libempathy/gossip-presence.h [new file with mode: 0644]
libempathy/gossip-telepathy-group.c [new file with mode: 0644]
libempathy/gossip-telepathy-group.h [new file with mode: 0644]
libempathy/gossip-time.c [new file with mode: 0644]
libempathy/gossip-time.h [new file with mode: 0644]
libempathy/gossip-utils.c [new file with mode: 0644]
libempathy/gossip-utils.h [new file with mode: 0644]
pixmaps/Makefile.am [new file with mode: 0644]
pixmaps/gossip-available.png [new file with mode: 0644]
pixmaps/gossip-away.png [new file with mode: 0644]
pixmaps/gossip-busy.png [new file with mode: 0644]
pixmaps/gossip-extended-away.png [new file with mode: 0644]
pixmaps/gossip-group-message.png [new file with mode: 0644]
pixmaps/gossip-message.png [new file with mode: 0644]
pixmaps/gossip-offline.png [new file with mode: 0644]
pixmaps/gossip-pending.png [new file with mode: 0644]
pixmaps/gossip-typing.png [new file with mode: 0644]
pixmaps/vcard_16.png [new file with mode: 0644]
pixmaps/vcard_48.png [new file with mode: 0644]
po/ChangeLog [new file with mode: 0644]
po/Makefile.in.in [new file with mode: 0644]
po/POTFILES.in [new file with mode: 0644]
profiles/Makefile.am [new file with mode: 0644]
profiles/gtalk.profile [new file with mode: 0644]
profiles/jabber.profile [new file with mode: 0644]
profiles/msn.profile [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..2847eaa
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+Mikael Hallendal <micke@imendio.com>
+Richard Hult <richard@imendio.com>
+Martyn Russell <martyn@gnome.org>
+Xavier Claessens <xclaesse@gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644 (file)
index 0000000..a9a8cd1
--- /dev/null
@@ -0,0 +1,17 @@
+Andreas Lööw
+Aurelien Naldi
+Bastien Nocera
+Christoffer Olsen
+Frederic Crozat
+Geert-Jan Van den Bogaerde
+Johan Hammar
+Jonatan Magnusson
+Jordi Mallach
+Kim Andersen
+Martyn Russell
+Mike Gratton
+Ross Burton
+Sjoerd Simons
+Thomas Reynolds
+Vincent Untz
+Xavier Claessens
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..86b8105
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,4 @@
+2006-03-16  Xavier Claessens  <xclaesse@gmail.com>
+
+       * Initial version
+
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644 (file)
index 0000000..422a3ed
--- /dev/null
@@ -0,0 +1 @@
+Xavier Claessens <xclaesse@gmail.com>
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..bf4cebf
--- /dev/null
@@ -0,0 +1,24 @@
+SUBDIRS = po profiles pixmaps libempathy libempathy-gtk launcher accounts contact-list chat
+
+DISTCHECK_CONFIGURE_FLAGS =    \
+       --disable-scrollkeeper
+
+INTLTOOL =                     \
+       intltool-extract.in     \
+       intltool-merge.in       \
+       intltool-update.in
+
+EXTRA_DIST =                   \
+        ChangeLog               \
+        README                  \
+       CONTRIBUTORS            \
+       $(INTLTOOL)
+
+DISTCLEANFILES =               \
+       intltool-extract        \
+       intltool-merge          \
+       intltool-update
+
+# Workaround broken scrollkeeper that doesn't remove its files on
+# uninstall.
+distuninstallcheck_listfiles = find . -type f -print | grep -v '^\./var/scrollkeeper'
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..ef16e2c
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+How to use empathy ?
+
+1) Install
+$ ./autogen
+$ make && make install
+
+2) Setup and enable some accounts
+$ empathy-accounts
+
+3) Get your contact list
+$ empathy-contact-list
+This will start MC and connect all enabled accounts. If you start a private chat
+or someone is saying something to you, empathy-chat will be started
+automagicaly and display a chat UI for your conversation.
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..9fe1f1c
--- /dev/null
+++ b/TODO
@@ -0,0 +1,20 @@
+Things you can do if you want to help:
+
+ - Find a good name, empathy is already used and gaim had legal problems with it.
+ - Rename all files and functions name to use the new name once we get a cool one.
+ - Porting gossip-account-widget-*.{c,h} from gossip project (Guillaume is already working on IRC widget).
+ - Make a EmpathyChatroom object inherited from EmpathyChat and adding support for topic, invite, members list, etc.
+ - Porting various UI widgets from gossip to libempathy-gtk for contact info, adding contact, personal info, etc.
+ - GtkWidget-ify gossip widgets imported in libempathy-gtk. Actually most window/dialog do not inherit from GtkWindow/GtkDialog...
+ - Set up the translation system and import po files from gossip? Or re-translate everything?
+ - Fix setting subscription for contacts in EmpathyContactList.
+ - Write a MC plugin to filter channels before dispatching them. For example we need a GtkStatusIcon that blink when an event arrives (text/voip/ft channel) and tells the MC to dispatch the channel only when the user clicked the icon. Like in gossip.
+ - Testing and Bugfixing.
+
+SoC projects:
+ - Adding VoIP support based on the patch proposed for gossip.
+ - Adding FileTransfer support.
+If you want to contribute you can ask for information at
+ - #telepathy on freenode
+ - Telepathy's mailing list.
diff --git a/accounts/Makefile.am b/accounts/Makefile.am
new file mode 100644 (file)
index 0000000..589cae3
--- /dev/null
@@ -0,0 +1,19 @@
+AM_CPPFLAGS =                                          \
+       -I$(top_srcdir)                                 \
+       -DPREFIX="\"$(prefix)"\"                        \
+       -DSYSCONFDIR=\""$(sysconfdir)"\"                \
+       -DDATADIR=\""$(datadir)"\"                      \
+       -DLIBDIR=\""$(libdir)"\"                        \
+       $(EMPATHY_CFLAGS)                               \
+       $(WARN_CFLAGS)
+
+bin_PROGRAMS = empathy-accounts
+
+empathy_accounts_SOURCES =                                                     \
+       empathy-accounts-main.c
+
+empathy_accounts_LDADD = \
+       $(top_builddir)/libempathy/libempathy.la                                \
+       $(top_builddir)/libempathy-gtk/libempathy-gtk.la                        \
+       $(EMPATHY_LIBS)
+
diff --git a/accounts/empathy-accounts-main.c b/accounts/empathy-accounts-main.c
new file mode 100644 (file)
index 0000000..ec30b83
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
+ *
+ * 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.
+ * 
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-session.h>
+#include <libempathy-gtk/gossip-accounts-dialog.h>
+
+static void
+destroy_cb (GtkWidget *dialog,
+           gpointer   user_data)
+{
+       empathy_session_finalize ();
+       gtk_main_quit ();
+}
+
+int
+main (int argc, char *argv[])
+{
+       GtkWidget *dialog;
+
+       gtk_init (&argc, &argv);
+
+       dialog = gossip_accounts_dialog_show ();
+
+       gtk_widget_show (dialog);
+       g_signal_connect (dialog, "destroy",
+                         G_CALLBACK (destroy_cb),
+                         NULL);
+
+       gtk_main ();
+
+       return EXIT_SUCCESS;
+}
+
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..3120d86
--- /dev/null
@@ -0,0 +1,65 @@
+dnl Turn on the additional warnings last, so -Werror doesn't affect other tests.
+
+AC_DEFUN([IDT_COMPILE_WARNINGS],[
+   if test -f $srcdir/autogen.sh; then
+       default_compile_warnings="error"
+    else
+       default_compile_warnings="no"
+    fi
+
+    AC_ARG_WITH(compile-warnings,
+                AS_HELP_STRING([--with-compile-warnings=@<:@no/yes/error@:>@],
+                               [Compiler warnings]),
+                [enable_compile_warnings="$withval"],
+                [enable_compile_warnings="$default_compile_warnings"])
+
+    warnCFLAGS=
+    if test "x$GCC" != xyes; then
+       enable_compile_warnings=no
+    fi
+
+    warning_flags=
+    realsave_CFLAGS="$CFLAGS"
+
+    case "$enable_compile_warnings" in
+    no)
+       warning_flags=
+       ;;
+    yes)
+       warning_flags="-Wall -Wunused -Wmissing-prototypes -Wmissing-declarations"
+       ;;
+    maximum|error)
+       warning_flags="-Wall -Wunused -Wchar-subscripts -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith"
+       CFLAGS="$warning_flags $CFLAGS"
+       for option in -Wno-sign-compare -Wno-pointer-sign; do
+               SAVE_CFLAGS="$CFLAGS"
+               CFLAGS="$CFLAGS $option"
+               AC_MSG_CHECKING([whether gcc understands $option])
+               AC_TRY_COMPILE([], [],
+                       has_option=yes,
+                       has_option=no,)
+               CFLAGS="$SAVE_CFLAGS"
+               AC_MSG_RESULT($has_option)
+               if test $has_option = yes; then
+                 warning_flags="$warning_flags $option"
+               fi
+               unset has_option
+               unset SAVE_CFLAGS
+       done
+       unset option
+       if test "$enable_compile_warnings" = "error" ; then
+           warning_flags="$warning_flags -Werror"
+       fi
+       ;;
+    *)
+       AC_MSG_ERROR(Unknown argument '$enable_compile_warnings' to --enable-compile-warnings)
+       ;;
+    esac
+    CFLAGS="$realsave_CFLAGS"
+    AC_MSG_CHECKING(what warning flags to pass to the C compiler)
+    AC_MSG_RESULT($warning_flags)
+
+    WARN_CFLAGS="$warning_flags"
+    AC_SUBST(WARN_CFLAGS)
+])
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100644 (file)
index 0000000..1b93b75
--- /dev/null
@@ -0,0 +1,174 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+: ${AUTOCONF=autoconf}
+: ${AUTOHEADER=autoheader}
+: ${AUTOMAKE=automake-1.9}
+: ${ACLOCAL=aclocal-1.9}
+: ${LIBTOOLIZE=libtoolize}
+: ${INTLTOOLIZE=intltoolize}
+: ${LIBTOOL=libtool}
+: ${GNOME_DOC_PREPARE=gnome-doc-prepare}
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+PROJECT="empathy"
+TEST_TYPE=-f
+CONFIGURE=configure.ac
+
+DIE=0
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+       echo
+       echo "You must have autoconf installed to compile $PROJECT."
+       echo "Download the appropriate package for your distribution,"
+       echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+       DIE=1
+}
+
+(grep "^AC_PROG_INTLTOOL" $srcdir/$CONFIGURE >/dev/null) && {
+  ($INTLTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+    echo
+    echo "You must have \`intltoolize' installed to compile $PROJECT."
+    echo "Get ftp://ftp.gnome.org/pub/GNOME/stable/sources/intltool/intltool-0.35.tar.gz"
+    echo "(or a newer version if it is available)"
+    DIE=1
+  }
+}
+
+(grep "^GNOME_DOC_INIT" $srcdir/$CONFIGURE >/dev/null) && {
+  ($GNOME_DOC_PREPARE --version) < /dev/null > /dev/null 2>&1 || {
+    echo
+    echo "You must have \`gnome-doc-prepare' installed to compile $PROJECT."
+    DIE=1
+  }
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+       echo
+       echo "You must have automake 1.9 installed to compile $PROJECT."
+       echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.9.6.tar.gz"
+       echo "(or a newer version of 1.9.x if it is available)"
+       DIE=1
+}
+
+(grep "^AM_PROG_LIBTOOL" $CONFIGURE >/dev/null) && {
+  ($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || {
+    echo
+    echo "**Error**: You must have \`libtool' installed to compile $PROJECT."
+    echo "Get ftp://ftp.gnu.org/pub/gnu/libtool/libtool-1.5.22.tar.gz"
+    echo "(or a newer version if it is available)"
+    DIE=1
+  }
+}
+
+if grep "^AM_[A-Z0-9_]\{1,\}_GETTEXT" "$CONFIGURE" >/dev/null; then
+  if grep "sed.*POTFILES" "$CONFIGURE" >/dev/null; then
+    GETTEXTIZE=""
+  else
+    if grep "^AM_GLIB_GNU_GETTEXT" "$CONFIGURE" >/dev/null; then
+      GETTEXTIZE="glib-gettextize"
+      GETTEXTIZE_URL="ftp://ftp.gtk.org/pub/gtk/v2.0/glib-2.0.0.tar.gz"
+    else
+      GETTEXTIZE="gettextize"
+      GETTEXTIZE_URL="ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz"
+    fi
+                                                                                                          
+    $GETTEXTIZE --version < /dev/null > /dev/null 2>&1
+    if test $? -ne 0; then
+      echo
+      echo "**Error**: You must have \`$GETTEXTIZE' installed to compile $PKG_NAME."
+      echo "Get $GETTEXTIZE_URL"
+      echo "(or a newer version if it is available)"
+      DIE=1
+    fi
+  fi
+fi
+
+if test "$DIE" -eq 1; then
+       exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+       echo "You must run this script in the top-level $PROJECT directory"
+       exit 1
+}
+
+#if test -z "$*"; then
+#      echo "I am going to run ./configure with no arguments - if you wish "
+#        echo "to pass any to it, please specify them on the $0 command line."
+#fi
+
+case $CC in
+*xlc | *xlc\ * | *lcc | *lcc\ *) am_opt=--include-deps;;
+esac
+
+for coin in .
+do 
+  dr=`dirname $coin`
+  if test -f $dr/NO-AUTO-GEN; then
+    echo skipping $dr -- flagged as no auto-gen
+  else
+    echo processing $dr
+    macrodirs= #`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin`
+    ( cd $dr
+      aclocalinclude="$ACLOCAL_FLAGS"
+      for k in $macrodirs; do
+       if test -d $k; then
+          aclocalinclude="$aclocalinclude -I $k"
+       ##else 
+       ##  echo "**Warning**: No such directory \`$k'.  Ignored."
+        fi
+      done
+      if grep "^AM_GLIB_GNU_GETTEXT" $CONFIGURE >/dev/null; then
+       if grep "sed.*POTFILES" $CONFIGURE >/dev/null; then
+         : do nothing -- we still have an old unmodified $CONFIGURE
+       else
+         echo "Creating $dr/aclocal.m4 ..."
+         test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
+         echo "Running glib-gettextize...  Ignore non-fatal messages."
+         echo "no" | glib-gettextize --force --copy
+         echo "Making $dr/aclocal.m4 writable ..."
+         test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+        fi
+      fi
+      if grep "^IT_PROG_INTLTOOL" $CONFIGURE >/dev/null; then
+        echo "Running intltoolize..."
+       intltoolize --copy --force --automake
+      fi
+      if grep "^GNOME_DOC_INIT" $CONFIGURE >/dev/null; then
+        echo "Running $GNOME_DOC_PREPARE..."
+       $GNOME_DOC_PREPARE --force --copy || exit 1
+      fi
+      if grep "^AM_PROG_LIBTOOL" $CONFIGURE >/dev/null; then
+       echo "Running $LIBTOOLIZE..."
+       $LIBTOOLIZE --force --copy
+      fi
+      echo "Running $ACLOCAL $aclocalinclude ..."
+      $ACLOCAL $aclocalinclude
+      if grep "^AM_CONFIG_HEADER" $CONFIGURE >/dev/null; then
+       echo "Running $AUTOHEADER..."
+       $AUTOHEADER
+      fi
+      echo "Running $AUTOMAKE --gnu $am_opt ..."
+      $AUTOMAKE --add-missing --gnu $am_opt
+      echo "Running $AUTOCONF ..."
+      $AUTOCONF
+    )
+  fi
+done
+
+conf_flags="--enable-maintainer-mode"
+
+cd "$ORIGDIR"
+
+if test x$NOCONFIGURE = x; then
+  echo Running $srcdir/configure $conf_flags "$@" ...
+  $srcdir/configure $conf_flags "$@" \
+  && echo Now type \`make\' to compile $PROJECT  || exit 1
+else
+  echo Skipping configure process.
+fi
diff --git a/chat/Makefile.am b/chat/Makefile.am
new file mode 100644 (file)
index 0000000..d8ee78a
--- /dev/null
@@ -0,0 +1,40 @@
+AM_CPPFLAGS =                                                  \
+       -I$(top_srcdir)                                         \
+       -DPREFIX="\"$(prefix)"\"                                \
+       -DSYSCONFDIR=\""$(sysconfdir)"\"                        \
+       -DDATADIR=\""$(datadir)"\"                              \
+       -DLIBDIR=\""$(libdir)"\"                                \
+       $(EMPATHY_CFLAGS)                                       \
+       $(WARN_CFLAGS)
+
+bin_PROGRAMS = empathy-chat
+
+empathy_chat_SOURCES =                                 \
+       empathy-chat-main.c
+
+empathy_chat_LDADD =                                           \
+       $(top_builddir)/libempathy/libempathy.la                \
+       $(top_builddir)/libempathy-gtk/libempathy-gtk.la        \
+       $(EMPATHY_LIBS)
+
+# Dbus service file
+servicedir = $(datadir)/dbus-1/services
+service_in_files = org.gnome.Empathy.Chat.service.in
+service_DATA = $(service_in_files:.service.in=.service)
+
+# Rule to make the service file with bindir expanded
+$(service_DATA): $(service_in_files) Makefile
+       @sed -e "s|\@bindir\@|$(bindir)|" $< > $@
+
+chandlerdir = $(datadir)/telepathy/managers
+chandler_DATA = empathy-chat.chandler
+
+
+EXTRA_DIST =                                   \
+       org.gnome.Empathy.Chat.service.in       \
+       $(chandler_DATA)
+
+BUILT_SOURCES =                                \
+       org.gnome.Empathy.Chat.service
+       
+CLEANFILES = $(BUILT_SOURCES)
diff --git a/chat/empathy-chat-main.c b/chat/empathy-chat-main.c
new file mode 100644 (file)
index 0000000..f279449
--- /dev/null
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
+ *
+ * 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.
+ * 
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <libtelepathy/tp-conn.h>
+#include <libtelepathy/tp-chan.h>
+#include <libmissioncontrol/mc-account.h>
+
+#include <libempathy/gossip-contact.h>
+#include <libempathy/empathy-chandler.h>
+#include <libempathy/empathy-session.h>
+#include <libempathy/empathy-contact-manager.h>
+#include <libempathy/empathy-contact-list.h>
+#include <libempathy-gtk/gossip-private-chat.h>
+#include <libempathy-gtk/gossip-stock.h>
+
+#define BUS_NAME "org.gnome.Empathy.Chat"
+#define OBJECT_PATH "/org/freedesktop/Telepathy/ChannelHandler"
+
+static void
+new_channel_cb (EmpathyChandler *chandler,
+               TpConn          *tp_conn,
+               TpChan          *tp_chan,
+               gpointer         user_data)
+{
+       if (tp_chan->handle_type == TP_HANDLE_TYPE_CONTACT) {
+               MissionControl        *mc;
+               McAccount             *account;
+               EmpathyContactManager *manager;
+               EmpathyContactList    *list;
+               GossipContact         *contact;
+               GossipPrivateChat     *chat;
+
+               /* We have a private chat channel */
+               mc = empathy_session_get_mission_control ();
+               account = mission_control_get_account_for_connection (mc, tp_conn, NULL);
+               manager = empathy_session_get_contact_manager ();
+               list = empathy_contact_manager_get_list (manager, account);
+               contact = empathy_contact_list_get_from_handle (list, tp_chan->handle);
+
+               chat = gossip_private_chat_new_with_channel (contact, tp_chan);
+               gossip_chat_present (GOSSIP_CHAT (chat));
+
+               g_object_unref (account);
+               g_object_unref (contact);
+               g_object_unref (chat);
+       }
+}
+
+int
+main (int argc, char *argv[])
+{
+       EmpathyChandler *chandler;
+
+       gtk_init (&argc, &argv);
+       /* FIXME: This is a horrible hack */
+       gossip_stock_init (gtk_window_new (GTK_WINDOW_TOPLEVEL));
+
+       chandler = empathy_chandler_new (BUS_NAME, OBJECT_PATH);
+
+       g_signal_connect (chandler, "new-channel",
+                         G_CALLBACK (new_channel_cb),
+                         NULL);
+
+       gtk_main ();
+
+       return EXIT_SUCCESS;
+}
+
diff --git a/chat/empathy-chat.chandler b/chat/empathy-chat.chandler
new file mode 100644 (file)
index 0000000..4cfe75b
--- /dev/null
@@ -0,0 +1,5 @@
+[ChannelHandler]
+BusName = org.gnome.Empathy.Chat
+ObjectPath = /org/freedesktop/Telepathy/ChannelHandler
+ChannelType = org.freedesktop.Telepathy.Channel.Type.Text
+
diff --git a/chat/org.gnome.Empathy.Chat.service.in b/chat/org.gnome.Empathy.Chat.service.in
new file mode 100644 (file)
index 0000000..6a794e4
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Empathy.Chat
+Exec=@bindir@/empathy-chat
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..42a1621
--- /dev/null
@@ -0,0 +1,75 @@
+AC_INIT(Empathy, 0.1)
+AC_PREREQ(2.59)
+AC_COPYRIGHT([Copyright (C) 2003-2007 Imendio AB])
+
+AM_CONFIG_HEADER(config.h)
+AM_INIT_AUTOMAKE(1.9 dist-bzip2 no-define)
+
+AM_MAINTAINER_MODE
+
+AC_ISC_POSIX
+AC_PROG_CC
+AC_HEADER_STDC
+
+AM_PROG_LIBTOOL
+AM_PATH_GLIB_2_0
+AC_PATH_XTRA
+
+AC_PATH_PROG(DBUS_BINDING_TOOL, dbus-binding-tool)
+AC_PATH_PROG(GCONFTOOL, gconftool-2)
+AM_GCONF_SOURCE_2
+
+IT_PROG_INTLTOOL([0.35.0])
+
+GLIB_REQUIRED=2.12.0
+GTK_REQUIRED=2.10.0
+GCONF_REQUIRED=1.2.0
+LIBGLADE_REQUIRED=2.0.0
+TELEPATHY_REQUIRED=0.0.51
+MISSION_CONTROL_REQUIRED=4.20
+
+GLIB_GENMARSHAL=`$PKG_CONFIG glib-2.0 --variable=glib_genmarshal`
+AC_SUBST(GLIB_GENMARSHAL)
+
+IDT_COMPILE_WARNINGS
+
+dnl -----------------------------------------------------------
+dnl Pkg-Config dependency checks
+dnl -----------------------------------------------------------
+
+PKG_CHECK_MODULES(EMPATHY,
+[
+   glib-2.0 >= $GLIB_REQUIRED
+   gobject-2.0
+   gtk+-2.0 >= $GTK_REQUIRED
+   gconf-2.0 >= $GCONF_REQUIRED
+   libglade-2.0 >= $LIBGLADE_REQUIRED
+   libgnomeui-2.0
+   libtelepathy >= $TELEPATHY_REQUIRED
+   libmissioncontrol >= $MISSION_CONTROL_REQUIRED
+])
+
+dnl -----------------------------------------------------------
+dnl Language Support
+dnl -----------------------------------------------------------
+GETTEXT_PACKAGE=empathy
+AC_SUBST(GETTEXT_PACKAGE)
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[Gettext package name])
+
+AM_GLIB_GNU_GETTEXT
+
+
+dnl -----------------------------------------------------------
+AC_OUTPUT([
+  Makefile
+  po/Makefile.in
+  profiles/Makefile
+  pixmaps/Makefile
+  libempathy/Makefile
+  libempathy-gtk/Makefile
+  launcher/Makefile
+  accounts/Makefile
+  contact-list/Makefile
+  chat/Makefile
+])
+
diff --git a/contact-list/Makefile.am b/contact-list/Makefile.am
new file mode 100644 (file)
index 0000000..de67863
--- /dev/null
@@ -0,0 +1,20 @@
+AM_CPPFLAGS =                                          \
+       -I$(top_srcdir)                                 \
+       -DPREFIX="\"$(prefix)"\"                        \
+       -DSYSCONFDIR=\""$(sysconfdir)"\"                \
+       -DDATADIR=\""$(datadir)"\"                      \
+       -DLIBDIR=\""$(libdir)"\"                        \
+       $(EMPATHY_CFLAGS)                               \
+       $(WARN_CFLAGS)
+
+bin_PROGRAMS = empathy-contact-list
+
+empathy_contact_list_SOURCES =                                                 \
+       empathy-contact-list-main.c
+
+empathy_contact_list_LDADD = \
+       $(top_builddir)/libempathy/libempathy.la                                \
+       $(top_builddir)/libempathy-gtk/libempathy-gtk.la                        \
+       $(EMPATHY_LIBS)
+
+
diff --git a/contact-list/empathy-contact-list-main.c b/contact-list/empathy-contact-list-main.c
new file mode 100644 (file)
index 0000000..2f43284
--- /dev/null
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
+ *
+ * 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.
+ * 
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <libempathy/gossip-contact.h>
+#include <libempathy/empathy-session.h>
+#include <libempathy-gtk/gossip-contact-list.h>
+#include <libempathy-gtk/gossip-private-chat.h>
+#include <libempathy-gtk/gossip-stock.h>
+
+static void
+destroy_cb (GtkWidget *window,
+           gpointer   user_data)
+{
+       gossip_stock_finalize ();
+       empathy_session_finalize ();
+       gtk_main_quit ();
+}
+
+static void
+contact_chat_cb (GtkWidget     *list,
+                GossipContact *contact,
+                gpointer       user_data)
+{
+       mission_control_request_channel (empathy_session_get_mission_control (),
+                                        gossip_contact_get_account (contact),
+                                        TP_IFACE_CHANNEL_TYPE_TEXT,
+                                        gossip_contact_get_handle (contact),
+                                        TP_HANDLE_TYPE_CONTACT,
+                                        NULL, NULL);
+}
+
+int
+main (int argc, char *argv[])
+{
+       GtkWidget *window;
+       GtkWidget *list;
+       GtkWidget *sw;
+
+       gtk_init (&argc, &argv);
+
+       empathy_session_connect ();
+
+       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+       gossip_stock_init (window);
+
+       list = GTK_WIDGET (gossip_contact_list_new ());
+       sw = gtk_scrolled_window_new (NULL, NULL);      
+       gtk_container_add (GTK_CONTAINER (window), sw);
+       gtk_container_add (GTK_CONTAINER (sw), list);
+
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                       GTK_POLICY_AUTOMATIC,
+                                       GTK_POLICY_AUTOMATIC);
+       gtk_widget_set_size_request (sw, 200, 400);
+
+       g_signal_connect (window, "destroy",
+                         G_CALLBACK (destroy_cb),
+                         NULL);
+       g_signal_connect (list, "contact-chat",
+                         G_CALLBACK (contact_chat_cb),
+                         NULL);
+
+       gtk_widget_show_all (window);
+
+       gtk_main ();
+
+       return EXIT_SUCCESS;
+}
+
diff --git a/launcher/Makefile.am b/launcher/Makefile.am
new file mode 100644 (file)
index 0000000..b066253
--- /dev/null
@@ -0,0 +1,12 @@
+INCLUDES =                                             \
+       -I$(top_srcdir)                                 \
+       $(EMPATHY_CFLAGS)                               \
+       $(WARN_CFLAGS)
+
+bin_PROGRAMS = empathy-launcher
+empathy_launcher_SOURCES = empathy-launcher.c
+empathy_launcher_LDADD =                                                       \
+       $(top_builddir)/libempathy/libempathy.la                                \
+       $(EMPATHY_LIBS)
+
+
diff --git a/launcher/empathy-launcher.c b/launcher/empathy-launcher.c
new file mode 100644 (file)
index 0000000..44b390f
--- /dev/null
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Xavier Claessens <xclaesse@gmail.com>
+ *
+ * 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.
+ * 
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include <libempathy/empathy-session.h>
+#include <libempathy/gossip-debug.h>
+
+#define DEBUG_DOMAIN "Launcher"
+
+int
+main (int argc, char *argv[])
+{
+       GMainLoop *main_loop;
+
+       g_type_init ();
+
+       empathy_session_connect ();
+
+       main_loop = g_main_loop_new (NULL, FALSE);
+       g_main_loop_run (main_loop);
+
+       return 0;
+}
+
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am
new file mode 100644 (file)
index 0000000..1627156
--- /dev/null
@@ -0,0 +1,68 @@
+AM_CPPFLAGS =                                           \
+       -I.                                             \
+       -I$(top_srcdir)                                 \
+       -DDATADIR=\""$(datadir)"\"                      \
+       -DLOCALEDIR=\""$(datadir)/locale"\"             \
+       $(EMPATHY_CFLAGS)                               \
+       $(WARN_CFLAGS)
+
+noinst_LTLIBRARIES = libempathy-gtk.la
+
+libempathy_gtk_la_SOURCES =                                                    \
+       gossip-accounts-dialog.c                gossip-accounts-dialog.h        \
+       gossip-account-widget-generic.c         gossip-account-widget-generic.h \
+       gossip-profile-chooser.c                gossip-profile-chooser.h        \
+       gossip-cell-renderer-expander.c         gossip-cell-renderer-expander.h \
+       gossip-cell-renderer-text.c             gossip-cell-renderer-text.h     \
+       gossip-stock.c                          gossip-stock.h                  \
+       gossip-spell.c                          gossip-spell.h                  \
+       gossip-contact-groups.c                 gossip-contact-groups.h         \
+       gossip-contact-list.c                   gossip-contact-list.h           \
+       gossip-preferences.c                    gossip-preferences.h            \
+       gossip-theme-manager.c                  gossip-theme-manager.h          \
+       gossip-chat.c                           gossip-chat.h                   \
+       gossip-chat-view.c                      gossip-chat-view.h              \
+       gossip-chat-window.c                    gossip-chat-window.h            \
+       gossip-private-chat.c                   gossip-private-chat.h           \
+       gossip-ui-utils.c                       gossip-ui-utils.h
+
+libempathy_gtk_la_LIBADD =                                                     \
+       $(top_builddir)/libempathy/libempathy.la                                \
+       $(EMPATHY_LIBS)
+
+libempathy_gtk_includedir = $(includedir)/empathy/
+
+gladedir = $(datadir)/empathy
+glade_DATA =                           \
+       empathy-accounts.glade          \
+       empathy-chat.glade
+
+dtddir = $(datadir)/empathy
+dtd_DATA =                                                                     \
+       gossip-contact-groups.dtd
+
+schemasdir      = $(GCONF_SCHEMA_FILE_DIR)
+schemas_in_files = empathy.schemas.in
+schemas_DATA    = $(schemas_in_files:.schemas.in=.schemas)
+@INTLTOOL_SCHEMAS_RULE@
+
+if GCONF_SCHEMAS_INSTALL
+install-data-local:
+       if test -z "$(DESTDIR)" ; then \
+         for p in $(schemas_DATA) ; do \
+           GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$$p ; \
+         done \
+       fi
+else
+install-data-local:
+endif
+
+EXTRA_DIST =                   \
+       $(glade_DATA)           \
+       $(dtd_DATA)             \
+       $(schemas_in_files)     \
+       $(schemas_DATA)
+
+DISTCLEANFILES =               \
+       $(schemas_DATA)
+
diff --git a/libempathy-gtk/empathy-accounts.glade b/libempathy-gtk/empathy-accounts.glade
new file mode 100644 (file)
index 0000000..a7bb391
--- /dev/null
@@ -0,0 +1,843 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <widget class="GtkDialog" id="accounts_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Accounts</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox3">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkHBox" id="hbox146">
+            <property name="visible">True</property>
+            <property name="border_width">6</property>
+            <property name="spacing">18</property>
+            <child>
+              <widget class="GtkVBox" id="vbox195">
+                <property name="visible">True</property>
+                <property name="spacing">6</property>
+                <child>
+                  <widget class="GtkScrolledWindow" id="scrolledwindow17">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                    <property name="shadow_type">GTK_SHADOW_IN</property>
+                    <child>
+                      <widget class="GtkTreeView" id="treeview">
+                        <property name="height_request">200</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="enable_search">False</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkVBox" id="vbox196">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <widget class="GtkButton" id="button_connect">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="label">gtk-connect</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkHBox" id="hbox148">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <property name="homogeneous">True</property>
+                        <child>
+                          <widget class="GtkButton" id="button_add">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label">gtk-add</property>
+                            <property name="use_stock">True</property>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkButton" id="button_remove">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label">gtk-remove</property>
+                            <property name="use_stock">True</property>
+                          </widget>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox214">
+                <property name="width_request">415</property>
+                <property name="visible">True</property>
+                <property name="spacing">18</property>
+                <child>
+                  <widget class="GtkVBox" id="vbox_details">
+                    <property name="visible">True</property>
+                    <property name="spacing">18</property>
+                    <child>
+                      <widget class="GtkFrame" id="frame1">
+                        <property name="visible">True</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">GTK_SHADOW_NONE</property>
+                        <child>
+                          <widget class="GtkAlignment" id="alignment7">
+                            <property name="visible">True</property>
+                            <property name="top_padding">6</property>
+                            <property name="left_padding">20</property>
+                            <child>
+                              <widget class="GtkVBox" id="vbox213">
+                                <property name="visible">True</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <widget class="GtkTable" id="table14">
+                                    <property name="visible">True</property>
+                                    <property name="n_rows">2</property>
+                                    <property name="n_columns">2</property>
+                                    <property name="column_spacing">6</property>
+                                    <property name="row_spacing">6</property>
+                                    <child>
+                                      <widget class="GtkLabel" id="label_type">
+                                        <property name="visible">True</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">Jabber</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="y_options"></property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkLabel" id="label_name">
+                                        <property name="visible">True</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">Imendio </property>
+                                        <property name="width_chars">0</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="top_attach">1</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="y_options"></property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkImage" id="image_type">
+                                        <property name="visible">True</property>
+                                        <property name="yalign">0</property>
+                                        <property name="stock">gtk-cut</property>
+                                        <property name="icon_size">6</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="left_attach">1</property>
+                                        <property name="right_attach">2</property>
+                                        <property name="bottom_attach">2</property>
+                                        <property name="x_options">GTK_FILL</property>
+                                        <property name="y_options">GTK_FILL</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label598">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">&lt;b&gt;Account&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                          </widget>
+                          <packing>
+                            <property name="type">label_item</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkFrame" id="frame2">
+                        <property name="visible">True</property>
+                        <property name="label_xalign">0</property>
+                        <property name="shadow_type">GTK_SHADOW_NONE</property>
+                        <child>
+                          <widget class="GtkAlignment" id="alignment_settings">
+                            <property name="visible">True</property>
+                            <property name="top_padding">6</property>
+                            <property name="left_padding">20</property>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </widget>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="label599">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">&lt;b&gt;Settings&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                          </widget>
+                          <packing>
+                            <property name="type">label_item</property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame_new_account">
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment29">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">20</property>
+                        <child>
+                          <widget class="GtkVBox" id="vbox216">
+                            <property name="visible">True</property>
+                            <property name="spacing">12</property>
+                            <child>
+                              <widget class="GtkTable" id="table_new_account">
+                                <property name="visible">True</property>
+                                <property name="n_rows">2</property>
+                                <property name="n_columns">2</property>
+                                <property name="column_spacing">6</property>
+                                <property name="row_spacing">6</property>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="label638">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">_Name:</property>
+                                    <property name="use_underline">True</property>
+                                    <property name="mnemonic_widget">entry_name</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkLabel" id="label640">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes">_Type:</property>
+                                    <property name="use_underline">True</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options"></property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkHBox" id="hbox180">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">6</property>
+                                    <child>
+                                      <widget class="GtkEntry" id="entry_name">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="tooltip" translatable="yes">A unique name for this account to identify it personally to you.</property>
+                                        <property name="invisible_char">*</property>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="left_attach">1</property>
+                                    <property name="right_attach">2</property>
+                                    <property name="top_attach">1</property>
+                                    <property name="bottom_attach">2</property>
+                                    <property name="x_options">GTK_FILL</property>
+                                    <property name="y_options">GTK_FILL</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkHBox" id="hbox181">
+                                <property name="visible">True</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <widget class="GtkButton" id="button_cancel">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="label">gtk-cancel</property>
+                                    <property name="use_stock">True</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="pack_type">GTK_PACK_END</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <widget class="GtkButton" id="button_create">
+                                    <property name="visible">True</property>
+                                    <property name="sensitive">False</property>
+                                    <property name="can_focus">True</property>
+                                    <child>
+                                      <widget class="GtkAlignment" id="alignment30">
+                                        <property name="visible">True</property>
+                                        <property name="xscale">0</property>
+                                        <property name="yscale">0</property>
+                                        <child>
+                                          <widget class="GtkHBox" id="hbox181">
+                                            <property name="visible">True</property>
+                                            <property name="spacing">2</property>
+                                            <child>
+                                              <widget class="GtkImage" id="image838">
+                                                <property name="visible">True</property>
+                                                <property name="stock">gtk-new</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">False</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkLabel" id="label642">
+                                                <property name="visible">True</property>
+                                                <property name="label" translatable="yes">Cr_eate!</property>
+                                                <property name="use_underline">True</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">False</property>
+                                                <property name="position">1</property>
+                                              </packing>
+                                            </child>
+                                          </widget>
+                                        </child>
+                                      </widget>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="pack_type">GTK_PACK_END</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="pack_type">GTK_PACK_END</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label643">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;New Account&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkFrame" id="frame_no_account">
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkAlignment" id="alignment21">
+                        <property name="visible">True</property>
+                        <property name="top_padding">6</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <widget class="GtkLabel" id="label_no_account_blurb">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">To add a new account, you can click on the 'Add' button and a new entry will be created for you to started configuring.
+
+If you do not want to add an account, simply click on the account you want to configure in the list on the left.</property>
+                            <property name="use_markup">True</property>
+                            <property name="wrap">True</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkLabel" id="label_no_account">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;No Account Selected&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="button_close">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-close</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="account_jabber_settings">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">jabber account settings</property>
+    <property name="resizable">False</property>
+    <child>
+      <widget class="GtkTable" id="vbox_jabber_settings">
+        <property name="visible">True</property>
+        <property name="n_rows">6</property>
+        <property name="n_columns">3</property>
+        <property name="column_spacing">12</property>
+        <property name="row_spacing">6</property>
+        <child>
+          <widget class="GtkLabel" id="label_id">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Login I_D:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_id</property>
+          </widget>
+          <packing>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label_password">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Pass_word:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_password</property>
+          </widget>
+          <packing>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label_resource">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Reso_urce:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_resource</property>
+          </widget>
+          <packing>
+            <property name="top_attach">2</property>
+            <property name="bottom_attach">3</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label_server">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">_Server:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_server</property>
+          </widget>
+          <packing>
+            <property name="top_attach">3</property>
+            <property name="bottom_attach">4</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label_port">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">_Port:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_port</property>
+          </widget>
+          <packing>
+            <property name="top_attach">4</property>
+            <property name="bottom_attach">5</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkEntry" id="entry_resource">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">*</property>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">3</property>
+            <property name="top_attach">2</property>
+            <property name="bottom_attach">3</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkEntry" id="entry_server">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">*</property>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">3</property>
+            <property name="top_attach">3</property>
+            <property name="bottom_attach">4</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkEntry" id="entry_port">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">*</property>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">3</property>
+            <property name="top_attach">4</property>
+            <property name="bottom_attach">5</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkCheckButton" id="checkbutton_ssl">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="label" translatable="yes">Use encryption (SS_L)</property>
+            <property name="use_underline">True</property>
+            <property name="draw_indicator">True</property>
+          </widget>
+          <packing>
+            <property name="right_attach">3</property>
+            <property name="top_attach">5</property>
+            <property name="bottom_attach">6</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="hbox174">
+            <property name="visible">True</property>
+            <property name="spacing">2</property>
+            <child>
+              <widget class="GtkEntry" id="entry_password">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="visibility">False</property>
+                <property name="invisible_char">*</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="button_forget">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="tooltip" translatable="yes">Forget password and clear the entry.</property>
+                <child>
+                  <widget class="GtkImage" id="image834">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-clear</property>
+                    <property name="icon_size">1</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options">GTK_FILL</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkButton" id="button_change_password">
+            <property name="visible">True</property>
+            <property name="sensitive">False</property>
+            <property name="can_focus">True</property>
+            <property name="label" translatable="yes">C_hange</property>
+            <property name="use_underline">True</property>
+          </widget>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="right_attach">3</property>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkEntry" id="entry_id">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">*</property>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkButton" id="button_register">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="label" translatable="yes">R_egister</property>
+            <property name="use_underline">True</property>
+          </widget>
+          <packing>
+            <property name="left_attach">2</property>
+            <property name="right_attach">3</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkWindow" id="account_msn_settings">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">msn account settings</property>
+    <property name="resizable">False</property>
+    <child>
+      <widget class="GtkTable" id="vbox_msn_settings">
+        <property name="visible">True</property>
+        <property name="n_rows">4</property>
+        <property name="n_columns">2</property>
+        <property name="column_spacing">12</property>
+        <property name="row_spacing">6</property>
+        <child>
+          <widget class="GtkLabel" id="label_id">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Login I_D:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_id</property>
+          </widget>
+          <packing>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label_password">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Pass_word:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_password</property>
+          </widget>
+          <packing>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="hbox182">
+            <property name="visible">True</property>
+            <property name="spacing">2</property>
+            <child>
+              <widget class="GtkEntry" id="entry_password">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="visibility">False</property>
+                <property name="invisible_char">*</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="button_forget">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="tooltip" translatable="yes">Forget password and clear the entry.</property>
+                <child>
+                  <widget class="GtkImage" id="image839">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-clear</property>
+                    <property name="icon_size">1</property>
+                  </widget>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">1</property>
+            <property name="bottom_attach">2</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options">GTK_FILL</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkEntry" id="entry_id">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">*</property>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label_server">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">_Server:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_server</property>
+          </widget>
+          <packing>
+            <property name="top_attach">2</property>
+            <property name="bottom_attach">3</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkEntry" id="entry_server">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">*</property>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">2</property>
+            <property name="bottom_attach">3</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkLabel" id="label_port">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">_Port:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">entry_port</property>
+          </widget>
+          <packing>
+            <property name="top_attach">3</property>
+            <property name="bottom_attach">4</property>
+            <property name="x_options">GTK_FILL</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkEntry" id="entry_port">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">*</property>
+          </widget>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="right_attach">2</property>
+            <property name="top_attach">3</property>
+            <property name="bottom_attach">4</property>
+            <property name="y_options"></property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
diff --git a/libempathy-gtk/empathy-chat.glade b/libempathy-gtk/empathy-chat.glade
new file mode 100644 (file)
index 0000000..e8bd47c
--- /dev/null
@@ -0,0 +1,700 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkWindow" id="chat_page_window">
+  <property name="title" translatable="yes">Chat</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="default_width">350</property>
+  <property name="default_height">250</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkVBox" id="chat_widget">
+      <property name="border_width">4</property>
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">3</property>
+
+      <child>
+       <widget class="GtkScrolledWindow" id="chat_view_sw">
+         <property name="visible">True</property>
+         <property name="can_focus">True</property>
+         <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+         <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
+         <property name="shadow_type">GTK_SHADOW_IN</property>
+         <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+         <child>
+           <placeholder/>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">True</property>
+         <property name="fill">True</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkScrolledWindow" id="input_text_view_sw">
+         <property name="visible">True</property>
+         <property name="can_focus">True</property>
+         <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+         <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
+         <property name="shadow_type">GTK_SHADOW_IN</property>
+         <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+         <child>
+           <placeholder/>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">False</property>
+       </packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkWindow" id="chat_window">
+  <property name="title" translatable="yes">Chat</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="default_width">350</property>
+  <property name="default_height">250</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkVBox" id="chat_vbox">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+       <widget class="GtkMenuBar" id="chats_menubar">
+         <property name="visible">True</property>
+         <property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
+         <property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
+
+         <child>
+           <widget class="GtkMenuItem" id="menu_conv">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">_Conversation</property>
+             <property name="use_underline">True</property>
+
+             <child>
+               <widget class="GtkMenu" id="menu_conv_menu">
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_conv_clear">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">C_lear</property>
+                     <property name="use_underline">True</property>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image235">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-clear</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_conv_insert_smiley">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Insert _Smiley</property>
+                     <property name="use_underline">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator11">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_conv_log">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_View Previous Conversations</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="F3" modifiers="0" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image236">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-justify-left</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="menu_conv_separator">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_conv_add_contact">
+                     <property name="label" translatable="yes">_Add Contact...</property>
+                     <property name="use_underline">True</property>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image237">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-add</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_conv_info">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Contact Infor_mation</property>
+                     <property name="use_underline">True</property>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image238">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-info</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator7">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_conv_close">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Close</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image239">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-close</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkMenuItem" id="menu_room">
+             <property name="label" translatable="yes">_Room</property>
+             <property name="use_underline">True</property>
+
+             <child>
+               <widget class="GtkMenu" id="menu_room_menu">
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_room_set_topic">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Change _Topic...</property>
+                     <property name="use_underline">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator12">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_room_join_new">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Join _New...</property>
+                     <property name="use_underline">True</property>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image240">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-new</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_room_invite">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">In_vite...</property>
+                     <property name="use_underline">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator7">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_room_add">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Add To Favorites</property>
+                     <property name="use_underline">True</property>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image241">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-add</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator10">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkCheckMenuItem" id="menu_room_show_contacts">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Show Contacts</property>
+                     <property name="use_underline">True</property>
+                     <property name="active">True</property>
+                     <accelerator key="F11" modifiers="0" signal="activate"/>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkMenuItem" id="menu_edit">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">_Edit</property>
+             <property name="use_underline">True</property>
+
+             <child>
+               <widget class="GtkMenu" id="menu_edit_menu">
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_edit_cut">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Cu_t</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="X" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image242">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-cut</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_edit_copy">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Copy</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="C" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image243">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-copy</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="menu_edit_paste">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Paste</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="V" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image244">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-paste</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkMenuItem" id="menu_tabs">
+             <property name="visible">True</property>
+             <property name="label" translatable="yes">_Tabs</property>
+             <property name="use_underline">True</property>
+
+             <child>
+               <widget class="GtkMenu" id="menu_tabs_menu">
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_tabs_prev">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Previous Tab</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="Page_Up" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_tabs_next">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Next Tab</property>
+                     <property name="use_underline">True</property>
+                     <accelerator key="Page_Down" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator4">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_tabs_left">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Move Tab _Left</property>
+                     <property name="use_underline">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_tabs_right">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">Move Tab _Right</property>
+                     <property name="use_underline">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkMenuItem" id="menu_tabs_detach">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Detach Tab</property>
+                     <property name="use_underline">True</property>
+                   </widget>
+                 </child>
+               </widget>
+             </child>
+           </widget>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">False</property>
+       </packing>
+      </child>
+
+      <child>
+       <placeholder/>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkDialog" id="chat_invite_dialog">
+  <property name="border_width">5</property>
+  <property name="title" translatable="yes">Invite</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+  <property name="modal">True</property>
+  <property name="default_width">275</property>
+  <property name="default_height">225</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+  <property name="has_separator">False</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="vbox6">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+       <widget class="GtkHButtonBox" id="hbuttonbox1">
+         <property name="visible">True</property>
+         <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+         <child>
+           <widget class="GtkButton" id="button_cancel">
+             <property name="visible">True</property>
+             <property name="can_default">True</property>
+             <property name="can_focus">True</property>
+             <property name="label">gtk-cancel</property>
+             <property name="use_stock">True</property>
+             <property name="relief">GTK_RELIEF_NORMAL</property>
+             <property name="focus_on_click">True</property>
+             <property name="response_id">-6</property>
+           </widget>
+         </child>
+
+         <child>
+           <widget class="GtkButton" id="button_invite">
+             <property name="visible">True</property>
+             <property name="sensitive">False</property>
+             <property name="can_default">True</property>
+             <property name="has_default">True</property>
+             <property name="can_focus">True</property>
+             <property name="label">In_vite</property>
+             <property name="use_underline">True</property>
+             <property name="relief">GTK_RELIEF_NORMAL</property>
+             <property name="focus_on_click">True</property>
+             <property name="response_id">-5</property>
+           </widget>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">False</property>
+         <property name="fill">True</property>
+         <property name="pack_type">GTK_PACK_END</property>
+       </packing>
+      </child>
+
+      <child>
+       <widget class="GtkVBox" id="vbox7">
+         <property name="border_width">5</property>
+         <property name="visible">True</property>
+         <property name="homogeneous">False</property>
+         <property name="spacing">18</property>
+
+         <child>
+           <widget class="GtkVBox" id="vbox7">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">6</property>
+
+             <child>
+               <widget class="GtkLabel" id="label">
+                 <property name="visible">True</property>
+                 <property name="label" translatable="yes">Select who would you like to invite:</property>
+                 <property name="use_underline">False</property>
+                 <property name="use_markup">True</property>
+                 <property name="justify">GTK_JUSTIFY_LEFT</property>
+                 <property name="wrap">True</property>
+                 <property name="selectable">False</property>
+                 <property name="xalign">0</property>
+                 <property name="yalign">0.5</property>
+                 <property name="xpad">0</property>
+                 <property name="ypad">0</property>
+                 <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+                 <property name="width_chars">-1</property>
+                 <property name="single_line_mode">False</property>
+                 <property name="angle">0</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                 <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                 <property name="shadow_type">GTK_SHADOW_IN</property>
+                 <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+                 <child>
+                   <widget class="GtkTreeView" id="treeview">
+                     <property name="visible">True</property>
+                     <property name="can_focus">True</property>
+                     <property name="headers_visible">False</property>
+                     <property name="rules_hint">False</property>
+                     <property name="reorderable">False</property>
+                     <property name="enable_search">True</property>
+                     <property name="fixed_height_mode">False</property>
+                     <property name="hover_selection">False</property>
+                     <property name="hover_expand">False</property>
+                   </widget>
+                 </child>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">True</property>
+                 <property name="fill">True</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="padding">0</property>
+             <property name="expand">True</property>
+             <property name="fill">True</property>
+           </packing>
+         </child>
+
+         <child>
+           <widget class="GtkVBox" id="vbox8">
+             <property name="visible">True</property>
+             <property name="homogeneous">False</property>
+             <property name="spacing">6</property>
+
+             <child>
+               <widget class="GtkLabel" id="label4">
+                 <property name="visible">True</property>
+                 <property name="label" translatable="yes">Invitation _message:</property>
+                 <property name="use_underline">True</property>
+                 <property name="use_markup">True</property>
+                 <property name="justify">GTK_JUSTIFY_LEFT</property>
+                 <property name="wrap">True</property>
+                 <property name="selectable">False</property>
+                 <property name="xalign">0</property>
+                 <property name="yalign">0.5</property>
+                 <property name="xpad">0</property>
+                 <property name="ypad">0</property>
+                 <property name="mnemonic_widget">entry</property>
+                 <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+                 <property name="width_chars">-1</property>
+                 <property name="single_line_mode">False</property>
+                 <property name="angle">0</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+
+             <child>
+               <widget class="GtkEntry" id="entry">
+                 <property name="visible">True</property>
+                 <property name="can_focus">True</property>
+                 <property name="editable">True</property>
+                 <property name="visibility">True</property>
+                 <property name="max_length">0</property>
+                 <property name="text" translatable="yes">You have been invited to join a chat conference.</property>
+                 <property name="has_frame">True</property>
+                 <property name="invisible_char">*</property>
+                 <property name="activates_default">True</property>
+                 <property name="width_chars">40</property>
+               </widget>
+               <packing>
+                 <property name="padding">0</property>
+                 <property name="expand">False</property>
+                 <property name="fill">False</property>
+               </packing>
+             </child>
+           </widget>
+           <packing>
+             <property name="padding">0</property>
+             <property name="expand">False</property>
+             <property name="fill">False</property>
+           </packing>
+         </child>
+       </widget>
+       <packing>
+         <property name="padding">0</property>
+         <property name="expand">True</property>
+         <property name="fill">True</property>
+       </packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff --git a/libempathy-gtk/empathy.schemas.in b/libempathy-gtk/empathy.schemas.in
new file mode 100644 (file)
index 0000000..243e6ce
--- /dev/null
@@ -0,0 +1,237 @@
+<gconfschemafile>
+  <schemalist>
+
+    <schema>
+      <key>/schemas/apps/empathy/ui/show_offline</key>
+      <applyto>/apps/empathy/ui/show_offline</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>Show offline contacts</short>
+       <long>
+       Whether or not to show contacts that are offline in the contact list.
+       </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/ui/show_avatars</key>
+      <applyto>/apps/empathy/ui/show_avatars</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Show avatars</short>
+       <long>
+       Whether or not to show avatars for contacts in the contact
+      list and chat windows.
+       </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/ui/compact_contact_list</key>
+      <applyto>/apps/empathy/ui/compact_contact_list</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+        <short>Compact contact list</short>
+       <long>
+       Whether to show the contact list in compact mode or not. 
+       </long>
+      </locale>
+    </schema>
+
+    <schema>
+       <key>/schemas/apps/empathy/ui/main_window_hidden</key>
+       <applyto>/apps/empathy/ui/main_window_hidden</applyto>
+       <owner>empathy</owner>
+       <type>bool</type>
+       <default>false</default>
+       <locale name="C">
+          <short>Hide main window</short>
+          <long>
+            Hide the main window.
+          </long>
+       </locale>
+    </schema>
+
+    <schema>
+       <key>/schemas/apps/empathy/ui/avatar_directory</key>
+       <applyto>/apps/empathy/ui/avatar_directory</applyto>
+       <owner>empathy</owner>
+       <type>string</type>
+       <default></default>
+       <locale name="C">
+          <short>Default directory to select an avatar image from</short>
+          <long>
+           The last directory that an avatar image was chosen from.
+          </long>
+       </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/notifications/play_sounds</key>
+      <applyto>/apps/empathy/notifications/play_sounds</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+         <short>Use notification sounds</short>
+         <long>
+        Whether or not to play a sound when messages arrive.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/notifications/sound_when_away</key>
+      <applyto>/apps/empathy/notifications/sound_when_away</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>false</default>
+      <locale name="C">
+         <short>Enable sound when away</short>
+         <long>
+        Whether or not to play sounds when away.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/notifications/sound_when_busy</key>
+      <applyto>/apps/empathy/notifications/sound_when_busy</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+         <short>Enable sound when busy</short>
+         <long>
+        Whether or not to play sounds when busy.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/notifications/popup_when_contact_available</key>
+      <applyto>/apps/empathy/notifications/popup_when_contact_available</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+         <short>Enable popup when contact is available</short>
+         <long>
+        Whether or not to show a popup when a contact becomes available.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
+       <key>/schemas/apps/empathy/ui/separate_chat_windows</key>
+       <applyto>/apps/empathy/ui/separate_chat_windows</applyto>
+       <owner>empathy</owner>
+       <type>bool</type>
+       <default>false</default>
+       <locale name="C">
+          <short>Open new chats in separate windows</short>
+          <long>
+           Always open a separate chat window for new chats.
+          </long>
+       </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/conversation/graphical_smileys</key>
+      <applyto>/apps/empathy/conversation/graphical_smileys</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+         <short>Use graphical smileys</short>
+         <long>
+        Whether or not to convert smileys into graphical images in
+        conversations.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/conversation/theme</key>
+      <applyto>/apps/empathy/conversation/theme</applyto>
+      <owner>empathy</owner>
+      <type>string</type>
+      <default>classic</default>
+      <locale name="C">
+         <short>Chat window theme</short>
+         <long>
+           The theme that is used to display the conversation in chat windows.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/conversation/theme_chat_room</key>
+      <applyto>/apps/empathy/conversation/theme_chat_room</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+         <short>Use theme for chat rooms</short>
+         <long>
+           Whether to use the theme for chat rooms or not.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/conversation/spell_checker_languages</key>
+      <applyto>/apps/empathy/conversation/spell_checker_languages</applyto>
+      <owner>empathy</owner>
+      <type>string</type>
+      <default>en</default>
+      <locale name="C">
+       <short>Spell checking languages</short>
+       <long>
+         Comma separated list of spell checker languages to use (e.g. en, fr, nl).
+       </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/conversation/spell_checker_enabled</key>
+      <applyto>/apps/empathy/conversation/spell_checker_enabled</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Enable spell checker</short>
+       <long>
+       Whether or not to check words typed against the languages you
+      want to check with.
+       </long>
+      </locale>
+    </schema>
+
+    <schema>
+      <key>/schemas/apps/empathy/hints/close_main_window</key>
+      <applyto>/apps/empathy/hints/close_main_window</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short>Show hint about closing the main window</short>
+       <long>
+       Whether or not to show the message dialog about closing the
+      main window with the 'x' button in the title bar.
+       </long>
+      </locale>
+    </schema>
+
+  </schemalist>  
+</gconfschemafile>
+
+
+
+
diff --git a/libempathy-gtk/gossip-account-widget-generic.c b/libempathy-gtk/gossip-account-widget-generic.c
new file mode 100644 (file)
index 0000000..7f78463
--- /dev/null
@@ -0,0 +1,309 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
+ *          Martyn Russell <martyn@imendio.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include <libmissioncontrol/mc-account.h>
+#include <libmissioncontrol/mc-protocol.h>
+
+#include <libempathy-gtk/gossip-ui-utils.h>
+
+#include "gossip-account-widget-generic.h"
+
+typedef struct {
+       McAccount     *account;
+
+       GtkWidget     *sw;
+       GtkWidget     *table_settings;
+       GtkSizeGroup  *size_group;
+
+       guint          n_rows;
+} GossipAccountWidgetGeneric;
+
+static gboolean account_widget_generic_entry_focus_cb         (GtkWidget                  *widget,
+                                                              GdkEventFocus              *event,
+                                                              GossipAccountWidgetGeneric *settings);
+static void     account_widget_generic_int_changed_cb         (GtkWidget                  *widget,
+                                                              GossipAccountWidgetGeneric *settings);
+static void     account_widget_generic_checkbutton_toggled_cb (GtkWidget                  *widget,
+                                                              GossipAccountWidgetGeneric *settings);
+static gchar *  account_widget_generic_format_param_name      (const gchar                *param_name);
+static void     account_widget_generic_setup_foreach          (McProtocolParam            *param,
+                                                              GossipAccountWidgetGeneric *settings);
+static void     account_widget_generic_destroy_cb             (GtkWidget                  *widget,
+                                                              GossipAccountWidgetGeneric *settings);
+
+static gboolean 
+account_widget_generic_entry_focus_cb (GtkWidget                  *widget,
+                                      GdkEventFocus              *event,
+                                      GossipAccountWidgetGeneric *settings)
+{
+       const gchar *str;
+       const gchar *param_name;
+
+       str = gtk_entry_get_text (GTK_ENTRY (widget));
+       param_name = g_object_get_data (G_OBJECT (widget), "param_name");
+
+       mc_account_set_param_string (settings->account, param_name, str);
+
+       return FALSE;
+}
+
+static void
+account_widget_generic_int_changed_cb (GtkWidget                  *widget,
+                                      GossipAccountWidgetGeneric *settings)
+{
+       const gchar *param_name;
+       gint         value;
+
+       value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
+       param_name = g_object_get_data (G_OBJECT (widget), "param_name");
+
+       mc_account_set_param_int (settings->account, param_name, value);
+}
+
+static void  
+account_widget_generic_checkbutton_toggled_cb (GtkWidget                  *widget,
+                                              GossipAccountWidgetGeneric *settings)
+{
+       gboolean     active;
+       const gchar *param_name;
+
+       active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+       param_name = g_object_get_data (G_OBJECT (widget), "param_name");
+
+       mc_account_set_param_boolean (settings->account, param_name, active);
+}
+
+static gchar *
+account_widget_generic_format_param_name (const gchar *param_name)
+{
+       gchar *str;
+       gchar *p;
+
+       str = g_strdup (param_name);
+       
+       if (str && g_ascii_isalpha (str[0])) {
+               str[0] = g_ascii_toupper (str[0]);
+       }
+       
+       while ((p = strchr (str, '-')) != NULL) {
+               if (p[1] != '\0' && g_ascii_isalpha (p[1])) {
+                       p[0] = ' ';
+                       p[1] = g_ascii_toupper (p[1]);
+               }
+
+               p++;
+       }
+       
+       return str;
+}
+
+static void
+account_widget_generic_setup_foreach (McProtocolParam            *param,
+                                     GossipAccountWidgetGeneric *settings)
+{
+       GtkWidget *widget;
+       gchar     *param_name_formatted;
+
+       param_name_formatted = account_widget_generic_format_param_name (param->name);
+
+       gtk_table_resize (GTK_TABLE (settings->table_settings),
+                         ++settings->n_rows,
+                         2);
+
+       if (param->signature[0] == 's') {
+               gchar *str = NULL;
+
+               str = g_strdup_printf (_("%s:"), param_name_formatted);
+               widget = gtk_label_new (str);
+               g_free (str);
+
+               gtk_size_group_add_widget (settings->size_group, widget);
+               gtk_table_attach (GTK_TABLE (settings->table_settings),
+                                 widget,
+                                 0, 1,
+                                 settings->n_rows - 1, settings->n_rows,
+                                 GTK_FILL, 0,
+                                 0, 0);
+
+               str = NULL;
+               widget = gtk_entry_new ();
+               mc_account_get_param_string (settings->account,
+                                            param->name,
+                                            &str);
+               if (str) {
+                       gtk_entry_set_text (GTK_ENTRY (widget), str);
+                       g_free (str);
+               }
+
+               if (strstr (param->name, "password")) {
+                       gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);
+               }
+
+               g_signal_connect (widget, "focus-out-event",
+                                 G_CALLBACK (account_widget_generic_entry_focus_cb),
+                                 settings);
+
+               gtk_table_attach (GTK_TABLE (settings->table_settings),
+                                 widget,
+                                 1, 2,
+                                 settings->n_rows - 1, settings->n_rows,
+                                 GTK_FILL | GTK_EXPAND, 0,
+                                 0, 0);
+       }
+       else if (param->signature[0] == 'q' ||
+                param->signature[0] == 'n') {
+               gchar *str = NULL;
+               gint   value = 0;
+
+               str = g_strdup_printf (_("%s:"), param_name_formatted);
+               widget = gtk_label_new (str);
+               g_free (str);
+
+               gtk_size_group_add_widget (settings->size_group, widget);
+               gtk_table_attach (GTK_TABLE (settings->table_settings),
+                                 widget,
+                                 0, 1,
+                                 settings->n_rows - 1, settings->n_rows,
+                                 GTK_FILL, 0,
+                                 0, 0);
+
+               widget = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+               mc_account_get_param_int (settings->account,
+                                         param->name,
+                                         &value);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), value);
+
+               g_signal_connect (widget, "value-changed",
+                                 G_CALLBACK (account_widget_generic_int_changed_cb),
+                                 settings);
+
+               gtk_table_attach (GTK_TABLE (settings->table_settings),
+                                 widget,
+                                 1, 2,
+                                 settings->n_rows - 1, settings->n_rows,
+                                 GTK_FILL | GTK_EXPAND, 0,
+                                 0, 0);
+       }
+       else if (param->signature[0] == 'b') {
+               gboolean value;
+
+               mc_account_get_param_boolean (settings->account,
+                                             param->name,
+                                             &value);
+
+               widget = gtk_check_button_new_with_label (param_name_formatted);
+               gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value);
+
+               g_signal_connect (widget, "toggled",
+                                 G_CALLBACK (account_widget_generic_checkbutton_toggled_cb),
+                                 settings);
+
+               gtk_table_attach (GTK_TABLE (settings->table_settings),
+                                 widget,
+                                 0, 2,
+                                 settings->n_rows - 1, settings->n_rows,
+                                 GTK_FILL | GTK_EXPAND, 0,
+                                 0, 0);
+       } else {
+               g_assert_not_reached ();
+       }
+
+       g_free (param_name_formatted);
+
+       g_object_set_data_full (G_OBJECT (widget), "param_name", 
+                               g_strdup (param->name), g_free);
+}
+
+static void
+accounts_widget_generic_setup (GossipAccountWidgetGeneric *settings)
+{
+       McProtocol *protocol;
+       McProfile  *profile;
+       GSList     *params;
+
+       profile = mc_account_get_profile (settings->account);
+       protocol = mc_profile_get_protocol (profile);
+       params = mc_protocol_get_params (protocol);
+
+       g_slist_foreach (params,
+                        (GFunc) account_widget_generic_setup_foreach,
+                        settings);
+
+       g_slist_free (params);
+}
+
+static void
+account_widget_generic_destroy_cb (GtkWidget                  *widget,
+                                  GossipAccountWidgetGeneric *settings)
+{
+       g_object_unref (settings->account);
+       g_object_unref (settings->size_group);
+
+       g_free (settings);
+}
+
+GtkWidget *
+gossip_account_widget_generic_new (McAccount *account,
+                                  GtkWidget *label_name)
+{
+       GossipAccountWidgetGeneric *settings;
+
+       g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
+       g_return_val_if_fail (GTK_IS_WIDGET (label_name), NULL);
+
+       settings = g_new0 (GossipAccountWidgetGeneric, 1);
+
+       settings->account = g_object_ref (account);
+
+       settings->table_settings = gtk_table_new (0, 2, FALSE);
+       gtk_table_set_row_spacings (GTK_TABLE (settings->table_settings), 6);
+       gtk_table_set_col_spacings (GTK_TABLE (settings->table_settings), 6);
+       settings->sw = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (settings->sw),
+                                       GTK_POLICY_AUTOMATIC,
+                                       GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (settings->sw),
+                                              settings->table_settings);
+
+       settings->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+       if (label_name) {
+               gtk_size_group_add_widget (settings->size_group, label_name);
+       }
+       
+       accounts_widget_generic_setup (settings);
+
+       g_signal_connect (settings->sw, "destroy",
+                         G_CALLBACK (account_widget_generic_destroy_cb),
+                         settings);
+
+       gtk_widget_show_all (settings->sw);
+
+       return settings->sw;
+}
diff --git a/libempathy-gtk/gossip-account-widget-generic.h b/libempathy-gtk/gossip-account-widget-generic.h
new file mode 100644 (file)
index 0000000..1a3db63
--- /dev/null
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Xavier Claessens <xclaesse@gmail.com>
+ *          Martyn Russell <martyn@imendio.com>
+ */
+
+#ifndef __GOSSIP_ACCOUNT_WIDGET_GENERIC_H__
+#define __GOSSIP_ACCOUNT_WIDGET_GENERIC_H__
+
+#include <gtk/gtk.h>
+
+#include <libmissioncontrol/mc-account.h>
+
+G_BEGIN_DECLS
+
+GtkWidget *gossip_account_widget_generic_new (McAccount *account,
+                                             GtkWidget *label_name);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_ACCOUNT_WIDGET_GENERIC_H__ */
diff --git a/libempathy-gtk/gossip-accounts-dialog.c b/libempathy-gtk/gossip-accounts-dialog.c
new file mode 100644 (file)
index 0000000..43d9747
--- /dev/null
@@ -0,0 +1,1032 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Martyn Russell <martyn@imendio.com>
+ *          Xavier Claessens <xclaesse@gmail.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus-glib.h>
+
+#include <libmissioncontrol/mc-account.h>
+#include <libmissioncontrol/mc-profile.h>
+#include <libmissioncontrol/mission-control.h>
+#include <libmissioncontrol/mc-account-monitor.h>
+#include <libtelepathy/tp-constants.h>
+
+#include <libempathy/empathy-session.h>
+#include <libempathy/gossip-debug.h>
+#include <libempathy/gossip-paths.h>
+#include <libempathy/gossip-utils.h>
+#include <libempathy-gtk/gossip-ui-utils.h>
+
+#include "gossip-accounts-dialog.h"
+#include "gossip-profile-chooser.h"
+#include "gossip-account-widget-generic.h"
+
+#define DEBUG_DOMAIN "AccountDialog"
+
+/* Flashing delay for icons (milliseconds). */
+#define FLASH_TIMEOUT 500
+
+typedef struct {
+       GtkWidget *window;
+
+       GtkWidget *alignment_settings;
+
+       GtkWidget *vbox_details;
+       GtkWidget *frame_no_account;
+       GtkWidget *label_no_account;
+       GtkWidget *label_no_account_blurb;
+
+       GtkWidget *treeview;
+
+       GtkWidget *button_remove;
+       GtkWidget *button_connect;
+
+       GtkWidget *frame_new_account;
+       GtkWidget *combobox_profile;
+       GtkWidget *entry_name;
+       GtkWidget *table_new_account;
+       GtkWidget *button_create;
+       GtkWidget *button_cancel;
+
+       GtkWidget *image_type;
+       GtkWidget *label_name;
+       GtkWidget *label_type;
+       GtkWidget *settings_widget;
+
+       gboolean   connecting_show;
+       guint      connecting_id;
+       gboolean   account_changed;
+} GossipAccountsDialog;
+
+enum {
+       COL_NAME,
+       COL_STATUS,
+       COL_ACCOUNT_POINTER,
+       COL_COUNT
+};
+
+static void       accounts_dialog_setup                     (GossipAccountsDialog            *dialog);
+static void       accounts_dialog_update_account            (GossipAccountsDialog            *dialog,
+                                                            McAccount                       *account);
+static void       accounts_dialog_model_setup               (GossipAccountsDialog            *dialog);
+static void       accounts_dialog_model_add_columns         (GossipAccountsDialog            *dialog);
+static void       accounts_dialog_model_select_first        (GossipAccountsDialog            *dialog);
+static void       accounts_dialog_model_pixbuf_data_func    (GtkTreeViewColumn               *tree_column,
+                                                            GtkCellRenderer                 *cell,
+                                                            GtkTreeModel                    *model,
+                                                            GtkTreeIter                     *iter,
+                                                            GossipAccountsDialog            *dialog);
+static McAccount *accounts_dialog_model_get_selected        (GossipAccountsDialog            *dialog);
+static void       accounts_dialog_model_set_selected        (GossipAccountsDialog            *dialog,
+                                                            McAccount                       *account);
+static gboolean   accounts_dialog_model_remove_selected     (GossipAccountsDialog            *dialog);
+static void       accounts_dialog_model_selection_changed   (GtkTreeSelection                *selection,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_add_account               (GossipAccountsDialog            *dialog,
+                                                            McAccount                       *account);
+static void       accounts_dialog_account_added_cb          (McAccountMonitor                *monitor,
+                                                            gchar                           *unique_name,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_account_removed_cb        (McAccountMonitor                *monitor,
+                                                            gchar                           *unique_name,
+                                                            GossipAccountsDialog            *dialog);
+static gboolean   accounts_dialog_row_changed_foreach       (GtkTreeModel                    *model,
+                                                            GtkTreePath                     *path,
+                                                            GtkTreeIter                     *iter,
+                                                            gpointer                         user_data);
+static gboolean   accounts_dialog_flash_connecting_cb       (GossipAccountsDialog            *dialog);
+static void       accounts_dialog_status_changed_cb         (MissionControl                  *mc,
+                                                            TelepathyConnectionStatus        status,
+                                                            McPresence                       presence,
+                                                            TelepathyConnectionStatusReason  reason,
+                                                            const gchar                     *unique_name,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_entry_name_changed_cb     (GtkWidget                       *widget,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_button_create_clicked_cb  (GtkWidget                       *button,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_button_cancel_clicked_cb  (GtkWidget                       *button,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_button_connect_clicked_cb (GtkWidget                       *button,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_button_add_clicked_cb     (GtkWidget                       *button,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_remove_response_cb        (GtkWidget                       *dialog,
+                                                            gint                             response,
+                                                            McAccount                       *account);
+static void       accounts_dialog_button_remove_clicked_cb  (GtkWidget                       *button,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_treeview_row_activated_cb (GtkTreeView                     *tree_view,
+                                                            GtkTreePath                     *path,
+                                                            GtkTreeViewColumn               *column,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_response_cb               (GtkWidget                       *widget,
+                                                            gint                             response,
+                                                            GossipAccountsDialog            *dialog);
+static void       accounts_dialog_destroy_cb                (GtkWidget                       *widget,
+                                                            GossipAccountsDialog            *dialog);
+
+static void
+accounts_dialog_setup (GossipAccountsDialog *dialog)
+{
+       GtkTreeView      *view;
+       GtkListStore     *store;
+       GtkTreeSelection *selection;
+       GtkTreeIter       iter;
+       GList            *accounts, *l;
+       MissionControl   *mc;
+
+       view = GTK_TREE_VIEW (dialog->treeview);
+       store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
+       selection = gtk_tree_view_get_selection (view);
+
+       mc = empathy_session_get_mission_control ();
+       accounts = mc_accounts_list ();
+
+       for (l = accounts; l; l = l->next) {
+               McAccount                 *account;
+               const gchar               *name;
+               TelepathyConnectionStatus  status;
+
+               account = l->data;
+
+               name = mc_account_get_display_name (account);
+               if (!name) {
+                       continue;
+               }
+
+               status = mission_control_get_connection_status (mc, account, NULL);
+
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter,
+                                   COL_NAME, name,
+                                   COL_STATUS, status,
+                                   COL_ACCOUNT_POINTER, account,
+                                   -1);
+
+               accounts_dialog_status_changed_cb (mc,
+                                                  status,
+                                                  MC_PRESENCE_UNSET,
+                                                  TP_CONN_STATUS_REASON_NONE_SPECIFIED,
+                                                  mc_account_get_unique_name (account),
+                                                  dialog);
+
+               g_object_unref (account);
+       }
+
+       g_list_free (accounts);
+}
+
+static void
+accounts_dialog_update_connect_button (GossipAccountsDialog *dialog)
+{
+       McAccount   *account;
+       GtkWidget   *image;
+       const gchar *stock_id;
+       const gchar *label;
+
+       account = accounts_dialog_model_get_selected (dialog);
+       
+       if (!account) {
+               gtk_widget_set_sensitive (dialog->button_connect, FALSE);
+               return;
+       }
+
+       if (mc_account_is_enabled (account)) {
+               label = _("Disable");
+               stock_id = GTK_STOCK_DISCONNECT;
+       } else {
+               label = _("Enable");
+               stock_id = GTK_STOCK_CONNECT;
+       }
+
+       image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+
+       gtk_button_set_label (GTK_BUTTON (dialog->button_connect), label);
+       gtk_button_set_image (GTK_BUTTON (dialog->button_connect), image);
+}
+
+static void
+accounts_dialog_update_account (GossipAccountsDialog *dialog,
+                               McAccount            *account)
+{
+       if (dialog->settings_widget) {
+               gtk_widget_destroy (dialog->settings_widget);
+               dialog->settings_widget = NULL;
+       }
+
+       if (!account) {
+               GtkTreeView  *view;
+               GtkTreeModel *model;
+
+               gtk_widget_show (dialog->frame_no_account);
+               gtk_widget_hide (dialog->vbox_details);
+
+               gtk_widget_set_sensitive (dialog->button_connect, FALSE);
+               gtk_widget_set_sensitive (dialog->button_remove, FALSE);
+
+               view = GTK_TREE_VIEW (dialog->treeview);
+               model = gtk_tree_view_get_model (view);
+
+               if (gtk_tree_model_iter_n_children (model, NULL) > 0) {
+                       gtk_label_set_markup (GTK_LABEL (dialog->label_no_account),
+                                             _("<b>No Account Selected</b>"));
+                       gtk_label_set_markup (GTK_LABEL (dialog->label_no_account_blurb),
+                                             _("To add a new account, you can click on the "
+                                               "'Add' button and a new entry will be created "
+                                               "for you to start configuring.\n"
+                                               "\n"
+                                               "If you do not want to add an account, simply "
+                                               "click on the account you want to configure in "
+                                               "the list on the left."));
+               } else {
+                       gtk_label_set_markup (GTK_LABEL (dialog->label_no_account),
+                                             _("<b>No Accounts Configured</b>"));
+                       gtk_label_set_markup (GTK_LABEL (dialog->label_no_account_blurb),
+                                             _("To add a new account, you can click on the "
+                                               "'Add' button and a new entry will be created "
+                                               "for you to start configuring."));
+               }
+       } else {
+               McProfile *profile;
+               const gchar *config_ui;
+
+               gtk_widget_hide (dialog->frame_no_account);
+               gtk_widget_show (dialog->vbox_details);
+
+               profile = mc_account_get_profile (account);
+               config_ui = mc_profile_get_configuration_ui (profile);
+
+               if (strcmp (config_ui, "blah") == 0) {
+               } else {
+                       dialog->settings_widget = 
+                               gossip_account_widget_generic_new (account,
+                                                                  dialog->label_name);
+               }
+               
+               gtk_widget_grab_focus (dialog->settings_widget);
+       }
+
+       if (dialog->settings_widget) {
+               gtk_container_add (GTK_CONTAINER (dialog->alignment_settings),
+                                  dialog->settings_widget);
+       }
+
+       if (account) {
+               McProfile *profile;
+               GdkPixbuf *pixbuf;
+
+               pixbuf = gossip_pixbuf_from_account (account, GTK_ICON_SIZE_DIALOG);
+               gtk_image_set_from_pixbuf (GTK_IMAGE (dialog->image_type), pixbuf);
+               if (pixbuf) {
+                       g_object_unref (pixbuf);
+               }
+
+               profile = mc_account_get_profile (account);
+
+               gtk_label_set_text (GTK_LABEL (dialog->label_type),
+                                   mc_profile_get_display_name (profile));
+               gtk_label_set_text (GTK_LABEL (dialog->label_name), 
+                                   mc_account_get_display_name (account));
+       }
+}
+
+static void
+accounts_dialog_model_setup (GossipAccountsDialog *dialog)
+{
+       GtkListStore     *store;
+       GtkTreeSelection *selection;
+
+       store = gtk_list_store_new (COL_COUNT,
+                                   G_TYPE_STRING,     /* name */
+                                   G_TYPE_UINT,       /* status */
+                                   MC_TYPE_ACCOUNT);  /* account */
+
+       gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->treeview),
+                                GTK_TREE_MODEL (store));
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->treeview));
+       gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+       g_signal_connect (selection, "changed",
+                         G_CALLBACK (accounts_dialog_model_selection_changed),
+                         dialog);
+
+       gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+                                             COL_NAME, GTK_SORT_ASCENDING);
+
+       accounts_dialog_model_add_columns (dialog);
+
+       g_object_unref (store);
+}
+
+static void
+accounts_dialog_model_add_columns (GossipAccountsDialog *dialog)
+{
+       GtkTreeView       *view;
+       GtkTreeViewColumn *column;
+       GtkCellRenderer   *cell;
+
+       view = GTK_TREE_VIEW (dialog->treeview);
+       gtk_tree_view_set_headers_visible (view, TRUE);
+
+       /* account name/status */
+       column = gtk_tree_view_column_new ();
+       gtk_tree_view_column_set_title (column, _("Accounts"));
+
+       cell = gtk_cell_renderer_pixbuf_new ();
+       gtk_tree_view_column_pack_start (column, cell, FALSE);
+       gtk_tree_view_column_set_cell_data_func (column, cell,
+                                                (GtkTreeCellDataFunc)
+                                                accounts_dialog_model_pixbuf_data_func,
+                                                dialog,
+                                                NULL);
+
+       cell = gtk_cell_renderer_text_new ();
+       g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+       gtk_tree_view_column_pack_start (column, cell, TRUE);
+       gtk_tree_view_column_add_attribute (column,
+                                           cell,
+                                           "text", COL_NAME);
+
+       gtk_tree_view_column_set_expand (column, TRUE);
+       gtk_tree_view_append_column (view, column);
+}
+
+static void
+accounts_dialog_model_select_first (GossipAccountsDialog *dialog)
+{
+       GtkTreeView      *view;
+       GtkTreeModel     *model;
+       GtkTreeSelection *selection;
+       GtkTreeIter       iter;
+
+       /* select first */
+       view = GTK_TREE_VIEW (dialog->treeview);
+       model = gtk_tree_view_get_model (view);
+       
+       if (gtk_tree_model_get_iter_first (model, &iter)) {
+               selection = gtk_tree_view_get_selection (view);
+               gtk_tree_selection_select_iter (selection, &iter);
+       } else {
+               accounts_dialog_update_account (dialog, NULL);
+       }
+}
+
+static void
+accounts_dialog_model_pixbuf_data_func (GtkTreeViewColumn    *tree_column,
+                                       GtkCellRenderer      *cell,
+                                       GtkTreeModel         *model,
+                                       GtkTreeIter          *iter,
+                                       GossipAccountsDialog *dialog)
+{
+       McAccount                 *account;
+       GdkPixbuf                 *pixbuf;
+       TelepathyConnectionStatus  status;
+
+       gtk_tree_model_get (model, iter,
+                           COL_STATUS, &status,
+                           COL_ACCOUNT_POINTER, &account,
+                           -1);
+
+       pixbuf = gossip_pixbuf_from_account (account, GTK_ICON_SIZE_BUTTON);
+
+       if (pixbuf) {
+               if (status == TP_CONN_STATUS_DISCONNECTED ||
+                   (status == TP_CONN_STATUS_CONNECTING && 
+                    !dialog->connecting_show)) {
+                       GdkPixbuf *modded_pixbuf;
+
+                       modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+                                                       TRUE,
+                                                       8,
+                                                       gdk_pixbuf_get_width (pixbuf),
+                                                       gdk_pixbuf_get_height (pixbuf));
+
+                       gdk_pixbuf_saturate_and_pixelate (pixbuf,
+                                                         modded_pixbuf,
+                                                         1.0,
+                                                         TRUE);
+                       g_object_unref (pixbuf);
+                       pixbuf = modded_pixbuf;
+               }
+       }
+
+       g_object_set (cell,
+                     "visible", TRUE,
+                     "pixbuf", pixbuf,
+                     NULL);
+
+       g_object_unref (account);
+       if (pixbuf) {
+               g_object_unref (pixbuf);
+       }
+}
+
+static McAccount *
+accounts_dialog_model_get_selected (GossipAccountsDialog *dialog)
+{
+       GtkTreeView      *view;
+       GtkTreeModel     *model;
+       GtkTreeSelection *selection;
+       GtkTreeIter       iter;
+       McAccount        *account;
+
+       view = GTK_TREE_VIEW (dialog->treeview);
+       selection = gtk_tree_view_get_selection (view);
+
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+               return NULL;
+       }
+
+       gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, &account, -1);
+
+       return account;
+}
+
+static void
+accounts_dialog_model_set_selected (GossipAccountsDialog *dialog,
+                                   McAccount            *account)
+{
+       GtkTreeView      *view;
+       GtkTreeSelection *selection;
+       GtkTreeModel     *model;
+       GtkTreeIter       iter;
+       gboolean          ok;
+
+       view = GTK_TREE_VIEW (dialog->treeview);
+       model = gtk_tree_view_get_model (view);
+       selection = gtk_tree_view_get_selection (view);
+
+       for (ok = gtk_tree_model_get_iter_first (model, &iter);
+            ok;
+            ok = gtk_tree_model_iter_next (model, &iter)) {
+               McAccount *this_account;
+               gboolean   equal;
+
+               gtk_tree_model_get (model, &iter,
+                                   COL_ACCOUNT_POINTER, &this_account,
+                                   -1);
+
+               equal = gossip_account_equal (this_account, account);
+               g_object_unref (this_account);
+
+               if (equal) {
+                       gtk_tree_selection_select_iter (selection, &iter);
+                       break;
+               }
+       }
+}
+
+static gboolean
+accounts_dialog_model_remove_selected (GossipAccountsDialog *dialog)
+{
+       GtkTreeView      *view;
+       GtkTreeModel     *model;
+       GtkTreeSelection *selection;
+       GtkTreeIter       iter;
+
+       view = GTK_TREE_VIEW (dialog->treeview);
+       selection = gtk_tree_view_get_selection (view);
+
+       if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+               return FALSE;
+       }
+
+       return gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+}
+
+static void
+accounts_dialog_model_selection_changed (GtkTreeSelection     *selection,
+                                        GossipAccountsDialog *dialog)
+{
+       McAccount    *account;
+       GtkTreeModel *model;
+       GtkTreeIter   iter;
+       gboolean      is_selection;
+
+       is_selection = gtk_tree_selection_get_selected (selection, &model, &iter);
+
+       gtk_widget_set_sensitive (dialog->button_remove, is_selection);
+       gtk_widget_set_sensitive (dialog->button_connect, is_selection);
+
+       accounts_dialog_update_connect_button (dialog);
+
+       account = accounts_dialog_model_get_selected (dialog);
+       accounts_dialog_update_account (dialog, account);
+
+       if (account) {
+               g_object_unref (account);
+       }
+}
+
+static void
+accounts_dialog_add_account (GossipAccountsDialog *dialog,
+                            McAccount            *account)
+{
+       MissionControl            *mc;
+       TelepathyConnectionStatus  status;
+       const gchar               *name;
+       GtkTreeView               *view;
+       GtkTreeModel              *model;
+       GtkListStore              *store;
+       GtkTreeIter                iter;
+       gboolean                   ok;
+
+       view = GTK_TREE_VIEW (dialog->treeview);
+       model = gtk_tree_view_get_model (view);
+       store = GTK_LIST_STORE (model);
+
+       for (ok = gtk_tree_model_get_iter_first (model, &iter);
+            ok;
+            ok = gtk_tree_model_iter_next (model, &iter)) {
+               McAccount *this_account;
+               gboolean   equal;
+
+               gtk_tree_model_get (model, &iter,
+                                   COL_ACCOUNT_POINTER, &this_account,
+                                   -1);
+
+               equal =  gossip_account_equal (this_account, account);
+               g_object_unref (this_account);
+
+               if (equal) {
+                       return;
+               }
+       }
+
+       mc = empathy_session_get_mission_control ();
+       status = mission_control_get_connection_status (mc, account, NULL);
+       name = mc_account_get_display_name (account);
+
+       g_return_if_fail (name != NULL);
+
+       gossip_debug (DEBUG_DOMAIN, "Adding new account: %s", name);
+
+       gtk_list_store_append (store, &iter);
+       gtk_list_store_set (store, &iter,
+                           COL_NAME, name,
+                           COL_STATUS, status,
+                           COL_ACCOUNT_POINTER, account,
+                           -1);
+}
+
+static void
+accounts_dialog_account_added_cb (McAccountMonitor     *monitor,
+                                 gchar                *unique_name,
+                                 GossipAccountsDialog *dialog)
+{
+       McAccount *account;
+
+       account = mc_account_lookup (unique_name);
+       accounts_dialog_add_account (dialog, account);
+       g_object_unref (account);
+}
+
+static void
+accounts_dialog_account_removed_cb (McAccountMonitor     *monitor,
+                                   gchar                *unique_name,
+                                   GossipAccountsDialog *dialog)
+{
+       MissionControl *mc;
+       McAccount      *account;
+
+       mc = empathy_session_get_mission_control ();
+       account = mc_account_lookup (unique_name);
+
+       accounts_dialog_model_set_selected (dialog, account);
+       accounts_dialog_model_remove_selected (dialog);
+
+       g_object_unref (account);
+}
+
+static gboolean
+accounts_dialog_row_changed_foreach (GtkTreeModel *model,
+                                    GtkTreePath  *path,
+                                    GtkTreeIter  *iter,
+                                    gpointer      user_data)
+{
+       gtk_tree_model_row_changed (model, path, iter);
+
+       return FALSE;
+}
+
+static gboolean
+accounts_dialog_flash_connecting_cb (GossipAccountsDialog *dialog)
+{
+       GtkTreeView  *view;
+       GtkTreeModel *model;
+
+       dialog->connecting_show = !dialog->connecting_show;
+
+       view = GTK_TREE_VIEW (dialog->treeview);
+       model = gtk_tree_view_get_model (view);
+
+       gtk_tree_model_foreach (model, accounts_dialog_row_changed_foreach, NULL);
+
+       return TRUE;
+}
+
+static void
+accounts_dialog_status_changed_cb (MissionControl                  *mc,
+                                  TelepathyConnectionStatus        status,
+                                  McPresence                       presence,
+                                  TelepathyConnectionStatusReason  reason,
+                                  const gchar                     *unique_name,
+                                  GossipAccountsDialog            *dialog)
+{
+       GtkTreeView      *view;
+       GtkTreeSelection *selection;
+       GtkTreeModel     *model;
+       GtkTreeIter       iter;
+       gboolean          ok;
+       McAccount        *account;
+       GList            *accounts, *l;
+       gboolean          found = FALSE;
+       
+       /* Update the status in the model */
+       view = GTK_TREE_VIEW (dialog->treeview);
+       selection = gtk_tree_view_get_selection (view);
+       model = gtk_tree_view_get_model (view);
+       account = mc_account_lookup (unique_name);
+
+       for (ok = gtk_tree_model_get_iter_first (model, &iter);
+            ok;
+            ok = gtk_tree_model_iter_next (model, &iter)) {
+               McAccount *this_account;
+               gboolean   equal;
+
+               gtk_tree_model_get (model, &iter,
+                                   COL_ACCOUNT_POINTER, &this_account,
+                                   -1);
+
+               equal = gossip_account_equal (this_account, account);
+               g_object_unref (this_account);
+
+               if (equal) {
+                       GtkTreePath *path;
+
+                       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                                           COL_STATUS, status,
+                                           -1);
+
+                       path = gtk_tree_model_get_path (model, &iter);
+                       gtk_tree_model_row_changed (model, path, &iter);
+                       gtk_tree_path_free (path);
+
+                       break;
+               }
+       }
+
+       g_object_unref (account);
+
+       /* Start to flash account if status is connecting */
+       if (status == TP_CONN_STATUS_CONNECTING) {
+               if (!dialog->connecting_id) {
+                       dialog->connecting_id = g_timeout_add (FLASH_TIMEOUT,
+                                                              (GSourceFunc) accounts_dialog_flash_connecting_cb,
+                                                              dialog);
+               }
+
+               return;
+       }
+
+       /* Stop to flash if no account is connecting */
+       accounts = mc_accounts_list ();
+       for (l = accounts; l; l = l->next) {
+               McAccount *this_account;
+
+               this_account = l->data;
+
+               if (mission_control_get_connection_status (mc, this_account, NULL) == TP_CONN_STATUS_CONNECTING) {
+                       found = TRUE;
+                       break;
+               }
+
+               g_object_unref (this_account);
+       }
+       g_list_free (accounts);
+
+       if (!found && dialog->connecting_id) {
+               g_source_remove (dialog->connecting_id);
+               dialog->connecting_id = 0;
+       }
+}
+
+static void          
+accounts_dialog_entry_name_changed_cb (GtkWidget             *widget,
+                                      GossipAccountsDialog  *dialog)
+{
+       const gchar *str;
+       
+       str = gtk_entry_get_text (GTK_ENTRY (widget));
+       gtk_widget_set_sensitive (dialog->button_create, !G_STR_EMPTY (str));
+}
+
+static void
+accounts_dialog_button_create_clicked_cb (GtkWidget             *button,
+                                         GossipAccountsDialog  *dialog)
+{
+       McProfile   *profile;
+       McAccount   *account;
+       const gchar *str;
+
+       /* Update widgets */
+       gtk_widget_show (dialog->vbox_details);
+       gtk_widget_hide (dialog->frame_no_account);
+       gtk_widget_hide (dialog->frame_new_account);
+
+       profile = gossip_profile_chooser_get_selected (dialog->combobox_profile);
+
+       /* Create account */
+       account = mc_account_create (profile);
+
+       str = gtk_entry_get_text (GTK_ENTRY (dialog->entry_name));
+       mc_account_set_display_name (account, str);
+
+       accounts_dialog_add_account (dialog, account);
+       accounts_dialog_model_set_selected (dialog, account);
+
+       g_object_unref (account);
+       g_object_unref (profile);
+}
+
+static void
+accounts_dialog_button_cancel_clicked_cb (GtkWidget             *button,
+                                         GossipAccountsDialog  *dialog)
+{
+       McAccount *account;
+
+       gtk_widget_hide (dialog->vbox_details);
+       gtk_widget_hide (dialog->frame_no_account);
+       gtk_widget_hide (dialog->frame_new_account);
+
+       account = accounts_dialog_model_get_selected (dialog);
+       accounts_dialog_update_account (dialog, account);
+}
+
+static void
+accounts_dialog_button_connect_clicked_cb (GtkWidget            *button,
+                                          GossipAccountsDialog *dialog)
+{
+       McAccount *account;
+       gboolean   enable;
+
+       account = accounts_dialog_model_get_selected (dialog);
+       enable = (!mc_account_is_enabled (account));
+       mc_account_set_enabled (account, enable);
+       accounts_dialog_update_connect_button (dialog);
+
+       g_object_unref (account);
+}
+
+static void
+accounts_dialog_button_add_clicked_cb (GtkWidget            *button,
+                                      GossipAccountsDialog *dialog)
+{
+       gtk_widget_hide (dialog->vbox_details);
+       gtk_widget_hide (dialog->frame_no_account);
+       gtk_widget_show (dialog->frame_new_account);
+
+       gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->combobox_profile), 0);
+       gtk_entry_set_text (GTK_ENTRY (dialog->entry_name), "");
+       gtk_widget_grab_focus (dialog->entry_name);
+}
+
+static void
+accounts_dialog_remove_response_cb (GtkWidget *dialog,
+                                   gint       response,
+                                   McAccount *account)
+{
+       if (response == GTK_RESPONSE_YES) {
+               mc_account_delete (account);
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+accounts_dialog_button_remove_clicked_cb (GtkWidget            *button,
+                                         GossipAccountsDialog *dialog)
+{
+       McAccount *account;
+       GtkWidget *message_dialog;
+
+       account = accounts_dialog_model_get_selected (dialog);
+
+       if (!mc_account_is_complete (account)) {
+               accounts_dialog_model_remove_selected (dialog);
+               return;
+       }
+       message_dialog = gtk_message_dialog_new
+               (GTK_WINDOW (dialog->window),
+                GTK_DIALOG_DESTROY_WITH_PARENT,
+                GTK_MESSAGE_QUESTION,
+                GTK_BUTTONS_NONE,
+                _("You are about to remove your %s account!\n"
+                  "Are you sure you want to proceed?"),
+                mc_account_get_display_name (account));
+
+       gtk_message_dialog_format_secondary_text
+               (GTK_MESSAGE_DIALOG (message_dialog),
+                _("Any associated conversations and chat rooms will NOT be "
+                  "removed if you decide to proceed.\n"
+                  "\n"
+                  "Should you decide to add the account back at a later time, "
+                  "they will still be available."));
+
+       gtk_dialog_add_button (GTK_DIALOG (message_dialog),
+                              GTK_STOCK_CANCEL, 
+                              GTK_RESPONSE_NO);
+       gtk_dialog_add_button (GTK_DIALOG (message_dialog),
+                              GTK_STOCK_REMOVE, 
+                              GTK_RESPONSE_YES);
+
+       g_signal_connect (message_dialog, "response",
+                         G_CALLBACK (accounts_dialog_remove_response_cb),
+                         account);
+
+       gtk_widget_show (message_dialog);
+}
+
+static void
+accounts_dialog_treeview_row_activated_cb (GtkTreeView          *tree_view,
+                                          GtkTreePath          *path,
+                                          GtkTreeViewColumn    *column,
+                                          GossipAccountsDialog *dialog)
+{
+
+       accounts_dialog_button_connect_clicked_cb (dialog->button_connect,
+                                                  dialog);
+}
+
+static void
+accounts_dialog_response_cb (GtkWidget            *widget,
+                            gint                  response,
+                            GossipAccountsDialog *dialog)
+{
+       gtk_widget_destroy (widget);
+}
+
+static void
+accounts_dialog_destroy_cb (GtkWidget            *widget,
+                           GossipAccountsDialog *dialog)
+{
+       MissionControl   *mc;
+       McAccountMonitor *monitor;
+       GList            *accounts, *l;
+
+       mc = empathy_session_get_mission_control ();
+       monitor = mc_account_monitor_new ();
+
+       /* Disconnect signals */
+       g_signal_handlers_disconnect_by_func (monitor,
+                                             accounts_dialog_account_added_cb,
+                                             dialog);
+       g_signal_handlers_disconnect_by_func (monitor,
+                                             accounts_dialog_account_removed_cb,
+                                             dialog);
+       dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (mc), "AccountStatusChanged",
+                                    G_CALLBACK (accounts_dialog_status_changed_cb),
+                                    dialog);
+
+       /* Delete incomplete accounts */
+       accounts = mc_accounts_list ();
+       for (l = accounts; l; l = l->next) {
+               McAccount *account;
+
+               account = l->data;
+               if (!mc_account_is_complete (account)) {
+                       /* FIXME: Warn the user the account is not complete
+                        *        and is going to be removed. */
+                       mc_account_delete (account);
+               }
+
+               g_object_unref (account);
+       }
+       g_list_free (accounts);
+
+       if (dialog->connecting_id) {
+               g_source_remove (dialog->connecting_id);
+       }
+
+       g_free (dialog);
+}
+
+GtkWidget *
+gossip_accounts_dialog_show (void)
+{
+       static GossipAccountsDialog *dialog = NULL;
+       MissionControl              *mc;
+       McAccountMonitor            *monitor;
+       GladeXML                    *glade;
+       GtkWidget                   *bbox;
+       GtkWidget                   *button_close;
+
+       if (dialog) {
+               gtk_window_present (GTK_WINDOW (dialog->window));
+               return dialog->window;
+       }
+
+       dialog = g_new0 (GossipAccountsDialog, 1);
+
+       glade = gossip_glade_get_file ("empathy-accounts.glade",
+                                      "accounts_dialog",
+                                      NULL,
+                                      "accounts_dialog", &dialog->window,
+                                      "vbox_details", &dialog->vbox_details,
+                                      "frame_no_account", &dialog->frame_no_account,
+                                      "label_no_account", &dialog->label_no_account,
+                                      "label_no_account_blurb", &dialog->label_no_account_blurb,
+                                      "alignment_settings", &dialog->alignment_settings,
+                                      "dialog-action_area", &bbox,
+                                      "treeview", &dialog->treeview,
+                                      "frame_new_account", &dialog->frame_new_account,
+                                      "entry_name", &dialog->entry_name,
+                                      "table_new_account", &dialog->table_new_account,
+                                      "button_create", &dialog->button_create,
+                                      "button_cancel", &dialog->button_cancel,
+                                      "image_type", &dialog->image_type,
+                                      "label_type", &dialog->label_type,
+                                      "label_name", &dialog->label_name,
+                                      "button_remove", &dialog->button_remove,
+                                      "button_connect", &dialog->button_connect,
+                                      "button_close", &button_close,
+                                      NULL);
+
+       gossip_glade_connect (glade,
+                             dialog,
+                             "accounts_dialog", "destroy", accounts_dialog_destroy_cb,
+                             "accounts_dialog", "response", accounts_dialog_response_cb,
+                             "button_create", "clicked", accounts_dialog_button_create_clicked_cb,
+                             "button_cancel", "clicked", accounts_dialog_button_cancel_clicked_cb,
+                             "entry_name", "changed", accounts_dialog_entry_name_changed_cb,
+                             "treeview", "row-activated", accounts_dialog_treeview_row_activated_cb,
+                             "button_connect", "clicked", accounts_dialog_button_connect_clicked_cb,
+                             "button_add", "clicked", accounts_dialog_button_add_clicked_cb,
+                             "button_remove", "clicked", accounts_dialog_button_remove_clicked_cb,
+                             NULL);
+
+       g_object_add_weak_pointer (G_OBJECT (dialog->window), (gpointer) &dialog);
+
+       g_object_unref (glade);
+
+       /* Create profile chooser */
+       dialog->combobox_profile = gossip_profile_chooser_new ();
+       gtk_table_attach_defaults (GTK_TABLE (dialog->table_new_account),
+                                  dialog->combobox_profile,
+                                  1, 2,
+                                  0, 1);
+       gtk_widget_show (dialog->combobox_profile);
+
+       /* Set up signalling */
+       mc = empathy_session_get_mission_control ();
+       monitor = mc_account_monitor_new ();
+
+       /* FIXME: connect account-enabled/disabled too */
+       g_signal_connect (monitor, "account-created",
+                         G_CALLBACK (accounts_dialog_account_added_cb),
+                         dialog);
+       g_signal_connect (monitor, "account-deleted",
+                         G_CALLBACK (accounts_dialog_account_removed_cb),
+                         dialog);
+       dbus_g_proxy_connect_signal (DBUS_G_PROXY (mc), "AccountStatusChanged",
+                                    G_CALLBACK (accounts_dialog_status_changed_cb),
+                                    dialog, NULL);
+
+       accounts_dialog_model_setup (dialog);
+       accounts_dialog_setup (dialog);
+
+       gtk_widget_show (dialog->window);
+
+       accounts_dialog_model_select_first (dialog);
+
+       return dialog->window;
+}
+
diff --git a/libempathy-gtk/gossip-accounts-dialog.h b/libempathy-gtk/gossip-accounts-dialog.h
new file mode 100644 (file)
index 0000000..b6b0593
--- /dev/null
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Martyn Russell <martyn@imendio.com>
+ */
+
+#ifndef __GOSSIP_ACCOUNTS_DIALOG_H__
+#define __GOSSIP_ACCOUNTS_DIALOG_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+GtkWidget *gossip_accounts_dialog_show (void);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_ACCOUNTS_DIALOG_H__ */
diff --git a/libempathy-gtk/gossip-cell-renderer-expander.c b/libempathy-gtk/gossip-cell-renderer-expander.c
new file mode 100644 (file)
index 0000000..e116ace
--- /dev/null
@@ -0,0 +1,482 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Kristian Rietveld <kris@imendio.com>
+ */
+
+/* To do:
+ *  - should probably cancel animation if model changes
+ *  - need to handle case where node-in-animation is removed
+ *  - it only handles a single animation at a time; but I guess users
+ *    aren't fast enough to trigger two or more animations at once anyway :P
+ *    (could guard for this by just cancelling the "old" animation, and
+ *     start the new one).
+ */
+
+#include <gtk/gtktreeview.h>
+
+#include "gossip-cell-renderer-expander.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CELL_RENDERER_EXPANDER, GossipCellRendererExpanderPriv))
+
+static void     gossip_cell_renderer_expander_init         (GossipCellRendererExpander      *expander);
+static void     gossip_cell_renderer_expander_class_init   (GossipCellRendererExpanderClass *klass);
+static void     gossip_cell_renderer_expander_get_property (GObject                         *object,
+                                                           guint                            param_id,
+                                                           GValue                          *value,
+                                                           GParamSpec                      *pspec);
+static void     gossip_cell_renderer_expander_set_property (GObject                         *object,
+                                                           guint                            param_id,
+                                                           const GValue                    *value,
+                                                           GParamSpec                      *pspec);
+static void     gossip_cell_renderer_expander_finalize     (GObject                         *object);
+static void     gossip_cell_renderer_expander_get_size     (GtkCellRenderer                 *cell,
+                                                           GtkWidget                       *widget,
+                                                           GdkRectangle                    *cell_area,
+                                                           gint                            *x_offset,
+                                                           gint                            *y_offset,
+                                                           gint                            *width,
+                                                           gint                            *height);
+static void     gossip_cell_renderer_expander_render       (GtkCellRenderer                 *cell,
+                                                           GdkWindow                       *window,
+                                                           GtkWidget                       *widget,
+                                                           GdkRectangle                    *background_area,
+                                                           GdkRectangle                    *cell_area,
+                                                           GdkRectangle                    *expose_area,
+                                                           GtkCellRendererState             flags);
+static gboolean gossip_cell_renderer_expander_activate     (GtkCellRenderer                 *cell,
+                                                           GdkEvent                        *event,
+                                                           GtkWidget                       *widget,
+                                                           const gchar                     *path,
+                                                           GdkRectangle                    *background_area,
+                                                           GdkRectangle                    *cell_area,
+                                                           GtkCellRendererState             flags);
+
+enum {
+       PROP_0,
+       PROP_EXPANDER_STYLE,
+       PROP_EXPANDER_SIZE,
+       PROP_ACTIVATABLE
+};
+
+typedef struct _GossipCellRendererExpanderPriv GossipCellRendererExpanderPriv;
+
+struct _GossipCellRendererExpanderPriv {
+       GtkExpanderStyle     expander_style;
+       gint                 expander_size;
+
+       GtkTreeView         *animation_view;
+       GtkTreeRowReference *animation_node;
+       GtkExpanderStyle     animation_style;
+       guint                animation_timeout;
+       GdkRectangle         animation_area;
+
+       guint                activatable : 1;
+       guint                animation_expanding : 1;
+};
+
+G_DEFINE_TYPE (GossipCellRendererExpander, gossip_cell_renderer_expander, GTK_TYPE_CELL_RENDERER)
+
+static void
+gossip_cell_renderer_expander_init (GossipCellRendererExpander *expander)
+{
+       GossipCellRendererExpanderPriv *priv;
+
+       priv = GET_PRIV (expander);
+
+       priv->expander_style = GTK_EXPANDER_COLLAPSED;
+       priv->expander_size = 12;
+       priv->activatable = TRUE;
+       priv->animation_node = NULL;
+
+       GTK_CELL_RENDERER (expander)->xpad = 2;
+       GTK_CELL_RENDERER (expander)->ypad = 2;
+       GTK_CELL_RENDERER (expander)->mode = GTK_CELL_RENDERER_MODE_ACTIVATABLE;
+}
+
+static void
+gossip_cell_renderer_expander_class_init (GossipCellRendererExpanderClass *klass)
+{
+       GObjectClass         *object_class;
+       GtkCellRendererClass *cell_class;
+
+       object_class  = G_OBJECT_CLASS (klass);
+       cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+       object_class->finalize = gossip_cell_renderer_expander_finalize;
+
+       object_class->get_property = gossip_cell_renderer_expander_get_property;
+       object_class->set_property = gossip_cell_renderer_expander_set_property;
+
+       cell_class->get_size = gossip_cell_renderer_expander_get_size;
+       cell_class->render = gossip_cell_renderer_expander_render;
+       cell_class->activate = gossip_cell_renderer_expander_activate;
+
+       g_object_class_install_property (object_class,
+                                        PROP_EXPANDER_STYLE,
+                                        g_param_spec_enum ("expander-style",
+                                                           "Expander Style",
+                                                           "Style to use when painting the expander",
+                                                           GTK_TYPE_EXPANDER_STYLE,
+                                                           GTK_EXPANDER_COLLAPSED,
+                                                           G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_EXPANDER_SIZE,
+                                        g_param_spec_int ("expander-size",
+                                                          "Expander Size",
+                                                          "The size of the expander",
+                                                          0,
+                                                          G_MAXINT,
+                                                          12,
+                                                          G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_ACTIVATABLE,
+                                        g_param_spec_boolean ("activatable",
+                                                              "Activatable",
+                                                              "The expander can be activated",
+                                                              TRUE,
+                                                              G_PARAM_READWRITE));
+
+       g_type_class_add_private (object_class, sizeof (GossipCellRendererExpanderPriv));
+}
+
+static void
+gossip_cell_renderer_expander_get_property (GObject    *object,
+                                           guint       param_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+       GossipCellRendererExpander     *expander;
+       GossipCellRendererExpanderPriv *priv;
+
+       expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
+       priv = GET_PRIV (expander);
+
+       switch (param_id) {
+       case PROP_EXPANDER_STYLE:
+               g_value_set_enum (value, priv->expander_style);
+               break;
+
+       case PROP_EXPANDER_SIZE:
+               g_value_set_int (value, priv->expander_size);
+               break;
+
+       case PROP_ACTIVATABLE:
+               g_value_set_boolean (value, priv->activatable);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       }
+}
+
+static void
+gossip_cell_renderer_expander_set_property (GObject      *object,
+                                           guint         param_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+       GossipCellRendererExpander     *expander;
+       GossipCellRendererExpanderPriv *priv;
+
+       expander = GOSSIP_CELL_RENDERER_EXPANDER (object);
+       priv = GET_PRIV (expander);
+
+       switch (param_id) {
+       case PROP_EXPANDER_STYLE:
+               priv->expander_style = g_value_get_enum (value);
+               break;
+
+       case PROP_EXPANDER_SIZE:
+               priv->expander_size = g_value_get_int (value);
+               break;
+
+       case PROP_ACTIVATABLE:
+               priv->activatable = g_value_get_boolean (value);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       }
+}
+
+static void
+gossip_cell_renderer_expander_finalize (GObject *object)
+{
+       GossipCellRendererExpanderPriv *priv;
+
+       priv = GET_PRIV (object);
+
+       if (priv->animation_timeout) {
+               g_source_remove (priv->animation_timeout);
+               priv->animation_timeout = 0;
+       }
+
+       if (priv->animation_node) {
+               gtk_tree_row_reference_free (priv->animation_node);
+       }
+
+       (* G_OBJECT_CLASS (gossip_cell_renderer_expander_parent_class)->finalize) (object);
+}
+
+GtkCellRenderer *
+gossip_cell_renderer_expander_new (void)
+{
+       return g_object_new (GOSSIP_TYPE_CELL_RENDERER_EXPANDER, NULL);
+}
+
+static void
+gossip_cell_renderer_expander_get_size (GtkCellRenderer *cell,
+                                       GtkWidget       *widget,
+                                       GdkRectangle    *cell_area,
+                                       gint            *x_offset,
+                                       gint            *y_offset,
+                                       gint            *width,
+                                       gint            *height)
+{
+       GossipCellRendererExpander     *expander;
+       GossipCellRendererExpanderPriv *priv;
+
+       expander = (GossipCellRendererExpander*) cell;
+       priv = GET_PRIV (expander);
+
+       if (cell_area) {
+               if (x_offset) {
+                       *x_offset = cell->xalign * (cell_area->width - (priv->expander_size + (2 * cell->xpad)));
+                       *x_offset = MAX (*x_offset, 0);
+               }
+
+               if (y_offset) {
+                       *y_offset = cell->yalign * (cell_area->height - (priv->expander_size + (2 * cell->ypad)));
+                       *y_offset = MAX (*y_offset, 0);
+               }
+       } else {
+               if (x_offset)
+                       *x_offset = 0;
+
+               if (y_offset)
+                       *y_offset = 0;
+       }
+
+       if (width)
+               *width = cell->xpad * 2 + priv->expander_size;
+
+       if (height)
+               *height = cell->ypad * 2 + priv->expander_size;
+}
+
+static void
+gossip_cell_renderer_expander_render (GtkCellRenderer      *cell,
+                                     GdkWindow            *window,
+                                     GtkWidget            *widget,
+                                     GdkRectangle         *background_area,
+                                     GdkRectangle         *cell_area,
+                                     GdkRectangle         *expose_area,
+                                     GtkCellRendererState  flags)
+{
+       GossipCellRendererExpander     *expander;
+       GossipCellRendererExpanderPriv *priv;
+       GtkExpanderStyle                expander_style;
+       gint                            x_offset, y_offset;
+
+       expander = (GossipCellRendererExpander*) cell;
+       priv = GET_PRIV (expander);
+
+       if (priv->animation_node) {
+               GtkTreePath *path;
+               GdkRectangle rect;
+
+               /* Not sure if I like this ... */
+               path = gtk_tree_row_reference_get_path (priv->animation_node);
+               gtk_tree_view_get_background_area (priv->animation_view, path,
+                                                  NULL, &rect);
+               gtk_tree_path_free (path);
+
+               if (background_area->y == rect.y)
+                       expander_style = priv->animation_style;
+               else
+                       expander_style = priv->expander_style;
+       } else
+               expander_style = priv->expander_style;
+
+       gossip_cell_renderer_expander_get_size (cell, widget, cell_area,
+                                               &x_offset, &y_offset,
+                                               NULL, NULL);
+
+       gtk_paint_expander (widget->style,
+                           window,
+                           GTK_STATE_NORMAL,
+                           expose_area,
+                           widget,
+                           "treeview",
+                           cell_area->x + x_offset + cell->xpad + priv->expander_size / 2,
+                           cell_area->y + y_offset + cell->ypad + priv->expander_size / 2,
+                           expander_style);
+}
+
+static void
+invalidate_node (GtkTreeView *tree_view,
+                GtkTreePath *path)
+{
+       GdkWindow    *bin_window;
+       GdkRectangle  rect;
+
+       bin_window = gtk_tree_view_get_bin_window (tree_view);
+
+       gtk_tree_view_get_background_area (tree_view, path, NULL, &rect);
+
+       rect.x = 0;
+       rect.width = GTK_WIDGET (tree_view)->allocation.width;
+
+       gdk_window_invalidate_rect (bin_window, &rect, TRUE);
+}
+
+static gboolean
+do_animation (GossipCellRendererExpander *expander)
+{
+       GossipCellRendererExpanderPriv *priv;
+       GtkTreePath                    *path;
+       gboolean                        done = FALSE;
+
+       priv = GET_PRIV (expander);
+
+       if (priv->animation_expanding) {
+               if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED)
+                       priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
+               else if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED) {
+                       priv->animation_style = GTK_EXPANDER_EXPANDED;
+                       done = TRUE;
+               }
+       } else {
+               if (priv->animation_style == GTK_EXPANDER_SEMI_EXPANDED)
+                       priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
+               else if (priv->animation_style == GTK_EXPANDER_SEMI_COLLAPSED) {
+                       priv->animation_style = GTK_EXPANDER_COLLAPSED;
+                       done = TRUE;
+               }
+       }
+
+       path = gtk_tree_row_reference_get_path (priv->animation_node);
+       invalidate_node (priv->animation_view, path);
+       gtk_tree_path_free (path);
+
+       if (done) {
+               gtk_tree_row_reference_free (priv->animation_node);
+               priv->animation_node = NULL;
+               priv->animation_timeout = 0;
+       }
+
+       return !done;
+}
+
+static gboolean
+animation_timeout (gpointer data)
+{
+       gboolean retval;
+
+       GDK_THREADS_ENTER ();
+
+       retval = do_animation (data);
+
+       GDK_THREADS_LEAVE ();
+
+       return retval;
+}
+
+static void
+gossip_cell_renderer_expander_start_animation (GossipCellRendererExpander *expander,
+                                              GtkTreeView                *tree_view,
+                                              GtkTreePath                *path,
+                                              gboolean                    expanding,
+                                              GdkRectangle               *background_area)
+{
+       GossipCellRendererExpanderPriv *priv;
+
+       priv = GET_PRIV (expander);
+
+       if (expanding) {
+               priv->animation_style = GTK_EXPANDER_SEMI_COLLAPSED;
+       } else {
+               priv->animation_style = GTK_EXPANDER_SEMI_EXPANDED;
+       }
+
+       invalidate_node (tree_view, path);
+
+       priv->animation_expanding = expanding;
+       priv->animation_view = tree_view;
+       priv->animation_node = gtk_tree_row_reference_new (gtk_tree_view_get_model (tree_view), path);
+       priv->animation_timeout = g_timeout_add (50, animation_timeout, expander);
+}
+
+static gboolean
+gossip_cell_renderer_expander_activate (GtkCellRenderer      *cell,
+                                       GdkEvent             *event,
+                                       GtkWidget            *widget,
+                                       const gchar          *path_string,
+                                       GdkRectangle         *background_area,
+                                       GdkRectangle         *cell_area,
+                                       GtkCellRendererState  flags)
+{
+       GossipCellRendererExpander     *expander;
+       GossipCellRendererExpanderPriv *priv;
+       GtkTreePath                    *path;
+       gboolean                        animate;
+       gboolean                        expanding;
+
+       expander = GOSSIP_CELL_RENDERER_EXPANDER (cell);
+       priv = GET_PRIV (cell);
+
+       if (!GTK_IS_TREE_VIEW (widget) || !priv->activatable)
+               return FALSE;
+
+       path = gtk_tree_path_new_from_string (path_string);
+
+       if (gtk_tree_path_get_depth (path) > 1) {
+               gtk_tree_path_free (path);
+               return TRUE;
+       }
+
+       g_object_get (gtk_widget_get_settings (GTK_WIDGET (widget)),
+                     "gtk-enable-animations", &animate,
+                     NULL);
+
+       if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
+               gtk_tree_view_collapse_row (GTK_TREE_VIEW (widget), path);
+               expanding = FALSE;
+       } else {
+               gtk_tree_view_expand_row (GTK_TREE_VIEW (widget), path, FALSE);
+               expanding = TRUE;
+       }
+
+       if (animate) {
+               gossip_cell_renderer_expander_start_animation (expander,
+                                                              GTK_TREE_VIEW (widget),
+                                                              path,
+                                                              expanding,
+                                                              background_area);
+       }
+
+       gtk_tree_path_free (path);
+
+       return TRUE;
+}
diff --git a/libempathy-gtk/gossip-cell-renderer-expander.h b/libempathy-gtk/gossip-cell-renderer-expander.h
new file mode 100644 (file)
index 0000000..7df4746
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2006-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Kristian Rietveld <kris@imendio.com>
+ */
+
+#ifndef __GOSSIP_CELL_RENDERER_EXPANDER_H__
+#define __GOSSIP_CELL_RENDERER_EXPANDER_H__
+
+#include <gtk/gtkcellrenderer.h>
+
+G_BEGIN_DECLS
+
+#define GOSSIP_TYPE_CELL_RENDERER_EXPANDER             (gossip_cell_renderer_expander_get_type ())
+#define GOSSIP_CELL_RENDERER_EXPANDER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOSSIP_TYPE_CELL_RENDERER_EXPANDER, GossipCellRendererExpander))
+#define GOSSIP_CELL_RENDERER_EXPANDER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GOSSIP_TYPE_CELL_RENDERER_EXPANDER, GossipCellRendererExpanderClass))
+#define GOSSIP_IS_CELL_RENDERER_EXPANDER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOSSIP_TYPE_CELL_RENDERER_EXPANDER))
+#define GOSSIP_IS_CELL_RENDERER_EXPANDER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GOSSIP_TYPE_CELL_RENDERER_EXPANDER))
+#define GOSSIP_CELL_RENDERER_EXPANDER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GOSSIP_TYPE_CELL_RENDERER_EXPANDER, GossipCellRendererExpanderClass))
+
+typedef struct _GossipCellRendererExpander GossipCellRendererExpander;
+typedef struct _GossipCellRendererExpanderClass GossipCellRendererExpanderClass;
+
+struct _GossipCellRendererExpander {
+  GtkCellRenderer parent;
+};
+
+struct _GossipCellRendererExpanderClass {
+  GtkCellRendererClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
+  void (*_gtk_reserved2) (void);
+  void (*_gtk_reserved3) (void);
+  void (*_gtk_reserved4) (void);
+};
+
+GType            gossip_cell_renderer_expander_get_type (void) G_GNUC_CONST;
+GtkCellRenderer *gossip_cell_renderer_expander_new      (void);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_CELL_RENDERER_EXPANDER_H__ */
diff --git a/libempathy-gtk/gossip-cell-renderer-text.c b/libempathy-gtk/gossip-cell-renderer-text.c
new file mode 100644 (file)
index 0000000..2b54eed
--- /dev/null
@@ -0,0 +1,368 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Mikael Hallendal <micke@imendio.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gossip-cell-renderer-text.h"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CELL_RENDERER_TEXT, GossipCellRendererTextPriv))
+
+struct _GossipCellRendererTextPriv {
+       gchar    *name;
+       gchar    *status;
+       gboolean  is_group;
+
+       gboolean  is_valid;
+       gboolean  is_selected;
+
+       gboolean  show_status;
+};
+
+static void gossip_cell_renderer_text_class_init (GossipCellRendererTextClass *klass);
+static void gossip_cell_renderer_text_init       (GossipCellRendererText      *cell);
+static void cell_renderer_text_finalize          (GObject                     *object);
+static void cell_renderer_text_get_property      (GObject                     *object,
+                                                 guint                        param_id,
+                                                 GValue                      *value,
+                                                 GParamSpec                  *pspec);
+static void cell_renderer_text_set_property      (GObject                     *object,
+                                                 guint                        param_id,
+                                                 const GValue                *value,
+                                                 GParamSpec                  *pspec);
+static void cell_renderer_text_get_size          (GtkCellRenderer             *cell,
+                                                 GtkWidget                   *widget,
+                                                 GdkRectangle                *cell_area,
+                                                 gint                        *x_offset,
+                                                 gint                        *y_offset,
+                                                 gint                        *width,
+                                                 gint                        *height);
+static void cell_renderer_text_render            (GtkCellRenderer             *cell,
+                                                 GdkDrawable                 *window,
+                                                 GtkWidget                   *widget,
+                                                 GdkRectangle                *background_area,
+                                                 GdkRectangle                *cell_area,
+                                                 GdkRectangle                *expose_area,
+                                                 GtkCellRendererState         flags);
+static void cell_renderer_text_update_text       (GossipCellRendererText      *cell,
+                                                 GtkWidget                   *widget,
+                                                 gboolean                     selected);
+
+/* Properties */
+enum {
+       PROP_0,
+       PROP_NAME,
+       PROP_STATUS,
+       PROP_IS_GROUP,
+       PROP_SHOW_STATUS,
+};
+
+G_DEFINE_TYPE (GossipCellRendererText, gossip_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);
+
+static void
+gossip_cell_renderer_text_class_init (GossipCellRendererTextClass *klass)
+{
+       GObjectClass         *object_class;
+       GtkCellRendererClass *cell_class;
+
+       object_class = G_OBJECT_CLASS (klass);
+       cell_class = GTK_CELL_RENDERER_CLASS (klass);
+
+       object_class->finalize = cell_renderer_text_finalize;
+
+       object_class->get_property = cell_renderer_text_get_property;
+       object_class->set_property = cell_renderer_text_set_property;
+
+       cell_class->get_size = cell_renderer_text_get_size;
+       cell_class->render = cell_renderer_text_render;
+
+       g_object_class_install_property (object_class,
+                                        PROP_NAME,
+                                        g_param_spec_string ("name",
+                                                             "Name",
+                                                             "Contact name",
+                                                             NULL,
+                                                             G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_STATUS,
+                                        g_param_spec_string ("status",
+                                                             "Status",
+                                                             "Contact status string",
+                                                             NULL,
+                                                             G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_IS_GROUP,
+                                        g_param_spec_boolean ("is_group",
+                                                              "Is group",
+                                                              "Whether this cell is a group",
+                                                              FALSE,
+                                                              G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_SHOW_STATUS,
+                                        g_param_spec_boolean ("show-status",
+                                                              "Show status",
+                                                              "Whether to show the status line",
+                                                              TRUE,
+                                                              G_PARAM_READWRITE));
+
+       g_type_class_add_private (object_class, sizeof (GossipCellRendererTextPriv));
+}
+
+static void
+gossip_cell_renderer_text_init (GossipCellRendererText *cell)
+{
+       GossipCellRendererTextPriv *priv;
+
+       priv = GET_PRIV (cell);
+
+       g_object_set (cell,
+                     "ellipsize", PANGO_ELLIPSIZE_END,
+                     NULL);
+
+       priv->name = g_strdup ("");
+       priv->status = g_strdup ("");
+       priv->show_status = TRUE;
+}
+
+static void
+cell_renderer_text_finalize (GObject *object)
+{
+       GossipCellRendererText     *cell;
+       GossipCellRendererTextPriv *priv;
+
+       cell = GOSSIP_CELL_RENDERER_TEXT (object);
+       priv = GET_PRIV (cell);
+
+       g_free (priv->name);
+       g_free (priv->status);
+
+       (G_OBJECT_CLASS (gossip_cell_renderer_text_parent_class)->finalize) (object);
+}
+
+static void
+cell_renderer_text_get_property (GObject    *object,
+                                guint       param_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+       GossipCellRendererText     *cell;
+       GossipCellRendererTextPriv *priv;
+
+       cell = GOSSIP_CELL_RENDERER_TEXT (object);
+       priv = GET_PRIV (cell);
+
+       switch (param_id) {
+       case PROP_NAME:
+               g_value_set_string (value, priv->name);
+               break;
+       case PROP_STATUS:
+               g_value_set_string (value, priv->status);
+               break;
+       case PROP_IS_GROUP:
+               g_value_set_boolean (value, priv->is_group);
+               break;
+       case PROP_SHOW_STATUS:
+               g_value_set_boolean (value, priv->show_status);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       }
+}
+
+static void
+cell_renderer_text_set_property (GObject      *object,
+                                guint         param_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+       GossipCellRendererText     *cell;
+       GossipCellRendererTextPriv *priv;
+       const gchar                *str;
+
+       cell = GOSSIP_CELL_RENDERER_TEXT (object);
+       priv = GET_PRIV (cell);
+
+       switch (param_id) {
+       case PROP_NAME:
+               g_free (priv->name);
+               str = g_value_get_string (value);
+               priv->name = g_strdup (str ? str : "");
+               g_strdelimit (priv->name, "\n\r\t", ' ');
+               priv->is_valid = FALSE;
+               break;
+       case PROP_STATUS:
+               g_free (priv->status);
+               str = g_value_get_string (value);
+               priv->status = g_strdup (str ? str : "");
+               g_strdelimit (priv->status, "\n\r\t", ' ');
+               priv->is_valid = FALSE;
+               break;
+       case PROP_IS_GROUP:
+               priv->is_group = g_value_get_boolean (value);
+               priv->is_valid = FALSE;
+               break;
+       case PROP_SHOW_STATUS:
+               priv->show_status = g_value_get_boolean (value);
+               priv->is_valid = FALSE;
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+               break;
+       }
+}
+
+static void
+cell_renderer_text_get_size (GtkCellRenderer *cell,
+                            GtkWidget       *widget,
+                            GdkRectangle    *cell_area,
+                            gint            *x_offset,
+                            gint            *y_offset,
+                            gint            *width,
+                            gint            *height)
+{
+       GossipCellRendererText     *celltext;
+       GossipCellRendererTextPriv *priv;
+
+       celltext = GOSSIP_CELL_RENDERER_TEXT (cell);
+       priv = GET_PRIV (cell);
+
+       /* Only update if not already valid so we get the right size. */
+       cell_renderer_text_update_text (celltext, widget, priv->is_selected);
+
+       (GTK_CELL_RENDERER_CLASS (gossip_cell_renderer_text_parent_class)->get_size) (cell, widget,
+                                                                                     cell_area,
+                                                                                     x_offset, y_offset,
+                                                                                     width, height);
+}
+
+static void
+cell_renderer_text_render (GtkCellRenderer      *cell,
+                          GdkWindow            *window,
+                          GtkWidget            *widget,
+                          GdkRectangle         *background_area,
+                          GdkRectangle         *cell_area,
+                          GdkRectangle         *expose_area,
+                          GtkCellRendererState  flags)
+{
+       GossipCellRendererText *celltext;
+
+       celltext = GOSSIP_CELL_RENDERER_TEXT (cell);
+
+       cell_renderer_text_update_text (celltext,
+                                       widget,
+                                       (flags & GTK_CELL_RENDERER_SELECTED));
+
+       (GTK_CELL_RENDERER_CLASS (gossip_cell_renderer_text_parent_class)->render) (
+               cell, window,
+               widget,
+               background_area,
+               cell_area,
+               expose_area, flags);
+}
+
+static void
+cell_renderer_text_update_text (GossipCellRendererText *cell,
+                               GtkWidget              *widget,
+                               gboolean                selected)
+{
+       GossipCellRendererTextPriv *priv;
+       PangoAttrList              *attr_list;
+       PangoAttribute             *attr_color, *attr_style, *attr_size;
+       GtkStyle                   *style;
+       gchar                      *str;
+
+       priv = GET_PRIV (cell);
+
+       if (priv->is_valid && priv->is_selected == selected) {
+               return;
+       }
+
+       if (priv->is_group) {
+               g_object_set (cell,
+                             "visible", TRUE,
+                             "weight", PANGO_WEIGHT_BOLD,
+                             "text", priv->name,
+                             "attributes", NULL,
+                             "xpad", 1,
+                             "ypad", 1,
+                             NULL);
+
+               priv->is_selected = selected;
+               priv->is_valid = TRUE;
+               return;
+       }
+
+       style = gtk_widget_get_style (widget);
+
+       attr_list = pango_attr_list_new ();
+
+       attr_style = pango_attr_style_new (PANGO_STYLE_ITALIC);
+       attr_style->start_index = strlen (priv->name) + 1;
+       attr_style->end_index = -1;
+       pango_attr_list_insert (attr_list, attr_style);
+
+       if (!selected) {
+               GdkColor color;
+
+               color = style->text_aa[GTK_STATE_NORMAL];
+
+               attr_color = pango_attr_foreground_new (color.red, color.green, color.blue);
+               attr_color->start_index = attr_style->start_index;
+               attr_color->end_index = -1;
+               pango_attr_list_insert (attr_list, attr_color);
+       }
+
+       attr_size = pango_attr_size_new (pango_font_description_get_size (style->font_desc) / 1.2);
+
+       attr_size->start_index = attr_style->start_index;
+       attr_size->end_index = -1;
+       pango_attr_list_insert (attr_list, attr_size);
+
+       if (!priv->status || !priv->status[0] || !priv->show_status) {
+               str = g_strdup (priv->name);
+       } else {
+               str = g_strdup_printf ("%s\n%s", priv->name, priv->status);
+       }
+
+       g_object_set (cell,
+                     "visible", TRUE,
+                     "weight", PANGO_WEIGHT_NORMAL,
+                     "text", str,
+                     "attributes", attr_list,
+                     "xpad", 0,
+                     "ypad", 1,
+                     NULL);
+
+       g_free (str);
+       pango_attr_list_unref (attr_list);
+
+       priv->is_selected = selected;
+       priv->is_valid = TRUE;
+}
+
+GtkCellRenderer *
+gossip_cell_renderer_text_new (void)
+{
+       return g_object_new (GOSSIP_TYPE_CELL_RENDERER_TEXT, NULL);
+}
diff --git a/libempathy-gtk/gossip-cell-renderer-text.h b/libempathy-gtk/gossip-cell-renderer-text.h
new file mode 100644 (file)
index 0000000..9b5a413
--- /dev/null
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Mikael Hallendal <micke@imendio.com>
+ */
+
+#ifndef __GOSSIP_CELL_RENDERER_TEXT_H__
+#define __GOSSIP_CELL_RENDERER_TEXT_H__
+
+#include <gtk/gtkcellrenderertext.h>
+
+G_BEGIN_DECLS
+
+#define GOSSIP_TYPE_CELL_RENDERER_TEXT         (gossip_cell_renderer_text_get_type ())
+#define GOSSIP_CELL_RENDERER_TEXT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_CELL_RENDERER_TEXT, GossipCellRendererText))
+#define GOSSIP_CELL_RENDERER_TEXT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GOSSIP_TYPE_CELL_RENDERER_TEXT, GossipCellRendererTextClass))
+#define GOSSIP_IS_CELL_RENDERER_TEXT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_CELL_RENDERER_TEXT))
+#define GOSSIP_IS_CELL_RENDERER_TEXT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_CELL_RENDERER_TEXT))
+#define GOSSIP_CELL_RENDERER_TEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_CELL_RENDERER_TEXT, GossipCellRendererTextClass))
+
+typedef struct _GossipCellRendererText      GossipCellRendererText;
+typedef struct _GossipCellRendererTextClass GossipCellRendererTextClass;
+typedef struct _GossipCellRendererTextPriv  GossipCellRendererTextPriv;
+
+struct _GossipCellRendererText {
+       GtkCellRendererText         parent;
+
+       GossipCellRendererTextPriv *priv;
+};
+
+struct _GossipCellRendererTextClass {
+       GtkCellRendererTextClass    parent_class;
+};
+
+GType             gossip_cell_renderer_text_get_type (void) G_GNUC_CONST;
+GtkCellRenderer * gossip_cell_renderer_text_new      (void);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_CELL_RENDERER_TEXT_H__ */
diff --git a/libempathy-gtk/gossip-chat-manager.c b/libempathy-gtk/gossip-chat-manager.c
new file mode 100644 (file)
index 0000000..86cd0ea
--- /dev/null
@@ -0,0 +1,327 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Martyn Russell <martyn@imendio.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <libgossip/gossip-account.h>
+#include <libgossip/gossip-event.h>
+#include <libgossip/gossip-session.h>
+#include <libgossip/gossip-message.h>
+#include <libgossip/gossip-debug.h>
+#include <libgossip/gossip-event-manager.h>
+
+#include "gossip-app.h"
+#include "gossip-chat.h"
+#include "gossip-chat-manager.h"
+
+#define DEBUG_DOMAIN "ChatManager"
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CHAT_MANAGER, GossipChatManagerPriv))
+
+typedef struct _GossipChatManagerPriv GossipChatManagerPriv;
+
+struct _GossipChatManagerPriv {
+       GHashTable *chats;
+       GHashTable *events;
+};
+
+static void chat_manager_finalize           (GObject             *object);
+static void chat_manager_new_message_cb     (GossipSession       *session,
+                                            GossipMessage       *msg,
+                                            GossipChatManager   *manager);
+static void chat_manager_event_activated_cb (GossipEventManager  *event_manager,
+                                            GossipEvent         *event,
+                                            GObject             *object);
+static void chat_manager_get_chats_foreach  (GossipContact       *contact,
+                                            GossipPrivateChat   *chat,
+                                            GList              **chats);
+static void chat_manager_chat_removed_cb    (GossipChatManager   *manager,
+                                            GossipChat          *chat,
+                                            gboolean             is_last_ref);
+
+G_DEFINE_TYPE (GossipChatManager, gossip_chat_manager, G_TYPE_OBJECT);
+
+static void
+gossip_chat_manager_class_init (GossipChatManagerClass *class)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (class);
+
+       object_class->finalize = chat_manager_finalize;
+
+       g_type_class_add_private (object_class, sizeof (GossipChatManagerPriv));
+}
+
+static void
+gossip_chat_manager_init (GossipChatManager *manager)
+{
+       GossipChatManagerPriv *priv;
+
+       priv = GET_PRIV (manager);
+
+       priv->chats = g_hash_table_new_full (gossip_contact_hash,
+                                            gossip_contact_equal,
+                                            (GDestroyNotify) g_object_unref,
+                                            (GDestroyNotify) g_object_unref);
+
+       priv->events = g_hash_table_new_full (gossip_contact_hash,
+                                             gossip_contact_equal,
+                                             (GDestroyNotify) g_object_unref,
+                                             (GDestroyNotify) g_object_unref);
+
+       /* Connect to signals on GossipSession to listen for new messages */
+       g_signal_connect (gossip_app_get_session (),
+                         "new-message",
+                         G_CALLBACK (chat_manager_new_message_cb),
+                         manager);
+}
+
+static void
+chat_manager_finalize (GObject *object)
+{
+       GossipChatManagerPriv *priv;
+
+       priv = GET_PRIV (object);
+
+       g_hash_table_destroy (priv->chats);
+       g_hash_table_destroy (priv->events);
+
+       G_OBJECT_CLASS (gossip_chat_manager_parent_class)->finalize (object);
+}
+
+static void
+chat_manager_new_message_cb (GossipSession     *session,
+                            GossipMessage     *message,
+                            GossipChatManager *manager)
+{
+       GossipChatManagerPriv *priv;
+       GossipPrivateChat     *chat;
+       GossipContact         *sender;
+       GossipEvent           *event = NULL;
+       GossipEvent           *old_event;
+
+       priv = GET_PRIV (manager);
+
+       sender = gossip_message_get_sender (message);
+       chat = g_hash_table_lookup (priv->chats, sender);
+
+       old_event = g_hash_table_lookup (priv->events, sender);
+
+       /* Add event to event manager if one doesn't exist already. */
+       if (!chat) {
+               gossip_debug (DEBUG_DOMAIN, "New chat for: %s",
+                             gossip_contact_get_id (sender));
+               chat = gossip_chat_manager_get_chat (manager, sender);
+
+               if (!old_event) {
+                       event = gossip_event_new (GOSSIP_EVENT_NEW_MESSAGE);
+               }
+       } else {
+               GossipChatWindow *window;
+
+               window = gossip_chat_get_window (GOSSIP_CHAT (chat));
+
+               if (!window && !old_event) {
+                       event = gossip_event_new (GOSSIP_EVENT_NEW_MESSAGE);
+               }
+       }
+
+       gossip_private_chat_append_message (chat, message);
+
+       if (event) {
+               gchar *str;
+
+               str = g_strdup_printf (_("New message from %s"),
+                                      gossip_contact_get_name (sender));
+               g_object_set (event,
+                             "message", str,
+                             "data", message,
+                             NULL);
+               g_free (str);
+
+               gossip_event_manager_add (gossip_app_get_event_manager (),
+                                         event,
+                                         chat_manager_event_activated_cb,
+                                         G_OBJECT (manager));
+
+               g_hash_table_insert (priv->events,
+                                    g_object_ref (sender),
+                                    g_object_ref (event));
+       }
+}
+
+static void
+chat_manager_event_activated_cb (GossipEventManager *event_manager,
+                                GossipEvent        *event,
+                                GObject            *object)
+{
+       GossipMessage *message;
+       GossipContact *contact;
+
+       message = GOSSIP_MESSAGE (gossip_event_get_data (event));
+       contact = gossip_message_get_sender (message);
+
+       gossip_chat_manager_show_chat (GOSSIP_CHAT_MANAGER (object), contact);
+}
+
+static void
+chat_manager_get_chats_foreach (GossipContact      *contact,
+                               GossipPrivateChat  *chat,
+                               GList             **chats)
+{
+       const gchar *contact_id;
+
+       contact_id = gossip_contact_get_id (contact);
+       *chats = g_list_prepend (*chats, g_strdup (contact_id));
+}
+
+GossipChatManager *
+gossip_chat_manager_new (void)
+{
+       return g_object_new (GOSSIP_TYPE_CHAT_MANAGER, NULL);
+}
+
+static void
+chat_manager_chat_removed_cb (GossipChatManager *manager,
+                             GossipChat        *chat,
+                             gboolean           is_last_ref) 
+{
+       GossipChatManagerPriv *priv;
+       GossipContact         *contact;
+
+       if (!is_last_ref) {
+               return;
+       }
+       
+       priv = GET_PRIV (manager);
+       
+       contact = gossip_chat_get_contact (chat);
+
+       gossip_debug (DEBUG_DOMAIN, 
+                     "Removing an old chat:'%s'",
+                     gossip_contact_get_id (contact));
+
+       g_hash_table_remove (priv->chats, contact);
+}                            
+
+GossipPrivateChat *
+gossip_chat_manager_get_chat (GossipChatManager *manager,
+                             GossipContact     *contact)
+{
+       GossipChatManagerPriv *priv;
+       GossipPrivateChat     *chat;
+
+       g_return_val_if_fail (GOSSIP_IS_CHAT_MANAGER (manager), NULL);
+       g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
+
+       priv = GET_PRIV (manager);
+
+       chat = g_hash_table_lookup (priv->chats, contact);
+
+       if (!chat) {
+               GossipSession *session;
+               GossipAccount *account;
+               GossipContact *own_contact;
+
+               session = gossip_app_get_session ();
+               account = gossip_contact_get_account (contact);
+               own_contact = gossip_session_get_own_contact (session, account);
+
+               chat = gossip_private_chat_new (own_contact, contact);
+               g_hash_table_insert (priv->chats,
+                                    g_object_ref (contact),
+                                    chat);
+               g_object_add_toggle_ref (G_OBJECT (chat),
+                                        (GToggleNotify) chat_manager_chat_removed_cb, 
+                                        manager);
+
+               gossip_debug (DEBUG_DOMAIN, 
+                             "Creating a new chat:'%s'",
+                             gossip_contact_get_id (contact));
+       }
+
+       return chat;
+}
+
+GList *
+gossip_chat_manager_get_chats (GossipChatManager *manager)
+{
+       GossipChatManagerPriv *priv;
+       GList                 *chats = NULL;
+
+       g_return_val_if_fail (GOSSIP_IS_CHAT_MANAGER (manager), NULL);
+
+       priv = GET_PRIV (manager);
+
+       g_hash_table_foreach (priv->chats,
+                             (GHFunc) chat_manager_get_chats_foreach,
+                             &chats);
+
+       chats = g_list_sort (chats, (GCompareFunc) strcmp);
+
+       return chats;
+}
+
+void
+gossip_chat_manager_remove_events (GossipChatManager *manager,
+                                  GossipContact     *contact)
+{
+       GossipChatManagerPriv *priv;
+       GossipEvent           *event;
+
+       g_return_if_fail (GOSSIP_IS_CHAT_MANAGER (manager));
+       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+       gossip_debug (DEBUG_DOMAIN, 
+                     "Removing events for contact:'%s'",
+                     gossip_contact_get_id (contact));
+
+       priv = GET_PRIV (manager);
+
+       event = g_hash_table_lookup (priv->events, contact);
+       if (event) {
+               gossip_event_manager_remove (gossip_app_get_event_manager (),
+                                            event, G_OBJECT (manager));
+               g_hash_table_remove (priv->events, contact);
+       }
+}
+
+void
+gossip_chat_manager_show_chat (GossipChatManager *manager,
+                              GossipContact     *contact)
+{
+       GossipPrivateChat     *chat;
+
+       g_return_if_fail (GOSSIP_IS_CHAT_MANAGER (manager));
+       g_return_if_fail (GOSSIP_IS_CONTACT (contact));
+
+       chat = gossip_chat_manager_get_chat (manager, contact);
+
+       gossip_chat_present (GOSSIP_CHAT (chat));
+
+       gossip_chat_manager_remove_events(manager, contact);
+}
diff --git a/libempathy-gtk/gossip-chat-manager.h b/libempathy-gtk/gossip-chat-manager.h
new file mode 100644 (file)
index 0000000..7500b59
--- /dev/null
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2004-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Martyn Russell <martyn@imendio.com>
+ */
+
+#ifndef __GOSSIP_CHAT_MANAGER_H__
+#define __GOSSIP_CHAT_MANAGER_H__
+
+#include <glib-object.h>
+
+#include <libempathy/gossip-contact.h>
+
+G_BEGIN_DECLS
+
+#define GOSSIP_TYPE_CHAT_MANAGER         (gossip_chat_manager_get_type ())
+#define GOSSIP_CHAT_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOSSIP_TYPE_CHAT_MANAGER, GossipChatManager))
+#define GOSSIP_CHAT_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GOSSIP_TYPE_CHAT_MANAGER, GossipChatManagerClass))
+#define GOSSIP_IS_CHAT_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOSSIP_TYPE_CHAT_MANAGER))
+#define GOSSIP_IS_CHAT_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GOSSIP_TYPE_CHAT_MANAGER))
+#define GOSSIP_CHAT_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOSSIP_TYPE_CHAT_MANAGER, GossipChatManagerClass))
+
+typedef struct _GossipChatManager      GossipChatManager;
+typedef struct _GossipChatManagerClass GossipChatManagerClass;
+
+#include "gossip-private-chat.h"
+
+struct _GossipChatManager {
+       GObject parent;
+};
+
+struct _GossipChatManagerClass {
+       GObjectClass parent_class;
+};
+
+GType              gossip_chat_manager_get_type      (void) G_GNUC_CONST;
+GossipChatManager *gossip_chat_manager_new           (void);
+GossipPrivateChat *gossip_chat_manager_get_chat      (GossipChatManager *manager,
+                                                     GossipContact     *contact);
+GList *            gossip_chat_manager_get_chats     (GossipChatManager *manager);
+void               gossip_chat_manager_remove_events (GossipChatManager *manager,
+                                                     GossipContact     *contact);
+void               gossip_chat_manager_show_chat     (GossipChatManager *manager,
+                                                     GossipContact     *contact);
+
+G_END_DECLS
+
+#endif /* __GOSSIP_CHAT_MANAGER_H__ */
+
diff --git a/libempathy-gtk/gossip-chat-manager.loT b/libempathy-gtk/gossip-chat-manager.loT
new file mode 100644 (file)
index 0000000..3d0d3ab
--- /dev/null
@@ -0,0 +1,7 @@
+# gossip-chat-manager.lo - a libtool object file
+# Generated by ltmain.sh - GNU libtool 1.5.22 Debian 1.5.22-4 (1.1220.2.365 2005/12/18 22:14:06)
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
diff --git a/libempathy-gtk/gossip-chat-view.c b/libempathy-gtk/gossip-chat-view.c
new file mode 100644 (file)
index 0000000..cc6482e
--- /dev/null
@@ -0,0 +1,2151 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2002-2007 Imendio AB
+ *
+ * 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.
+ * 
+ * Authors: Mikael Hallendal <micke@imendio.com>
+ *          Richard Hult <richard@imendio.com>
+ *          Martyn Russell <martyn@imendio.com>
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkmenuitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtksizegroup.h>
+#include <glade/glade.h>
+
+#include <libmissioncontrol/mc-account.h>
+
+#include <libempathy/gossip-utils.h>
+#include <libempathy/gossip-debug.h>
+#include <libempathy/gossip-conf.h>
+
+#include "gossip-chat-view.h"
+#include "gossip-chat.h"
+#include "gossip-preferences.h"
+#include "gossip-theme-manager.h"
+#include "gossip-ui-utils.h"
+
+#define DEBUG_DOMAIN "ChatView"
+
+/* Number of seconds between timestamps when using normal mode, 5 minutes. */
+#define TIMESTAMP_INTERVAL 300
+
+#define MAX_LINES 800
+
+#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CHAT_VIEW, GossipChatViewPriv))
+
+typedef enum {
+       BLOCK_TYPE_NONE,
+       BLOCK_TYPE_SELF,
+       BLOCK_TYPE_OTHER,
+       BLOCK_TYPE_EVENT,
+       BLOCK_TYPE_TIME,
+       BLOCK_TYPE_INVITE
+} BlockType;
+
+struct _GossipChatViewPriv {
+       GtkTextBuffer *buffer;
+
+       gboolean       irc_style;
+       time_t         last_timestamp;
+       BlockType      last_block_type;
+
+       gboolean       allow_scrolling;
+       gboolean       is_group_chat;
+
+       GtkTextMark   *find_mark_previous;
+       GtkTextMark   *find_mark_next;
+       gboolean       find_wrapped;
+       gboolean       find_last_direction;
+
+       /* This is for the group chat so we know if the "other" last contact
+        * changed, so we know whether to insert a header or not.
+        */
+       GossipContact *last_contact;
+
+       guint          notify_system_fonts_id;
+       guint          notify_show_avatars_id;
+};
+
+typedef struct {
+       GossipSmiley  smiley;
+       const gchar  *pattern;
+} GossipSmileyPattern;
+
+static const GossipSmileyPattern smileys[] = {
+       /* Forward smileys. */
+       { GOSSIP_SMILEY_NORMAL,       ":)"  },
+       { GOSSIP_SMILEY_WINK,         ";)"  },
+       { GOSSIP_SMILEY_WINK,         ";-)" },
+       { GOSSIP_SMILEY_BIGEYE,       "=)"  },
+       { GOSSIP_SMILEY_NOSE,         ":-)" },
+       { GOSSIP_SMILEY_CRY,          ":'(" },
+       { GOSSIP_SMILEY_SAD,          ":("  },
+       { GOSSIP_SMILEY_SAD,          ":-(" },
+       { GOSSIP_SMILEY_SCEPTICAL,    ":/"  },
+       { GOSSIP_SMILEY_SCEPTICAL,    ":\\" },
+       { GOSSIP_SMILEY_BIGSMILE,     ":D"  },
+       { GOSSIP_SMILEY_BIGSMILE,     ":-D" },
+       { GOSSIP_SMILEY_INDIFFERENT,  ":|"  },
+       { GOSSIP_SMILEY_TOUNGE,       ":p"  },
+       { GOSSIP_SMILEY_TOUNGE,       ":-p" },
+       { GOSSIP_SMILEY_TOUNGE,       ":P"  },
+       { GOSSIP_SMILEY_TOUNGE,       ":-P" },
+       { GOSSIP_SMILEY_TOUNGE,       ";p"  },
+       { GOSSIP_SMILEY_TOUNGE,       ";-p" },
+       { GOSSIP_SMILEY_TOUNGE,       ";P"  },
+       { GOSSIP_SMILEY_TOUNGE,       ";-P" },
+       { GOSSIP_SMILEY_SHOCKED,      ":o"  },
+       { GOSSIP_SMILEY_SHOCKED,      ":-o" },
+       { GOSSIP_SMILEY_SHOCKED,      ":O"  },
+       { GOSSIP_SMILEY_SHOCKED,      ":-O" },
+       { GOSSIP_SMILEY_COOL,         "8)"  },
+       { GOSSIP_SMILEY_COOL,         "B)"  },
+       { GOSSIP_SMILEY_SORRY,        "*|"  },
+       { GOSSIP_SMILEY_KISS,         ":*"  },
+       { GOSSIP_SMILEY_SHUTUP,       ":#"  },
+       { GOSSIP_SMILEY_SHUTUP,       ":-#" },
+       { GOSSIP_SMILEY_YAWN,         "|O"  },
+       { GOSSIP_SMILEY_CONFUSED,     ":S"  },
+       { GOSSIP_SMILEY_CONFUSED,     ":s"  },
+       { GOSSIP_SMILEY_ANGEL,        "<)"  },
+       { GOSSIP_SMILEY_OOOH,         ":x"  },
+       { GOSSIP_SMILEY_LOOKAWAY,     "*)"  },
+       { GOSSIP_SMILEY_LOOKAWAY,     "*-)" },
+       { GOSSIP_SMILEY_BLUSH,        "*S"  },
+       { GOSSIP_SMILEY_BLUSH,        "*s"  },
+       { GOSSIP_SMILEY_BLUSH,        "*$"  },
+       { GOSSIP_SMILEY_COOLBIGSMILE, "8D"  },
+       { GOSSIP_SMILEY_ANGRY,        ":@"  },
+       { GOSSIP_SMILEY_BOSS,         "@)"  },
+       { GOSSIP_SMILEY_MONKEY,       "#)"  },
+       { GOSSIP_SMILEY_SILLY,        "O)"  },
+       { GOSSIP_SMILEY_SICK,         "+o(" },
+
+       /* Backward smileys. */
+       { GOSSIP_SMILEY_NORMAL,       "(:"  },
+       { GOSSIP_SMILEY_WINK,         "(;"  },
+       { GOSSIP_SMILEY_WINK,         "(-;" },
+       { GOSSIP_SMILEY_BIGEYE,       "(="  },
+       { GOSSIP_SMILEY_NOSE,         "(-:" },
+       { GOSSIP_SMILEY_CRY,          ")':" },
+       { GOSSIP_SMILEY_SAD,          "):"  },
+       { GOSSIP_SMILEY_SAD,          ")-:" },
+       { GOSSIP_SMILEY_SCEPTICAL,    "/:"  },
+       { GOSSIP_SMILEY_SCEPTICAL,    "//:" },
+       { GOSSIP_SMILEY_INDIFFERENT,  "|:"  },
+       { GOSSIP_SMILEY_TOUNGE,       "d:"  },
+       { GOSSIP_SMILEY_TOUNGE,       "d-:" },
+       { GOSSIP_SMILEY_TOUNGE,       "d;"  },
+       { GOSSIP_SMILEY_TOUNGE,       "d-;" },
+       { GOSSIP_SMILEY_SHOCKED,      "o:"  },
+       { GOSSIP_SMILEY_SHOCKED,      "O:"  },
+       { GOSSIP_SMILEY_COOL,         "(8"  },
+       { GOSSIP_SMILEY_COOL,         "(B"  },
+       { GOSSIP_SMILEY_SORRY,        "|*"  },
+       { GOSSIP_SMILEY_KISS,         "*:"  },
+       { GOSSIP_SMILEY_SHUTUP,       "#:"  },
+       { GOSSIP_SMILEY_SHUTUP,       "#-:" },
+       { GOSSIP_SMILEY_YAWN,         "O|"  },
+       { GOSSIP_SMILEY_CONFUSED,     "S:"  },
+       { GOSSIP_SMILEY_CONFUSED,     "s:"  },
+       { GOSSIP_SMILEY_ANGEL,        "(>"  },
+       { GOSSIP_SMILEY_OOOH,         "x:"  },
+       { GOSSIP_SMILEY_LOOKAWAY,     "(*"  },
+       { GOSSIP_SMILEY_LOOKAWAY,     "(-*" },
+       { GOSSIP_SMILEY_BLUSH,        "S*"  },
+       { GOSSIP_SMILEY_BLUSH,        "s*"  },
+       { GOSSIP_SMILEY_BLUSH,        "$*"  },
+       { GOSSIP_SMILEY_ANGRY,        "@:"  },
+       { GOSSIP_SMILEY_BOSS,         "(@"  },
+       { GOSSIP_SMILEY_MONKEY,       "#)"  },
+       { GOSSIP_SMILEY_SILLY,        "(O"  },
+       { GOSSIP_SMILEY_SICK,         ")o+" }
+};
+
+static void     gossip_chat_view_class_init          (GossipChatViewClass      *klass);
+static void     gossip_chat_view_init                (GossipChatView           *view);
+static void     chat_view_finalize                   (GObject                  *object);
+static gboolean chat_view_drag_motion                (GtkWidget                *widget,
+                                                     GdkDragContext           *context,
+                                                     gint                      x,
+                                                     gint                      y,
+                                                     guint                     time);
+static void     chat_view_size_allocate              (GtkWidget                *widget,
+                                                     GtkAllocation            *alloc);
+static void     chat_view_setup_tags                 (GossipChatView           *view);
+static void     chat_view_system_font_update         (GossipChatView           *view);
+static void     chat_view_notify_system_font_cb      (GossipConf               *conf,
+                                                     const gchar              *key,
+                                                     gpointer                  user_data);
+static void     chat_view_notify_show_avatars_cb     (GossipConf               *conf,
+                                                     const gchar              *key,
+                                                     gpointer                  user_data);
+static void     chat_view_populate_popup             (GossipChatView           *view,
+                                                     GtkMenu                  *menu,
+                                                     gpointer                  user_data);
+static gboolean chat_view_event_cb                   (GossipChatView           *view,
+                                                     GdkEventMotion           *event,
+                                                     GtkTextTag               *tag);
+static gboolean chat_view_url_event_cb               (GtkTextTag               *tag,
+                                                     GObject                  *object,
+                                                     GdkEvent                 *event,
+                                                     GtkTextIter              *iter,
+                                                     GtkTextBuffer            *buffer);
+static void     chat_view_open_address_cb            (GtkMenuItem              *menuitem,
+                                                     const gchar              *url);
+static void     chat_view_copy_address_cb            (GtkMenuItem              *menuitem,
+                                                     const gchar              *url);
+static void     chat_view_clear_view_cb              (GtkMenuItem              *menuitem,
+                                                     GossipChatView           *view);
+static void     chat_view_insert_text_with_emoticons (GtkTextBuffer            *buf,
+                                                     GtkTextIter              *iter,
+                                                     const gchar              *str);
+static gboolean chat_view_is_scrolled_down           (GossipChatView           *view);
+static void     chat_view_theme_changed_cb           (GossipThemeManager       *manager,
+                                                     GossipChatView           *view);
+static void     chat_view_maybe_append_date_and_time (GossipChatView           *view,
+                                                     GossipMessage            *msg);
+static void     chat_view_append_spacing             (GossipChatView           *view);
+static void     chat_view_append_text                (GossipChatView           *view,
+                                                     const gchar              *body,
+                                                     const gchar              *tag);
+static void     chat_view_maybe_append_fancy_header  (GossipChatView           *view,
+                                                     GossipMessage            *msg);
+static void     chat_view_append_irc_action          (GossipChatView           *view,
+                                                     GossipMessage            *msg);
+static void     chat_view_append_fancy_action        (GossipChatView           *view,
+                                                     GossipMessage            *msg);
+static void     chat_view_append_irc_message         (GossipChatView           *view,
+                                                     GossipMessage            *msg);
+static void     chat_view_append_fancy_message       (GossipChatView           *view,
+                                                     GossipMessage            *msg);
+
+G_DEFINE_TYPE (GossipChatView, gossip_chat_view, GTK_TYPE_TEXT_VIEW);
+
+static void
+gossip_chat_view_class_init (GossipChatViewClass *klass)
+{
+       GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->finalize = chat_view_finalize;
+       widget_class->size_allocate = chat_view_size_allocate;
+       widget_class->drag_motion = chat_view_drag_motion; 
+
+       g_type_class_add_private (object_class, sizeof (GossipChatViewPriv));
+}
+
+static void
+gossip_chat_view_init (GossipChatView *view)
+{
+       GossipChatViewPriv *priv;
+       gboolean            show_avatars;
+
+       priv = GET_PRIV (view);
+
+       priv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+       priv->last_block_type = BLOCK_TYPE_NONE;
+       priv->last_timestamp = 0;
+
+       priv->allow_scrolling = TRUE;
+
+       priv->is_group_chat = FALSE;
+
+       g_object_set (view,
+                     "wrap-mode", GTK_WRAP_WORD_CHAR,
+                     "editable", FALSE,
+                     "cursor-visible", FALSE,
+                     NULL);
+
+       priv->notify_system_fonts_id =
+               gossip_conf_notify_add (gossip_conf_get (),
+                                        "/desktop/gnome/interface/document_font_name",
+                                        chat_view_notify_system_font_cb,
+                                        view);
+       chat_view_system_font_update (view);
+
+       priv->notify_show_avatars_id =
+               gossip_conf_notify_add (gossip_conf_get (),
+                                        GOSSIP_PREFS_UI_SHOW_AVATARS,
+                                        chat_view_notify_show_avatars_cb,
+                                        view);
+
+       chat_view_setup_tags (view);
+
+       gossip_theme_manager_apply_saved (gossip_theme_manager_get (), view);
+
+       show_avatars = FALSE;
+       gossip_conf_get_bool (gossip_conf_get (),
+                              GOSSIP_PREFS_UI_SHOW_AVATARS,
+                              &show_avatars);
+
+       gossip_theme_manager_update_show_avatars (gossip_theme_manager_get (),
+                                                 view, show_avatars);
+
+       g_signal_connect (view,
+                         "populate-popup",
+                         G_CALLBACK (chat_view_populate_popup),
+                         NULL);
+
+       g_signal_connect_object (gossip_theme_manager_get (),
+                                "theme-changed",
+                                G_CALLBACK (chat_view_theme_changed_cb),
+                                view,
+                                0);
+}
+
+static void
+chat_view_finalize (GObject *object)
+{
+       GossipChatView     *view;
+       GossipChatViewPriv *priv;
+
+       view = GOSSIP_CHAT_VIEW (object);
+       priv = GET_PRIV (view);
+
+       gossip_debug (DEBUG_DOMAIN, "finalize: %p", object);
+
+       gossip_conf_notify_remove (gossip_conf_get (), priv->notify_system_fonts_id);
+       gossip_conf_notify_remove (gossip_conf_get (), priv->notify_show_avatars_id);
+
+       if (priv->last_contact) {
+               g_object_unref (priv->last_contact);
+       }
+
+       G_OBJECT_CLASS (gossip_chat_view_parent_class)->finalize (object);
+}
+
+static gboolean
+chat_view_drag_motion (GtkWidget        *widget,
+                      GdkDragContext   *context,
+                      gint              x,
+                      gint              y,
+                      guint             time)
+{
+       /* Don't handle drag motion, since we don't want the view to scroll as
+        * the result of dragging something across it.
+        */
+
+       return FALSE;
+}
+
+static void
+chat_view_size_allocate (GtkWidget     *widget,
+                        GtkAllocation *alloc)
+{
+       gboolean down;
+
+       down = chat_view_is_scrolled_down (GOSSIP_CHAT_VIEW (widget));
+
+       GTK_WIDGET_CLASS (gossip_chat_view_parent_class)->size_allocate (widget, alloc);
+
+       if (down) {
+               gossip_chat_view_scroll_down (GOSSIP_CHAT_VIEW (widget));
+       }
+}
+
+static void
+chat_view_setup_tags (GossipChatView *view)
+{
+       GossipChatViewPriv *priv;
+       GtkTextTag         *tag;
+
+       priv = GET_PRIV (view);
+
+       gtk_text_buffer_create_tag (priv->buffer,
+                                   "cut",
+                                   NULL);
+
+       /* FIXME: Move to the theme and come up with something that looks a bit
+        * nicer.
+        */
+       gtk_text_buffer_create_tag (priv->buffer,
+                                   "highlight",
+                                   "background", "yellow",
+                                   NULL);
+
+       tag = gtk_text_buffer_create_tag (priv->buffer,
+                                         "link",
+                                         NULL);
+
+       g_signal_connect (tag,
+                         "event",
+                         G_CALLBACK (chat_view_url_event_cb),
+                         priv->buffer);
+
+       g_signal_connect (view,
+                         "motion-notify-event",
+                         G_CALLBACK (chat_view_event_cb),
+                         tag);
+}
+
+static void
+chat_view_system_font_update (GossipChatView *view)
+{
+       PangoFontDescription *font_description = NULL;
+       gchar                *font_name;
+
+       if (gossip_conf_get_string (gossip_conf_get (),
+                                    "/desktop/gnome/interface/document_font_name",
+                                    &font_name) && font_name) {
+               font_description = pango_font_description_from_string (font_name);
+               g_free (font_name);
+       } else {
+               font_description = NULL;
+       }
+
+       gtk_widget_modify_font (GTK_WIDGET (view), font_description);
+
+       if (font_description) {
+               pango_font_description_free (font_description);
+       }
+}
+
+static void
+chat_view_notify_system_font_cb (GossipConf  *conf,
+                                const gchar *key,
+                                gpointer     user_data)
+{
+       GossipChatView *view;
+       gboolean        show_avatars = FALSE;
+
+       view = user_data;
+
+       chat_view_system_font_update (view);
+
+       /* Ugly, again, to adjust the vertical position of the nick... Will fix
+        * this when reworking the theme manager so that view register
+        * themselves with it instead of the other way around.
+        */
+       gossip_conf_get_bool (conf,
+                              GOSSIP_PREFS_UI_SHOW_AVATARS,
+                              &show_avatars);
+
+       gossip_theme_manager_update_show_avatars (gossip_theme_manager_get (),
+                                                 view, show_avatars);
+}
+
+static void
+chat_view_notify_show_avatars_cb (GossipConf  *conf,
+                                 const gchar *key,
+                                 gpointer     user_data)
+{
+       GossipChatView     *view;
+       GossipChatViewPriv *priv;
+       gboolean            show_avatars = FALSE;
+
+       view = user_data;
+       priv = GET_PRIV (view);
+
+       gossip_conf_get_bool (conf, key, &show_avatars);
+
+       gossip_theme_manager_update_show_avatars (gossip_theme_manager_get (),
+                                                 view, show_avatars);
+}
+
+static void
+chat_view_populate_popup (GossipChatView *view,
+                         GtkMenu        *menu,
+                         gpointer        user_data)
+{
+       GossipChatViewPriv *priv;
+       GtkTextTagTable    *table;
+       GtkTextTag         *tag;
+       gint                x, y;
+       GtkTextIter         iter, start, end;
+       GtkWidget          *item;
+       gchar              *str = NULL;
+
+       priv = GET_PRIV (view);
+
+       /* Clear menu item */
+       if (gtk_text_buffer_get_char_count (priv->buffer) > 0) {
+               item = gtk_menu_item_new ();
+               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+               gtk_widget_show (item);
+
+               item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
+               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+               gtk_widget_show (item);
+
+               g_signal_connect (item,
+                                 "activate",
+                                 G_CALLBACK (chat_view_clear_view_cb),
+                                 view);
+       }
+
+       /* Link context menu items */
+       table = gtk_text_buffer_get_tag_table (priv->buffer);
+       tag = gtk_text_tag_table_lookup (table, "link");
+
+       gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
+
+       gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+                                              GTK_TEXT_WINDOW_WIDGET,
+                                              x, y,
+                                              &x, &y);
+
+       gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y);
+
+       start = end = iter;
+
+       if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
+           gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
+               str = gtk_text_buffer_get_text (priv->buffer,
+                                               &start, &end, FALSE);
+       }
+
+       if (G_STR_EMPTY (str)) {
+               g_free (str);
+               return;
+       }
+
+       /* NOTE: Set data just to get the string freed when not needed. */
+       g_object_set_data_full (G_OBJECT (menu),
+                               "url", str,
+                               (GDestroyNotify) g_free);
+
+       item = gtk_menu_item_new ();
+       gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+       gtk_widget_show (item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Address"));
+       g_signal_connect (item,
+                         "activate",
+                         G_CALLBACK (chat_view_copy_address_cb),
+                         str);
+       gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+       gtk_widget_show (item);
+
+       item = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
+       g_signal_connect (item,
+                         "activate",
+                         G_CALLBACK (chat_view_open_address_cb),
+                         str);
+       gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+       gtk_widget_show (item);
+}
+
+static gboolean
+chat_view_event_cb (GossipChatView *view,
+                   GdkEventMotion *event,
+                   GtkTextTag     *tag)
+{
+       static GdkCursor  *hand = NULL;
+       static GdkCursor  *beam = NULL;
+       GtkTextWindowType  type;
+       GtkTextIter        iter;
+       GdkWindow         *win;
+       gint               x, y, buf_x, buf_y;
+
+       type = gtk_text_view_get_window_type (GTK_TEXT_VIEW (view),
+                                             event->window);
+
+       if (type != GTK_TEXT_WINDOW_TEXT) {
+               return FALSE;
+       }
+
+       /* Get where the pointer really is. */
+       win = gtk_text_view_get_window (GTK_TEXT_VIEW (view), type);
+       if (!win) {
+               return FALSE;
+       }
+
+       gdk_window_get_pointer (win, &x, &y, NULL);
+
+       /* Get the iter where the cursor is at */
+       gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view), type,
+                                              x, y,
+                                              &buf_x, &buf_y);
+
+       gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view),
+                                           &iter,
+                                           buf_x, buf_y);
+
+       if (gtk_text_iter_has_tag (&iter, tag)) {
+               if (!hand) {
+                       hand = gdk_cursor_new (GDK_HAND2);
+                       beam = gdk_cursor_new (GDK_XTERM);
+               }
+               gdk_window_set_cursor (win, hand);
+       } else {
+               if (!beam) {
+                       beam = gdk_cursor_new (GDK_XTERM);
+               }
+               gdk_window_set_cursor (win, beam);
+       }
+
+       return FALSE;
+}
+
+static gboolean
+chat_view_url_event_cb (GtkTextTag    *tag,
+                       GObject       *object,
+                       GdkEvent      *event,
+                       GtkTextIter   *iter,
+                       GtkTextBuffer *buffer)
+{
+       GtkTextIter  start, end;
+       gchar       *str;
+
+       /* If the link is being selected, don't do anything. */
+       gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+       if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end)) {
+               return FALSE;
+       }
+
+       if (event->type == GDK_BUTTON_RELEASE && event->button.button == 1) {
+               start = end = *iter;
+
+               if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
+                   gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
+                       str = gtk_text_buffer_get_text (buffer,
+                                                       &start,
+                                                       &end,
+                                                       FALSE);
+
+                       gossip_url_show (str);
+                       g_free (str);
+               }
+       }
+
+       return FALSE;
+}
+
+static void
+chat_view_open_address_cb (GtkMenuItem *menuitem, const gchar *url)
+{
+       gossip_url_show (url);
+}
+
+static void
+chat_view_copy_address_cb (GtkMenuItem *menuitem, const gchar *url)
+{
+       GtkClipboard *clipboard;
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+       gtk_clipboard_set_text (clipboard, url, -1);
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+       gtk_clipboard_set_text (clipboard, url, -1);
+}
+
+static void
+chat_view_clear_view_cb (GtkMenuItem *menuitem, GossipChatView *view)
+{
+       gossip_chat_view_clear (view);
+}
+
+static void
+chat_view_insert_text_with_emoticons (GtkTextBuffer *buf,
+                                     GtkTextIter   *iter,
+                                     const gchar   *str)
+{
+       const gchar *p;
+       gunichar     c, prev_c;
+       gint         i;
+       gint         match;
+       gint         submatch;
+       gboolean     use_smileys = FALSE;
+
+       gossip_conf_get_bool (gossip_conf_get (),
+                              GOSSIP_PREFS_CHAT_SHOW_SMILEYS,
+                &n