diff options
author | Alan Fitton <ajf@eth0.org.uk> | 2011-01-30 10:57:07 +0000 |
---|---|---|
committer | Alan Fitton <ajf@eth0.org.uk> | 2011-01-30 10:57:07 +0000 |
commit | eca35c468094fc6b7177f33ef51fa873eb88e79c (patch) | |
tree | fa07fd0ea1db72ff2ad4bd8b04b8e546717f2731 |
hello world!
98 files changed, 14021 insertions, 0 deletions
@@ -0,0 +1,6 @@ + transmission-remote-gtk authors + =============================== + +transmission-remote-gtk is developed by: + + Alan Fitton <alan-at-eth0.org.uk> @@ -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 @@ -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 @@ -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, ¤tId, -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_ */ |