summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alan Fitton <ajf@eth0.org.uk>2011-01-30 10:57:07 +0000
committerGravatar Alan Fitton <ajf@eth0.org.uk>2011-01-30 10:57:07 +0000
commiteca35c468094fc6b7177f33ef51fa873eb88e79c (patch)
treefa07fd0ea1db72ff2ad4bd8b04b8e546717f2731
hello world!
-rw-r--r--AUTHORS6
-rw-r--r--COPYING339
-rw-r--r--Makefile.am3
-rw-r--r--README25
-rw-r--r--TODO11
-rw-r--r--aclocal.m4997
-rwxr-xr-xautogen.sh1578
-rw-r--r--config.h.in68
-rw-r--r--configure.ac43
-rw-r--r--m4/pkg.m4157
-rw-r--r--redhat/transmission-remote-gtk.spec89
-rw-r--r--src/Makefile.am28
-rw-r--r--src/base64.c66
-rw-r--r--src/base64.h25
-rw-r--r--src/dispatch.c109
-rw-r--r--src/dispatch.h40
-rw-r--r--src/hig.c208
-rw-r--r--src/hig.h85
-rw-r--r--src/http.c145
-rw-r--r--src/http.h43
-rw-r--r--src/json.c93
-rw-r--r--src/json.h34
-rw-r--r--src/main.c135
-rw-r--r--src/protocol-constants.h115
-rw-r--r--src/requests.c155
-rw-r--r--src/requests.h39
-rw-r--r--src/session-get.c161
-rw-r--r--src/session-get.h89
-rw-r--r--src/tfile.c44
-rw-r--r--src/tfile.h31
-rw-r--r--src/torrent.c234
-rw-r--r--src/torrent.h71
-rw-r--r--src/tpeer.c71
-rw-r--r--src/tpeer.h47
-rw-r--r--src/transmission-remote-gtk.desktop12
-rw-r--r--src/transmission-remote-gtk.desktop.in12
-rw-r--r--src/transmission-remote-gtk.schemas109
-rw-r--r--src/trg-about-window.c82
-rw-r--r--src/trg-about-window.h27
-rw-r--r--src/trg-cell-renderer-eta.c116
-rw-r--r--src/trg-cell-renderer-eta.h51
-rw-r--r--src/trg-cell-renderer-priority.c115
-rw-r--r--src/trg-cell-renderer-priority.h52
-rw-r--r--src/trg-cell-renderer-ratio.c113
-rw-r--r--src/trg-cell-renderer-ratio.h51
-rw-r--r--src/trg-cell-renderer-size.c112
-rw-r--r--src/trg-cell-renderer-size.h52
-rw-r--r--src/trg-cell-renderer-speed.c114
-rw-r--r--src/trg-cell-renderer-speed.h52
-rw-r--r--src/trg-cell-renderer-wanted.c105
-rw-r--r--src/trg-cell-renderer-wanted.h52
-rw-r--r--src/trg-client.c80
-rw-r--r--src/trg-client.h45
-rw-r--r--src/trg-files-model.c177
-rw-r--r--src/trg-files-model.h74
-rw-r--r--src/trg-files-tree-view.c369
-rw-r--r--src/trg-files-tree-view.h56
-rw-r--r--src/trg-general-panel.c209
-rw-r--r--src/trg-general-panel.h59
-rw-r--r--src/trg-json-widgets.c71
-rw-r--r--src/trg-json-widgets.h40
-rw-r--r--src/trg-main-window.c1449
-rw-r--r--src/trg-main-window.h71
-rw-r--r--src/trg-menu-bar.c374
-rw-r--r--src/trg-menu-bar.h57
-rw-r--r--src/trg-model.c65
-rw-r--r--src/trg-model.h29
-rw-r--r--src/trg-peers-model.c225
-rw-r--r--src/trg-peers-model.h85
-rw-r--r--src/trg-peers-tree-view.c69
-rw-r--r--src/trg-peers-tree-view.h54
-rw-r--r--src/trg-preferences-dialog.c327
-rw-r--r--src/trg-preferences-dialog.h56
-rw-r--r--src/trg-preferences.h32
-rw-r--r--src/trg-remote-prefs-dialog.c541
-rw-r--r--src/trg-remote-prefs-dialog.h57
-rw-r--r--src/trg-state-selector.c161
-rw-r--r--src/trg-state-selector.h63
-rw-r--r--src/trg-status-bar.c130
-rw-r--r--src/trg-status-bar.h60
-rw-r--r--src/trg-toolbar.c267
-rw-r--r--src/trg-toolbar.h56
-rw-r--r--src/trg-torrent-add-url-dialog.c129
-rw-r--r--src/trg-torrent-add-url-dialog.h57
-rw-r--r--src/trg-torrent-model.c297
-rw-r--r--src/trg-torrent-model.h102
-rw-r--r--src/trg-torrent-props-dialog.c372
-rw-r--r--src/trg-torrent-props-dialog.h57
-rw-r--r--src/trg-torrent-tree-view.c122
-rw-r--r--src/trg-torrent-tree-view.h58
-rw-r--r--src/trg-trackers-model.c72
-rw-r--r--src/trg-trackers-model.h62
-rw-r--r--src/trg-trackers-tree-view.c51
-rw-r--r--src/trg-trackers-tree-view.h54
-rw-r--r--src/trg-tree-view.c178
-rw-r--r--src/trg-tree-view.h74
-rw-r--r--src/util.c261
-rw-r--r--src/util.h56
98 files changed, 14021 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..b79e880
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,6 @@
+ transmission-remote-gtk authors
+ ===============================
+
+transmission-remote-gtk is developed by:
+
+ Alan Fitton <alan-at-eth0.org.uk>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..8828815
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,3 @@
+ACLOCAL_AMFLAGS = -I m4
+AUTOMAKE_OPTIONS = foreign
+SUBDIRS = src
diff --git a/README b/README
new file mode 100644
index 0000000..58ae42a
--- /dev/null
+++ b/README
@@ -0,0 +1,25 @@
+ABOUT
+
+ transmission-remote-gtk is a GTK client for remote management of
+ the Transmission BitTorrent client, using its HTTP RPC protocol.
+
+BUILDING
+
+ * To optionally see which country peers are located in,
+ install libgeoip and its headers when building.
+
+ Building a Transmission release from the command line:
+
+ $ tar zxfv transmission-remote-gtk-0.1.0.tar.gz
+ $ cd transmission-0.1.0
+ $ ./configure
+ $ make
+ $ sudo make install
+
+ Building from subversion checkout:
+
+ $ svn co <repository URL>
+ $ ./autogen.sh
+ $ ./configure
+ $ make
+ $ sudo make install
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..043b605
--- /dev/null
+++ b/TODO
@@ -0,0 +1,11 @@
+trg todo
+
+stats window
+remote settings
+better icons, inc status icon
+statusicon right click
+proxy
+file priorities
+
+peers graph
+geoip flags
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..a9230a8
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,997 @@
+# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.66],,
+[m4_warning([this file was generated for autoconf 2.66.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+dnl AM_GCONF_SOURCE_2
+dnl Defines GCONF_SCHEMA_CONFIG_SOURCE which is where you should install schemas
+dnl (i.e. pass to gconftool-2
+dnl Defines GCONF_SCHEMA_FILE_DIR which is a filesystem directory where
+dnl you should install foo.schemas files
+dnl
+
+AC_DEFUN([AM_GCONF_SOURCE_2],
+[
+ if test "x$GCONF_SCHEMA_INSTALL_SOURCE" = "x"; then
+ GCONF_SCHEMA_CONFIG_SOURCE=`gconftool-2 --get-default-source`
+ else
+ GCONF_SCHEMA_CONFIG_SOURCE=$GCONF_SCHEMA_INSTALL_SOURCE
+ fi
+
+ AC_ARG_WITH([gconf-source],
+ AC_HELP_STRING([--with-gconf-source=sourceaddress],
+ [Config database for installing schema files.]),
+ [GCONF_SCHEMA_CONFIG_SOURCE="$withval"],)
+
+ AC_SUBST(GCONF_SCHEMA_CONFIG_SOURCE)
+ AC_MSG_RESULT([Using config source $GCONF_SCHEMA_CONFIG_SOURCE for schema installation])
+
+ if test "x$GCONF_SCHEMA_FILE_DIR" = "x"; then
+ GCONF_SCHEMA_FILE_DIR='$(sysconfdir)/gconf/schemas'
+ fi
+
+ AC_ARG_WITH([gconf-schema-file-dir],
+ AC_HELP_STRING([--with-gconf-schema-file-dir=dir],
+ [Directory for installing schema files.]),
+ [GCONF_SCHEMA_FILE_DIR="$withval"],)
+
+ AC_SUBST(GCONF_SCHEMA_FILE_DIR)
+ AC_MSG_RESULT([Using $GCONF_SCHEMA_FILE_DIR as install directory for schema files])
+
+ AC_ARG_ENABLE(schemas-install,
+ AC_HELP_STRING([--disable-schemas-install],
+ [Disable the schemas installation]),
+ [case ${enableval} in
+ yes|no) ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-install]) ;;
+ esac])
+ AM_CONDITIONAL([GCONF_SCHEMAS_INSTALL], [test "$enable_schemas_install" != no])
+])
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.11.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.11.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 10
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 5
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled. FIXME. This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 16
+
+# This macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+
+dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([m4/pkg.m4])
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..5bce9ba
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,1578 @@
+#!/bin/sh
+# a u t o g e n . s h
+#
+# Copyright (c) 2005-2009 United States Government as represented by
+# the U.S. Army Research Laboratory.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+###
+#
+# Script for automatically preparing the sources for compilation by
+# performing the myriad of necessary steps. The script attempts to
+# detect proper version support, and outputs warnings about particular
+# systems that have autotool peculiarities.
+#
+# Basically, if everything is set up and installed correctly, the
+# script will validate that minimum versions of the GNU Build System
+# tools are installed, account for several common configuration
+# issues, and then simply run autoreconf for you.
+#
+# If autoreconf fails, which can happen for many valid configurations,
+# this script proceeds to run manual preparation steps effectively
+# providing a POSIX shell script (mostly complete) reimplementation of
+# autoreconf.
+#
+# The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER
+# environment variables and corresponding _OPTIONS variables (e.g.
+# AUTORECONF_OPTIONS) may be used to override the default automatic
+# detection behaviors. Similarly the _VERSION variables will override
+# the minimum required version numbers.
+#
+# Examples:
+#
+# To obtain help on usage:
+# ./autogen.sh --help
+#
+# To obtain verbose output:
+# ./autogen.sh --verbose
+#
+# To skip autoreconf and prepare manually:
+# AUTORECONF=false ./autogen.sh
+#
+# To verbosely try running with an older (unsupported) autoconf:
+# AUTOCONF_VERSION=2.50 ./autogen.sh --verbose
+#
+# Author:
+# Christopher Sean Morrison <morrison@brlcad.org>
+#
+# Patches:
+# Sebastian Pipping <sebastian@pipping.org>
+#
+######################################################################
+
+# set to minimum acceptable version of autoconf
+if [ "x$AUTOCONF_VERSION" = "x" ] ; then
+ AUTOCONF_VERSION=2.52
+fi
+# set to minimum acceptable version of automake
+if [ "x$AUTOMAKE_VERSION" = "x" ] ; then
+ AUTOMAKE_VERSION=1.6.0
+fi
+# set to minimum acceptable version of libtool
+if [ "x$LIBTOOL_VERSION" = "x" ] ; then
+ LIBTOOL_VERSION=1.4.2
+fi
+
+
+##################
+# ident function #
+##################
+ident ( ) {
+ # extract copyright from header
+ __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`"
+ if [ "x$__copyright" = "x" ] ; then
+ __copyright="`date +%Y`"
+ fi
+
+ # extract version from CVS Id string
+ __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $"
+ __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`"
+ if [ "x$__version" = "x" ] ; then
+ __version=""
+ fi
+
+ echo "autogen.sh build preparation script by Christopher Sean Morrison"
+ echo " + config.guess download patch by Sebastian Pipping (2008-12-03)"
+ echo "revised 3-clause BSD-style license, copyright (c) $__copyright"
+ echo "script version $__version, ISO/IEC 9945 POSIX shell script"
+}
+
+
+##################
+# USAGE FUNCTION #
+##################
+usage ( ) {
+ echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]"
+ echo " --help Help on $NAME_OF_AUTOGEN usage"
+ echo " --verbose Verbose progress output"
+ echo " --quiet Quiet suppressed progress output"
+ echo " --download Download the latest config.guess from gnulib"
+ echo " --version Only perform GNU Build System version checks"
+ echo
+ echo "Description: This script will validate that minimum versions of the"
+ echo "GNU Build System tools are installed and then run autoreconf for you."
+ echo "Should autoreconf fail, manual preparation steps will be run"
+ echo "potentially accounting for several common preparation issues. The"
+
+ echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER,"
+ echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS"
+ echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the"
+ echo "default automatic detection behavior."
+ echo
+
+ ident
+
+ return 0
+}
+
+
+##########################
+# VERSION_ERROR FUNCTION #
+##########################
+version_error ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided a version"
+ exit 1
+ fi
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided an application name"
+ exit 1
+ fi
+ $ECHO
+ $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch,"
+ $ECHO " at least version $1 of $2 must be installed."
+ $ECHO
+ $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will"
+ $ECHO "run configure or make. Either the GNU Autotools will need to be installed"
+ $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source"
+ $ECHO "code on another system and then transferred to here. -- Cheers!"
+ $ECHO
+}
+
+##########################
+# VERSION_CHECK FUNCTION #
+##########################
+version_check ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_check was not provided a minimum version"
+ exit 1
+ fi
+ _min="$1"
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version check was not provided a comparison version"
+ exit 1
+ fi
+ _cur="$2"
+
+ # needed to handle versions like 1.10 and 1.4-p6
+ _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+ _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+
+ _min_major="`echo $_min | cut -d. -f1`"
+ _min_minor="`echo $_min | cut -d. -f2`"
+ _min_patch="`echo $_min | cut -d. -f3`"
+
+ _cur_major="`echo $_cur | cut -d. -f1`"
+ _cur_minor="`echo $_cur | cut -d. -f2`"
+ _cur_patch="`echo $_cur | cut -d. -f3`"
+
+ if [ "x$_min_major" = "x" ] ; then
+ _min_major=0
+ fi
+ if [ "x$_min_minor" = "x" ] ; then
+ _min_minor=0
+ fi
+ if [ "x$_min_patch" = "x" ] ; then
+ _min_patch=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_major=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_minor=0
+ fi
+ if [ "x$_cur_patch" = "x" ] ; then
+ _cur_patch=0
+ fi
+
+ $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}"
+
+ if [ $_min_major -lt $_cur_major ] ; then
+ return 0
+ elif [ $_min_major -eq $_cur_major ] ; then
+ if [ $_min_minor -lt $_cur_minor ] ; then
+ return 0
+ elif [ $_min_minor -eq $_cur_minor ] ; then
+ if [ $_min_patch -lt $_cur_patch ] ; then
+ return 0
+ elif [ $_min_patch -eq $_cur_patch ] ; then
+ return 0
+ fi
+ fi
+ fi
+ return 1
+}
+
+
+######################################
+# LOCATE_CONFIGURE_TEMPLATE FUNCTION #
+######################################
+locate_configure_template ( ) {
+ _pwd="`pwd`"
+ if test -f "./configure.ac" ; then
+ echo "./configure.ac"
+ elif test -f "./configure.in" ; then
+ echo "./configure.in"
+ elif test -f "$_pwd/configure.ac" ; then
+ echo "$_pwd/configure.ac"
+ elif test -f "$_pwd/configure.in" ; then
+ echo "$_pwd/configure.in"
+ elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then
+ echo "$PATH_TO_AUTOGEN/configure.ac"
+ elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then
+ echo "$PATH_TO_AUTOGEN/configure.in"
+ fi
+}
+
+
+##################
+# argument check #
+##################
+ARGS="$*"
+PATH_TO_AUTOGEN="`dirname $0`"
+NAME_OF_AUTOGEN="`basename $0`"
+AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN"
+
+LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4"
+
+if [ "x$HELP" = "x" ] ; then
+ HELP=no
+fi
+if [ "x$QUIET" = "x" ] ; then
+ QUIET=no
+fi
+if [ "x$VERBOSE" = "x" ] ; then
+ VERBOSE=no
+fi
+if [ "x$VERSION_ONLY" = "x" ] ; then
+ VERSION_ONLY=no
+fi
+if [ "x$DOWNLOAD" = "x" ] ; then
+ DOWNLOAD=no
+fi
+if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then
+ AUTORECONF_OPTIONS="-i -f"
+fi
+if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then
+ AUTOCONF_OPTIONS="-f"
+fi
+if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then
+ AUTOMAKE_OPTIONS="-a -c -f"
+fi
+ALT_AUTOMAKE_OPTIONS="-a -c"
+if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then
+ LIBTOOLIZE_OPTIONS="--automake -c -f"
+fi
+ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force"
+if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then
+ ACLOCAL_OPTIONS=""
+fi
+if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then
+ AUTOHEADER_OPTIONS=""
+fi
+if [ "x$CONFIG_GUESS_URL" = "x" ] ; then
+ CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD"
+fi
+for arg in $ARGS ; do
+ case "x$arg" in
+ x--help) HELP=yes ;;
+ x-[hH]) HELP=yes ;;
+ x--quiet) QUIET=yes ;;
+ x-[qQ]) QUIET=yes ;;
+ x--verbose) VERBOSE=yes ;;
+ x-[dD]) DOWNLOAD=yes ;;
+ x--download) DOWNLOAD=yes ;;
+ x-[vV]) VERBOSE=yes ;;
+ x--version) VERSION_ONLY=yes ;;
+ *)
+ echo "Unknown option: $arg"
+ echo
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+
+#####################
+# environment check #
+#####################
+
+# sanity check before recursions potentially begin
+if [ ! -f "$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: $AUTOGEN_SH does not exist"
+ if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH"
+ fi
+ exit 1
+fi
+
+# force locale setting to C so things like date output as expected
+LC_ALL=C
+
+# commands that this script expects
+for __cmd in echo head tail pwd ; do
+ echo "test" | $__cmd > /dev/null 2>&1
+ if [ $? != 0 ] ; then
+ echo "INTERNAL ERROR: '${__cmd}' command is required"
+ exit 2
+ fi
+done
+echo "test" | grep "test" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: grep command is required"
+ exit 1
+fi
+echo "test" | sed "s/test/test/" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: sed command is required"
+ exit 1
+fi
+
+
+# determine the behavior of echo
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+# determine the behavior of head
+case "x`echo 'head' | head -n 1 2>&1`" in
+ *xhead*) HEAD_N="n " ;;
+ *) HEAD_N="" ;;
+esac
+
+# determine the behavior of tail
+case "x`echo 'tail' | tail -n 1 2>&1`" in
+ *xtail*) TAIL_N="n " ;;
+ *) TAIL_N="" ;;
+esac
+
+VERBOSE_ECHO=:
+ECHO=:
+if [ "x$QUIET" = "xyes" ] ; then
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output quelled by quiet option. Further output disabled."
+ fi
+else
+ ECHO=echo
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output enabled"
+ VERBOSE_ECHO=echo
+ fi
+fi
+
+
+# allow a recursive run to disable further recursions
+if [ "x$RUN_RECURSIVE" = "x" ] ; then
+ RUN_RECURSIVE=yes
+fi
+
+
+################################################
+# check for help arg and bypass version checks #
+################################################
+if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then
+ HELP=yes
+fi
+if [ "x$HELP" = "xyes" ] ; then
+ usage
+ $ECHO "---"
+ $ECHO "Help was requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#######################
+# set up signal traps #
+#######################
+untrap_abnormal ( ) {
+ for sig in 1 2 13 15; do
+ trap - $sig
+ done
+}
+
+# do this cleanup whenever we exit.
+trap '
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # restore/delete backup files
+ if test "x$PFC_INIT" = "x1" ; then
+ recursive_restore
+ fi
+' 0
+
+# trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15)
+for sig in 1 2 13 15; do
+ trap '
+ $ECHO ""
+ $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'"
+
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # clean up on abnormal exit
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+
+ if test -f "acinclude.m4.$$.backup" ; then
+ $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4"
+ chmod u+w acinclude.m4
+ cat acinclude.m4.$$.backup > acinclude.m4
+
+ $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup"
+ rm -f acinclude.m4.$$.backup
+ fi
+
+ { (exit 1); exit 1; }
+' $sig
+done
+
+
+#############################
+# look for a configure file #
+#############################
+if [ "x$CONFIGURE" = "x" ] ; then
+ CONFIGURE="`locate_configure_template`"
+ if [ ! "x$CONFIGURE" = "x" ] ; then
+ $VERBOSE_ECHO "Found a configure template: $CONFIGURE"
+ fi
+else
+ $ECHO "Using CONFIGURE environment variable override: $CONFIGURE"
+fi
+if [ "x$CONFIGURE" = "x" ] ; then
+ if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ CONFIGURE=/dev/null
+ else
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+fi
+
+####################
+# get project name #
+####################
+if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if [ "x$PROJECT" = "xAC_INIT" ] ; then
+ # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead
+ PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ fi
+ if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then
+ PROJECT="project"
+ fi
+ if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="project"
+ fi
+else
+ $ECHO "Using PROJECT environment variable override: $PROJECT"
+fi
+$ECHO "Preparing the $PROJECT build system...please wait"
+$ECHO
+
+
+########################
+# check for autoreconf #
+########################
+HAVE_AUTORECONF=no
+if [ "x$AUTORECONF" = "x" ] ; then
+ for AUTORECONF in autoreconf ; do
+ $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version"
+ $AUTORECONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ HAVE_AUTORECONF=yes
+ break
+ fi
+ done
+else
+ HAVE_AUTORECONF=yes
+ $ECHO "Using AUTORECONF environment variable override: $AUTORECONF"
+fi
+
+
+##########################
+# autoconf version check #
+##########################
+_acfound=no
+if [ "x$AUTOCONF" = "x" ] ; then
+ for AUTOCONF in autoconf ; do
+ $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version"
+ $AUTOCONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _acfound=yes
+ break
+ fi
+ done
+else
+ _acfound=yes
+ $ECHO "Using AUTOCONF environment variable override: $AUTOCONF"
+fi
+
+_report_error=no
+if [ ! "x$_acfound" = "xyes" ] ; then
+ $ECHO "ERROR: Unable to locate GNU Autoconf."
+ _report_error=yes
+else
+ _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Autoconf version $_version"
+ version_check "$AUTOCONF_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOCONF_VERSION" "GNU Autoconf"
+ exit 1
+fi
+
+
+##########################
+# automake version check #
+##########################
+_amfound=no
+if [ "x$AUTOMAKE" = "x" ] ; then
+ for AUTOMAKE in automake ; do
+ $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version"
+ $AUTOMAKE --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _amfound=yes
+ break
+ fi
+ done
+else
+ _amfound=yes
+ $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE"
+fi
+
+
+_report_error=no
+if [ ! "x$_amfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Automake."
+ _report_error=yes
+else
+ _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Automake version $_version"
+ version_check "$AUTOMAKE_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOMAKE_VERSION" "GNU Automake"
+ exit 1
+fi
+
+
+########################
+# check for libtoolize #
+########################
+HAVE_LIBTOOLIZE=yes
+HAVE_ALT_LIBTOOLIZE=no
+_ltfound=no
+if [ "x$LIBTOOLIZE" = "x" ] ; then
+ LIBTOOLIZE=libtoolize
+ $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version"
+ $LIBTOOLIZE --version > /dev/null 2>&1
+ if [ ! $? = 0 ] ; then
+ HAVE_LIBTOOLIZE=no
+ $ECHO
+ if [ "x$HAVE_AUTORECONF" = "xno" ] ; then
+ $ECHO "Warning: libtoolize does not appear to be available."
+ else
+ $ECHO "Warning: libtoolize does not appear to be available. This means that"
+ $ECHO "the automatic build preparation via autoreconf will probably not work."
+ $ECHO "Preparing the build by running each step individually, however, should"
+ $ECHO "work and will be done automatically for you if autoreconf fails."
+ fi
+
+ # look for some alternates
+ for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do
+ $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version"
+ _glibtoolize="`$tool --version > /dev/null 2>&1`"
+ if [ $? = 0 ] ; then
+ $VERBOSE_ECHO "Found $tool --version"
+ _glti="`which $tool`"
+ if [ "x$_glti" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool with which"
+ continue;
+ fi
+ if test ! -f "$_glti" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file"
+ continue;
+ fi
+ _gltidir="`dirname $_glti`"
+ if [ "x$_gltidir" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti"
+ continue;
+ fi
+ if test ! -d "$_gltidir" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory"
+ continue;
+ fi
+ HAVE_ALT_LIBTOOLIZE=yes
+ LIBTOOLIZE="$tool"
+ $ECHO
+ $ECHO "Fortunately, $tool was found which means that your system may simply"
+ $ECHO "have a non-standard or incomplete GNU Autotools install. If you have"
+ $ECHO "sufficient system access, it may be possible to quell this warning by"
+ $ECHO "running:"
+ $ECHO
+ sudo -V > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ $ECHO " sudo ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ else
+ $ECHO " ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ $ECHO "Run that as root or with proper permissions to the $_gltidir directory"
+ $ECHO
+ fi
+ _ltfound=yes
+ break
+ fi
+ done
+ else
+ _ltfound=yes
+ fi
+else
+ _ltfound=yes
+ $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE"
+fi
+
+
+############################
+# libtoolize version check #
+############################
+_report_error=no
+if [ ! "x$_ltfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Libtool."
+ _report_error=yes
+else
+ _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Libtool version $_version"
+ version_check "$LIBTOOL_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$LIBTOOL_VERSION" "GNU Libtool"
+ exit 1
+fi
+
+
+#####################
+# check for aclocal #
+#####################
+if [ "x$ACLOCAL" = "x" ] ; then
+ for ACLOCAL in aclocal ; do
+ $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version"
+ $ACLOCAL --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using ACLOCAL environment variable override: $ACLOCAL"
+fi
+
+
+########################
+# check for autoheader #
+########################
+if [ "x$AUTOHEADER" = "x" ] ; then
+ for AUTOHEADER in autoheader ; do
+ $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version"
+ $AUTOHEADER --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER"
+fi
+
+
+#########################
+# check if version only #
+#########################
+$VERBOSE_ECHO "Checking whether to only output version information"
+if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ $ECHO
+ ident
+ $ECHO "---"
+ $ECHO "Version requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#################################
+# PROTECT_FROM_CLOBBER FUNCTION #
+#################################
+protect_from_clobber ( ) {
+ PFC_INIT=1
+
+ # protect COPYING & INSTALL from overwrite by automake. the
+ # automake force option will (inappropriately) ignore the existing
+ # contents of a COPYING and/or INSTALL files (depending on the
+ # version) instead of just forcing *missing* files like it does
+ # for AUTHORS, NEWS, and README. this is broken but extremely
+ # prevalent behavior, so we protect against it by keeping a backup
+ # of the file that can later be restored.
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file} ; then
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "Already backed up ${file} in `pwd`"
+ else
+ $VERBOSE_ECHO "Backing up ${file} in `pwd`"
+ $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup"
+ cp -p ${file} ${file}.$$.protect_from_automake.backup
+ fi
+ fi
+ done
+}
+
+
+##############################
+# RECURSIVE_PROTECT FUNCTION #
+##############################
+recursive_protect ( ) {
+
+ # for projects using recursive configure, run the build
+ # preparation steps for the subdirectories. this function assumes
+ # START_PATH was set to pwd before recursion begins so that
+ # relative paths work.
+
+ # git 'r done, protect COPYING and INSTALL from being clobbered
+ protect_from_clobber
+
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+ # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure"
+
+ # look for subdirs
+ # $VERBOSE_ECHO "Looking for subdirs in `pwd`"
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Protecting files from automake in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r done
+ recursive_protect
+ done
+ fi
+} # end of recursive_protect
+
+
+#############################
+# RESTORE_CLOBBERED FUNCION #
+#############################
+restore_clobbered ( ) {
+
+ # The automake (and autoreconf by extension) -f/--force-missing
+ # option may overwrite COPYING and INSTALL even if they do exist.
+ # Here we restore the files if necessary.
+
+ spacer=no
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ if test -f ${file} ; then
+ # compare entire content, restore if needed
+ if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then
+ if test "x$spacer" = "xno" ; then
+ $VERBOSE_ECHO
+ spacer=yes
+ fi
+ # restore the backup
+ $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)"
+ $VERBOSE_ECHO "rm -f ${file}"
+ rm -f ${file}
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # check contents
+ elif test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # -f ${file}
+
+ # just in case
+ $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup"
+ rm -f ${file}.$$.protect_from_automake.backup
+ fi # -f ${file}.$$.protect_from_automake.backup
+ done
+
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ return
+ fi
+
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ fi
+
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\""
+ rm -f "${_aux_dir}/${file}.backup"
+ fi
+ done
+} # end of restore_clobbered
+
+
+##############################
+# RECURSIVE_RESTORE FUNCTION #
+##############################
+recursive_restore ( ) {
+
+ # restore COPYING and INSTALL from backup if they were clobbered
+ # for each directory recursively.
+
+ # git 'r undone
+ restore_clobbered
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+
+ # look for subdirs
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Checking files for automake damage in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r undone
+ recursive_restore
+ done
+ fi
+} # end of recursive_restore
+
+
+#######################
+# INITIALIZE FUNCTION #
+#######################
+initialize ( ) {
+
+ # this routine performs a variety of directory-specific
+ # initializations. some are sanity checks, some are preventive,
+ # and some are necessary setup detection.
+ #
+ # this function sets:
+ # CONFIGURE
+ # SEARCH_DIRS
+ # CONFIG_SUBDIRS
+
+ ##################################
+ # check for a configure template #
+ ##################################
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+
+ #####################
+ # detect an aux dir #
+ #####################
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ else
+ $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir"
+ fi
+
+ ################################
+ # detect a recursive configure #
+ ################################
+ CONFIG_SUBDIRS=""
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir"
+ CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir"
+ fi
+ done
+
+ ###########################################################
+ # make sure certain required files exist for GNU projects #
+ ###########################################################
+ _marker_found=""
+ _marker_found_message_intro='Detected non-GNU marker "'
+ _marker_found_message_mid='" in '
+ for marker in foreign cygnus ; do
+ _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid}
+ _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`"
+ break
+ fi
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}Makefile.am"
+ break
+ fi
+ fi
+ done
+ if [ "x${_marker_found}" = "x" ] ; then
+ _suggest_foreign=no
+ for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do
+ if [ ! -f $file ] ; then
+ $VERBOSE_ECHO "Touching ${file} since it does not exist"
+ _suggest_foreign=yes
+ touch $file
+ fi
+ done
+
+ if [ "x${_suggest_foreign}" = "xyes" ] ; then
+ $ECHO
+ $ECHO "Warning: Several files expected of projects that conform to the GNU"
+ $ECHO "coding standards were not found. The files were automatically added"
+ $ECHO "for you since you do not have a 'foreign' declaration specified."
+ $ECHO
+ $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`"
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file."
+ fi
+ $ECHO
+ fi
+ fi
+
+ ##################################################
+ # make sure certain generated files do not exist #
+ ##################################################
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\""
+ mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup"
+ fi
+ done
+
+ ############################
+ # search alternate m4 dirs #
+ ############################
+ SEARCH_DIRS=""
+ for dir in m4 ; do
+ if [ -d $dir ] ; then
+ $VERBOSE_ECHO "Found extra aclocal search directory: $dir"
+ SEARCH_DIRS="$SEARCH_DIRS -I $dir"
+ fi
+ done
+
+ ######################################
+ # remove any previous build products #
+ ######################################
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+# tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it
+# if test -f aclocal.m4 ; then
+# $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it"
+# $VERBOSE_ECHO "rm -f aclocal.m4"
+# rm -f aclocal.m4
+# fi
+
+} # end of initialize()
+
+
+##############
+# initialize #
+##############
+
+# stash path
+START_PATH="`pwd`"
+
+# Before running autoreconf or manual steps, some prep detection work
+# is necessary or useful. Only needs to occur once per directory, but
+# does need to traverse the entire subconfigure hierarchy to protect
+# files from being clobbered even by autoreconf.
+recursive_protect
+
+# start from where we started
+cd "$START_PATH"
+
+# get ready to process
+initialize
+
+
+#########################################
+# DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION #
+#########################################
+
+# TODO - should make sure wget/curl exist and/or work before trying to
+# use them.
+
+download_gnulib_config_guess () {
+ # abuse gitweb to download gnulib's latest config.guess via HTTP
+ config_guess_temp="config.guess.$$.download"
+ ret=1
+ for __cmd in wget curl fetch ; do
+ $VERBOSE_ECHO "Checking for command ${__cmd}"
+ ${__cmd} --version > /dev/null 2>&1
+ ret=$?
+ if [ ! $ret = 0 ] ; then
+ continue
+ fi
+
+ __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'`
+ $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}"
+
+ opts=""
+ case ${__cmd} in
+ wget)
+ opts="-O"
+ ;;
+ curl)
+ opts="-o"
+ ;;
+ fetch)
+ opts="-t 5 -f"
+ ;;
+ esac
+
+ $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\""
+ eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ mv -f "${config_guess_temp}" ${_aux_dir}/config.guess
+ ret=0
+ break
+ fi
+ done
+
+ if [ ! $ret = 0 ] ; then
+ $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL"
+ rm -f "${config_guess_temp}"
+ fi
+}
+
+
+##############################
+# LIBTOOLIZE_NEEDED FUNCTION #
+##############################
+libtoolize_needed () {
+ ret=1 # means no, don't need libtoolize
+ for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ ret=0 # means yes, need to run libtoolize
+ break
+ fi
+ done
+ return ${ret}
+}
+
+
+
+############################################
+# prepare build via autoreconf or manually #
+############################################
+reconfigure_manually=no
+if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then
+ $ECHO
+ $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C"
+
+ $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS"
+ autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoreconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then
+ $ECHO
+ $ECHO "Warning: autoreconf failed but due to what is usually a common libtool"
+ $ECHO "misconfiguration issue. This problem is encountered on systems that"
+ $ECHO "have installed libtoolize under a different name without providing a"
+ $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable."
+ $ECHO
+ $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE"
+
+ export LIBTOOLIZE
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+
+ $ECHO "Warning: $AUTORECONF failed"
+
+ if test -f ltmain.sh ; then
+ $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should"
+ fi
+
+ $ECHO "Attempting to run the preparation steps individually"
+ reconfigure_manually=yes
+ else
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ if libtoolize_needed ; then
+ download_gnulib_config_guess
+ fi
+ fi
+ fi
+else
+ reconfigure_manually=yes
+fi
+
+
+############################
+# LIBTOOL_FAILURE FUNCTION #
+############################
+libtool_failure ( ) {
+
+ # libtool is rather error-prone in comparison to the other
+ # autotools and this routine attempts to compensate for some
+ # common failures. the output after a libtoolize failure is
+ # parsed for an error related to AC_PROG_LIBTOOL and if found, we
+ # attempt to inject a project-provided libtool.m4 file.
+
+ _autoconf_output="$1"
+
+ if [ "x$RUN_RECURSIVE" = "xno" ] ; then
+ # we already tried the libtool.m4, don't try again
+ return 1
+ fi
+
+ if test -f "$LIBTOOL_M4" ; then
+ found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`"
+ if test ! "x$found_libtool" = "x" ; then
+ if test -f acinclude.m4 ; then
+ rm -f acinclude.m4.$$.backup
+ $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup"
+ cat acinclude.m4 > acinclude.m4.$$.backup
+ fi
+ $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4"
+ chmod u+w acinclude.m4
+ cat "$LIBTOOL_M4" >> acinclude.m4
+
+ # don't keep doing this
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $ECHO
+ $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4"
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+}
+
+
+###########################
+# MANUAL_AUTOGEN FUNCTION #
+###########################
+manual_autogen ( ) {
+
+ ##################################################
+ # Manual preparation steps taken are as follows: #
+ # aclocal [-I m4] #
+ # libtoolize --automake -c -f #
+ # aclocal [-I m4] #
+ # autoconf -f #
+ # autoheader #
+ # automake -a -c -f #
+ ##################################################
+
+ ###########
+ # aclocal #
+ ###########
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi
+
+ ##############
+ # libtoolize #
+ ##############
+ if libtoolize_needed ; then
+ if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ else
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ fi
+ fi
+
+ ###########
+ # aclocal #
+ ###########
+ # re-run again as instructed by libtoolize
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+
+ # libtoolize might put ltmain.sh in the wrong place
+ if test -f ltmain.sh ; then
+ if test ! -f "${_aux_dir}/ltmain.sh" ; then
+ $ECHO
+ $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory"
+ $ECHO
+ $ECHO "Fortunately, the problem can be worked around by simply copying the"
+ $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you."
+ $ECHO
+ $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\""
+ cp -p ltmain.sh "${_aux_dir}/ltmain.sh"
+ $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+ fi
+ fi # ltmain.sh
+
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ download_gnulib_config_guess
+ fi
+ fi # libtoolize_needed
+
+ ############
+ # autoconf #
+ ############
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS"
+ autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # retry without the -f and check for usage of macros that are too new
+ ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE"
+ ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE"
+ ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T"
+
+ macros_to_search=""
+ ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`"
+ ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`"
+
+ if [ $ac_major -lt 2 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ else
+ if [ $ac_minor -lt 54 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ elif [ $ac_minor -lt 55 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros"
+ elif [ $ac_minor -lt 59 ] ; then
+ macros_to_search="$ac2_59_macros"
+ fi
+ fi
+
+ configure_ac_macros=__none__
+ for feature in $macros_to_search ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ if [ "x$configure_ac_macros" = "x__none__" ] ; then
+ configure_ac_macros="$feature"
+ else
+ configure_ac_macros="$feature $configure_ac_macros"
+ fi
+ fi
+ done
+ if [ ! "x$configure_ac_macros" = "x__none__" ] ; then
+ $ECHO
+ $ECHO "Warning: Unsupported macros were found in $CONFIGURE"
+ $ECHO
+ $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any"
+ $ECHO "unsupported macros are used that exceed the minimum version"
+ $ECHO "settings specified within this file. As such, the following macros"
+ $ECHO "should be removed from configure.ac or the version numbers in this"
+ $ECHO "file should be increased:"
+ $ECHO
+ $ECHO "$configure_ac_macros"
+ $ECHO
+ $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C"
+ fi
+
+ ###################
+ # autoconf, retry #
+ ###################
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF"
+ autoconf_output="`$AUTOCONF 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # test if libtool is busted
+ libtool_failure "$autoconf_output"
+
+ # let the user know what went wrong
+ cat <<EOF
+$autoconf_output
+EOF
+ $ECHO "ERROR: $AUTOCONF failed"
+ exit 2
+ else
+ # autoconf sans -f and possibly sans unsupported options succeed so warn verbosely
+ $ECHO
+ $ECHO "Warning: autoconf seems to have succeeded by removing the following options:"
+ $ECHO " AUTOCONF_OPTIONS=\"$AUTOCONF_OPTIONS\""
+ $ECHO
+ $ECHO "Removing those options should not be necessary and indicate some other"
+ $ECHO "problem with the build system. The build preparation is highly suspect"
+ $ECHO "and may result in configuration or compilation errors. Consider"
+ if [ "x$VERBOSE_ECHO" = "x:" ] ; then
+ $ECHO "rerunning the build preparation with verbose output enabled."
+ $ECHO " $AUTOGEN_SH --verbose"
+ else
+ $ECHO "reviewing the minimum GNU Autotools version settings contained in"
+ $ECHO "this script along with the macros being used in your `basename \"$CONFIGURE\"` file."
+ fi
+ $ECHO
+ $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+ fi # autoconf ret = 0
+ fi # autoconf ret = 0
+
+ ##############
+ # autoheader #
+ ##############
+ need_autoheader=no
+ for feature in AM_CONFIG_HEADER AC_CONFIG_HEADER ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ need_autoheader=yes
+ break
+ fi
+ done
+ if [ "x$need_autoheader" = "xyes" ] ; then
+ $VERBOSE_ECHO "$AUTOHEADER $AUTOHEADER_OPTIONS"
+ autoheader_output="`$AUTOHEADER $AUTOHEADER_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoheader_output"
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $AUTOHEADER failed" && exit 2 ; fi
+ fi # need_autoheader
+
+ ############
+ # automake #
+ ############
+ need_automake=no
+ for feature in AM_INIT_AUTOMAKE ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ need_automake=yes
+ break
+ fi
+ done
+
+ if [ "x$need_automake" = "xyes" ] ; then
+ $VERBOSE_ECHO "$AUTOMAKE $AUTOMAKE_OPTIONS"
+ automake_output="`$AUTOMAKE $AUTOMAKE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$automake_output"
+
+ if [ ! $ret = 0 ] ; then
+
+ ###################
+ # automake, retry #
+ ###################
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOMAKE $ALT_AUTOMAKE_OPTIONS"
+ # retry without the -f
+ automake_output="`$AUTOMAKE $ALT_AUTOMAKE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$automake_output"
+
+ if [ ! $ret = 0 ] ; then
+ # test if libtool is busted
+ libtool_failure "$automake_output"
+
+ # let the user know what went wrong
+ cat <<EOF
+$automake_output
+EOF
+ $ECHO "ERROR: $AUTOMAKE failed"
+ exit 2
+ fi # automake retry
+ fi # automake ret = 0
+ fi # need_automake
+} # end of manual_autogen
+
+
+#####################################
+# RECURSIVE_MANUAL_AUTOGEN FUNCTION #
+#####################################
+recursive_manual_autogen ( ) {
+
+ # run the build preparation steps manually for this directory
+ manual_autogen
+
+ # for projects using recursive configure, run the build
+ # preparation steps for the subdirectories.
+ if [ ! "x$CONFIG_SUBDIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively configuring the following directories:"
+ $VERBOSE_ECHO " $CONFIG_SUBDIRS"
+ for dir in $CONFIG_SUBDIRS ; do
+ $VERBOSE_ECHO "Processing recursive configure in $dir"
+ cd "$START_PATH"
+ cd "$dir"
+
+ # new directory, prepare
+ initialize
+
+ # run manual steps for the subdir and any others below
+ recursive_manual_autogen
+ done
+ fi
+}
+
+
+################################
+# run manual preparation steps #
+################################
+if [ "x$reconfigure_manually" = "xyes" ] ; then
+ $ECHO
+ $ECHO $ECHO_N "Preparing build ... $ECHO_C"
+
+ recursive_manual_autogen
+fi
+
+
+#########################
+# restore and summarize #
+#########################
+cd "$START_PATH"
+
+# restore COPYING and INSTALL from backup if necessary
+recursive_restore
+
+# make sure we end up with a configure script
+config_ac="`locate_configure_template`"
+config="`echo $config_ac | sed 's/\.ac$//' | sed 's/\.in$//'`"
+if [ "x$config" = "x" ] ; then
+ $VERBOSE_ECHO "Could not locate the configure template (from `pwd`)"
+fi
+
+# summarize
+$ECHO "done"
+$ECHO
+if test "x$config" = "x" -o ! -f "$config" ; then
+ $ECHO "WARNING: The $PROJECT build system should now be prepared but there"
+ $ECHO "does not seem to be a resulting configure file. This is unexpected"
+ $ECHO "and likely the result of an error. You should run $NAME_OF_AUTOGEN"
+ $ECHO "with the --verbose option to get more details on a potential"
+ $ECHO "misconfiguration."
+else
+ $ECHO "The $PROJECT build system is now prepared. To build here, run:"
+ $ECHO " $config"
+ $ECHO " make"
+fi
+
+
+# Local Variables:
+# mode: sh
+# tab-width: 8
+# sh-basic-offset: 4
+# sh-indentation: 4
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=4 tabstop=8
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..ec6b96d
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,68 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* GeoIP Support. */
+#undef HAVE_GEOIP
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+ and to 0 otherwise. */
+#undef HAVE_REALLOC
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to rpl_realloc if the replacement function should be used. */
+#undef realloc
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..2b9eab6
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,43 @@
+#AC_PREREQ([2.65])
+AC_INIT(transmission-remote-gtk, 0.1, alan@eth0.org.uk)
+AM_INIT_AUTOMAKE(transmission-remote-gtk, 0.1.0)
+AC_OUTPUT(Makefile src/Makefile)
+
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_SRCDIR([src/main.c])
+AC_CONFIG_HEADERS([config.h])
+
+AC_PROG_CC
+
+# Checks for header files.
+
+AC_CHECK_HEADERS([stdlib.h string.h])
+
+# Checks for programs.
+PKG_CHECK_MODULES([jsonglib], [json-glib-1.0])
+PKG_CHECK_MODULES([gthread], [gthread-2.0])
+PKG_CHECK_MODULES([gconf], [gconf-2.0])
+PKG_CHECK_MODULES([gtk], [gtk+-2.0])
+PKG_CHECK_MODULES([gio], [gio-2.0])
+PKG_CHECK_MODULES([unique], [unique-1.0])
+PKG_CHECK_MODULES([notify], [libnotify])
+
+AC_ARG_WITH([libgeoip], AC_HELP_STRING([--without-libgeoip], [disable GeoIP support]))
+have_libgeoip=no
+if test x$with_libgeoip != xno; then
+ AC_CHECK_HEADER([GeoIP.h],[have_geoip=yes],[have_geoip=no])
+fi
+if test "x$have_geoip" = "xyes"; then
+ AC_DEFINE(HAVE_GEOIP, 1, [GeoIP Support.])
+ GEOIP_LIBS="-lGeoIP"
+ AC_SUBST([GEOIP_LIBS])
+fi
+AM_CONDITIONAL([HAVE_GEOIP], [test "x$have_geoip" = "xyes"])
+
+AM_GCONF_SOURCE_2
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_FUNC_REALLOC
+
+AC_OUTPUT
diff --git a/m4/pkg.m4 b/m4/pkg.m4
new file mode 100644
index 0000000..73973f7
--- /dev/null
+++ b/m4/pkg.m4
@@ -0,0 +1,157 @@
+# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
+# serial 1 (pkg-config-0.24)
+#
+# Copyright © 2004 Scott James Remnant <scott@netsplit.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.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists. Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+# only at the first occurence in configure.ac, so if the first place
+# it's called might be skipped (such as if it is within an "if", you
+# have to call PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_default([$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+ [pkg_failed=yes])
+ else
+ pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ AC_MSG_RESULT([no])
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])
+ ])
+elif test $pkg_failed = untried; then
+ AC_MSG_RESULT([no])
+ m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])
+ ])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ $3
+fi[]dnl
+])# PKG_CHECK_MODULES
diff --git a/redhat/transmission-remote-gtk.spec b/redhat/transmission-remote-gtk.spec
new file mode 100644
index 0000000..0ce37a6
--- /dev/null
+++ b/redhat/transmission-remote-gtk.spec
@@ -0,0 +1,89 @@
+Name: transmission-remote-gtk
+Version: 0.1.0
+Release: 1
+Summary: Remote control client for Transmission BitTorrent
+
+Group: Applications/Internet
+License: GPLv2+
+URL: http://code.google.com/p/transmission-remote-gtk/
+Source0: %{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+# These two are just the oldest version tested
+Requires: gtk2
+Requires: glib2
+Requires: unique
+Requires: GConf2
+Requires: libnotify
+Requires: json-glib
+Requires: libcurl
+Requires: GeoIP
+
+BuildRequires: gtk2-devel
+BuildRequires: glib2-devel
+BuildRequires: unique-devel
+BuildRequires: GeoIP-devel
+BuildRequires: GConf2-devel
+BuildRequires: json-glib-devel
+BuildRequires: libcurl-devel
+BuildRequires: libnotify-devel
+
+Requires(pre): GConf2
+Requires(post): GConf2
+Requires(preun): GConf2
+Requires(post): desktop-file-utils
+Requires(postun): desktop-file-utils
+
+%description
+transmission-remote-gtk is a GTK client to remotely manage the Transmission BitTorrent client.
+
+%prep
+%setup -q
+
+%build
+%configure
+make %{?_smp_mflags}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+export GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL=1
+make install DESTDIR=$RPM_BUILD_ROOT
+unset GCONF_DISABLE_MAKEFILE_SCHEMA_INSTALL
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre
+if [ "$1" -gt 1 ]; then
+ export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
+ gconftool-2 --makefile-uninstall-rule \
+ %{_sysconfdir}/gconf/schemas/%{name}.schemas > /dev/null || :
+fi
+
+%post
+export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
+gconftool-2 --makefile-install-rule \
+ %{_sysconfdir}/gconf/schemas/%{name}.schemas > /dev/null || :
+
+update-desktop-database %{_datadir}/applications
+
+%postun
+update-desktop-database %{_datadir}/applications
+
+%preun
+if [ "$1" -eq 0 ]; then
+ export GCONF_CONFIG_SOURCE=`gconftool-2 --get-default-source`
+ gconftool-2 --makefile-uninstall-rule \
+ %{_sysconfdir}/gconf/schemas/%{name}.schemas > /dev/null || :
+fi
+
+%files
+%defattr(-,root,root,-)
+%doc README COPYING AUTHORS
+%{_sysconfdir}/gconf/schemas/%{name}.schemas
+%{_bindir}/%{name}
+%{_datadir}/applications/%{name}.desktop
+
+%changelog
+* Sun Jan 30 2010 Alan Fitton <alan@eth0.org.uk> - 0.1.0
+- Initial RPM Build
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..34fb0d1
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,28 @@
+EXTRA_DIST = transmission-remote-gtk.desktop.in
+CLEANFILES = transmission-remote-gtk.desktop
+
+desktopdir = $(datadir)/applications
+desktop_DATA = transmission-remote-gtk.desktop
+
+schemadir = @GCONF_SCHEMA_FILE_DIR@
+schema_DATA = transmission-remote-gtk.schemas
+
+bin_PROGRAMS = transmission-remote-gtk
+INCLUDES = --pedantic -Wall -I.. -O2 $(jsonglib_CFLAGS) $(gthread_CFLAGS) $(gtk_CFLAGS) $(gconf_CFLAGS) $(gio_CFLAGS) $(unique_CFLAGS) $(notify_CFLAGS)
+
+transmission_remote_gtk_SOURCES = main.c requests.c base64.c json.c http.c dispatch.c trg-main-window.c util.c trg-about-window.c torrent.c \
+ tpeer.c tfile.c session-get.c trg-client.c trg-preferences-dialog.c hig.c trg-torrent-tree-view.c \
+ trg-tree-view.c trg-torrent-model.c trg-peers-model.c trg-peers-tree-view.c trg-model.c trg-files-model.c \
+ trg-files-tree-view.c trg-state-selector.c trg-general-panel.c trg-toolbar.c trg-menu-bar.c \
+ trg-status-bar.c trg-trackers-tree-view.c trg-trackers-model.c trg-torrent-props-dialog.c \
+ trg-json-widgets.c trg-torrent-add-url-dialog.c trg-cell-renderer-speed.c trg-cell-renderer-size.c \
+ trg-cell-renderer-ratio.c trg-cell-renderer-eta.c trg-remote-prefs-dialog.c trg-cell-renderer-wanted.c \
+ trg-cell-renderer-priority.c
+
+transmission_remote_gtk_LDFLAGS = -lcurl $(jsonglib_LIBS) $(gtk_LIBS) $(gthread_LIBS) $(GEOIP_LIBS) $(gconf_LIBS) $(gio_LIBS) $(unique_LIBS) $(notify_LIBS)
+
+install-data-local:
+ GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) gconftool-2 --makefile-install-rule $(srcdir)/$(schema_DATA)
+
+transmission-remote-gtk.desktop: transmission-remote-gtk.desktop.in
+ sed -e 's,@bindir\@,$(bindir),g' $< > $@
diff --git a/src/base64.c b/src/base64.c
new file mode 100644
index 0000000..fbf07cb
--- /dev/null
+++ b/src/base64.c
@@ -0,0 +1,66 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "base64.h"
+
+static const char cb64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+void encodeblock(unsigned char in[3], unsigned char out[4], int len)
+{
+ out[0] = cb64[in[0] >> 2];
+ out[1] = cb64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)];
+ out[2] =
+ (unsigned char) (len >
+ 1 ? cb64[((in[1] & 0x0f) << 2) |
+ ((in[2] & 0xc0) >> 6)] : '=');
+ out[3] = (unsigned char) (len > 2 ? cb64[in[2] & 0x3f] : '=');
+}
+
+char *base64encode(char *filename)
+{
+ FILE *infile = fopen(filename, "rb");
+ unsigned char in[3], out[4];
+ int i, len, j = 0;
+ char *output = NULL;
+ while (!feof(infile)) {
+ len = 0;
+ for (i = 0; i < 3; i++) {
+ in[i] = (unsigned char) getc(infile);
+ if (!feof(infile)) {
+ len++;
+ } else {
+ in[i] = 0;
+ }
+ }
+ if (len) {
+ output = (char *) realloc(output, j + 5);
+ encodeblock(in, out, len);
+ for (i = 0; i < 4; i++) {
+ output[j++] = out[i];
+ }
+ }
+ }
+ output[j] = '\0';
+ fclose(infile);
+ return output;
+}
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 0000000..45cef3d
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,25 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BASE64_H_
+#define BASE64_H_
+
+char *base64encode(char *filename);
+
+#endif /* BASE64_H_ */
diff --git a/src/dispatch.c b/src/dispatch.c
new file mode 100644
index 0000000..8967ded
--- /dev/null
+++ b/src/dispatch.c
@@ -0,0 +1,109 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <json-glib/json-glib.h>
+#include <curl/curl.h>
+
+#include "dispatch.h"
+#include "http.h"
+#include "json.h"
+
+static gpointer dispatch_async_threadfunc(gpointer ptr);
+
+JsonObject *dispatch(trg_client * client, JsonNode * req, int *status)
+{
+ gchar *serialized;
+ struct http_response *response;
+ JsonObject *deserialized;
+ JsonNode *result;
+ GError *decode_error = NULL;
+
+ serialized = trg_serialize(req);
+ json_node_free(req);
+
+ g_printf("=>(outgoing)=> %s\n", serialized);
+ response = trg_http_perform(client, serialized);
+ g_free(serialized);
+
+ if (status != NULL)
+ *status = response->status;
+
+ if (response->status != CURLE_OK) {
+ http_response_free(response);
+ return NULL;
+ }
+
+ deserialized = trg_deserialize(response, &decode_error);
+ http_response_free(response);
+
+ if (decode_error != NULL) {
+ g_printf("JSON decoding error: %s\n", decode_error->message);
+ g_error_free(decode_error);
+ if (status != NULL)
+ *status = FAIL_JSON_DECODE;
+ return NULL;
+ }
+
+ result = json_object_get_member(deserialized, "result");
+ if (status != NULL
+ && (result == NULL
+ || g_strcmp0(json_node_get_string(result), "success") != 0))
+ *status = FAIL_RESPONSE_UNSUCCESSFUL;
+
+ return deserialized;
+}
+
+static gpointer dispatch_async_threadfunc(gpointer ptr)
+{
+ struct dispatch_async_args *args = (struct dispatch_async_args *) ptr;
+ int status;
+ JsonObject *result = dispatch(args->client, args->req, &status);
+ if (args->callback != NULL)
+ args->callback(result, status, args->data);
+ g_free(args);
+ return NULL;
+}
+
+GThread *dispatch_async(trg_client * client, JsonNode * req,
+ void (*callback) (JsonObject *, int, gpointer),
+ gpointer data)
+{
+ GError *error = NULL;
+ GThread *thread;
+ struct dispatch_async_args *args;
+
+ args = g_new(struct dispatch_async_args, 1);
+ args->callback = callback;
+ args->data = data;
+ args->req = req;
+ args->client = client;
+
+ thread =
+ g_thread_create(dispatch_async_threadfunc, args, FALSE, &error);
+ if (error != NULL) {
+ g_printf("thread creation error: %s\n", error->message);
+ g_error_free(error);
+ g_free(args);
+ return NULL;
+ } else {
+ return thread;
+ }
+}
diff --git a/src/dispatch.h b/src/dispatch.h
new file mode 100644
index 0000000..7c755b7
--- /dev/null
+++ b/src/dispatch.h
@@ -0,0 +1,40 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DISPATCH_H_
+#define DISPATCH_H_
+
+#include "trg-client.h"
+
+#define FAIL_JSON_DECODE -2
+#define FAIL_RESPONSE_UNSUCCESSFUL -3
+
+struct dispatch_async_args {
+ gpointer *data;
+ JsonNode *req;
+ trg_client *client;
+ void (*callback) (JsonObject *, int, gpointer);
+};
+
+JsonObject *dispatch(trg_client * client, JsonNode * req, int *status);
+GThread *dispatch_async(trg_client * client, JsonNode * req,
+ void (*callback) (JsonObject *, int, gpointer),
+ gpointer data);
+
+#endif /* DISPATCH_H_ */
diff --git a/src/hig.c b/src/hig.c
new file mode 100644
index 0000000..3c0586f
--- /dev/null
+++ b/src/hig.c
@@ -0,0 +1,208 @@
+/*
+ * This file Copyright (C) 2007-2010 Mnemosyne LLC
+ *
+ * This file is licensed by the GPL version 2. Works owned by the
+ * Transmission project are granted a special exemption to clause 2(b)
+ * so that the bulk of its code can remain under the MIT license.
+ * This exemption does not extend to derived works not owned by
+ * the Transmission project.
+ *
+ * $Id: hig.c 9868 2010-01-04 21:00:47Z charles $
+ */
+
+#include <gtk/gtk.h>
+#include "hig.h"
+
+GtkWidget *hig_workarea_create(void)
+{
+ GtkWidget *t = gtk_table_new(1, 2, FALSE);
+
+ gtk_container_set_border_width(GTK_CONTAINER(t), GUI_PAD_BIG);
+ gtk_table_set_col_spacing(GTK_TABLE(t), 0, GUI_PAD_BIG);
+ gtk_table_set_row_spacings(GTK_TABLE(t), GUI_PAD);
+ return t;
+}
+
+void hig_workarea_add_section_divider(GtkWidget * t, int *row)
+{
+ GtkWidget *w = gtk_alignment_new(0.0f, 0.0f, 0.0f, 0.0f);
+
+ gtk_widget_set_size_request(w, 0u, 6u);
+ gtk_table_attach(GTK_TABLE(t), w, 0, 2, *row, *row + 1, 0, 0, 0, 0);
+ ++*row;
+}
+
+void
+hig_workarea_add_section_title_widget(GtkWidget * t,
+ int *row, GtkWidget * w)
+{
+ gtk_table_attach(GTK_TABLE(t), w, 0, 2, *row, *row + 1, ~0, 0, 0, 0);
+ ++*row;
+}
+
+void
+hig_workarea_add_section_title(GtkWidget * t,
+ int *row, const char *section_title)
+{
+ char buf[512];
+ GtkWidget *l;
+
+ g_snprintf(buf, sizeof(buf), "<b>%s</b>", section_title);
+ l = gtk_label_new(buf);
+ gtk_misc_set_alignment(GTK_MISC(l), 0.0f, 0.5f);
+ gtk_label_set_use_markup(GTK_LABEL(l), TRUE);
+ hig_workarea_add_section_title_widget(t, row, l);
+}
+
+static GtkWidget *rowNew(GtkWidget * w)
+{
+ GtkWidget *a;
+ GtkWidget *h = gtk_hbox_new(FALSE, 0);
+
+ /* spacer */
+ a = gtk_alignment_new(0.0f, 0.0f, 0.0f, 0.0f);
+ gtk_widget_set_size_request(a, 18u, 0u);
+ gtk_box_pack_start(GTK_BOX(h), a, FALSE, FALSE, 0);
+
+ /* lhs widget */
+ if (GTK_IS_MISC(w))
+ gtk_misc_set_alignment(GTK_MISC(w), 0.0f, 0.5f);
+ if (GTK_IS_LABEL(w))
+ gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
+ gtk_box_pack_start(GTK_BOX(h), w, TRUE, TRUE, 0);
+
+ return h;
+}
+
+void hig_workarea_add_wide_control(GtkWidget * t, int *row, GtkWidget * w)
+{
+ GtkWidget *r = rowNew(w);
+
+ gtk_table_attach(GTK_TABLE(t), r, 0, 2, *row, *row + 1, GTK_FILL,
+ 0, 0, 0);
+ ++*row;
+}
+
+void
+hig_workarea_add_wide_tall_control(GtkWidget * t, int *row, GtkWidget * w)
+{
+ GtkWidget *r = rowNew(w);
+
+ gtk_table_attach(GTK_TABLE(t), r, 0, 2, *row, *row + 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+
+ ++*row;
+}
+
+GtkWidget *hig_workarea_add_wide_checkbutton(GtkWidget * t,
+ int *row,
+ const char *mnemonic_string,
+ gboolean is_active)
+{
+ GtkWidget *w = gtk_check_button_new_with_mnemonic(mnemonic_string);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), is_active);
+ hig_workarea_add_wide_control(t, row, w);
+ return w;
+}
+
+void hig_workarea_add_label_w(GtkWidget * t, int row, GtkWidget * l)
+{
+ GtkWidget *w = rowNew(l);
+
+ gtk_table_attach(GTK_TABLE(t), w, 0, 1, row, row + 1, GTK_FILL,
+ GTK_FILL, 0, 0);
+}
+
+GtkWidget *hig_workarea_add_label(GtkWidget * t,
+ int row, const char *mnemonic_string)
+{
+ GtkWidget *l = gtk_label_new_with_mnemonic(mnemonic_string);
+
+ hig_workarea_add_label_w(t, row, l);
+ return l;
+}
+
+static void
+hig_workarea_add_tall_control(GtkWidget * t, int row, GtkWidget * control)
+{
+ if (GTK_IS_MISC(control))
+ gtk_misc_set_alignment(GTK_MISC(control), 0.0f, 0.5f);
+
+ gtk_table_attach(GTK_TABLE(t), control,
+ 1, 2, row, row + 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+}
+
+void hig_workarea_add_control(GtkWidget * t, int row, GtkWidget * control)
+{
+ if (GTK_IS_MISC(control))
+ gtk_misc_set_alignment(GTK_MISC(control), 0.0f, 0.5f);
+
+ gtk_table_attach(GTK_TABLE(t), control,
+ 1, 2, row, row + 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0, 0);
+}
+
+void
+hig_workarea_add_row_w(GtkWidget * t,
+ int *row,
+ GtkWidget * label,
+ GtkWidget * control, GtkWidget * mnemonic)
+{
+ hig_workarea_add_label_w(t, *row, label);
+ hig_workarea_add_control(t, *row, control);
+ if (GTK_IS_LABEL(label))
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label),
+ mnemonic ? mnemonic : control);
+ ++*row;
+}
+
+GtkWidget *hig_workarea_add_row(GtkWidget * t,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control, GtkWidget * mnemonic)
+{
+ GtkWidget *l = gtk_label_new_with_mnemonic(mnemonic_string);
+
+ hig_workarea_add_row_w(t, row, l, control, mnemonic);
+ return l;
+}
+
+GtkWidget *hig_workarea_add_tall_row(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control,
+ GtkWidget * mnemonic)
+{
+ GtkWidget *l = gtk_label_new_with_mnemonic(mnemonic_string);
+ GtkWidget *h = gtk_hbox_new(FALSE, 0);
+ GtkWidget *v = gtk_vbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(h), l, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(v), h, FALSE, FALSE, GUI_PAD_SMALL);
+
+ hig_workarea_add_label_w(table, *row, v);
+ hig_workarea_add_tall_control(table, *row, control);
+
+ if (GTK_IS_LABEL(l))
+ gtk_label_set_mnemonic_widget(GTK_LABEL(l),
+ mnemonic ? mnemonic : control);
+
+ ++*row;
+ return l;
+}
+
+void hig_workarea_finish(GtkWidget * t, int *row)
+{
+ gtk_table_resize(GTK_TABLE(t), *row, 2);
+}
+
+void
+hig_message_dialog_set_text(GtkMessageDialog * dialog,
+ const char *primary, const char *secondary)
+{
+ gtk_message_dialog_set_markup(dialog, primary);
+ gtk_message_dialog_format_secondary_text(dialog, "%s", secondary);
+}
diff --git a/src/hig.h b/src/hig.h
new file mode 100644
index 0000000..186e66a
--- /dev/null
+++ b/src/hig.h
@@ -0,0 +1,85 @@
+/*
+ * This file Copyright (C) 2007-2010 Mnemosyne LLC
+ *
+ * This file is licensed by the GPL version 2. Works owned by the
+ * Transmission project are granted a special exemption to clause 2(b)
+ * so that the bulk of its code can remain under the MIT license.
+ * This exemption does not extend to derived works not owned by
+ * the Transmission project.
+ *
+ * $Id: hig.h 9868 2010-01-04 21:00:47Z charles $
+ */
+
+#ifndef __HIG_H__
+#define __HIG_H__
+
+#include <gtk/gtk.h>
+
+/**
+*** utility code for making dialog layout that follows the Gnome HIG.
+*** see section 8.2.2, Visual Design > Window Layout > Dialogs.
+**/
+
+GtkWidget *hig_workarea_create(void);
+
+void hig_workarea_add_section_divider(GtkWidget * table, int *row);
+
+void hig_workarea_add_section_title_widget(GtkWidget * t,
+ int *row, GtkWidget * w);
+
+void hig_workarea_add_section_title(GtkWidget * table,
+ int *row, const char *section_title);
+
+void hig_workarea_add_wide_tall_control(GtkWidget * table,
+ int *row, GtkWidget * w);
+
+void hig_workarea_add_wide_control(GtkWidget * table,
+ int *row, GtkWidget * w);
+
+GtkWidget *hig_workarea_add_wide_checkbutton(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ gboolean is_active);
+
+GtkWidget *hig_workarea_add_label(GtkWidget * table,
+ int row, const char *mnemonic_string);
+
+void hig_workarea_add_label_w(GtkWidget * table,
+ int row, GtkWidget * label_widget);
+
+void hig_workarea_add_control(GtkWidget * table,
+ int row, GtkWidget * control);
+
+GtkWidget *hig_workarea_add_tall_row(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control,
+ GtkWidget *
+ mnemonic_or_null_for_control);
+
+GtkWidget *hig_workarea_add_row(GtkWidget * table,
+ int *row,
+ const char *mnemonic_string,
+ GtkWidget * control,
+ GtkWidget * mnemonic_or_null_for_control);
+
+void hig_workarea_add_row_w(GtkWidget * table,
+ int *row,
+ GtkWidget * label,
+ GtkWidget * control,
+ GtkWidget * mnemonic_or_null_for_control);
+
+void hig_workarea_finish(GtkWidget * table, int *row);
+
+void hig_message_dialog_set_text(GtkMessageDialog * dialog,
+ const char *primary,
+ const char *secondary);
+
+enum {
+ GUI_PAD_SMALL = 3,
+ GUI_PAD = 6,
+ GUI_PAD_BIG = 12,
+ GUI_PAD_LARGE = 12
+};
+
+#endif /* __HIG_H__ */
diff --git a/src/http.c b/src/http.c
new file mode 100644
index 0000000..8239ac8
--- /dev/null
+++ b/src/http.c
@@ -0,0 +1,145 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-client.h"
+#include "http.h"
+
+static struct http_response *trg_http_perform_inner(trg_client * client,
+ gchar * req,
+ gboolean recurse);
+
+static size_t http_receive_callback(void *ptr, size_t size, size_t nmemb,
+ void *data);
+
+static size_t header_callback(void *ptr, size_t size, size_t nmemb,
+ void *data);
+
+void http_response_free(struct http_response *response)
+{
+ if (response->data != NULL)
+ g_free(response->data);
+
+ g_free(response);
+}
+
+static struct http_response *trg_http_perform_inner(trg_client * tc,
+ gchar * req,
+ gboolean recurse)
+{
+ CURL *handle;
+ long httpCode;
+ struct http_response *response;
+ struct curl_slist *headers = NULL;
+
+ response = g_new(struct http_response, 1);
+ response->size = 0;
+ response->status = -1;
+ response->data = NULL;
+
+ handle = curl_easy_init();
+
+ curl_easy_setopt(handle, CURLOPT_USERAGENT, "trg");
+ curl_easy_setopt(handle, CURLOPT_PASSWORD, tc->password);
+ curl_easy_setopt(handle, CURLOPT_USERNAME, tc->username);
+ curl_easy_setopt(handle, CURLOPT_URL, tc->url);
+ curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION,
+ &http_receive_callback);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *) response);
+ curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &header_callback);
+ curl_easy_setopt(handle, CURLOPT_WRITEHEADER, (void *) tc);
+ curl_easy_setopt(handle, CURLOPT_POSTFIELDS, req);
+ /*curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
+
+ if (tc->session_id != NULL) {
+ headers = curl_slist_append(headers, tc->session_id);
+ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
+ }
+
+ response->status = curl_easy_perform(handle);
+
+ curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &httpCode);
+ curl_easy_cleanup(handle);
+
+ if (headers != NULL)
+ curl_slist_free_all(headers);
+
+ if (response->status == CURLE_OK) {
+ if (httpCode == HTTP_CONFLICT && recurse == TRUE) {
+ http_response_free(response);
+ return trg_http_perform_inner(tc, req, FALSE);
+ } else if (httpCode != HTTP_OK) {
+ response->status = (-httpCode) - 100;
+ }
+ }
+
+ return response;
+}
+
+struct http_response *trg_http_perform(trg_client * tc, gchar * req)
+{
+ return trg_http_perform_inner(tc, req, TRUE);
+}
+
+static size_t
+http_receive_callback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ size_t realsize = size * nmemb;
+ struct http_response *mem = (struct http_response *) data;
+
+ mem->data = realloc(mem->data, mem->size + realsize + 1);
+ if (mem->data) {
+ memcpy(&(mem->data[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->data[mem->size] = 0;
+ }
+ return realsize;
+}
+
+static size_t header_callback(void *ptr, size_t size, size_t nmemb,
+ void *data)
+{
+ char *header = (char *) (ptr);
+ trg_client *client = (trg_client *) data;
+
+ if (g_str_has_prefix(header, "X-Transmission-Session-Id: ") == TRUE) {
+ char *nl;
+
+ if (client->session_id != NULL)
+ g_free(client->session_id);
+
+ client->session_id = g_strdup(header);
+ nl = strrchr(client->session_id, '\r');
+ if (nl)
+ *nl = '\0';
+ }
+
+ return (nmemb * size);
+}
diff --git a/src/http.h b/src/http.h
new file mode 100644
index 0000000..cc7a30f
--- /dev/null
+++ b/src/http.h
@@ -0,0 +1,43 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef HTTP_H_
+#define HTTP_H_
+
+#include <stdlib.h>
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+#include <curl/curl.h>
+
+#include "trg-client.h"
+
+#define HTTP_OK 200
+#define HTTP_CONFLICT 409
+
+struct http_response {
+ int status;
+ char *data;
+ int size;
+};
+
+void http_response_free(struct http_response *response);
+struct http_response *trg_http_perform(trg_client * client, gchar * req);
+
+#endif /* HTTP_H_ */
diff --git a/src/json.c b/src/json.c
new file mode 100644
index 0000000..fab44a8
--- /dev/null
+++ b/src/json.c
@@ -0,0 +1,93 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <glib/gprintf.h>
+#include <json-glib/json-glib.h>
+#include <gtk/gtk.h>
+
+#include "protocol-constants.h"
+#include "requests.h"
+#include "dispatch.h"
+#include "http.h"
+#include "json.h"
+
+gchar *trg_serialize(JsonNode * req)
+{
+ JsonGenerator *generator;
+ gsize length;
+ gchar *response;
+
+ generator = json_generator_new();
+ json_generator_set_root(generator, req);
+
+ response = json_generator_to_data(generator, &length);
+ g_object_unref(generator);
+
+ return response;
+}
+
+JsonObject *trg_deserialize(struct http_response * response,
+ GError ** error)
+{
+ JsonParser *parser;
+ JsonNode *root;
+ JsonObject *ret = NULL;
+
+ parser = json_parser_new();
+ json_parser_load_from_data(parser, response->data, response->size,
+ error);
+ if (*error == NULL) {
+ root = json_parser_get_root(parser);
+ if (g_getenv("SHOW_INCOMING") != NULL) {
+ g_printf("incoming JSON data:\n%s\n", response->data);
+ } else if (g_getenv("SHOW_INCOMING_PRETTY") != NULL) {
+ JsonGenerator *pg;
+ gsize len;
+ gchar *pgdata;
+
+ pg = json_generator_new();
+ g_object_set(pg, "pretty", TRUE, NULL);
+ json_generator_set_root(pg, root);
+
+ pgdata = json_generator_to_data(pg, &len);
+ g_printf("incoming JSON data:\n%s\n", pgdata);
+ g_free(pgdata);
+
+ g_object_unref(pg);
+ }
+
+ ret = json_node_get_object(root);
+ json_object_ref(ret);
+ }
+
+ g_object_unref(parser);
+ return ret;
+}
+
+JsonObject *node_get_arguments(JsonNode * req)
+{
+ JsonObject *rootObj = json_node_get_object(req);
+ return get_arguments(rootObj);
+}
+
+JsonObject *get_arguments(JsonObject * req)
+{
+ return json_object_get_object_member(req, PARAM_ARGUMENTS);
+}
diff --git a/src/json.h b/src/json.h
new file mode 100644
index 0000000..bd0dbd3
--- /dev/null
+++ b/src/json.h
@@ -0,0 +1,34 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef JSON_H_
+#define JSON_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "http.h"
+
+gchar *trg_serialize(JsonNode * req);
+JsonObject *trg_deserialize(struct http_response *response,
+ GError ** error);
+JsonObject *get_arguments(JsonObject * req);
+JsonObject *node_get_arguments(JsonNode * req);
+
+#endif /* JSON_H_ */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..cf7d54a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,135 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <curl/curl.h>
+#include <curl/types.h>
+#include <curl/easy.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#include <unique/unique.h>
+#include "http.h"
+#include "trg-main-window.h"
+#include "trg-client.h"
+
+enum {
+ COMMAND_0,
+ COMMAND_ADD
+};
+
+static UniqueResponse
+message_received_cb(UniqueApp * app,
+ gint command,
+ UniqueMessageData * message,
+ guint time_, gpointer user_data)
+{
+ TrgMainWindow *win;
+ UniqueResponse res;
+
+ win = TRG_MAIN_WINDOW(user_data);
+
+ switch (command) {
+ case UNIQUE_ACTIVATE:
+ gtk_window_set_screen(GTK_WINDOW(user_data),
+ unique_message_data_get_screen(message));
+ gtk_window_present_with_time(GTK_WINDOW(user_data), time_);
+ res = UNIQUE_RESPONSE_OK;
+ break;
+ case COMMAND_ADD:
+ res =
+ trg_add_from_filename(win,
+ unique_message_data_get_filename
+ (message)) ? UNIQUE_RESPONSE_OK :
+ UNIQUE_RESPONSE_FAIL;
+ break;
+ default:
+ res = UNIQUE_RESPONSE_OK;
+ break;
+ }
+
+ return res;
+}
+
+int main(int argc, char *argv[])
+{
+ int returnValue;
+ UniqueApp *app;
+ TrgMainWindow *window;
+ trg_client *client;
+
+ g_type_init();
+ g_thread_init(NULL);
+ gdk_threads_init();
+ gtk_init(&argc, &argv);
+
+ app = unique_app_new_with_commands("org.eth0.uk.org.trg", NULL,
+ "add", COMMAND_ADD, NULL);
+
+ if (unique_app_is_running(app)) {
+ UniqueCommand command;
+ UniqueResponse response;
+ UniqueMessageData *message;
+
+ if (argc > 1) {
+ command = COMMAND_ADD;
+ message = unique_message_data_new();
+ unique_message_data_set_filename(message, argv[1]);
+ } else {
+ command = UNIQUE_ACTIVATE;
+ message = NULL;
+ }
+
+ response = unique_app_send_message(app, command, message);
+ unique_message_data_free(message);
+
+ if (response == UNIQUE_RESPONSE_OK) {
+ returnValue = 0;
+ } else {
+ returnValue = 1;
+ }
+ } else {
+ client = trg_init_client();
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ window = trg_main_window_new(client);
+ unique_app_watch_window(app, GTK_WINDOW(window));
+
+ g_signal_connect(app, "message-received",
+ G_CALLBACK(message_received_cb), window);
+
+ gtk_widget_show_all(GTK_WIDGET(window));
+
+ auto_connect_if_required(window, client);
+ gtk_main();
+
+ curl_global_cleanup();
+ }
+
+ g_object_unref(app);
+ gdk_threads_leave();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/protocol-constants.h b/src/protocol-constants.h
new file mode 100644
index 0000000..462a612
--- /dev/null
+++ b/src/protocol-constants.h
@@ -0,0 +1,115 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PROTOCOL_CONSTANTS_H_
+#define PROTOCOL_CONSTANTS_H_
+
+/* generic contstants */
+
+#define PARAM_METHOD "method"
+#define FIELD_ID "id"
+
+/* torrents */
+
+#define FIELD_TORRENTS "torrents" /* parent node */
+#define FIELD_ANNOUNCE_URL "announceUrl"
+#define FIELD_LEFT_UNTIL_DONE "leftUntilDone"
+#define FIELD_TOTAL_SIZE "totalSize"
+#define FIELD_DONE_DATE "doneDate"
+#define FIELD_ADDED_DATE "addedDate"
+#define FIELD_TRACKERS "trackers"
+#define FIELD_DOWNLOAD_DIR "downloadDir"
+#define FIELD_HASH_STRING "hashString"
+#define FIELD_SWARM_SPEED "swarmSpeed"
+#define FIELD_ERROR_STRING "errorString"
+#define FIELD_NAME "name"
+#define FIELD_SIZEWHENDONE "sizeWhenDone"
+#define FIELD_STATUS "status"
+#define FIELD_RATEDOWNLOAD "rateDownload"
+#define FIELD_RATEUPLOAD "rateUpload"
+#define FIELD_ETA "eta"
+#define FIELD_UPLOADEDEVER "uploadedEver"
+#define FIELD_DOWNLOADEDEVER "downloadedEver"
+#define FIELD_HAVEVALID "haveValid"
+#define FIELD_HAVEUNCHECKED "haveUnchecked"
+#define FIELD_PERCENTDONE "percentDone"
+#define FIELD_TRACKERS "trackers"
+#define FIELD_PEERS "peers"
+#define FIELD_FILES "files"
+#define FIELD_WANTED "wanted"
+#define FIELD_PRIORITIES "priorities"
+#define FIELD_COMMENT "comment"
+#define FIELD_LEFTUNTILDONE "leftUntilDone"
+#define FIELD_ISFINISHED "isFinished"
+#define FIELD_ERRORSTR "errorString"
+#define FIELD_BANDWIDTH_PRIORITY "bandwidthPriority"
+#define FIELD_UPLOAD_LIMIT "uploadLimit"
+#define FIELD_UPLOAD_LIMITED "uploadLimited"
+#define FIELD_DOWNLOAD_LIMIT "downloadLimit"
+#define FIELD_DOWNLOAD_LIMITED "downloadLimited"
+#define FIELD_HONORS_SESSION_LIMITS "honorsSessionLimits"
+#define FIELD_SEED_RATIO_MODE "seedRatioMode"
+#define FIELD_SEED_RATIO_LIMIT "seedRatioLimit"
+#define FIELD_PEER_LIMIT "peer-limit"
+
+#define FIELD_FILES_WANTED "files-wanted"
+#define FIELD_FILES_UNWANTED "files-unwanted"
+#define FIELD_FILES_PRIORITY_HIGH "priority-high"
+#define FIELD_FILES_PRIORITY_NORMAL "priority-normal"
+#define FIELD_FILES_PRIORITY_LOW "priority-low"
+
+/* trackers */
+
+#define FIELD_TIER "tier"
+#define FIELD_ANNOUNCE "announce"
+#define FIELD_SCRAPE "scrape"
+
+/* methods */
+
+#define METHOD_TORRENT_START "torrent-start"
+#define METHOD_SESSION_GET "session-get"
+#define METHOD_SESSION_SET "session-set"
+#define METHOD_TORRENT_GET "torrent-get"
+#define METHOD_TORRENT_SET "torrent-set"
+#define METHOD_TORRENT_STOP "torrent-stop"
+#define METHOD_TORRENT_VERIFY "torrent-verify"
+#define METHOD_TORRENT_REMOVE "torrent-remove"
+#define METHOD_TORRENT_ADD "torrent-add"
+
+#define PARAM_IDS "ids"
+#define PARAM_DELETE_LOCAL_DATA "delete-local-data"
+#define PARAM_ARGUMENTS "arguments"
+#define PARAM_FIELDS "fields"
+#define PARAM_METAINFO "metainfo"
+#define PARAM_FILENAME "filename"
+#define PARAM_TAG "tag"
+
+enum {
+ STATUS_WAITING_TO_CHECK = 1,
+ STATUS_CHECKING = 2,
+ STATUS_DOWNLOADING = 4,
+ STATUS_SEEDING = 8,
+ STATUS_PAUSED = 16
+} TorrentState;
+
+#define TFILE_LENGTH "length"
+#define TFILE_BYTES_COMPLETED "bytesCompleted"
+#define TFILE_NAME "name"
+
+#endif /* PROTOCOL_CONSTANTS_H_ */
diff --git a/src/requests.c b/src/requests.c
new file mode 100644
index 0000000..7ee8782
--- /dev/null
+++ b/src/requests.c
@@ -0,0 +1,155 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "protocol-constants.h"
+#include "base64.h"
+#include "json.h"
+#include "requests.h"
+
+static JsonNode *base_request(gchar * method);
+
+JsonNode *generic_request(gchar * method, JsonArray * ids)
+{
+ JsonNode *root = base_request(method);
+
+ if (ids != NULL)
+ json_object_set_array_member(node_get_arguments(root),
+ PARAM_IDS, ids);
+
+ return root;
+}
+
+JsonNode *session_get()
+{
+ return generic_request(METHOD_SESSION_GET, NULL);
+}
+
+JsonNode *torrent_start(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_START, array);
+}
+
+JsonNode *torrent_pause(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_STOP, array);
+}
+
+JsonNode *torrent_verify(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_VERIFY, array);
+}
+
+JsonNode *session_set(void)
+{
+ return generic_request(METHOD_SESSION_SET, NULL);
+}
+
+JsonNode *torrent_set(JsonArray * array)
+{
+ return generic_request(METHOD_TORRENT_SET, array);
+}
+
+JsonNode *torrent_remove(JsonArray * array, gboolean removeData)
+{
+ JsonNode *root = base_request(METHOD_TORRENT_REMOVE);
+ JsonObject *args = node_get_arguments(root);
+ json_object_set_array_member(args, PARAM_IDS, array);
+ json_object_set_boolean_member(args, PARAM_DELETE_LOCAL_DATA,
+ removeData);
+ return root;
+}
+
+JsonNode *torrent_get()
+{
+ JsonNode *root = base_request(METHOD_TORRENT_GET);
+ JsonArray *fields = json_array_new();
+ json_array_add_string_element(fields, FIELD_ETA);
+ json_array_add_string_element(fields, FIELD_PEERS);
+ json_array_add_string_element(fields, FIELD_FILES);
+ json_array_add_string_element(fields, FIELD_HAVEVALID);
+ json_array_add_string_element(fields, FIELD_HAVEUNCHECKED);
+ json_array_add_string_element(fields, FIELD_RATEUPLOAD);
+ json_array_add_string_element(fields, FIELD_RATEDOWNLOAD);
+ json_array_add_string_element(fields, FIELD_STATUS);
+ json_array_add_string_element(fields, FIELD_UPLOADEDEVER);
+ json_array_add_string_element(fields, FIELD_SIZEWHENDONE);
+ json_array_add_string_element(fields, FIELD_ID);
+ json_array_add_string_element(fields, FIELD_NAME);
+ json_array_add_string_element(fields, FIELD_PERCENTDONE);
+ json_array_add_string_element(fields, FIELD_COMMENT);
+ json_array_add_string_element(fields, FIELD_ADDED_DATE);
+ json_array_add_string_element(fields, FIELD_TOTAL_SIZE);
+ json_array_add_string_element(fields, FIELD_LEFT_UNTIL_DONE);
+ json_array_add_string_element(fields, FIELD_ANNOUNCE_URL);
+ json_array_add_string_element(fields, FIELD_ERROR_STRING);
+ json_array_add_string_element(fields, FIELD_SWARM_SPEED);
+ json_array_add_string_element(fields, FIELD_TRACKERS);
+ json_array_add_string_element(fields, FIELD_DOWNLOAD_DIR);
+ json_array_add_string_element(fields, FIELD_HASH_STRING);
+ json_array_add_string_element(fields, FIELD_DONE_DATE);
+ json_array_add_string_element(fields, FIELD_HONORS_SESSION_LIMITS);
+ json_array_add_string_element(fields, FIELD_UPLOAD_LIMIT);
+ json_array_add_string_element(fields, FIELD_UPLOAD_LIMITED);
+ json_array_add_string_element(fields, FIELD_DOWNLOAD_LIMIT);
+ json_array_add_string_element(fields, FIELD_DOWNLOAD_LIMITED);
+ json_array_add_string_element(fields, FIELD_BANDWIDTH_PRIORITY);
+ json_array_add_string_element(fields, FIELD_SEED_RATIO_LIMIT);
+ json_array_add_string_element(fields, FIELD_SEED_RATIO_MODE);
+ json_array_add_string_element(fields, FIELD_PEER_LIMIT);
+ json_array_add_string_element(fields, FIELD_ERRORSTR);
+ json_array_add_string_element(fields, FIELD_WANTED);
+ json_array_add_string_element(fields, FIELD_PRIORITIES);
+ json_object_set_array_member(node_get_arguments(root),
+ PARAM_FIELDS, fields);
+ return root;
+}
+
+JsonNode *torrent_add_url(const gchar * url, gboolean paused)
+{
+ JsonNode *root = base_request(METHOD_TORRENT_ADD);
+ JsonObject *args = node_get_arguments(root);
+ json_object_set_string_member(args, PARAM_FILENAME, url);
+ return root;
+}
+
+JsonNode *torrent_add(gchar * filename, gboolean paused)
+{
+ JsonNode *root = base_request(METHOD_TORRENT_ADD);
+ JsonObject *args = node_get_arguments(root);
+ gchar *encodedFile = base64encode(filename);
+ json_object_set_string_member(args, PARAM_METAINFO, encodedFile);
+ g_free(encodedFile);
+ return root;
+}
+
+static JsonNode *base_request(gchar * method)
+{
+ JsonNode *root = json_node_new(JSON_NODE_OBJECT);
+ JsonObject *object = json_object_new();
+ JsonObject *args = json_object_new();
+ json_object_set_string_member(object, PARAM_METHOD, method);
+ json_object_set_object_member(object, PARAM_ARGUMENTS, args);
+ json_node_take_object(root, object);
+ return root;
+}
diff --git a/src/requests.h b/src/requests.h
new file mode 100644
index 0000000..85f0b26
--- /dev/null
+++ b/src/requests.h
@@ -0,0 +1,39 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef REQUESTS_H_
+#define REQUESTS_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+JsonNode *generic_request(gchar * method, JsonArray * array);
+
+JsonNode *session_set(void);
+JsonNode *session_get();
+JsonNode *torrent_get();
+JsonNode *torrent_set(JsonArray * array);
+JsonNode *torrent_pause(JsonArray * array);
+JsonNode *torrent_start(JsonArray * array);
+JsonNode *torrent_verify(JsonArray * array);
+JsonNode *torrent_remove(JsonArray * array, int removeData);
+JsonNode *torrent_add(gchar * filename, gboolean paused);
+JsonNode *torrent_add_url(const gchar * url, gboolean paused);
+
+#endif /* REQUESTS_H_ */
diff --git a/src/session-get.c b/src/session-get.c
new file mode 100644
index 0000000..18b0bf5
--- /dev/null
+++ b/src/session-get.c
@@ -0,0 +1,161 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+
+#include <json-glib/json-glib.h>
+
+#include "protocol-constants.h"
+#include "session-get.h"
+
+int session_get_version(JsonObject * s, float *version, int *revision)
+{
+ return sscanf(json_object_get_string_member(s, SGET_VERSION),
+ "%f (%d)", version, revision);
+}
+
+gboolean session_get_pex_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_PEX_ENABLED);
+}
+
+gboolean session_get_lpd_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_LPD_ENABLED);
+}
+
+const gchar *session_get_download_dir(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_DOWNLOAD_DIR);
+}
+
+gboolean session_get_peer_port_random(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_PEER_PORT_RANDOM_ON_START);
+}
+
+gint64 session_get_peer_port(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_PEER_PORT);
+}
+
+gboolean session_get_port_forwarding_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_PORT_FORWARDING_ENABLED);
+}
+
+const gchar *session_get_blocklist_url(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_BLOCKLIST_URL);
+}
+
+gboolean session_get_blocklist_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_BLOCKLIST_ENABLED);
+}
+
+gboolean session_get_rename_partial_files(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_RENAME_PARTIAL_FILES);
+}
+
+const gchar *session_get_encryption(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_ENCRYPTION);
+}
+
+const gchar *session_get_incomplete_dir(JsonObject * s)
+{
+ return json_object_get_string_member(s, SGET_INCOMPLETE_DIR);
+}
+
+gboolean session_get_incomplete_dir_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_INCOMPLETE_DIR_ENABLED);
+}
+
+gboolean session_get_seed_ratio_limited(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_SEED_RATIO_LIMITED);
+}
+
+const gchar *session_get_torrent_done_filename(JsonObject * s)
+{
+ return json_object_get_string_member(s,
+ SGET_SCRIPT_TORRENT_DONE_FILENAME);
+}
+
+gboolean session_get_torrent_done_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_SCRIPT_TORRENT_DONE_ENABLED);
+}
+
+gint64 session_get_cache_size_mb(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_CACHE_SIZE_MB);
+}
+
+gdouble session_get_seed_ratio_limit(JsonObject * s)
+{
+ return json_object_get_double_member(s, SGET_SEED_RATIO_LIMIT);
+}
+
+gboolean session_get_start_added_torrents(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_START_ADDED_TORRENTS);
+}
+
+gboolean session_get_trash_original_torrent_files(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_TRASH_ORIGINAL_TORRENT_FILES);
+}
+
+gboolean session_get_speed_limit_up_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s, SGET_SPEED_LIMIT_UP_ENABLED);
+}
+
+gint64 session_get_peer_limit_per_torrent(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_PEER_LIMIT_PER_TORRENT);
+}
+
+gint64 session_get_peer_limit_global(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_PEER_LIMIT_GLOBAL);
+}
+
+gint64 session_get_speed_limit_up(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_SPEED_LIMIT_UP);
+}
+
+gboolean session_get_speed_limit_down_enabled(JsonObject * s)
+{
+ return json_object_get_boolean_member(s,
+ SGET_SPEED_LIMIT_DOWN_ENABLED);
+}
+
+gint64 session_get_speed_limit_down(JsonObject * s)
+{
+ return json_object_get_int_member(s, SGET_SPEED_LIMIT_DOWN);
+}
diff --git a/src/session-get.h b/src/session-get.h
new file mode 100644
index 0000000..26e15e6
--- /dev/null
+++ b/src/session-get.h
@@ -0,0 +1,89 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef SESSION_GET_H_
+#define SESSION_GET_H_
+
+#include <glib-object.h>
+
+#define SGET_ALT_SPEED_DOWN "alt-speed-down"
+#define SGET_ALT_SPEED_ENABLED "alt-speed-enabled"
+#define SGET_ALT_SPEED_TIME_BEGIN "alt-speed-time-begin"
+#define SGET_ALT_SPEED_TIME_DAY "alt-speed-time-day"
+#define SGET_ALT_SPEED_TIME_END "alt-speed-time-end"
+#define SGET_ALT_SPEED_UP "alt-speed-up"
+#define SGET_BLOCKLIST_ENABLED "blocklist-enabled"
+#define SGET_BLOCKLIST_URL "blocklist-url"
+#define SGET_BLOCKLIST_SIZE "blocklist-size"
+#define SGET_DHT_ENABLED "dht-enabled"
+#define SGET_LPD_ENABLED "lpd-enabled"
+#define SGET_DOWNLOAD_DIR "download-dir"
+#define SGET_INCOMPLETE_DIR "incomplete-dir"
+#define SGET_INCOMPLETE_DIR_ENABLED "incomplete-dir-enabled"
+#define SGET_ENCRYPTION "encryption"
+#define SGET_PEER_LIMIT_GLOBAL "peer-limit-global"
+#define SGET_PEER_LIMIT_PER_TORRENT "peer-limit-per-torrent"
+#define SGET_PEER_PORT "peer-port"
+#define SGET_PEER_PORT_RANDOM_ON_START "peer-port-random-on-start"
+#define SGET_PEX_ENABLED "pex-enabled"
+#define SGET_PORT_FORWARDING_ENABLED "port-forwarding-enabled"
+#define SGET_RPC_VERSION "rpc-version"
+#define SGET_RPC_VERSION_MINIMUM "rpc-version-minimum"
+#define SGET_SEED_RATIO_LIMIT "seedRatioLimit"
+#define SGET_SEED_RATIO_LIMITED "seedRatioLimited"
+#define SGET_SPEED_LIMIT_DOWN "speed-limit-down"
+#define SGET_SPEED_LIMIT_DOWN_ENABLED "speed-limit-down-enabled"
+#define SGET_SPEED_LIMIT_UP "speed-limit-up"
+#define SGET_SPEED_LIMIT_UP_ENABLED "speed-limit-up-enabled"
+#define SGET_VERSION "version"
+#define SGET_TRASH_ORIGINAL_TORRENT_FILES "trash-original-torrent-files"
+#define SGET_START_ADDED_TORRENTS "start-added-torrents"
+#define SGET_RENAME_PARTIAL_FILES "rename-partial-files"
+#define SGET_CACHE_SIZE_MB "cache-size-mb"
+#define SGET_SCRIPT_TORRENT_DONE_FILENAME "script-torrent-done-filename"
+#define SGET_SCRIPT_TORRENT_DONE_ENABLED "script-torrent-done-enabled"
+
+const gchar *session_get_torrent_done_filename(JsonObject * s);
+gboolean session_get_torrent_done_enabled(JsonObject * s);
+gint64 session_get_cache_size_mb(JsonObject * s);
+int session_get_version(JsonObject * s, float *version, int *revision);
+gboolean session_get_pex_enabled(JsonObject * s);
+gboolean session_get_lpd_enabled(JsonObject * s);
+const gchar *session_get_download_dir(JsonObject * s);
+gboolean session_get_peer_port_random(JsonObject * s);
+gint64 session_get_peer_port(JsonObject * s);
+gint64 session_get_peer_limit_global(JsonObject * s);
+gint64 session_get_peer_limit_per_torrent(JsonObject * s);
+gboolean session_get_port_forwarding_enabled(JsonObject * s);
+const gchar *session_get_blocklist_url(JsonObject * s);
+gboolean session_get_blocklist_enabled(JsonObject * s);
+gboolean session_get_rename_partial_files(JsonObject * s);
+const gchar *session_get_encryption(JsonObject * s);
+const gchar *session_get_incomplete_dir(JsonObject * s);
+gboolean session_get_incomplete_dir_enabled(JsonObject * s);
+gboolean session_get_seed_ratio_limited(JsonObject * s);
+gdouble session_get_seed_ratio_limit(JsonObject * s);
+gboolean session_get_start_added_torrents(JsonObject * s);
+gboolean session_get_trash_original_torrent_files(JsonObject * s);
+gboolean session_get_speed_limit_up_enabled(JsonObject * s);
+gint64 session_get_speed_limit_up(JsonObject * s);
+gboolean session_get_speed_limit_down_enabled(JsonObject * s);
+gint64 session_get_speed_limit_down(JsonObject * s);
+
+#endif /* SESSION_GET_H_ */
diff --git a/src/tfile.c b/src/tfile.c
new file mode 100644
index 0000000..0c0a757
--- /dev/null
+++ b/src/tfile.c
@@ -0,0 +1,44 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <json-glib/json-glib.h>
+
+#include "protocol-constants.h"
+#include "tfile.h"
+
+gdouble file_get_progress(JsonObject * f)
+{
+ return ((gdouble) file_get_bytes_completed(f) /
+ (gdouble) file_get_length(f)) * 100.0;
+}
+
+gint64 file_get_length(JsonObject * f)
+{
+ return json_object_get_int_member(f, TFILE_LENGTH);
+}
+
+gint64 file_get_bytes_completed(JsonObject * f)
+{
+ return json_object_get_int_member(f, TFILE_BYTES_COMPLETED);
+}
+
+const gchar *file_get_name(JsonObject * f)
+{
+ return json_object_get_string_member(f, TFILE_NAME);
+}
diff --git a/src/tfile.h b/src/tfile.h
new file mode 100644
index 0000000..cf9fdbb
--- /dev/null
+++ b/src/tfile.h
@@ -0,0 +1,31 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TFILE_H_
+#define TFILE_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+gint64 file_get_length(JsonObject * f);
+gint64 file_get_bytes_completed(JsonObject * f);
+const gchar *file_get_name(JsonObject * f);
+gdouble file_get_progress(JsonObject * f);
+
+#endif /* TFILE_H_ */
diff --git a/src/torrent.c b/src/torrent.c
new file mode 100644
index 0000000..a49935d
--- /dev/null
+++ b/src/torrent.c
@@ -0,0 +1,234 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "protocol-constants.h"
+
+JsonArray *torrent_get_peers(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_PEERS);
+}
+
+JsonArray *torrent_get_wanted(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_WANTED);
+}
+
+JsonArray *torrent_get_priorities(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_PRIORITIES);
+}
+
+JsonArray *torrent_get_trackers(JsonObject * t)
+{
+ return json_object_get_array_member(t, FIELD_TRACKERS);
+}
+
+gint64 torrent_get_id(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_ID);
+}
+
+const gchar *torrent_get_name(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_NAME);
+}
+
+gboolean torrent_get_honors_session_limits(JsonObject * t)
+{
+ return json_object_get_boolean_member(t, FIELD_HONORS_SESSION_LIMITS);
+}
+
+gint64 torrent_get_bandwidth_priority(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_BANDWIDTH_PRIORITY);
+}
+
+gint64 torrent_get_upload_limit(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_UPLOAD_LIMIT);
+}
+
+gint64 torrent_get_peer_limit(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_PEER_LIMIT);
+}
+
+gboolean torrent_get_upload_limited(JsonObject * t)
+{
+ return json_object_get_boolean_member(t, FIELD_UPLOAD_LIMITED);
+}
+
+gint64 torrent_get_seed_ratio_mode(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_SEED_RATIO_MODE);
+}
+
+gdouble torrent_get_seed_ratio_limit(JsonObject * t)
+{
+ return json_object_get_double_member(t, FIELD_SEED_RATIO_LIMIT);
+}
+
+gint64 torrent_get_download_limit(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_DOWNLOAD_LIMIT);
+}
+
+gboolean torrent_get_download_limited(JsonObject * t)
+{
+ return json_object_get_boolean_member(t, FIELD_DOWNLOAD_LIMITED);
+}
+
+gint64 torrent_get_size(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_SIZEWHENDONE);
+}
+
+gint64 torrent_get_rate_down(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_RATEDOWNLOAD);
+}
+
+gint64 torrent_get_rate_up(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_RATEUPLOAD);
+}
+
+gint64 torrent_get_eta(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_ETA);
+}
+
+gint64 torrent_get_uploaded(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_UPLOADEDEVER);
+}
+
+gint64 torrent_get_have_valid(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_HAVEVALID);
+}
+
+gint64 torrent_get_have_unchecked(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_HAVEUNCHECKED);
+}
+
+gint64 torrent_get_downloaded(JsonObject * t)
+{
+ return torrent_get_have_valid(t) + torrent_get_have_unchecked(t);
+}
+
+gint64 torrent_get_status(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_STATUS);
+}
+
+gboolean torrent_get_is_finished(JsonObject * t)
+{
+ return torrent_get_left_until_done(t) <= 0;
+}
+
+gdouble torrent_get_percent_done(JsonObject * t)
+{
+ JsonNode *percentDone = json_object_get_member(t, FIELD_PERCENTDONE);
+ GValue a = { 0 };
+ json_node_get_value(percentDone, &a);
+ switch (G_VALUE_TYPE(&a)) {
+ case G_TYPE_INT64:
+ return (gdouble) g_value_get_int64(&a) * 100.0;
+ case G_TYPE_DOUBLE:
+ return g_value_get_double(&a) * 100.0;
+ default:
+ return 0.0;
+ }
+}
+
+gchar *torrent_get_status_icon(guint flags)
+{
+ if (flags & TORRENT_FLAG_ERROR)
+ return g_strdup(GTK_STOCK_DIALOG_WARNING);
+ else if (flags & TORRENT_FLAG_DOWNLOADING)
+ return g_strdup(GTK_STOCK_GO_DOWN);
+ else if (flags & TORRENT_FLAG_PAUSED)
+ return g_strdup(GTK_STOCK_MEDIA_PAUSE);
+ else if (flags & TORRENT_FLAG_SEEDING)
+ return g_strdup(GTK_STOCK_GO_UP);
+ else if (flags & TORRENT_FLAG_CHECKING)
+ return g_strdup(GTK_STOCK_REFRESH);
+ else
+ g_strdup(GTK_STOCK_DIALOG_QUESTION);
+}
+
+const gchar *torrent_get_errorstr(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_ERRORSTR);
+}
+
+gchar *torrent_get_status_string(gint64 value)
+{
+ switch (value) {
+ case STATUS_DOWNLOADING:
+ return g_strdup("Downloading");
+ case STATUS_PAUSED:
+ return g_strdup("Paused");
+ case STATUS_SEEDING:
+ return g_strdup("Seeding");
+ case STATUS_CHECKING:
+ return g_strdup("Checking");
+ case STATUS_WAITING_TO_CHECK:
+ return g_strdup("Waiting To Check");
+ default:
+ return g_strdup("Unknown");
+ }
+}
+
+gint64 tracker_get_tier(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_TIER);
+}
+
+gint64 torrent_get_left_until_done(JsonObject * t)
+{
+ return json_object_get_int_member(t, FIELD_LEFTUNTILDONE);
+}
+
+const gchar *tracker_get_announce(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_ANNOUNCE);
+}
+
+const gchar *tracker_get_scrape(JsonObject * t)
+{
+ return json_object_get_string_member(t, FIELD_SCRAPE);
+}
+
+JsonArray *get_torrents(JsonObject * response)
+{
+ return json_object_get_array_member(response, FIELD_TORRENTS);
+}
+
+JsonArray *torrent_get_files(JsonObject * args)
+{
+ return json_object_get_array_member(args, FIELD_FILES);
+}
diff --git a/src/torrent.h b/src/torrent.h
new file mode 100644
index 0000000..832adb1
--- /dev/null
+++ b/src/torrent.h
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TORRENT_H_
+#define TORRENT_H_
+
+#include <json-glib/json-glib.h>
+
+#define TORRENT_FLAG_ERROR (1 << 0) /* 0x01 */
+#define TORRENT_FLAG_COMPLETE (1 << 1) /* 0x02 */
+#define TORRENT_FLAG_INCOMPLETE (1 << 2) /* 0x02 */
+#define TORRENT_FLAG_SEEDING (1 << 3) /* 0x04 */
+#define TORRENT_FLAG_CHECKING (1 << 4) /* 0x08 */
+#define TORRENT_FLAG_WAITING_CHECK (1 << 5) /* 0x16 */
+#define TORRENT_FLAG_DOWNLOADING (1 << 6) /* 0x32 */
+#define TORRENT_FLAG_PAUSED (1 << 7) /* 0x64 */
+
+gint64 torrent_get_size(JsonObject * t);
+const gchar *torrent_get_name(JsonObject * t);
+gint64 torrent_get_rate_down(JsonObject * t);
+gint64 torrent_get_rate_up(JsonObject * t);
+gint64 torrent_get_eta(JsonObject * t);
+gint64 torrent_get_uploaded(JsonObject * t);
+gint64 torrent_get_downloaded(JsonObject * t);
+const gchar *torrent_get_errorstr(JsonObject * t);
+gint64 torrent_get_have_unchecked(JsonObject * t);
+gint64 torrent_get_have_valid(JsonObject * t);
+gint64 torrent_get_status(JsonObject * t);
+gchar *torrent_get_status_string(gint64 value);
+gchar *torrent_get_status_icon(guint flags);
+JsonArray *torrent_get_peers(JsonObject * t);
+JsonArray *torrent_get_trackers(JsonObject * t);
+JsonArray *torrent_get_wanted(JsonObject * t);
+JsonArray *torrent_get_priorities(JsonObject * t);
+gint64 torrent_get_id(JsonObject * t);
+gint64 tracker_get_tier(JsonObject * t);
+const gchar *tracker_get_announce(JsonObject * t);
+const gchar *tracker_get_scrape(JsonObject * t);
+JsonArray *torrent_get_files(JsonObject * args);
+gdouble torrent_get_percent_done(JsonObject * t);
+gint64 torrent_get_left_until_done(JsonObject * t);
+gboolean torrent_get_is_finished(JsonObject * t);
+gboolean torrent_get_honors_session_limits(JsonObject * t);
+gint64 torrent_get_bandwidth_priority(JsonObject * t);
+gint64 torrent_get_upload_limit(JsonObject * t);
+gboolean torrent_get_upload_limited(JsonObject * t);
+gint64 torrent_get_download_limit(JsonObject * t);
+gboolean torrent_get_download_limited(JsonObject * t);
+gdouble torrent_get_seed_ratio_limit(JsonObject * t);
+gint64 torrent_get_seed_ratio_mode(JsonObject * t);
+gint64 torrent_get_peer_limit(JsonObject * t);
+
+JsonArray *get_torrents(JsonObject * response);
+
+#endif /* TORRENT_H_ */
diff --git a/src/tpeer.c b/src/tpeer.c
new file mode 100644
index 0000000..37e63b2
--- /dev/null
+++ b/src/tpeer.c
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "tpeer.h"
+#include "protocol-constants.h"
+
+const gchar *peer_get_address(JsonObject * p)
+{
+ return json_object_get_string_member(p, TPEER_ADDRESS);
+}
+
+const gchar *peer_get_flagstr(JsonObject * p)
+{
+ return json_object_get_string_member(p, TPEER_FLAGSTR);
+}
+
+const gchar *peer_get_client_name(JsonObject * p)
+{
+ return json_object_get_string_member(p, TPEER_CLIENT_NAME);
+}
+
+gboolean peer_get_is_encrypted(JsonObject * p)
+{
+ return json_object_get_boolean_member(p, TPEER_IS_ENCRYPTED);
+}
+
+gboolean peer_get_is_uploading_to(JsonObject * p)
+{
+ return json_object_get_boolean_member(p, TPEER_IS_UPLOADING_TO);
+}
+
+gboolean peer_get_is_downloading_from(JsonObject * p)
+{
+ return json_object_get_boolean_member(p, TPEER_IS_DOWNLOADING_FROM);
+}
+
+gdouble peer_get_progress(JsonObject * p)
+{
+ return json_object_get_double_member(p, TPEER_PROGRESS) * 100.0;
+}
+
+gint64 peer_get_rate_to_client(JsonObject * p)
+{
+ return (gint64)
+ json_node_get_int(json_object_get_member(p, TPEER_RATE_TO_CLIENT));
+}
+
+gint64 peer_get_rate_to_peer(JsonObject * p)
+{
+ return (gint64)
+ json_node_get_int(json_object_get_member(p, TPEER_RATE_TO_PEER));
+}
diff --git a/src/tpeer.h b/src/tpeer.h
new file mode 100644
index 0000000..c9a0e02
--- /dev/null
+++ b/src/tpeer.h
@@ -0,0 +1,47 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TPEER_H_
+#define TPEER_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#define TPEER_ADDRESS "address"
+#define TPEER_CLIENT_NAME "client_name"
+#define TPEER_PROGRESS "progress"
+#define TPEER_RATE_TO_CLIENT "rateToClient"
+#define TPEER_RATE_TO_PEER "rateToPeer"
+#define TPEER_IS_ENCRYPTED "isEncrypted"
+#define TPEER_IS_DOWNLOADING_FROM "isDownloadingFrom"
+#define TPEER_IS_UPLOADING_TO "isUploadingTo"
+
+#define TPEER_FLAGSTR "flagStr"
+
+const gchar *peer_get_address(JsonObject * p);
+const gchar *peer_get_client_name(JsonObject * p);
+gboolean peer_get_is_encrypted(JsonObject * p);
+gdouble peer_get_progress(JsonObject * p);
+const gchar *peer_get_flagstr(JsonObject * p);
+gint64 peer_get_rate_to_client(JsonObject * p);
+gint64 peer_get_rate_to_peer(JsonObject * p);
+gboolean peer_get_is_uploading_to(JsonObject * p);
+gboolean peer_get_is_downloading_from(JsonObject * p);
+
+#endif /* TPEER_H_ */
diff --git a/src/transmission-remote-gtk.desktop b/src/transmission-remote-gtk.desktop
new file mode 100644
index 0000000..7d608e0
--- /dev/null
+++ b/src/transmission-remote-gtk.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=transmission-remote-gtk
+GenericName=Remote BitTorrent Client
+X-GNOME-FullName=Transmission BitTorrent Remote Control
+Comment=Remotely control Transmission BitTorrent client
+Exec=/usr/local/bin/transmission-remote-gtk %F
+Icon=transmission
+Terminal=false
+TryExec=transmission-remote-gtk
+Type=Application
+MimeType=application/x-bittorrent;
+Categories=Network;FileTransfer;P2P;GTK;
diff --git a/src/transmission-remote-gtk.desktop.in b/src/transmission-remote-gtk.desktop.in
new file mode 100644
index 0000000..3edeb63
--- /dev/null
+++ b/src/transmission-remote-gtk.desktop.in
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=transmission-remote-gtk
+GenericName=Remote BitTorrent Client
+X-GNOME-FullName=Transmission BitTorrent Remote Control
+Comment=Remotely control Transmission BitTorrent client
+Exec=@bindir@/transmission-remote-gtk %F
+Icon=transmission
+Terminal=false
+TryExec=transmission-remote-gtk
+Type=Application
+MimeType=application/x-bittorrent;
+Categories=Network;FileTransfer;P2P;GTK;
diff --git a/src/transmission-remote-gtk.schemas b/src/transmission-remote-gtk.schemas
new file mode 100644
index 0000000..11c8cdb
--- /dev/null
+++ b/src/transmission-remote-gtk.schemas
@@ -0,0 +1,109 @@
+<gconfschemafile>
+ <schemalist>
+
+ <schema>
+ <key>/schemas/apps/trg/auto-connect</key>
+ <applyto>/apps/trg/auto-connect</applyto>
+ <owner>trg</owner>
+ <type>bool</type>
+ <default>0</default>
+
+ <locale name="C">
+ <short>Auto connect</short>
+ <long>Auto connect</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/system-tray</key>
+ <applyto>/apps/trg/system-tray</applyto>
+ <owner>trg</owner>
+ <type>bool</type>
+ <default>0</default>
+
+ <locale name="C">
+ <short>Show system tray icon</short>
+ <long>Show system tray icon while app is running</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/window_height</key>
+ <applyto>/apps/trg/window_height</applyto>
+ <owner>trg</owner>
+ <type>int</type>
+ <default>600</default>
+
+ <locale name="C">
+ <short>Window height</short>
+ <long>Window height</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/window_width</key>
+ <applyto>/apps/trg/window_width</applyto>
+ <owner>trg</owner>
+ <type>int</type>
+ <default>800</default>
+
+ <locale name="C">
+ <short>Window width</short>
+ <long>Window width</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/port</key>
+ <applyto>/apps/trg/port</applyto>
+ <owner>trg</owner>
+ <type>int</type>
+ <default>9091</default>
+
+ <locale name="C">
+ <short>Port</short>
+ <long>Port</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/username</key>
+ <applyto>/apps/trg/username</applyto>
+ <owner>trg</owner>
+ <type>string</type>
+ <default></default>
+
+ <locale name="C">
+ <short>Username</short>
+ <long>Username</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/password</key>
+ <applyto>/apps/trg/password</applyto>
+ <owner>trg</owner>
+ <type>string</type>
+ <default></default>
+
+ <locale name="C">
+ <short>Password</short>
+ <long>Password</long>
+ </locale>
+ </schema>
+
+ <schema>
+ <key>/schemas/apps/trg/hostname</key>
+ <applyto>/apps/trg/hostname</applyto>
+ <owner>trg</owner>
+ <type>string</type>
+ <default></default>
+
+ <locale name="C">
+ <short>Hostname</short>
+ <long>Hostname</long>
+ </locale>
+ </schema>
+
+ </schemalist>
+</gconfschemafile>
diff --git a/src/trg-about-window.c b/src/trg-about-window.c
new file mode 100644
index 0000000..32e35ca
--- /dev/null
+++ b/src/trg-about-window.c
@@ -0,0 +1,82 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "dispatch.h"
+#include "trg-about-window.h"
+#include "util.h"
+
+GtkWidget *trg_about_window_new(GtkWindow * parent)
+{
+ GtkWidget *dialog;
+ GdkPixbuf *logo;
+ GError *error = NULL;
+ const gchar *trgAuthors[] = { "Alan Fitton <alan@eth0.org.uk>", NULL };
+
+ dialog = gtk_about_dialog_new();
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
+
+ logo =
+ gdk_pixbuf_new_from_file("/usr/share/pixmaps/transmission.png",
+ &error);
+ if (error == NULL)
+ gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), logo);
+ else {
+ if (error->domain == GDK_PIXBUF_ERROR)
+ g_print("GdkPixbufError: %s\n", error->message);
+ else if (error->domain == G_FILE_ERROR)
+ g_print("GFileError: %s\n", error->message);
+ else
+ g_print
+ ("An error in the domain: %d has occurred!\n",
+ error->domain);
+ g_error_free(error);
+ }
+
+ /* Set application data that will be displayed in the main dialog. */
+ gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), PACKAGE_NAME);
+ gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog),
+ PACKAGE_VERSION);
+ gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog),
+ "(C) 2009 Alan F");
+ gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog),
+ "A remote client to transmission-daemon.");
+ /* Set the license text, which is usually loaded from a file. Also, set the
+ * web site address and label. */
+ gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(dialog), "GPL2");
+ gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog),
+ "http://eth0.org.uk/");
+ gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(dialog),
+ "eth0.org.uk");
+
+ /* Set the application authors, documenters and translators. */
+ gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(dialog), trgAuthors);
+ /*gtk_about_dialog_set_documenters(GTK_ABOUT_DIALOG(dialog), documenters);
+ gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(dialog),
+ "Translator #1\nTranslator #2"); */
+ return dialog;
+}
diff --git a/src/trg-about-window.h b/src/trg-about-window.h
new file mode 100644
index 0000000..4b1dcbe
--- /dev/null
+++ b/src/trg-about-window.h
@@ -0,0 +1,27 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef ABOUT_WINDOW_H_
+#define ABOUT_WINDOW_H_
+
+#include <gtk/gtk.h>
+
+GtkWidget *trg_about_window_new(GtkWindow * parent);
+
+#endif /* ABOUT_WINDOW_H_ */
diff --git a/src/trg-cell-renderer-eta.c b/src/trg-cell-renderer-eta.c
new file mode 100644
index 0000000..93ef7d7
--- /dev/null
+++ b/src/trg-cell-renderer-eta.c
@@ -0,0 +1,116 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-eta.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_ETA_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererEta, trg_cell_renderer_eta,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_ETA_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEtaPrivate))
+typedef struct _TrgCellRendererEtaPrivate TrgCellRendererEtaPrivate;
+
+struct _TrgCellRendererEtaPrivate {
+ gdouble eta_value;
+};
+
+static void
+trg_cell_renderer_eta_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererEtaPrivate *priv =
+ TRG_CELL_RENDERER_ETA_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_ETA_VALUE:
+ g_value_set_int64(value, priv->eta_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_eta_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererEtaPrivate *priv =
+ TRG_CELL_RENDERER_ETA_GET_PRIVATE(object);
+
+ if (property_id == PROP_ETA_VALUE) {
+ priv->eta_value = g_value_get_int64(value);
+ if (priv->eta_value > 0) {
+ char etaString[32];
+ tr_strltime_short(etaString, priv->eta_value,
+ sizeof(etaString));
+ g_object_set(object, "text", etaString, NULL);
+ } else if (priv->eta_value == -2) {
+ g_object_set(object, "text", "∞", NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_eta_class_init(TrgCellRendererEtaClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_eta_get_property;
+ object_class->set_property = trg_cell_renderer_eta_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_ETA_VALUE,
+ g_param_spec_int64("eta-value",
+ "Eta Value",
+ "Eta Value",
+ INT64_MIN,
+ INT64_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererEtaPrivate));
+}
+
+static void trg_cell_renderer_eta_init(TrgCellRendererEta * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_eta_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new(TRG_TYPE_CELL_RENDERER_ETA, NULL));
+}
diff --git a/src/trg-cell-renderer-eta.h b/src/trg-cell-renderer-eta.h
new file mode 100644
index 0000000..62b0b4b
--- /dev/null
+++ b/src/trg-cell-renderer-eta.h
@@ -0,0 +1,51 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_CELL_RENDERER_ETA_H_
+#define TRG_CELL_RENDERER_ETA_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_ETA trg_cell_renderer_eta_get_type()
+#define TRG_CELL_RENDERER_ETA(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEta))
+#define TRG_CELL_RENDERER_ETA_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEtaClass))
+#define TRG_IS_CELL_RENDERER_ETA(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_ETA))
+#define TRG_IS_CELL_RENDERER_ETA_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_ETA))
+#define TRG_CELL_RENDERER_ETA_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_ETA, TrgCellRendererEtaClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererEta;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererEtaClass;
+
+GType trg_cell_renderer_eta_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_eta_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_ETA_H_ */
diff --git a/src/trg-cell-renderer-priority.c b/src/trg-cell-renderer-priority.c
new file mode 100644
index 0000000..549bab7
--- /dev/null
+++ b/src/trg-cell-renderer-priority.c
@@ -0,0 +1,115 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-priority.h"
+#include "trg-files-model.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_PRIORITY_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererPriority, trg_cell_renderer_priority,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_PRIORITY_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriorityPrivate))
+typedef struct _TrgCellRendererPriorityPrivate
+ TrgCellRendererPriorityPrivate;
+
+struct _TrgCellRendererPriorityPrivate {
+ gint64 priority_value;
+};
+
+static void
+trg_cell_renderer_priority_get_property(GObject * object,
+ guint property_id, GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererPriorityPrivate *priv =
+ TRG_CELL_RENDERER_PRIORITY_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_PRIORITY_VALUE:
+ g_value_set_int64(value, priv->priority_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_priority_set_property(GObject * object,
+ guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererPriorityPrivate *priv =
+ TRG_CELL_RENDERER_PRIORITY_GET_PRIVATE(object);
+
+ if (property_id == PROP_PRIORITY_VALUE) {
+ priv->priority_value = g_value_get_int64(value);
+ if (priv->priority_value == T_PRIORITY_LOW) {
+ g_object_set(object, "text", "Low", NULL);
+ } else if (priv->priority_value == T_PRIORITY_HIGH) {
+ g_object_set(object, "text", "High", NULL);
+ } else {
+ g_object_set(object, "text", "Normal", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_priority_class_init(TrgCellRendererPriorityClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_priority_get_property;
+ object_class->set_property = trg_cell_renderer_priority_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_PRIORITY_VALUE,
+ g_param_spec_int64
+ ("priority-value",
+ "Priority Value",
+ "Priority Value", T_PRIORITY_LOW,
+ T_PRIORITY_HIGH, T_PRIORITY_NORMAL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass,
+ sizeof(TrgCellRendererPriorityPrivate));
+}
+
+static void trg_cell_renderer_priority_init(TrgCellRendererPriority * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_priority_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_PRIORITY, NULL));
+}
diff --git a/src/trg-cell-renderer-priority.h b/src/trg-cell-renderer-priority.h
new file mode 100644
index 0000000..d6d39c2
--- /dev/null
+++ b/src/trg-cell-renderer-priority.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_PRIORITY_H_
+#define TRG_CELL_RENDERER_PRIORITY_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_PRIORITY trg_cell_renderer_priority_get_type()
+#define TRG_CELL_RENDERER_PRIORITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriority))
+#define TRG_CELL_RENDERER_PRIORITY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriorityClass))
+#define TRG_IS_CELL_RENDERER_PRIORITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_PRIORITY))
+#define TRG_IS_CELL_RENDERER_PRIORITY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_PRIORITY))
+#define TRG_CELL_RENDERER_PRIORITY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_PRIORITY, TrgCellRendererPriorityClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererPriority;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererPriorityClass;
+
+GType trg_cell_renderer_priority_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_priority_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_PRIORITY_H_ */
diff --git a/src/trg-cell-renderer-ratio.c b/src/trg-cell-renderer-ratio.c
new file mode 100644
index 0000000..f08b220
--- /dev/null
+++ b/src/trg-cell-renderer-ratio.c
@@ -0,0 +1,113 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <limits.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-ratio.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_RATIO_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererRatio, trg_cell_renderer_ratio,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_RATIO_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatioPrivate))
+typedef struct _TrgCellRendererRatioPrivate TrgCellRendererRatioPrivate;
+
+struct _TrgCellRendererRatioPrivate {
+ gdouble ratio_value;
+};
+
+static void
+trg_cell_renderer_ratio_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererRatioPrivate *priv =
+ TRG_CELL_RENDERER_RATIO_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_RATIO_VALUE:
+ g_value_set_double(value, priv->ratio_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_ratio_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererRatioPrivate *priv =
+ TRG_CELL_RENDERER_RATIO_GET_PRIVATE(object);
+ if (property_id == PROP_RATIO_VALUE) {
+ priv->ratio_value = g_value_get_double(value);
+ if (priv->ratio_value > 0) {
+ char ratioString[32];
+ trg_strlratio(ratioString, priv->ratio_value);
+ g_object_set(object, "text", ratioString, NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_ratio_class_init(TrgCellRendererRatioClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_ratio_get_property;
+ object_class->set_property = trg_cell_renderer_ratio_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_RATIO_VALUE,
+ g_param_spec_double("ratio-value",
+ "Ratio Value",
+ "Ratio Value",
+ 0,
+ DBL_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererRatioPrivate));
+}
+
+static void trg_cell_renderer_ratio_init(TrgCellRendererRatio * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_ratio_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_RATIO, NULL));
+}
diff --git a/src/trg-cell-renderer-ratio.h b/src/trg-cell-renderer-ratio.h
new file mode 100644
index 0000000..64c3e82
--- /dev/null
+++ b/src/trg-cell-renderer-ratio.h
@@ -0,0 +1,51 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_CELL_RENDERER_RATIO_H_
+#define TRG_CELL_RENDERER_RATIO_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_RATIO trg_cell_renderer_ratio_get_type()
+#define TRG_CELL_RENDERER_RATIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatio))
+#define TRG_CELL_RENDERER_RATIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatioClass))
+#define TRG_IS_CELL_RENDERER_RATIO(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_RATIO))
+#define TRG_IS_CELL_RENDERER_RATIO_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_RATIO))
+#define TRG_CELL_RENDERER_RATIO_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_RATIO, TrgCellRendererRatioClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererRatio;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererRatioClass;
+
+GType trg_cell_renderer_ratio_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_ratio_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_RATIO_H_ */
diff --git a/src/trg-cell-renderer-size.c b/src/trg-cell-renderer-size.c
new file mode 100644
index 0000000..758c3f5
--- /dev/null
+++ b/src/trg-cell-renderer-size.c
@@ -0,0 +1,112 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-size.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_SIZE_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererSize, trg_cell_renderer_size,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_SIZE_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSizePrivate))
+typedef struct _TrgCellRendererSizePrivate TrgCellRendererSizePrivate;
+
+struct _TrgCellRendererSizePrivate {
+ gint64 size_value;
+};
+
+static void
+trg_cell_renderer_size_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererSizePrivate *priv =
+ TRG_CELL_RENDERER_SIZE_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_SIZE_VALUE:
+ g_value_set_int64(value, priv->size_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_size_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererSizePrivate *priv =
+ TRG_CELL_RENDERER_SIZE_GET_PRIVATE(object);
+ if (property_id == PROP_SIZE_VALUE) {
+ priv->size_value = g_value_get_int64(value);
+ if (priv->size_value > 0) {
+ char sizeString[32];
+ trg_strlsize(sizeString, priv->size_value);
+ g_object_set(object, "text", sizeString, NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_size_class_init(TrgCellRendererSizeClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_size_get_property;
+ object_class->set_property = trg_cell_renderer_size_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_SIZE_VALUE,
+ g_param_spec_int64("size-value",
+ "Size Value",
+ "Size Value",
+ 0,
+ INT64_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererSizePrivate));
+}
+
+static void trg_cell_renderer_size_init(TrgCellRendererSize * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_size_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new(TRG_TYPE_CELL_RENDERER_SIZE, NULL));
+}
diff --git a/src/trg-cell-renderer-size.h b/src/trg-cell-renderer-size.h
new file mode 100644
index 0000000..5b901e3
--- /dev/null
+++ b/src/trg-cell-renderer-size.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_SIZE_H_
+#define TRG_CELL_RENDERER_SIZE_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_SIZE trg_cell_renderer_size_get_type()
+#define TRG_CELL_RENDERER_SIZE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSize))
+#define TRG_CELL_RENDERER_SIZE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSizeClass))
+#define TRG_IS_CELL_RENDERER_SIZE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_SIZE))
+#define TRG_IS_CELL_RENDERER_SIZE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_SIZE))
+#define TRG_CELL_RENDERER_SIZE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_SIZE, TrgCellRendererSizeClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererSize;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererSizeClass;
+
+GType trg_cell_renderer_size_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_size_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_SIZE_H_ */
diff --git a/src/trg-cell-renderer-speed.c b/src/trg-cell-renderer-speed.c
new file mode 100644
index 0000000..35ccc5f
--- /dev/null
+++ b/src/trg-cell-renderer-speed.c
@@ -0,0 +1,114 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-speed.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_SPEED_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererSpeed, trg_cell_renderer_speed,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_SPEED_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeedPrivate))
+typedef struct _TrgCellRendererSpeedPrivate TrgCellRendererSpeedPrivate;
+
+struct _TrgCellRendererSpeedPrivate {
+ gint64 speed_value;
+};
+
+static void
+trg_cell_renderer_speed_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererSpeedPrivate *priv =
+ TRG_CELL_RENDERER_SPEED_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_SPEED_VALUE:
+ g_value_set_int64(value, priv->speed_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_speed_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererSpeedPrivate *priv =
+ TRG_CELL_RENDERER_SPEED_GET_PRIVATE(object);
+ if (property_id == PROP_SPEED_VALUE) {
+ priv->speed_value = g_value_get_int64(value);
+ if (priv->speed_value > 0) {
+ char speedString[32];
+ trg_strlspeed(speedString,
+ priv->speed_value / KILOBYTE_FACTOR);
+ g_object_set(object, "text", speedString, NULL);
+ } else {
+ g_object_set(object, "text", "", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_speed_class_init(TrgCellRendererSpeedClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_speed_get_property;
+ object_class->set_property = trg_cell_renderer_speed_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_SPEED_VALUE,
+ g_param_spec_int64("speed-value",
+ "Speed Value",
+ "Speed Value",
+ 0,
+ INT64_MAX,
+ 0,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererSpeedPrivate));
+}
+
+static void trg_cell_renderer_speed_init(TrgCellRendererSpeed * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_speed_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_SPEED, NULL));
+}
diff --git a/src/trg-cell-renderer-speed.h b/src/trg-cell-renderer-speed.h
new file mode 100644
index 0000000..9a9bac6
--- /dev/null
+++ b/src/trg-cell-renderer-speed.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_SPEED_H_
+#define TRG_CELL_RENDERER_SPEED_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_SPEED trg_cell_renderer_speed_get_type()
+#define TRG_CELL_RENDERER_SPEED(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeed))
+#define TRG_CELL_RENDERER_SPEED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeedClass))
+#define TRG_IS_CELL_RENDERER_SPEED(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_SPEED))
+#define TRG_IS_CELL_RENDERER_SPEED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_SPEED))
+#define TRG_CELL_RENDERER_SPEED_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_SPEED, TrgCellRendererSpeedClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererSpeed;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererSpeedClass;
+
+GType trg_cell_renderer_speed_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_speed_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_SPEED_H_ */
diff --git a/src/trg-cell-renderer-wanted.c b/src/trg-cell-renderer-wanted.c
new file mode 100644
index 0000000..962dab1
--- /dev/null
+++ b/src/trg-cell-renderer-wanted.c
@@ -0,0 +1,105 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <gtk/gtk.h>
+
+#include "trg-cell-renderer-wanted.h"
+#include "util.h"
+
+enum {
+ PROP_0,
+ PROP_WANTED_VALUE
+};
+
+G_DEFINE_TYPE(TrgCellRendererWanted, trg_cell_renderer_wanted,
+ GTK_TYPE_CELL_RENDERER_TEXT)
+#define TRG_CELL_RENDERER_WANTED_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWantedPrivate))
+typedef struct _TrgCellRendererWantedPrivate TrgCellRendererWantedPrivate;
+
+struct _TrgCellRendererWantedPrivate {
+ gboolean wanted_value;
+};
+
+static void
+trg_cell_renderer_wanted_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgCellRendererWantedPrivate *priv =
+ TRG_CELL_RENDERER_WANTED_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_WANTED_VALUE:
+ g_value_set_boolean(value, priv->wanted_value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_wanted_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgCellRendererWantedPrivate *priv =
+ TRG_CELL_RENDERER_WANTED_GET_PRIVATE(object);
+ if (property_id == PROP_WANTED_VALUE) {
+ priv->wanted_value = g_value_get_boolean(value);
+ if (priv->wanted_value) {
+ g_object_set(object, "text", "Yes", NULL);
+ } else {
+ g_object_set(object, "text", "No", NULL);
+ }
+ } else {
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_cell_renderer_wanted_class_init(TrgCellRendererWantedClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = trg_cell_renderer_wanted_get_property;
+ object_class->set_property = trg_cell_renderer_wanted_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_WANTED_VALUE,
+ g_param_spec_boolean
+ ("wanted-value", "Wanted Value",
+ "Wanted Value", TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(klass, sizeof(TrgCellRendererWantedPrivate));
+}
+
+static void trg_cell_renderer_wanted_init(TrgCellRendererWanted * self)
+{
+}
+
+GtkCellRenderer *trg_cell_renderer_wanted_new(void)
+{
+ return
+ GTK_CELL_RENDERER(g_object_new
+ (TRG_TYPE_CELL_RENDERER_WANTED, NULL));
+}
diff --git a/src/trg-cell-renderer-wanted.h b/src/trg-cell-renderer-wanted.h
new file mode 100644
index 0000000..02920ac
--- /dev/null
+++ b/src/trg-cell-renderer-wanted.h
@@ -0,0 +1,52 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_CELL_RENDERER_WANTED_H_
+#define TRG_CELL_RENDERER_WANTED_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_CELL_RENDERER_WANTED trg_cell_renderer_wanted_get_type()
+#define TRG_CELL_RENDERER_WANTED(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWanted))
+#define TRG_CELL_RENDERER_WANTED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWantedClass))
+#define TRG_IS_CELL_RENDERER_WANTED(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_CELL_RENDERER_WANTED))
+#define TRG_IS_CELL_RENDERER_WANTED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_CELL_RENDERER_WANTED))
+#define TRG_CELL_RENDERER_WANTED_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_CELL_RENDERER_WANTED, TrgCellRendererWantedClass))
+ typedef struct {
+ GtkCellRendererText parent;
+} TrgCellRendererWanted;
+
+typedef struct {
+ GtkCellRendererTextClass parent_class;
+} TrgCellRendererWantedClass;
+
+GType trg_cell_renderer_wanted_get_type(void);
+
+GtkCellRenderer *trg_cell_renderer_wanted_new(void);
+
+G_END_DECLS
+#endif /* TRG_CELL_RENDERER_WANTED_H_ */
diff --git a/src/trg-client.c b/src/trg-client.c
new file mode 100644
index 0000000..76824b9
--- /dev/null
+++ b/src/trg-client.c
@@ -0,0 +1,80 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gconf/gconf-client.h>
+
+#include "trg-client.h"
+#include "trg-preferences.h"
+
+trg_client *trg_init_client()
+{
+ trg_client *client;
+
+ client = g_new0(trg_client, 1);
+ client->gconf = gconf_client_get_default();
+ client->updateMutex = g_mutex_new();
+
+ return client;
+}
+
+gboolean trg_client_populate_with_settings(trg_client * tc,
+ GConfClient * gconf)
+{
+ gint port;
+ gchar *host;
+ GError *error = NULL;
+
+ g_free(tc->url);
+ tc->url = NULL;
+
+ g_free(tc->username);
+ tc->username = NULL;
+
+ g_free(tc->password);
+ tc->password = NULL;
+
+ port = gconf_client_get_int(gconf, TRG_GCONF_KEY_PORT, &error);
+ if (error != NULL) {
+ g_error_free(error);
+ return FALSE;
+ }
+
+ if ((host =
+ gconf_client_get_string(gconf, TRG_GCONF_KEY_HOSTNAME,
+ NULL)) == NULL) {
+ return FALSE;
+ } else {
+ tc->url =
+ g_strdup_printf("http://%s:%d/transmission/rpc", host, port);
+ g_free(host);
+ }
+
+ if ((tc->username =
+ gconf_client_get_string(gconf, TRG_GCONF_KEY_USERNAME,
+ NULL)) == NULL)
+ return FALSE;
+
+ if ((tc->password =
+ gconf_client_get_string(gconf, TRG_GCONF_KEY_PASSWORD,
+ NULL)) == NULL)
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/trg-client.h b/src/trg-client.h
new file mode 100644
index 0000000..e75e979
--- /dev/null
+++ b/src/trg-client.h
@@ -0,0 +1,45 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_CLIENT_H_
+#define TRG_CLIENT_H_
+
+#include <json-glib/json-glib.h>
+#include <gconf/gconf-client.h>
+
+#include "session-get.h"
+
+typedef struct {
+ char *session_id;
+ gint failCount;
+ gint64 updateSerial;
+ JsonObject *session;
+ char *url;
+ char *username;
+ char *password;
+ JsonArray *torrents;
+ GConfClient *gconf;
+ GMutex *updateMutex;
+} trg_client;
+
+trg_client *trg_init_client();
+gboolean trg_client_populate_with_settings(trg_client * tc,
+ GConfClient * gconf);
+
+#endif /* TRG_CLIENT_H_ */
diff --git a/src/trg-files-model.c b/src/trg-files-model.c
new file mode 100644
index 0000000..16f58f9
--- /dev/null
+++ b/src/trg-files-model.c
@@ -0,0 +1,177 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-files-model.h"
+#include "torrent.h"
+#include "tfile.h"
+#include "util.h"
+
+#include "trg-files-model.h"
+
+G_DEFINE_TYPE(TrgFilesModel, trg_files_model, GTK_TYPE_LIST_STORE)
+#define TRG_FILES_MODEL_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_FILES_MODEL, TrgFilesModelPrivate))
+typedef struct _TrgFilesModelPrivate TrgFilesModelPrivate;
+
+struct _TrgFilesModelPrivate {
+ gint torrentId;
+ JsonArray *files;
+ JsonArray *wanted;
+ JsonArray *priorities;
+ gint64 updateBarrier;
+ gint64 currentSerial;
+};
+
+static void trg_files_model_iter_new(TrgFilesModel * model,
+ GtkTreeIter * iter, JsonObject * file,
+ int id)
+{
+ char size[32];
+
+ gtk_list_store_append(GTK_LIST_STORE(model), iter);
+
+ trg_strlsize(size, file_get_length(file));
+ gtk_list_store_set(GTK_LIST_STORE(model), iter,
+ FILESCOL_NAME, file_get_name(file),
+ FILESCOL_SIZE, size, FILESCOL_ID, id, -1);
+}
+
+void trg_files_model_set_update_barrier(TrgFilesModel * model,
+ gint64 serial)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ priv->updateBarrier = serial;
+}
+
+static void
+trg_files_model_iter_update(TrgFilesModel * model,
+ GtkTreeIter * filesIter, JsonObject * file,
+ JsonArray * wantedArray,
+ JsonArray * prioritiesArray, int id)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+
+ gboolean wanted = json_node_get_int(json_array_get_element
+ (wantedArray, id)) == 1;
+ gint64 priority =
+ json_node_get_int(json_array_get_element(prioritiesArray, id));
+ gdouble progress = file_get_progress(file);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), filesIter,
+ FILESCOL_PROGRESS, progress, -1);
+
+ if (priv->updateBarrier < 0
+ || priv->currentSerial >= priv->updateBarrier) {
+ gtk_list_store_set(GTK_LIST_STORE(model), filesIter,
+ FILESCOL_ICON,
+ wanted ? GTK_STOCK_FILE :
+ GTK_STOCK_STOP, FILESCOL_WANTED, wanted,
+ FILESCOL_PRIORITY, priority, -1);
+ }
+}
+
+static void trg_files_model_class_init(TrgFilesModelClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgFilesModelPrivate));
+}
+
+static void trg_files_model_init(TrgFilesModel * self)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(self);
+ GType column_types[FILESCOL_COLUMNS];
+
+ priv->updateBarrier = -1;
+
+ column_types[FILESCOL_ICON] = G_TYPE_STRING;
+ column_types[FILESCOL_NAME] = G_TYPE_STRING;
+ column_types[FILESCOL_SIZE] = G_TYPE_STRING;
+ column_types[FILESCOL_PROGRESS] = G_TYPE_DOUBLE;
+ column_types[FILESCOL_ID] = G_TYPE_INT;
+ column_types[FILESCOL_WANTED] = G_TYPE_BOOLEAN;
+ column_types[FILESCOL_PRIORITY] = G_TYPE_INT;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ FILESCOL_COLUMNS, column_types);
+}
+
+gboolean
+trg_files_model_update_foreach(GtkListStore * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ JsonObject *file;
+ gint id;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), iter, FILESCOL_ID, &id, -1);
+
+ file = json_node_get_object(json_array_get_element(priv->files, id));
+ trg_files_model_iter_update(TRG_FILES_MODEL(model), iter, file,
+ priv->wanted, priv->priorities, id);
+
+ return FALSE;
+}
+
+void
+trg_files_model_update(TrgFilesModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ int j;
+
+ if (first == TRUE)
+ gtk_list_store_clear(GTK_LIST_STORE(model));
+
+ priv->torrentId = torrent_get_id(t);
+ priv->priorities = torrent_get_priorities(t);
+ priv->wanted = torrent_get_wanted(t);
+ priv->files = torrent_get_files(t);
+ priv->currentSerial = updateSerial;
+
+ if (first == TRUE) {
+ for (j = 0; j < json_array_get_length(priv->files); j++) {
+ JsonObject *file =
+ json_node_get_object(json_array_get_element
+ (priv->files, j));
+ GtkTreeIter filesIter;
+ trg_files_model_iter_new(model, &filesIter, file, j);
+ trg_files_model_iter_update(model, &filesIter,
+ file, priv->wanted,
+ priv->priorities, j);
+ }
+ } else {
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ (GtkTreeModelForeachFunc)
+ trg_files_model_update_foreach, NULL);
+ }
+}
+
+gint trg_files_model_get_torrent_id(TrgFilesModel * model)
+{
+ TrgFilesModelPrivate *priv = TRG_FILES_MODEL_GET_PRIVATE(model);
+ return priv->torrentId;
+}
+
+TrgFilesModel *trg_files_model_new(void)
+{
+ return g_object_new(TRG_TYPE_FILES_MODEL, NULL);
+}
diff --git a/src/trg-files-model.h b/src/trg-files-model.h
new file mode 100644
index 0000000..658ffd3
--- /dev/null
+++ b/src/trg-files-model.h
@@ -0,0 +1,74 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_FILES_MODEL_H_
+#define TRG_FILES_MODEL_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_FILES_MODEL trg_files_model_get_type()
+#define TRG_FILES_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_FILES_MODEL, TrgFilesModel))
+#define TRG_FILES_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_FILES_MODEL, TrgFilesModelClass))
+#define TRG_IS_FILES_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_FILES_MODEL))
+#define TRG_IS_FILES_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_FILES_MODEL))
+#define TRG_FILES_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_FILES_MODEL, TrgFilesModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgFilesModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+} TrgFilesModelClass;
+
+GType trg_files_model_get_type(void);
+
+TrgFilesModel *trg_files_model_new(void);
+
+G_END_DECLS enum {
+ FILESCOL_ICON,
+ FILESCOL_NAME,
+ FILESCOL_SIZE,
+ FILESCOL_PROGRESS,
+ FILESCOL_ID,
+ FILESCOL_WANTED,
+ FILESCOL_PRIORITY,
+ FILESCOL_COLUMNS
+};
+
+#define T_PRIORITY_LOW -1
+#define T_PRIORITY_NORMAL 0
+#define T_PRIORITY_HIGH 1
+
+void
+trg_files_model_update(TrgFilesModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first);
+gint trg_files_model_get_torrent_id(TrgFilesModel * model);
+void trg_files_model_set_update_barrier(TrgFilesModel * model,
+ gint64 serial);
+
+#endif /* TRG_FILES_MODEL_H_ */
diff --git a/src/trg-files-tree-view.c b/src/trg-files-tree-view.c
new file mode 100644
index 0000000..5486a08
--- /dev/null
+++ b/src/trg-files-tree-view.c
@@ -0,0 +1,369 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+
+#include "trg-client.h"
+#include "trg-tree-view.h"
+#include "trg-files-tree-view.h"
+#include "trg-files-model.h"
+#include "trg-cell-renderer-wanted.h"
+#include "trg-cell-renderer-priority.h"
+#include "trg-main-window.h"
+#include "requests.h"
+#include "json.h"
+#include "dispatch.h"
+#include "protocol-constants.h"
+
+G_DEFINE_TYPE(TrgFilesTreeView, trg_files_tree_view, TRG_TYPE_TREE_VIEW)
+#define TRG_FILES_TREE_VIEW_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeViewPrivate))
+typedef struct _TrgFilesTreeViewPrivate TrgFilesTreeViewPrivate;
+
+struct _TrgFilesTreeViewPrivate {
+ trg_client *client;
+ TrgMainWindow *win;
+};
+
+static void trg_files_tree_view_class_init(TrgFilesTreeViewClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgFilesTreeViewPrivate));
+}
+
+static void set_unwanted_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_WANTED,
+ FALSE, FILESCOL_ICON, GTK_STOCK_CANCEL, -1);
+}
+
+static void set_wanted_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_WANTED,
+ TRUE, FILESCOL_ICON, GTK_STOCK_FILE, -1);
+}
+
+static void set_low_foreachfunc(GtkTreeModel * model, GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_PRIORITY,
+ T_PRIORITY_LOW, -1);
+}
+
+static void set_normal_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_PRIORITY,
+ T_PRIORITY_NORMAL, -1);
+}
+
+static void set_high_foreachfunc(GtkTreeModel * model, GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, FILESCOL_PRIORITY,
+ T_PRIORITY_HIGH, -1);
+}
+
+static void add_file_id_to_array(JsonObject * args, gchar * key,
+ gint index)
+{
+ JsonArray *array = json_object_get_array_member(args, key);
+ json_array_add_int_element(array, index);
+}
+
+static void send_updated_file_prefs_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ gpointer data)
+{
+ JsonObject *args = (JsonObject *) data;
+ gint priority, id;
+ gboolean wanted;
+
+ gtk_tree_model_get(model, iter, FILESCOL_WANTED, &wanted,
+ FILESCOL_PRIORITY, &priority, FILESCOL_ID, &id, -1);
+
+ if (wanted == FALSE)
+ add_file_id_to_array(args, FIELD_FILES_UNWANTED, id);
+ else
+ add_file_id_to_array(args, FIELD_FILES_WANTED, id);
+
+ if (priority == T_PRIORITY_LOW)
+ add_file_id_to_array(args, FIELD_FILES_PRIORITY_LOW, id);
+ else if (priority == T_PRIORITY_HIGH)
+ add_file_id_to_array(args, FIELD_FILES_PRIORITY_HIGH, id);
+ else
+ add_file_id_to_array(args, FIELD_FILES_PRIORITY_NORMAL, id);
+}
+
+static void remove_array_if_empty(JsonObject * args, gchar * key)
+{
+ JsonArray *array = json_object_get_array_member(args, key);
+ if (json_array_get_length(array) == 0)
+ json_object_remove_member(args, key);
+}
+
+static void send_updated_file_prefs(TrgFilesTreeView * tv)
+{
+ TrgFilesTreeViewPrivate *priv = TRG_FILES_TREE_VIEW_GET_PRIVATE(tv);
+ JsonNode *req;
+ JsonObject *args;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ gint targetId;
+ JsonArray *targetIdArray;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(tv));
+ targetId = trg_files_model_get_torrent_id(TRG_FILES_MODEL(model));
+ targetIdArray = json_array_new();
+ json_array_add_int_element(targetIdArray, targetId);
+
+ req = torrent_set(targetIdArray);
+ args = node_get_arguments(req);
+
+ json_object_set_array_member(args, FIELD_FILES_WANTED,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_UNWANTED,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_PRIORITY_HIGH,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_PRIORITY_NORMAL,
+ json_array_new());
+ json_object_set_array_member(args, FIELD_FILES_PRIORITY_LOW,
+ json_array_new());
+
+ gtk_tree_selection_selected_foreach(selection,
+ send_updated_file_prefs_foreachfunc,
+ args);
+
+ remove_array_if_empty(args, FIELD_FILES_WANTED);
+ remove_array_if_empty(args, FIELD_FILES_UNWANTED);
+ remove_array_if_empty(args, FIELD_FILES_PRIORITY_HIGH);
+ remove_array_if_empty(args, FIELD_FILES_PRIORITY_NORMAL);
+ remove_array_if_empty(args, FIELD_FILES_PRIORITY_LOW);
+
+ trg_files_model_set_update_barrier(TRG_FILES_MODEL(model),
+ priv->client->updateSerial + 1);
+ dispatch_async(priv->client, req, on_generic_interactive_action,
+ priv->win);
+}
+
+static void set_low(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection, set_low_foreachfunc,
+ NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_normal(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_normal_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_high(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_high_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_unwanted(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_unwanted_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void set_wanted(GtkWidget * w, gpointer data)
+{
+ TrgFilesTreeView *tv = TRG_FILES_TREE_VIEW(data);
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(data));
+ gtk_tree_selection_selected_foreach(selection,
+ set_wanted_foreachfunc, NULL);
+ send_updated_file_prefs(tv);
+}
+
+static void
+view_popup_menu(GtkWidget * treeview, GdkEventButton * event,
+ gpointer data)
+{
+ GtkWidget *menu, *menuitem;
+
+ menu = gtk_menu_new();
+
+ menuitem = gtk_menu_item_new_with_label("High Priority");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_high), treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ menuitem = gtk_menu_item_new_with_label("Normal Priority");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_normal),
+ treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ menuitem = gtk_menu_item_new_with_label("Low Priority");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_low), treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu),
+ gtk_separator_menu_item_new());
+
+ menuitem = gtk_image_menu_item_new_with_label(GTK_STOCK_FILE);
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(menuitem), TRUE);
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (menuitem), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(menuitem), "Download");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_wanted),
+ treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ menuitem = gtk_image_menu_item_new_with_label(GTK_STOCK_STOP);
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(menuitem), TRUE);
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (menuitem), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(menuitem), "Skip");
+ g_signal_connect(menuitem, "activate", G_CALLBACK(set_unwanted),
+ treeview);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
+
+ gtk_widget_show_all(menu);
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ (event != NULL) ? event->button : 0,
+ gdk_event_get_time((GdkEvent *) event));
+}
+
+static gboolean
+view_onButtonPressed(GtkWidget * treeview, GdkEventButton * event,
+ gpointer userdata)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
+ (gint) event->x,
+ (gint) event->y, &path,
+ NULL, NULL, NULL)) {
+ if (!gtk_tree_selection_path_is_selected(selection, path)) {
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_path(selection, path);
+ }
+ gtk_tree_path_free(path);
+
+ view_popup_menu(treeview, event, userdata);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean view_onPopupMenu(GtkWidget * treeview, gpointer userdata)
+{
+ view_popup_menu(treeview, NULL, userdata);
+ return TRUE;
+}
+
+static void trg_files_tree_view_add_priority_column(TrgTreeView * tv,
+ char *title, int index,
+ int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_priority_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "priority-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+static void trg_files_tree_view_add_wanted_column(TrgTreeView * tv,
+ char *title, int index,
+ int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_wanted_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "wanted-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+static void trg_files_tree_view_init(TrgFilesTreeView * self)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW(self),
+ FILESCOL_ICON, FILESCOL_NAME,
+ "Name", -1);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Size", FILESCOL_SIZE);
+ trg_tree_view_add_prog_column(TRG_TREE_VIEW(self), "Progress",
+ FILESCOL_PROGRESS, -1);
+ trg_files_tree_view_add_wanted_column(TRG_TREE_VIEW(self),
+ "Wanted", FILESCOL_WANTED, -1);
+ trg_files_tree_view_add_priority_column(TRG_TREE_VIEW(self),
+ "Priority",
+ FILESCOL_PRIORITY, -1);
+
+ g_signal_connect(self, "button-press-event",
+ G_CALLBACK(view_onButtonPressed), NULL);
+ g_signal_connect(self, "popup-menu", G_CALLBACK(view_onPopupMenu),
+ NULL);
+}
+
+TrgFilesTreeView *trg_files_tree_view_new(TrgFilesModel * model,
+ TrgMainWindow * win,
+ trg_client * client)
+{
+ GObject *obj = g_object_new(TRG_TYPE_FILES_TREE_VIEW, NULL);
+ TrgFilesTreeViewPrivate *priv = TRG_FILES_TREE_VIEW_GET_PRIVATE(obj);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), GTK_TREE_MODEL(model));
+ priv->client = client;
+ priv->win = win;
+
+ return TRG_FILES_TREE_VIEW(obj);
+}
diff --git a/src/trg-files-tree-view.h b/src/trg-files-tree-view.h
new file mode 100644
index 0000000..410ed4c
--- /dev/null
+++ b/src/trg-files-tree-view.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_FILES_TREE_VIEW_H_
+#define TRG_FILES_TREE_VIEW_H_
+
+#include <glib-object.h>
+
+#include "trg-main-window.h"
+#include "trg-client.h"
+#include "trg-files-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_FILES_TREE_VIEW trg_files_tree_view_get_type()
+#define TRG_FILES_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeView))
+#define TRG_FILES_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeViewClass))
+#define TRG_IS_FILES_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_FILES_TREE_VIEW))
+#define TRG_IS_FILES_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_FILES_TREE_VIEW))
+#define TRG_FILES_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_FILES_TREE_VIEW, TrgFilesTreeViewClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgFilesTreeView;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+} TrgFilesTreeViewClass;
+
+GType trg_files_tree_view_get_type(void);
+
+TrgFilesTreeView *trg_files_tree_view_new(TrgFilesModel * model,
+ TrgMainWindow * win,
+ trg_client * client);
+
+G_END_DECLS
+#endif /* TRG_FILES_TREE_VIEW_H_ */
diff --git a/src/trg-general-panel.c b/src/trg-general-panel.c
new file mode 100644
index 0000000..b4be30b
--- /dev/null
+++ b/src/trg-general-panel.c
@@ -0,0 +1,209 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "torrent.h"
+#include "util.h"
+#include "trg-general-panel.h"
+#include "trg-torrent-model.h"
+
+static void gtk_label_clear(GtkLabel * l);
+static GtkLabel *trg_general_panel_add_label(TrgGeneralPanel * fixed,
+ char *key, int col, int row);
+
+G_DEFINE_TYPE(TrgGeneralPanel, trg_general_panel, GTK_TYPE_FIXED)
+#define TRG_GENERAL_PANEL_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanelPrivate))
+typedef struct _TrgGeneralPanelPrivate TrgGeneralPanelPrivate;
+
+struct _TrgGeneralPanelPrivate {
+ GtkLabel *gen_name_label;
+ GtkLabel *gen_size_label;
+ GtkLabel *gen_completed_label;
+ GtkLabel *gen_seeders_label;
+ GtkLabel *gen_leechers_label;
+ GtkLabel *gen_status_label;
+ GtkLabel *gen_eta_label;
+ GtkLabel *gen_downloaded_label;
+ GtkLabel *gen_uploaded_label;
+ GtkLabel *gen_down_rate_label;
+ GtkLabel *gen_up_rate_label;
+ GtkLabel *gen_ratio_label;
+ GtkTreeModel *model;
+};
+
+void trg_general_panel_clear(TrgGeneralPanel * panel)
+{
+ TrgGeneralPanelPrivate *priv = TRG_GENERAL_PANEL_GET_PRIVATE(panel);
+
+ gtk_label_clear(priv->gen_name_label);
+ gtk_label_clear(priv->gen_size_label);
+ gtk_label_clear(priv->gen_completed_label);
+ gtk_label_clear(priv->gen_seeders_label);
+ gtk_label_clear(priv->gen_leechers_label);
+ gtk_label_clear(priv->gen_status_label);
+ gtk_label_clear(priv->gen_eta_label);
+ gtk_label_clear(priv->gen_downloaded_label);
+ gtk_label_clear(priv->gen_uploaded_label);
+ gtk_label_clear(priv->gen_down_rate_label);
+ gtk_label_clear(priv->gen_up_rate_label);
+ gtk_label_clear(priv->gen_ratio_label);
+}
+
+static void gtk_label_clear(GtkLabel * l)
+{
+ gtk_label_set_text(l, "");
+}
+
+static void trg_general_panel_class_init(TrgGeneralPanelClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgGeneralPanelPrivate));
+}
+
+void trg_general_panel_update(TrgGeneralPanel * panel, JsonObject * t,
+ GtkTreeIter * iter)
+{
+ TrgGeneralPanelPrivate *priv;
+ gchar buf[32];
+ gint sizeOfBuf;
+ gchar *statusString;
+ gint64 eta;
+ gint seeders, leechers;
+
+ priv = TRG_GENERAL_PANEL_GET_PRIVATE(panel);
+
+ sizeOfBuf = sizeof(buf);
+
+ tr_strlsize(buf, torrent_get_size(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_size_label), buf);
+
+ tr_strlspeed(buf, torrent_get_rate_down(t) / KILOBYTE_FACTOR,
+ sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_down_rate_label), buf);
+
+ tr_strlspeed(buf, torrent_get_rate_up(t) / KILOBYTE_FACTOR, sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_up_rate_label), buf);
+
+ tr_strlsize(buf, torrent_get_uploaded(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_uploaded_label), buf);
+
+ tr_strlsize(buf, torrent_get_downloaded(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_downloaded_label), buf);
+
+ tr_strlratio(buf,
+ (double) torrent_get_uploaded(t) /
+ (double) torrent_get_downloaded(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_ratio_label), buf);
+
+ statusString = torrent_get_status_string(torrent_get_status(t));
+ gtk_label_set_text(GTK_LABEL(priv->gen_status_label), statusString);
+ g_free(statusString);
+
+ tr_strlpercent(buf, torrent_get_percent_done(t), sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_completed_label), buf);
+
+ gtk_label_set_text(GTK_LABEL(priv->gen_name_label),
+ torrent_get_name(t));
+
+ if ((eta = torrent_get_eta(t)) > 0) {
+ tr_strltime_long(buf, eta, sizeOfBuf);
+ gtk_label_set_text(GTK_LABEL(priv->gen_eta_label), buf);
+ } else {
+ gtk_label_set_text(GTK_LABEL(priv->gen_eta_label), "N/A");
+ }
+
+ seeders = 0;
+ leechers = 0;
+ gtk_tree_model_get(GTK_TREE_MODEL(priv->model), iter,
+ TORRENT_COLUMN_SEEDS, &seeders,
+ TORRENT_COLUMN_LEECHERS, &leechers, -1);
+
+ snprintf(buf, sizeof(buf), "%d", seeders);
+ gtk_label_set_text(GTK_LABEL(priv->gen_seeders_label), buf);
+ snprintf(buf, sizeof(buf), "%d", leechers);
+ gtk_label_set_text(GTK_LABEL(priv->gen_leechers_label), buf);
+}
+
+static GtkLabel *trg_general_panel_add_label(TrgGeneralPanel * fixed,
+ char *key, int col, int row)
+{
+ GtkWidget *keyLabel;
+ GtkWidget *value;
+ gchar *keyMarkup;
+
+ keyLabel = gtk_label_new(NULL);
+ keyMarkup = g_markup_printf_escaped("<b>%s</b>", key);
+ gtk_label_set_markup(GTK_LABEL(keyLabel), keyMarkup);
+ g_free(keyMarkup);
+ gtk_fixed_put(GTK_FIXED(fixed), keyLabel, 10 + (col * 300),
+ 10 + (row * 24));
+
+ value = gtk_label_new(NULL);
+ gtk_label_set_selectable(GTK_LABEL(value), TRUE);
+ gtk_fixed_put(GTK_FIXED(fixed), value, 140 + (col * 320),
+ 10 + (row * 24));
+
+ return GTK_LABEL(value);
+}
+
+static void trg_general_panel_init(TrgGeneralPanel * self)
+{
+ TrgGeneralPanelPrivate *priv = TRG_GENERAL_PANEL_GET_PRIVATE(self);
+
+ priv->gen_name_label =
+ trg_general_panel_add_label(self, "Name:", 0, 0);
+ priv->gen_size_label =
+ trg_general_panel_add_label(self, "Size:", 0, 1);
+ priv->gen_completed_label =
+ trg_general_panel_add_label(self, "Completed:", 0, 2);
+ priv->gen_seeders_label =
+ trg_general_panel_add_label(self, "Seeders:", 0, 3);
+ priv->gen_leechers_label =
+ trg_general_panel_add_label(self, "Leechers:", 0, 4);
+ priv->gen_status_label =
+ trg_general_panel_add_label(self, "Status:", 0, 5);
+ priv->gen_eta_label = trg_general_panel_add_label(self, "ETA:", 1, 1);
+ priv->gen_downloaded_label =
+ trg_general_panel_add_label(self, "Downloaded:", 1, 2);
+ priv->gen_uploaded_label =
+ trg_general_panel_add_label(self, "Uploaded:", 1, 3);
+ priv->gen_down_rate_label =
+ trg_general_panel_add_label(self, "Rate Down:", 1, 4);
+ priv->gen_up_rate_label =
+ trg_general_panel_add_label(self, "Rate Up:", 1, 5);
+ priv->gen_ratio_label =
+ trg_general_panel_add_label(self, "Ratio:", 2, 3);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(self), FALSE);
+}
+
+TrgGeneralPanel *trg_general_panel_new(GtkTreeModel * model)
+{
+ GObject *obj;
+ TrgGeneralPanelPrivate *priv;
+
+ obj = g_object_new(TRG_TYPE_GENERAL_PANEL, NULL);
+
+ priv = TRG_GENERAL_PANEL_GET_PRIVATE(obj);
+ priv->model = model;
+
+ return TRG_GENERAL_PANEL(obj);
+}
diff --git a/src/trg-general-panel.h b/src/trg-general-panel.h
new file mode 100644
index 0000000..2765de9
--- /dev/null
+++ b/src/trg-general-panel.h
@@ -0,0 +1,59 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_GENERAL_PANEL_H_
+#define TRG_GENERAL_PANEL_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include <glib-object.h>
+
+#include "trg-torrent-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_GENERAL_PANEL trg_general_panel_get_type()
+#define TRG_GENERAL_PANEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanel))
+#define TRG_GENERAL_PANEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanelClass))
+#define TRG_IS_GENERAL_PANEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_GENERAL_PANEL))
+#define TRG_IS_GENERAL_PANEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_GENERAL_PANEL))
+#define TRG_GENERAL_PANEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_GENERAL_PANEL, TrgGeneralPanelClass))
+ typedef struct {
+ GtkFixed parent;
+} TrgGeneralPanel;
+
+typedef struct {
+ GtkFixedClass parent_class;
+} TrgGeneralPanelClass;
+
+GType trg_general_panel_get_type(void);
+
+TrgGeneralPanel *trg_general_panel_new(GtkTreeModel * model);
+
+G_END_DECLS
+ void trg_general_panel_update(TrgGeneralPanel * panel, JsonObject * t,
+ GtkTreeIter * iter);
+void trg_general_panel_clear(TrgGeneralPanel * panel);
+
+#endif /* TRG_GENERAL_PANEL_H_ */
diff --git a/src/trg-json-widgets.c b/src/trg-json-widgets.c
new file mode 100644
index 0000000..3b34d1e
--- /dev/null
+++ b/src/trg-json-widgets.c
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-json-widgets.h"
+
+void toggle_active_arg_is_sensitive(GtkToggleButton * b, gpointer data)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(data),
+ gtk_toggle_button_get_active(b));
+}
+
+void gtk_spin_button_json_int_out(GtkSpinButton * spin, JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(spin), JSON_OBJECT_KEY);
+ json_object_set_int_member(out, key, gtk_spin_button_get_value(spin));
+}
+
+void gtk_combo_box_json_string_output(GtkComboBox * c, JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(c), JSON_OBJECT_KEY);
+ gchar *value = g_object_get_data(G_OBJECT(c), JSON_OBJECT_VALUE);
+ json_object_set_string_member(out, key, value);
+}
+
+void gtk_spin_button_json_double_out(GtkSpinButton * spin,
+ JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(spin), JSON_OBJECT_KEY);
+ json_object_set_double_member(out, key,
+ gtk_spin_button_get_value(spin));
+}
+
+void gtk_entry_json_output(GtkEntry * e, JsonObject * out)
+{
+ gchar *key = g_object_get_data(G_OBJECT(e), JSON_OBJECT_KEY);
+ json_object_set_string_member(out, key, gtk_entry_get_text(e));
+}
+
+void widget_set_json_key(GtkWidget * w, gchar * key)
+{
+ g_object_set_data_full(G_OBJECT(w), JSON_OBJECT_KEY,
+ g_strdup(key), g_free);
+}
+
+gboolean gtk_toggle_button_json_out(GtkToggleButton * button,
+ JsonObject * out)
+{
+ gboolean active = gtk_toggle_button_get_active(button);
+ gchar *key = g_object_get_data(G_OBJECT(button), JSON_OBJECT_KEY);
+ json_object_set_boolean_member(out, key, active);
+ return active;
+}
diff --git a/src/trg-json-widgets.h b/src/trg-json-widgets.h
new file mode 100644
index 0000000..40b9b39
--- /dev/null
+++ b/src/trg-json-widgets.h
@@ -0,0 +1,40 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_JSON_WIDGETS_H_
+#define TRG_JSON_WIDGETS_H_
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#define JSON_OBJECT_KEY "json-object-key"
+#define JSON_OBJECT_VALUE "json-object-value"
+
+void widget_set_json_key(GtkWidget * w, gchar * key);
+
+void gtk_spin_button_json_int_out(GtkSpinButton * spin, JsonObject * out);
+void gtk_spin_button_json_double_out(GtkSpinButton * spin,
+ JsonObject * out);
+gboolean gtk_toggle_button_json_out(GtkToggleButton * button,
+ JsonObject * out);
+void gtk_entry_json_output(GtkEntry * e, JsonObject * out);
+void gtk_combo_box_json_string_output(GtkComboBox * c, JsonObject * out);
+void toggle_active_arg_is_sensitive(GtkToggleButton * b, gpointer data);
+
+#endif /* TRG_JSON_WIDGETS_H_ */
diff --git a/src/trg-main-window.c b/src/trg-main-window.c
new file mode 100644
index 0000000..caacd72
--- /dev/null
+++ b/src/trg-main-window.c
@@ -0,0 +1,1449 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#include <gdk/gdkkeysyms.h>
+#include <curl/curl.h>
+#include <libnotify/notify.h>
+
+#include "dispatch.h"
+#include "trg-client.h"
+#include "http.h"
+#include "json.h"
+
+#include "trg-main-window.h"
+#include "trg-about-window.h"
+#include "trg-tree-view.h"
+#include "trg-preferences.h"
+#include "trg-torrent-model.h"
+#include "trg-torrent-tree-view.h"
+#include "trg-peers-model.h"
+#include "trg-peers-tree-view.h"
+#include "trg-files-tree-view.h"
+#include "trg-files-model.h"
+#include "trg-trackers-tree-view.h"
+#include "trg-trackers-model.h"
+#include "trg-state-selector.h"
+#include "trg-torrent-props-dialog.h"
+#include "trg-torrent-add-url-dialog.h"
+#include "trg-toolbar.h"
+#include "trg-menu-bar.h"
+#include "trg-status-bar.h"
+#include "trg-remote-prefs-dialog.h"
+
+#include "util.h"
+#include "requests.h"
+#include "trg-preferences-dialog.h"
+#include "session-get.h"
+
+/* Events */
+
+static void on_torrent_get(JsonObject * response, int status,
+ gpointer data);
+
+static void on_torrent_get_first(JsonObject * response, int status,
+ gpointer data);
+
+static void on_torrent_get_multipurpose(JsonObject * response,
+ gboolean first, int status,
+ gpointer data);
+
+static void trg_main_window_conn_changed(TrgMainWindow * win,
+ gboolean connected);
+static void
+trg_main_window_update_notebook_displays(TrgMainWindow * win,
+ JsonObject * t,
+ GtkTreeIter * iter,
+ gboolean first);
+
+static gboolean torrent_selection_changed(GtkWidget * w, gpointer data);
+
+static gboolean trg_update_torrents_timerfunc(gpointer data);
+
+static void destroy_window();
+
+static void on_torrent_completed(TrgTorrentModel * model,
+ GtkTreeIter * iter, gpointer * data);
+
+static void on_session_get(JsonObject * response, int status,
+ gpointer data);
+
+static void entry_filter_changed_cb(GtkWidget * w, gpointer data);
+
+/* Actions */
+
+static void connect_cb(GtkWidget * w, gpointer data);
+static void disconnect_cb(GtkWidget * w, gpointer data);
+static void add_cb(GtkWidget * w, gpointer data);
+static void resume_cb(GtkWidget * w, gpointer data);
+static void pause_cb(GtkWidget * w, gpointer data);
+static void verify_cb(GtkWidget * w, gpointer data);
+static void remove_cb(GtkWidget * w, gpointer data);
+static void delete_cb(GtkWidget * w, gpointer data);
+static void open_about_cb(GtkWidget * w, GtkWindow * parent);
+
+/* Utility */
+
+static
+void trg_main_window_torrent_scrub(TrgMainWindow * win);
+static const gchar *make_error_message(JsonObject * response, int status);
+static GtkWidget *my_scrolledwin_new(GtkWidget * child);
+static gboolean
+trg_dialog_error_handler(TrgMainWindow * win, JsonObject * response,
+ int status);
+static void response_unref(JsonObject * response);
+
+G_DEFINE_TYPE(TrgMainWindow, trg_main_window, GTK_TYPE_WINDOW)
+#define TRG_MAIN_WINDOW_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_MAIN_WINDOW, TrgMainWindowPrivate))
+typedef struct _TrgMainWindowPrivate TrgMainWindowPrivate;
+
+struct _TrgMainWindowPrivate {
+ trg_client *client;
+ TrgToolbar *toolBar;
+ TrgMenuBar *menuBar;
+
+ TrgStatusBar *statusBar;
+ GtkStatusIcon *statusIcon;
+ TrgStateSelector *stateSelector;
+ TrgGeneralPanel *genDetails;
+ GtkWidget *notebook;
+
+ TrgTorrentModel *torrentModel;
+ TrgTorrentTreeView *torrentTreeView;
+ GtkTreeModel *filteredTorrentModel;
+ GtkTreeModel *sortedTorrentModel;
+ gint selectedTorrentId;
+
+ TrgTrackersModel *trackersModel;
+ TrgTrackersTreeView *trackersTreeView;
+
+ TrgFilesModel *filesModel;
+ TrgFilesTreeView *filesTreeView;
+
+ TrgPeersModel *peersModel;
+ TrgPeersTreeView *peersTreeView;
+
+ GtkWidget *hpaned, *vpaned;
+ GtkWidget *filterEntry, *filterEntryClearButton;
+};
+
+enum {
+ PROP_0,
+ PROP_CLIENT
+};
+
+static void trg_main_window_init(TrgMainWindow * self)
+{
+}
+
+static gboolean update_selected_torrent_notebook(TrgMainWindow * win,
+ gboolean first)
+{
+ TrgMainWindowPrivate *priv;
+ GtkTreeIter iter;
+ gint newFirstSelected;
+ JsonObject *json = NULL;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+ newFirstSelected =
+ get_first_selected(priv->torrentTreeView, &iter, &json);
+
+ if (priv->selectedTorrentId >= 0
+ && (priv->selectedTorrentId != newFirstSelected
+ || newFirstSelected < 0)) {
+ trg_main_window_torrent_scrub(win);
+ }
+
+ if ((priv->selectedTorrentId = newFirstSelected) >= 0) {
+ trg_main_window_update_notebook_displays(win, json, &iter, first);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void response_unref(JsonObject * response)
+{
+ if (response != NULL)
+ json_object_unref(response);
+}
+
+static void on_torrent_completed(TrgTorrentModel * model,
+ GtkTreeIter * iter, gpointer * data)
+{
+ TrgMainWindowPrivate *priv;
+ gchar *name;
+ NotifyNotification *notify;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ if (!gtk_status_icon_is_embedded(priv->statusIcon))
+ return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), iter,
+ TORRENT_COLUMN_NAME, &name, -1);
+
+ notify =
+ notify_notification_new(name,
+ "This torrent has finished downloading",
+ GTK_STOCK_APPLY, NULL);
+
+ notify_notification_attach_to_status_icon(notify, priv->statusIcon);
+ notify_notification_set_urgency(notify, NOTIFY_URGENCY_LOW);
+ notify_notification_set_timeout(notify, 8000);
+
+ g_free(name);
+
+ notify_notification_show(notify, NULL);
+}
+
+static gboolean delete_event(GtkWidget * w, GdkEvent * event,
+ gpointer * data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(w);
+ int width, height;
+ gtk_window_get_size(GTK_WINDOW(w), &width, &height);
+ gconf_client_set_int(priv->client->gconf, TRG_GCONF_KEY_WINDOW_HEIGHT,
+ height, NULL);
+ gconf_client_set_int(priv->client->gconf, TRG_GCONF_KEY_WINDOW_WIDTH,
+ width, NULL);
+
+ return FALSE;
+}
+
+static void destroy_window(GtkWidget * w, gpointer data)
+{
+ gtk_main_quit();
+}
+
+static const gchar *make_error_message(JsonObject * response, int status)
+{
+ if (status == FAIL_JSON_DECODE) {
+ return g_strdup("JSON decoding error.");
+ } else if (status == FAIL_RESPONSE_UNSUCCESSFUL) {
+ const gchar *resultStr =
+ json_object_get_string_member(response, "result");
+ if (resultStr == NULL)
+ return g_strdup("Server responded, but with no result.");
+ else
+ return g_strdup(resultStr);
+ } else if (status <= -100) {
+ return g_strdup_printf("Request failed with HTTP code %d",
+ -(status + 100));
+ } else {
+ return g_strdup(curl_easy_strerror(status));
+ }
+}
+
+static void open_props_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ TrgTorrentPropsDialog *dialog =
+ trg_torrent_props_dialog_new(GTK_WINDOW(data),
+ priv->torrentTreeView,
+ priv->client);
+
+ gtk_widget_show_all(GTK_WIDGET(dialog));
+}
+
+/* Use synchronous dispatch() in our dedicated thread function.
+ * This means torrents are added in sequence, instead of dispatch_async()
+ * working concurrently for each upload.
+ */
+
+static gpointer add_files_threadfunc(gpointer data)
+{
+ JsonObject *response;
+ JsonNode *request;
+ struct add_torrent_threadfunc_args *args;
+ gint status;
+ GSList *li;
+
+ args = (struct add_torrent_threadfunc_args *) data;
+
+ for (li = args->list; li != NULL; li = g_slist_next(li)) {
+ request = torrent_add((gchar *) li->data, FALSE);
+ g_free(li->data);
+ response = dispatch(args->client, request, &status);
+ on_generic_interactive_action(response, status, args->cb_data);
+ }
+
+ g_slist_free(args->list);
+ g_free(args);
+
+ return NULL;
+}
+
+static void add_url_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindow *win = TRG_MAIN_WINDOW(data);
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ TrgTorrentAddUrlDialog *dlg =
+ trg_torrent_add_url_dialog_new(win, priv->client);
+ gtk_widget_show_all(GTK_WIDGET(dlg));
+}
+
+static void add_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ dialog = gtk_file_chooser_dialog_new("Open File",
+ GTK_WINDOW(data),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN,
+ GTK_RESPONSE_ACCEPT, NULL);
+ gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
+
+ filter = gtk_file_filter_new();
+ gtk_file_filter_add_pattern(filter, "*.torrent");
+ gtk_file_filter_set_name(filter, "BitTorrent Metadata");
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ GThread *thread;
+ GError *error = NULL;
+ struct add_torrent_threadfunc_args *args;
+
+ args = g_new(struct add_torrent_threadfunc_args, 1);
+ args->list =
+ gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+ args->cb_data = data;
+ args->client = priv->client;
+
+ thread =
+ g_thread_create(add_files_threadfunc, args, FALSE, &error);
+ if (error != NULL) {
+ g_printf("thread creation error: %s\n", error->message);
+ g_error_free(error);
+ g_free(args);
+ }
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void pause_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ dispatch_async(priv->client,
+ torrent_pause(build_json_id_array
+ (priv->torrentTreeView)),
+ on_generic_interactive_action, data);
+}
+
+gboolean trg_add_from_filename(TrgMainWindow * win, gchar * fileName)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ if (g_file_test(fileName, G_FILE_TEST_EXISTS) == TRUE) {
+ JsonNode *torrentAddReq = torrent_add(fileName, FALSE);
+ dispatch_async(priv->client, torrentAddReq,
+ on_generic_interactive_action, win);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void resume_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ dispatch_async(priv->client,
+ torrent_start(build_json_id_array
+ (priv->torrentTreeView)),
+ on_generic_interactive_action, data);
+}
+
+static void disconnect_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ trg_main_window_conn_changed(TRG_MAIN_WINDOW(data), FALSE);
+ trg_status_bar_push_connection_msg(priv->statusBar, "Disconnected.");
+}
+
+static void connect_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ if (trg_client_populate_with_settings
+ (priv->client, priv->client->gconf) == FALSE) {
+ GtkWidget *dialog;
+
+ trg_status_bar_push_connection_msg(priv->statusBar,
+ "Unable to get gconf settings.");
+
+ dialog =
+ gtk_message_dialog_new(GTK_WINDOW(data),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "Unable to retrieve connection settings from GConf. Schema not installed?");
+ gtk_dialog_run(GTK_DIALOG(dialog));
+
+ return;
+ }
+
+ trg_status_bar_push_connection_msg(priv->statusBar, "Connecting...");
+ dispatch_async(priv->client, session_get(), on_session_get, data);
+}
+
+static void open_local_prefs_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ GtkWidget *dlg = trg_preferences_dialog_get_instance(GTK_WINDOW(data),
+ priv->client->
+ gconf);
+ gtk_widget_show_all(dlg);
+}
+
+static void open_remote_prefs_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ TrgRemotePrefsDialog *dlg =
+ trg_remote_prefs_dialog_get_instance(TRG_MAIN_WINDOW(data),
+ priv->client);
+
+ gtk_widget_show_all(GTK_WIDGET(dlg));
+}
+
+static TrgToolbar *trg_main_window_toolbar_new(TrgMainWindow * win)
+{
+ GObject *b_connect, *b_disconnect, *b_add, *b_resume, *b_pause;
+ GObject *b_remove, *b_delete, *b_props, *b_local_prefs,
+ *b_remote_prefs;
+
+ TrgToolbar *toolBar = trg_toolbar_new();
+ g_object_get(toolBar,
+ "connect-button", &b_connect,
+ "disconnect-button", &b_disconnect,
+ "add-button", &b_add,
+ "resume-button", &b_resume,
+ "pause-button", &b_pause,
+ "delete-button", &b_delete,
+ "remove-button", &b_remove,
+ "props-button", &b_props,
+ "remote-prefs-button", &b_remote_prefs,
+ "local-prefs-button", &b_local_prefs, NULL);
+
+ g_signal_connect(b_connect, "clicked", G_CALLBACK(connect_cb), win);
+ g_signal_connect(b_disconnect, "clicked",
+ G_CALLBACK(disconnect_cb), win);
+ g_signal_connect(b_add, "clicked", G_CALLBACK(add_cb), win);
+ g_signal_connect(b_resume, "clicked", G_CALLBACK(resume_cb), win);
+ g_signal_connect(b_pause, "clicked", G_CALLBACK(pause_cb), win);
+ g_signal_connect(b_delete, "clicked", G_CALLBACK(delete_cb), win);
+ g_signal_connect(b_remove, "clicked", G_CALLBACK(remove_cb), win);
+ g_signal_connect(b_props, "clicked", G_CALLBACK(open_props_cb), win);
+ g_signal_connect(b_local_prefs, "clicked",
+ G_CALLBACK(open_local_prefs_cb), win);
+ g_signal_connect(b_remote_prefs, "clicked",
+ G_CALLBACK(open_remote_prefs_cb), win);
+
+ return toolBar;
+}
+
+static void verify_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ g_printf("verify_cb()\n");
+ dispatch_async(priv->client,
+ torrent_verify(build_json_id_array
+ (priv->torrentTreeView)),
+ on_generic_interactive_action, data);
+}
+
+static gint confirm_action_dialog(GtkWindow * win,
+ GtkTreeSelection * selection,
+ gchar * question_single,
+ gchar * question_multi,
+ gchar * action_stock)
+{
+ TrgMainWindowPrivate *priv;
+ gint selectCount;
+ gint response;
+ GtkWidget *dialog = NULL;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ selectCount = gtk_tree_selection_count_selected_rows(selection);
+
+ if (selectCount == 1) {
+ GList *list;
+ GList *firstNode;
+ GtkTreeIter firstIter;
+ gchar *name = NULL;
+
+ list = gtk_tree_selection_get_selected_rows(selection, NULL);
+ firstNode = g_list_first(list);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL
+ (priv->sortedTorrentModel),
+ &firstIter, firstNode->data);
+ gtk_tree_model_get(GTK_TREE_MODEL
+ (priv->sortedTorrentModel),
+ &firstIter, TORRENT_COLUMN_NAME, &name, -1);
+ g_list_free(list);
+
+ dialog =
+ gtk_message_dialog_new_with_markup(GTK_WINDOW(win),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ question_single, name);
+ g_free(name);
+ } else if (selectCount > 1) {
+ dialog =
+ gtk_message_dialog_new_with_markup(GTK_WINDOW(win),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ question_multi,
+ selectCount);
+
+ } else {
+ return 0;
+ }
+
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ action_stock, GTK_RESPONSE_ACCEPT, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CANCEL, -1);
+
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return response;
+}
+
+static void remove_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+ GtkTreeSelection *selection;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->torrentTreeView));
+
+ if (confirm_action_dialog
+ (GTK_WINDOW(data), selection,
+ "<big><b>Remove torrent \"%s\"?</b></big>",
+ "<big><b>Remove %d torrents?</b></big>",
+ GTK_STOCK_REMOVE) == GTK_RESPONSE_ACCEPT)
+ dispatch_async(priv->client,
+ torrent_remove(build_json_id_array
+ (priv->torrentTreeView),
+ FALSE),
+ on_generic_interactive_action, data);
+}
+
+static void delete_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv;
+ GtkTreeSelection *selection;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->torrentTreeView));
+
+ if (confirm_action_dialog
+ (GTK_WINDOW(data), selection,
+ "<big><b>Remove and delete torrent \"%s\"?</b></big>",
+ "<big><b>Remove and delete %d torrents?</b></big>",
+ GTK_STOCK_DELETE) == GTK_RESPONSE_ACCEPT)
+ dispatch_async(priv->client,
+ torrent_remove(build_json_id_array
+ (priv->torrentTreeView),
+ TRUE),
+ on_generic_interactive_action, data);
+}
+
+static
+GtkWidget *my_scrolledwin_new(GtkWidget * child)
+{
+ GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrolled_win), child);
+ return scrolled_win;
+}
+
+static void view_states_toggled_cb(GtkCheckMenuItem * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gtk_widget_set_visible(GTK_WIDGET(priv->stateSelector),
+ gtk_check_menu_item_get_active(w));
+}
+
+static void view_notebook_toggled_cb(GtkCheckMenuItem * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gtk_widget_set_visible(priv->notebook,
+ gtk_check_menu_item_get_active(w));
+}
+
+static
+GtkWidget *trg_main_window_notebook_new(TrgMainWindow * win)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ GtkWidget *notebook = gtk_notebook_new();
+
+ gtk_widget_set_size_request(notebook, -1, 200);
+
+ priv->genDetails = trg_general_panel_new(priv->sortedTorrentModel);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ GTK_WIDGET(priv->genDetails),
+ gtk_label_new("General"));
+
+ priv->trackersModel = trg_trackers_model_new();
+ priv->trackersTreeView =
+ trg_trackers_tree_view_new(priv->trackersModel);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->trackersTreeView)),
+ gtk_label_new("Trackers"));
+
+ priv->filesModel = trg_files_model_new();
+ priv->filesTreeView =
+ trg_files_tree_view_new(priv->filesModel, win, priv->client);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->filesTreeView)),
+ gtk_label_new("Files"));
+
+ priv->peersModel = trg_peers_model_new();
+ priv->peersTreeView = trg_peers_tree_view_new(priv->peersModel);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->peersTreeView)),
+ gtk_label_new("Peers"));
+
+ return notebook;
+}
+
+void on_session_set(JsonObject * response, int status, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ gdk_threads_enter();
+ trg_dialog_error_handler(TRG_MAIN_WINDOW(data), response, status);
+ gdk_threads_leave();
+
+ response_unref(response);
+
+ if (status == CURLE_OK || status == FAIL_RESPONSE_UNSUCCESSFUL)
+ dispatch_async(priv->client, session_get(), on_session_get, data);
+}
+
+static void on_session_get(JsonObject * response, int status,
+ gpointer data)
+{
+ TrgMainWindow *win = TRG_MAIN_WINDOW(data);
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ trg_client *client = priv->client;
+ JsonObject *newSession;
+
+ gdk_threads_enter();
+
+ if (trg_dialog_error_handler(win, response, status) == TRUE) {
+ response_unref(response);
+ gdk_threads_leave();
+ return;
+ }
+
+ newSession = get_arguments(response);
+
+ if (client->session != NULL) {
+ json_object_unref(client->session);
+ } else {
+ trg_status_bar_connect(priv->statusBar, newSession);
+ trg_main_window_conn_changed(win, TRUE);
+
+ dispatch_async(client, torrent_get(), on_torrent_get_first, data);
+ }
+
+ client->session = newSession;
+
+ gdk_threads_leave();
+ json_object_ref(newSession);
+ response_unref(response);
+}
+
+static void
+on_torrent_get_first(JsonObject * response, int status, gpointer data)
+{
+ on_torrent_get_multipurpose(response, TRUE, status, data);
+}
+
+static void on_torrent_get(JsonObject * response, int status,
+ gpointer data)
+{
+ on_torrent_get_multipurpose(response, FALSE, status, data);
+}
+
+static gboolean trg_update_torrents_timerfunc(gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ if (priv->client->session != NULL)
+ dispatch_async(priv->client, torrent_get(), on_torrent_get, data);
+
+ return FALSE;
+}
+
+static void
+trg_main_window_update_notebook_displays(TrgMainWindow * win,
+ JsonObject * t,
+ GtkTreeIter * iter,
+ gboolean first)
+{
+ TrgMainWindowPrivate *priv;
+ trg_client *client;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+ client = priv->client;
+
+ trg_general_panel_update(priv->genDetails, t, iter);
+
+ if (first == TRUE)
+ trg_trackers_model_update(priv->trackersModel, t);
+
+ trg_files_model_update(priv->filesModel, client->updateSerial,
+ t, first);
+ trg_peers_model_update(priv->peersModel, client->updateSerial,
+ t, first);
+}
+
+static
+void open_about_cb(GtkWidget * w, GtkWindow * parent)
+{
+ GtkWidget *aboutDialog = trg_about_window_new(parent);
+
+ gtk_dialog_run(GTK_DIALOG(aboutDialog));
+ gtk_widget_destroy(aboutDialog);
+}
+
+static gboolean
+trg_torrent_tree_view_visible_func(GtkTreeModel * model,
+ GtkTreeIter * iter, gpointer data)
+{
+ guint flags;
+ gchar *name = NULL;
+ gboolean visible = TRUE;
+
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+
+ guint32 criteria = trg_state_selector_get_flag(priv->stateSelector);
+
+ gtk_tree_model_get(model, iter,
+ TORRENT_COLUMN_FLAGS, &flags,
+ TORRENT_COLUMN_NAME, &name, -1);
+
+ if (criteria != 0 && !(flags & criteria)) {
+ visible = FALSE;
+ } else if (name != NULL) {
+ const gchar *filterText =
+ gtk_entry_get_text(GTK_ENTRY(priv->filterEntry));
+ if (strlen(filterText) > 0 && strstr(name, filterText) == NULL)
+ visible = FALSE;
+ }
+
+ g_free(name);
+
+ return visible;
+}
+
+static
+TrgTorrentTreeView *trg_main_window_torrent_tree_view_new(TrgMainWindow *
+ win,
+ GtkTreeModel *
+ model,
+ TrgStateSelector
+ * selector)
+{
+ TrgTorrentTreeView *torrentTreeView = trg_torrent_tree_view_new(model);
+
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(torrentTreeView));
+
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(torrent_selection_changed), win);
+
+ return torrentTreeView;
+}
+
+static gboolean
+trg_dialog_error_handler(TrgMainWindow * win, JsonObject * response,
+ int status)
+{
+ TrgMainWindowPrivate *priv;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ if (status != CURLE_OK) {
+ GtkWidget *dialog;
+ const gchar *msg;
+
+ msg = make_error_message(response, status);
+ trg_status_bar_push_connection_msg(priv->statusBar, msg);
+ dialog = gtk_message_dialog_new(GTK_WINDOW(win),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK, msg);
+ gtk_window_set_title(GTK_WINDOW(dialog), "Error");
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ g_free((gpointer) msg);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean torrent_selection_changed(GtkWidget * w, gpointer data)
+{
+ TrgMainWindow *win;
+ TrgMainWindowPrivate *priv;
+ gboolean isSelected;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ win = TRG_MAIN_WINDOW(data);
+
+ isSelected = update_selected_torrent_notebook(win, TRUE);
+
+ trg_toolbar_torrent_actions_sensitive(priv->toolBar, isSelected);
+ trg_menu_bar_torrent_actions_sensitive(priv->menuBar, isSelected);
+
+ return TRUE;
+}
+
+void
+on_generic_interactive_action(JsonObject * response, int status,
+ gpointer data)
+{
+ gdk_threads_enter();
+ trg_dialog_error_handler(TRG_MAIN_WINDOW(data), response, status);
+ gdk_threads_leave();
+
+ response_unref(response);
+}
+
+static
+void trg_main_window_torrent_scrub(TrgMainWindow * win)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+
+ gtk_list_store_clear(GTK_LIST_STORE(priv->filesModel));
+ gtk_list_store_clear(GTK_LIST_STORE(priv->trackersModel));
+ gtk_list_store_clear(GTK_LIST_STORE(priv->peersModel));
+ trg_general_panel_clear(priv->genDetails);
+}
+
+static void
+on_torrent_get_multipurpose(JsonObject * response, gboolean first,
+ int status, gpointer data)
+{
+ TrgTorrentModelClassUpdateStats stats;
+ TrgMainWindowPrivate *priv;
+ trg_client *client;
+
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ client = priv->client;
+
+ g_mutex_lock(client->updateMutex);
+ gdk_threads_enter();
+
+ /* Disconnected between request and response callback */
+ if (client->session == NULL) {
+ gdk_threads_leave();
+ g_mutex_unlock(client->updateMutex);
+ response_unref(response);
+ return;
+ }
+
+ if (status != CURLE_OK) {
+ client->failCount++;
+ if (client->failCount >= 3) {
+ trg_main_window_conn_changed(TRG_MAIN_WINDOW(data), FALSE);
+ trg_dialog_error_handler(TRG_MAIN_WINDOW(data),
+ response, status);
+ } else {
+ const gchar *msg;
+ gchar *statusBarMsg;
+
+ msg = make_error_message(response, status);
+ statusBarMsg =
+ g_strdup_printf("Request %d/%d failed: %s",
+ client->failCount, 3, msg);
+ trg_status_bar_push_connection_msg(priv->statusBar,
+ statusBarMsg);
+ g_free((gpointer) msg);
+ g_free(statusBarMsg);
+ g_timeout_add_seconds(3, trg_update_torrents_timerfunc, data);
+ }
+ gdk_threads_leave();
+ g_mutex_unlock(client->updateMutex);
+ response_unref(response);
+ return;
+ }
+
+ client->failCount = 0;
+ stats.downRateTotal = 0;
+ stats.upRateTotal = 0;
+ stats.seeding = 0;
+ stats.down = 0;
+ stats.paused = 0;
+
+ trg_torrent_model_update(priv->torrentModel, priv->client,
+ response, &stats, first);
+
+ trg_status_bar_update(priv->statusBar, &stats);
+
+ update_selected_torrent_notebook(TRG_MAIN_WINDOW(data), first);
+
+ g_timeout_add_seconds(3, trg_update_torrents_timerfunc, data);
+
+ client->updateSerial++;
+
+ gdk_threads_leave();
+ g_mutex_unlock(client->updateMutex);
+ response_unref(response);
+}
+
+static void entry_filter_changed_cb(GtkWidget * w, gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gboolean clearSensitive = gtk_entry_get_text_length(GTK_ENTRY(w)) > 0;
+
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER
+ (priv->filteredTorrentModel));
+
+#if GTK_CHECK_VERSION( 2,16,0 )
+ g_object_set(priv->filterEntryClearButton,
+ "secondary-icon-sensitive", clearSensitive, NULL);
+#else
+ gtk_widget_set_sensitive(priv->filterEntryClearButton, clearSensitive);
+#endif
+}
+
+static void torrent_state_selection_changed(TrgStateSelector * selector,
+ guint flag, gpointer data)
+{
+ gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(data));
+}
+
+static
+void trg_main_window_conn_changed(TrgMainWindow * win, gboolean connected)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(win);
+ trg_client *tc = priv->client;
+
+ trg_toolbar_connected_change(priv->toolBar, connected);
+ trg_menu_bar_connected_change(priv->menuBar, connected);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->torrentTreeView), connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->peersTreeView), connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->filesTreeView), connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->trackersTreeView),
+ connected);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->genDetails), connected);
+
+ if (connected == FALSE) {
+ json_object_unref(tc->session);
+ tc->session = NULL;
+
+ gtk_list_store_clear(GTK_LIST_STORE(priv->torrentModel));
+ trg_main_window_torrent_scrub(win);
+ }
+}
+
+static void
+trg_main_window_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_CLIENT:
+ g_value_set_pointer(value, priv->client);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_main_window_set_property(GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_CLIENT:
+ priv->client = g_value_get_pointer(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void quit_cb(GtkWidget * w, gpointer data)
+{
+ gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static TrgMenuBar *trg_main_window_menu_bar_new(TrgMainWindow * win)
+{
+ GObject *b_connect, *b_disconnect, *b_add, *b_resume, *b_pause,
+ *b_verify, *b_remove, *b_delete, *b_props, *b_local_prefs,
+ *b_remote_prefs, *b_about, *b_view_states, *b_view_notebook,
+ *b_add_url, *b_quit;
+ TrgMenuBar *menuBar;
+
+ menuBar = trg_menu_bar_new(win);
+ g_object_get(menuBar,
+ "connect-button", &b_connect,
+ "disconnect-button", &b_disconnect,
+ "add-button", &b_add,
+ "add-url-button", &b_add_url,
+ "resume-button", &b_resume,
+ "pause-button", &b_pause,
+ "delete-button", &b_delete,
+ "remove-button", &b_remove,
+ "verify-button", &b_verify,
+ "props-button", &b_props,
+ "remote-prefs-button", &b_remote_prefs,
+ "local-prefs-button", &b_local_prefs,
+ "view-notebook-button", &b_view_notebook,
+ "view-states-button", &b_view_states,
+ "about-button", &b_about, "quit-button", &b_quit, NULL);
+
+ g_signal_connect(b_connect, "activate", G_CALLBACK(connect_cb), win);
+ g_signal_connect(b_disconnect, "activate",
+ G_CALLBACK(disconnect_cb), win);
+ g_signal_connect(b_add, "activate", G_CALLBACK(add_cb), win);
+ g_signal_connect(b_add_url, "activate", G_CALLBACK(add_url_cb), win);
+ g_signal_connect(b_resume, "activate", G_CALLBACK(resume_cb), win);
+ g_signal_connect(b_pause, "activate", G_CALLBACK(pause_cb), win);
+ g_signal_connect(b_verify, "activate", G_CALLBACK(verify_cb), win);
+ g_signal_connect(b_delete, "activate", G_CALLBACK(delete_cb), win);
+ g_signal_connect(b_remove, "activate", G_CALLBACK(remove_cb), win);
+ g_signal_connect(b_about, "activate", G_CALLBACK(open_about_cb), win);
+ g_signal_connect(b_local_prefs, "activate",
+ G_CALLBACK(open_local_prefs_cb), win);
+ g_signal_connect(b_remote_prefs, "activate",
+ G_CALLBACK(open_remote_prefs_cb), win);
+ g_signal_connect(b_view_notebook, "toggled",
+ G_CALLBACK(view_notebook_toggled_cb), win);
+ g_signal_connect(b_view_states, "toggled",
+ G_CALLBACK(view_states_toggled_cb), win);
+ g_signal_connect(b_props, "activate", G_CALLBACK(open_props_cb), win);
+ g_signal_connect(b_quit, "activate", G_CALLBACK(quit_cb), win);
+
+ return menuBar;
+}
+
+static void status_icon_activated(GtkStatusIcon * icon, gpointer data)
+{
+ gtk_window_present(GTK_WINDOW(data));
+}
+
+static void clear_filter_entry_cb(GtkWidget * w, gpointer data)
+{
+ gtk_entry_set_text(GTK_ENTRY(w), "");
+}
+
+static gboolean torrent_tv_key_press_event(GtkWidget * w,
+ GdkEventKey * key,
+ gpointer data)
+{
+ if (key->keyval == GDK_KEY_Delete) {
+ if (key->state & GDK_SHIFT_MASK)
+ delete_cb(w, data);
+ else
+ remove_cb(w, data);
+ }
+ return FALSE;
+}
+
+static
+GtkWidget *trg_imagemenuitem_new(GtkMenuShell * shell, char *text,
+ char *stock_id, gboolean sensitive,
+ GCallback cb, gpointer cbdata)
+{
+ GtkWidget *item = gtk_image_menu_item_new_with_label(stock_id);
+
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(item), TRUE);
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (item), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(item), text);
+ g_signal_connect(item, "activate", cb, cbdata);
+ gtk_widget_set_sensitive(item, sensitive);
+ gtk_menu_shell_append(shell, item);
+
+ return item;
+}
+
+static void
+trg_torrent_tv_view_menu(GtkWidget * treeview, GdkEventButton * event,
+ gpointer data)
+{
+ GtkWidget *menu;
+
+ menu = gtk_menu_new();
+
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Properties",
+ GTK_STOCK_PROPERTIES, TRUE,
+ G_CALLBACK(open_props_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Resume",
+ GTK_STOCK_MEDIA_PLAY, TRUE,
+ G_CALLBACK(resume_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Pause",
+ GTK_STOCK_MEDIA_PAUSE, TRUE,
+ G_CALLBACK(pause_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Verify",
+ GTK_STOCK_REFRESH, TRUE, G_CALLBACK(verify_cb),
+ data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Remove", GTK_STOCK_REMOVE,
+ TRUE, G_CALLBACK(remove_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Remove & Delete",
+ GTK_STOCK_DELETE, TRUE, G_CALLBACK(delete_cb),
+ data);
+
+ gtk_widget_show_all(menu);
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ (event != NULL) ? event->button : 0,
+ gdk_event_get_time((GdkEvent *) event));
+}
+
+static void
+trg_status_icon_view_menu(GtkStatusIcon * icon, GdkEventButton * event,
+ gpointer data)
+{
+ TrgMainWindowPrivate *priv = TRG_MAIN_WINDOW_GET_PRIVATE(data);
+ gboolean connected = priv->client->session != NULL;
+ GtkWidget *menu;
+
+ menu = gtk_menu_new();
+
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Connect",
+ GTK_STOCK_CONNECT, !connected,
+ G_CALLBACK(connect_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Disconnect",
+ GTK_STOCK_DISCONNECT, connected,
+ G_CALLBACK(disconnect_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Add", GTK_STOCK_ADD,
+ connected, G_CALLBACK(add_cb), data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Add from URL",
+ GTK_STOCK_ADD, connected, G_CALLBACK(add_url_cb),
+ data);
+ trg_imagemenuitem_new(GTK_MENU_SHELL(menu), "Quit", GTK_STOCK_QUIT,
+ TRUE, G_CALLBACK(quit_cb), data);
+
+ gtk_widget_show_all(menu);
+
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ (event != NULL) ? event->button : 0,
+ gdk_event_get_time((GdkEvent *) event));
+}
+
+static gboolean trg_status_icon_popup_menu_cb(GtkStatusIcon * icon,
+ gpointer userdata)
+{
+ trg_status_icon_view_menu(icon, NULL, userdata);
+ return TRUE;
+}
+
+static gboolean status_icon_button_press_event(GtkStatusIcon * icon,
+ GdkEventButton * event,
+ gpointer data)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ trg_status_icon_view_menu(icon, event, data);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static gboolean
+torrent_tv_button_pressed_cb(GtkWidget * treeview, GdkEventButton * event,
+ gpointer userdata)
+{
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
+ (gint) event->x,
+ (gint) event->y, &path,
+ NULL, NULL, NULL)) {
+ if (!gtk_tree_selection_path_is_selected(selection, path)) {
+ gtk_tree_selection_unselect_all(selection);
+ gtk_tree_selection_select_path(selection, path);
+ }
+
+ gtk_tree_path_free(path);
+
+ trg_torrent_tv_view_menu(treeview, event, userdata);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean torrent_tv_popup_menu_cb(GtkWidget * treeview,
+ gpointer userdata)
+{
+ trg_torrent_tv_view_menu(treeview, NULL, userdata);
+ return TRUE;
+}
+
+static GObject *trg_main_window_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam
+ * construct_params)
+{
+ TrgMainWindow *self;
+ TrgMainWindowPrivate *priv;
+ GtkWidget *w;
+ GtkWidget *outerVbox;
+ GError *iconError = NULL;
+ GtkWidget *toolbarHbox;
+ gint width, height;
+
+ self = TRG_MAIN_WINDOW(G_OBJECT_CLASS
+ (trg_main_window_parent_class)->constructor
+ (type, n_construct_properties,
+ construct_params));
+ priv = TRG_MAIN_WINDOW_GET_PRIVATE(self);
+
+ gtk_window_set_title(GTK_WINDOW(self), PACKAGE_NAME);
+ gtk_container_set_border_width(GTK_CONTAINER(self), 5);
+ gtk_window_set_default_size(GTK_WINDOW(self), 1000, 600);
+ g_signal_connect(G_OBJECT(self), "delete-event",
+ G_CALLBACK(delete_event), NULL);
+ g_signal_connect(G_OBJECT(self), "destroy", G_CALLBACK(destroy_window),
+ NULL);
+
+ gtk_window_set_icon_from_file
+ (GTK_WINDOW(self), WINDOW_ICON_FILE, &iconError);
+
+ if (iconError != NULL) {
+ g_printf("setting icon failed: %s\n", iconError->message);
+ g_error_free(iconError);
+ }
+
+ priv->torrentModel = trg_torrent_model_new();
+ g_signal_connect(priv->torrentModel, "torrent-completed",
+ G_CALLBACK(on_torrent_completed), self);
+
+ priv->filteredTorrentModel =
+ gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->torrentModel),
+ NULL);
+ gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER
+ (priv->filteredTorrentModel),
+ trg_torrent_tree_view_visible_func,
+ self, NULL);
+
+ priv->sortedTorrentModel =
+ gtk_tree_model_sort_new_with_model(priv->filteredTorrentModel);
+
+ priv->torrentTreeView =
+ trg_main_window_torrent_tree_view_new(self,
+ priv->sortedTorrentModel,
+ priv->stateSelector);
+ g_signal_connect(priv->torrentTreeView, "key-press-event",
+ G_CALLBACK(torrent_tv_key_press_event), self);
+ g_signal_connect(priv->torrentTreeView, "popup-menu",
+ G_CALLBACK(torrent_tv_popup_menu_cb), self);
+ g_signal_connect(priv->torrentTreeView, "button-press-event",
+ G_CALLBACK(torrent_tv_button_pressed_cb), self);
+
+
+ outerVbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(self), outerVbox);
+
+ priv->menuBar = trg_main_window_menu_bar_new(self);
+ gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(priv->menuBar),
+ FALSE, FALSE, 0);
+
+ toolbarHbox = gtk_hbox_new(FALSE, 0);
+ priv->toolBar = trg_main_window_toolbar_new(self);
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), GTK_WIDGET(priv->toolBar),
+ TRUE, TRUE, 0);
+
+#if GTK_CHECK_VERSION( 2,16,0 )
+ w = gtk_entry_new();
+ gtk_entry_set_icon_from_stock(GTK_ENTRY(w),
+ GTK_ENTRY_ICON_SECONDARY,
+ GTK_STOCK_CLEAR);
+ g_signal_connect(w, "icon-release",
+ G_CALLBACK(clear_filter_entry_cb), w);
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), w, FALSE, FALSE, 0);
+ g_object_set(w, "secondary-icon-sensitive", FALSE, NULL);
+ priv->filterEntryClearButton = priv->filterEntry = w;
+#else
+ priv->filterEntry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), priv->filterEntry, FALSE,
+ FALSE, 0);
+ w = gtk_button_new();
+ gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
+ gtk_button_set_image(GTK_BUTTON(w),
+ gtk_image_new_from_stock(GTK_STOCK_CLEAR,
+ GTK_ICON_SIZE_MENU));
+ gtk_box_pack_start(GTK_BOX(toolbarHbox), w, FALSE, FALSE, 0);
+ g_signal_connect_swapped(w, "clicked",
+ G_CALLBACK(clear_filter_entry_cb),
+ priv->filterEntry);
+ priv->filterEntryClearButton = w;
+#endif
+
+ g_signal_connect(G_OBJECT(priv->filterEntry), "changed",
+ G_CALLBACK(entry_filter_changed_cb), self);
+
+ gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(toolbarHbox),
+ FALSE, FALSE, 0);
+
+ priv->vpaned = gtk_vpaned_new();
+ priv->hpaned = gtk_hpaned_new();
+ gtk_box_pack_start(GTK_BOX(outerVbox), priv->vpaned, TRUE, TRUE, 0);
+ gtk_paned_pack1(GTK_PANED(priv->vpaned), priv->hpaned, TRUE, TRUE);
+
+ priv->stateSelector = trg_state_selector_new();
+ gtk_paned_pack1(GTK_PANED(priv->hpaned),
+ GTK_WIDGET(priv->stateSelector), FALSE, FALSE);
+
+ gtk_paned_pack2(GTK_PANED(priv->hpaned),
+ my_scrolledwin_new(GTK_WIDGET
+ (priv->torrentTreeView)), TRUE,
+ TRUE);
+
+ g_signal_connect(G_OBJECT(priv->stateSelector),
+ "torrent-state-changed",
+ G_CALLBACK(torrent_state_selection_changed),
+ priv->filteredTorrentModel);
+
+ priv->notebook = trg_main_window_notebook_new(self);
+ gtk_paned_pack2(GTK_PANED(priv->vpaned), priv->notebook, FALSE, FALSE);
+
+ priv->statusIcon = gtk_status_icon_new_from_file(WINDOW_ICON_FILE);
+ gtk_status_icon_set_screen(priv->statusIcon,
+ gtk_window_get_screen(GTK_WINDOW(self)));
+ g_signal_connect(priv->statusIcon, "activate",
+ G_CALLBACK(status_icon_activated), self);
+ g_signal_connect(priv->statusIcon, "button-press-event",
+ G_CALLBACK(status_icon_button_press_event), self);
+ g_signal_connect(priv->statusIcon, "popup-menu",
+ G_CALLBACK(trg_status_icon_popup_menu_cb), self);
+
+ priv->statusBar = trg_status_bar_new(priv->statusIcon);
+ gtk_box_pack_start(GTK_BOX(outerVbox), GTK_WIDGET(priv->statusBar),
+ FALSE, FALSE, 2);
+
+ width =
+ gconf_client_get_int(priv->client->gconf,
+ TRG_GCONF_KEY_WINDOW_WIDTH, NULL);
+ height =
+ gconf_client_get_int(priv->client->gconf,
+ TRG_GCONF_KEY_WINDOW_HEIGHT, NULL);
+
+ if (width > 0 && height > 0)
+ gtk_window_set_default_size(GTK_WINDOW(self), width, height);
+
+ return G_OBJECT(self);
+}
+
+static void trg_main_window_class_init(TrgMainWindowClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(TrgMainWindowPrivate));
+
+ object_class->constructor = trg_main_window_constructor;
+ object_class->get_property = trg_main_window_get_property;
+ object_class->set_property = trg_main_window_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_CLIENT,
+ g_param_spec_pointer
+ ("trg-client", "TClient",
+ "Client",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+}
+
+void auto_connect_if_required(TrgMainWindow * win, trg_client * tc)
+{
+ gchar *host =
+ gconf_client_get_string(tc->gconf, TRG_GCONF_KEY_HOSTNAME,
+ NULL);
+ if (host != NULL) {
+ gint len = strlen(host);
+ g_free(host);
+ if (len > 0
+ && gconf_client_get_bool(tc->gconf,
+ TRG_GCONF_KEY_AUTO_CONNECT,
+ NULL) == TRUE)
+ connect_cb(NULL, win);
+ }
+}
+
+TrgMainWindow *trg_main_window_new(trg_client * tc)
+{
+ return g_object_new(TRG_TYPE_MAIN_WINDOW, "trg-client", tc, NULL);
+}
diff --git a/src/trg-main-window.h b/src/trg-main-window.h
new file mode 100644
index 0000000..24cac9f
--- /dev/null
+++ b/src/trg-main-window.h
@@ -0,0 +1,71 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MAIN_WINDOW_H_
+#define MAIN_WINDOW_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-torrent-model.h"
+#include "trg-peers-model.h"
+#include "trg-files-model.h"
+#include "trg-trackers-model.h"
+#include "trg-general-panel.h"
+#include "trg-torrent-tree-view.h"
+#include "trg-client.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_MAIN_WINDOW trg_main_window_get_type()
+#define TRG_MAIN_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_MAIN_WINDOW, TrgMainWindow))
+#define TRG_MAIN_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_MAIN_WINDOW, TrgMainWindowClass))
+#define TRG_IS_MAIN_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_MAIN_WINDOW))
+#define TRG_IS_MAIN_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_MAIN_WINDOW))
+#define TRG_MAIN_WINDOW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_MAIN_WINDOW, TrgMainWindowClass))
+ typedef struct {
+ GtkWindow parent;
+} TrgMainWindow;
+
+typedef struct {
+ GtkWindowClass parent_class;
+} TrgMainWindowClass;
+
+GType trg_main_window_get_type(void);
+
+TrgMainWindow *trg_main_window_new(trg_client * tc);
+gboolean trg_add_from_filename(TrgMainWindow * win, gchar * fileName);
+void on_generic_interactive_action(JsonObject * response,
+ int status, gpointer data);
+void auto_connect_if_required(TrgMainWindow * win, trg_client * tc);
+void on_session_set(JsonObject * response, int status, gpointer data);
+
+G_END_DECLS struct add_torrent_threadfunc_args {
+ GSList *list;
+ trg_client *client;
+ gpointer cb_data;
+};
+
+#endif /* MAIN_WINDOW_H_ */
diff --git a/src/trg-menu-bar.c b/src/trg-menu-bar.c
new file mode 100644
index 0000000..23354b3
--- /dev/null
+++ b/src/trg-menu-bar.c
@@ -0,0 +1,374 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include "trg-main-window.h"
+#include "trg-menu-bar.h"
+
+enum {
+ PROP_0,
+ PROP_CONNECT_BUTTON,
+ PROP_DISCONNECT_BUTTON,
+ PROP_ADD_BUTTON,
+ PROP_ADD_URL_BUTTON,
+ PROP_REMOVE_BUTTON,
+ PROP_DELETE_BUTTON,
+ PROP_RESUME_BUTTON,
+ PROP_PAUSE_BUTTON,
+ PROP_VERIFY_BUTTON,
+ PROP_PROPS_BUTTON,
+ PROP_REMOTE_PREFS_BUTTON,
+ PROP_LOCAL_PREFS_BUTTON,
+ PROP_ABOUT_BUTTON,
+ PROP_VIEW_STATES_BUTTON,
+ PROP_VIEW_NOTEBOOK_BUTTON,
+ PROP_QUIT
+};
+
+G_DEFINE_TYPE(TrgMenuBar, trg_menu_bar, GTK_TYPE_MENU_BAR)
+#define TRG_MENU_BAR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_MENU_BAR, TrgMenuBarPrivate))
+typedef struct _TrgMenuBarPrivate TrgMenuBarPrivate;
+
+struct _TrgMenuBarPrivate {
+ GtkWidget *mb_connect;
+ GtkWidget *mb_disconnect;
+ GtkWidget *mb_add;
+ GtkWidget *mb_add_url;
+ GtkWidget *mb_remove;
+ GtkWidget *mb_delete;
+ GtkWidget *mb_resume;
+ GtkWidget *mb_pause;
+ GtkWidget *mb_verify;
+ GtkWidget *mb_props;
+ GtkWidget *mb_local_prefs;
+ GtkWidget *mb_remote_prefs;
+ GtkWidget *mb_view_states;
+ GtkWidget *mb_view_notebook;
+ GtkWidget *mb_about;
+ GtkWidget *mb_quit;
+};
+
+void trg_menu_bar_connected_change(TrgMenuBar * mb, gboolean connected)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(mb);
+
+ gtk_widget_set_sensitive(priv->mb_add, connected);
+ gtk_widget_set_sensitive(priv->mb_add_url, connected);
+ gtk_widget_set_sensitive(priv->mb_connect, !connected);
+ gtk_widget_set_sensitive(priv->mb_disconnect, connected);
+ gtk_widget_set_sensitive(priv->mb_remote_prefs, connected);
+}
+
+void trg_menu_bar_torrent_actions_sensitive(TrgMenuBar * mb,
+ gboolean sensitive)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(mb);
+
+ gtk_widget_set_sensitive(priv->mb_props, sensitive);
+ gtk_widget_set_sensitive(priv->mb_remove, sensitive);
+ gtk_widget_set_sensitive(priv->mb_delete, sensitive);
+ gtk_widget_set_sensitive(priv->mb_resume, sensitive);
+ gtk_widget_set_sensitive(priv->mb_pause, sensitive);
+ gtk_widget_set_sensitive(priv->mb_verify, sensitive);
+}
+
+static void
+trg_menu_bar_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_CONNECT_BUTTON:
+ g_value_set_object(value, priv->mb_connect);
+ break;
+ case PROP_DISCONNECT_BUTTON:
+ g_value_set_object(value, priv->mb_disconnect);
+ break;
+ case PROP_ADD_BUTTON:
+ g_value_set_object(value, priv->mb_add);
+ break;
+ case PROP_ADD_URL_BUTTON:
+ g_value_set_object(value, priv->mb_add_url);
+ break;
+ case PROP_REMOVE_BUTTON:
+ g_value_set_object(value, priv->mb_remove);
+ break;
+ case PROP_DELETE_BUTTON:
+ g_value_set_object(value, priv->mb_delete);
+ break;
+ case PROP_RESUME_BUTTON:
+ g_value_set_object(value, priv->mb_resume);
+ break;
+ case PROP_PAUSE_BUTTON:
+ g_value_set_object(value, priv->mb_pause);
+ break;
+ case PROP_VERIFY_BUTTON:
+ g_value_set_object(value, priv->mb_verify);
+ break;
+ case PROP_PROPS_BUTTON:
+ g_value_set_object(value, priv->mb_props);
+ break;
+ case PROP_REMOTE_PREFS_BUTTON:
+ g_value_set_object(value, priv->mb_remote_prefs);
+ break;
+ case PROP_LOCAL_PREFS_BUTTON:
+ g_value_set_object(value, priv->mb_local_prefs);
+ break;
+ case PROP_ABOUT_BUTTON:
+ g_value_set_object(value, priv->mb_about);
+ break;
+ case PROP_VIEW_STATES_BUTTON:
+ g_value_set_object(value, priv->mb_view_states);
+ break;
+ case PROP_VIEW_NOTEBOOK_BUTTON:
+ g_value_set_object(value, priv->mb_view_notebook);
+ break;
+ case PROP_QUIT:
+ g_value_set_object(value, priv->mb_quit);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_menu_bar_install_widget_prop(GObjectClass * class, guint propId,
+ const gchar * name, const gchar * nick)
+{
+ g_object_class_install_property(class,
+ propId,
+ g_param_spec_object(name,
+ nick,
+ nick,
+ GTK_TYPE_WIDGET,
+ G_PARAM_READABLE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+}
+
+static
+GtkWidget *trg_menu_bar_item_new(GtkMenuShell * shell, char *text,
+ char *stock_id, gboolean sensitive)
+{
+ GtkWidget *item = gtk_image_menu_item_new_with_label(stock_id);
+ gtk_image_menu_item_set_use_stock(GTK_IMAGE_MENU_ITEM(item), TRUE);
+
+ gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM
+ (item), TRUE);
+ gtk_menu_item_set_label(GTK_MENU_ITEM(item), text);
+ gtk_widget_set_sensitive(item, sensitive);
+
+ gtk_menu_shell_append(shell, item);
+
+ return item;
+}
+
+static GtkWidget *trg_menu_bar_view_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *view = gtk_menu_item_new_with_label("View");
+ GtkWidget *viewMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewMenu);
+
+ priv->mb_view_states =
+ gtk_check_menu_item_new_with_label("State selector");
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
+ (priv->mb_view_states), TRUE);
+
+ priv->mb_view_notebook =
+ gtk_check_menu_item_new_with_label("Torrent details");
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
+ (priv->mb_view_notebook), TRUE);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(viewMenu), priv->mb_view_states);
+ gtk_menu_shell_append(GTK_MENU_SHELL(viewMenu),
+ priv->mb_view_notebook);
+
+ return view;
+}
+
+static
+GtkWidget *trg_menu_bar_options_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *opts = gtk_menu_item_new_with_label("Options");
+ GtkWidget *optsMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(opts), optsMenu);
+
+ priv->mb_local_prefs =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(optsMenu),
+ "Local Preferences",
+ GTK_STOCK_PREFERENCES, TRUE);
+
+ priv->mb_remote_prefs =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(optsMenu),
+ "Remote Preferences",
+ GTK_STOCK_NETWORK, FALSE);
+
+ return opts;
+}
+
+static
+GtkWidget *trg_menu_bar_file_file_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *file = gtk_menu_item_new_with_label("File");
+ GtkWidget *fileMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), fileMenu);
+
+ priv->mb_connect =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Connect",
+ GTK_STOCK_CONNECT, TRUE);
+ priv->mb_disconnect =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Disconnect",
+ GTK_STOCK_DISCONNECT, FALSE);
+ priv->mb_add =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Add",
+ GTK_STOCK_ADD, FALSE);
+ priv->mb_add_url =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Add from URL",
+ GTK_STOCK_ADD, FALSE);
+
+ priv->mb_quit = trg_menu_bar_item_new(GTK_MENU_SHELL(fileMenu), "Quit",
+ GTK_STOCK_QUIT, TRUE);
+
+ return file;
+}
+
+static
+GtkWidget *trg_menu_bar_torrent_menu_new(TrgMenuBarPrivate * priv)
+{
+ GtkWidget *torrent = gtk_menu_item_new_with_label("Torrent");
+ GtkWidget *torrentMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(torrent), torrentMenu);
+
+ priv->mb_props =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu),
+ "Properties", GTK_STOCK_PROPERTIES, FALSE);
+ priv->mb_resume =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Resume",
+ GTK_STOCK_MEDIA_PLAY, FALSE);
+ priv->mb_pause =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Pause",
+ GTK_STOCK_MEDIA_PAUSE, FALSE);
+ priv->mb_verify =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Verify",
+ GTK_STOCK_REFRESH, FALSE);
+ priv->mb_remove =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu), "Remove",
+ GTK_STOCK_REMOVE, FALSE);
+ priv->mb_delete =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(torrentMenu),
+ "Remove and Delete", GTK_STOCK_DELETE,
+ FALSE);
+
+ return torrent;
+}
+
+static
+GtkWidget *trg_menu_bar_help_menu_new(TrgMenuBar * menuBar)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(menuBar);
+
+ GtkWidget *help = gtk_menu_item_new_with_label("Help");
+ GtkWidget *helpMenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), helpMenu);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menuBar), help);
+
+ priv->mb_about =
+ trg_menu_bar_item_new(GTK_MENU_SHELL(helpMenu), "About",
+ GTK_STOCK_ABOUT, TRUE);
+
+ return helpMenu;
+}
+
+static void trg_menu_bar_class_init(TrgMenuBarClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ object_class->get_property = trg_menu_bar_get_property;
+
+ g_type_class_add_private(klass, sizeof(TrgMenuBarPrivate));
+
+ trg_menu_bar_install_widget_prop(object_class, PROP_CONNECT_BUTTON,
+ "connect-button", "Connect Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_DISCONNECT_BUTTON,
+ "disconnect-button",
+ "Disconnect Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_ADD_BUTTON,
+ "add-button", "Add Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_ADD_URL_BUTTON,
+ "add-url-button", "Add URL Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_REMOVE_BUTTON,
+ "remove-button", "Remove Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_DELETE_BUTTON,
+ "delete-button", "Delete Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_RESUME_BUTTON,
+ "resume-button", "Resume Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_VERIFY_BUTTON,
+ "verify-button", "Verify Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_PAUSE_BUTTON,
+ "pause-button", "Pause Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_PROPS_BUTTON,
+ "props-button", "Props Button");
+ trg_menu_bar_install_widget_prop(object_class, PROP_ABOUT_BUTTON,
+ "about-button", "About Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_VIEW_STATES_BUTTON,
+ "view-states-button",
+ "View states Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_VIEW_NOTEBOOK_BUTTON,
+ "view-notebook-button",
+ "View notebook Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_REMOTE_PREFS_BUTTON,
+ "remote-prefs-button",
+ "Remote Prefs Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_LOCAL_PREFS_BUTTON,
+ "local-prefs-button",
+ "Local Prefs Button");
+ trg_menu_bar_install_widget_prop(object_class,
+ PROP_QUIT,
+ "quit-button", "Quit Button");
+}
+
+static void trg_menu_bar_init(TrgMenuBar * self)
+{
+ TrgMenuBarPrivate *priv = TRG_MENU_BAR_GET_PRIVATE(self);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_file_file_menu_new(priv));
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_torrent_menu_new(priv));
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_options_menu_new(priv));
+ gtk_menu_shell_append(GTK_MENU_SHELL(self),
+ trg_menu_bar_view_menu_new(priv));
+ trg_menu_bar_help_menu_new(TRG_MENU_BAR(self));
+}
+
+TrgMenuBar *trg_menu_bar_new(TrgMainWindow * win)
+{
+ GObject *obj = g_object_new(TRG_TYPE_MENU_BAR, NULL);
+ return TRG_MENU_BAR(obj);
+}
diff --git a/src/trg-menu-bar.h b/src/trg-menu-bar.h
new file mode 100644
index 0000000..43c5f45
--- /dev/null
+++ b/src/trg-menu-bar.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_MENU_BAR_H_
+#define TRG_MENU_BAR_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-main-window.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_MENU_BAR trg_menu_bar_get_type()
+#define TRG_MENU_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_MENU_BAR, TrgMenuBar))
+#define TRG_MENU_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_MENU_BAR, TrgMenuBarClass))
+#define TRG_IS_MENU_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_MENU_BAR))
+#define TRG_IS_MENU_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_MENU_BAR))
+#define TRG_MENU_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_MENU_BAR, TrgMenuBarClass))
+ typedef struct {
+ GtkMenuBar parent;
+} TrgMenuBar;
+
+typedef struct {
+ GtkMenuBarClass parent_class;
+} TrgMenuBarClass;
+
+GType trg_menu_bar_get_type(void);
+
+TrgMenuBar *trg_menu_bar_new(TrgMainWindow * win);
+
+G_END_DECLS
+ void trg_menu_bar_torrent_actions_sensitive(TrgMenuBar * mb,
+ gboolean sensitive);
+void trg_menu_bar_connected_change(TrgMenuBar * mb, gboolean connected);
+
+#endif /* TRG_MENU_BAR_H_ */
diff --git a/src/trg-model.c b/src/trg-model.c
new file mode 100644
index 0000000..585c796
--- /dev/null
+++ b/src/trg-model.c
@@ -0,0 +1,65 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+struct trg_model_remove_removed_foreachfunc_args {
+ gint64 currentSerial;
+ gint serial_column;
+ GList *toRemove;
+};
+
+gboolean
+trg_model_remove_removed_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ struct trg_model_remove_removed_foreachfunc_args *args =
+ (struct trg_model_remove_removed_foreachfunc_args *) data;
+ gint64 rowSerial;
+ gtk_tree_model_get(model, iter, args->serial_column, &rowSerial, -1);
+ if (rowSerial != args->currentSerial)
+ args->toRemove =
+ g_list_append(args->toRemove, gtk_tree_iter_copy(iter));
+
+ return FALSE;
+}
+
+void
+trg_model_remove_removed(GtkListStore * model, gint serial_column,
+ gint64 currentSerial)
+{
+ struct trg_model_remove_removed_foreachfunc_args args;
+ GList *li;
+
+ args.toRemove = NULL;
+ args.currentSerial = currentSerial;
+ args.serial_column = serial_column;
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ trg_model_remove_removed_foreachfunc, &args);
+ if (args.toRemove != NULL) {
+ for (li = g_list_last(args.toRemove); li != NULL;
+ li = g_list_previous(li)) {
+ gtk_list_store_remove(model, (GtkTreeIter *) li->data);
+ gtk_tree_iter_free((GtkTreeIter *) li->data);
+ }
+ g_list_free(args.toRemove);
+ }
+}
diff --git a/src/trg-model.h b/src/trg-model.h
new file mode 100644
index 0000000..db03996
--- /dev/null
+++ b/src/trg-model.h
@@ -0,0 +1,29 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_MODEL_H_
+#define TRG_MODEL_H_
+
+#include <gtk/gtk.h>
+
+void trg_model_remove_removed(GtkListStore * model, gint serial_column,
+ gint64 currentSerial);
+
+#endif /* TRG_MODEL_H_ */
diff --git a/src/trg-peers-model.c b/src/trg-peers-model.c
new file mode 100644
index 0000000..b45ac20
--- /dev/null
+++ b/src/trg-peers-model.c
@@ -0,0 +1,225 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#if HAVE_GEOIP
+#include <GeoIP.h>
+#endif
+
+#include "torrent.h"
+#include "tpeer.h"
+#include "trg-peers-model.h"
+#include "trg-model.h"
+#include "util.h"
+
+G_DEFINE_TYPE(TrgPeersModel, trg_peers_model, GTK_TYPE_LIST_STORE)
+
+static void trg_peers_model_class_init(TrgPeersModelClass * klass)
+{
+}
+
+gboolean
+find_existing_peer_item_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path, GtkTreeIter * iter,
+ gpointer data)
+{
+ struct peerAndIter *pi;
+ gchar *ip;
+
+ pi = (struct peerAndIter *) data;
+
+ gtk_tree_model_get(model, iter, PEERSCOL_IP, &ip, -1);
+ if (g_strcmp0(ip, pi->ip) == 0) {
+ pi->iter = *iter;
+ pi->found = TRUE;
+ }
+ g_free(ip);
+ return pi->found;
+}
+
+gboolean
+find_existing_peer_item(TrgPeersModel * model, JsonObject * p,
+ GtkTreeIter * iter)
+{
+ struct peerAndIter pi;
+ pi.ip = peer_get_address(p);
+ pi.found = FALSE;
+
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ find_existing_peer_item_foreachfunc, &pi);
+
+ if (pi.found == TRUE)
+ *iter = pi.iter;
+
+ return pi.found;
+}
+
+static void resolved_dns_cb(GObject * source_object,
+ GAsyncResult * res, gpointer data)
+{
+ GtkTreeRowReference *treeRef;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ treeRef = (GtkTreeRowReference *) data;
+ model = gtk_tree_row_reference_get_model(treeRef);
+ path = gtk_tree_row_reference_get_path(treeRef);
+
+ if (path != NULL) {
+ gchar *rdns =
+ g_resolver_lookup_by_address_finish(G_RESOLVER(source_object),
+ res, NULL);
+ if (rdns != NULL) {
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter(model, &iter, path) == TRUE) {
+ gdk_threads_enter();
+ gtk_list_store_set(GTK_LIST_STORE(model),
+ &iter, PEERSCOL_HOST, rdns, -1);
+ gdk_threads_leave();
+ }
+ g_free(rdns);
+ }
+ gtk_tree_path_free(path);
+ }
+
+ gtk_tree_row_reference_free(treeRef);
+}
+
+void trg_peers_model_update(TrgPeersModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first)
+{
+ JsonArray *peers;
+ GtkTreeIter peerIter;
+ int j;
+ gboolean isNew;
+
+ peers = torrent_get_peers(t);
+
+ if (first == TRUE)
+ gtk_list_store_clear(GTK_LIST_STORE(model));
+
+ for (j = 0; j < json_array_get_length(peers); j++) {
+ JsonObject *peer;
+ const gchar *address, *flagStr;
+#if HAVE_GEOIP
+ GeoIP *gi;
+ const gchar *country = NULL;
+#endif
+ peer = json_node_get_object(json_array_get_element(peers, j));
+
+ if (first == TRUE
+ || find_existing_peer_item(model, peer, &peerIter) == FALSE) {
+ gtk_list_store_append(GTK_LIST_STORE(model), &peerIter);
+ isNew = TRUE;
+ } else {
+ isNew = FALSE;
+ }
+
+ address = peer_get_address(peer);
+ flagStr = peer_get_flagstr(peer);
+#if HAVE_GEOIP
+ if ((gi = g_object_get_data(G_OBJECT(model), "geoip")) != NULL)
+ country = GeoIP_country_name_by_addr(gi, address);
+#endif
+ gtk_list_store_set(GTK_LIST_STORE(model), &peerIter,
+ PEERSCOL_ICON,
+ GTK_STOCK_NETWORK, PEERSCOL_IP, address,
+#if HAVE_GEOIP
+ PEERSCOL_COUNTRY,
+ country != NULL ? country : "",
+#endif
+ PEERSCOL_FLAGS, flagStr,
+ PEERSCOL_PROGRESS,
+ peer_get_progress(peer),
+ PEERSCOL_DOWNSPEED,
+ peer_get_rate_to_client(peer),
+ PEERSCOL_UPSPEED,
+ peer_get_rate_to_peer(peer),
+ PEERSCOL_UPDATESERIAL, updateSerial, -1);
+
+ if (isNew == TRUE) {
+ GtkTreePath *path;
+ GtkTreeRowReference *treeRef;
+ GInetAddress *inetAddr;
+ GResolver *resolver;
+
+ path =
+ gtk_tree_model_get_path(GTK_TREE_MODEL(model), &peerIter);
+ treeRef =
+ gtk_tree_row_reference_new(GTK_TREE_MODEL(model), path);
+ gtk_tree_path_free(path);
+
+ inetAddr = g_inet_address_new_from_string(address);
+ resolver = g_resolver_get_default();
+ g_resolver_lookup_by_address_async(resolver,
+ inetAddr, NULL,
+ resolved_dns_cb, treeRef);
+ g_object_unref(resolver);
+ g_object_unref(inetAddr);
+ }
+ }
+
+ if (first == FALSE)
+ trg_model_remove_removed(GTK_LIST_STORE(model),
+ PEERSCOL_UPDATESERIAL, updateSerial);
+}
+
+static void trg_peers_model_init(TrgPeersModel * self)
+{
+ GType column_types[PEERSCOL_COLUMNS];
+
+ column_types[PEERSCOL_ICON] = G_TYPE_STRING;
+ column_types[PEERSCOL_IP] = G_TYPE_STRING;
+#if HAVE_GEOIP
+ column_types[PEERSCOL_COUNTRY] = G_TYPE_STRING;
+#endif
+ column_types[PEERSCOL_HOST] = G_TYPE_STRING;
+ column_types[PEERSCOL_FLAGS] = G_TYPE_STRING;
+ column_types[PEERSCOL_PROGRESS] = G_TYPE_DOUBLE;
+ column_types[PEERSCOL_DOWNSPEED] = G_TYPE_INT64;
+ column_types[PEERSCOL_UPSPEED] = G_TYPE_INT64;
+ column_types[PEERSCOL_UPDATESERIAL] = G_TYPE_INT64;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ PEERSCOL_COLUMNS, column_types);
+
+#if HAVE_GEOIP
+ if (g_file_test(TRG_GEOIP_DATABASE, G_FILE_TEST_EXISTS) == TRUE) {
+ GeoIP *gi = GeoIP_open(TRG_GEOIP_DATABASE,
+ GEOIP_STANDARD | GEOIP_CHECK_CACHE);
+ g_object_set_data(G_OBJECT(self), "geoip", gi);
+ }
+#endif
+}
+
+
+TrgPeersModel *trg_peers_model_new()
+{
+ GObject *obj = g_object_new(TRG_TYPE_PEERS_MODEL, NULL);
+
+ return TRG_PEERS_MODEL(obj);
+}
diff --git a/src/trg-peers-model.h b/src/trg-peers-model.h
new file mode 100644
index 0000000..0e18723
--- /dev/null
+++ b/src/trg-peers-model.h
@@ -0,0 +1,85 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_PEERS_MODEL_H_
+#define TRG_PEERS_MODEL_H_
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+#if HAVE_GEOIP
+#include <GeoIP.h>
+#endif
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_PEERS_MODEL trg_peers_model_get_type()
+#define TRG_PEERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_PEERS_MODEL, TrgPeersModel))
+#define TRG_PEERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_PEERS_MODEL, TrgPeersModelClass))
+#define TRG_IS_PEERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_PEERS_MODEL))
+#define TRG_IS_PEERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_PEERS_MODEL))
+#define TRG_PEERS_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_PEERS_MODEL, TrgPeersModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgPeersModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+} TrgPeersModelClass;
+
+GType trg_peers_model_get_type(void);
+
+TrgPeersModel *trg_peers_model_new();
+
+G_END_DECLS struct peerAndIter {
+ const gchar *ip;
+ GtkTreeIter iter;
+ gboolean found;
+};
+
+enum {
+ PEERSCOL_ICON,
+ PEERSCOL_IP,
+#if HAVE_GEOIP
+ PEERSCOL_COUNTRY,
+#endif
+ PEERSCOL_HOST,
+ PEERSCOL_FLAGS,
+ PEERSCOL_PROGRESS,
+ PEERSCOL_DOWNSPEED,
+ PEERSCOL_UPSPEED,
+ PEERSCOL_UPDATESERIAL,
+ PEERSCOL_COLUMNS
+};
+
+void trg_peers_model_update(TrgPeersModel * model, gint64 updateSerial,
+ JsonObject * t, gboolean first);
+
+#endif /* TRG_PEERS_MODEL_H_ */
+
+#define TRG_GEOIP_DATABASE "/usr/share/GeoIP/GeoIP.dat"
diff --git a/src/trg-peers-tree-view.c b/src/trg-peers-tree-view.c
new file mode 100644
index 0000000..bd109a5
--- /dev/null
+++ b/src/trg-peers-tree-view.c
@@ -0,0 +1,69 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_GEOIP
+#include <GeoIP.h>
+#endif
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-tree-view.h"
+#include "trg-peers-model.h"
+#include "trg-peers-tree-view.h"
+
+G_DEFINE_TYPE(TrgPeersTreeView, trg_peers_tree_view, TRG_TYPE_TREE_VIEW)
+
+static void trg_peers_tree_view_class_init(TrgPeersTreeViewClass * klass)
+{
+}
+
+static void trg_peers_tree_view_init(TrgPeersTreeView * self)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW
+ (self),
+ PEERSCOL_ICON,
+ PEERSCOL_IP, "IP", 180);
+#if HAVE_GEOIP
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Country",
+ PEERSCOL_COUNTRY);
+#endif
+ trg_tree_view_add_column_fixed_width(TRG_TREE_VIEW(self), "Host",
+ PEERSCOL_HOST, 300);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Flags", PEERSCOL_FLAGS);
+ trg_tree_view_add_prog_column(TRG_TREE_VIEW(self), "Progress",
+ PEERSCOL_PROGRESS, -1);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(self), "Down Speed",
+ PEERSCOL_DOWNSPEED, -1);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(self), "Up Speed",
+ PEERSCOL_UPSPEED, -1);
+}
+
+TrgPeersTreeView *trg_peers_tree_view_new(TrgPeersModel * model)
+{
+ GObject *obj = g_object_new(TRG_TYPE_PEERS_TREE_VIEW, NULL);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), GTK_TREE_MODEL(model));
+
+ return TRG_PEERS_TREE_VIEW(obj);
+}
diff --git a/src/trg-peers-tree-view.h b/src/trg-peers-tree-view.h
new file mode 100644
index 0000000..ce3e5ab
--- /dev/null
+++ b/src/trg-peers-tree-view.h
@@ -0,0 +1,54 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_PEERS_TREE_VIEW_H_
+#define TRG_PEERS_TREE_VIEW_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-peers-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_PEERS_TREE_VIEW trg_peers_tree_view_get_type()
+#define TRG_PEERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_PEERS_TREE_VIEW, TrgPeersTreeView))
+#define TRG_PEERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_PEERS_TREE_VIEW, TrgPeersTreeViewClass))
+#define TRG_IS_PEERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_PEERS_TREE_VIEW))
+#define TRG_IS_PEERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_PEERS_TREE_VIEW))
+#define TRG_PEERS_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_PEERS_TREE_VIEW, TrgPeersTreeViewClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgPeersTreeView;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+} TrgPeersTreeViewClass;
+
+GType trg_peers_tree_view_get_type(void);
+
+TrgPeersTreeView *trg_peers_tree_view_new(TrgPeersModel * model);
+
+G_END_DECLS
+#endif /* TRG_PEERS_TREE_VIEW_H_ */
diff --git a/src/trg-preferences-dialog.c b/src/trg-preferences-dialog.c
new file mode 100644
index 0000000..971ff80
--- /dev/null
+++ b/src/trg-preferences-dialog.c
@@ -0,0 +1,327 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gconf/gconf-client.h>
+
+#include "hig.h"
+#include "trg-preferences-dialog.h"
+#include "trg-preferences.h"
+
+#define TRG_PREFERENCES_DIALOG_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialogPrivate))
+
+G_DEFINE_TYPE(TrgPreferencesDialog, trg_preferences_dialog,
+ GTK_TYPE_DIALOG)
+
+enum {
+ PROP_0,
+ PROP_GCONF_CLIENT,
+ PROP_PARENT_WINDOW
+};
+
+#define GCONF_OBJECT_KEY "gconf-key"
+
+struct _TrgPreferencesDialogPrivate {
+ GConfClient *gconf;
+ GtkWindow *parent;
+};
+
+static GObject *instance = NULL;
+
+static void
+trg_preferences_dialog_set_property(GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgPreferencesDialog *pref_dlg = TRG_PREFERENCES_DIALOG(object);
+
+ switch (prop_id) {
+ case PROP_GCONF_CLIENT:
+ pref_dlg->priv->gconf = g_value_get_object(value);
+ break;
+ case PROP_PARENT_WINDOW:
+ pref_dlg->priv->parent = g_value_get_object(value);
+ break;
+ }
+}
+
+static void
+trg_preferences_response_cb(GtkDialog * dlg, gint res_id, gpointer data)
+{
+ switch (res_id) {
+ default:
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+ instance = NULL;
+ }
+}
+
+static void
+trg_preferences_dialog_get_property(GObject * object,
+ guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgPreferencesDialog *pref_dlg = TRG_PREFERENCES_DIALOG(object);
+
+ switch (prop_id) {
+ case PROP_GCONF_CLIENT:
+ g_value_set_object(value, pref_dlg->priv->gconf);
+ break;
+ case PROP_PARENT_WINDOW:
+ g_value_set_object(value, pref_dlg->priv->parent);
+ break;
+ }
+}
+
+static void toggled_cb(GtkToggleButton * w, gpointer gconf)
+{
+ const char *key;
+ gboolean flag;
+
+ key = g_object_get_data(G_OBJECT(w), GCONF_OBJECT_KEY);
+ flag = gtk_toggle_button_get_active(w);
+
+ gconf_client_set_bool(GCONF_CLIENT(gconf), key, flag, NULL);
+}
+
+static GtkWidget *new_check_button(GConfClient * gconf,
+ const char *mnemonic, const char *key)
+{
+ GtkWidget *w = gtk_check_button_new_with_mnemonic(mnemonic);
+
+ g_object_set_data_full(G_OBJECT(w), GCONF_OBJECT_KEY,
+ g_strdup(key), g_free);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),
+ gconf_client_get_bool(gconf, key, NULL));
+ g_signal_connect(w, "toggled", G_CALLBACK(toggled_cb), gconf);
+ return w;
+}
+
+static void spun_cb_int(GtkWidget * widget, gpointer gconf)
+{
+ gchar *key;
+
+ key = g_object_get_data(G_OBJECT(widget), GCONF_OBJECT_KEY);
+
+ gconf_client_set_int(GCONF_CLIENT(gconf),
+ key,
+ gtk_spin_button_get_value_as_int
+ (GTK_SPIN_BUTTON(widget)), NULL);
+}
+
+static GtkWidget *new_spin_button(GConfClient * gconf,
+ const char *key,
+ int low, int high, int step)
+{
+ GtkWidget *w;
+ gint currentValue;
+ GError *error = NULL;
+
+ w = gtk_spin_button_new_with_range(low, high, step);
+ g_object_set_data_full(G_OBJECT(w), GCONF_OBJECT_KEY,
+ g_strdup(key), g_free);
+
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(w), 0);
+
+ currentValue = gconf_client_get_int(gconf, key, &error);
+
+ if (error != NULL) {
+ g_error_free(error);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), 9091);
+ } else {
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), currentValue);
+ }
+
+ g_signal_connect(w, "value-changed", G_CALLBACK(spun_cb_int), gconf);
+
+ return w;
+}
+
+static void entry_changed_cb(GtkEntry * w, gpointer gconf)
+{
+ const char *key, *value;
+
+ key = g_object_get_data(G_OBJECT(w), GCONF_OBJECT_KEY);
+ value = gtk_entry_get_text(w);
+
+ gconf_client_set_string(GCONF_CLIENT(gconf), key, value, NULL);
+}
+
+static GtkWidget *new_entry(GConfClient * gconf, const char *key)
+{
+ GtkWidget *w;
+ const char *value;
+
+ w = gtk_entry_new();
+ value = gconf_client_get_string(gconf, key, NULL);
+
+ if (value != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(w), value);
+ g_free((gpointer) value);
+ }
+
+ g_object_set_data_full(G_OBJECT(w), GCONF_OBJECT_KEY,
+ g_strdup(key), g_free);
+
+ g_signal_connect(w, "changed", G_CALLBACK(entry_changed_cb), gconf);
+ return w;
+}
+
+static GtkWidget *trg_prefs_serverPage(GConfClient * gconf)
+{
+ GtkWidget *w, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ hig_workarea_add_section_title(t, &row, "Server");
+
+ w = new_entry(gconf, TRG_GCONF_KEY_HOSTNAME);
+ hig_workarea_add_row(t, &row, "Host:", w, NULL);
+
+ w = new_spin_button(gconf, TRG_GCONF_KEY_PORT, 1, 65535, 1);
+ hig_workarea_add_row(t, &row, "Port:", w, NULL);
+
+ w = new_check_button(gconf, "Automatically connect",
+ TRG_GCONF_KEY_AUTO_CONNECT);
+ hig_workarea_add_wide_control(t, &row, w);
+
+ hig_workarea_add_section_divider(t, &row);
+ hig_workarea_add_section_title(t, &row, "Authentication");
+
+ w = new_entry(gconf, TRG_GCONF_KEY_USERNAME);
+ hig_workarea_add_row(t, &row, "Username:", w, NULL);
+
+ w = new_entry(gconf, TRG_GCONF_KEY_PASSWORD);
+ gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
+ hig_workarea_add_row(t, &row, "Password:", w, NULL);
+
+ return t;
+}
+
+static GObject *trg_preferences_dialog_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam *
+ construct_params)
+{
+ GObject *object;
+ TrgPreferencesDialogPrivate *priv;
+ GtkWidget *notebook;
+
+ object =
+ G_OBJECT_CLASS
+ (trg_preferences_dialog_parent_class)->constructor(type,
+ n_construct_properties,
+ construct_params);
+ priv = TRG_PREFERENCES_DIALOG_GET_PRIVATE(object);
+
+ gtk_window_set_transient_for(GTK_WINDOW(object), priv->parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(object), TRUE);
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE);
+ gtk_window_set_title(GTK_WINDOW(object), "Local Preferences");
+ gtk_container_set_border_width(GTK_CONTAINER(object), GUI_PAD);
+
+ g_signal_connect(G_OBJECT(object),
+ "response",
+ G_CALLBACK(trg_preferences_response_cb), NULL);
+
+ notebook = gtk_notebook_new();
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_prefs_serverPage(priv->gconf),
+ gtk_label_new("Connection"));
+
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(object)->vbox), notebook,
+ TRUE, TRUE, 0);
+
+ gtk_widget_show_all(GTK_DIALOG(object)->vbox);
+
+ return object;
+}
+
+static void
+trg_preferences_dialog_class_init(TrgPreferencesDialogClass * class)
+{
+ GObjectClass *g_object_class = (GObjectClass *) class;
+
+ g_object_class->constructor = trg_preferences_dialog_constructor;
+ g_object_class->set_property = trg_preferences_dialog_set_property;
+ g_object_class->get_property = trg_preferences_dialog_get_property;
+
+ g_object_class_install_property(g_object_class,
+ PROP_GCONF_CLIENT,
+ g_param_spec_object("gconf-client",
+ "GConf Client",
+ "GConf Client",
+ GCONF_TYPE_CLIENT,
+ G_PARAM_READWRITE
+ |
+ G_PARAM_CONSTRUCT_ONLY
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property(g_object_class,
+ PROP_PARENT_WINDOW,
+ g_param_spec_object
+ ("parent-window", "Parent window",
+ "Parent window", GTK_TYPE_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_type_class_add_private(g_object_class,
+ sizeof(TrgPreferencesDialogPrivate));
+}
+
+static void trg_preferences_dialog_init(TrgPreferencesDialog * pref_dlg)
+{
+ pref_dlg->priv = TRG_PREFERENCES_DIALOG_GET_PRIVATE(pref_dlg);
+
+ pref_dlg->priv->gconf = NULL;
+}
+
+GtkWidget *trg_preferences_dialog_get_instance(GtkWindow * parent,
+ GConfClient * client)
+{
+ if (instance == NULL) {
+ instance = g_object_new(TRG_TYPE_PREFERENCES_DIALOG,
+ "parent-window", parent,
+ "gconf-client", client, NULL);
+ }
+
+ return GTK_WIDGET(instance);
+}
diff --git a/src/trg-preferences-dialog.h b/src/trg-preferences-dialog.h
new file mode 100644
index 0000000..1ddacb2
--- /dev/null
+++ b/src/trg-preferences-dialog.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_PREFERENCES_WINDOW_H_
+#define TRG_PREFERENCES_WINDOW_H_
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gconf/gconf-client.h>
+
+G_BEGIN_DECLS
+
+typedef struct _TrgPreferencesDialog TrgPreferencesDialog;
+typedef struct _TrgPreferencesDialogClass TrgPreferencesDialogClass;
+typedef struct _TrgPreferencesDialogPrivate TrgPreferencesDialogPrivate;
+
+#define TRG_TYPE_PREFERENCES_DIALOG (trg_preferences_dialog_get_type ())
+#define TRG_PREFERENCES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialog))
+#define TRG_PREFERENCES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialogClass))
+#define TRG_IS_PREFERENCES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TRG_TYPE_PREFERENCES_DIALOG))
+#define TRG_IS_PREFERENCES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TRG_TYPE_PREFERENCES_DIALOG))
+#define TRG_PREFERENCES_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TRG_TYPE_PREFERENCES_DIALOG, TrgPreferencesDialogClass))
+
+struct _TrgPreferencesDialog {
+ GtkDialog dialog;
+ TrgPreferencesDialogPrivate *priv;
+};
+
+struct _TrgPreferencesDialogClass {
+ GtkDialogClass parent_class;
+};
+
+GType trg_preferences_dialog_get_type(void);
+
+GtkWidget * trg_preferences_dialog_get_instance(GtkWindow * parent,
+ GConfClient * client);
+G_END_DECLS
+
+#endif /* TRG_PREFERENCES_WINDOW_H_ */
diff --git a/src/trg-preferences.h b/src/trg-preferences.h
new file mode 100644
index 0000000..8a2db59
--- /dev/null
+++ b/src/trg-preferences.h
@@ -0,0 +1,32 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_PREFERENCES_H_
+#define TRG_PREFERENCES_H_
+
+#define TRG_GCONF_KEY_HOSTNAME "/apps/trg/hostname"
+#define TRG_GCONF_KEY_PORT "/apps/trg/port"
+#define TRG_GCONF_KEY_USERNAME "/apps/trg/username"
+#define TRG_GCONF_KEY_PASSWORD "/apps/trg/password"
+#define TRG_GCONF_KEY_AUTO_CONNECT "/apps/trg/auto-connect"
+#define TRG_GCONF_KEY_WINDOW_WIDTH "/schemas/apps/trg/window_width"
+#define TRG_GCONF_KEY_WINDOW_HEIGHT "/schemas/apps/trg/window_height"
+
+
+#endif /* TRG_PREFERENCES_H_ */
diff --git a/src/trg-remote-prefs-dialog.c b/src/trg-remote-prefs-dialog.c
new file mode 100644
index 0000000..dbd6068
--- /dev/null
+++ b/src/trg-remote-prefs-dialog.c
@@ -0,0 +1,541 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-main-window.h"
+#include "trg-remote-prefs-dialog.h"
+#include "hig.h"
+#include "dispatch.h"
+#include "requests.h"
+#include "json.h"
+#include "trg-json-widgets.h"
+#include "session-get.h"
+
+G_DEFINE_TYPE(TrgRemotePrefsDialog, trg_remote_prefs_dialog,
+ GTK_TYPE_DIALOG)
+#define TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialogPrivate))
+enum {
+ PROP_0,
+ PROP_PARENT,
+ PROP_CLIENT
+};
+
+typedef struct _TrgRemotePrefsDialogPrivate TrgRemotePrefsDialogPrivate;
+
+struct _TrgRemotePrefsDialogPrivate {
+ trg_client *client;
+ TrgMainWindow *parent;
+
+ GtkWidget *done_script_entry;
+ GtkWidget *done_script_enabled_check;
+ GtkWidget *pex_enabled_check;
+ GtkWidget *lpd_enabled_check;
+ GtkWidget *download_dir_entry;
+ GtkWidget *peer_port_random_check;
+ GtkWidget *peer_port_spin;
+ GtkWidget *peer_limit_global_spin;
+ GtkWidget *peer_limit_per_torrent_spin;
+ GtkWidget *peer_port_forwarding_check;
+ GtkWidget *blocklist_url_entry;
+ GtkWidget *blocklist_check;
+ GtkWidget *rename_partial_files_check;
+ GtkWidget *encryption_combo;
+ GtkWidget *incomplete_dir_entry;
+ GtkWidget *incomplete_dir_check;
+ GtkWidget *seed_ratio_limit_check;
+ GtkWidget *seed_ratio_limit_spin;
+ GtkWidget *cache_size_mb_spin;
+ GtkWidget *start_added_torrent_check;
+ GtkWidget *trash_original_torrent_files_check;
+ GtkWidget *speed_limit_down_check;
+ GtkWidget *speed_limit_down_spin;
+ GtkWidget *speed_limit_up_check;
+ GtkWidget *speed_limit_up_spin;
+};
+
+static GObject *instance = NULL;
+
+static void update_session(GtkDialog * dlg)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(dlg);
+ JsonNode *request = session_set();
+ JsonObject *args = node_get_arguments(request);
+ gchar *encryption;
+
+ /* General */
+
+ gtk_entry_json_output(GTK_ENTRY(priv->download_dir_entry), args);
+ gtk_entry_json_output(GTK_ENTRY(priv->incomplete_dir_entry), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->done_script_enabled_check), args);
+ gtk_entry_json_output(GTK_ENTRY(priv->done_script_entry), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->rename_partial_files_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->incomplete_dir_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->trash_original_torrent_files_check),
+ args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->start_added_torrent_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON(priv->cache_size_mb_spin),
+ args);
+
+ /* Connection */
+
+ encryption =
+ g_ascii_strdown(gtk_combo_box_get_active_text
+ (GTK_COMBO_BOX(priv->encryption_combo)), -1);
+ json_object_set_string_member(args, SGET_ENCRYPTION, encryption);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->peer_port_random_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->peer_port_forwarding_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->pex_enabled_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->lpd_enabled_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON(priv->peer_port_spin),
+ args);
+
+ /* Limits */
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->seed_ratio_limit_check), args);
+ gtk_spin_button_json_double_out(GTK_SPIN_BUTTON
+ (priv->seed_ratio_limit_spin), args);
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->speed_limit_down_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->speed_limit_down_spin), args);
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->speed_limit_up_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->speed_limit_up_spin), args);
+
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->peer_limit_global_spin), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->peer_limit_per_torrent_spin),
+ args);
+
+ g_free(encryption);
+
+ dispatch_async(priv->client, request, on_session_set, priv->parent);
+}
+
+static void
+trg_remote_prefs_response_cb(GtkDialog * dlg, gint res_id, gpointer data)
+{
+ if (res_id == GTK_RESPONSE_ACCEPT) {
+ update_session(dlg);
+ }
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+ instance = NULL;
+}
+
+static void
+trg_remote_prefs_dialog_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_PARENT:
+ g_value_set_object(value, priv->parent);
+ break;
+ case PROP_CLIENT:
+ g_value_set_pointer(value, priv->client);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_remote_prefs_dialog_set_property(GObject * object, guint property_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(object);
+ switch (property_id) {
+ case PROP_PARENT:
+ priv->parent = g_value_get_object(value);
+ break;
+ case PROP_CLIENT:
+ priv->client = g_value_get_pointer(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static GtkWidget *trg_rprefs_limitsPage(TrgRemotePrefsDialog * win,
+ JsonObject * json)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(win);
+ GtkWidget *w, *tb, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ hig_workarea_add_section_title(t, &row, "Bandwidth");
+
+ tb = priv->speed_limit_down_check = gtk_check_button_new_with_mnemonic
+ ("Limit download speed (KB/s)");
+ widget_set_json_key(tb, SGET_SPEED_LIMIT_DOWN_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_speed_limit_down_enabled
+ (json));
+
+ w = priv->speed_limit_down_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, SGET_SPEED_LIMIT_DOWN);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_speed_limit_down(json));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ tb = priv->speed_limit_up_check = gtk_check_button_new_with_mnemonic
+ ("Limit upload speed (KB/s)");
+ widget_set_json_key(tb, SGET_SPEED_LIMIT_UP_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_speed_limit_up_enabled(json));
+
+ w = priv->speed_limit_up_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, SGET_SPEED_LIMIT_UP);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_speed_limit_up(json));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ hig_workarea_add_section_title(t, &row, "Seeding");
+
+ tb = priv->seed_ratio_limit_check = gtk_check_button_new_with_mnemonic
+ ("Seed ratio limit");
+ widget_set_json_key(tb, SGET_SEED_RATIO_LIMITED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_seed_ratio_limited(json));
+
+ w = priv->seed_ratio_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 0.1);
+ widget_set_json_key(w, SGET_SEED_RATIO_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_seed_ratio_limit(json));
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ hig_workarea_add_section_title(t, &row, "Peers");
+
+ w = priv->peer_limit_global_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(GTK_WIDGET(w), SGET_PEER_LIMIT_GLOBAL);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_peer_limit_global(json));
+ hig_workarea_add_row(t, &row, "Global peer limit", w, w);
+
+ w = priv->peer_limit_per_torrent_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(GTK_WIDGET(w), SGET_PEER_LIMIT_PER_TORRENT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_peer_limit_per_torrent(json));
+ hig_workarea_add_row(t, &row, "Per torrent peer limit", w, w);
+
+ return t;
+}
+
+static GtkWidget *trg_rprefs_connPage(TrgRemotePrefsDialog * win,
+ JsonObject * s)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(win);
+
+ GtkWidget *w, *t;
+ const gchar *encryption;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ w = priv->encryption_combo = gtk_combo_box_new_text();
+ widget_set_json_key(GTK_WIDGET(w), SGET_ENCRYPTION);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Required");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Preferred");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Tolerated");
+ encryption = session_get_encryption(s);
+ if (g_strcmp0(encryption, "required") == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w), 0);
+ } else if (g_strcmp0(encryption, "tolerated") == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w), 2);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w), 1);
+ }
+ hig_workarea_add_row(t, &row, "Encryption", w, NULL);
+
+ w = priv->peer_port_spin = gtk_spin_button_new_with_range(0, 65535, 1);
+ widget_set_json_key(w, SGET_PEER_PORT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_peer_port(s));
+ hig_workarea_add_row(t, &row, "Peer port", w, w);
+
+ w = priv->peer_port_random_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Random peer port on start",
+ session_get_peer_port_random(s));
+ widget_set_json_key(w, SGET_PEER_PORT_RANDOM_ON_START);
+
+ w = priv->peer_port_forwarding_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Peer port forwarding",
+ session_get_port_forwarding_enabled
+ (s));
+ widget_set_json_key(w, SGET_PORT_FORWARDING_ENABLED);
+
+ w = priv->pex_enabled_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Peer exchange (PEX)",
+ session_get_pex_enabled(s));
+ widget_set_json_key(w, SGET_PEX_ENABLED);
+
+ w = priv->lpd_enabled_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Local peer discovery",
+ session_get_lpd_enabled(s));
+ widget_set_json_key(w, SGET_LPD_ENABLED);
+
+ return t;
+}
+
+static GtkWidget *trg_rprefs_generalPage(TrgRemotePrefsDialog * win,
+ JsonObject * s)
+{
+ TrgRemotePrefsDialogPrivate *priv =
+ TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(win);
+
+ GtkWidget *w, *tb, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ w = priv->download_dir_entry = gtk_entry_new();
+ widget_set_json_key(w, SGET_DOWNLOAD_DIR);
+ gtk_entry_set_text(GTK_ENTRY(w), session_get_download_dir(s));
+ hig_workarea_add_row(t, &row, "Download directory", w, NULL);
+
+ tb = priv->incomplete_dir_check =
+ gtk_check_button_new_with_mnemonic("Incomplete download dir");
+ widget_set_json_key(tb, SGET_INCOMPLETE_DIR_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_incomplete_dir_enabled(s));
+
+ w = priv->incomplete_dir_entry = gtk_entry_new();
+ widget_set_json_key(w, SGET_INCOMPLETE_DIR);
+ gtk_entry_set_text(GTK_ENTRY(w), session_get_incomplete_dir(s));
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ tb = priv->done_script_enabled_check =
+ gtk_check_button_new_with_mnemonic("Torrent done script");
+ widget_set_json_key(tb, SGET_SCRIPT_TORRENT_DONE_ENABLED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ session_get_torrent_done_enabled(s));
+
+ w = priv->done_script_entry = gtk_entry_new();
+ widget_set_json_key(w, SGET_SCRIPT_TORRENT_DONE_FILENAME);
+ gtk_entry_set_text(GTK_ENTRY(w), session_get_torrent_done_filename(s));
+ gtk_widget_set_sensitive(w,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
+ (tb)));
+ g_signal_connect(G_OBJECT(tb), "toggled",
+ G_CALLBACK(toggle_active_arg_is_sensitive), w);
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ w = priv->cache_size_mb_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 1);
+ widget_set_json_key(w, SGET_CACHE_SIZE_MB);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ session_get_cache_size_mb(s));
+ hig_workarea_add_row(t, &row, "Cache size (MB)", w, w);
+
+ w = priv->rename_partial_files_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Rename partial files",
+ session_get_rename_partial_files
+ (s));
+ widget_set_json_key(w, SGET_RENAME_PARTIAL_FILES);
+
+ w = priv->trash_original_torrent_files_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Trash original torrent files",
+ session_get_trash_original_torrent_files
+ (s));
+ widget_set_json_key(w, SGET_TRASH_ORIGINAL_TORRENT_FILES);
+
+ w = priv->start_added_torrent_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Start added torrents",
+ session_get_start_added_torrents
+ (s));
+ widget_set_json_key(w, SGET_START_ADDED_TORRENTS);
+
+ return t;
+}
+
+static GObject *trg_remote_prefs_dialog_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam
+ * construct_params)
+{
+ GObject *object;
+ TrgRemotePrefsDialogPrivate *priv;
+ JsonObject *session;
+ GtkWidget *notebook;
+
+ object = G_OBJECT_CLASS
+ (trg_remote_prefs_dialog_parent_class)->constructor(type,
+ n_construct_properties,
+ construct_params);
+ priv = TRG_REMOTE_PREFS_DIALOG_GET_PRIVATE(object);
+ session = priv->client->session;
+
+ gtk_window_set_title(GTK_WINDOW(object), "Remote Preferences");
+ gtk_window_set_transient_for(GTK_WINDOW(object),
+ GTK_WINDOW(priv->parent));
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(object), TRUE);
+
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE);
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_APPLY,
+ GTK_RESPONSE_ACCEPT);
+
+ gtk_container_set_border_width(GTK_CONTAINER(object), GUI_PAD);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(object),
+ GTK_RESPONSE_CLOSE);
+
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(object),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CLOSE, -1);
+
+ g_signal_connect(G_OBJECT(object),
+ "response",
+ G_CALLBACK(trg_remote_prefs_response_cb), NULL);
+
+ notebook = gtk_notebook_new();
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_rprefs_generalPage
+ (TRG_REMOTE_PREFS_DIALOG(object),
+ session), gtk_label_new("General"));
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_rprefs_connPage
+ (TRG_REMOTE_PREFS_DIALOG(object),
+ session), gtk_label_new("Connections"));
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_rprefs_limitsPage
+ (TRG_REMOTE_PREFS_DIALOG(object),
+ session), gtk_label_new("Limits"));
+
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(object)->vbox), notebook,
+ TRUE, TRUE, 0);
+
+ return object;
+}
+
+static void
+trg_remote_prefs_dialog_class_init(TrgRemotePrefsDialogClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(TrgRemotePrefsDialogPrivate));
+
+ object_class->constructor = trg_remote_prefs_dialog_constructor;
+ object_class->get_property = trg_remote_prefs_dialog_get_property;
+ object_class->set_property = trg_remote_prefs_dialog_set_property;
+
+ g_object_class_install_property(object_class,
+ PROP_CLIENT,
+ g_param_spec_pointer
+ ("trg-client", "TClient",
+ "Client",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property(object_class,
+ PROP_PARENT,
+ g_param_spec_object
+ ("parent-window", "Parent window",
+ "Parent window",
+ TRG_TYPE_MAIN_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+}
+
+static void trg_remote_prefs_dialog_init(TrgRemotePrefsDialog * self)
+{
+}
+
+TrgRemotePrefsDialog *trg_remote_prefs_dialog_get_instance(TrgMainWindow *
+ parent,
+ trg_client *
+ client)
+{
+ if (instance == NULL) {
+ instance = g_object_new(TRG_TYPE_REMOTE_PREFS_DIALOG,
+ "parent-window", parent,
+ "trg-client", client, NULL);
+ }
+
+ return TRG_REMOTE_PREFS_DIALOG(instance);
+}
diff --git a/src/trg-remote-prefs-dialog.h b/src/trg-remote-prefs-dialog.h
new file mode 100644
index 0000000..661ca24
--- /dev/null
+++ b/src/trg-remote-prefs-dialog.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_REMOTE_PREFS_DIALOG_H_
+#define TRG_REMOTE_PREFS_DIALOG_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-main-window.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_REMOTE_PREFS_DIALOG trg_remote_prefs_dialog_get_type()
+#define TRG_REMOTE_PREFS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialog))
+#define TRG_REMOTE_PREFS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialogClass))
+#define TRG_IS_REMOTE_PREFS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_REMOTE_PREFS_DIALOG))
+#define TRG_IS_REMOTE_PREFS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_REMOTE_PREFS_DIALOG))
+#define TRG_REMOTE_PREFS_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_REMOTE_PREFS_DIALOG, TrgRemotePrefsDialogClass))
+ typedef struct {
+ GtkDialog parent;
+} TrgRemotePrefsDialog;
+
+typedef struct {
+ GtkDialogClass parent_class;
+} TrgRemotePrefsDialogClass;
+
+GType trg_remote_prefs_dialog_get_type(void);
+
+TrgRemotePrefsDialog *trg_remote_prefs_dialog_get_instance(TrgMainWindow *
+ parent,
+ trg_client *
+ client);
+
+G_END_DECLS
+#endif /* TRG_REMOTE_PREFS_DIALOG_H_ */
diff --git a/src/trg-state-selector.c b/src/trg-state-selector.c
new file mode 100644
index 0000000..325b363
--- /dev/null
+++ b/src/trg-state-selector.c
@@ -0,0 +1,161 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "torrent.h"
+#include "trg-state-selector.h"
+
+enum {
+ SELECTOR_STATE_CHANGED,
+ SELECTOR_SIGNAL_COUNT
+};
+
+static guint signals[SELECTOR_SIGNAL_COUNT] = { 0 };
+
+G_DEFINE_TYPE(TrgStateSelector, trg_state_selector, GTK_TYPE_TREE_VIEW)
+#define TRG_STATE_SELECTOR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_STATE_SELECTOR, TrgStateSelectorPrivate))
+typedef struct _TrgStateSelectorPrivate TrgStateSelectorPrivate;
+
+struct _TrgStateSelectorPrivate {
+ guint flag;
+};
+
+guint32 trg_state_selector_get_flag(TrgStateSelector * s)
+{
+ TrgStateSelectorPrivate *priv = TRG_STATE_SELECTOR_GET_PRIVATE(s);
+ return priv->flag;
+}
+
+static void trg_state_selector_class_init(TrgStateSelectorClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ signals[SELECTOR_STATE_CHANGED] =
+ g_signal_new("torrent-state-changed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(TrgStateSelectorClass,
+ torrent_state_changed), NULL,
+ NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE,
+ 1, G_TYPE_UINT);
+
+ g_type_class_add_private(klass, sizeof(TrgStateSelectorPrivate));
+}
+
+static void state_selection_changed(GtkTreeSelection * selection,
+ gpointer data)
+{
+ TrgStateSelectorPrivate *priv;
+ GtkTreeIter iter;
+ GtkTreeView *tv;
+ GtkTreeModel *stateModel;
+
+ priv = TRG_STATE_SELECTOR_GET_PRIVATE(data);
+
+ tv = gtk_tree_selection_get_tree_view(selection);
+ stateModel = gtk_tree_view_get_model(tv);
+
+ if (gtk_tree_selection_get_selected(selection, &stateModel, &iter))
+ gtk_tree_model_get(stateModel, &iter, STATE_SELECTOR_BIT,
+ &(priv->flag), -1);
+ else
+ priv->flag = 0;
+
+ g_signal_emit(TRG_STATE_SELECTOR(data),
+ signals[SELECTOR_STATE_CHANGED], 0, priv->flag);
+}
+
+static void trg_state_selector_add_state(GtkListStore * model,
+ GtkTreeIter * iter, gchar * icon,
+ gchar * name, guint32 flag)
+{
+ gtk_list_store_append(model, iter);
+ gtk_list_store_set(model, iter,
+ STATE_SELECTOR_ICON, icon,
+ STATE_SELECTOR_NAME, name,
+ STATE_SELECTOR_BIT, flag, -1);
+}
+
+static void trg_state_selector_init(TrgStateSelector * self)
+{
+ TrgStateSelectorPrivate *priv;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ priv = TRG_STATE_SELECTOR_GET_PRIVATE(self);
+ priv->flag = 0;
+
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self), FALSE);
+
+ column = gtk_tree_view_column_new();
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ g_object_set(renderer, "stock-size", 4, NULL);
+ gtk_tree_view_column_set_attributes(column, renderer, "stock-id",
+ 0, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
+
+ gtk_tree_view_append_column(GTK_TREE_VIEW(self), column);
+
+ store =
+ gtk_list_store_new(STATE_SELECTOR_COLUMNS, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_UINT);
+
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_ABOUT, "All", 0);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_GO_DOWN,
+ "Downloading", TORRENT_FLAG_DOWNLOADING);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_MEDIA_PAUSE,
+ "Paused", TORRENT_FLAG_PAUSED);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_REFRESH,
+ "Checking", TORRENT_FLAG_CHECKING);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_APPLY,
+ "Complete", TORRENT_FLAG_COMPLETE);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_SELECT_ALL,
+ "Incomplete", TORRENT_FLAG_INCOMPLETE);
+ trg_state_selector_add_state(store, &iter, GTK_STOCK_GO_UP,
+ "Seeding", TORRENT_FLAG_SEEDING);
+ trg_state_selector_add_state(store, &iter,
+ GTK_STOCK_DIALOG_WARNING, "Error",
+ TORRENT_FLAG_ERROR);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(self), GTK_TREE_MODEL(store));
+ gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(self), TRUE);
+
+ gtk_widget_set_size_request(GTK_WIDGET(self), 120, -1);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
+
+ g_signal_connect(G_OBJECT(selection), "changed",
+ G_CALLBACK(state_selection_changed), self);
+}
+
+TrgStateSelector *trg_state_selector_new(void)
+{
+ return g_object_new(TRG_TYPE_STATE_SELECTOR, NULL);
+}
diff --git a/src/trg-state-selector.h b/src/trg-state-selector.h
new file mode 100644
index 0000000..03d1070
--- /dev/null
+++ b/src/trg-state-selector.h
@@ -0,0 +1,63 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_STATE_LIST_H_
+#define TRG_STATE_LIST_H_
+
+#include <glib-object.h>
+
+enum {
+ STATE_SELECTOR_ICON,
+ STATE_SELECTOR_NAME,
+ STATE_SELECTOR_BIT,
+ STATE_SELECTOR_COLUMNS
+};
+
+G_BEGIN_DECLS
+#define TRG_TYPE_STATE_SELECTOR trg_state_selector_get_type()
+#define TRG_STATE_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_STATE_SELECTOR, TrgStateSelector))
+#define TRG_STATE_SELECTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_STATE_SELECTOR, TrgStateSelectorClass))
+#define TRG_IS_STATE_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_STATE_SELECTOR))
+#define TRG_IS_STATE_SELECTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_STATE_SELECTOR))
+#define TRG_STATE_SELECTOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_STATE_SELECTOR, TrgStateSelectorClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgStateSelector;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+
+ /* SIGNALS */
+
+ void (*torrent_state_changed) (TrgStateSelector * selector,
+ guint flag, gpointer data);
+} TrgStateSelectorClass;
+
+GType trg_state_selector_get_type(void);
+TrgStateSelector *trg_state_selector_new(void);
+
+G_END_DECLS guint32 trg_state_selector_get_flag(TrgStateSelector * s);
+
+#endif /* TRG_STATE_LIST_H_ */
diff --git a/src/trg-status-bar.c b/src/trg-status-bar.c
new file mode 100644
index 0000000..f7c5bd5
--- /dev/null
+++ b/src/trg-status-bar.c
@@ -0,0 +1,130 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+
+#include "trg-status-bar.h"
+#include "trg-torrent-model.h"
+#include "session-get.h"
+#include "util.h"
+
+G_DEFINE_TYPE(TrgStatusBar, trg_status_bar, GTK_TYPE_STATUSBAR)
+#define TRG_STATUS_BAR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_STATUS_BAR, TrgStatusBarPrivate))
+typedef struct _TrgStatusBarPrivate TrgStatusBarPrivate;
+
+struct _TrgStatusBarPrivate {
+ guint connectionCtx;
+ guint countSpeedsCtx;
+ GtkStatusIcon *icon;
+};
+
+static void trg_status_bar_class_init(TrgStatusBarClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgStatusBarPrivate));
+}
+
+static void trg_status_bar_init(TrgStatusBar * self)
+{
+ TrgStatusBarPrivate *priv = TRG_STATUS_BAR_GET_PRIVATE(self);
+
+ priv->connectionCtx =
+ gtk_statusbar_get_context_id(GTK_STATUSBAR(self), "connection");
+ priv->countSpeedsCtx =
+ gtk_statusbar_get_context_id(GTK_STATUSBAR(self),
+ "counts and speeds");
+}
+
+void trg_status_bar_push_connection_msg(TrgStatusBar * sb,
+ const gchar * msg)
+{
+ TrgStatusBarPrivate *priv;
+
+ priv = TRG_STATUS_BAR_GET_PRIVATE(sb);
+
+ gtk_status_icon_set_tooltip(priv->icon, msg);
+ gtk_statusbar_pop(GTK_STATUSBAR(sb), priv->connectionCtx);
+ gtk_statusbar_push(GTK_STATUSBAR(sb), priv->connectionCtx, msg);
+}
+
+void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session)
+{
+ gchar *statusMsg;
+ float version;
+ int revision;
+
+ session_get_version(session, &version, &revision);
+ statusMsg =
+ g_strdup_printf
+ ("Connected to Transmission %g (r%d), getting torrents...",
+ version, revision);
+ g_printf("%s\n", statusMsg);
+ trg_status_bar_push_connection_msg(sb, statusMsg);
+ g_free(statusMsg);
+
+}
+
+void trg_status_bar_update(TrgStatusBar * sb,
+ TrgTorrentModelClassUpdateStats * stats)
+{
+ TrgStatusBarPrivate *priv;
+ gchar *statusBarUpdate;
+ gchar downRateTotalString[32], upRateTotalString[32];
+
+ priv = TRG_STATUS_BAR_GET_PRIVATE(sb);
+
+ trg_strlspeed(downRateTotalString,
+ stats->downRateTotal / KILOBYTE_FACTOR);
+ trg_strlspeed(upRateTotalString, stats->upRateTotal / KILOBYTE_FACTOR);
+
+ statusBarUpdate =
+ g_strdup_printf
+ ("%d torrents .. Down %s, Up %s .. %d seeding, %d downloading, %d paused",
+ stats->count,
+ downRateTotalString, upRateTotalString,
+ stats->seeding, stats->down, stats->paused);
+ gtk_statusbar_pop(GTK_STATUSBAR(sb), priv->countSpeedsCtx);
+ gtk_statusbar_push(GTK_STATUSBAR(sb),
+ priv->countSpeedsCtx, statusBarUpdate);
+ g_free(statusBarUpdate);
+
+ if (priv->icon != NULL) {
+ gchar *toolTipUpdate =
+ g_strdup_printf("%d torrents: %s down/%s up",
+ stats->count, downRateTotalString,
+ upRateTotalString);
+ gtk_status_icon_set_tooltip(priv->icon, toolTipUpdate);
+ g_free(toolTipUpdate);
+ }
+}
+
+
+TrgStatusBar *trg_status_bar_new(GtkStatusIcon * icon)
+{
+ TrgStatusBarPrivate *priv;
+ GObject *obj;
+
+ obj = g_object_new(TRG_TYPE_STATUS_BAR, NULL);
+
+ priv = TRG_STATUS_BAR_GET_PRIVATE(obj);
+ priv->icon = icon;
+
+ return TRG_STATUS_BAR(obj);
+}
diff --git a/src/trg-status-bar.h b/src/trg-status-bar.h
new file mode 100644
index 0000000..ed96505
--- /dev/null
+++ b/src/trg-status-bar.h
@@ -0,0 +1,60 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_STATUS_BAR_H_
+#define TRG_STATUS_BAR_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-torrent-model.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_STATUS_BAR trg_status_bar_get_type()
+#define TRG_STATUS_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_STATUS_BAR, TrgStatusBar))
+#define TRG_STATUS_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_STATUS_BAR, TrgStatusBarClass))
+#define TRG_IS_STATUS_BAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_STATUS_BAR))
+#define TRG_IS_STATUS_BAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_STATUS_BAR))
+#define TRG_STATUS_BAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_STATUS_BAR, TrgStatusBarClass))
+ typedef struct {
+ GtkStatusbar parent;
+} TrgStatusBar;
+
+typedef struct {
+ GtkStatusbarClass parent_class;
+} TrgStatusBarClass;
+
+GType trg_status_bar_get_type(void);
+
+TrgStatusBar *trg_status_bar_new(GtkStatusIcon * icon);
+
+G_END_DECLS
+ void trg_status_bar_update(TrgStatusBar * sb,
+ TrgTorrentModelClassUpdateStats * stats);
+void trg_status_bar_connect(TrgStatusBar * sb, JsonObject * session);
+void trg_status_bar_push_connection_msg(TrgStatusBar * sb,
+ const gchar * msg);
+
+#endif /* TRG_STATUS_BAR_H_ */
diff --git a/src/trg-toolbar.c b/src/trg-toolbar.c
new file mode 100644
index 0000000..672b30f
--- /dev/null
+++ b/src/trg-toolbar.c
@@ -0,0 +1,267 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include "trg-toolbar.h"
+
+enum {
+ PROP_0,
+ PROP_CONNECT_BUTTON,
+ PROP_DISCONNECT_BUTTON,
+ PROP_ADD_BUTTON,
+ PROP_ADD_URL_BUTTON,
+ PROP_REMOVE_BUTTON,
+ PROP_DELETE_BUTTON,
+ PROP_RESUME_BUTTON,
+ PROP_PAUSE_BUTTON,
+ /*PROP_VERIFY_BUTTON, */
+ PROP_PROPS_BUTTON,
+ PROP_REMOTE_PREFS_BUTTON,
+ PROP_LOCAL_PREFS_BUTTON
+};
+
+G_DEFINE_TYPE(TrgToolbar, trg_toolbar, GTK_TYPE_TOOLBAR)
+#define TRG_TOOLBAR_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_TOOLBAR, TrgToolbarPrivate))
+typedef struct _TrgToolbarPrivate TrgToolbarPrivate;
+
+struct _TrgToolbarPrivate {
+ GtkWidget *tb_connect;
+ GtkWidget *tb_disconnect;
+ GtkWidget *tb_add;
+ /*GtkWidget *tb_add_url; */
+ GtkWidget *tb_remove;
+ GtkWidget *tb_delete;
+ GtkWidget *tb_resume;
+ GtkWidget *tb_pause;
+ /*GtkWidget *tb_verify; */
+ GtkWidget *tb_props;
+ GtkWidget *tb_remote_prefs;
+ GtkWidget *tb_local_prefs;
+};
+
+static void
+trg_toolbar_get_property(GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgToolbarPrivate *priv = TRG_TOOLBAR_GET_PRIVATE(object);
+
+ switch (property_id) {
+ case PROP_CONNECT_BUTTON:
+ g_value_set_object(value, priv->tb_connect);
+ break;
+ case PROP_DISCONNECT_BUTTON:
+ g_value_set_object(value, priv->tb_disconnect);
+ break;
+ case PROP_ADD_BUTTON:
+ g_value_set_object(value, priv->tb_add);
+ break;
+/* case PROP_ADD_URL_BUTTON:
+ g_value_set_object(value, priv->tb_add_url);
+ break;*/
+ case PROP_REMOVE_BUTTON:
+ g_value_set_object(value, priv->tb_remove);
+ break;
+ case PROP_DELETE_BUTTON:
+ g_value_set_object(value, priv->tb_delete);
+ break;
+ case PROP_RESUME_BUTTON:
+ g_value_set_object(value, priv->tb_resume);
+ break;
+ case PROP_PAUSE_BUTTON:
+ g_value_set_object(value, priv->tb_pause);
+ break;
+ /*case PROP_VERIFY_BUTTON:
+ g_value_set_object(value, priv->tb_verify);
+ break; */
+ case PROP_PROPS_BUTTON:
+ g_value_set_object(value, priv->tb_props);
+ break;
+ case PROP_REMOTE_PREFS_BUTTON:
+ g_value_set_object(value, priv->tb_remote_prefs);
+ break;
+ case PROP_LOCAL_PREFS_BUTTON:
+ g_value_set_object(value, priv->tb_local_prefs);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+trg_toolbar_install_widget_prop(GObjectClass * class, guint propId,
+ const gchar * name, const gchar * nick)
+{
+ g_object_class_install_property(class,
+ propId,
+ g_param_spec_object(name,
+ nick,
+ nick,
+ GTK_TYPE_WIDGET,
+ G_PARAM_READABLE
+ |
+ G_PARAM_STATIC_NAME
+ |
+ G_PARAM_STATIC_NICK
+ |
+ G_PARAM_STATIC_BLURB));
+}
+
+static void trg_toolbar_class_init(TrgToolbarClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ object_class->get_property = trg_toolbar_get_property;
+
+ trg_toolbar_install_widget_prop(object_class, PROP_CONNECT_BUTTON,
+ "connect-button", "Connect Button");
+ trg_toolbar_install_widget_prop(object_class,
+ PROP_DISCONNECT_BUTTON,
+ "disconnect-button",
+ "Disconnect Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_ADD_BUTTON,
+ "add-button", "Add Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_ADD_URL_BUTTON,
+ "add-url-button", "Add URL Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_REMOVE_BUTTON,
+ "remove-button", "Remove Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_DELETE_BUTTON,
+ "delete-button", "Delete Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_RESUME_BUTTON,
+ "resume-button", "Resume Button");
+ trg_toolbar_install_widget_prop(object_class, PROP_PAUSE_BUTTON,
+ "pause-button", "Pause Button");
+ /*trg_toolbar_install_widget_prop(object_class, PROP_VERIFY_BUTTON,
+ "verify-button", "Verify Button"); */
+ trg_toolbar_install_widget_prop(object_class, PROP_PROPS_BUTTON,
+ "props-button", "Props Button");
+ trg_toolbar_install_widget_prop(object_class,
+ PROP_REMOTE_PREFS_BUTTON,
+ "remote-prefs-button",
+ "Remote Prefs Button");
+ trg_toolbar_install_widget_prop(object_class,
+ PROP_LOCAL_PREFS_BUTTON,
+ "local-prefs-button",
+ "Local Prefs Button");
+
+ g_type_class_add_private(klass, sizeof(TrgToolbarPrivate));
+}
+
+static
+GtkWidget *trg_toolbar_item_new(TrgToolbar * toolbar, gchar * text,
+ int *index, gchar * icon,
+ gboolean sensitive)
+{
+ GtkToolItem *w = gtk_tool_button_new_from_stock(icon);
+ gtk_widget_set_sensitive(GTK_WIDGET(w), sensitive);
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), w, (*index)++);
+ return GTK_WIDGET(w);
+}
+
+void trg_toolbar_connected_change(TrgToolbar * tb, gboolean connected)
+{
+ TrgToolbarPrivate *priv = TRG_TOOLBAR_GET_PRIVATE(tb);
+
+ gtk_widget_set_sensitive(priv->tb_add, connected);
+ /*gtk_widget_set_sensitive(priv->tb_add_url, connected); */
+ gtk_widget_set_sensitive(priv->tb_connect, !connected);
+ gtk_widget_set_sensitive(priv->tb_disconnect, connected);
+ gtk_widget_set_sensitive(priv->tb_remote_prefs, connected);
+}
+
+void trg_toolbar_torrent_actions_sensitive(TrgToolbar * tb,
+ gboolean sensitive)
+{
+ TrgToolbarPrivate *priv = TRG_TOOLBAR_GET_PRIVATE(tb);
+
+ gtk_widget_set_sensitive(priv->tb_props, sensitive);
+ gtk_widget_set_sensitive(priv->tb_remove, sensitive);
+ gtk_widget_set_sensitive(priv->tb_delete, sensitive);
+ gtk_widget_set_sensitive(priv->tb_resume, sensitive);
+ gtk_widget_set_sensitive(priv->tb_pause, sensitive);
+ /*gtk_widget_set_sensitive(priv->tb_verify, sensitive); */
+}
+
+static void trg_toolbar_init(TrgToolbar * self)
+{
+ GtkToolItem *separator;
+ TrgToolbarPrivate *priv;
+ int position = 0;
+
+ priv = TRG_TOOLBAR_GET_PRIVATE(self);
+
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(self),
+ GTK_ORIENTATION_HORIZONTAL);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(self),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_toolbar_set_style(GTK_TOOLBAR(self), GTK_TOOLBAR_ICONS);
+
+ priv->tb_connect =
+ trg_toolbar_item_new(self, "Connect", &position,
+ GTK_STOCK_CONNECT, TRUE);
+ priv->tb_disconnect =
+ trg_toolbar_item_new(self, "Disconnect", &position,
+ GTK_STOCK_DISCONNECT, FALSE);
+ priv->tb_add =
+ trg_toolbar_item_new(self, "Add", &position, GTK_STOCK_ADD, FALSE);
+
+ /*priv->tb_add_url =
+ trg_toolbar_item_new(self, "Add URL", 3, GTK_STOCK_ADD, FALSE); */
+
+ separator = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(self), separator, position++);
+
+ priv->tb_resume =
+ trg_toolbar_item_new(self, "Resume", &position,
+ GTK_STOCK_MEDIA_PLAY, FALSE);
+ priv->tb_pause =
+ trg_toolbar_item_new(self, "Pause", &position,
+ GTK_STOCK_MEDIA_PAUSE, FALSE);
+
+ priv->tb_props =
+ trg_toolbar_item_new(self, "Properties", &position,
+ GTK_STOCK_PROPERTIES, FALSE);
+
+ priv->tb_remove =
+ trg_toolbar_item_new(self, "Remove", &position,
+ GTK_STOCK_REMOVE, FALSE);
+
+ priv->tb_delete =
+ trg_toolbar_item_new(self, "Remove with data",
+ &position, GTK_STOCK_DELETE, FALSE);
+
+ /*priv->tb_verify =
+ trg_toolbar_item_new(self, "Verify", 11,
+ GTK_STOCK_REFRESH, FALSE); */
+
+ separator = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(self), separator, position++);
+
+ priv->tb_local_prefs =
+ trg_toolbar_item_new(self, "Local Preferences", &position,
+ GTK_STOCK_PREFERENCES, TRUE);
+
+ priv->tb_remote_prefs =
+ trg_toolbar_item_new(self, "Remote Preferences",
+ &position, GTK_STOCK_NETWORK, FALSE);
+}
+
+TrgToolbar *trg_toolbar_new(void)
+{
+ return g_object_new(TRG_TYPE_TOOLBAR, NULL);
+}
diff --git a/src/trg-toolbar.h b/src/trg-toolbar.h
new file mode 100644
index 0000000..624f33e
--- /dev/null
+++ b/src/trg-toolbar.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TOOLBAR_H_
+#define TRG_TOOLBAR_H_
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TOOLBAR trg_toolbar_get_type()
+#define TRG_TOOLBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TOOLBAR, TrgToolbar))
+#define TRG_TOOLBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TOOLBAR, TrgToolbarClass))
+#define TRG_IS_TOOLBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TOOLBAR))
+#define TRG_IS_TOOLBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TOOLBAR))
+#define TRG_TOOLBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TOOLBAR, TrgToolbarClass))
+ typedef struct {
+ GtkToolbar parent;
+} TrgToolbar;
+
+typedef struct {
+ GtkToolbarClass parent_class;
+} TrgToolbarClass;
+
+GType trg_toolbar_get_type(void);
+
+TrgToolbar *trg_toolbar_new(void);
+
+G_END_DECLS
+ void trg_toolbar_torrent_actions_sensitive(TrgToolbar * mb,
+ gboolean sensitive);
+void trg_toolbar_connected_change(TrgToolbar * mb, gboolean connected);
+
+#endif /* TRG_TOOLBAR_H_ */
diff --git a/src/trg-torrent-add-url-dialog.c b/src/trg-torrent-add-url-dialog.c
new file mode 100644
index 0000000..b416cec
--- /dev/null
+++ b/src/trg-torrent-add-url-dialog.c
@@ -0,0 +1,129 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-client.h"
+#include "trg-main-window.h"
+#include "trg-torrent-add-url-dialog.h"
+#include "hig.h"
+#include "requests.h"
+#include "dispatch.h"
+
+G_DEFINE_TYPE(TrgTorrentAddUrlDialog, trg_torrent_add_url_dialog,
+ GTK_TYPE_DIALOG)
+#define TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialogPrivate))
+typedef struct _TrgTorrentAddUrlDialogPrivate
+ TrgTorrentAddUrlDialogPrivate;
+
+struct _TrgTorrentAddUrlDialogPrivate {
+ trg_client *client;
+ TrgMainWindow *win;
+ GtkWidget *urlEntry, *startCheck, *addButton;
+};
+
+static void
+trg_torrent_add_url_dialog_class_init(TrgTorrentAddUrlDialogClass * klass)
+{
+ g_type_class_add_private(klass, sizeof(TrgTorrentAddUrlDialogPrivate));
+}
+
+static void
+trg_torrent_add_url_response_cb(GtkDialog * dlg, gint res_id,
+ gpointer data)
+{
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(dlg);
+ if (res_id == GTK_RESPONSE_ACCEPT) {
+ JsonNode *request =
+ torrent_add_url(gtk_entry_get_text(GTK_ENTRY(priv->urlEntry)),
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(priv->startCheck)));
+ dispatch_async(priv->client, request,
+ on_generic_interactive_action, data);
+ }
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+}
+
+static void url_entry_changed(GtkWidget * w, gpointer data)
+{
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(data);
+ gtk_widget_set_sensitive(priv->addButton,
+ gtk_entry_get_text_length(GTK_ENTRY(w)) > 0);
+}
+
+static void trg_torrent_add_url_dialog_init(TrgTorrentAddUrlDialog * self)
+{
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(self);
+ GtkWidget *w, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ w = priv->urlEntry = gtk_entry_new();
+ g_signal_connect(w, "changed", G_CALLBACK(url_entry_changed), self);
+ w = hig_workarea_add_row(t, &row, "URL:", w, NULL);
+
+ priv->startCheck =
+ hig_workarea_add_wide_checkbutton(t, &row, "Start Paused", FALSE);
+
+ gtk_window_set_title(GTK_WINDOW(self), "Add torrent from URL");
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(self), TRUE);
+
+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CANCEL);
+ priv->addButton =
+ gtk_dialog_add_button(GTK_DIALOG(self), GTK_STOCK_ADD,
+ GTK_RESPONSE_ACCEPT);
+ gtk_widget_set_sensitive(priv->addButton, FALSE);
+
+ gtk_container_set_border_width(GTK_CONTAINER(self), GUI_PAD);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(self), GTK_RESPONSE_ACCEPT);
+
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(self),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CANCEL, -1);
+
+ gtk_container_set_border_width(GTK_CONTAINER(t), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(self)->vbox), t, TRUE, TRUE, 0);
+}
+
+TrgTorrentAddUrlDialog *trg_torrent_add_url_dialog_new(TrgMainWindow * win,
+ trg_client * client)
+{
+ GObject *obj = g_object_new(TRG_TYPE_TORRENT_ADD_URL_DIALOG, NULL);
+ TrgTorrentAddUrlDialogPrivate *priv =
+ TRG_TORRENT_ADD_URL_DIALOG_GET_PRIVATE(obj);
+
+ priv->client = client;
+ priv->win = win;
+
+ gtk_window_set_transient_for(GTK_WINDOW(obj), GTK_WINDOW(win));
+ g_signal_connect(G_OBJECT(obj),
+ "response",
+ G_CALLBACK(trg_torrent_add_url_response_cb), win);
+
+ return TRG_TORRENT_ADD_URL_DIALOG(obj);
+}
diff --git a/src/trg-torrent-add-url-dialog.h b/src/trg-torrent-add-url-dialog.h
new file mode 100644
index 0000000..b2005ef
--- /dev/null
+++ b/src/trg-torrent-add-url-dialog.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TORRENT_ADD_URL_DIALOG_H_
+#define TRG_TORRENT_ADD_URL_DIALOG_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-client.h"
+#include "trg-main-window.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_ADD_URL_DIALOG trg_torrent_add_url_dialog_get_type()
+#define TRG_TORRENT_ADD_URL_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialog))
+#define TRG_TORRENT_ADD_URL_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialogClass))
+#define TRG_IS_TORRENT_ADD_URL_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_ADD_URL_DIALOG))
+#define TRG_IS_TORRENT_ADD_URL_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_ADD_URL_DIALOG))
+#define TRG_TORRENT_ADD_URL_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_ADD_URL_DIALOG, TrgTorrentAddUrlDialogClass))
+ typedef struct {
+ GtkDialog parent;
+} TrgTorrentAddUrlDialog;
+
+typedef struct {
+ GtkDialogClass parent_class;
+} TrgTorrentAddUrlDialogClass;
+
+GType trg_torrent_add_url_dialog_get_type(void);
+
+TrgTorrentAddUrlDialog *trg_torrent_add_url_dialog_new(TrgMainWindow * win,
+ trg_client *
+ client);
+
+G_END_DECLS
+#endif /* TRG_TORRENT_ADD_URL_DIALOG_H_ */
diff --git a/src/trg-torrent-model.c b/src/trg-torrent-model.c
new file mode 100644
index 0000000..3bf8229
--- /dev/null
+++ b/src/trg-torrent-model.c
@@ -0,0 +1,297 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "tpeer.h"
+#include "json.h"
+#include "trg-torrent-model.h"
+#include "protocol-constants.h"
+#include "trg-model.h"
+#include "util.h"
+
+enum {
+ TMODEL_TORRENT_COMPLETED,
+ TMODEL_SIGNAL_COUNT
+};
+
+static guint signals[TMODEL_SIGNAL_COUNT] = { 0 };
+
+G_DEFINE_TYPE(TrgTorrentModel, trg_torrent_model, GTK_TYPE_LIST_STORE)
+
+static guint32 torrent_get_flags(JsonObject * t, gint64 status,
+ TrgTorrentModelClassUpdateStats * stats);
+
+static void
+update_torrent_iter(gint64 serial, TrgTorrentModel * model,
+ GtkTreeIter * iter, JsonObject * t,
+ TrgTorrentModelClassUpdateStats * stats);
+
+static gboolean
+find_existing_torrent_item_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data);
+
+static gboolean
+find_existing_torrent_item(TrgTorrentModel * model, JsonObject * t,
+ GtkTreeIter * iter);
+
+static void trg_torrent_model_class_init(TrgTorrentModelClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ signals[TMODEL_TORRENT_COMPLETED] =
+ g_signal_new("torrent-completed",
+ G_TYPE_FROM_CLASS(object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(TrgTorrentModelClass,
+ torrent_completed), NULL,
+ NULL, g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+}
+
+static void trg_torrent_module_count_peers(TrgTorrentModel * model,
+ GtkTreeIter * iter,
+ JsonObject * t)
+{
+ JsonArray *peers;
+ gint seeders, leechers, j;
+
+ peers = torrent_get_peers(t);
+
+ seeders = 0;
+ leechers = 0;
+
+ for (j = 0; j < json_array_get_length(peers); j++) {
+ JsonObject *peer;
+
+ peer = json_node_get_object(json_array_get_element(peers, j));
+
+ if (peer_get_is_downloading_from(peer))
+ seeders++;
+
+ if (peer_get_is_uploading_to(peer))
+ leechers++;
+ }
+
+ gtk_list_store_set(GTK_LIST_STORE(model), iter,
+ TORRENT_COLUMN_SEEDS, seeders,
+ TORRENT_COLUMN_LEECHERS, leechers, -1);
+}
+
+static void trg_torrent_model_init(TrgTorrentModel * self)
+{
+ GType column_types[TORRENT_COLUMN_COLUMNS];
+
+ column_types[TORRENT_COLUMN_ICON] = G_TYPE_STRING;
+ column_types[TORRENT_COLUMN_NAME] = G_TYPE_STRING;
+ column_types[TORRENT_COLUMN_SIZE] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_DONE] = G_TYPE_DOUBLE;
+ column_types[TORRENT_COLUMN_STATUS] = G_TYPE_STRING;
+ column_types[TORRENT_COLUMN_SEEDS] = G_TYPE_INT;
+ column_types[TORRENT_COLUMN_LEECHERS] = G_TYPE_INT;
+ column_types[TORRENT_COLUMN_DOWNSPEED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_UPSPEED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_ETA] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_UPLOADED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_DOWNLOADED] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_RATIO] = G_TYPE_DOUBLE;
+ column_types[TORRENT_COLUMN_ID] = G_TYPE_INT;
+ column_types[TORRENT_COLUMN_JSON] = G_TYPE_POINTER;
+ column_types[TORRENT_COLUMN_UPDATESERIAL] = G_TYPE_INT64;
+ column_types[TORRENT_COLUMN_FLAGS] = G_TYPE_INT;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ TORRENT_COLUMN_COLUMNS, column_types);
+}
+
+static guint32 torrent_get_flags(JsonObject * t, gint64 status,
+ TrgTorrentModelClassUpdateStats * stats)
+{
+ guint32 flags = 0;
+ switch (status) {
+ case STATUS_DOWNLOADING:
+ flags |= TORRENT_FLAG_DOWNLOADING;
+ stats->down++;
+ break;
+ case STATUS_PAUSED:
+ flags |= TORRENT_FLAG_PAUSED;
+ stats->paused++;
+ break;
+ case STATUS_SEEDING:
+ flags |= TORRENT_FLAG_SEEDING;
+ stats->seeding++;
+ break;
+ case STATUS_CHECKING:
+ flags |= TORRENT_FLAG_CHECKING;
+ break;
+ case STATUS_WAITING_TO_CHECK:
+ flags |= TORRENT_FLAG_WAITING_CHECK;
+ flags |= TORRENT_FLAG_CHECKING;
+ break;
+ }
+
+ if (torrent_get_is_finished(t) == TRUE)
+ flags |= TORRENT_FLAG_COMPLETE;
+ else
+ flags |= TORRENT_FLAG_INCOMPLETE;
+
+ if (strlen(torrent_get_errorstr(t)) > 0)
+ flags |= TORRENT_FLAG_ERROR;
+
+ return flags;
+}
+
+static void
+update_torrent_iter(gint64 serial, TrgTorrentModel * model,
+ GtkTreeIter * iter, JsonObject * t,
+ TrgTorrentModelClassUpdateStats * stats)
+{
+ guint lastFlags, newFlags;
+ gchar *statusString, *statusIcon;
+ gint64 downRate, upRate, downloaded, uploaded, id, status;
+
+ downRate = torrent_get_rate_down(t);
+ stats->downRateTotal += downRate;
+
+ upRate = torrent_get_rate_up(t);
+ stats->upRateTotal += upRate;
+
+ uploaded = torrent_get_uploaded(t);
+ downloaded = torrent_get_downloaded(t);
+
+ id = torrent_get_id(t);
+
+ status = torrent_get_status(t);
+ statusString = torrent_get_status_string(status);
+ newFlags = torrent_get_flags(t, status, stats);
+ statusIcon = torrent_get_status_icon(newFlags);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(model), iter,
+ TORRENT_COLUMN_FLAGS, &lastFlags, -1);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), iter,
+ TORRENT_COLUMN_ICON, statusIcon,
+ TORRENT_COLUMN_NAME, torrent_get_name(t),
+ TORRENT_COLUMN_SIZE, torrent_get_size(t),
+ TORRENT_COLUMN_DONE,
+ torrent_get_percent_done(t),
+ TORRENT_COLUMN_STATUS, statusString,
+ TORRENT_COLUMN_DOWNSPEED, downRate,
+ TORRENT_COLUMN_FLAGS, newFlags,
+ TORRENT_COLUMN_UPSPEED, upRate,
+ TORRENT_COLUMN_ETA, torrent_get_eta(t),
+ TORRENT_COLUMN_UPLOADED, uploaded,
+ TORRENT_COLUMN_DOWNLOADED, downloaded,
+ TORRENT_COLUMN_RATIO,
+ uploaded >
+ 0 ? (double) uploaded / (double) downloaded : 0,
+ TORRENT_COLUMN_ID, id, TORRENT_COLUMN_JSON, t,
+ TORRENT_COLUMN_UPDATESERIAL, serial, -1);
+
+
+ if ((lastFlags & TORRENT_FLAG_DOWNLOADING) ==
+ TORRENT_FLAG_DOWNLOADING
+ && (newFlags & TORRENT_FLAG_COMPLETE) == TORRENT_FLAG_COMPLETE)
+ g_signal_emit(model, signals[TMODEL_TORRENT_COMPLETED], 0, iter);
+
+ trg_torrent_module_count_peers(model, iter, t);
+
+ g_free(statusString);
+ g_free(statusIcon);
+}
+
+TrgTorrentModel *trg_torrent_model_new(void)
+{
+ return g_object_new(TRG_TYPE_TORRENT_MODEL, NULL);
+}
+
+static gboolean
+find_existing_torrent_item_foreachfunc(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter, gpointer data)
+{
+ struct idAndIter *ii;
+ gint currentId;
+
+ ii = (struct idAndIter *) data;
+
+ gtk_tree_model_get(model, iter, TORRENT_COLUMN_ID, &currentId, -1);
+ if (currentId == ii->id) {
+ ii->iter = iter;
+ return ii->found = TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+find_existing_torrent_item(TrgTorrentModel * model, JsonObject * t,
+ GtkTreeIter * iter)
+{
+ struct idAndIter ii;
+ ii.id = torrent_get_id(t);
+ ii.found = FALSE;
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model),
+ find_existing_torrent_item_foreachfunc, &ii);
+ if (ii.found == TRUE)
+ *iter = *(ii.iter);
+ return ii.found;
+}
+
+void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc,
+ JsonObject * response,
+ TrgTorrentModelClassUpdateStats * stats,
+ gboolean first)
+{
+ int i;
+ JsonArray *newTorrents;
+
+ newTorrents = get_torrents(get_arguments(response));
+
+ stats->count = json_array_get_length(newTorrents);
+
+ for (i = 0; i < stats->count; i++) {
+ GtkTreeIter iter;
+ JsonObject *t;
+
+ t = json_array_get_object_element(newTorrents, i);
+
+ if (first == TRUE
+ || find_existing_torrent_item(model, t, &iter) == FALSE)
+ gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+
+ update_torrent_iter(tc->updateSerial, model, &iter, t, stats);
+ }
+
+ json_array_ref(newTorrents);
+
+ if (tc->torrents != NULL)
+ json_array_unref(tc->torrents);
+
+ tc->torrents = newTorrents;
+
+ trg_model_remove_removed(GTK_LIST_STORE(model),
+ TORRENT_COLUMN_UPDATESERIAL,
+ tc->updateSerial);
+}
diff --git a/src/trg-torrent-model.h b/src/trg-torrent-model.h
new file mode 100644
index 0000000..1157474
--- /dev/null
+++ b/src/trg-torrent-model.h
@@ -0,0 +1,102 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TORRENT_MODEL_H_
+#define TRG_TORRENT_MODEL_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-client.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_MODEL trg_torrent_model_get_type()
+#define TRG_TORRENT_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_MODEL, TrgTorrentModel))
+#define TRG_TORRENT_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_MODEL, TrgTorrentModelClass))
+#define TRG_IS_TORRENT_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_MODEL))
+#define TRG_IS_TORRENT_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_MODEL))
+#define TRG_TORRENT_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_MODEL, TrgTorrentModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgTorrentModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+ void (*torrent_completed) (TrgTorrentModel * model,
+ GtkTreeIter * iter, JsonObject * t,
+ gpointer data);
+} TrgTorrentModelClass;
+
+typedef struct {
+ gint64 downRateTotal;
+ gint64 upRateTotal;
+ gint seeding;
+ gint down;
+ gint paused;
+ gint count;
+} TrgTorrentModelClassUpdateStats;
+
+GType trg_torrent_model_get_type(void);
+
+TrgTorrentModel *trg_torrent_model_new();
+
+G_END_DECLS
+ gboolean
+find_existing_peer_item(GtkListStore * model, JsonObject * p,
+ GtkTreeIter * iter);
+
+void trg_torrent_model_update(TrgTorrentModel * model, trg_client * tc,
+ JsonObject * response,
+ TrgTorrentModelClassUpdateStats * stats,
+ gboolean first);
+
+enum {
+ TORRENT_COLUMN_ICON,
+ TORRENT_COLUMN_NAME,
+ TORRENT_COLUMN_SIZE,
+ TORRENT_COLUMN_DONE,
+ TORRENT_COLUMN_STATUS,
+ TORRENT_COLUMN_SEEDS,
+ TORRENT_COLUMN_LEECHERS,
+ TORRENT_COLUMN_DOWNSPEED,
+ TORRENT_COLUMN_UPSPEED,
+ TORRENT_COLUMN_ETA,
+ TORRENT_COLUMN_UPLOADED,
+ TORRENT_COLUMN_DOWNLOADED,
+ TORRENT_COLUMN_RATIO,
+ TORRENT_COLUMN_ID,
+ TORRENT_COLUMN_JSON,
+ TORRENT_COLUMN_UPDATESERIAL,
+ TORRENT_COLUMN_FLAGS,
+ TORRENT_COLUMN_COLUMNS
+};
+
+struct idAndIter {
+ gint id;
+ GtkTreeIter *iter;
+ gboolean found;
+};
+
+#endif /* TRG_TORRENT_MODEL_H_ */
diff --git a/src/trg-torrent-props-dialog.c b/src/trg-torrent-props-dialog.c
new file mode 100644
index 0000000..1740502
--- /dev/null
+++ b/src/trg-torrent-props-dialog.c
@@ -0,0 +1,372 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "json.h"
+#include "dispatch.h"
+#include "trg-client.h"
+#include "requests.h"
+#include "protocol-constants.h"
+#include "trg-torrent-model.h"
+#include "trg-torrent-tree-view.h"
+#include "trg-torrent-props-dialog.h"
+#include "trg-main-window.h"
+#include "trg-json-widgets.h"
+#include "hig.h"
+
+G_DEFINE_TYPE(TrgTorrentPropsDialog, trg_torrent_props_dialog,
+ GTK_TYPE_DIALOG)
+
+enum {
+ PROP_0,
+ PROP_TREEVIEW,
+ PROP_PARENT_WINDOW
+};
+
+#define TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialogPrivate))
+typedef struct _TrgTorrentPropsDialogPrivate TrgTorrentPropsDialogPrivate;
+
+struct _TrgTorrentPropsDialogPrivate {
+ TrgTorrentTreeView *tv;
+ trg_client *client;
+ GtkWindow *parent;
+ JsonArray *targetIds;
+
+ GtkWidget *bandwidthPriorityCombo;
+ GtkWidget *down_limited_check, *down_limit_spin;
+ GtkWidget *up_limited_check, *up_limit_spin;
+ GtkWidget *seedRatioMode, *seedRatioLimit;
+ GtkWidget *honor_limits_check;
+ GtkWidget *peer_limit_spin;
+};
+
+static void
+trg_torrent_props_dialog_set_property(GObject * object,
+ guint prop_id,
+ const GValue * value,
+ GParamSpec * pspec)
+{
+ TrgTorrentPropsDialogPrivate *priv =
+ TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_PARENT_WINDOW:
+ priv->parent = g_value_get_object(value);
+ break;
+ case PROP_TREEVIEW:
+ priv->tv = g_value_get_object(value);
+ break;
+ }
+}
+
+static void
+trg_torrent_props_dialog_get_property(GObject * object,
+ guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ TrgTorrentPropsDialogPrivate *priv =
+ TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(object);
+
+ switch (prop_id) {
+ case PROP_TREEVIEW:
+ g_value_set_object(value, priv->tv);
+ break;
+ case PROP_PARENT_WINDOW:
+ g_value_set_object(value, priv->parent);
+ break;
+ }
+}
+
+static void
+trg_torrent_props_response_cb(GtkDialog * dlg, gint res_id, gpointer data)
+{
+ TrgTorrentPropsDialogPrivate *priv;
+ JsonNode *request;
+ JsonObject *args;
+
+ priv = TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(dlg);
+
+ if (res_id != GTK_RESPONSE_ACCEPT) {
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+ return;
+ }
+
+ request = torrent_set(priv->targetIds);
+ args = node_get_arguments(request);
+
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->honor_limits_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->down_limited_check), args);
+ gtk_toggle_button_json_out(GTK_TOGGLE_BUTTON
+ (priv->up_limited_check), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->down_limit_spin), args);
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON(priv->up_limit_spin),
+ args);
+ gtk_spin_button_json_double_out(GTK_SPIN_BUTTON
+ (priv->seedRatioLimit), args);
+ json_object_set_int_member(args, FIELD_SEED_RATIO_MODE,
+ gtk_combo_box_get_active(GTK_COMBO_BOX
+ (priv->
+ seedRatioMode)));
+ json_object_set_int_member(args, FIELD_BANDWIDTH_PRIORITY,
+ gtk_combo_box_get_active(GTK_COMBO_BOX
+ (priv->
+ bandwidthPriorityCombo))
+ + 1);
+
+ gtk_spin_button_json_int_out(GTK_SPIN_BUTTON
+ (priv->peer_limit_spin), args);
+
+ dispatch_async(priv->client, request,
+ on_generic_interactive_action, priv->parent);
+
+ gtk_widget_destroy(GTK_WIDGET(dlg));
+}
+
+static void seed_ratio_mode_changed_cb(GtkWidget * w, gpointer data)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(data),
+ gtk_combo_box_get_active(GTK_COMBO_BOX(w))
+ == 1 ? TRUE : FALSE);
+}
+
+static GtkWidget *trg_props_limitsPage(TrgTorrentPropsDialog * win,
+ JsonObject * json)
+{
+ TrgTorrentPropsDialogPrivate *priv =
+ TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(win);
+ GtkWidget *w, *tb, *t;
+ gint row = 0;
+
+ t = hig_workarea_create();
+
+ hig_workarea_add_section_title(t, &row, "Bandwidth");
+
+ w = priv->honor_limits_check =
+ hig_workarea_add_wide_checkbutton(t, &row,
+ "Honor global limits",
+ torrent_get_honors_session_limits
+ (json));
+ widget_set_json_key(w, FIELD_HONORS_SESSION_LIMITS);
+
+ w = priv->bandwidthPriorityCombo = gtk_combo_box_new_text();
+ widget_set_json_key(w, FIELD_BANDWIDTH_PRIORITY);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Low");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Normal");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "High");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w),
+ torrent_get_bandwidth_priority(json) + 1);
+ hig_workarea_add_row(t, &row, "Torrent priority:", w, NULL);
+
+ tb = priv->down_limited_check = gtk_check_button_new_with_mnemonic
+ ("Limit download speed (Kbps)");
+ widget_set_json_key(tb, FIELD_DOWNLOAD_LIMITED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ torrent_get_download_limited(json));
+
+ w = priv->down_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, FIELD_DOWNLOAD_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_download_limit(json));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ tb = priv->up_limited_check = gtk_check_button_new_with_mnemonic
+ ("Limit upload speed (Kbps)");
+ widget_set_json_key(tb, FIELD_UPLOAD_LIMITED);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tb),
+ torrent_get_upload_limited(json));
+
+ w = priv->up_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(w, FIELD_UPLOAD_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_upload_limit(json));
+ hig_workarea_add_row_w(t, &row, tb, w, NULL);
+
+ hig_workarea_add_section_title(t, &row, "Seeding");
+
+ w = priv->seedRatioMode = gtk_combo_box_new_text();
+ widget_set_json_key(GTK_WIDGET(w), FIELD_SEED_RATIO_MODE);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Use global settings");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w), "Stop seeding at ratio");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(w),
+ "Seed regardless of ratio");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(w),
+ torrent_get_seed_ratio_mode(json));
+ hig_workarea_add_row(t, &row, "Seed ratio mode:", w, NULL);
+
+ w = priv->seedRatioLimit =
+ gtk_spin_button_new_with_range(0, INT_MAX, 0.2);
+ gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
+ widget_set_json_key(GTK_WIDGET(w), FIELD_SEED_RATIO_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_seed_ratio_limit(json));
+ hig_workarea_add_row(t, &row, "Seed ratio limit:", w, w);
+ g_signal_connect(G_OBJECT(priv->seedRatioMode), "changed",
+ G_CALLBACK(seed_ratio_mode_changed_cb), w);
+
+ hig_workarea_add_section_title(t, &row, "Peers");
+
+ w = priv->peer_limit_spin =
+ gtk_spin_button_new_with_range(0, INT_MAX, 5);
+ widget_set_json_key(GTK_WIDGET(w), FIELD_PEER_LIMIT);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(w),
+ torrent_get_peer_limit(json));
+ hig_workarea_add_row(t, &row, "Peer limit:", w, w);
+
+ return t;
+}
+
+static GObject *trg_torrent_props_dialog_constructor(GType type,
+ guint
+ n_construct_properties,
+ GObjectConstructParam
+ * construct_params)
+{
+ GObject *object;
+ TrgTorrentPropsDialogPrivate *priv;
+ JsonObject *json;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gint rowCount;
+ GtkWidget *notebook;
+
+ object = G_OBJECT_CLASS
+ (trg_torrent_props_dialog_parent_class)->constructor(type,
+ n_construct_properties,
+ construct_params);
+
+ priv = TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(object);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->tv));
+ rowCount = gtk_tree_selection_count_selected_rows(selection);
+ get_first_selected(priv->tv, &iter, &json);
+ priv->targetIds = build_json_id_array(priv->tv);
+
+ if (rowCount > 1) {
+ gchar *windowTitle =
+ g_strdup_printf("Multiple (%d) torrent properties",
+ rowCount);
+ gtk_window_set_title(GTK_WINDOW(object), windowTitle);
+ g_free(windowTitle);
+ } else if (rowCount == 1) {
+ gtk_window_set_title(GTK_WINDOW(object), torrent_get_name(json));
+ }
+
+ gtk_window_set_transient_for(GTK_WINDOW(object), priv->parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(object), TRUE);
+
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE);
+ gtk_dialog_add_button(GTK_DIALOG(object), GTK_STOCK_APPLY,
+ GTK_RESPONSE_ACCEPT);
+
+ gtk_container_set_border_width(GTK_CONTAINER(object), GUI_PAD);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(object),
+ GTK_RESPONSE_CLOSE);
+
+ gtk_dialog_set_alternative_button_order(GTK_DIALOG(object),
+ GTK_RESPONSE_ACCEPT,
+ GTK_RESPONSE_CLOSE, -1);
+
+ g_signal_connect(G_OBJECT(object),
+ "response",
+ G_CALLBACK(trg_torrent_props_response_cb), NULL);
+
+ notebook = gtk_notebook_new();
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
+ trg_props_limitsPage
+ (TRG_TORRENT_PROPS_DIALOG(object), json),
+ gtk_label_new("Limits"));
+
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), GUI_PAD);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(object)->vbox), notebook,
+ TRUE, TRUE, 0);
+
+ return object;
+}
+
+static void
+trg_torrent_props_dialog_class_init(TrgTorrentPropsDialogClass * klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->constructor = trg_torrent_props_dialog_constructor;
+ object_class->set_property = trg_torrent_props_dialog_set_property;
+ object_class->get_property = trg_torrent_props_dialog_get_property;
+
+ g_type_class_add_private(klass, sizeof(TrgTorrentPropsDialogPrivate));
+
+ g_object_class_install_property(object_class,
+ PROP_TREEVIEW,
+ g_param_spec_object
+ ("torrent-tree-view",
+ "TrgTorrentTreeView",
+ "TrgTorrentTreeView",
+ TRG_TYPE_TORRENT_TREE_VIEW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
+ g_object_class_install_property(object_class,
+ PROP_PARENT_WINDOW,
+ g_param_spec_object
+ ("parent-window", "Parent window",
+ "Parent window", GTK_TYPE_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+}
+
+static void trg_torrent_props_dialog_init(TrgTorrentPropsDialog * self)
+{
+
+}
+
+TrgTorrentPropsDialog *trg_torrent_props_dialog_new(GtkWindow * window,
+ TrgTorrentTreeView *
+ treeview,
+ trg_client * client)
+{
+ GObject *obj;
+ TrgTorrentPropsDialogPrivate *priv;
+
+ obj = g_object_new(TRG_TYPE_TORRENT_PROPS_DIALOG,
+ "parent-window", window,
+ "torrent-tree-view", treeview, NULL);
+
+ priv = TRG_TORRENT_PROPS_DIALOG_GET_PRIVATE(obj);
+ priv->client = client;
+
+ return TRG_TORRENT_PROPS_DIALOG(obj);
+}
diff --git a/src/trg-torrent-props-dialog.h b/src/trg-torrent-props-dialog.h
new file mode 100644
index 0000000..ff7d710
--- /dev/null
+++ b/src/trg-torrent-props-dialog.h
@@ -0,0 +1,57 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef TRG_TORRENT_PROPS_DIALOG_H_
+#define TRG_TORRENT_PROPS_DIALOG_H_
+
+#include <glib-object.h>
+
+#include "trg-torrent-tree-view.h"
+#include "trg-client.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_PROPS_DIALOG trg_torrent_props_dialog_get_type()
+#define TRG_TORRENT_PROPS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialog))
+#define TRG_TORRENT_PROPS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialogClass))
+#define TRG_IS_TORRENT_PROPS_DIALOG(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_PROPS_DIALOG))
+#define TRG_IS_TORRENT_PROPS_DIALOG_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_PROPS_DIALOG))
+#define TRG_TORRENT_PROPS_DIALOG_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_PROPS_DIALOG, TrgTorrentPropsDialogClass))
+ typedef struct {
+ GtkDialog parent;
+} TrgTorrentPropsDialog;
+
+typedef struct {
+ GtkDialogClass parent_class;
+} TrgTorrentPropsDialogClass;
+
+GType trg_torrent_props_dialog_get_type(void);
+
+TrgTorrentPropsDialog *trg_torrent_props_dialog_new(GtkWindow * window,
+ TrgTorrentTreeView *
+ treeview,
+ trg_client * client);
+
+G_END_DECLS
+#endif /* TRG_TORRENT_PROPS_DIALOG_H_ */
diff --git a/src/trg-torrent-tree-view.c b/src/trg-torrent-tree-view.c
new file mode 100644
index 0000000..02010b0
--- /dev/null
+++ b/src/trg-torrent-tree-view.c
@@ -0,0 +1,122 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+
+#include "trg-tree-view.h"
+#include "trg-torrent-model.h"
+#include "trg-torrent-tree-view.h"
+
+G_DEFINE_TYPE(TrgTorrentTreeView, trg_torrent_tree_view,
+ TRG_TYPE_TREE_VIEW)
+
+static void
+trg_torrent_tree_view_class_init(TrgTorrentTreeViewClass * klass)
+{
+}
+
+static void trg_torrent_tree_view_init(TrgTorrentTreeView * tv)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW(tv),
+ TORRENT_COLUMN_ICON,
+ TORRENT_COLUMN_NAME, "Name", -1);
+ trg_tree_view_add_size_column(TRG_TREE_VIEW(tv), "Size",
+ TORRENT_COLUMN_SIZE, -1);
+ trg_tree_view_add_prog_column(TRG_TREE_VIEW(tv), "Done",
+ TORRENT_COLUMN_DONE, 70);
+ trg_tree_view_add_column(TRG_TREE_VIEW(tv), "Status",
+ TORRENT_COLUMN_STATUS);
+ trg_tree_view_add_column(TRG_TREE_VIEW(tv), "Seeds",
+ TORRENT_COLUMN_SEEDS);
+ trg_tree_view_add_column(TRG_TREE_VIEW(tv), "Leechers",
+ TORRENT_COLUMN_LEECHERS);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(tv), "Down Speed",
+ TORRENT_COLUMN_DOWNSPEED, -1);
+ trg_tree_view_add_speed_column(TRG_TREE_VIEW(tv), "Up Speed",
+ TORRENT_COLUMN_UPSPEED, -1);
+ trg_tree_view_add_eta_column(TRG_TREE_VIEW(tv), "ETA",
+ TORRENT_COLUMN_ETA, -1);
+ trg_tree_view_add_size_column(TRG_TREE_VIEW(tv), "Uploaded",
+ TORRENT_COLUMN_UPLOADED, -1);
+ trg_tree_view_add_size_column(TRG_TREE_VIEW(tv), "Downloaded",
+ TORRENT_COLUMN_DOWNLOADED, -1);
+ trg_tree_view_add_ratio_column(TRG_TREE_VIEW(tv), "Ratio",
+ TORRENT_COLUMN_RATIO, -1);
+}
+
+gint get_first_selected(TrgTorrentTreeView * view, GtkTreeIter * iter,
+ JsonObject ** json)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GList *selectionList;
+ GList *firstNode;
+ gint id = -1;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+ selectionList = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ if ((firstNode = g_list_first(selectionList)) != NULL) {
+ if (gtk_tree_model_get_iter(model, iter, firstNode->data) == TRUE) {
+ gtk_tree_model_get(model, iter,
+ TORRENT_COLUMN_JSON, json,
+ TORRENT_COLUMN_ID, &id, -1);
+ }
+ }
+
+ g_list_free(selectionList);
+
+ return id;
+}
+
+void
+trg_torrent_model_get_json_id_array_foreach(GtkTreeModel * model,
+ GtkTreePath * path,
+ GtkTreeIter * iter,
+ gpointer data)
+{
+ JsonArray *output = (JsonArray *) data;
+ gint id;
+ gtk_tree_model_get(model, iter, TORRENT_COLUMN_ID, &id, -1);
+ json_array_add_int_element(output, id);
+}
+
+JsonArray *build_json_id_array(TrgTorrentTreeView * tv)
+{
+ GtkTreeSelection *selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
+
+ JsonArray *ids = json_array_new();
+ gtk_tree_selection_selected_foreach(selection,
+ (GtkTreeSelectionForeachFunc)
+ trg_torrent_model_get_json_id_array_foreach,
+ ids);
+ return ids;
+}
+
+TrgTorrentTreeView *trg_torrent_tree_view_new(GtkTreeModel * model)
+{
+ GObject *obj = g_object_new(TRG_TYPE_TORRENT_TREE_VIEW, NULL);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), model);
+
+ return TRG_TORRENT_TREE_VIEW(obj);
+}
diff --git a/src/trg-torrent-tree-view.h b/src/trg-torrent-tree-view.h
new file mode 100644
index 0000000..b900985
--- /dev/null
+++ b/src/trg-torrent-tree-view.h
@@ -0,0 +1,58 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _TRG_TORRENT_TREE_VIEW_H_
+#define _TRG_TORRENT_TREE_VIEW_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+#include "trg-torrent-model.h"
+#include "trg-tree-view.h"
+#include "trg-state-selector.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TORRENT_TREE_VIEW trg_torrent_tree_view_get_type()
+#define TRG_TORRENT_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TORRENT_TREE_VIEW, TrgTorrentTreeView))
+#define TRG_TORRENT_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TORRENT_TREE_VIEW, TrgTorrentTreeViewClass))
+#define TRG_IS_TORRENT_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TORRENT_TREE_VIEW))
+#define TRG_IS_TORRENT_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TORRENT_TREE_VIEW))
+#define TRG_TORRENT_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TORRENT_TREE_VIEW, TrgTorrentTreeViewClass))
+ typedef struct {
+ TrgTreeView parent;
+} TrgTorrentTreeView;
+
+typedef struct {
+ TrgTreeViewClass parent_class;
+} TrgTorrentTreeViewClass;
+
+GType trg_torrent_tree_view_get_type(void);
+
+TrgTorrentTreeView *trg_torrent_tree_view_new(GtkTreeModel * model);
+JsonArray *build_json_id_array(TrgTorrentTreeView * tv);
+gint get_first_selected(TrgTorrentTreeView * view, GtkTreeIter * iter,
+ JsonObject ** json);
+
+G_END_DECLS
+#endif /* _TRG_TORRENT_TREE_VIEW_H_ */
diff --git a/src/trg-trackers-model.c b/src/trg-trackers-model.c
new file mode 100644
index 0000000..9a7a5e7
--- /dev/null
+++ b/src/trg-trackers-model.c
@@ -0,0 +1,72 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include <json-glib/json-glib.h>
+
+#include "torrent.h"
+#include "trg-trackers-model.h"
+
+G_DEFINE_TYPE(TrgTrackersModel, trg_trackers_model, GTK_TYPE_LIST_STORE)
+
+void trg_trackers_model_update(TrgTrackersModel * model, JsonObject * t)
+{
+ int j;
+ JsonArray *trackers;
+
+ gtk_list_store_clear(GTK_LIST_STORE(model));
+
+ trackers = torrent_get_trackers(t);
+ for (j = 0; j < json_array_get_length(trackers); j++) {
+ JsonObject *tracker =
+ json_node_get_object(json_array_get_element(trackers, j));
+ GtkTreeIter trackIter;
+ gtk_list_store_append(GTK_LIST_STORE(model), &trackIter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &trackIter,
+ TRACKERCOL_ICON, GTK_STOCK_NETWORK,
+ TRACKERCOL_TIER,
+ tracker_get_tier(tracker),
+ TRACKERCOL_ANNOUNCE,
+ tracker_get_announce(tracker),
+ TRACKERCOL_SCRAPE,
+ tracker_get_scrape(tracker), -1);
+ }
+}
+
+static void trg_trackers_model_class_init(TrgTrackersModelClass * klass)
+{
+}
+
+static void trg_trackers_model_init(TrgTrackersModel * self)
+{
+ GType column_types[TRACKERCOL_COLUMNS];
+
+ column_types[TRACKERCOL_ICON] = G_TYPE_STRING;
+ column_types[TRACKERCOL_TIER] = G_TYPE_INT;
+ column_types[TRACKERCOL_ANNOUNCE] = G_TYPE_STRING;
+ column_types[TRACKERCOL_SCRAPE] = G_TYPE_STRING;
+
+ gtk_list_store_set_column_types(GTK_LIST_STORE(self),
+ TRACKERCOL_COLUMNS, column_types);
+}
+
+TrgTrackersModel *trg_trackers_model_new(void)
+{
+ return g_object_new(TRG_TYPE_TRACKERS_MODEL, NULL);
+}
diff --git a/src/trg-trackers-model.h b/src/trg-trackers-model.h
new file mode 100644
index 0000000..2de1b78
--- /dev/null
+++ b/src/trg-trackers-model.h
@@ -0,0 +1,62 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_TRACKERS_MODEL_H_
+#define TRG_TRACKERS_MODEL_H_
+
+#include <glib-object.h>
+#include <json-glib/json-glib.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TRACKERS_MODEL trg_trackers_model_get_type()
+#define TRG_TRACKERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TRACKERS_MODEL, TrgTrackersModel))
+#define TRG_TRACKERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TRACKERS_MODEL, TrgTrackersModelClass))
+#define TRG_IS_TRACKERS_MODEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TRACKERS_MODEL))
+#define TRG_IS_TRACKERS_MODEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TRACKERS_MODEL))
+#define TRG_TRACKERS_MODEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TRACKERS_MODEL, TrgTrackersModelClass))
+ typedef struct {
+ GtkListStore parent;
+} TrgTrackersModel;
+
+typedef struct {
+ GtkListStoreClass parent_class;
+} TrgTrackersModelClass;
+
+GType trg_trackers_model_get_type(void);
+
+TrgTrackersModel *trg_trackers_model_new(void);
+
+G_END_DECLS
+ void
+trg_trackers_model_update(TrgTrackersModel * model, JsonObject * t);
+
+enum {
+ TRACKERCOL_ICON,
+ TRACKERCOL_TIER,
+ TRACKERCOL_ANNOUNCE,
+ TRACKERCOL_SCRAPE,
+ TRACKERCOL_COLUMNS
+};
+
+#endif /* TRG_TRACKERS_MODEL_H_ */
diff --git a/src/trg-trackers-tree-view.c b/src/trg-trackers-tree-view.c
new file mode 100644
index 0000000..6f8275f
--- /dev/null
+++ b/src/trg-trackers-tree-view.c
@@ -0,0 +1,51 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+#include "trg-trackers-tree-view.h"
+
+#include "trg-tree-view.h"
+
+G_DEFINE_TYPE(TrgTrackersTreeView, trg_trackers_tree_view,
+ TRG_TYPE_TREE_VIEW)
+
+
+static void
+trg_trackers_tree_view_class_init(TrgTrackersTreeViewClass * klass)
+{
+}
+
+static void trg_trackers_tree_view_init(TrgTrackersTreeView * self)
+{
+ trg_tree_view_add_pixbuf_text_column(TRG_TREE_VIEW(self),
+ TRACKERCOL_ICON,
+ TRACKERCOL_TIER, "Tier", -1);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Announce URL",
+ TRACKERCOL_ANNOUNCE);
+ trg_tree_view_add_column(TRG_TREE_VIEW(self), "Scrape URL",
+ TRACKERCOL_SCRAPE);
+}
+
+TrgTrackersTreeView *trg_trackers_tree_view_new(TrgTrackersModel * model)
+{
+ GObject *obj = g_object_new(TRG_TYPE_TRACKERS_TREE_VIEW, NULL);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(obj), GTK_TREE_MODEL(model));
+
+ return TRG_TRACKERS_TREE_VIEW(obj);
+}
diff --git a/src/trg-trackers-tree-view.h b/src/trg-trackers-tree-view.h
new file mode 100644
index 0000000..02c89fd
--- /dev/null
+++ b/src/trg-trackers-tree-view.h
@@ -0,0 +1,54 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TRG_TRACKERS_TREE_VIEW_H_
+#define TRG_TRACKERS_TREE_VIEW_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "trg-trackers-model.h"
+#include "trg-tree-view.h"
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TRACKERS_TREE_VIEW trg_trackers_tree_view_get_type()
+#define TRG_TRACKERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TRACKERS_TREE_VIEW, TrgTrackersTreeView))
+#define TRG_TRACKERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TRACKERS_TREE_VIEW, TrgTrackersTreeViewClass))
+#define TRG_IS_TRACKERS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TRACKERS_TREE_VIEW))
+#define TRG_IS_TRACKERS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TRACKERS_TREE_VIEW))
+#define TRG_TRACKERS_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TRACKERS_TREE_VIEW, TrgTrackersTreeViewClass))
+ typedef struct {
+ TrgTreeView parent;
+} TrgTrackersTreeView;
+
+typedef struct {
+ TrgTreeViewClass parent_class;
+} TrgTrackersTreeViewClass;
+
+GType trg_trackers_tree_view_get_type(void);
+
+TrgTrackersTreeView *trg_trackers_tree_view_new(TrgTrackersModel * model);
+
+G_END_DECLS
+#endif /* TRG_TRACKERS_TREE_VIEW_H_ */
diff --git a/src/trg-tree-view.c b/src/trg-tree-view.c
new file mode 100644
index 0000000..a3575c7
--- /dev/null
+++ b/src/trg-tree-view.c
@@ -0,0 +1,178 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <gtk/gtk.h>
+
+#include "trg-tree-view.h"
+#include "trg-cell-renderer-speed.h"
+#include "trg-cell-renderer-size.h"
+#include "trg-cell-renderer-ratio.h"
+#include "trg-cell-renderer-eta.h"
+
+G_DEFINE_TYPE(TrgTreeView, trg_tree_view, GTK_TYPE_TREE_VIEW)
+
+void trg_tree_view_std_column_setup(GtkTreeViewColumn * column, int index,
+ int width)
+{
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_reorderable(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, index);
+
+ if (width > 0) {
+ gtk_tree_view_column_set_sizing(column,
+ GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_fixed_width(column, width);
+ }
+}
+
+static void trg_tree_view_class_init(TrgTreeViewClass * klass)
+{
+}
+
+static void trg_tree_view_init(TrgTreeView * tv)
+{
+ gtk_tree_view_set_rubber_banding(GTK_TREE_VIEW(tv), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(tv), TRUE);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tv), TRUE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection
+ (GTK_TREE_VIEW(tv)),
+ GTK_SELECTION_MULTIPLE);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(tv), FALSE);
+}
+
+void trg_tree_view_add_size_column(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_size_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "size-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_eta_column(TrgTreeView * tv, char *title, int index,
+ int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_eta_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "eta-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_prog_column(TrgTreeView * tv,
+ gchar * title, gint index, gint width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_progress_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "value", index,
+ NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_speed_column(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_speed_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "speed-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_ratio_column(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = trg_cell_renderer_ratio_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "ratio-value",
+ index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void trg_tree_view_add_column_fixed_width(TrgTreeView * tv, char *title,
+ int index, int width)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(title, renderer,
+ "text", index, NULL);
+
+ trg_tree_view_std_column_setup(column, index, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+void
+trg_tree_view_add_pixbuf_text_column(TrgTreeView * tv,
+ int iconIndex,
+ int nameIndex,
+ gchar * text, int width)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ column = gtk_tree_view_column_new();
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_set_title(GTK_TREE_VIEW_COLUMN(column), text);
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer, "stock-id",
+ iconIndex, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer, "text",
+ nameIndex, NULL);
+
+ trg_tree_view_std_column_setup(column, nameIndex, width);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+}
+
+GtkWidget *trg_tree_view_new(void)
+{
+ return GTK_WIDGET(g_object_new(TRG_TYPE_TREE_VIEW, NULL));
+}
diff --git a/src/trg-tree-view.h b/src/trg-tree-view.h
new file mode 100644
index 0000000..7400d9e
--- /dev/null
+++ b/src/trg-tree-view.h
@@ -0,0 +1,74 @@
+/*
+ * transmission-remote-gtk - Transmission RPC client for GTK
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _TRG_TREE_VIEW_H_
+#define _TRG_TREE_VIEW_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define TRG_TYPE_TREE_VIEW trg_tree_view_get_type()
+#define TRG_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRG_TYPE_TREE_VIEW, TrgTreeView))
+#define TRG_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), TRG_TYPE_TREE_VIEW, TrgTreeViewClass))
+#define TRG_IS_TREE_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRG_TYPE_TREE_VIEW))
+#define TRG_IS_TREE_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), TRG_TYPE_TREE_VIEW))
+#define TRG_TREE_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), TRG_TYPE_TREE_VIEW, TrgTreeViewClass))
+ typedef struct {
+ GtkTreeView parent;
+} TrgTreeView;
+
+typedef struct {
+ GtkTreeViewClass parent_class;
+} TrgTreeViewClass;
+
+GType trg_tree_view_get_type(void);
+
+GtkWidget *trg_tree_view_new(void);
+
+G_END_DECLS
+#define trg_tree_view_add_column(tv, title, index) trg_tree_view_add_column_fixed_width(tv, title, index, -1)
+void trg_tree_view_add_column_fixed_width(TrgTreeView * treeview,
+ char *title, int index,
+ int width);
+
+void trg_tree_view_add_pixbuf_text_column(TrgTreeView *
+ treeview,
+ int iconIndex,
+ int nameIndex,
+ gchar * text, int width);
+
+void trg_tree_view_add_speed_column(TrgTreeView * tv, char *title,
+ int index, int width);
+void trg_tree_view_add_size_column(TrgTreeView * tv, char *title,
+ int index, int width);
+void trg_tree_view_add_prog_column(TrgTreeView * tv, gchar * title,
+ gint index, gint width);
+void trg_tree_view_add_ratio_column(TrgTreeView * tv, char *title,
+ int index, int width);
+void trg_tree_view_add_eta_column(TrgTreeView * tv, char *title, int index,
+ int width);
+void trg_tree_view_std_column_setup(GtkTreeViewColumn * column, int index,
+ int width);
+
+#endif /* _TRG_TREE_VIEW_H_ */
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..1ca2a15
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,261 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include <glib-object.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+
+#include "util.h"
+
+char *tr_strlpercent(char *buf, double x, size_t buflen)
+{
+ return tr_strpercent(buf, x, buflen);
+}
+
+char *tr_strpercent(char *buf, double x, size_t buflen)
+{
+ int precision;
+ if (x < 10.0)
+ precision = 2;
+ else if (x < 100.0)
+ precision = 1;
+ else
+ precision = 0;
+
+ tr_snprintf(buf, buflen, "%.*f%%", precision, tr_truncd(x, precision));
+ return buf;
+}
+
+double tr_truncd(double x, int decimal_places)
+{
+ const int i = (int) pow(10, decimal_places);
+ double x2 = (int) (x * i);
+ return x2 / i;
+}
+
+char *tr_strratio(char *buf, size_t buflen, double ratio,
+ const char *infinity)
+{
+ if ((int) ratio == TR_RATIO_NA)
+ tr_strlcpy(buf, "None", buflen);
+ else if ((int) ratio == TR_RATIO_INF)
+ tr_strlcpy(buf, infinity, buflen);
+ else if (ratio < 10.0)
+ tr_snprintf(buf, buflen, "%.2f", tr_truncd(ratio, 2));
+ else if (ratio < 100.0)
+ tr_snprintf(buf, buflen, "%.1f", tr_truncd(ratio, 1));
+ else
+ tr_snprintf(buf, buflen, "%'.0f", ratio);
+ return buf;
+}
+
+char *tr_strlratio(char *buf, double ratio, size_t buflen)
+{
+ return tr_strratio(buf, buflen, ratio, "\xE2\x88\x9E");
+}
+
+char *tr_strlsize(char *buf, guint64 size, size_t buflen)
+{
+ if (!size)
+ g_strlcpy(buf, "None", buflen);
+#if GLIB_CHECK_VERSION( 2, 16, 0 )
+ else {
+ char *tmp = g_format_size_for_display(size);
+ g_strlcpy(buf, tmp, buflen);
+ g_free(tmp);
+ }
+#else
+ else if (size < (guint64) KILOBYTE_FACTOR)
+ g_snprintf(buf, buflen,
+ ngettext("%'u byte", "%'u bytes", (guint) size),
+ (guint) size);
+ else {
+ gdouble displayed_size;
+ if (size < (guint64) MEGABYTE_FACTOR) {
+ displayed_size = (gdouble) size / KILOBYTE_FACTOR;
+ g_snprintf(buf, buflen, _("%'.1f KB"), displayed_size);
+ } else if (size < (guint64) GIGABYTE_FACTOR) {
+ displayed_size = (gdouble) size / MEGABYTE_FACTOR;
+ g_snprintf(buf, buflen, _("%'.1f MB"), displayed_size);
+ } else {
+ displayed_size = (gdouble) size / GIGABYTE_FACTOR;
+ g_snprintf(buf, buflen, _("%'.1f GB"), displayed_size);
+ }
+ }
+#endif
+ return buf;
+}
+
+char *tr_strlspeed(char *buf, double kb_sec, size_t buflen)
+{
+ const double speed = kb_sec;
+
+ if (speed < 1000.0) /* 0.0 KB to 999.9 KB */
+ g_snprintf(buf, buflen, "%.1f KB/s", speed);
+ else if (speed < 102400.0) /* 0.98 MB to 99.99 MB */
+ g_snprintf(buf, buflen, "%.2f MB/s", (speed / KILOBYTE_FACTOR));
+ else if (speed < 1024000.0) /* 100.0 MB to 999.9 MB */
+ g_snprintf(buf, buflen, "%.1f MB/s", (speed / MEGABYTE_FACTOR));
+ else /* insane speeds */
+ g_snprintf(buf, buflen, "%.2f GB/s", (speed / GIGABYTE_FACTOR));
+
+ return buf;
+}
+
+char *tr_strltime_short(char *buf, gint64 seconds, size_t buflen)
+{
+ int hours, minutes;
+
+ if (seconds < 0)
+ seconds = 0;
+
+ hours = seconds / 3600;
+ minutes = (seconds % 3600) / 60;
+ seconds = (seconds % 3600) % 60;
+
+ g_snprintf(buf, buflen, "%02d:%02d:%02ld", hours, minutes, seconds);
+
+ return buf;
+}
+
+char *tr_strltime_long(char *buf, gint64 seconds, size_t buflen)
+{
+ int days, hours, minutes;
+ char d[128], h[128], m[128], s[128];
+
+ if (seconds < 0)
+ seconds = 0;
+
+ days = seconds / 86400;
+ hours = (seconds % 86400) / 3600;
+ minutes = (seconds % 3600) / 60;
+ seconds = (seconds % 3600) % 60;
+
+ g_snprintf(d, sizeof(d), days > 1 ? "%d days" : "%d day", days);
+ g_snprintf(h, sizeof(h), hours > 1 ? "%d hours" : "%d hour", hours);
+ g_snprintf(m, sizeof(m), minutes > 1 ? "%d minutes" : "%d minute",
+ minutes);
+ g_snprintf(s, sizeof(s),
+ seconds > 1 ? "%ld seconds" : "%ld second", seconds);
+
+ if (days) {
+ if (days >= 4 || !hours) {
+ g_strlcpy(buf, d, buflen);
+ } else {
+ g_snprintf(buf, buflen, "%s, %s", d, h);
+ }
+ } else if (hours) {
+ if (hours >= 4 || !minutes) {
+ g_strlcpy(buf, h, buflen);
+ } else {
+ g_snprintf(buf, buflen, "%s, %s", h, m);
+ }
+ } else if (minutes) {
+ if (minutes >= 4 || !seconds) {
+ g_strlcpy(buf, m, buflen);
+ } else {
+ g_snprintf(buf, buflen, "%s, %s", m, s);
+ }
+ } else {
+ g_strlcpy(buf, s, buflen);
+ }
+
+ return buf;
+}
+
+char *gtr_localtime(time_t time)
+{
+ const struct tm tm = *localtime(&time);
+ char buf[256], *eoln;
+
+ g_strlcpy(buf, asctime(&tm), sizeof(buf));
+ if ((eoln = strchr(buf, '\n')))
+ *eoln = '\0';
+
+ return g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
+}
+
+char *gtr_localtime2(char *buf, time_t time, size_t buflen)
+{
+ char *tmp = gtr_localtime(time);
+ g_strlcpy(buf, tmp, buflen);
+ g_free(tmp);
+ return buf;
+}
+
+int tr_snprintf(char *buf, size_t buflen, const char *fmt, ...)
+{
+ int len;
+ va_list args;
+
+ va_start(args, fmt);
+ len = evutil_vsnprintf(buf, buflen, fmt, args);
+ va_end(args);
+ return len;
+}
+
+size_t tr_strlcpy(char *dst, const void *src, size_t siz)
+{
+#ifdef HAVE_STRLCPY
+ return strlcpy(dst, src, siz);
+#else
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++);
+ }
+
+ return s - (char *) src - 1; /* count does not include NUL */
+#endif
+}
+
+int
+evutil_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap)
+{
+#ifdef _MSC_VER
+ int r = _vsnprintf(buf, buflen, format, ap);
+ buf[buflen - 1] = '\0';
+ if (r >= 0)
+ return r;
+ else
+ return _vscprintf(format, ap);
+#else
+ int r = vsnprintf(buf, buflen, format, ap);
+ buf[buflen - 1] = '\0';
+ return r;
+#endif
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..27ca2d7
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,56 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2010 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <glib-object.h>
+
+#define WINDOW_ICON_FILE "/usr/share/icons/hicolor/scalable/apps/transmission.svg"
+
+#define trg_strlspeed(a, b) tr_strlspeed(a, b, sizeof(a))
+#define trg_strlpercent(a, b) tr_strlpercent(a, b, sizeof(a))
+#define trg_strlsize(a, b) tr_strlsize(a, b, sizeof(a))
+#define trg_strlratio(a, b) tr_strlratio(a, b, sizeof(a))
+
+#define TR_RATIO_NA -1
+#define TR_RATIO_INF -2
+#define KILOBYTE_FACTOR 1024.0
+#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
+#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
+
+char *tr_strltime_long(char *buf, gint64 seconds, size_t buflen);
+char *tr_strltime_short(char *buf, gint64 seconds, size_t buflen);
+char *tr_strpercent(char *buf, double x, size_t buflen);
+char *tr_strlpercent(char *buf, double x, size_t buflen);
+char *tr_strratio(char *buf, size_t buflen, double ratio,
+ const char *infinity);
+char *tr_strlratio(char *buf, double ratio, size_t buflen);
+char *tr_strlspeed(char *buf, double kb_sec, size_t buflen);
+char *tr_strlsize(char *buf, guint64 size, size_t buflen);
+char *gtr_localtime(time_t time);
+char *gtr_localtime2(char *buf, time_t time, size_t buflen);
+int tr_snprintf(char *buf, size_t buflen, const char *fmt, ...);
+int tr_snprintf(char *buf, size_t buflen, const char *fmt, ...);
+size_t tr_strlcpy(char *dst, const void *src, size_t siz);
+double tr_truncd(double x, int decimal_places);
+int evutil_vsnprintf(char *buf, size_t buflen, const char *format,
+ va_list ap);
+
+#endif /* UTIL_H_ */