From eca35c468094fc6b7177f33ef51fa873eb88e79c Mon Sep 17 00:00:00 2001 From: Alan Fitton Date: Sun, 30 Jan 2011 10:57:07 +0000 Subject: hello world! --- AUTHORS | 6 + COPYING | 339 +++++++ Makefile.am | 3 + README | 25 + TODO | 11 + aclocal.m4 | 997 ++++++++++++++++++++ autogen.sh | 1578 ++++++++++++++++++++++++++++++++ config.h.in | 68 ++ configure.ac | 43 + m4/pkg.m4 | 157 ++++ redhat/transmission-remote-gtk.spec | 89 ++ src/Makefile.am | 28 + src/base64.c | 66 ++ src/base64.h | 25 + src/dispatch.c | 109 +++ src/dispatch.h | 40 + src/hig.c | 208 +++++ src/hig.h | 85 ++ src/http.c | 145 +++ src/http.h | 43 + src/json.c | 93 ++ src/json.h | 34 + src/main.c | 135 +++ src/protocol-constants.h | 115 +++ src/requests.c | 155 ++++ src/requests.h | 39 + src/session-get.c | 161 ++++ src/session-get.h | 89 ++ src/tfile.c | 44 + src/tfile.h | 31 + src/torrent.c | 234 +++++ src/torrent.h | 71 ++ src/tpeer.c | 71 ++ src/tpeer.h | 47 + src/transmission-remote-gtk.desktop | 12 + src/transmission-remote-gtk.desktop.in | 12 + src/transmission-remote-gtk.schemas | 109 +++ src/trg-about-window.c | 82 ++ src/trg-about-window.h | 27 + src/trg-cell-renderer-eta.c | 116 +++ src/trg-cell-renderer-eta.h | 51 ++ src/trg-cell-renderer-priority.c | 115 +++ src/trg-cell-renderer-priority.h | 52 ++ src/trg-cell-renderer-ratio.c | 113 +++ src/trg-cell-renderer-ratio.h | 51 ++ src/trg-cell-renderer-size.c | 112 +++ src/trg-cell-renderer-size.h | 52 ++ src/trg-cell-renderer-speed.c | 114 +++ src/trg-cell-renderer-speed.h | 52 ++ src/trg-cell-renderer-wanted.c | 105 +++ src/trg-cell-renderer-wanted.h | 52 ++ src/trg-client.c | 80 ++ src/trg-client.h | 45 + src/trg-files-model.c | 177 ++++ src/trg-files-model.h | 74 ++ src/trg-files-tree-view.c | 369 ++++++++ src/trg-files-tree-view.h | 56 ++ src/trg-general-panel.c | 209 +++++ src/trg-general-panel.h | 59 ++ src/trg-json-widgets.c | 71 ++ src/trg-json-widgets.h | 40 + src/trg-main-window.c | 1449 +++++++++++++++++++++++++++++ src/trg-main-window.h | 71 ++ src/trg-menu-bar.c | 374 ++++++++ src/trg-menu-bar.h | 57 ++ src/trg-model.c | 65 ++ src/trg-model.h | 29 + src/trg-peers-model.c | 225 +++++ src/trg-peers-model.h | 85 ++ src/trg-peers-tree-view.c | 69 ++ src/trg-peers-tree-view.h | 54 ++ src/trg-preferences-dialog.c | 327 +++++++ src/trg-preferences-dialog.h | 56 ++ src/trg-preferences.h | 32 + src/trg-remote-prefs-dialog.c | 541 +++++++++++ src/trg-remote-prefs-dialog.h | 57 ++ src/trg-state-selector.c | 161 ++++ src/trg-state-selector.h | 63 ++ src/trg-status-bar.c | 130 +++ src/trg-status-bar.h | 60 ++ src/trg-toolbar.c | 267 ++++++ src/trg-toolbar.h | 56 ++ src/trg-torrent-add-url-dialog.c | 129 +++ src/trg-torrent-add-url-dialog.h | 57 ++ src/trg-torrent-model.c | 297 ++++++ src/trg-torrent-model.h | 102 +++ src/trg-torrent-props-dialog.c | 372 ++++++++ src/trg-torrent-props-dialog.h | 57 ++ src/trg-torrent-tree-view.c | 122 +++ src/trg-torrent-tree-view.h | 58 ++ src/trg-trackers-model.c | 72 ++ src/trg-trackers-model.h | 62 ++ src/trg-trackers-tree-view.c | 51 ++ src/trg-trackers-tree-view.h | 54 ++ src/trg-tree-view.c | 178 ++++ src/trg-tree-view.h | 74 ++ src/util.c | 261 ++++++ src/util.h | 56 ++ 98 files changed, 14021 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 Makefile.am create mode 100644 README create mode 100644 TODO create mode 100644 aclocal.m4 create mode 100755 autogen.sh create mode 100644 config.h.in create mode 100644 configure.ac create mode 100644 m4/pkg.m4 create mode 100644 redhat/transmission-remote-gtk.spec create mode 100644 src/Makefile.am create mode 100644 src/base64.c create mode 100644 src/base64.h create mode 100644 src/dispatch.c create mode 100644 src/dispatch.h create mode 100644 src/hig.c create mode 100644 src/hig.h create mode 100644 src/http.c create mode 100644 src/http.h create mode 100644 src/json.c create mode 100644 src/json.h create mode 100644 src/main.c create mode 100644 src/protocol-constants.h create mode 100644 src/requests.c create mode 100644 src/requests.h create mode 100644 src/session-get.c create mode 100644 src/session-get.h create mode 100644 src/tfile.c create mode 100644 src/tfile.h create mode 100644 src/torrent.c create mode 100644 src/torrent.h create mode 100644 src/tpeer.c create mode 100644 src/tpeer.h create mode 100644 src/transmission-remote-gtk.desktop create mode 100644 src/transmission-remote-gtk.desktop.in create mode 100644 src/transmission-remote-gtk.schemas create mode 100644 src/trg-about-window.c create mode 100644 src/trg-about-window.h create mode 100644 src/trg-cell-renderer-eta.c create mode 100644 src/trg-cell-renderer-eta.h create mode 100644 src/trg-cell-renderer-priority.c create mode 100644 src/trg-cell-renderer-priority.h create mode 100644 src/trg-cell-renderer-ratio.c create mode 100644 src/trg-cell-renderer-ratio.h create mode 100644 src/trg-cell-renderer-size.c create mode 100644 src/trg-cell-renderer-size.h create mode 100644 src/trg-cell-renderer-speed.c create mode 100644 src/trg-cell-renderer-speed.h create mode 100644 src/trg-cell-renderer-wanted.c create mode 100644 src/trg-cell-renderer-wanted.h create mode 100644 src/trg-client.c create mode 100644 src/trg-client.h create mode 100644 src/trg-files-model.c create mode 100644 src/trg-files-model.h create mode 100644 src/trg-files-tree-view.c create mode 100644 src/trg-files-tree-view.h create mode 100644 src/trg-general-panel.c create mode 100644 src/trg-general-panel.h create mode 100644 src/trg-json-widgets.c create mode 100644 src/trg-json-widgets.h create mode 100644 src/trg-main-window.c create mode 100644 src/trg-main-window.h create mode 100644 src/trg-menu-bar.c create mode 100644 src/trg-menu-bar.h create mode 100644 src/trg-model.c create mode 100644 src/trg-model.h create mode 100644 src/trg-peers-model.c create mode 100644 src/trg-peers-model.h create mode 100644 src/trg-peers-tree-view.c create mode 100644 src/trg-peers-tree-view.h create mode 100644 src/trg-preferences-dialog.c create mode 100644 src/trg-preferences-dialog.h create mode 100644 src/trg-preferences.h create mode 100644 src/trg-remote-prefs-dialog.c create mode 100644 src/trg-remote-prefs-dialog.h create mode 100644 src/trg-state-selector.c create mode 100644 src/trg-state-selector.h create mode 100644 src/trg-status-bar.c create mode 100644 src/trg-status-bar.h create mode 100644 src/trg-toolbar.c create mode 100644 src/trg-toolbar.h create mode 100644 src/trg-torrent-add-url-dialog.c create mode 100644 src/trg-torrent-add-url-dialog.h create mode 100644 src/trg-torrent-model.c create mode 100644 src/trg-torrent-model.h create mode 100644 src/trg-torrent-props-dialog.c create mode 100644 src/trg-torrent-props-dialog.h create mode 100644 src/trg-torrent-tree-view.c create mode 100644 src/trg-torrent-tree-view.h create mode 100644 src/trg-trackers-model.c create mode 100644 src/trg-trackers-model.h create mode 100644 src/trg-trackers-tree-view.c create mode 100644 src/trg-trackers-tree-view.h create mode 100644 src/trg-tree-view.c create mode 100644 src/trg-tree-view.h create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..b79e880 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ + transmission-remote-gtk authors + =============================== + +transmission-remote-gtk is developed by: + + Alan Fitton diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..8828815 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ +ACLOCAL_AMFLAGS = -I m4 +AUTOMAKE_OPTIONS = foreign +SUBDIRS = src diff --git a/README b/README new file mode 100644 index 0000000..58ae42a --- /dev/null +++ b/README @@ -0,0 +1,25 @@ +ABOUT + + transmission-remote-gtk is a GTK client for remote management of + the Transmission BitTorrent client, using its HTTP RPC protocol. + +BUILDING + + * To optionally see which country peers are located in, + install libgeoip and its headers when building. + + Building a Transmission release from the command line: + + $ tar zxfv transmission-remote-gtk-0.1.0.tar.gz + $ cd transmission-0.1.0 + $ ./configure + $ make + $ sudo make install + + Building from subversion checkout: + + $ svn co + $ ./autogen.sh + $ ./configure + $ make + $ sudo make install diff --git a/TODO b/TODO new file mode 100644 index 0000000..043b605 --- /dev/null +++ b/TODO @@ -0,0 +1,11 @@ +trg todo + +stats window +remote settings +better icons, inc status icon +statusicon right click +proxy +file priorities + +peers graph +geoip flags diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..a9230a8 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,997 @@ +# generated automatically by aclocal 1.11.1 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.66],, +[m4_warning([this file was generated for autoconf 2.66. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically `autoreconf'.])]) + +dnl AM_GCONF_SOURCE_2 +dnl Defines GCONF_SCHEMA_CONFIG_SOURCE which is where you should install schemas +dnl (i.e. pass to gconftool-2 +dnl Defines GCONF_SCHEMA_FILE_DIR which is a filesystem directory where +dnl you should install foo.schemas files +dnl + +AC_DEFUN([AM_GCONF_SOURCE_2], +[ + if test "x$GCONF_SCHEMA_INSTALL_SOURCE" = "x"; then + GCONF_SCHEMA_CONFIG_SOURCE=`gconftool-2 --get-default-source` + else + GCONF_SCHEMA_CONFIG_SOURCE=$GCONF_SCHEMA_INSTALL_SOURCE + fi + + AC_ARG_WITH([gconf-source], + AC_HELP_STRING([--with-gconf-source=sourceaddress], + [Config database for installing schema files.]), + [GCONF_SCHEMA_CONFIG_SOURCE="$withval"],) + + AC_SUBST(GCONF_SCHEMA_CONFIG_SOURCE) + AC_MSG_RESULT([Using config source $GCONF_SCHEMA_CONFIG_SOURCE for schema installation]) + + if test "x$GCONF_SCHEMA_FILE_DIR" = "x"; then + GCONF_SCHEMA_FILE_DIR='$(sysconfdir)/gconf/schemas' + fi + + AC_ARG_WITH([gconf-schema-file-dir], + AC_HELP_STRING([--with-gconf-schema-file-dir=dir], + [Directory for installing schema files.]), + [GCONF_SCHEMA_FILE_DIR="$withval"],) + + AC_SUBST(GCONF_SCHEMA_FILE_DIR) + AC_MSG_RESULT([Using $GCONF_SCHEMA_FILE_DIR as install directory for schema files]) + + AC_ARG_ENABLE(schemas-install, + AC_HELP_STRING([--disable-schemas-install], + [Disable the schemas installation]), + [case ${enableval} in + yes|no) ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-install]) ;; + esac]) + AM_CONDITIONAL([GCONF_SCHEMAS_INSTALL], [test "$enable_schemas_install" != no]) +]) + +# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.11' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.11.1], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.11.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 9 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 10 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], UPC, [depcc="$UPC" am_compiler_list=], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 5 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 16 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.62])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES(OBJC)], + [define([AC_PROG_OBJC], + defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl +]) +_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl +dnl The `parallel-tests' driver may need to know about EXEEXT, so add the +dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro +dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl +]) + +dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 6 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check for `mkdir -p'. +AC_DEFUN([AM_PROG_MKDIR_P], +[AC_PREREQ([2.60])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, +dnl while keeping a definition of mkdir_p for backward compatibility. +dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. +dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of +dnl Makefile.ins that do not define MKDIR_P, so we do our own +dnl adjustment using top_builddir (which is defined more often than +dnl MKDIR_P). +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl +case $mkdir_p in + [[\\/$]]* | ?:[[\\/]]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 5 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006, 2008 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /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 +# +# Patches: +# Sebastian Pipping +# +###################################################################### + +# 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 < header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the 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 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 . +# +# 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 .]) + ]) +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 - 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 +#include + +#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 +#include +#include +#include + +#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 +#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), "%s", 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 + +/** +*** 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 +#include +#include + +#include +#include +#include + +#include +#include + +#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 + +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include + +#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 + +#include +#include +#include +#include +#include +#include +#include +#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 + +#include +#include + +#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 +#include + +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 + +#include + +#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 + +#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 + +#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 +#include + +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 +#include +#include + +#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 + +#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 +#include + +#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 +#include + +#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 @@ + + + + + /schemas/apps/trg/auto-connect + /apps/trg/auto-connect + trg + bool + 0 + + + Auto connect + Auto connect + + + + + /schemas/apps/trg/system-tray + /apps/trg/system-tray + trg + bool + 0 + + + Show system tray icon + Show system tray icon while app is running + + + + + /schemas/apps/trg/window_height + /apps/trg/window_height + trg + int + 600 + + + Window height + Window height + + + + + /schemas/apps/trg/window_width + /apps/trg/window_width + trg + int + 800 + + + Window width + Window width + + + + + /schemas/apps/trg/port + /apps/trg/port + trg + int + 9091 + + + Port + Port + + + + + /schemas/apps/trg/username + /apps/trg/username + trg + string + + + + Username + Username + + + + + /schemas/apps/trg/password + /apps/trg/password + trg + string + + + + Password + Password + + + + + /schemas/apps/trg/hostname + /apps/trg/hostname + trg + string + + + + Hostname + Hostname + + + + + 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 +#include + +#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 ", 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 + +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 +#include + +#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 +#include + +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 +#include + +#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 +#include + +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 +#include + +#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 +#include + +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 +#include + +#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 +#include + +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 +#include + +#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 +#include + +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 +#include + +#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 +#include + +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 +#include + +#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 +#include + +#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 +#include + +#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 +#include + +#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 + +#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 + +#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 +#include + +#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("%s", 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 +#include + +#include + +#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 +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, + "Remove torrent \"%s\"?", + "Remove %d torrents?", + 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, + "Remove and delete torrent \"%s\"?", + "Remove and delete %d torrents?", + 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 +#include +#include +#include + +#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 +#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 +#include + +#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 +#include + +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 + +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 +#include +#include +#include +#include +#if HAVE_GEOIP +#include +#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 +#include +#if HAVE_GEOIP +#include +#endif +#include + +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 +#endif + +#include +#include + +#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 +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include + +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 +#include + +#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 +#include + +#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 +#include + +#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 + +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 +#include + +#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 +#include + +#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 +#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 +#include + +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 +#include + +#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 +#include + +#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 +#include +#include + +#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 +#include + +#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 +#include + +#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 + +#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 + +#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 +#include + +#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 +#include + +#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 +#include + +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 +#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 +#include + +#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 + +#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 + +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 +#include +#include + +#include +#include +#include +#include + +#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 + +#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_ */ -- cgit v1.2.3